Skip to content

Commit 11153d6

Browse files
committed
feat: store git identity with GitHub info
1 parent 5b1503c commit 11153d6

File tree

5 files changed

+115
-60
lines changed

5 files changed

+115
-60
lines changed

packages/config/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ export {
1515
getSlackTargetChannels,
1616
getDefaultCwd,
1717
getDefaultOpenCodeServerUrl,
18-
getGitHubTokenForUser,
18+
getGitHubInfoForUser,
1919
resolveChannelCwd,
2020
setChannelCwd,
2121
setChannelWorkingDirectory,
22-
setGitHubTokenForUser,
23-
clearGitHubTokenForUser,
22+
setGitHubInfoForUser,
23+
clearGitHubInfoForUser,
2424
getChannelOpenCodeServerUrl,
2525
setChannelModel,
2626
setChannelDevServerId,

packages/config/local/ode.ts

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,17 @@ const workspaceSchema = z.object({
6060

6161
const odeConfigSchema = z.object({
6262
user: userSchema,
63-
githubTokens: z.record(z.string(), z.string()).optional().default({}),
63+
githubInfos: z
64+
.record(
65+
z.string(),
66+
z.object({
67+
token: z.string().optional().default(""),
68+
gitName: z.string().optional().default(""),
69+
gitEmail: z.string().optional().default(""),
70+
})
71+
)
72+
.optional()
73+
.default({}),
6474
devServers: z.array(devServerSchema),
6575
workspaces: z.array(workspaceSchema),
6676
updates: updateSchema.optional().default({
@@ -86,7 +96,7 @@ const EMPTY_TEMPLATE: OdeConfig = {
8696
avatar: "",
8797
defaultMessageFrequency: "medium",
8898
},
89-
githubTokens: {},
99+
githubInfos: {},
90100
devServers: [],
91101
workspaces: [],
92102
updates: {
@@ -231,29 +241,44 @@ export function getChannelDetails(channelId: string): ChannelDetail | null {
231241
return null;
232242
}
233243

234-
export function getGitHubTokenForUser(userId: string): string | null {
235-
const token = loadOdeConfig().githubTokens?.[userId]?.trim();
236-
return token && token.length > 0 ? token : null;
244+
export type GitHubInfo = {
245+
token: string;
246+
gitName?: string;
247+
gitEmail?: string;
248+
};
249+
250+
export function getGitHubInfoForUser(userId: string): GitHubInfo | null {
251+
const info = loadOdeConfig().githubInfos?.[userId];
252+
if (!info) return null;
253+
const token = info.token?.trim();
254+
if (!token) return null;
255+
const gitName = info.gitName?.trim() || undefined;
256+
const gitEmail = info.gitEmail?.trim() || undefined;
257+
return { token, gitName, gitEmail };
237258
}
238259

239-
export function setGitHubTokenForUser(userId: string, token: string): void {
260+
export function setGitHubInfoForUser(userId: string, info: GitHubInfo): void {
240261
const config = loadOdeConfig();
241-
const githubTokens = { ...(config.githubTokens ?? {}) };
242-
const trimmed = token.trim();
243-
if (trimmed.length === 0) {
244-
delete githubTokens[userId];
262+
const githubInfos = { ...(config.githubInfos ?? {}) };
263+
const token = info.token.trim();
264+
if (token.length === 0) {
265+
delete githubInfos[userId];
245266
} else {
246-
githubTokens[userId] = trimmed;
267+
githubInfos[userId] = {
268+
token,
269+
gitName: info.gitName?.trim() || "",
270+
gitEmail: info.gitEmail?.trim() || "",
271+
};
247272
}
248-
saveOdeConfig({ ...config, githubTokens });
273+
saveOdeConfig({ ...config, githubInfos });
249274
}
250275

251-
export function clearGitHubTokenForUser(userId: string): void {
276+
export function clearGitHubInfoForUser(userId: string): void {
252277
const config = loadOdeConfig();
253-
const githubTokens = { ...(config.githubTokens ?? {}) };
254-
if (!(userId in githubTokens)) return;
255-
delete githubTokens[userId];
256-
saveOdeConfig({ ...config, githubTokens });
278+
const githubInfos = { ...(config.githubInfos ?? {}) };
279+
if (!(userId in githubInfos)) return;
280+
delete githubInfos[userId];
281+
saveOdeConfig({ ...config, githubInfos });
257282
}
258283

259284
export type ChannelCwdInfo = {

packages/ims/slack/client.ts

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {
1212
getChannelOpenCodeServerUrl,
1313
getDevServers,
1414
getDefaultOpenCodeServerUrl,
15-
getGitHubTokenForUser,
15+
getGitHubInfoForUser,
1616
loadOdeConfig,
1717
isLocalMode,
1818
resolveChannelCwd,
@@ -170,7 +170,7 @@ async function buildSlackContext(
170170
threadHistory: threadHistory || undefined,
171171
hasCustomSlackTool: await hasOdeSlackTool(cwd),
172172
odeSlackApiUrl: getOdeSlackApiUrl(),
173-
hasGitHubToken: Boolean(getGitHubTokenForUser(userId)),
173+
hasGitHubToken: Boolean(getGitHubInfoForUser(userId)?.token),
174174
},
175175
};
176176
}
@@ -389,21 +389,21 @@ async function postGitHubLauncher(
389389
userId: string,
390390
client: WebClient
391391
): Promise<void> {
392-
const hasToken = Boolean(getGitHubTokenForUser(userId));
392+
const hasToken = Boolean(getGitHubInfoForUser(userId)?.token);
393393
const statusText = hasToken
394394
? "GitHub token is set for your account."
395395
: "No GitHub token set yet.";
396396

397397
await client.chat.postEphemeral({
398398
channel: channelId,
399399
user: userId,
400-
text: "Open GitHub token settings",
400+
text: "Open GitHub info settings",
401401
blocks: [
402402
{
403403
type: "section",
404404
text: {
405405
type: "mrkdwn",
406-
text: `${statusText} Add or update your token to enable GitHub CLI actions.`,
406+
text: `${statusText} Add or update your info to enable GitHub CLI actions.`,
407407
},
408408
},
409409
{
@@ -412,7 +412,7 @@ async function postGitHubLauncher(
412412
{
413413
type: "button",
414414
action_id: "open_github_token_modal",
415-
text: { type: "plain_text", text: "Set GitHub token" },
415+
text: { type: "plain_text", text: "Set GitHub info" },
416416
value: channelId,
417417
},
418418
],
@@ -1373,10 +1373,18 @@ async function handleUserMessageInternal(
13731373
let session = loadSession(channelId, threadId);
13741374
const threadOwnerUserId = session?.threadOwnerUserId ?? context.userId;
13751375
const sessionEnv: Record<string, string> = {};
1376-
const githubToken = getGitHubTokenForUser(threadOwnerUserId);
1377-
if (githubToken) {
1378-
sessionEnv.GH_TOKEN = githubToken;
1379-
sessionEnv.GITHUB_TOKEN = githubToken;
1376+
const githubInfo = getGitHubInfoForUser(threadOwnerUserId);
1377+
if (githubInfo?.token) {
1378+
sessionEnv.GH_TOKEN = githubInfo.token;
1379+
sessionEnv.GITHUB_TOKEN = githubInfo.token;
1380+
}
1381+
if (githubInfo?.gitName) {
1382+
sessionEnv.GIT_AUTHOR_NAME = githubInfo.gitName;
1383+
sessionEnv.GIT_COMMITTER_NAME = githubInfo.gitName;
1384+
}
1385+
if (githubInfo?.gitEmail) {
1386+
sessionEnv.GIT_AUTHOR_EMAIL = githubInfo.gitEmail;
1387+
sessionEnv.GIT_COMMITTER_EMAIL = githubInfo.gitEmail;
13801388
}
13811389
if (context.opencodeServerUrl) {
13821390
sessionEnv.OPENCODE_SERVER_URL = context.opencodeServerUrl;

packages/ims/slack/commands.ts

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@ import {
44
getChannelModel,
55
resolveChannelCwd,
66
getDevServers,
7-
getGitHubTokenForUser,
7+
getGitHubInfoForUser,
88
isLocalMode,
99
setChannelDevServerId,
1010
setChannelModel,
1111
setChannelWorkingDirectory,
12-
setGitHubTokenForUser,
12+
setGitHubInfoForUser,
1313
} from "@ode/config";
1414

1515
const SETTINGS_LAUNCH_ACTION = "open_settings_modal";
@@ -18,6 +18,10 @@ const GITHUB_LAUNCH_ACTION = "open_github_token_modal";
1818
const GITHUB_MODAL_ID = "github_token_modal";
1919
const GITHUB_TOKEN_BLOCK = "github_token";
2020
const GITHUB_TOKEN_ACTION = "github_token_input";
21+
const GITHUB_NAME_BLOCK = "github_name";
22+
const GITHUB_NAME_ACTION = "github_name_input";
23+
const GITHUB_EMAIL_BLOCK = "github_email";
24+
const GITHUB_EMAIL_ACTION = "github_email_input";
2125
const DEV_SERVER_BLOCK = "dev_server";
2226
const DEV_SERVER_ACTION = "dev_server_select";
2327
const MODEL_BLOCK = "model";
@@ -110,11 +114,16 @@ function buildSettingsModal(params: {
110114
};
111115
}
112116

113-
function buildGitHubTokenModal(params: { channelId: string; hasToken: boolean }) {
114-
const { channelId, hasToken } = params;
117+
function buildGitHubTokenModal(params: {
118+
channelId: string;
119+
hasToken: boolean;
120+
gitName?: string;
121+
gitEmail?: string;
122+
}) {
123+
const { channelId, hasToken, gitName, gitEmail } = params;
115124
const statusText = hasToken
116125
? "A GitHub token is already set for your account. Submit a new value to update it."
117-
: "Set a GitHub token to enable GitHub CLI actions.";
126+
: "Set a GitHub token to enable GitHub CLI actions and git identity.";
118127

119128
return {
120129
type: "modal" as const,
@@ -138,6 +147,30 @@ function buildGitHubTokenModal(params: { channelId: string; hasToken: boolean })
138147
placeholder: { type: "plain_text" as const, text: "ghp_..." },
139148
},
140149
},
150+
{
151+
type: "input" as const,
152+
block_id: GITHUB_NAME_BLOCK,
153+
optional: true,
154+
label: { type: "plain_text" as const, text: "Git Name" },
155+
element: {
156+
type: "plain_text_input" as const,
157+
action_id: GITHUB_NAME_ACTION,
158+
initial_value: gitName ?? "",
159+
placeholder: { type: "plain_text" as const, text: "Jane Doe" },
160+
},
161+
},
162+
{
163+
type: "input" as const,
164+
block_id: GITHUB_EMAIL_BLOCK,
165+
optional: true,
166+
label: { type: "plain_text" as const, text: "Git Email" },
167+
element: {
168+
type: "plain_text_input" as const,
169+
action_id: GITHUB_EMAIL_ACTION,
170+
initial_value: gitEmail ?? "",
171+
placeholder: { type: "plain_text" as const, text: "jane@example.com" },
172+
},
173+
},
141174
],
142175
};
143176
}
@@ -202,14 +235,17 @@ export function setupInteractiveHandlers(): void {
202235
await client.chat.postEphemeral({
203236
channel: channelId,
204237
user: userId,
205-
text: "GitHub token updates are not implemented in cloud mode.",
238+
text: "GitHub info updates are not implemented in cloud mode.",
206239
});
207240
return;
208241
}
209242

243+
const info = getGitHubInfoForUser(userId);
210244
const view = buildGitHubTokenModal({
211245
channelId,
212-
hasToken: Boolean(getGitHubTokenForUser(userId)),
246+
hasToken: Boolean(info?.token),
247+
gitName: info?.gitName,
248+
gitEmail: info?.gitEmail,
213249
});
214250

215251
await client.views.open({
@@ -313,6 +349,8 @@ export function setupInteractiveHandlers(): void {
313349
slackApp.view(GITHUB_MODAL_ID, async ({ ack, view, body, client }) => {
314350
const values = view.state.values;
315351
const token = values?.[GITHUB_TOKEN_BLOCK]?.[GITHUB_TOKEN_ACTION]?.value || "";
352+
const gitName = values?.[GITHUB_NAME_BLOCK]?.[GITHUB_NAME_ACTION]?.value || "";
353+
const gitEmail = values?.[GITHUB_EMAIL_BLOCK]?.[GITHUB_EMAIL_ACTION]?.value || "";
316354
const trimmed = token.trim();
317355
const errors: Record<string, string> = {};
318356

@@ -333,20 +371,24 @@ export function setupInteractiveHandlers(): void {
333371
if (!userId || !channelId) return;
334372

335373
try {
336-
setGitHubTokenForUser(userId, trimmed);
374+
setGitHubInfoForUser(userId, {
375+
token: trimmed,
376+
gitName,
377+
gitEmail,
378+
});
337379
} catch (err) {
338380
await client.chat.postEphemeral({
339381
channel: channelId,
340382
user: userId,
341-
text: `Failed to save GitHub token: ${err instanceof Error ? err.message : String(err)}`,
383+
text: `Failed to save GitHub info: ${err instanceof Error ? err.message : String(err)}`,
342384
});
343385
return;
344386
}
345387

346388
await client.chat.postEphemeral({
347389
channel: channelId,
348390
user: userId,
349-
text: "GitHub token updated.",
391+
text: "GitHub info updated.",
350392
});
351393
});
352394

packages/web-ui/src/routes/local-setting/+page.svelte

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
email: string;
3737
initials?: string;
3838
avatar?: string;
39-
githubToken: string;
4039
defaultMessageFrequency: "aggressive" | "medium" | "minimum";
4140
};
4241
@@ -867,25 +866,6 @@
867866
</div>
868867
</div>
869868

870-
<div class="input-field">
871-
<label for="github-token">GitHub Access Token</label
872-
>
873-
<input
874-
id="github-token"
875-
type="password"
876-
value={user.githubToken}
877-
on:input={(event) => {
878-
const target =
879-
event.currentTarget as HTMLInputElement;
880-
user = {
881-
...user,
882-
githubToken: target.value,
883-
};
884-
scheduleAutoSave();
885-
}}
886-
/>
887-
</div>
888-
889869
<div class="message-freq-group">
890870
<span class="message-freq-label"
891871
>Message Update Frequency</span

0 commit comments

Comments
 (0)