botmux 2.40.0-canary.0 → 2.40.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +1 -0
- package/README.md +1 -0
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +22 -2
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +0 -8
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +0 -9
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/core/command-handler.d.ts +20 -0
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +105 -106
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +0 -2
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +0 -56
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +1 -17
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +10 -64
- package/dist/core/role-resolver.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +9 -14
- package/dist/core/session-manager.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +19 -27
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/web/app.js +0 -3
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard-web/app.js +244 -310
- package/dist/dashboard-web/index.html +0 -1
- package/dist/dashboard.js +1 -149
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +7 -22
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +7 -22
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/client.d.ts +0 -21
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +18 -86
- package/dist/im/lark/client.js.map +1 -1
- package/dist/services/group-creator.d.ts +0 -6
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +1 -10
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +0 -11
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +0 -27
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/project-scanner.d.ts +10 -0
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +11 -0
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +10 -64
- package/dist/skills/definitions.js.map +1 -1
- package/dist/workflows/events/payloads.d.ts +2 -2
- package/dist/workflows/events/schema.d.ts +8 -8
- package/package.json +1 -1
- package/dist/core/trigger-session.d.ts +0 -9
- package/dist/core/trigger-session.d.ts.map +0 -1
- package/dist/core/trigger-session.js +0 -158
- package/dist/core/trigger-session.js.map +0 -1
- package/dist/dashboard/connector-api.d.ts +0 -3
- package/dist/dashboard/connector-api.d.ts.map +0 -1
- package/dist/dashboard/connector-api.js +0 -362
- package/dist/dashboard/connector-api.js.map +0 -1
- package/dist/dashboard/federation-api.d.ts +0 -31
- package/dist/dashboard/federation-api.d.ts.map +0 -1
- package/dist/dashboard/federation-api.js +0 -256
- package/dist/dashboard/federation-api.js.map +0 -1
- package/dist/dashboard/federation-spoke-api.d.ts +0 -41
- package/dist/dashboard/federation-spoke-api.d.ts.map +0 -1
- package/dist/dashboard/federation-spoke-api.js +0 -398
- package/dist/dashboard/federation-spoke-api.js.map +0 -1
- package/dist/dashboard/pairing-api.d.ts +0 -19
- package/dist/dashboard/pairing-api.d.ts.map +0 -1
- package/dist/dashboard/pairing-api.js +0 -83
- package/dist/dashboard/pairing-api.js.map +0 -1
- package/dist/dashboard/team-group.d.ts +0 -18
- package/dist/dashboard/team-group.d.ts.map +0 -1
- package/dist/dashboard/team-group.js +0 -7
- package/dist/dashboard/team-group.js.map +0 -1
- package/dist/dashboard/team-page.d.ts +0 -8
- package/dist/dashboard/team-page.d.ts.map +0 -1
- package/dist/dashboard/team-page.js +0 -480
- package/dist/dashboard/team-page.js.map +0 -1
- package/dist/dashboard/team-routes.d.ts +0 -30
- package/dist/dashboard/team-routes.d.ts.map +0 -1
- package/dist/dashboard/team-routes.js +0 -458
- package/dist/dashboard/team-routes.js.map +0 -1
- package/dist/dashboard/trigger-api.d.ts +0 -13
- package/dist/dashboard/trigger-api.d.ts.map +0 -1
- package/dist/dashboard/trigger-api.js +0 -77
- package/dist/dashboard/trigger-api.js.map +0 -1
- package/dist/dashboard/web/team-federation.d.ts +0 -2
- package/dist/dashboard/web/team-federation.d.ts.map +0 -1
- package/dist/dashboard/web/team-federation.js +0 -304
- package/dist/dashboard/web/team-federation.js.map +0 -1
- package/dist/dashboard/webhook-routes.d.ts +0 -19
- package/dist/dashboard/webhook-routes.d.ts.map +0 -1
- package/dist/dashboard/webhook-routes.js +0 -321
- package/dist/dashboard/webhook-routes.js.map +0 -1
- package/dist/services/bot-owner-store.d.ts +0 -28
- package/dist/services/bot-owner-store.d.ts.map +0 -1
- package/dist/services/bot-owner-store.js +0 -82
- package/dist/services/bot-owner-store.js.map +0 -1
- package/dist/services/bot-profile-store.d.ts +0 -16
- package/dist/services/bot-profile-store.d.ts.map +0 -1
- package/dist/services/bot-profile-store.js +0 -98
- package/dist/services/bot-profile-store.js.map +0 -1
- package/dist/services/connector-store.d.ts +0 -63
- package/dist/services/connector-store.d.ts.map +0 -1
- package/dist/services/connector-store.js +0 -79
- package/dist/services/connector-store.js.map +0 -1
- package/dist/services/deployment-identity.d.ts +0 -9
- package/dist/services/deployment-identity.d.ts.map +0 -1
- package/dist/services/deployment-identity.js +0 -47
- package/dist/services/deployment-identity.js.map +0 -1
- package/dist/services/federation-membership-store.d.ts +0 -23
- package/dist/services/federation-membership-store.d.ts.map +0 -1
- package/dist/services/federation-membership-store.js +0 -66
- package/dist/services/federation-membership-store.js.map +0 -1
- package/dist/services/federation-roster.d.ts +0 -45
- package/dist/services/federation-roster.d.ts.map +0 -1
- package/dist/services/federation-roster.js +0 -51
- package/dist/services/federation-roster.js.map +0 -1
- package/dist/services/federation-store.d.ts +0 -65
- package/dist/services/federation-store.d.ts.map +0 -1
- package/dist/services/federation-store.js +0 -125
- package/dist/services/federation-store.js.map +0 -1
- package/dist/services/invite-store.d.ts +0 -28
- package/dist/services/invite-store.d.ts.map +0 -1
- package/dist/services/invite-store.js +0 -85
- package/dist/services/invite-store.js.map +0 -1
- package/dist/services/pairing-store.d.ts +0 -47
- package/dist/services/pairing-store.d.ts.map +0 -1
- package/dist/services/pairing-store.js +0 -132
- package/dist/services/pairing-store.js.map +0 -1
- package/dist/services/team-roster.d.ts +0 -28
- package/dist/services/team-roster.d.ts.map +0 -1
- package/dist/services/team-roster.js +0 -58
- package/dist/services/team-roster.js.map +0 -1
- package/dist/services/team-store.d.ts +0 -54
- package/dist/services/team-store.d.ts.map +0 -1
- package/dist/services/team-store.js +0 -156
- package/dist/services/team-store.js.map +0 -1
- package/dist/services/trigger-log-store.d.ts +0 -46
- package/dist/services/trigger-log-store.d.ts.map +0 -1
- package/dist/services/trigger-log-store.js +0 -132
- package/dist/services/trigger-log-store.js.map +0 -1
- package/dist/services/trigger-types.d.ts +0 -57
- package/dist/services/trigger-types.d.ts.map +0 -1
- package/dist/services/trigger-types.js +0 -28
- package/dist/services/trigger-types.js.map +0 -1
- package/dist/services/web-session-store.d.ts +0 -28
- package/dist/services/web-session-store.d.ts.map +0 -1
- package/dist/services/web-session-store.js +0 -84
- package/dist/services/web-session-store.js.map +0 -1
- package/dist/services/webhook-key.d.ts +0 -16
- package/dist/services/webhook-key.d.ts.map +0 -1
- package/dist/services/webhook-key.js +0 -123
- package/dist/services/webhook-key.js.map +0 -1
- package/dist/services/webhook-lifecycle-extractors.d.ts +0 -15
- package/dist/services/webhook-lifecycle-extractors.d.ts.map +0 -1
- package/dist/services/webhook-lifecycle-extractors.js +0 -59
- package/dist/services/webhook-lifecycle-extractors.js.map +0 -1
- package/dist/services/webhook-lifecycle-store.d.ts +0 -45
- package/dist/services/webhook-lifecycle-store.d.ts.map +0 -1
- package/dist/services/webhook-lifecycle-store.js +0 -159
- package/dist/services/webhook-lifecycle-store.js.map +0 -1
- package/dist/workflows/trigger-from-envelope.d.ts +0 -13
- package/dist/workflows/trigger-from-envelope.d.ts.map +0 -1
- package/dist/workflows/trigger-from-envelope.js +0 -67
- package/dist/workflows/trigger-from-envelope.js.map +0 -1
package/README.en.md
CHANGED
|
@@ -308,6 +308,7 @@ Send these straight into a topic — the daemon intercepts them (no clash with t
|
|
|
308
308
|
|---------|-------------|
|
|
309
309
|
| `/repo` | Show project selector card (interactive dropdown + text list) |
|
|
310
310
|
| `/repo <N>` | Switch to Nth project from last scan |
|
|
311
|
+
| `/repo <path\|name>` | Skip the selector card; pass a path (relative/absolute) or a first-level project name under workingDir |
|
|
311
312
|
| `/skip` | Skip the repo selector card, start the session in the default dir |
|
|
312
313
|
| `/cd <path>` | Change working directory and restart the CLI process |
|
|
313
314
|
| `/status` | Show session info (uptime, terminal URL, etc.) |
|
package/README.md
CHANGED
|
@@ -302,6 +302,7 @@ botmux autostart enable
|
|
|
302
302
|
|------|------|
|
|
303
303
|
| `/repo` | 显示项目选择卡片(交互式下拉 + 文本列表) |
|
|
304
304
|
| `/repo <N>` | 切换到上次扫描的第 N 个项目 |
|
|
305
|
+
| `/repo <路径\|项目名>` | 跳过选择卡片,直接指定路径(相对/绝对)或 workingDir 下的一级项目名 |
|
|
305
306
|
| `/skip` | 跳过仓库选择卡片,直接用默认目录开启会话 |
|
|
306
307
|
| `/cd <路径>` | 切换工作目录并重启 CLI 进程 |
|
|
307
308
|
| `/status` | 查看会话信息(运行时间、终端地址等) |
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-installer.d.ts","sourceRoot":"","sources":["../../src/adapters/hook-installer.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;CACxD;
|
|
1
|
+
{"version":3,"file":"hook-installer.d.ts","sourceRoot":"","sources":["../../src/adapters/hook-installer.ts"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;CACxD;AA0LD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,MAAM,GAClB,IAAI,CAoBN"}
|
|
@@ -40,9 +40,29 @@ function writeIfChanged(filePath, content) {
|
|
|
40
40
|
throw new Error(`写入 ${filePath} 失败:${err.message}`);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* 从完整 hookCommand 中提取 `hook <cliId>` 尾签名。
|
|
45
|
+
* hookCommand 形如:`"<node>" "<...dist/cli.js>" hook claude-code`,
|
|
46
|
+
* 尾部 `hook <cliId>` 不随 node / cli.js 安装路径变化。
|
|
47
|
+
*/
|
|
48
|
+
function botmuxHookSuffix(hookCommand) {
|
|
49
|
+
const idx = hookCommand.lastIndexOf(' hook ');
|
|
50
|
+
return idx === -1 ? hookCommand : hookCommand.slice(idx + 1); // "hook <cliId>"
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 判断某个 hook group 是否是 botmux ask hook(用于幂等替换)。
|
|
54
|
+
*
|
|
55
|
+
* 不能只按命令字符串完全相等比对:同一台机器上 dev 源码 checkout 与 npm global
|
|
56
|
+
* 安装的 cli.js 绝对路径不同,命令字符串就不同,会导致两条 botmux hook 同时残留、
|
|
57
|
+
* 同一次 AskUserQuestion 触发两次 → 飞书发出两张卡。
|
|
58
|
+
* 因此结构化识别:命令引用了 botmux 的 `cli.js` 且尾部是相同的 `hook <cliId>` 签名,
|
|
59
|
+
* 即视为 botmux hook,无论它指向哪个安装路径。
|
|
60
|
+
*/
|
|
44
61
|
function isBotmuxAskHookGroup(group, hookCommand) {
|
|
45
|
-
|
|
62
|
+
const suffix = botmuxHookSuffix(hookCommand); // e.g. "hook claude-code"
|
|
63
|
+
return group.hooks.some((e) => e.type === 'command' &&
|
|
64
|
+
(e.command === hookCommand ||
|
|
65
|
+
(e.command.includes('cli.js') && e.command.trimEnd().endsWith(suffix))));
|
|
46
66
|
}
|
|
47
67
|
function removeBotmuxAskHookGroups(hooks, eventName, hookCommand) {
|
|
48
68
|
const existing = hooks[eventName] ?? [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hook-installer.js","sourceRoot":"","sources":["../../src/adapters/hook-installer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AASrD,6EAA6E;AAE7E,8BAA8B;AAC9B,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED,2BAA2B;AAC3B,SAAS,YAAY,CAAI,QAAgB;IACvC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAM,CAAC;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gCAAgC;AAChC,SAAS,cAAc,CAAC,QAAgB,EAAE,OAAe;IACvD,IAAI,CAAC;QACH,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjD,IAAI,QAAQ,KAAK,OAAO;gBAAE,OAAO,KAAK,CAAC,CAAC,YAAY;QACtD,CAAC;QACD,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,aAAa,CAAC,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;QAC1C,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,GAAQ,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,MAAM,QAAQ,OAAO,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAoBD,
|
|
1
|
+
{"version":3,"file":"hook-installer.js","sourceRoot":"","sources":["../../src/adapters/hook-installer.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,14 +15,6 @@ export type BotListOutputEntry = {
|
|
|
15
15
|
larkAppId: string;
|
|
16
16
|
/** Alias for workflow authors. Equal to larkAppId when locally configured. */
|
|
17
17
|
workflowBot: string | null;
|
|
18
|
-
/** Short capability label (team-level), for picking who to hand off to. */
|
|
19
|
-
capability: string | null;
|
|
20
|
-
/** Whether this bot has a team-level role registered. */
|
|
21
|
-
hasTeamRole: boolean;
|
|
22
|
-
/** Whether YOU (the listing bot) can reliably @-mention it from here. */
|
|
23
|
-
mentionable: boolean;
|
|
24
|
-
/** How the @-mention handle was resolved. */
|
|
25
|
-
mentionSource: 'cross-ref' | 'self' | 'observed' | 'fallback';
|
|
26
18
|
};
|
|
27
19
|
export declare function formatChatBotsForCli(chatBots: ChatBotMember[], currentLarkAppId: string): BotListOutputEntry[];
|
|
28
20
|
export declare function formatBotInfoEntriesForCli(botEntries: BotInfoEntryForList[], currentLarkAppId: string): BotListOutputEntry[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bots-list-output.d.ts","sourceRoot":"","sources":["../../src/cli/bots-list-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,YAAY,GAAG,WAAW,CAAC;IACnC,gGAAgG;IAChG,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"bots-list-output.d.ts","sourceRoot":"","sources":["../../src/cli/bots-list-output.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,MAAM,MAAM,mBAAmB,GAAG;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wFAAwF;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,YAAY,GAAG,WAAW,CAAC;IACnC,gGAAgG;IAChG,SAAS,EAAE,MAAM,CAAC;IAClB,8EAA8E;IAC9E,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EAAE,EACzB,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,EAAE,CAStB;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,mBAAmB,EAAE,EACjC,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,EAAE,CAWtB"}
|
|
@@ -6,10 +6,6 @@ export function formatChatBotsForCli(chatBots, currentLarkAppId) {
|
|
|
6
6
|
source: cb.source,
|
|
7
7
|
larkAppId: cb.larkAppId,
|
|
8
8
|
workflowBot: cb.larkAppId || null,
|
|
9
|
-
capability: cb.capability ?? null,
|
|
10
|
-
hasTeamRole: cb.hasTeamRole,
|
|
11
|
-
mentionable: cb.mentionable,
|
|
12
|
-
mentionSource: cb.mentionSource,
|
|
13
9
|
}));
|
|
14
10
|
}
|
|
15
11
|
export function formatBotInfoEntriesForCli(botEntries, currentLarkAppId) {
|
|
@@ -22,11 +18,6 @@ export function formatBotInfoEntriesForCli(botEntries, currentLarkAppId) {
|
|
|
22
18
|
source: 'configured',
|
|
23
19
|
larkAppId: b.larkAppId,
|
|
24
20
|
workflowBot: b.larkAppId,
|
|
25
|
-
// Local fallback path (no live chat query): we only know self reliably.
|
|
26
|
-
capability: null,
|
|
27
|
-
hasTeamRole: false,
|
|
28
|
-
mentionable: b.larkAppId === currentLarkAppId,
|
|
29
|
-
mentionSource: (b.larkAppId === currentLarkAppId ? 'self' : 'fallback'),
|
|
30
21
|
}));
|
|
31
22
|
}
|
|
32
23
|
//# sourceMappingURL=bots-list-output.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bots-list-output.js","sourceRoot":"","sources":["../../src/cli/bots-list-output.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bots-list-output.js","sourceRoot":"","sources":["../../src/cli/bots-list-output.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,oBAAoB,CAClC,QAAyB,EACzB,gBAAwB;IAExB,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QAC3B,IAAI,EAAE,EAAE,CAAC,WAAW;QACpB,MAAM,EAAE,EAAE,CAAC,MAAM;QACjB,MAAM,EAAE,EAAE,CAAC,SAAS,KAAK,gBAAgB;QACzC,MAAM,EAAE,EAAE,CAAC,MAAM;QACjB,SAAS,EAAE,EAAE,CAAC,SAAS;QACvB,WAAW,EAAE,EAAE,CAAC,SAAS,IAAI,IAAI;KAClC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,UAAiC,EACjC,gBAAwB;IAExB,OAAO,UAAU;SACd,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;SAC1B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,IAAI,EAAE,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,KAAK;QAC1B,MAAM,EAAE,CAAC,CAAC,SAAU;QACpB,MAAM,EAAE,CAAC,CAAC,SAAS,KAAK,gBAAgB;QACxC,MAAM,EAAE,YAAqB;QAC7B,SAAS,EAAE,CAAC,CAAC,SAAS;QACtB,WAAW,EAAE,CAAC,CAAC,SAAS;KACzB,CAAC,CAAC,CAAC;AACR,CAAC"}
|
|
@@ -16,6 +16,26 @@ export interface SlashCommandInvocation {
|
|
|
16
16
|
content: string;
|
|
17
17
|
}
|
|
18
18
|
export { validateWorkingDir };
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a non-numeric `/repo <arg>` into a concrete repo path + display name.
|
|
21
|
+
* `arg` is either a path (absolute or relative) or a first-level project name
|
|
22
|
+
* under one of the bot's scan dirs — letting the user skip the selection card.
|
|
23
|
+
*
|
|
24
|
+
* Resolution:
|
|
25
|
+
* 1. Build candidate absolute paths — absolute / `~` taken as-is; relative or
|
|
26
|
+
* bare names resolved against each scan dir, then the daemon cwd (mirrors
|
|
27
|
+
* how the card's project list is rooted).
|
|
28
|
+
* 2. Prefer a candidate matching a scanned git project (carries a branch label).
|
|
29
|
+
* 3. For a bare name, also match a scanned project by basename (covers projects
|
|
30
|
+
* nested deeper than the scan-dir top level).
|
|
31
|
+
* 4. Fall back to any existing directory — lenient like `/cd`, whose trust model
|
|
32
|
+
* is "owner explicitly chose a dir"; the CLI already runs with full FS access.
|
|
33
|
+
* Returns null when nothing resolves to an existing directory.
|
|
34
|
+
*/
|
|
35
|
+
export declare function resolveRepoSelection(repoArg: string, scanDirs: string[]): {
|
|
36
|
+
path: string;
|
|
37
|
+
displayName: string;
|
|
38
|
+
} | null;
|
|
19
39
|
/**
|
|
20
40
|
* Parse a force-topic invocation: `/t [prompt]` or `/topic [prompt]`.
|
|
21
41
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"command-handler.d.ts","sourceRoot":"","sources":["../../src/core/command-handler.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAkD,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAK/G,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,eAAO,MAAM,eAAe,aAAoJ,CAAC;AAEjL;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,aAO/B,CAAC;AAIH,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAMD,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAE9B;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAAE,GACjB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CA0C9C;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAKpF;AAED;;;;mCAImC;AACnC,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,GAAG,sBAAsB,GAAG,IAAI,CAqB1F;AA8DD,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC3C,YAAY,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACzG,cAAc,EAAE,MAAM,MAAM,CAAC;IAC7B,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,gCAAgC,EAAE,WAAW,EAAE,CAAC,CAAC;CACnF;AAmLD,wBAAsB,aAAa,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,WAAW,EACpB,IAAI,EAAE,kBAAkB,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CA6pBf;AAID,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,gBAAgB,EACxB,EAAE,EAAE,aAAa,EACjB,IAAI,EAAE,kBAAkB,EACxB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CA+Bf"}
|
|
@@ -2,18 +2,17 @@
|
|
|
2
2
|
* Command handler — processes /slash commands from users.
|
|
3
3
|
* Extracted from daemon.ts for modularity.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
-
import { join } from 'node:path';
|
|
5
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
6
|
+
import { join, resolve, basename } from 'node:path';
|
|
7
7
|
import { config } from '../config.js';
|
|
8
8
|
import { getBot, getAllBots, getBotOpenId } from '../bot-registry.js';
|
|
9
9
|
import * as sessionStore from '../services/session-store.js';
|
|
10
10
|
import * as scheduleStore from '../services/schedule-store.js';
|
|
11
11
|
import * as scheduler from './scheduler.js';
|
|
12
|
-
import { scanMultipleProjects } from '../services/project-scanner.js';
|
|
12
|
+
import { scanMultipleProjects, describeProjectDir } from '../services/project-scanner.js';
|
|
13
13
|
import { buildRepoSelectCard, buildAdoptSelectCard, buildSessionClosedCard, getCliDisplayName } from '../im/lark/card-builder.js';
|
|
14
14
|
import { createCliAdapterSync } from '../adapters/cli/registry.js';
|
|
15
|
-
import { deleteMessage, listChatBotMembers
|
|
16
|
-
import { claimPairing } from '../services/pairing-store.js';
|
|
15
|
+
import { deleteMessage, listChatBotMembers } from '../im/lark/client.js';
|
|
17
16
|
import { logger } from '../utils/logger.js';
|
|
18
17
|
import { killWorker, forkWorker, forkAdoptWorker, getCurrentCliVersion } from './worker-pool.js';
|
|
19
18
|
import { expandHome, getSessionWorkingDir, getProjectScanDirs, rememberLastCliInput } from './session-manager.js';
|
|
@@ -22,12 +21,11 @@ import { discoverAdoptableSessions, validateAdoptTarget } from './session-discov
|
|
|
22
21
|
import { generateAuthUrl, getTokenStatus } from '../utils/user-token.js';
|
|
23
22
|
import { bindOncall, unbindOncall, getOncallStatus } from '../services/oncall-store.js';
|
|
24
23
|
import { invalidWorkingDirs } from '../utils/working-dir.js';
|
|
25
|
-
import { writeRoleFile, deleteRoleFile
|
|
26
|
-
import { getBotCapability, setBotCapability, clearBotCapability } from '../services/bot-profile-store.js';
|
|
24
|
+
import { resolveRoleFile, writeRoleFile, deleteRoleFile } from './role-resolver.js';
|
|
27
25
|
import { sessionKey, sessionAnchorId } from './types.js';
|
|
28
26
|
import { t, localeForBot } from '../i18n/index.js';
|
|
29
27
|
// ─── Exported constants ──────────────────────────────────────────────────────
|
|
30
|
-
export const DAEMON_COMMANDS = new Set(['/close', '/restart', '/status', '/help', '/cd', '/repo', '/skip', '/schedule', '/role', '/
|
|
28
|
+
export const DAEMON_COMMANDS = new Set(['/close', '/restart', '/status', '/help', '/cd', '/repo', '/skip', '/schedule', '/role', '/login', '/adopt', '/oncall', '/group', '/g']);
|
|
31
29
|
/**
|
|
32
30
|
* Slash commands that are forwarded verbatim to the underlying CLI (e.g.
|
|
33
31
|
* Claude Code's `/compact`, `/model`, `/usage`). The daemon does NOT handle
|
|
@@ -47,6 +45,67 @@ const MULTILINE_COMMANDS = new Set(['/schedule', '/role']);
|
|
|
47
45
|
// `validateWorkingDir` now lives in ./working-dir.js (leaf module the CLI can
|
|
48
46
|
// import without the daemon graph); re-exported here for existing callers.
|
|
49
47
|
export { validateWorkingDir };
|
|
48
|
+
/**
|
|
49
|
+
* Resolve a non-numeric `/repo <arg>` into a concrete repo path + display name.
|
|
50
|
+
* `arg` is either a path (absolute or relative) or a first-level project name
|
|
51
|
+
* under one of the bot's scan dirs — letting the user skip the selection card.
|
|
52
|
+
*
|
|
53
|
+
* Resolution:
|
|
54
|
+
* 1. Build candidate absolute paths — absolute / `~` taken as-is; relative or
|
|
55
|
+
* bare names resolved against each scan dir, then the daemon cwd (mirrors
|
|
56
|
+
* how the card's project list is rooted).
|
|
57
|
+
* 2. Prefer a candidate matching a scanned git project (carries a branch label).
|
|
58
|
+
* 3. For a bare name, also match a scanned project by basename (covers projects
|
|
59
|
+
* nested deeper than the scan-dir top level).
|
|
60
|
+
* 4. Fall back to any existing directory — lenient like `/cd`, whose trust model
|
|
61
|
+
* is "owner explicitly chose a dir"; the CLI already runs with full FS access.
|
|
62
|
+
* Returns null when nothing resolves to an existing directory.
|
|
63
|
+
*/
|
|
64
|
+
export function resolveRepoSelection(repoArg, scanDirs) {
|
|
65
|
+
const existingScanDirs = scanDirs.filter((d) => existsSync(d));
|
|
66
|
+
const projects = existingScanDirs.length > 0 ? scanMultipleProjects(existingScanDirs) : [];
|
|
67
|
+
const isExplicitPath = repoArg.startsWith('/') ||
|
|
68
|
+
repoArg.startsWith('~') ||
|
|
69
|
+
repoArg.startsWith('.') ||
|
|
70
|
+
repoArg.includes('/');
|
|
71
|
+
const candidates = [];
|
|
72
|
+
if (repoArg.startsWith('/') || repoArg.startsWith('~')) {
|
|
73
|
+
candidates.push(resolve(expandHome(repoArg)));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
for (const d of scanDirs)
|
|
77
|
+
candidates.push(resolve(d, repoArg));
|
|
78
|
+
candidates.push(resolve(expandHome(repoArg))); // daemon-cwd fallback (matches /cd)
|
|
79
|
+
}
|
|
80
|
+
// 1) Exact scanned-project match — preferred, gives the "name (branch)" label.
|
|
81
|
+
for (const cand of candidates) {
|
|
82
|
+
const proj = projects.find((p) => resolve(p.path) === cand);
|
|
83
|
+
if (proj)
|
|
84
|
+
return { path: proj.path, displayName: `${proj.name} (${proj.branch})` };
|
|
85
|
+
}
|
|
86
|
+
// 2) Bare name → match a scanned project by basename.
|
|
87
|
+
if (!isExplicitPath) {
|
|
88
|
+
const byName = projects.find((p) => p.name === repoArg);
|
|
89
|
+
if (byName)
|
|
90
|
+
return { path: byName.path, displayName: `${byName.name} (${byName.branch})` };
|
|
91
|
+
}
|
|
92
|
+
// 3) Lenient fallback: any existing directory. Label it with a git ref when
|
|
93
|
+
// it's a repo (covers explicit paths outside the scan roots), else basename.
|
|
94
|
+
for (const cand of candidates) {
|
|
95
|
+
try {
|
|
96
|
+
if (!statSync(cand).isDirectory())
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
continue; // missing / not a dir — try next candidate
|
|
101
|
+
}
|
|
102
|
+
const desc = describeProjectDir(cand);
|
|
103
|
+
return desc
|
|
104
|
+
? { path: cand, displayName: `${desc.name} (${desc.branch})` }
|
|
105
|
+
: { path: cand, displayName: basename(cand) };
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
50
109
|
/**
|
|
51
110
|
* Parse a force-topic invocation: `/t [prompt]` or `/topic [prompt]`.
|
|
52
111
|
*
|
|
@@ -159,69 +218,16 @@ function invalidConfiguredWorkingDirs(ds, larkAppId) {
|
|
|
159
218
|
});
|
|
160
219
|
}
|
|
161
220
|
// ─── Schedule command ────────────────────────────────────────────────────────
|
|
162
|
-
async function handleRoleCommand(args, rootId, chatId, larkAppId,
|
|
221
|
+
async function handleRoleCommand(args, rootId, chatId, larkAppId, deps) {
|
|
163
222
|
const sessionReply = (rid, content, msgType) => deps.sessionReply(rid, content, msgType, larkAppId);
|
|
164
223
|
const trimmed = args.trim();
|
|
165
224
|
const loc = localeForBot(larkAppId);
|
|
166
|
-
|
|
167
|
-
// /role team [...] — manage the team-level (per-bot, cross-chat) role
|
|
168
|
-
const teamMatch = trimmed.match(/^team\b([\s\S]*)$/);
|
|
169
|
-
if (teamMatch) {
|
|
170
|
-
const teamArgs = teamMatch[1].trim();
|
|
171
|
-
const teamSet = teamArgs.match(/^set\s+([\s\S]+)/);
|
|
172
|
-
if (teamSet) {
|
|
173
|
-
const content = teamSet[1].trim();
|
|
174
|
-
if (!content) {
|
|
175
|
-
await sessionReply(rootId, t('role.set_empty', undefined, loc));
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
|
-
writeTeamRoleFile(larkAppId, content);
|
|
179
|
-
await sessionReply(rootId, t('role.team_saved', { bytes: Buffer.byteLength(content, 'utf-8'), max: 4096 }, loc));
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (teamArgs === 'delete' || teamArgs === '删除') {
|
|
183
|
-
await sessionReply(rootId, deleteTeamRoleFile(larkAppId) ? t('role.team_deleted', undefined, loc) : t('role.team_nothing', undefined, loc));
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const content = resolveTeamRoleFile(larkAppId);
|
|
187
|
-
if (content) {
|
|
188
|
-
await sessionReply(rootId, `${t('role.team_current', undefined, loc)}\n\`\`\`markdown\n${content}\n\`\`\`\n${t('role.byte_count', { bytes: Buffer.byteLength(content, 'utf-8'), max: 4096 }, loc)}`);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
await sessionReply(rootId, t('role.team_empty', undefined, loc));
|
|
192
|
-
}
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
// /role cap [...] — manage the short capability label shown in the roster
|
|
196
|
-
const capMatch = trimmed.match(/^cap\b([\s\S]*)$/);
|
|
197
|
-
if (capMatch) {
|
|
198
|
-
const capArgs = capMatch[1].trim();
|
|
199
|
-
const capSet = capArgs.match(/^set\s+([\s\S]+)/);
|
|
200
|
-
if (capSet) {
|
|
201
|
-
const label = capSet[1].trim();
|
|
202
|
-
if (!label) {
|
|
203
|
-
await sessionReply(rootId, t('role.cap_set_empty', undefined, loc));
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
setBotCapability(dataDir, larkAppId, label, senderId);
|
|
207
|
-
await sessionReply(rootId, t('role.cap_saved', { cap: getBotCapability(dataDir, larkAppId) ?? label }, loc));
|
|
208
|
-
return;
|
|
209
|
-
}
|
|
210
|
-
if (capArgs === 'clear' || capArgs === '清除') {
|
|
211
|
-
await sessionReply(rootId, clearBotCapability(dataDir, larkAppId) ? t('role.cap_cleared', undefined, loc) : t('role.cap_empty', undefined, loc));
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
const cap = getBotCapability(dataDir, larkAppId);
|
|
215
|
-
await sessionReply(rootId, cap ? t('role.cap_current', { cap }, loc) : t('role.cap_empty', undefined, loc));
|
|
216
|
-
return;
|
|
217
|
-
}
|
|
218
|
-
// /role → show the EFFECTIVE role + where it comes from (chat override > team > none)
|
|
225
|
+
// /role → show current role
|
|
219
226
|
if (!trimmed) {
|
|
220
|
-
const
|
|
227
|
+
const content = resolveRoleFile(larkAppId, chatId);
|
|
221
228
|
if (content) {
|
|
222
229
|
const len = Buffer.byteLength(content, 'utf-8');
|
|
223
|
-
|
|
224
|
-
await sessionReply(rootId, `${t('role.current', undefined, loc)} ${srcLabel}\n\`\`\`markdown\n${content}\n\`\`\`\n${t('role.byte_count', { bytes: len, max: 4096 }, loc)}`);
|
|
230
|
+
await sessionReply(rootId, `${t('role.current', undefined, loc)}\n\`\`\`markdown\n${content}\n\`\`\`\n${t('role.byte_count', { bytes: len, max: 4096 }, loc)}`);
|
|
225
231
|
}
|
|
226
232
|
else {
|
|
227
233
|
await sessionReply(rootId, t('role.empty', undefined, loc));
|
|
@@ -452,20 +458,11 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
452
458
|
}
|
|
453
459
|
case '/repo': {
|
|
454
460
|
const repoArg = message.content.replace(/^\/repo\s*/, '').trim();
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
break;
|
|
461
|
-
}
|
|
462
|
-
if (repoIndex < 1 || repoIndex > cached.length) {
|
|
463
|
-
await sessionReply(rootId, t('cmd.repo.index_out_of_range', { max: cached.length }, loc));
|
|
464
|
-
break;
|
|
465
|
-
}
|
|
466
|
-
const project = cached[repoIndex - 1];
|
|
467
|
-
const selectedPath = project.path;
|
|
468
|
-
const displayName = `${project.name} (${project.branch})`;
|
|
461
|
+
// Shared commit path for an already-resolved repo: update the session's
|
|
462
|
+
// working dir, then either fork into the pending CLI (first spawn) or
|
|
463
|
+
// close + recreate the session (mid-session switch). Used by both the
|
|
464
|
+
// numeric `/repo <N>` form and the `/repo <path|name>` form.
|
|
465
|
+
const commitRepoSelection = async (selectedPath, displayName, how) => {
|
|
469
466
|
ds.workingDir = selectedPath;
|
|
470
467
|
ds.session.workingDir = selectedPath;
|
|
471
468
|
sessionStore.updateSession(ds.session);
|
|
@@ -503,7 +500,33 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
503
500
|
deleteMessage(ds.larkAppId, ds.repoCardMessageId);
|
|
504
501
|
ds.repoCardMessageId = undefined;
|
|
505
502
|
}
|
|
506
|
-
logger.info(`[${logTag}] Repo selected via
|
|
503
|
+
logger.info(`[${logTag}] Repo selected via ${how}: ${selectedPath}`);
|
|
504
|
+
};
|
|
505
|
+
// Numeric arg → pick by 1-based index from the last scan.
|
|
506
|
+
if (repoArg && ds && /^\d+$/.test(repoArg)) {
|
|
507
|
+
const repoIndex = parseInt(repoArg, 10);
|
|
508
|
+
const cached = lastRepoScan.get(ds.chatId);
|
|
509
|
+
if (!cached || cached.length === 0) {
|
|
510
|
+
await sessionReply(rootId, t('cmd.repo.no_prior_scan', undefined, loc));
|
|
511
|
+
break;
|
|
512
|
+
}
|
|
513
|
+
if (repoIndex < 1 || repoIndex > cached.length) {
|
|
514
|
+
await sessionReply(rootId, t('cmd.repo.index_out_of_range', { max: cached.length }, loc));
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
const project = cached[repoIndex - 1];
|
|
518
|
+
await commitRepoSelection(project.path, `${project.name} (${project.branch})`, `/repo ${repoIndex}`);
|
|
519
|
+
break;
|
|
520
|
+
}
|
|
521
|
+
// Non-numeric arg → a path (relative/absolute) or first-level project
|
|
522
|
+
// name under workingDir; resolve it directly and skip the card.
|
|
523
|
+
if (repoArg && ds) {
|
|
524
|
+
const resolved = resolveRepoSelection(repoArg, getProjectScanDirs(ds));
|
|
525
|
+
if (!resolved) {
|
|
526
|
+
await sessionReply(rootId, t('cmd.repo.path_not_found', { arg: repoArg }, loc));
|
|
527
|
+
break;
|
|
528
|
+
}
|
|
529
|
+
await commitRepoSelection(resolved.path, resolved.displayName, `/repo ${repoArg}`);
|
|
507
530
|
break;
|
|
508
531
|
}
|
|
509
532
|
if (ds?.worker && !ds.worker.killed) {
|
|
@@ -604,35 +627,10 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
604
627
|
break;
|
|
605
628
|
}
|
|
606
629
|
const roleArgs = message.content.replace(/^\/role\s*/, '');
|
|
607
|
-
await handleRoleCommand(roleArgs, rootId, chatId, larkAppId,
|
|
630
|
+
await handleRoleCommand(roleArgs, rootId, chatId, larkAppId, deps);
|
|
608
631
|
logger.info(`[${logTag}] Role command handled`);
|
|
609
632
|
break;
|
|
610
633
|
}
|
|
611
|
-
case '/pair': {
|
|
612
|
-
const code = message.content.replace(/^\/pair\s*/, '').trim();
|
|
613
|
-
if (!larkAppId) {
|
|
614
|
-
await sessionReply(rootId, t('role.no_chat', undefined, loc));
|
|
615
|
-
break;
|
|
616
|
-
}
|
|
617
|
-
if (!code) {
|
|
618
|
-
await sessionReply(rootId, t('pair.usage', undefined, loc));
|
|
619
|
-
break;
|
|
620
|
-
}
|
|
621
|
-
// Resolve the sender's canonical union_id (best-effort) so the web
|
|
622
|
-
// session is keyed stably across apps; degrade to open_id-only.
|
|
623
|
-
const who = await resolveUserUnionId(larkAppId, message.senderId);
|
|
624
|
-
const result = claimPairing(config.session.dataDir, code, { openId: message.senderId, unionId: who.unionId, name: who.name, larkAppId });
|
|
625
|
-
if (result.ok)
|
|
626
|
-
await sessionReply(rootId, t('pair.ok', undefined, loc));
|
|
627
|
-
else if (result.reason === 'expired')
|
|
628
|
-
await sessionReply(rootId, t('pair.expired', undefined, loc));
|
|
629
|
-
else if (result.reason === 'already_claimed')
|
|
630
|
-
await sessionReply(rootId, t('pair.already', undefined, loc));
|
|
631
|
-
else
|
|
632
|
-
await sessionReply(rootId, t('pair.not_found', undefined, loc));
|
|
633
|
-
logger.info(`[${logTag}] Pair command handled: ${result.ok ? 'ok' : result.reason}`);
|
|
634
|
-
break;
|
|
635
|
-
}
|
|
636
634
|
case '/login': {
|
|
637
635
|
const subCmd = message.content.replace(/^\/login\s*/, '').trim();
|
|
638
636
|
if (subCmd === 'status' || subCmd === '状态') {
|
|
@@ -952,6 +950,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
952
950
|
t('help.cd', { cliName }, loc),
|
|
953
951
|
t('help.repo_list', undefined, loc),
|
|
954
952
|
t('help.repo_n', undefined, loc),
|
|
953
|
+
t('help.repo_path', undefined, loc),
|
|
955
954
|
t('help.status', undefined, loc),
|
|
956
955
|
'',
|
|
957
956
|
t('help.heading_passthrough', { cliName }, loc),
|