botmux 2.86.0 → 2.88.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 +27 -0
- package/README.md +27 -0
- package/dist/adapters/backend/herdr-backend.d.ts.map +1 -1
- package/dist/adapters/backend/herdr-backend.js +12 -1
- package/dist/adapters/backend/herdr-backend.js.map +1 -1
- package/dist/adapters/backend/liveness-gate.d.ts +38 -0
- package/dist/adapters/backend/liveness-gate.d.ts.map +1 -0
- package/dist/adapters/backend/liveness-gate.js +53 -0
- package/dist/adapters/backend/liveness-gate.js.map +1 -0
- package/dist/adapters/backend/pty-backend.d.ts.map +1 -1
- package/dist/adapters/backend/pty-backend.js +4 -1
- package/dist/adapters/backend/pty-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-backend.d.ts +22 -1
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +124 -36
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.d.ts +20 -0
- package/dist/adapters/backend/tmux-pipe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +86 -20
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +9 -0
- package/dist/adapters/backend/types.d.ts.map +1 -1
- package/dist/adapters/backend/types.js.map +1 -1
- package/dist/adapters/backend/zellij-backend.js +1 -1
- package/dist/adapters/backend/zellij-backend.js.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.d.ts +13 -0
- package/dist/adapters/backend/zellij-observe-backend.d.ts.map +1 -1
- package/dist/adapters/backend/zellij-observe-backend.js +51 -12
- package/dist/adapters/backend/zellij-observe-backend.js.map +1 -1
- package/dist/adapters/cli/codex.d.ts.map +1 -1
- package/dist/adapters/cli/codex.js +8 -5
- package/dist/adapters/cli/codex.js.map +1 -1
- package/dist/adapters/cli/shared-hints.d.ts +2 -1
- package/dist/adapters/cli/shared-hints.d.ts.map +1 -1
- package/dist/adapters/cli/shared-hints.js +25 -3
- package/dist/adapters/cli/shared-hints.js.map +1 -1
- package/dist/adapters/cli/traex.d.ts.map +1 -1
- package/dist/adapters/cli/traex.js +4 -0
- package/dist/adapters/cli/traex.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +6 -0
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/bot-registry.d.ts +23 -0
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +9 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +368 -16
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +7 -5
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
- package/dist/core/dashboard-ipc-server.js +210 -5
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +3 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +4 -1
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/maintenance.d.ts +18 -0
- package/dist/core/maintenance.d.ts.map +1 -1
- package/dist/core/maintenance.js +58 -30
- package/dist/core/maintenance.js.map +1 -1
- package/dist/core/per-bot-env.d.ts +38 -0
- package/dist/core/per-bot-env.d.ts.map +1 -0
- package/dist/core/per-bot-env.js +79 -0
- package/dist/core/per-bot-env.js.map +1 -0
- package/dist/core/session-activity.d.ts +1 -0
- package/dist/core/session-activity.d.ts.map +1 -1
- package/dist/core/session-activity.js +7 -4
- package/dist/core/session-activity.js.map +1 -1
- package/dist/core/session-create.d.ts +62 -0
- package/dist/core/session-create.d.ts.map +1 -0
- package/dist/core/session-create.js +131 -0
- package/dist/core/session-create.js.map +1 -0
- package/dist/core/session-manager.d.ts +43 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +306 -8
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/skills/discovery.d.ts +7 -0
- package/dist/core/skills/discovery.d.ts.map +1 -1
- package/dist/core/skills/discovery.js +51 -0
- package/dist/core/skills/discovery.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +5 -2
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/update-check.d.ts +67 -0
- package/dist/core/update-check.d.ts.map +1 -0
- package/dist/core/update-check.js +159 -0
- package/dist/core/update-check.js.map +1 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +82 -4
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +40 -11
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +2 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +2 -0
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/session-cleanup.d.ts +30 -0
- package/dist/dashboard/session-cleanup.d.ts.map +1 -0
- package/dist/dashboard/session-cleanup.js +47 -0
- package/dist/dashboard/session-cleanup.js.map +1 -0
- package/dist/dashboard/skill-install-request.d.ts +10 -0
- package/dist/dashboard/skill-install-request.d.ts.map +1 -1
- package/dist/dashboard/skill-install-request.js +17 -0
- package/dist/dashboard/skill-install-request.js.map +1 -1
- package/dist/dashboard/web/app.d.ts.map +1 -1
- package/dist/dashboard/web/app.js +29 -1
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +82 -6
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +312 -2
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/insights.d.ts.map +1 -1
- package/dist/dashboard/web/insights.js +355 -30
- package/dist/dashboard/web/insights.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts +2 -0
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +457 -30
- package/dist/dashboard/web/sessions.js.map +1 -1
- package/dist/dashboard/web/settings.d.ts.map +1 -1
- package/dist/dashboard/web/settings.js +289 -0
- package/dist/dashboard/web/settings.js.map +1 -1
- package/dist/dashboard/web/skills.d.ts.map +1 -1
- package/dist/dashboard/web/skills.js +183 -10
- package/dist/dashboard/web/skills.js.map +1 -1
- package/dist/dashboard/web/whiteboards.d.ts +2 -0
- package/dist/dashboard/web/whiteboards.d.ts.map +1 -0
- package/dist/dashboard/web/whiteboards.js +267 -0
- package/dist/dashboard/web/whiteboards.js.map +1 -0
- package/dist/dashboard-web/app.js +879 -591
- package/dist/dashboard-web/index.html +1 -0
- package/dist/dashboard-web/style.css +528 -8
- package/dist/dashboard.js +472 -5
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +6 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +12 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +21 -1
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +21 -1
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts +4 -5
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +113 -11
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +3 -2
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +174 -14
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +19 -5
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-pending.d.ts.map +1 -1
- package/dist/im/lark/grant-pending.js +9 -2
- package/dist/im/lark/grant-pending.js.map +1 -1
- package/dist/index-daemon.js +5 -0
- package/dist/index-daemon.js.map +1 -1
- package/dist/platform/bind.d.ts +2 -0
- package/dist/platform/bind.d.ts.map +1 -0
- package/dist/platform/bind.js +91 -0
- package/dist/platform/bind.js.map +1 -0
- package/dist/platform/binding.d.ts +22 -0
- package/dist/platform/binding.d.ts.map +1 -0
- package/dist/platform/binding.js +33 -0
- package/dist/platform/binding.js.map +1 -0
- package/dist/platform/tunnel-client.d.ts +15 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -0
- package/dist/platform/tunnel-client.js +193 -0
- package/dist/platform/tunnel-client.js.map +1 -0
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +20 -0
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/git-worktree.d.ts +10 -0
- package/dist/services/git-worktree.d.ts.map +1 -1
- package/dist/services/git-worktree.js +71 -26
- package/dist/services/git-worktree.js.map +1 -1
- package/dist/services/grant-prefs-store.d.ts +3 -0
- package/dist/services/grant-prefs-store.d.ts.map +1 -1
- package/dist/services/grant-prefs-store.js +17 -2
- package/dist/services/grant-prefs-store.js.map +1 -1
- package/dist/services/insight/claude-span-reader.d.ts.map +1 -1
- package/dist/services/insight/claude-span-reader.js +18 -2
- package/dist/services/insight/claude-span-reader.js.map +1 -1
- package/dist/services/insight/codex-span-reader.d.ts.map +1 -1
- package/dist/services/insight/codex-span-reader.js +17 -1
- package/dist/services/insight/codex-span-reader.js.map +1 -1
- package/dist/services/insight/report.d.ts.map +1 -1
- package/dist/services/insight/report.js +48 -6
- package/dist/services/insight/report.js.map +1 -1
- package/dist/services/insight/types.d.ts +9 -0
- package/dist/services/insight/types.d.ts.map +1 -1
- package/dist/services/insight/types.js.map +1 -1
- package/dist/services/resumable-session-discovery.d.ts.map +1 -1
- package/dist/services/resumable-session-discovery.js +21 -4
- package/dist/services/resumable-session-discovery.js.map +1 -1
- package/dist/services/skill-registry-store.d.ts +1 -0
- package/dist/services/skill-registry-store.d.ts.map +1 -1
- package/dist/services/skill-registry-store.js +32 -0
- package/dist/services/skill-registry-store.js.map +1 -1
- package/dist/services/whiteboard-store.d.ts +67 -0
- package/dist/services/whiteboard-store.d.ts.map +1 -0
- package/dist/services/whiteboard-store.js +450 -0
- package/dist/services/whiteboard-store.js.map +1 -0
- package/dist/services/worktree-slug-ai.d.ts.map +1 -1
- package/dist/services/worktree-slug-ai.js +3 -1
- package/dist/services/worktree-slug-ai.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +8 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +9 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +118 -0
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +15 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/crash-log.d.ts +22 -0
- package/dist/utils/crash-log.d.ts.map +1 -0
- package/dist/utils/crash-log.js +37 -0
- package/dist/utils/crash-log.js.map +1 -0
- package/dist/utils/input-gate.d.ts +10 -0
- package/dist/utils/input-gate.d.ts.map +1 -1
- package/dist/utils/input-gate.js +7 -0
- package/dist/utils/input-gate.js.map +1 -1
- package/dist/utils/install-diagnostics.d.ts +52 -0
- package/dist/utils/install-diagnostics.d.ts.map +1 -0
- package/dist/utils/install-diagnostics.js +155 -0
- package/dist/utils/install-diagnostics.js.map +1 -0
- package/dist/utils/install-info.d.ts +4 -0
- package/dist/utils/install-info.d.ts.map +1 -1
- package/dist/utils/install-info.js +6 -0
- package/dist/utils/install-info.js.map +1 -1
- package/dist/utils/stdio-epipe-guard.d.ts +39 -0
- package/dist/utils/stdio-epipe-guard.d.ts.map +1 -0
- package/dist/utils/stdio-epipe-guard.js +67 -0
- package/dist/utils/stdio-epipe-guard.js.map +1 -0
- package/dist/worker.js +222 -26
- package/dist/worker.js.map +1 -1
- package/dist/workflows/daemon-spawn.d.ts +2 -0
- package/dist/workflows/daemon-spawn.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-ipc-server.d.ts","sourceRoot":"","sources":["../../src/core/dashboard-ipc-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAe,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard-ipc-server.d.ts","sourceRoot":"","sources":["../../src/core/dashboard-ipc-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAe,MAAM,WAAW,CAAC;AAsDjG,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAM/E,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,aAAa,CAAC,GAAG,IAAI,CAE3F;AACD,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EAIpB,KAAK,UAAU,EAChB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAiC,cAAc,EAAW,MAAM,aAAa,CAAC;AAK1F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC5B;AAED,MAAM,MAAM,OAAO,GAAG,CACpB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC3B,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAW1B,0FAA0F;AAC1F,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAM7E;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAGhF;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,CAAC,CAAC,CAKhF;AAiBD,sEAAsE;AACtE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAgC;AAyB7F,YAAY,EAAE,UAAU,EAAE,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,CAAC;AAMtD,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAA0B;AAKxE,wBAAgB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAA0B;AAkpBxE,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC;IACrD,OAAO,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC;IAC3C,cAAc,EAAE,MAAM,CAAC;CACxB;AAozBD,wBAAgB,cAAc,CAAC,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,eAAe,CAAC,CAiC7F"}
|
|
@@ -28,11 +28,12 @@ import { readGlobalConfig } from '../global-config.js';
|
|
|
28
28
|
import { normalizeChatReplyMode } from '../services/chat-reply-mode-store.js';
|
|
29
29
|
import * as chatFirstSeenStore from '../services/chat-first-seen-store.js';
|
|
30
30
|
import * as scheduler from './scheduler.js';
|
|
31
|
-
import { listActiveSessions, findActiveBySessionId, closeSession, getActiveSessionsRegistry, transferSession, deliverWriteLinkCardToOwners } from './worker-pool.js';
|
|
31
|
+
import { listActiveSessions, findActiveBySessionId, closeSession, getActiveSessionsRegistry, transferSession, deliverWriteLinkCardToOwners, forkWorker } from './worker-pool.js';
|
|
32
32
|
import { listOnlineDaemons } from '../utils/daemon-discovery.js';
|
|
33
33
|
import { getChatMode, replyMessage, sendMessage, resolveUnionIdFromOpenId, listThreadMessages, listChatMessages, getUserProfile } from '../im/lark/client.js';
|
|
34
34
|
import { parseApiMessage, cardContentHasUpgradeFallback, resolveMergedCardContent } from '../im/lark/message-parser.js';
|
|
35
|
-
import { resumeSession } from './session-manager.js';
|
|
35
|
+
import { resumeSession, spawnDashboardSession, activateQueuedSession } from './session-manager.js';
|
|
36
|
+
import { parseSpawnRequest } from './session-create.js';
|
|
36
37
|
import { getCliDisplayName } from '../im/lark/card-builder.js';
|
|
37
38
|
import { locateLimiter } from './dashboard-locate.js';
|
|
38
39
|
import { buildTerminalUrl } from './terminal-url.js';
|
|
@@ -146,6 +147,63 @@ ipcRoute('POST', '/api/sessions/:sessionId/close', async (_req, res, params) =>
|
|
|
146
147
|
const r = await closeSession(params.sessionId);
|
|
147
148
|
jsonRes(res, 200, r);
|
|
148
149
|
});
|
|
150
|
+
/** Post a scope-aware "restarting" notice into the session's Lark thread/chat,
|
|
151
|
+
* mirroring the /resume route — so a Feishu-side observer sees why the CLI just
|
|
152
|
+
* restarted under them (the IM `/restart` command and the card button notify
|
|
153
|
+
* too; the dashboard was the lone silent path). `fresh` = the worker was gone
|
|
154
|
+
* and we re-forked it (revive) rather than doing an in-place CLI restart.
|
|
155
|
+
* Best-effort and fire-and-forget; never blocks the HTTP response. */
|
|
156
|
+
function postRestartNotice(ds, fresh) {
|
|
157
|
+
if (!ds.larkAppId)
|
|
158
|
+
return;
|
|
159
|
+
const loc = localeForBot(ds.larkAppId);
|
|
160
|
+
const cliName = getCliDisplayName(ds.session.cliId ?? 'claude-code');
|
|
161
|
+
const text = fresh
|
|
162
|
+
? t('card.action.restarted_fresh', { cliName }, loc)
|
|
163
|
+
: t('cmd.restart.in_progress', { cliName }, loc);
|
|
164
|
+
const notice = JSON.stringify({ text });
|
|
165
|
+
if (ds.scope === 'chat' && ds.chatId) {
|
|
166
|
+
getChatMode(ds.larkAppId, ds.chatId, { forceRefresh: true })
|
|
167
|
+
.then((mode) => mode === 'topic' && ds.session.rootMessageId
|
|
168
|
+
? replyMessage(ds.larkAppId, ds.session.rootMessageId, notice, 'text', true)
|
|
169
|
+
: sendMessage(ds.larkAppId, ds.chatId, notice, 'text'))
|
|
170
|
+
.catch(err => logger.debug(`[restart] failed to post chat-scope restart notice: ${err}`));
|
|
171
|
+
}
|
|
172
|
+
else if (ds.session.rootMessageId) {
|
|
173
|
+
replyMessage(ds.larkAppId, ds.session.rootMessageId, notice, 'text', true)
|
|
174
|
+
.catch(err => logger.debug(`[restart] failed to post thread-scope restart notice: ${err}`));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
ipcRoute('POST', '/api/sessions/:sessionId/restart', (_req, res, params) => {
|
|
178
|
+
const ds = findActiveBySessionId(params.sessionId);
|
|
179
|
+
if (!ds)
|
|
180
|
+
return jsonRes(res, 404, { ok: false, error: 'session_not_active' });
|
|
181
|
+
// Adopt/observed sessions: botmux never owned the CLI — restarting would kill
|
|
182
|
+
// the user's real tmux/zellij pane. Hard-reject (the worker self-guards too).
|
|
183
|
+
if (ds.adoptedFrom || ds.initConfig?.adoptMode) {
|
|
184
|
+
return jsonRes(res, 409, { ok: false, error: 'adopt_restart_unsupported' });
|
|
185
|
+
}
|
|
186
|
+
const cliId = ds.session.cliId ?? 'unknown';
|
|
187
|
+
if (ds.worker && !ds.worker.killed) {
|
|
188
|
+
// Live worker → in-place CLI restart (kills the CLI, respawns with --resume).
|
|
189
|
+
try {
|
|
190
|
+
ds.worker.send({ type: 'restart' });
|
|
191
|
+
}
|
|
192
|
+
catch (err) {
|
|
193
|
+
return jsonRes(res, 502, { ok: false, error: String(err) });
|
|
194
|
+
}
|
|
195
|
+
postRestartNotice(ds, false);
|
|
196
|
+
return jsonRes(res, 200, { ok: true, sessionId: params.sessionId, cliId, revived: false });
|
|
197
|
+
}
|
|
198
|
+
// Worker is gone but the session is still active — idle-suspended (over the
|
|
199
|
+
// per-bot cap), lazy-restored after a daemon restart, or crash-loop-stopped.
|
|
200
|
+
// Revive it the same way the Feishu card restart does (forkWorker), so the
|
|
201
|
+
// dashboard isn't a dead-end: a 409 here would leave NO working control to
|
|
202
|
+
// bring the CLI back (the resume button only shows for closed sessions).
|
|
203
|
+
forkWorker(ds, '', ds.hasHistory);
|
|
204
|
+
postRestartNotice(ds, true);
|
|
205
|
+
jsonRes(res, 200, { ok: true, sessionId: params.sessionId, cliId, revived: true });
|
|
206
|
+
});
|
|
149
207
|
/** 解析 session(活跃优先,已关闭兜底)。活跃会话取 ds.session —— registry 与
|
|
150
208
|
* store 持有同一对象,改字段后 updateSession 即落盘。 */
|
|
151
209
|
function findSessionRecord(sessionId) {
|
|
@@ -168,8 +226,15 @@ ipcRoute('POST', '/api/sessions/:sessionId/board', async (req, res, params) => {
|
|
|
168
226
|
const session = findSessionRecord(params.sessionId);
|
|
169
227
|
if (!session)
|
|
170
228
|
return jsonRes(res, 404, { ok: false, error: 'session_not_found' });
|
|
171
|
-
|
|
229
|
+
// 待办池(queued)会话被拖到「进行中」= 激活:把暂存内容当首轮发给 CLI 开跑。
|
|
230
|
+
// activateQueuedSession 内部会清 queued + 把列设成 in_progress + forkWorker。
|
|
231
|
+
const activeDs = findActiveBySessionId(params.sessionId);
|
|
232
|
+
if (column === 'in_progress' && activeDs?.session.queued) {
|
|
233
|
+
await activateQueuedSession(activeDs);
|
|
234
|
+
}
|
|
235
|
+
else if (column) {
|
|
172
236
|
session.kanbanColumn = column;
|
|
237
|
+
}
|
|
173
238
|
if (position !== null)
|
|
174
239
|
session.kanbanPosition = position;
|
|
175
240
|
sessionStore.updateSession(session);
|
|
@@ -177,11 +242,65 @@ ipcRoute('POST', '/api/sessions/:sessionId/board', async (req, res, params) => {
|
|
|
177
242
|
type: 'session.update',
|
|
178
243
|
body: {
|
|
179
244
|
sessionId: params.sessionId,
|
|
180
|
-
|
|
245
|
+
// queued 一并下发:激活后 session.queued 已为 false,前端浅合并若不带这个字段
|
|
246
|
+
// 会残留 queued=true(卡片仍显示「开始」、再点 409)。!!session.queued 始终反映现态。
|
|
247
|
+
patch: { kanbanColumn: session.kanbanColumn, kanbanPosition: session.kanbanPosition, queued: !!session.queued },
|
|
181
248
|
},
|
|
182
249
|
});
|
|
183
250
|
jsonRes(res, 200, { ok: true });
|
|
184
251
|
});
|
|
252
|
+
// 待办池会话「开始」:把 parked 会话激活(发首轮、起 CLI),与拖到「进行中」同义。
|
|
253
|
+
ipcRoute('POST', '/api/sessions/:sessionId/start', async (_req, res, params) => {
|
|
254
|
+
const ds = findActiveBySessionId(params.sessionId);
|
|
255
|
+
if (!ds)
|
|
256
|
+
return jsonRes(res, 404, { ok: false, error: 'session_not_found' });
|
|
257
|
+
if (!ds.session.queued)
|
|
258
|
+
return jsonRes(res, 409, { ok: false, error: 'not_queued' });
|
|
259
|
+
const r = await activateQueuedSession(ds);
|
|
260
|
+
if (!r.ok)
|
|
261
|
+
return jsonRes(res, 500, r);
|
|
262
|
+
sessionStore.updateSession(ds.session);
|
|
263
|
+
dashboardEventBus.publish({
|
|
264
|
+
type: 'session.update',
|
|
265
|
+
body: { sessionId: params.sessionId, patch: { kanbanColumn: ds.session.kanbanColumn, queued: false } },
|
|
266
|
+
});
|
|
267
|
+
jsonRes(res, 200, { ok: true });
|
|
268
|
+
});
|
|
269
|
+
// Dashboard「创建会话」spawn:在新建的群里为本 daemon 的 bot 拉起/暂存一条 chat-scope
|
|
270
|
+
// 会话。aggregator 建完群后按模式(一起开工/lead 分配)对每个目标 bot 的 daemon 调一次。
|
|
271
|
+
ipcRoute('POST', '/api/sessions/spawn', async (req, res) => {
|
|
272
|
+
if (!cachedLarkAppId)
|
|
273
|
+
return jsonRes(res, 503, { ok: false, error: 'bot_not_found' });
|
|
274
|
+
const activeSessions = getActiveSessionsRegistry();
|
|
275
|
+
if (!activeSessions)
|
|
276
|
+
return jsonRes(res, 503, { ok: false, error: 'registry_unavailable' });
|
|
277
|
+
let body;
|
|
278
|
+
try {
|
|
279
|
+
body = await readJsonBody(req);
|
|
280
|
+
}
|
|
281
|
+
catch {
|
|
282
|
+
return jsonRes(res, 400, { ok: false, error: 'invalid_json' });
|
|
283
|
+
}
|
|
284
|
+
const parsed = parseSpawnRequest(body);
|
|
285
|
+
if (!parsed.ok)
|
|
286
|
+
return jsonRes(res, 400, { ok: false, error: parsed.error });
|
|
287
|
+
const postBanner = !!body.postBanner;
|
|
288
|
+
const r = await spawnDashboardSession(activeSessions, undefined, {
|
|
289
|
+
larkAppId: cachedLarkAppId,
|
|
290
|
+
chatId: parsed.value.chatId,
|
|
291
|
+
content: parsed.value.content,
|
|
292
|
+
column: parsed.value.column,
|
|
293
|
+
role: parsed.value.role,
|
|
294
|
+
coworkers: parsed.value.coworkers,
|
|
295
|
+
title: parsed.value.title,
|
|
296
|
+
postBanner,
|
|
297
|
+
ownerOpenId: parsed.value.ownerOpenId,
|
|
298
|
+
ownerUnionId: parsed.value.ownerUnionId,
|
|
299
|
+
});
|
|
300
|
+
if (!r.ok)
|
|
301
|
+
return jsonRes(res, r.error === 'session_exists' ? 409 : 500, r);
|
|
302
|
+
jsonRes(res, 200, r);
|
|
303
|
+
});
|
|
185
304
|
// 会话历史:实时拉取该会话所在话题/群的飞书消息(与 botmux history 同链路,
|
|
186
305
|
// 消息体不落盘),给 dashboard 的会话历史弹窗。复杂卡片的「请升级」兜底文本
|
|
187
306
|
// 用 message.get 的完整表示补齐;merge_forward 保持占位符(原型不展开)。
|
|
@@ -454,7 +573,7 @@ ipcRoute('POST', '/api/sessions/:sessionId/sandbox-land/:action', (_req, res, pa
|
|
|
454
573
|
* CLI command (via this HTTP route). The CLI route also drops a notice into
|
|
455
574
|
* the original Lark thread so users see why the session is alive again.
|
|
456
575
|
*/
|
|
457
|
-
ipcRoute('POST', '/api/sessions/:sessionId/resume', async (
|
|
576
|
+
ipcRoute('POST', '/api/sessions/:sessionId/resume', async (req, res, params) => {
|
|
458
577
|
const sessionId = params.sessionId;
|
|
459
578
|
const reg = getActiveSessionsRegistry();
|
|
460
579
|
if (!reg)
|
|
@@ -465,6 +584,12 @@ ipcRoute('POST', '/api/sessions/:sessionId/resume', async (_req, res, params) =>
|
|
|
465
584
|
return jsonRes(res, status, { ok: false, error: result.error, activeSessionId: result.activeSessionId });
|
|
466
585
|
}
|
|
467
586
|
const ds = result.ds;
|
|
587
|
+
// `?wake=1` is an opt-in operational hook (no UI/CLI caller wires it today —
|
|
588
|
+
// it's meant for direct `curl` recovery): instead of the default lazy
|
|
589
|
+
// cold-resume on the next inbound message, fork the worker immediately so the
|
|
590
|
+
// session is usable right away. Off by default keeps every existing caller's
|
|
591
|
+
// behaviour unchanged.
|
|
592
|
+
const wake = new URL(req.url ?? '/', 'http://localhost').searchParams.get('wake') === '1';
|
|
468
593
|
// Tell the dashboard the row flipped back to active (mirror of session.update
|
|
469
594
|
// emitted by closeSession). Use `null` for closedAt — `undefined` would be
|
|
470
595
|
// dropped by JSON.stringify on the SSE wire and the aggregator's spread
|
|
@@ -496,9 +621,18 @@ ipcRoute('POST', '/api/sessions/:sessionId/resume', async (_req, res, params) =>
|
|
|
496
621
|
.catch(err => logger.debug(`[resume] failed to post thread-scope resume notice: ${err}`));
|
|
497
622
|
}
|
|
498
623
|
}
|
|
624
|
+
// Report the EFFECTIVE action, not the raw request flag: only fork when wake
|
|
625
|
+
// was asked AND there's no live worker to clobber. (resumeSession always hands
|
|
626
|
+
// back a worker:null ds today, so this matches `wake` in practice — but
|
|
627
|
+
// reporting the action keeps the response honest if the guard ever broadens.)
|
|
628
|
+
const woke = wake && (!ds.worker || ds.worker.killed);
|
|
629
|
+
if (woke) {
|
|
630
|
+
forkWorker(ds, '', true);
|
|
631
|
+
}
|
|
499
632
|
jsonRes(res, 200, {
|
|
500
633
|
ok: true,
|
|
501
634
|
sessionId,
|
|
635
|
+
wake: woke,
|
|
502
636
|
title: ds.session.title,
|
|
503
637
|
chatId: ds.chatId,
|
|
504
638
|
rootMessageId: ds.session.rootMessageId,
|
|
@@ -1061,6 +1195,16 @@ ipcRoute('GET', '/api/bot-default-oncall', async (_req, res) => {
|
|
|
1061
1195
|
startupCommands = sc.join('\n');
|
|
1062
1196
|
}
|
|
1063
1197
|
catch { /* none */ }
|
|
1198
|
+
// Per-bot env → pretty JSON for the dashboard textarea. The dashboard is
|
|
1199
|
+
// owner-authenticated, so showing the real values here is acceptable (same
|
|
1200
|
+
// as editing bots.json directly); the chat-facing /config get masks them.
|
|
1201
|
+
let env = '';
|
|
1202
|
+
try {
|
|
1203
|
+
const e = getBot(cachedLarkAppId).config.env;
|
|
1204
|
+
if (e && typeof e === 'object' && Object.keys(e).length)
|
|
1205
|
+
env = JSON.stringify(e, null, 2);
|
|
1206
|
+
}
|
|
1207
|
+
catch { /* none */ }
|
|
1064
1208
|
jsonRes(res, 200, {
|
|
1065
1209
|
larkAppId: cachedLarkAppId,
|
|
1066
1210
|
botName: getBotName(),
|
|
@@ -1078,10 +1222,12 @@ ipcRoute('GET', '/api/bot-default-oncall', async (_req, res) => {
|
|
|
1078
1222
|
regularGroupMentionMode: cardPrefs.regularGroupMentionMode,
|
|
1079
1223
|
docSubscribeDefaultMode: cardPrefs.docSubscribeDefaultMode,
|
|
1080
1224
|
restrictGrantCommands: grantPrefs.restrictGrantCommands,
|
|
1225
|
+
autoGrantRequestCards: grantPrefs.autoGrantRequestCards,
|
|
1081
1226
|
messageQuotaDefaultLimit: grantPrefs.messageQuotaDefaultLimit,
|
|
1082
1227
|
p2pMode,
|
|
1083
1228
|
maxLiveWorkers,
|
|
1084
1229
|
startupCommands,
|
|
1230
|
+
env,
|
|
1085
1231
|
skills: getBot(cachedLarkAppId).config.skills ?? null,
|
|
1086
1232
|
});
|
|
1087
1233
|
});
|
|
@@ -1130,6 +1276,7 @@ ipcRoute('PUT', '/api/bot-card-prefs', async (req, res) => {
|
|
|
1130
1276
|
});
|
|
1131
1277
|
// Per-bot 授权偏好。Body 任意子集:
|
|
1132
1278
|
// • restrictGrantCommands: boolean — 限制被授权人只能纯对话
|
|
1279
|
+
// • autoGrantRequestCards: boolean — 未授权 @ 被挡住时是否发 grant 申请卡
|
|
1133
1280
|
// • messageQuotaDefaultLimit: number|null — 默认消息额度(null = 关闭,正整数 = 启用)
|
|
1134
1281
|
ipcRoute('PUT', '/api/bot-grant-prefs', async (req, res) => {
|
|
1135
1282
|
if (!cachedLarkAppId)
|
|
@@ -1149,6 +1296,8 @@ ipcRoute('PUT', '/api/bot-grant-prefs', async (req, res) => {
|
|
|
1149
1296
|
const patch = {};
|
|
1150
1297
|
if (typeof body.restrictGrantCommands === 'boolean')
|
|
1151
1298
|
patch.restrictGrantCommands = body.restrictGrantCommands;
|
|
1299
|
+
if (typeof body.autoGrantRequestCards === 'boolean')
|
|
1300
|
+
patch.autoGrantRequestCards = body.autoGrantRequestCards;
|
|
1152
1301
|
// null(含 JSON null)= 关闭默认额度;number = 设定(store 内再校验正整数)。
|
|
1153
1302
|
if (body.messageQuotaDefaultLimit === null)
|
|
1154
1303
|
patch.messageQuotaDefaultLimit = null;
|
|
@@ -1237,6 +1386,40 @@ ipcRoute('PUT', '/api/bot-startup-commands', async (req, res) => {
|
|
|
1237
1386
|
return jsonRes(res, 400, { ok: false, error: r.reason });
|
|
1238
1387
|
jsonRes(res, 200, { ok: true, startupCommands: (value ?? []).join('\n') });
|
|
1239
1388
|
});
|
|
1389
|
+
// Per-bot 环境变量 env。Body `{ env: string }`(原始 JSON 文本,如
|
|
1390
|
+
// `{"ANTHROPIC_BASE_URL":"…","ANTHROPIC_AUTH_TOKEN":"…"}` 让本 bot 走 GLM/第三方
|
|
1391
|
+
// 服务商):空白 → 清除;否则按 json kind 解析 + sanitizePerBotEnv 过滤后落盘。
|
|
1392
|
+
// 走 applyConfigField(与 /botconfig 同一写盘 + 内存热更新路径),next-session 生效
|
|
1393
|
+
// (下个会话起注入到 CLI 进程)。回包返回脱敏后的 pretty JSON 供 textarea 回填。
|
|
1394
|
+
ipcRoute('PUT', '/api/bot-env', async (req, res) => {
|
|
1395
|
+
if (!cachedLarkAppId)
|
|
1396
|
+
return jsonRes(res, 503, { error: 'larkAppId_not_set' });
|
|
1397
|
+
let body;
|
|
1398
|
+
try {
|
|
1399
|
+
body = await readJsonBody(req);
|
|
1400
|
+
}
|
|
1401
|
+
catch {
|
|
1402
|
+
return jsonRes(res, 400, { ok: false, error: 'bad_json' });
|
|
1403
|
+
}
|
|
1404
|
+
const spec = findConfigField('env');
|
|
1405
|
+
if (!spec)
|
|
1406
|
+
return jsonRes(res, 500, { ok: false, error: 'spec_missing' });
|
|
1407
|
+
const raw = typeof body.env === 'string' ? body.env : '';
|
|
1408
|
+
let value;
|
|
1409
|
+
if (!raw.trim()) {
|
|
1410
|
+
value = null; // 清除
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
const coerced = coerceConfigValue(spec, raw);
|
|
1414
|
+
if (!coerced.ok)
|
|
1415
|
+
return jsonRes(res, 400, { ok: false, error: coerced.reason });
|
|
1416
|
+
value = coerced.value;
|
|
1417
|
+
}
|
|
1418
|
+
const r = await applyConfigField(cachedLarkAppId, spec, value);
|
|
1419
|
+
if (!r.ok)
|
|
1420
|
+
return jsonRes(res, 400, { ok: false, error: r.reason });
|
|
1421
|
+
jsonRes(res, 200, { ok: true, env: value ? JSON.stringify(value, null, 2) : '' });
|
|
1422
|
+
});
|
|
1240
1423
|
// Per-bot 最大同时活跃会话数 maxLiveWorkers。Body `{ maxLiveWorkers: number | null }`:
|
|
1241
1424
|
// • 正整数 → 设上限;超过后 idle-worker sweeper 把最久未用的会话休眠到上限内
|
|
1242
1425
|
// • null → 清除(回落到内置默认 30)
|
|
@@ -1491,9 +1674,31 @@ ipcRoute('GET', '/api/events', (_req, res) => {
|
|
|
1491
1674
|
});
|
|
1492
1675
|
// Initial flush so the client sees the connection alive immediately.
|
|
1493
1676
|
res.write('retry: 5000\n\n');
|
|
1677
|
+
// Subscribe BEFORE snapshotting so no event published in the gap is missed.
|
|
1494
1678
|
const off = dashboardEventBus.subscribe(ev => {
|
|
1495
1679
|
res.write(`event: ${ev.type}\ndata: ${JSON.stringify(ev.body)}\n\n`);
|
|
1496
1680
|
});
|
|
1681
|
+
// Replay the current active sessions as `session.spawned` right after
|
|
1682
|
+
// subscribing. `DashboardEventBus` has no buffer/replay, and the daemon
|
|
1683
|
+
// publishes its discovery descriptor BEFORE restoreActiveSessions() runs
|
|
1684
|
+
// (daemon.ts) — so a dashboard that hydrates (GET /api/sessions) during the
|
|
1685
|
+
// descriptor→restore window gets an EMPTY snapshot, and any restore-time
|
|
1686
|
+
// `announceSessionRow()` that fires before THIS subscription is established is
|
|
1687
|
+
// dropped. Without this replay the aggregator would then have neither a
|
|
1688
|
+
// snapshot row nor a spawned row, and later session.update/close patches would
|
|
1689
|
+
// be discarded as unknown-row. Replaying here makes SSE attach deterministic:
|
|
1690
|
+
// a row registered before subscribe arrives via this snapshot; one registered
|
|
1691
|
+
// after arrives via the live subscription above. Idempotent — both the
|
|
1692
|
+
// aggregator and the browser store upsert by sessionId, so any row also
|
|
1693
|
+
// delivered live just refreshes the same entry.
|
|
1694
|
+
try {
|
|
1695
|
+
for (const ds of listActiveSessions()) {
|
|
1696
|
+
res.write(`event: session.spawned\ndata: ${JSON.stringify({ session: composeRowFromActive(ds) })}\n\n`);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
catch (err) {
|
|
1700
|
+
logger.warn(`[dashboard-ipc] /api/events snapshot replay failed: ${err}`);
|
|
1701
|
+
}
|
|
1497
1702
|
const hb = setInterval(() => {
|
|
1498
1703
|
res.write(`event: heartbeat\ndata: ${JSON.stringify({ ts: Date.now() })}\n\n`);
|
|
1499
1704
|
}, 15_000);
|