botmux 2.40.0 → 2.41.0-canary.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 +0 -1
- package/README.md +0 -1
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +2 -22
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +8 -0
- package/dist/cli/bots-list-output.d.ts.map +1 -1
- package/dist/cli/bots-list-output.js +9 -0
- package/dist/cli/bots-list-output.js.map +1 -1
- package/dist/core/command-handler.d.ts +0 -20
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +106 -105
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts +2 -0
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +56 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/role-resolver.d.ts +17 -1
- package/dist/core/role-resolver.d.ts.map +1 -1
- package/dist/core/role-resolver.js +64 -10
- 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 +14 -9
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/trigger-session.d.ts +9 -0
- package/dist/core/trigger-session.d.ts.map +1 -0
- package/dist/core/trigger-session.js +158 -0
- package/dist/core/trigger-session.js.map +1 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +27 -19
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts +3 -0
- package/dist/dashboard/connector-api.d.ts.map +1 -0
- package/dist/dashboard/connector-api.js +362 -0
- package/dist/dashboard/connector-api.js.map +1 -0
- package/dist/dashboard/federation-api.d.ts +35 -0
- package/dist/dashboard/federation-api.d.ts.map +1 -0
- package/dist/dashboard/federation-api.js +256 -0
- package/dist/dashboard/federation-api.js.map +1 -0
- package/dist/dashboard/federation-spoke-api.d.ts +46 -0
- package/dist/dashboard/federation-spoke-api.d.ts.map +1 -0
- package/dist/dashboard/federation-spoke-api.js +400 -0
- package/dist/dashboard/federation-spoke-api.js.map +1 -0
- package/dist/dashboard/pairing-api.d.ts +19 -0
- package/dist/dashboard/pairing-api.d.ts.map +1 -0
- package/dist/dashboard/pairing-api.js +83 -0
- package/dist/dashboard/pairing-api.js.map +1 -0
- package/dist/dashboard/team-group.d.ts +18 -0
- package/dist/dashboard/team-group.d.ts.map +1 -0
- package/dist/dashboard/team-group.js +7 -0
- package/dist/dashboard/team-group.js.map +1 -0
- package/dist/dashboard/team-page.d.ts +8 -0
- package/dist/dashboard/team-page.d.ts.map +1 -0
- package/dist/dashboard/team-page.js +480 -0
- package/dist/dashboard/team-page.js.map +1 -0
- package/dist/dashboard/team-routes.d.ts +30 -0
- package/dist/dashboard/team-routes.d.ts.map +1 -0
- package/dist/dashboard/team-routes.js +458 -0
- package/dist/dashboard/team-routes.js.map +1 -0
- package/dist/dashboard/trigger-api.d.ts +13 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -0
- package/dist/dashboard/trigger-api.js +77 -0
- package/dist/dashboard/trigger-api.js.map +1 -0
- package/dist/dashboard/web/app.js +3 -0
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/team-federation.d.ts +2 -0
- package/dist/dashboard/web/team-federation.d.ts.map +1 -0
- package/dist/dashboard/web/team-federation.js +304 -0
- package/dist/dashboard/web/team-federation.js.map +1 -0
- package/dist/dashboard/webhook-routes.d.ts +19 -0
- package/dist/dashboard/webhook-routes.d.ts.map +1 -0
- package/dist/dashboard/webhook-routes.js +321 -0
- package/dist/dashboard/webhook-routes.js.map +1 -0
- package/dist/dashboard-web/app.js +310 -244
- package/dist/dashboard-web/index.html +1 -0
- package/dist/dashboard.js +155 -1
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +22 -7
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +22 -7
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/client.d.ts +21 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +86 -18
- package/dist/im/lark/client.js.map +1 -1
- package/dist/services/bot-owner-store.d.ts +28 -0
- package/dist/services/bot-owner-store.d.ts.map +1 -0
- package/dist/services/bot-owner-store.js +82 -0
- package/dist/services/bot-owner-store.js.map +1 -0
- package/dist/services/bot-profile-store.d.ts +16 -0
- package/dist/services/bot-profile-store.d.ts.map +1 -0
- package/dist/services/bot-profile-store.js +98 -0
- package/dist/services/bot-profile-store.js.map +1 -0
- package/dist/services/connector-store.d.ts +63 -0
- package/dist/services/connector-store.d.ts.map +1 -0
- package/dist/services/connector-store.js +79 -0
- package/dist/services/connector-store.js.map +1 -0
- package/dist/services/deployment-identity.d.ts +9 -0
- package/dist/services/deployment-identity.d.ts.map +1 -0
- package/dist/services/deployment-identity.js +47 -0
- package/dist/services/deployment-identity.js.map +1 -0
- package/dist/services/federation-membership-store.d.ts +23 -0
- package/dist/services/federation-membership-store.d.ts.map +1 -0
- package/dist/services/federation-membership-store.js +66 -0
- package/dist/services/federation-membership-store.js.map +1 -0
- package/dist/services/federation-roster.d.ts +54 -0
- package/dist/services/federation-roster.d.ts.map +1 -0
- package/dist/services/federation-roster.js +51 -0
- package/dist/services/federation-roster.js.map +1 -0
- package/dist/services/federation-store.d.ts +65 -0
- package/dist/services/federation-store.d.ts.map +1 -0
- package/dist/services/federation-store.js +125 -0
- package/dist/services/federation-store.js.map +1 -0
- package/dist/services/group-creator.d.ts +6 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +10 -1
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +11 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +27 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/invite-store.d.ts +28 -0
- package/dist/services/invite-store.d.ts.map +1 -0
- package/dist/services/invite-store.js +85 -0
- package/dist/services/invite-store.js.map +1 -0
- package/dist/services/pairing-store.d.ts +47 -0
- package/dist/services/pairing-store.d.ts.map +1 -0
- package/dist/services/pairing-store.js +132 -0
- package/dist/services/pairing-store.js.map +1 -0
- package/dist/services/project-scanner.d.ts +0 -10
- package/dist/services/project-scanner.d.ts.map +1 -1
- package/dist/services/project-scanner.js +0 -11
- package/dist/services/project-scanner.js.map +1 -1
- package/dist/services/team-roster.d.ts +38 -0
- package/dist/services/team-roster.d.ts.map +1 -0
- package/dist/services/team-roster.js +82 -0
- package/dist/services/team-roster.js.map +1 -0
- package/dist/services/team-store.d.ts +54 -0
- package/dist/services/team-store.d.ts.map +1 -0
- package/dist/services/team-store.js +156 -0
- package/dist/services/team-store.js.map +1 -0
- package/dist/services/trigger-log-store.d.ts +46 -0
- package/dist/services/trigger-log-store.d.ts.map +1 -0
- package/dist/services/trigger-log-store.js +132 -0
- package/dist/services/trigger-log-store.js.map +1 -0
- package/dist/services/trigger-types.d.ts +57 -0
- package/dist/services/trigger-types.d.ts.map +1 -0
- package/dist/services/trigger-types.js +28 -0
- package/dist/services/trigger-types.js.map +1 -0
- package/dist/services/web-session-store.d.ts +28 -0
- package/dist/services/web-session-store.d.ts.map +1 -0
- package/dist/services/web-session-store.js +84 -0
- package/dist/services/web-session-store.js.map +1 -0
- package/dist/services/webhook-key.d.ts +16 -0
- package/dist/services/webhook-key.d.ts.map +1 -0
- package/dist/services/webhook-key.js +123 -0
- package/dist/services/webhook-key.js.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts +15 -0
- package/dist/services/webhook-lifecycle-extractors.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-extractors.js +59 -0
- package/dist/services/webhook-lifecycle-extractors.js.map +1 -0
- package/dist/services/webhook-lifecycle-store.d.ts +45 -0
- package/dist/services/webhook-lifecycle-store.d.ts.map +1 -0
- package/dist/services/webhook-lifecycle-store.js +159 -0
- package/dist/services/webhook-lifecycle-store.js.map +1 -0
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +64 -10
- 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/dist/workflows/trigger-from-envelope.d.ts +13 -0
- package/dist/workflows/trigger-from-envelope.d.ts.map +1 -0
- package/dist/workflows/trigger-from-envelope.js +67 -0
- package/dist/workflows/trigger-from-envelope.js.map +1 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -308,7 +308,6 @@ 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 |
|
|
312
311
|
| `/skip` | Skip the repo selector card, start the session in the default dir |
|
|
313
312
|
| `/cd <path>` | Change working directory and restart the CLI process |
|
|
314
313
|
| `/status` | Show session info (uptime, terminal URL, etc.) |
|
package/README.md
CHANGED
|
@@ -302,7 +302,6 @@ botmux autostart enable
|
|
|
302
302
|
|------|------|
|
|
303
303
|
| `/repo` | 显示项目选择卡片(交互式下拉 + 文本列表) |
|
|
304
304
|
| `/repo <N>` | 切换到上次扫描的第 N 个项目 |
|
|
305
|
-
| `/repo <路径\|项目名>` | 跳过选择卡片,直接指定路径(相对/绝对)或 workingDir 下的一级项目名 |
|
|
306
305
|
| `/skip` | 跳过仓库选择卡片,直接用默认目录开启会话 |
|
|
307
306
|
| `/cd <路径>` | 切换工作目录并重启 CLI 进程 |
|
|
308
307
|
| `/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;AAoKD;;;;;;;GAOG;AACH,wBAAgB,WAAW,CACzB,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,iBAAiB,EAC9B,WAAW,EAAE,MAAM,GAClB,IAAI,CAoBN"}
|
|
@@ -40,29 +40,9 @@ function writeIfChanged(filePath, content) {
|
|
|
40
40
|
throw new Error(`写入 ${filePath} 失败:${err.message}`);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
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
|
-
*/
|
|
43
|
+
/** 判断某个 hook group 是否是 botmux ask hook(用于幂等替换)。 */
|
|
61
44
|
function isBotmuxAskHookGroup(group, hookCommand) {
|
|
62
|
-
|
|
63
|
-
return group.hooks.some((e) => e.type === 'command' &&
|
|
64
|
-
(e.command === hookCommand ||
|
|
65
|
-
(e.command.includes('cli.js') && e.command.trimEnd().endsWith(suffix))));
|
|
45
|
+
return group.hooks.some((e) => e.type === 'command' && e.command === hookCommand);
|
|
66
46
|
}
|
|
67
47
|
function removeBotmuxAskHookGroups(hooks, eventName, hookCommand) {
|
|
68
48
|
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,6 +15,14 @@ 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';
|
|
18
26
|
};
|
|
19
27
|
export declare function formatChatBotsForCli(chatBots: ChatBotMember[], currentLarkAppId: string): BotListOutputEntry[];
|
|
20
28
|
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;IAC3B,2EAA2E;IAC3E,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,yDAAyD;IACzD,WAAW,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,WAAW,EAAE,OAAO,CAAC;IACrB,6CAA6C;IAC7C,aAAa,EAAE,WAAW,GAAG,MAAM,GAAG,UAAU,GAAG,UAAU,CAAC;CAC/D,CAAC;AAEF,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,aAAa,EAAE,EACzB,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,EAAE,CAatB;AAED,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,mBAAmB,EAAE,EACjC,gBAAgB,EAAE,MAAM,GACvB,kBAAkB,EAAE,CAgBtB"}
|
|
@@ -6,6 +6,10 @@ 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,
|
|
9
13
|
}));
|
|
10
14
|
}
|
|
11
15
|
export function formatBotInfoEntriesForCli(botEntries, currentLarkAppId) {
|
|
@@ -18,6 +22,11 @@ export function formatBotInfoEntriesForCli(botEntries, currentLarkAppId) {
|
|
|
18
22
|
source: 'configured',
|
|
19
23
|
larkAppId: b.larkAppId,
|
|
20
24
|
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'),
|
|
21
30
|
}));
|
|
22
31
|
}
|
|
23
32
|
//# 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":"AA6BA,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;QACjC,UAAU,EAAE,EAAE,CAAC,UAAU,IAAI,IAAI;QACjC,WAAW,EAAE,EAAE,CAAC,WAAW;QAC3B,WAAW,EAAE,EAAE,CAAC,WAAW;QAC3B,aAAa,EAAE,EAAE,CAAC,aAAa;KAChC,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;QACxB,wEAAwE;QACxE,UAAU,EAAE,IAAI;QAChB,WAAW,EAAE,KAAK;QAClB,WAAW,EAAE,CAAC,CAAC,SAAS,KAAK,gBAAgB;QAC7C,aAAa,EAAE,CAAC,CAAC,CAAC,SAAS,KAAK,gBAAgB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAwB;KAC/F,CAAC,CAAC,CAAC;AACR,CAAC"}
|
|
@@ -16,26 +16,6 @@ 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;
|
|
39
19
|
/**
|
|
40
20
|
* Parse a force-topic invocation: `/t [prompt]` or `/topic [prompt]`.
|
|
41
21
|
*
|
|
@@ -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":"AAmBA,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,EAAkD,KAAK,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAM/G,OAAO,KAAK,EAAE,WAAW,EAAkB,MAAM,aAAa,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAKhD,eAAO,MAAM,eAAe,aAA6J,CAAC;AAE1L;;;;;;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;;;;;;;;;;;;;;;;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;AAoOD,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,CAypBf;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,17 +2,18 @@
|
|
|
2
2
|
* Command handler — processes /slash commands from users.
|
|
3
3
|
* Extracted from daemon.ts for modularity.
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync
|
|
6
|
-
import { join
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } 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
|
|
12
|
+
import { scanMultipleProjects } 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 } from '../im/lark/client.js';
|
|
15
|
+
import { deleteMessage, listChatBotMembers, resolveUserUnionId } from '../im/lark/client.js';
|
|
16
|
+
import { claimPairing } from '../services/pairing-store.js';
|
|
16
17
|
import { logger } from '../utils/logger.js';
|
|
17
18
|
import { killWorker, forkWorker, forkAdoptWorker, getCurrentCliVersion } from './worker-pool.js';
|
|
18
19
|
import { expandHome, getSessionWorkingDir, getProjectScanDirs, rememberLastCliInput } from './session-manager.js';
|
|
@@ -21,11 +22,12 @@ import { discoverAdoptableSessions, validateAdoptTarget } from './session-discov
|
|
|
21
22
|
import { generateAuthUrl, getTokenStatus } from '../utils/user-token.js';
|
|
22
23
|
import { bindOncall, unbindOncall, getOncallStatus } from '../services/oncall-store.js';
|
|
23
24
|
import { invalidWorkingDirs } from '../utils/working-dir.js';
|
|
24
|
-
import {
|
|
25
|
+
import { writeRoleFile, deleteRoleFile, resolveRole, resolveTeamRoleFile, writeTeamRoleFile, deleteTeamRoleFile } from './role-resolver.js';
|
|
26
|
+
import { getBotCapability, setBotCapability, clearBotCapability } from '../services/bot-profile-store.js';
|
|
25
27
|
import { sessionKey, sessionAnchorId } from './types.js';
|
|
26
28
|
import { t, localeForBot } from '../i18n/index.js';
|
|
27
29
|
// ─── Exported constants ──────────────────────────────────────────────────────
|
|
28
|
-
export const DAEMON_COMMANDS = new Set(['/close', '/restart', '/status', '/help', '/cd', '/repo', '/skip', '/schedule', '/role', '/login', '/adopt', '/oncall', '/group', '/g']);
|
|
30
|
+
export const DAEMON_COMMANDS = new Set(['/close', '/restart', '/status', '/help', '/cd', '/repo', '/skip', '/schedule', '/role', '/pair', '/login', '/adopt', '/oncall', '/group', '/g']);
|
|
29
31
|
/**
|
|
30
32
|
* Slash commands that are forwarded verbatim to the underlying CLI (e.g.
|
|
31
33
|
* Claude Code's `/compact`, `/model`, `/usage`). The daemon does NOT handle
|
|
@@ -45,67 +47,6 @@ const MULTILINE_COMMANDS = new Set(['/schedule', '/role']);
|
|
|
45
47
|
// `validateWorkingDir` now lives in ./working-dir.js (leaf module the CLI can
|
|
46
48
|
// import without the daemon graph); re-exported here for existing callers.
|
|
47
49
|
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
|
-
}
|
|
109
50
|
/**
|
|
110
51
|
* Parse a force-topic invocation: `/t [prompt]` or `/topic [prompt]`.
|
|
111
52
|
*
|
|
@@ -218,16 +159,69 @@ function invalidConfiguredWorkingDirs(ds, larkAppId) {
|
|
|
218
159
|
});
|
|
219
160
|
}
|
|
220
161
|
// ─── Schedule command ────────────────────────────────────────────────────────
|
|
221
|
-
async function handleRoleCommand(args, rootId, chatId, larkAppId, deps) {
|
|
162
|
+
async function handleRoleCommand(args, rootId, chatId, larkAppId, senderId, deps) {
|
|
222
163
|
const sessionReply = (rid, content, msgType) => deps.sessionReply(rid, content, msgType, larkAppId);
|
|
223
164
|
const trimmed = args.trim();
|
|
224
165
|
const loc = localeForBot(larkAppId);
|
|
225
|
-
|
|
166
|
+
const dataDir = config.session.dataDir;
|
|
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)
|
|
226
219
|
if (!trimmed) {
|
|
227
|
-
const content =
|
|
220
|
+
const { content, source } = resolveRole(larkAppId, chatId);
|
|
228
221
|
if (content) {
|
|
229
222
|
const len = Buffer.byteLength(content, 'utf-8');
|
|
230
|
-
|
|
223
|
+
const srcLabel = source === 'chat' ? t('role.src_chat', undefined, loc) : t('role.src_team', undefined, loc);
|
|
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)}`);
|
|
231
225
|
}
|
|
232
226
|
else {
|
|
233
227
|
await sessionReply(rootId, t('role.empty', undefined, loc));
|
|
@@ -458,11 +452,20 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
458
452
|
}
|
|
459
453
|
case '/repo': {
|
|
460
454
|
const repoArg = message.content.replace(/^\/repo\s*/, '').trim();
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
455
|
+
const repoIndex = repoArg ? parseInt(repoArg, 10) : NaN;
|
|
456
|
+
if (!isNaN(repoIndex) && ds) {
|
|
457
|
+
const cached = lastRepoScan.get(ds.chatId);
|
|
458
|
+
if (!cached || cached.length === 0) {
|
|
459
|
+
await sessionReply(rootId, t('cmd.repo.no_prior_scan', undefined, loc));
|
|
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})`;
|
|
466
469
|
ds.workingDir = selectedPath;
|
|
467
470
|
ds.session.workingDir = selectedPath;
|
|
468
471
|
sessionStore.updateSession(ds.session);
|
|
@@ -500,33 +503,7 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
500
503
|
deleteMessage(ds.larkAppId, ds.repoCardMessageId);
|
|
501
504
|
ds.repoCardMessageId = undefined;
|
|
502
505
|
}
|
|
503
|
-
logger.info(`[${logTag}] Repo selected via ${
|
|
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}`);
|
|
506
|
+
logger.info(`[${logTag}] Repo selected via /repo ${repoIndex}: ${selectedPath}`);
|
|
530
507
|
break;
|
|
531
508
|
}
|
|
532
509
|
if (ds?.worker && !ds.worker.killed) {
|
|
@@ -627,10 +604,35 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
627
604
|
break;
|
|
628
605
|
}
|
|
629
606
|
const roleArgs = message.content.replace(/^\/role\s*/, '');
|
|
630
|
-
await handleRoleCommand(roleArgs, rootId, chatId, larkAppId, deps);
|
|
607
|
+
await handleRoleCommand(roleArgs, rootId, chatId, larkAppId, message.senderId, deps);
|
|
631
608
|
logger.info(`[${logTag}] Role command handled`);
|
|
632
609
|
break;
|
|
633
610
|
}
|
|
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
|
+
}
|
|
634
636
|
case '/login': {
|
|
635
637
|
const subCmd = message.content.replace(/^\/login\s*/, '').trim();
|
|
636
638
|
if (subCmd === 'status' || subCmd === '状态') {
|
|
@@ -950,7 +952,6 @@ export async function handleCommand(cmd, rootId, message, deps, larkAppId) {
|
|
|
950
952
|
t('help.cd', { cliName }, loc),
|
|
951
953
|
t('help.repo_list', undefined, loc),
|
|
952
954
|
t('help.repo_n', undefined, loc),
|
|
953
|
-
t('help.repo_path', undefined, loc),
|
|
954
955
|
t('help.status', undefined, loc),
|
|
955
956
|
'',
|
|
956
957
|
t('help.heading_passthrough', { cliName }, loc),
|