botmux 2.94.0 → 2.96.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 +4 -4
- package/README.md +3 -3
- package/dist/adapters/backend/tmux-backend.d.ts +18 -2
- package/dist/adapters/backend/tmux-backend.d.ts.map +1 -1
- package/dist/adapters/backend/tmux-backend.js +44 -11
- package/dist/adapters/backend/tmux-backend.js.map +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js +1 -1
- package/dist/adapters/backend/tmux-pipe-backend.js.map +1 -1
- package/dist/adapters/backend/types.d.ts +8 -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/cli/kimi.d.ts +4 -0
- package/dist/adapters/cli/kimi.d.ts.map +1 -0
- package/dist/adapters/cli/kimi.js +54 -0
- package/dist/adapters/cli/kimi.js.map +1 -0
- package/dist/adapters/cli/registry.d.ts +2 -1
- package/dist/adapters/cli/registry.d.ts.map +1 -1
- package/dist/adapters/cli/registry.js +6 -20
- package/dist/adapters/cli/registry.js.map +1 -1
- package/dist/adapters/cli/types.d.ts +1 -1
- package/dist/adapters/cli/types.d.ts.map +1 -1
- package/dist/adapters/hook-installer.d.ts.map +1 -1
- package/dist/adapters/hook-installer.js +138 -28
- package/dist/adapters/hook-installer.js.map +1 -1
- package/dist/bot-registry.d.ts +101 -4
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +192 -3
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/stdin-encoding.d.ts +21 -0
- package/dist/cli/stdin-encoding.d.ts.map +1 -1
- package/dist/cli/stdin-encoding.js +37 -0
- package/dist/cli/stdin-encoding.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +27 -4
- package/dist/cli.js.map +1 -1
- package/dist/core/ask-hook/opencode.d.ts +15 -17
- package/dist/core/ask-hook/opencode.d.ts.map +1 -1
- package/dist/core/ask-hook/opencode.js +17 -19
- package/dist/core/ask-hook/opencode.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +29 -3
- 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 +282 -5
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/dashboard-rows.d.ts +2 -0
- package/dist/core/dashboard-rows.d.ts.map +1 -1
- package/dist/core/dashboard-rows.js +2 -0
- package/dist/core/dashboard-rows.js.map +1 -1
- package/dist/core/session-discovery.d.ts +9 -0
- package/dist/core/session-discovery.d.ts.map +1 -1
- package/dist/core/session-discovery.js +24 -0
- package/dist/core/session-discovery.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +7 -6
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/terminal-url.d.ts.map +1 -1
- package/dist/core/terminal-url.js +15 -0
- package/dist/core/terminal-url.js.map +1 -1
- package/dist/core/trigger-session.d.ts.map +1 -1
- package/dist/core/trigger-session.js +101 -9
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/types.d.ts +16 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/worker-pool.d.ts +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +69 -11
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +14 -11
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/aggregator.d.ts +5 -0
- package/dist/dashboard/aggregator.d.ts.map +1 -1
- package/dist/dashboard/aggregator.js +7 -0
- package/dist/dashboard/aggregator.js.map +1 -1
- package/dist/dashboard/bot-payload.d.ts +8 -0
- package/dist/dashboard/bot-payload.d.ts.map +1 -1
- package/dist/dashboard/bot-payload.js +8 -1
- package/dist/dashboard/bot-payload.js.map +1 -1
- package/dist/dashboard/connector-api.d.ts.map +1 -1
- package/dist/dashboard/connector-api.js +14 -3
- package/dist/dashboard/connector-api.js.map +1 -1
- package/dist/dashboard/session-cleanup.d.ts +1 -0
- package/dist/dashboard/session-cleanup.d.ts.map +1 -1
- package/dist/dashboard/session-cleanup.js +2 -0
- package/dist/dashboard/session-cleanup.js.map +1 -1
- package/dist/dashboard/settings-write-applier.d.ts +2 -1
- package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
- package/dist/dashboard/settings-write-applier.js +7 -0
- package/dist/dashboard/settings-write-applier.js.map +1 -1
- package/dist/dashboard/trigger-api.d.ts +4 -0
- package/dist/dashboard/trigger-api.d.ts.map +1 -1
- package/dist/dashboard/trigger-api.js +20 -0
- package/dist/dashboard/trigger-api.js.map +1 -1
- package/dist/dashboard/web/bot-defaults.d.ts +1 -0
- package/dist/dashboard/web/bot-defaults.d.ts.map +1 -1
- package/dist/dashboard/web/bot-defaults.js +352 -37
- package/dist/dashboard/web/bot-defaults.js.map +1 -1
- package/dist/dashboard/web/connectors.d.ts +11 -0
- package/dist/dashboard/web/connectors.d.ts.map +1 -1
- package/dist/dashboard/web/connectors.js +45 -1
- package/dist/dashboard/web/connectors.js.map +1 -1
- package/dist/dashboard/web/groups.d.ts +7 -0
- package/dist/dashboard/web/groups.d.ts.map +1 -1
- package/dist/dashboard/web/groups.js +62 -15
- package/dist/dashboard/web/groups.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +82 -4
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/sessions.d.ts.map +1 -1
- package/dist/dashboard/web/sessions.js +149 -5
- 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 +11 -0
- package/dist/dashboard/web/settings.js.map +1 -1
- package/dist/dashboard/web/whiteboards.d.ts.map +1 -1
- package/dist/dashboard/web/whiteboards.js +14 -5
- package/dist/dashboard/web/whiteboards.js.map +1 -1
- package/dist/dashboard/webhook-routes.d.ts.map +1 -1
- package/dist/dashboard/webhook-routes.js +126 -6
- package/dist/dashboard/webhook-routes.js.map +1 -1
- package/dist/dashboard-web/app.js +814 -725
- package/dist/dashboard-web/apple-touch-icon.png +0 -0
- package/dist/dashboard-web/favicon.png +0 -0
- package/dist/dashboard-web/index.html +2 -0
- package/dist/dashboard-web/style.css +47 -0
- package/dist/dashboard.js +247 -16
- package/dist/dashboard.js.map +1 -1
- package/dist/global-config.d.ts +10 -0
- package/dist/global-config.d.ts.map +1 -1
- package/dist/global-config.js +8 -0
- package/dist/global-config.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +21 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +21 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-builder.d.ts.map +1 -1
- package/dist/im/lark/card-builder.js +1 -0
- package/dist/im/lark/card-builder.js.map +1 -1
- package/dist/im/lark/client.d.ts +12 -0
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +48 -21
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +15 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +199 -16
- package/dist/im/lark/event-dispatcher.js.map +1 -1
- package/dist/im/lark/grant-command.js +4 -4
- package/dist/im/lark/grant-command.js.map +1 -1
- package/dist/im/lark/message-parser.d.ts +40 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +37 -2
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/summary-command.d.ts +19 -0
- package/dist/im/lark/summary-command.d.ts.map +1 -0
- package/dist/im/lark/summary-command.js +276 -0
- package/dist/im/lark/summary-command.js.map +1 -0
- package/dist/mir-local-runtime.d.ts +40 -0
- package/dist/mir-local-runtime.d.ts.map +1 -1
- package/dist/mir-local-runtime.js +246 -52
- package/dist/mir-local-runtime.js.map +1 -1
- package/dist/mir-runner.js +26 -1
- package/dist/mir-runner.js.map +1 -1
- package/dist/platform/binding.d.ts +6 -0
- package/dist/platform/binding.d.ts.map +1 -1
- package/dist/platform/binding.js +17 -0
- package/dist/platform/binding.js.map +1 -1
- package/dist/platform/tunnel-client.d.ts +2 -0
- package/dist/platform/tunnel-client.d.ts.map +1 -1
- package/dist/platform/tunnel-client.js.map +1 -1
- package/dist/services/bot-config-store.d.ts.map +1 -1
- package/dist/services/bot-config-store.js +1 -0
- package/dist/services/bot-config-store.js.map +1 -1
- package/dist/services/card-prefs-store.d.ts +2 -2
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +5 -5
- package/dist/services/card-prefs-store.js.map +1 -1
- package/dist/services/chat-reply-mode-store.d.ts +6 -4
- package/dist/services/chat-reply-mode-store.d.ts.map +1 -1
- package/dist/services/chat-reply-mode-store.js +6 -4
- package/dist/services/chat-reply-mode-store.js.map +1 -1
- package/dist/services/oncall-store.d.ts +25 -0
- package/dist/services/oncall-store.d.ts.map +1 -1
- package/dist/services/oncall-store.js +56 -0
- package/dist/services/oncall-store.js.map +1 -1
- package/dist/services/pi-transcript.d.ts +19 -0
- package/dist/services/pi-transcript.d.ts.map +1 -0
- package/dist/services/pi-transcript.js +222 -0
- package/dist/services/pi-transcript.js.map +1 -0
- package/dist/services/summary-range-store.d.ts +21 -0
- package/dist/services/summary-range-store.d.ts.map +1 -0
- package/dist/services/summary-range-store.js +89 -0
- package/dist/services/summary-range-store.js.map +1 -0
- package/dist/services/transcript-resolver.d.ts.map +1 -1
- package/dist/services/transcript-resolver.js +21 -4
- package/dist/services/transcript-resolver.js.map +1 -1
- package/dist/services/trigger-types.d.ts +14 -2
- package/dist/services/trigger-types.d.ts.map +1 -1
- package/dist/services/trigger-types.js +15 -1
- package/dist/services/trigger-types.js.map +1 -1
- package/dist/setup/bot-config-editor.d.ts +7 -0
- package/dist/setup/bot-config-editor.d.ts.map +1 -1
- package/dist/setup/bot-config-editor.js +35 -0
- package/dist/setup/bot-config-editor.js.map +1 -1
- package/dist/types.d.ts +21 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/executable.d.ts +3 -0
- package/dist/utils/executable.d.ts.map +1 -0
- package/dist/utils/executable.js +24 -0
- package/dist/utils/executable.js.map +1 -0
- package/dist/utils/terminal-favicon.d.ts +2 -0
- package/dist/utils/terminal-favicon.d.ts.map +1 -0
- package/dist/utils/terminal-favicon.js +5 -0
- package/dist/utils/terminal-favicon.js.map +1 -0
- package/dist/worker.js +211 -12
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +8 -8
- package/dist/workflows/events/payloads.d.ts +10 -10
- package/dist/workflows/events/schema.d.ts +28 -28
- package/dist/workflows/v3/bot-resolve.d.ts +1 -1
- package/dist/workflows/v3/bot-resolve.d.ts.map +1 -1
- package/dist/workflows/v3/bot-resolve.js +2 -1
- package/dist/workflows/v3/bot-resolve.js.map +1 -1
- package/dist/workflows/v3/cli-run.js +2 -2
- package/dist/workflows/v3/cli-run.js.map +1 -1
- package/package.json +2 -2
|
Binary file
|
|
Binary file
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<title>botmux dashboard</title>
|
|
7
|
+
<link rel="icon" type="image/png" href="/assets/favicon.png">
|
|
8
|
+
<link rel="apple-touch-icon" href="/assets/apple-touch-icon.png">
|
|
7
9
|
<link rel="stylesheet" href="/assets/style.css">
|
|
8
10
|
</head>
|
|
9
11
|
<body>
|
|
@@ -919,6 +919,22 @@ button.primary {
|
|
|
919
919
|
border: 1px solid var(--border-soft);
|
|
920
920
|
}
|
|
921
921
|
|
|
922
|
+
.session-lock-badge {
|
|
923
|
+
display: inline-flex;
|
|
924
|
+
align-items: center;
|
|
925
|
+
min-height: 22px;
|
|
926
|
+
margin-left: 6px;
|
|
927
|
+
padding: 0 8px;
|
|
928
|
+
border-radius: var(--radius-sm);
|
|
929
|
+
border: 1px solid color-mix(in srgb, var(--warning) 38%, var(--border-soft));
|
|
930
|
+
background: color-mix(in srgb, var(--warning) 10%, var(--surface-muted));
|
|
931
|
+
color: var(--warning);
|
|
932
|
+
font-size: 11px;
|
|
933
|
+
font-weight: 800;
|
|
934
|
+
line-height: 1;
|
|
935
|
+
text-transform: uppercase;
|
|
936
|
+
}
|
|
937
|
+
|
|
922
938
|
.cli-claude-code { background: #e8f0ff; color: #234fb4; border-color: #c8d9ff; }
|
|
923
939
|
.cli-codex { background: #fff0df; color: #a14c11; border-color: #ffd8ad; }
|
|
924
940
|
.cli-codex-app { background: #e9f7ff; color: #11638f; border-color: #bde4fa; }
|
|
@@ -1140,6 +1156,10 @@ button.primary {
|
|
|
1140
1156
|
background: color-mix(in srgb, var(--accent) 5%, var(--surface-raised));
|
|
1141
1157
|
}
|
|
1142
1158
|
|
|
1159
|
+
.session-card.locked {
|
|
1160
|
+
border-color: color-mix(in srgb, var(--warning) 45%, var(--border));
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1143
1163
|
@media (prefers-reduced-motion: reduce) {
|
|
1144
1164
|
.session-card { animation: none; transition: none; }
|
|
1145
1165
|
.session-board-needs-you h2::before { animation: none; }
|
|
@@ -1197,6 +1217,19 @@ button.primary {
|
|
|
1197
1217
|
font-size: 12px;
|
|
1198
1218
|
}
|
|
1199
1219
|
|
|
1220
|
+
.session-card-status-group,
|
|
1221
|
+
.drawer-status-line {
|
|
1222
|
+
display: inline-flex;
|
|
1223
|
+
align-items: center;
|
|
1224
|
+
gap: 6px;
|
|
1225
|
+
flex-wrap: wrap;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
.session-card-status-group .session-lock-badge,
|
|
1229
|
+
.drawer-status-line .session-lock-badge {
|
|
1230
|
+
margin-left: 0;
|
|
1231
|
+
}
|
|
1232
|
+
|
|
1200
1233
|
.session-signal {
|
|
1201
1234
|
display: inline-flex;
|
|
1202
1235
|
align-items: center;
|
|
@@ -1487,6 +1520,10 @@ button.primary {
|
|
|
1487
1520
|
background: color-mix(in srgb, var(--accent) 4%, var(--surface-raised));
|
|
1488
1521
|
}
|
|
1489
1522
|
|
|
1523
|
+
.kanban-card.locked {
|
|
1524
|
+
border-color: color-mix(in srgb, var(--warning) 45%, var(--border));
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1490
1527
|
.kanban-card:focus-visible {
|
|
1491
1528
|
outline: 2px solid var(--accent);
|
|
1492
1529
|
outline-offset: 1px;
|
|
@@ -1873,6 +1910,11 @@ button.primary {
|
|
|
1873
1910
|
border-color: var(--danger);
|
|
1874
1911
|
background: color-mix(in srgb, var(--danger) 9%, var(--surface-raised));
|
|
1875
1912
|
}
|
|
1913
|
+
.card-act.locked {
|
|
1914
|
+
color: var(--warning);
|
|
1915
|
+
border-color: color-mix(in srgb, var(--warning) 45%, var(--border));
|
|
1916
|
+
background: color-mix(in srgb, var(--warning) 8%, var(--surface-raised));
|
|
1917
|
+
}
|
|
1876
1918
|
.card-act:active { transform: scale(0.9); }
|
|
1877
1919
|
|
|
1878
1920
|
/* 终端访问药丸:左「终端」段(accent ghost·只读旁观) + 右「钥匙」段(accent 实心·
|
|
@@ -4216,6 +4258,11 @@ button.contrast:hover { background: var(--danger-soft); border-color: var(--dang
|
|
|
4216
4258
|
box-shadow: 0 0 8px color-mix(in srgb, var(--accent) 55%, transparent);
|
|
4217
4259
|
flex: none;
|
|
4218
4260
|
}
|
|
4261
|
+
.bd-summary-limits {
|
|
4262
|
+
display: grid;
|
|
4263
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
4264
|
+
gap: 10px;
|
|
4265
|
+
}
|
|
4219
4266
|
|
|
4220
4267
|
.settings-grid {
|
|
4221
4268
|
display: grid;
|
package/dist/dashboard.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/dashboard.ts
|
|
2
|
-
import { createServer, get as httpGet } from 'node:http';
|
|
3
|
-
import { createServer as createTcpServer } from 'node:net';
|
|
2
|
+
import { createServer, get as httpGet, request as httpRequest } from 'node:http';
|
|
3
|
+
import { createServer as createTcpServer, connect as netConnect } from 'node:net';
|
|
4
4
|
import { readFileSync, existsSync, mkdirSync, statSync, createReadStream, } from 'node:fs';
|
|
5
5
|
import { atomicWriteFileSync } from './utils/atomic-write.js';
|
|
6
6
|
import { join, dirname, extname, resolve, relative, isAbsolute } from 'node:path';
|
|
@@ -178,6 +178,7 @@ function resolveDashboardSettings() {
|
|
|
178
178
|
maintenance: global.maintenance ?? {},
|
|
179
179
|
localDevInstall: isLocalDevInstall(),
|
|
180
180
|
whiteboard: { enabled: global.whiteboard?.enabled === true },
|
|
181
|
+
remoteAccess: global.remoteAccess === true,
|
|
181
182
|
};
|
|
182
183
|
}
|
|
183
184
|
// Single shared deps object for `applySettingsWrite` — both the browser
|
|
@@ -474,8 +475,27 @@ function configuredCliIds() {
|
|
|
474
475
|
return new Map();
|
|
475
476
|
}
|
|
476
477
|
}
|
|
478
|
+
function configuredBotAgentFields() {
|
|
479
|
+
try {
|
|
480
|
+
return new Map(loadBotConfigs().map(b => [b.larkAppId, {
|
|
481
|
+
cliId: b.cliId,
|
|
482
|
+
wrapperCli: b.wrapperCli,
|
|
483
|
+
model: b.model,
|
|
484
|
+
}]));
|
|
485
|
+
}
|
|
486
|
+
catch {
|
|
487
|
+
return new Map();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
477
490
|
function withConfiguredCliId(bot, ids) {
|
|
478
|
-
|
|
491
|
+
const raw = ids.get(bot.larkAppId);
|
|
492
|
+
const fallback = typeof raw === 'string' ? { cliId: raw } : raw;
|
|
493
|
+
return {
|
|
494
|
+
...bot,
|
|
495
|
+
cliId: bot.cliId || fallback?.cliId,
|
|
496
|
+
wrapperCli: bot.wrapperCli || fallback?.wrapperCli,
|
|
497
|
+
model: bot.model || fallback?.model,
|
|
498
|
+
};
|
|
479
499
|
}
|
|
480
500
|
function liveBots() {
|
|
481
501
|
const ids = configuredCliIds();
|
|
@@ -833,6 +853,15 @@ async function dashboardSkillReferences(skillName) {
|
|
|
833
853
|
bots: [...refs.values()].sort((a, b) => a.botName.localeCompare(b.botName)),
|
|
834
854
|
};
|
|
835
855
|
}
|
|
856
|
+
/** Extract the sessionId from a terminal path `/s/<sessionId>[/...]`. Returns
|
|
857
|
+
* the first path segment after `/s/` (stops at the next `/`; query/hash are
|
|
858
|
+
* already stripped by URL.pathname). undefined when there's no segment. */
|
|
859
|
+
function parseTerminalSessionId(pathname) {
|
|
860
|
+
if (!pathname.startsWith('/s/'))
|
|
861
|
+
return undefined;
|
|
862
|
+
const seg = pathname.slice(3).split('/')[0];
|
|
863
|
+
return seg || undefined;
|
|
864
|
+
}
|
|
836
865
|
const server = createServer(async (req, res) => {
|
|
837
866
|
try {
|
|
838
867
|
const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
|
|
@@ -847,6 +876,35 @@ const server = createServer(async (req, res) => {
|
|
|
847
876
|
res.writeHead(200, { 'content-type': 'text/plain' });
|
|
848
877
|
return res.end(DASHBOARD_SELF_NONCE);
|
|
849
878
|
}
|
|
879
|
+
// Web terminal reverse-proxy: `/s/<sessionId>/*` → the owning bot daemon's
|
|
880
|
+
// terminal proxy. The central platform only tunnels the dashboard port, so
|
|
881
|
+
// terminal links served under the machine subdomain
|
|
882
|
+
// (`https://m-<id>.<host>/s/<sessionId>`) land here. The dashboard is the
|
|
883
|
+
// aggregator process (it fronts many bot daemons, each with its own terminal
|
|
884
|
+
// proxy on proxyBasePort+idx), so we resolve the session's owning daemon's
|
|
885
|
+
// proxy port from the aggregator rows and forward there, streaming the
|
|
886
|
+
// response straight back. Mounted before the dashboard auth gate because the
|
|
887
|
+
// terminal proxy / worker enforces its own write gate (read-only without
|
|
888
|
+
// token / platform role).
|
|
889
|
+
if (url.pathname === '/s' || url.pathname.startsWith('/s/')) {
|
|
890
|
+
const sessionId = parseTerminalSessionId(url.pathname);
|
|
891
|
+
const tport = sessionId ? aggregator.terminalProxyPortOf(sessionId) : undefined;
|
|
892
|
+
if (!tport) {
|
|
893
|
+
res.writeHead(404, { 'content-type': 'text/plain; charset=utf-8' });
|
|
894
|
+
return res.end('session terminal not available');
|
|
895
|
+
}
|
|
896
|
+
const upstream = httpRequest({ host: '127.0.0.1', port: tport, method: req.method, path: req.url, headers: req.headers }, (up) => {
|
|
897
|
+
res.writeHead(up.statusCode ?? 502, up.headers);
|
|
898
|
+
up.pipe(res);
|
|
899
|
+
});
|
|
900
|
+
upstream.on('error', () => {
|
|
901
|
+
if (!res.headersSent)
|
|
902
|
+
res.writeHead(502, { 'content-type': 'text/plain; charset=utf-8' });
|
|
903
|
+
res.end('terminal proxy error');
|
|
904
|
+
});
|
|
905
|
+
req.pipe(upstream);
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
850
908
|
if (await handleWebhookRoute(req, res, url, {
|
|
851
909
|
proxyToDaemon,
|
|
852
910
|
createLifecycleGroup: createLifecycleGroupForWebhook,
|
|
@@ -1091,7 +1149,14 @@ const server = createServer(async (req, res) => {
|
|
|
1091
1149
|
// `lang` is the global UI locale (single source of truth shared with
|
|
1092
1150
|
// `botmux lang` and the Feishu cards) — the web UI reads it as its
|
|
1093
1151
|
// authoritative initial language when set.
|
|
1094
|
-
|
|
1152
|
+
// `bound` reflects central-platform binding; the Settings UI only shows the
|
|
1153
|
+
// 远程访问 toggle when bound (the central URLs are meaningless otherwise).
|
|
1154
|
+
return jsonRes(res, 200, {
|
|
1155
|
+
settings: dashboardSettings,
|
|
1156
|
+
lang: readGlobalConfig().lang ?? null,
|
|
1157
|
+
authed,
|
|
1158
|
+
bound: readPlatformBinding() !== null,
|
|
1159
|
+
});
|
|
1095
1160
|
}
|
|
1096
1161
|
if (req.method === 'PUT' && url.pathname === '/api/settings') {
|
|
1097
1162
|
let parsed;
|
|
@@ -1480,9 +1545,9 @@ const server = createServer(async (req, res) => {
|
|
|
1480
1545
|
return jsonRes(res, 400, { ok: false, error: 'bad_request' });
|
|
1481
1546
|
return jsonRes(res, 200, { ok: true, entry });
|
|
1482
1547
|
}
|
|
1483
|
-
// 看板放置 /
|
|
1548
|
+
// 看板放置 / 重命名 / 锁定:带 JSON body 的会话写操作,原样转发给 owner daemon。
|
|
1484
1549
|
// 不在公开读白名单内 → 只读访客在 decideDashboardAuth 已被 401。
|
|
1485
|
-
if (req.method === 'POST' && (m = url.pathname.match(/^\/api\/sessions\/([^/]+)\/(board|rename)$/))) {
|
|
1550
|
+
if (req.method === 'POST' && (m = url.pathname.match(/^\/api\/sessions\/([^/]+)\/(board|rename|lock)$/))) {
|
|
1486
1551
|
const sid = decodeURIComponent(m[1]);
|
|
1487
1552
|
const op = m[2];
|
|
1488
1553
|
const owner = aggregator.ownerOf(sid);
|
|
@@ -1848,8 +1913,8 @@ const server = createServer(async (req, res) => {
|
|
|
1848
1913
|
// [{larkAppId, botName, defaultOncall, ...}]
|
|
1849
1914
|
// PUT /api/bots/:appId/default-oncall — proxy to that bot's daemon
|
|
1850
1915
|
if (req.method === 'GET' && url.pathname === '/api/bots') {
|
|
1851
|
-
const
|
|
1852
|
-
const onlineBots = [...registry.list()].map(b => withConfiguredCliId(b,
|
|
1916
|
+
const agentFields = configuredBotAgentFields();
|
|
1917
|
+
const onlineBots = [...registry.list()].map(b => withConfiguredCliId(b, agentFields)).sort((a, b) => a.botIndex - b.botIndex);
|
|
1853
1918
|
const out = await Promise.all(onlineBots.map(async (d) => {
|
|
1854
1919
|
try {
|
|
1855
1920
|
const r = await fetch(`http://127.0.0.1:${d.ipcPort}/api/bot-default-oncall`);
|
|
@@ -1857,7 +1922,13 @@ const server = createServer(async (req, res) => {
|
|
|
1857
1922
|
return botDefaultsPayload(d, undefined, `http_${r.status}`);
|
|
1858
1923
|
}
|
|
1859
1924
|
const j = await r.json();
|
|
1860
|
-
return botDefaultsPayload({
|
|
1925
|
+
return botDefaultsPayload({
|
|
1926
|
+
...d,
|
|
1927
|
+
botName: d.botName ?? j.botName,
|
|
1928
|
+
cliId: j.cliId || d.cliId,
|
|
1929
|
+
wrapperCli: j.wrapperCli || d.wrapperCli,
|
|
1930
|
+
model: j.model || d.model,
|
|
1931
|
+
}, j);
|
|
1861
1932
|
}
|
|
1862
1933
|
catch (e) {
|
|
1863
1934
|
return botDefaultsPayload(d, undefined, e?.message ?? String(e));
|
|
@@ -1881,6 +1952,43 @@ const server = createServer(async (req, res) => {
|
|
|
1881
1952
|
res.end(await upstream.text());
|
|
1882
1953
|
return;
|
|
1883
1954
|
}
|
|
1955
|
+
// PUT /api/bots/:appId/working-dir-mode — proxy to that bot's daemon. Body
|
|
1956
|
+
// `{ mode: 'off'|'default'|'oncall', workingDir }` — sets the 3-way
|
|
1957
|
+
// mutually-exclusive default-dir mode (defaultWorkingDir vs defaultOncall).
|
|
1958
|
+
let mBotWdMode;
|
|
1959
|
+
if (req.method === 'PUT' && (mBotWdMode = url.pathname.match(/^\/api\/bots\/([^/]+)\/working-dir-mode$/))) {
|
|
1960
|
+
const appId = decodeURIComponent(mBotWdMode[1]);
|
|
1961
|
+
const chunks = [];
|
|
1962
|
+
for await (const c of req)
|
|
1963
|
+
chunks.push(c);
|
|
1964
|
+
const raw = Buffer.concat(chunks).toString('utf8') || '{}';
|
|
1965
|
+
const upstream = await proxyToDaemon(appId, `/api/bot-working-dir-mode`, {
|
|
1966
|
+
method: 'PUT',
|
|
1967
|
+
headers: { 'content-type': 'application/json' },
|
|
1968
|
+
body: raw,
|
|
1969
|
+
});
|
|
1970
|
+
res.writeHead(upstream.status, { 'content-type': 'application/json' });
|
|
1971
|
+
res.end(await upstream.text());
|
|
1972
|
+
return;
|
|
1973
|
+
}
|
|
1974
|
+
// PUT /api/bots/:appId/agent — proxy to that bot's daemon. Body
|
|
1975
|
+
// `{ cliId, model }`; cliId is the dashboard selection key.
|
|
1976
|
+
let mBotAgent;
|
|
1977
|
+
if (req.method === 'PUT' && (mBotAgent = url.pathname.match(/^\/api\/bots\/([^/]+)\/agent$/))) {
|
|
1978
|
+
const appId = decodeURIComponent(mBotAgent[1]);
|
|
1979
|
+
const chunks = [];
|
|
1980
|
+
for await (const c of req)
|
|
1981
|
+
chunks.push(c);
|
|
1982
|
+
const raw = Buffer.concat(chunks).toString('utf8') || '{}';
|
|
1983
|
+
const upstream = await proxyToDaemon(appId, `/api/bot-agent`, {
|
|
1984
|
+
method: 'PUT',
|
|
1985
|
+
headers: { 'content-type': 'application/json' },
|
|
1986
|
+
body: raw,
|
|
1987
|
+
});
|
|
1988
|
+
res.writeHead(upstream.status, { 'content-type': 'application/json' });
|
|
1989
|
+
res.end(await upstream.text());
|
|
1990
|
+
return;
|
|
1991
|
+
}
|
|
1884
1992
|
// PUT /api/bots/:appId/skills — proxy to that bot's daemon. Body accepts
|
|
1885
1993
|
// `{ action:'attach'|'detach', name }` or `{ action:'set', policy|null }`.
|
|
1886
1994
|
let mBotSkills;
|
|
@@ -1935,6 +2043,24 @@ const server = createServer(async (req, res) => {
|
|
|
1935
2043
|
res.end(await upstream.text());
|
|
1936
2044
|
return;
|
|
1937
2045
|
}
|
|
2046
|
+
// PUT /api/bots/:appId/launch-shell — proxy to that bot's daemon. Body
|
|
2047
|
+
// `{ launchShell: string }` (shell name or absolute path; '' = clear → $SHELL).
|
|
2048
|
+
let mBotLaunchShell;
|
|
2049
|
+
if (req.method === 'PUT' && (mBotLaunchShell = url.pathname.match(/^\/api\/bots\/([^/]+)\/launch-shell$/))) {
|
|
2050
|
+
const appId = decodeURIComponent(mBotLaunchShell[1]);
|
|
2051
|
+
const chunks = [];
|
|
2052
|
+
for await (const c of req)
|
|
2053
|
+
chunks.push(c);
|
|
2054
|
+
const raw = Buffer.concat(chunks).toString('utf8') || '{}';
|
|
2055
|
+
const upstream = await proxyToDaemon(appId, `/api/bot-launch-shell`, {
|
|
2056
|
+
method: 'PUT',
|
|
2057
|
+
headers: { 'content-type': 'application/json' },
|
|
2058
|
+
body: raw,
|
|
2059
|
+
});
|
|
2060
|
+
res.writeHead(upstream.status, { 'content-type': 'application/json' });
|
|
2061
|
+
res.end(await upstream.text());
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
1938
2064
|
// PUT /api/bots/:appId/env — proxy to that bot's daemon. Body
|
|
1939
2065
|
// `{ env: string }` (raw JSON text; '' = clear).
|
|
1940
2066
|
let mBotEnv;
|
|
@@ -1988,6 +2114,41 @@ const server = createServer(async (req, res) => {
|
|
|
1988
2114
|
res.end(await upstream.text());
|
|
1989
2115
|
return;
|
|
1990
2116
|
}
|
|
2117
|
+
// PUT /api/bots/:appId/summary-range — proxy to that bot's daemon. Body
|
|
2118
|
+
// `{ limit, sinceHours }`; daemon updates the explicit /summary range.
|
|
2119
|
+
let mBotSummaryRange;
|
|
2120
|
+
if (req.method === 'PUT' && (mBotSummaryRange = url.pathname.match(/^\/api\/bots\/([^/]+)\/summary-range$/))) {
|
|
2121
|
+
const appId = decodeURIComponent(mBotSummaryRange[1]);
|
|
2122
|
+
const chunks = [];
|
|
2123
|
+
for await (const c of req)
|
|
2124
|
+
chunks.push(c);
|
|
2125
|
+
const raw = Buffer.concat(chunks).toString('utf8') || '{}';
|
|
2126
|
+
const upstream = await proxyToDaemon(appId, `/api/bot-summary-range`, {
|
|
2127
|
+
method: 'PUT',
|
|
2128
|
+
headers: { 'content-type': 'application/json' },
|
|
2129
|
+
body: raw,
|
|
2130
|
+
});
|
|
2131
|
+
res.writeHead(upstream.status, { 'content-type': 'application/json' });
|
|
2132
|
+
res.end(await upstream.text());
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
// Backward-compatible alias from the short-lived keyword-trigger dashboard.
|
|
2136
|
+
let mBotSummaryTrigger;
|
|
2137
|
+
if (req.method === 'PUT' && (mBotSummaryTrigger = url.pathname.match(/^\/api\/bots\/([^/]+)\/summary-trigger$/))) {
|
|
2138
|
+
const appId = decodeURIComponent(mBotSummaryTrigger[1]);
|
|
2139
|
+
const chunks = [];
|
|
2140
|
+
for await (const c of req)
|
|
2141
|
+
chunks.push(c);
|
|
2142
|
+
const raw = Buffer.concat(chunks).toString('utf8') || '{}';
|
|
2143
|
+
const upstream = await proxyToDaemon(appId, `/api/bot-summary-trigger`, {
|
|
2144
|
+
method: 'PUT',
|
|
2145
|
+
headers: { 'content-type': 'application/json' },
|
|
2146
|
+
body: raw,
|
|
2147
|
+
});
|
|
2148
|
+
res.writeHead(upstream.status, { 'content-type': 'application/json' });
|
|
2149
|
+
res.end(await upstream.text());
|
|
2150
|
+
return;
|
|
2151
|
+
}
|
|
1991
2152
|
// PUT /api/bots/:appId/p2p-mode — proxy to that bot's daemon. Body
|
|
1992
2153
|
// `{ p2pMode: 'chat' | 'thread' }` ('chat' = flat continuous DM session;
|
|
1993
2154
|
// anything else clears back to the per-message thread default).
|
|
@@ -2307,6 +2468,59 @@ const server = createServer(async (req, res) => {
|
|
|
2307
2468
|
jsonRes(res, 500, { error: String(err) });
|
|
2308
2469
|
}
|
|
2309
2470
|
});
|
|
2471
|
+
// Web terminal WebSocket reverse-proxy: bridge `/s/*` upgrade requests through to
|
|
2472
|
+
// the local terminal proxy (which in turn bridges to the session worker). Raw
|
|
2473
|
+
// socket-to-socket bridge: dial 127.0.0.1:<terminalProxyPort>, replay the upgrade
|
|
2474
|
+
// request line + headers verbatim, then pipe both directions. Mirrors how
|
|
2475
|
+
// terminal-proxy.ts bridges to the worker. Non-`/s/*` upgrades are dropped (the
|
|
2476
|
+
// dashboard SPA uses SSE, not WebSocket).
|
|
2477
|
+
server.on('upgrade', (req, clientSocket, head) => {
|
|
2478
|
+
try {
|
|
2479
|
+
const rawUrl = req.url ?? '/';
|
|
2480
|
+
if (!(rawUrl === '/s' || rawUrl.startsWith('/s/') || rawUrl.startsWith('/s?'))) {
|
|
2481
|
+
return clientSocket.destroy();
|
|
2482
|
+
}
|
|
2483
|
+
// Strip query/hash before extracting the sessionId path segment.
|
|
2484
|
+
const pathname = rawUrl.split(/[?#]/)[0];
|
|
2485
|
+
const sessionId = parseTerminalSessionId(pathname);
|
|
2486
|
+
const tport = sessionId ? aggregator.terminalProxyPortOf(sessionId) : undefined;
|
|
2487
|
+
if (!tport)
|
|
2488
|
+
return clientSocket.destroy();
|
|
2489
|
+
const upstream = netConnect(tport, '127.0.0.1', () => {
|
|
2490
|
+
// rawHeaders is a flat [k, v, k, v, ...] list — preserves casing/duplicates.
|
|
2491
|
+
const lines = [`${req.method} ${req.url} HTTP/1.1`];
|
|
2492
|
+
const rh = req.rawHeaders;
|
|
2493
|
+
for (let i = 0; i + 1 < rh.length; i += 2)
|
|
2494
|
+
lines.push(`${rh[i]}: ${rh[i + 1]}`);
|
|
2495
|
+
lines.push('', '');
|
|
2496
|
+
upstream.write(lines.join('\r\n'));
|
|
2497
|
+
if (head?.length)
|
|
2498
|
+
upstream.write(head);
|
|
2499
|
+
upstream.pipe(clientSocket);
|
|
2500
|
+
clientSocket.pipe(upstream);
|
|
2501
|
+
});
|
|
2502
|
+
const cleanup = () => {
|
|
2503
|
+
try {
|
|
2504
|
+
upstream.destroy();
|
|
2505
|
+
}
|
|
2506
|
+
catch { /* ignore */ }
|
|
2507
|
+
try {
|
|
2508
|
+
clientSocket.destroy();
|
|
2509
|
+
}
|
|
2510
|
+
catch { /* ignore */ }
|
|
2511
|
+
};
|
|
2512
|
+
upstream.on('error', cleanup);
|
|
2513
|
+
clientSocket.on('error', cleanup);
|
|
2514
|
+
upstream.on('close', () => clientSocket.destroy());
|
|
2515
|
+
clientSocket.on('close', () => upstream.destroy());
|
|
2516
|
+
}
|
|
2517
|
+
catch {
|
|
2518
|
+
try {
|
|
2519
|
+
clientSocket.destroy();
|
|
2520
|
+
}
|
|
2521
|
+
catch { /* ignore */ }
|
|
2522
|
+
}
|
|
2523
|
+
});
|
|
2310
2524
|
// Probe upward on EADDRINUSE rather than crashing with an unhandled 'error':
|
|
2311
2525
|
// a second botmux instance on this host (or a stray process) holding the
|
|
2312
2526
|
// configured port would otherwise tear the dashboard process down on bind.
|
|
@@ -2396,14 +2610,31 @@ function readPlatformBotsInfo() {
|
|
|
2396
2610
|
const entries = JSON.parse(readFileSync(fp, 'utf8'));
|
|
2397
2611
|
if (!Array.isArray(entries))
|
|
2398
2612
|
return [];
|
|
2613
|
+
// Merge per-bot team-visibility config (showInTeam) from bots.json by
|
|
2614
|
+
// larkAppId so the platform team page can hide bots. Default: showInTeam =
|
|
2615
|
+
// true (shown). bots.json may be unreadable from the dashboard process →
|
|
2616
|
+
// fall back to the default.
|
|
2617
|
+
const cfgByAppId = new Map();
|
|
2618
|
+
try {
|
|
2619
|
+
for (const cfg of loadBotConfigs()) {
|
|
2620
|
+
cfgByAppId.set(cfg.larkAppId, { showInTeam: cfg.showInTeam });
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
catch {
|
|
2624
|
+
/* defaults below */
|
|
2625
|
+
}
|
|
2399
2626
|
return entries
|
|
2400
|
-
.map((e) =>
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2627
|
+
.map((e) => {
|
|
2628
|
+
const cfg = cfgByAppId.get(e.larkAppId || '');
|
|
2629
|
+
return {
|
|
2630
|
+
appId: e.larkAppId || '',
|
|
2631
|
+
openId: e.botOpenId ?? null,
|
|
2632
|
+
name: e.botName || e.larkAppId || 'bot',
|
|
2633
|
+
avatar: e.botAvatarUrl || undefined,
|
|
2634
|
+
cli: e.cliId,
|
|
2635
|
+
showInTeam: cfg?.showInTeam !== false, // default true
|
|
2636
|
+
};
|
|
2637
|
+
})
|
|
2407
2638
|
.filter((b) => b.appId);
|
|
2408
2639
|
}
|
|
2409
2640
|
catch {
|