botmux 2.93.1 → 2.95.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/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 +45 -3
- 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/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +6 -1
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/bot-registry.d.ts +50 -1
- package/dist/bot-registry.d.ts.map +1 -1
- package/dist/bot-registry.js +30 -0
- package/dist/bot-registry.js.map +1 -1
- package/dist/cli/workflow.d.ts.map +1 -1
- package/dist/cli/workflow.js +11 -4
- package/dist/cli/workflow.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +146 -1
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +28 -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 +92 -0
- package/dist/core/dashboard-ipc-server.js.map +1 -1
- package/dist/core/inherit-peer.d.ts +5 -0
- package/dist/core/inherit-peer.d.ts.map +1 -1
- package/dist/core/inherit-peer.js +3 -0
- package/dist/core/inherit-peer.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.js +2 -2
- package/dist/core/trigger-session.js.map +1 -1
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +30 -0
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +296 -23
- 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/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +7 -1
- package/dist/dashboard/auth.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/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/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/v3-runs-api.d.ts +26 -0
- package/dist/dashboard/v3-runs-api.d.ts.map +1 -0
- package/dist/dashboard/v3-runs-api.js +67 -0
- package/dist/dashboard/v3-runs-api.js.map +1 -0
- package/dist/dashboard/web/app.js +21 -9
- 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 +128 -33
- 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 +42 -6
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/legacy-workflow-link.d.ts +2 -0
- package/dist/dashboard/web/legacy-workflow-link.d.ts.map +1 -0
- package/dist/dashboard/web/legacy-workflow-link.js +12 -0
- package/dist/dashboard/web/legacy-workflow-link.js.map +1 -0
- 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/v3-terminal.d.ts +11 -0
- package/dist/dashboard/web/v3-terminal.d.ts.map +1 -0
- package/dist/dashboard/web/v3-terminal.js +143 -0
- package/dist/dashboard/web/v3-terminal.js.map +1 -0
- package/dist/dashboard/web/v3.d.ts +2 -0
- package/dist/dashboard/web/v3.d.ts.map +1 -0
- package/dist/dashboard/web/v3.js +564 -0
- package/dist/dashboard/web/v3.js.map +1 -0
- 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/web/workflows.d.ts.map +1 -1
- package/dist/dashboard/web/workflows.js +12 -8
- package/dist/dashboard/web/workflows.js.map +1 -1
- package/dist/dashboard-web/app.js +834 -724
- package/dist/dashboard-web/apple-touch-icon.png +0 -0
- package/dist/dashboard-web/favicon.png +0 -0
- package/dist/dashboard-web/index.html +3 -1
- package/dist/dashboard-web/style.css +223 -0
- package/dist/dashboard.js +170 -10
- 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 +23 -3
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +23 -3
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +12 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +28 -0
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +3 -12
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/event-dispatcher.d.ts +2 -1
- package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
- package/dist/im/lark/event-dispatcher.js +20 -6
- 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 +29 -1
- package/dist/im/lark/message-parser.d.ts.map +1 -1
- package/dist/im/lark/message-parser.js +35 -1
- package/dist/im/lark/message-parser.js.map +1 -1
- package/dist/im/lark/v3-blocked-card-handler.d.ts +28 -0
- package/dist/im/lark/v3-blocked-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-blocked-card-handler.js +134 -0
- package/dist/im/lark/v3-blocked-card-handler.js.map +1 -0
- package/dist/im/lark/v3-blocked-card.d.ts +75 -0
- package/dist/im/lark/v3-blocked-card.d.ts.map +1 -0
- package/dist/im/lark/v3-blocked-card.js +206 -0
- package/dist/im/lark/v3-blocked-card.js.map +1 -0
- package/dist/im/lark/v3-gate-card-handler.d.ts +31 -0
- package/dist/im/lark/v3-gate-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-gate-card-handler.js +110 -0
- package/dist/im/lark/v3-gate-card-handler.js.map +1 -0
- package/dist/im/lark/v3-gate-card.d.ts +47 -0
- package/dist/im/lark/v3-gate-card.d.ts.map +1 -0
- package/dist/im/lark/v3-gate-card.js +119 -0
- package/dist/im/lark/v3-gate-card.js.map +1 -0
- package/dist/im/lark/v3-loop-grant-card-handler.d.ts +28 -0
- package/dist/im/lark/v3-loop-grant-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-loop-grant-card-handler.js +90 -0
- package/dist/im/lark/v3-loop-grant-card-handler.js.map +1 -0
- package/dist/im/lark/v3-loop-grant-card.d.ts +46 -0
- package/dist/im/lark/v3-loop-grant-card.d.ts.map +1 -0
- package/dist/im/lark/v3-loop-grant-card.js +119 -0
- package/dist/im/lark/v3-loop-grant-card.js.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.d.ts +29 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.d.ts.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.js +96 -0
- package/dist/im/lark/v3-revisit-grant-card-handler.js.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card.d.ts +52 -0
- package/dist/im/lark/v3-revisit-grant-card.d.ts.map +1 -0
- package/dist/im/lark/v3-revisit-grant-card.js +123 -0
- package/dist/im/lark/v3-revisit-grant-card.js.map +1 -0
- package/dist/im/lark/workflow-cards.d.ts.map +1 -1
- package/dist/im/lark/workflow-cards.js +5 -1
- package/dist/im/lark/workflow-cards.js.map +1 -1
- package/dist/im/lark/workflow-progress-card.d.ts +1 -1
- package/dist/im/lark/workflow-progress-card.js +1 -1
- package/dist/im/lark/workflow-slash-command.d.ts +28 -0
- package/dist/im/lark/workflow-slash-command.d.ts.map +1 -1
- package/dist/im/lark/workflow-slash-command.js +61 -5
- package/dist/im/lark/workflow-slash-command.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 +4 -0
- package/dist/services/card-prefs-store.d.ts.map +1 -1
- package/dist/services/card-prefs-store.js +19 -0
- package/dist/services/card-prefs-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/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/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 +33 -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 +208 -4
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/dist/types.d.ts.map +1 -1
- 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 +152 -8
- package/dist/worker.js.map +1 -1
- package/dist/workflows/definition.d.ts +80 -80
- package/dist/workflows/events/payloads.d.ts +4 -4
- package/dist/workflows/events/schema.d.ts +16 -16
- package/dist/workflows/v3/architect.d.ts +37 -0
- package/dist/workflows/v3/architect.d.ts.map +1 -0
- package/dist/workflows/v3/architect.js +226 -0
- package/dist/workflows/v3/architect.js.map +1 -0
- package/dist/workflows/v3/bot-resolve.d.ts +21 -0
- package/dist/workflows/v3/bot-resolve.d.ts.map +1 -0
- package/dist/workflows/v3/bot-resolve.js +48 -0
- package/dist/workflows/v3/bot-resolve.js.map +1 -0
- package/dist/workflows/v3/cli-run.d.ts +24 -0
- package/dist/workflows/v3/cli-run.d.ts.map +1 -0
- package/dist/workflows/v3/cli-run.js +265 -0
- package/dist/workflows/v3/cli-run.js.map +1 -0
- package/dist/workflows/v3/contract.d.ts +302 -0
- package/dist/workflows/v3/contract.d.ts.map +1 -0
- package/dist/workflows/v3/contract.js +91 -0
- package/dist/workflows/v3/contract.js.map +1 -0
- package/dist/workflows/v3/daemon-run.d.ts +311 -0
- package/dist/workflows/v3/daemon-run.d.ts.map +1 -0
- package/dist/workflows/v3/daemon-run.js +775 -0
- package/dist/workflows/v3/daemon-run.js.map +1 -0
- package/dist/workflows/v3/dag.d.ts +332 -0
- package/dist/workflows/v3/dag.d.ts.map +1 -0
- package/dist/workflows/v3/dag.js +1040 -0
- package/dist/workflows/v3/dag.js.map +1 -0
- package/dist/workflows/v3/ephemeral-pool.d.ts +29 -0
- package/dist/workflows/v3/ephemeral-pool.d.ts.map +1 -0
- package/dist/workflows/v3/ephemeral-pool.js +331 -0
- package/dist/workflows/v3/ephemeral-pool.js.map +1 -0
- package/dist/workflows/v3/grill-state.d.ts +92 -0
- package/dist/workflows/v3/grill-state.d.ts.map +1 -0
- package/dist/workflows/v3/grill-state.js +151 -0
- package/dist/workflows/v3/grill-state.js.map +1 -0
- package/dist/workflows/v3/host.d.ts +88 -0
- package/dist/workflows/v3/host.d.ts.map +1 -0
- package/dist/workflows/v3/host.js +349 -0
- package/dist/workflows/v3/host.js.map +1 -0
- package/dist/workflows/v3/human-gate.d.ts +85 -0
- package/dist/workflows/v3/human-gate.d.ts.map +1 -0
- package/dist/workflows/v3/human-gate.js +153 -0
- package/dist/workflows/v3/human-gate.js.map +1 -0
- package/dist/workflows/v3/journal.d.ts +207 -0
- package/dist/workflows/v3/journal.d.ts.map +1 -0
- package/dist/workflows/v3/journal.js +74 -0
- package/dist/workflows/v3/journal.js.map +1 -0
- package/dist/workflows/v3/manifest.d.ts +17 -0
- package/dist/workflows/v3/manifest.d.ts.map +1 -0
- package/dist/workflows/v3/manifest.js +266 -0
- package/dist/workflows/v3/manifest.js.map +1 -0
- package/dist/workflows/v3/ops-projection.d.ts +110 -0
- package/dist/workflows/v3/ops-projection.d.ts.map +1 -0
- package/dist/workflows/v3/ops-projection.js +0 -0
- package/dist/workflows/v3/ops-projection.js.map +1 -0
- package/dist/workflows/v3/orchestrator.d.ts +168 -0
- package/dist/workflows/v3/orchestrator.d.ts.map +1 -0
- package/dist/workflows/v3/orchestrator.js +417 -0
- package/dist/workflows/v3/orchestrator.js.map +1 -0
- package/dist/workflows/v3/runtime.d.ts +193 -0
- package/dist/workflows/v3/runtime.d.ts.map +1 -0
- package/dist/workflows/v3/runtime.js +1284 -0
- package/dist/workflows/v3/runtime.js.map +1 -0
- package/dist/workflows/v3/spec.d.ts +32 -0
- package/dist/workflows/v3/spec.d.ts.map +1 -0
- package/dist/workflows/v3/spec.js +173 -0
- package/dist/workflows/v3/spec.js.map +1 -0
- package/dist/workflows/v3/state.d.ts +74 -0
- package/dist/workflows/v3/state.d.ts.map +1 -0
- package/dist/workflows/v3/state.js +353 -0
- package/dist/workflows/v3/state.js.map +1 -0
- package/package.json +2 -2
- package/dist/dashboard/web/workflow-catalog.d.ts +0 -2
- package/dist/dashboard/web/workflow-catalog.d.ts.map +0 -1
- package/dist/dashboard/web/workflow-catalog.js +0 -323
- package/dist/dashboard/web/workflow-catalog.js.map +0 -1
|
@@ -0,0 +1,564 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3 workflow runs dashboard page — a DAG graph of a run's nodes (colored by
|
|
3
|
+
* status) with a per-node detail panel that hosts the terminal slot.
|
|
4
|
+
*
|
|
5
|
+
* Read-only: fetches `/api/v3/runs` (list) and `/api/v3/runs/:id` (a RunView —
|
|
6
|
+
* see ops-projection.ts). The RunView carries NO write token / no raw fs path
|
|
7
|
+
* (codex security review); the per-node terminal component (`v3-terminal.ts`,
|
|
8
|
+
* codex) only needs `(runId, node)`.
|
|
9
|
+
*
|
|
10
|
+
* Visual language ("flight recorder"): mono-led ids & round numbers, status
|
|
11
|
+
* conveyed through theme tokens (`--v3r-*` in style.css — works in light/dark/
|
|
12
|
+
* skins), composite loop nodes drawn as dashed capsules with an iteration-dot
|
|
13
|
+
* budget row, and a per-round timeline in the panel (instances + verdict per
|
|
14
|
+
* round, granted rounds marked). All motion is CSS-only (spin / marching-ants
|
|
15
|
+
* edges / breathe) and honors prefers-reduced-motion.
|
|
16
|
+
*
|
|
17
|
+
* Vanilla DOM + poll loop, mirroring `workflows.ts` (disposer clears the timer;
|
|
18
|
+
* skip polling while the tab is hidden; stop once the run is terminal).
|
|
19
|
+
*/
|
|
20
|
+
import { renderNodeTerminal } from './v3-terminal.js';
|
|
21
|
+
import { legacyWorkflowDetailHash } from './legacy-workflow-link.js';
|
|
22
|
+
const POLL_MS = 2000;
|
|
23
|
+
/** Single source of truth for "stop polling": only succeeded/failed are terminal.
|
|
24
|
+
* `running` and `blocked` (awaiting human retry/grant) can still change, so the
|
|
25
|
+
* detail page keeps polling. Used by both poll() and loop() so the stop/re-arm
|
|
26
|
+
* decisions can't drift apart. */
|
|
27
|
+
function isTerminalRunStatus(status) {
|
|
28
|
+
return status === 'succeeded' || status === 'failed';
|
|
29
|
+
}
|
|
30
|
+
const NODE_LABEL = {
|
|
31
|
+
pending: '待机',
|
|
32
|
+
gateWaiting: '等审批',
|
|
33
|
+
running: '运行中',
|
|
34
|
+
done: '完成',
|
|
35
|
+
skipped: '已跳过', // edge-activation: 分支未选中的中性终态(非失败)
|
|
36
|
+
cancelled: '已取消', // early-release loser:已无关并被收尸的中性终态
|
|
37
|
+
blocked: '受阻',
|
|
38
|
+
superseded: '已刷新', // 跨节点回溯把该 instance 刷新了,节点会重跑出新 instance
|
|
39
|
+
failed: '失败',
|
|
40
|
+
};
|
|
41
|
+
const DECISION_LABEL = {
|
|
42
|
+
exit: '✓ 通过',
|
|
43
|
+
continue: '↻ 继续返工',
|
|
44
|
+
exhausted: '⛔ 轮数耗尽',
|
|
45
|
+
};
|
|
46
|
+
function esc(s) {
|
|
47
|
+
return s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
48
|
+
}
|
|
49
|
+
/** Signature of a node's terminal-relevant inputs. The panel remounts the
|
|
50
|
+
* terminal slot only when this changes (live→closed, webPort appears, pty log
|
|
51
|
+
* shows up) — so a stable live iframe survives the 2s poll loop. */
|
|
52
|
+
function termSig(node) {
|
|
53
|
+
const wt = node.webTerminal;
|
|
54
|
+
return `${wt?.status ?? '-'}|${wt?.webPort ?? '-'}|${node.hasPtyLog ? '1' : '0'}`;
|
|
55
|
+
}
|
|
56
|
+
export function renderV3RunsPage(root) {
|
|
57
|
+
const m = location.hash.match(/^#\/workflows\/([^?#]+)/);
|
|
58
|
+
if (m)
|
|
59
|
+
return renderV3DetailPage(root, decodeURIComponent(m[1]));
|
|
60
|
+
return renderV3ListPage(root);
|
|
61
|
+
}
|
|
62
|
+
// ─── List ───────────────────────────────────────────────────────────────────
|
|
63
|
+
function renderV3ListPage(root) {
|
|
64
|
+
root.innerHTML = `
|
|
65
|
+
<div class="page-head">
|
|
66
|
+
<h1>工作流</h1>
|
|
67
|
+
<p class="muted">LLM 编排的 workflow 运行 — DAG 图 + 每节点终端</p>
|
|
68
|
+
</div>
|
|
69
|
+
<table class="data-table">
|
|
70
|
+
<thead><tr><th>Run</th><th>状态</th><th>节点数</th></tr></thead>
|
|
71
|
+
<tbody id="v3-tbody"></tbody>
|
|
72
|
+
</table>
|
|
73
|
+
<div id="v3-empty" class="muted" hidden style="padding:1rem">暂无工作流运行(用 <code>/workflow new</code> 发起一个)</div>`;
|
|
74
|
+
const tbody = root.querySelector('#v3-tbody');
|
|
75
|
+
const empty = root.querySelector('#v3-empty');
|
|
76
|
+
let timer = null;
|
|
77
|
+
let disposed = false;
|
|
78
|
+
async function poll() {
|
|
79
|
+
if (disposed || document.hidden)
|
|
80
|
+
return;
|
|
81
|
+
try {
|
|
82
|
+
const r = await fetch('/api/v3/runs');
|
|
83
|
+
const body = r.ok ? (await r.json()) : { runs: [] };
|
|
84
|
+
if (disposed)
|
|
85
|
+
return;
|
|
86
|
+
const runs = body.runs ?? [];
|
|
87
|
+
empty.hidden = runs.length > 0;
|
|
88
|
+
tbody.innerHTML = runs
|
|
89
|
+
.map((rn) => `<tr>
|
|
90
|
+
<td><a class="v3r-runid" href="#/workflows/${encodeURIComponent(rn.runId)}">${esc(rn.runId)}</a></td>
|
|
91
|
+
<td><span class="v3r-pill rs-${esc(rn.runStatus)}">${esc(rn.runStatus)}</span></td>
|
|
92
|
+
<td>${rn.nodeCount}</td>
|
|
93
|
+
</tr>`)
|
|
94
|
+
.join('');
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* transient fetch error — next tick retries */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function loop() {
|
|
101
|
+
if (disposed)
|
|
102
|
+
return;
|
|
103
|
+
void poll().then(() => { if (!disposed)
|
|
104
|
+
timer = window.setTimeout(loop, POLL_MS); });
|
|
105
|
+
}
|
|
106
|
+
loop();
|
|
107
|
+
return () => { disposed = true; if (timer !== null)
|
|
108
|
+
window.clearTimeout(timer); };
|
|
109
|
+
}
|
|
110
|
+
// ─── Detail (DAG graph + node panel) ─────────────────────────────────────────
|
|
111
|
+
/** Geometry: loop capsules are taller than plain nodes (budget-dot row). */
|
|
112
|
+
const NW = 168;
|
|
113
|
+
const PLAIN_H = 48;
|
|
114
|
+
const LOOP_H = 70;
|
|
115
|
+
const COL = 226;
|
|
116
|
+
const VGAP = 30;
|
|
117
|
+
const PAD = 26;
|
|
118
|
+
/** Budget-dot row caps at this many slots — beyond it the text fraction is the
|
|
119
|
+
* honest display (a 20-dot row would not fit the capsule anyway). */
|
|
120
|
+
const MAX_DOTS = 8;
|
|
121
|
+
function nodeH(n) {
|
|
122
|
+
return n.isLoop ? LOOP_H : PLAIN_H;
|
|
123
|
+
}
|
|
124
|
+
function renderV3DetailPage(root, runId) {
|
|
125
|
+
root.innerHTML = `
|
|
126
|
+
<div class="page-head">
|
|
127
|
+
<a href="#/workflows" class="btn-link">← 工作流</a>
|
|
128
|
+
<h1 class="v3r-title">${esc(runId)}</h1>
|
|
129
|
+
<span id="v3-runstatus" class="v3r-pill"></span>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="v3r-wrap">
|
|
132
|
+
<div class="v3r-graph-card">
|
|
133
|
+
<div id="v3-graph" class="v3r-graph"></div>
|
|
134
|
+
<div class="v3r-legend">
|
|
135
|
+
<span class="lg st-pending">待机</span>
|
|
136
|
+
<span class="lg st-running">运行中</span>
|
|
137
|
+
<span class="lg st-gateWaiting">等审批</span>
|
|
138
|
+
<span class="lg st-done">完成</span>
|
|
139
|
+
<span class="lg st-skipped">已跳过</span>
|
|
140
|
+
<span class="lg st-cancelled">已取消</span>
|
|
141
|
+
<span class="lg st-blocked">受阻</span>
|
|
142
|
+
<span class="lg st-failed">失败</span>
|
|
143
|
+
<span class="lg lg-loop">⟳ 循环容器</span>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
<div id="v3-node-panel" class="v3r-panel">
|
|
147
|
+
<p class="muted">点一个节点看详情与终端</p>
|
|
148
|
+
</div>
|
|
149
|
+
</div>`;
|
|
150
|
+
const graphEl = root.querySelector('#v3-graph');
|
|
151
|
+
const panelEl = root.querySelector('#v3-node-panel');
|
|
152
|
+
const runStatusEl = root.querySelector('#v3-runstatus');
|
|
153
|
+
let timer = null;
|
|
154
|
+
let disposed = false;
|
|
155
|
+
// One-shot guard for the legacy v2 fallback probe (see poll()'s 404 branch):
|
|
156
|
+
// already-sent v2 cards / bookmarks point at #/workflows/<v2-id>, which now
|
|
157
|
+
// belongs to v3; probe the v2 backend once and bounce them to #/legacy-workflow.
|
|
158
|
+
let triedV2Fallback = false;
|
|
159
|
+
let selected = null;
|
|
160
|
+
let lastView = null;
|
|
161
|
+
// Which node the panel SHELL is built for, and the signature of the terminal
|
|
162
|
+
// currently mounted in the slot. Both gate re-renders so a live iframe is
|
|
163
|
+
// NOT rebuilt on every 2s poll (codex caveat — only remount when the node
|
|
164
|
+
// selection or the terminal's own inputs actually change).
|
|
165
|
+
let panelSelected = null;
|
|
166
|
+
let renderedTermSig = null;
|
|
167
|
+
// Loop sub-selection: which body INSTANCE's terminal shows under the round
|
|
168
|
+
// timeline (the timeline itself stays visible — selecting an instance must
|
|
169
|
+
// not lose the loop context). Auto-follows the live frontier until the user
|
|
170
|
+
// pins a specific instance by clicking it.
|
|
171
|
+
let instSel = null;
|
|
172
|
+
let instPinned = false;
|
|
173
|
+
function select(id) {
|
|
174
|
+
selected = id;
|
|
175
|
+
instSel = null;
|
|
176
|
+
instPinned = false;
|
|
177
|
+
if (lastView)
|
|
178
|
+
renderGraph(lastView);
|
|
179
|
+
renderPanel();
|
|
180
|
+
}
|
|
181
|
+
function selectInstance(id) {
|
|
182
|
+
instSel = id;
|
|
183
|
+
instPinned = true;
|
|
184
|
+
renderPanel();
|
|
185
|
+
}
|
|
186
|
+
/** The instance whose terminal the loop panel shows: the pinned one if the
|
|
187
|
+
* user clicked, else the live frontier (a running instance, falling back to
|
|
188
|
+
* the most recently dispatched one). */
|
|
189
|
+
function resolveInstSel(insts) {
|
|
190
|
+
if (instPinned && instSel) {
|
|
191
|
+
const pinned = insts.find((x) => x.id === instSel);
|
|
192
|
+
if (pinned)
|
|
193
|
+
return pinned;
|
|
194
|
+
}
|
|
195
|
+
const running = insts.filter((x) => x.status === 'running');
|
|
196
|
+
return running[running.length - 1] ?? insts[insts.length - 1] ?? null;
|
|
197
|
+
}
|
|
198
|
+
/** Compact mini-dag for ONE round: the loop's body template gives the shape
|
|
199
|
+
* (so undispatched slots show as pending ghosts); dispatched instances fill
|
|
200
|
+
* in live status + click-to-view-terminal. */
|
|
201
|
+
function roundMiniDagSvg(tpl, instOf, activeInstId) {
|
|
202
|
+
const W2 = 104, H2 = 30, COL2 = 138, ROW2 = 42, PAD2 = 6;
|
|
203
|
+
// Depth layout over the TEMPLATE (authored body ids, body-internal deps).
|
|
204
|
+
const memo = new Map();
|
|
205
|
+
const depth = (id) => {
|
|
206
|
+
if (memo.has(id))
|
|
207
|
+
return memo.get(id);
|
|
208
|
+
memo.set(id, 0); // defensive against malformed cycles
|
|
209
|
+
const deps = tpl.find((t) => t.id === id)?.depends.filter((d) => tpl.some((t) => t.id === d)) ?? [];
|
|
210
|
+
const d = deps.length ? 1 + Math.max(...deps.map(depth)) : 0;
|
|
211
|
+
memo.set(id, d);
|
|
212
|
+
return d;
|
|
213
|
+
};
|
|
214
|
+
const rows = new Map();
|
|
215
|
+
const pos = new Map();
|
|
216
|
+
let maxRow = 0;
|
|
217
|
+
for (const t of tpl) {
|
|
218
|
+
const d = depth(t.id);
|
|
219
|
+
const r = rows.get(d) ?? 0;
|
|
220
|
+
rows.set(d, r + 1);
|
|
221
|
+
maxRow = Math.max(maxRow, r);
|
|
222
|
+
pos.set(t.id, { x: PAD2 + d * COL2, y: PAD2 + r * ROW2 });
|
|
223
|
+
}
|
|
224
|
+
const width = PAD2 * 2 + (memo.size ? Math.max(...memo.values()) * COL2 + W2 : 0);
|
|
225
|
+
const height = PAD2 * 2 + maxRow * ROW2 + H2;
|
|
226
|
+
const edges = [];
|
|
227
|
+
for (const t of tpl) {
|
|
228
|
+
const to = pos.get(t.id);
|
|
229
|
+
for (const dep of t.depends) {
|
|
230
|
+
const from = pos.get(dep);
|
|
231
|
+
if (!from)
|
|
232
|
+
continue;
|
|
233
|
+
const x1 = from.x + W2, y1 = from.y + H2 / 2, x2 = to.x, y2 = to.y + H2 / 2;
|
|
234
|
+
const mx = (x1 + x2) / 2;
|
|
235
|
+
const live = instOf.get(t.id)?.status === 'running' ? ' live' : '';
|
|
236
|
+
edges.push(`<path class="v3r-edge${live}" d="M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}" marker-end="url(#v3arrow-mini)"/>`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const boxes = tpl.map((t) => {
|
|
240
|
+
const p = pos.get(t.id);
|
|
241
|
+
const inst = instOf.get(t.id);
|
|
242
|
+
const status = inst?.status ?? 'pending';
|
|
243
|
+
const ghost = inst ? '' : ' ghost';
|
|
244
|
+
const sel = inst && inst.id === activeInstId ? ' sel' : '';
|
|
245
|
+
const dataSel = inst ? ` data-sel="${esc(inst.id)}"` : '';
|
|
246
|
+
return `<g class="v3r-node v3r-mini st-${status}${sel}${ghost}"${dataSel}${inst ? ` style="cursor:pointer"` : ''}>
|
|
247
|
+
<rect class="v3r-box" x="${p.x}" y="${p.y}" width="${W2}" height="${H2}" rx="8"/>
|
|
248
|
+
<circle class="v3r-mini-dot" cx="${p.x + 13}" cy="${p.y + H2 / 2}" r="3.4"/>
|
|
249
|
+
<text class="v3r-nid" x="${p.x + 24}" y="${p.y + H2 / 2 + 4}">${esc(trunc(t.id, 12))}</text>
|
|
250
|
+
</g>`;
|
|
251
|
+
});
|
|
252
|
+
return `<svg class="v3r-mini-svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
|
|
253
|
+
<defs><marker id="v3arrow-mini" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
|
|
254
|
+
<path d="M0,0 L10,5 L0,10 z" fill="var(--faint, #8b98aa)"/></marker></defs>
|
|
255
|
+
${edges.join('')}${boxes.join('')}
|
|
256
|
+
</svg>`;
|
|
257
|
+
}
|
|
258
|
+
/** Round timeline for a loop node: rounds stack vertically (a flight-recorder
|
|
259
|
+
* log, newest last), each round drawn as a real mini-dag of its body
|
|
260
|
+
* instances. Rounds past the authored maxIterations are tagged as
|
|
261
|
+
* human-granted. Selecting an instance keeps this timeline on screen — the
|
|
262
|
+
* terminal mounts BELOW it. */
|
|
263
|
+
function loopTimelineHtml(node, activeInstId) {
|
|
264
|
+
const ls = node.loopState;
|
|
265
|
+
if (!ls)
|
|
266
|
+
return '<p class="muted">loop 未开始</p>';
|
|
267
|
+
const maxIt = ls.maxIterations;
|
|
268
|
+
const budget = maxIt !== undefined ? maxIt + ls.granted : undefined;
|
|
269
|
+
const verdictOf = new Map(ls.decisions.map((d) => [d.iteration, d.decision]));
|
|
270
|
+
const insts = (lastView?.nodes ?? []).filter((x) => x.loop?.loopId === node.id);
|
|
271
|
+
const byIter = new Map();
|
|
272
|
+
for (const inst of insts) {
|
|
273
|
+
const k = inst.loop.iteration;
|
|
274
|
+
(byIter.get(k) ?? byIter.set(k, []).get(k)).push(inst);
|
|
275
|
+
}
|
|
276
|
+
const rounds = [...byIter.entries()]
|
|
277
|
+
.sort((a, b) => a[0] - b[0])
|
|
278
|
+
.map(([iter, ns]) => {
|
|
279
|
+
const verdict = verdictOf.get(iter);
|
|
280
|
+
const isCur = iter === ls.iteration;
|
|
281
|
+
const granted = maxIt !== undefined && iter > maxIt;
|
|
282
|
+
const verdictChip = verdict
|
|
283
|
+
? `<span class="v3r-verdict vd-${verdict}">${DECISION_LABEL[verdict]}</span>`
|
|
284
|
+
: isCur && node.status === 'running'
|
|
285
|
+
? '<span class="v3r-verdict vd-live">▶ 进行中</span>'
|
|
286
|
+
: '';
|
|
287
|
+
const instOf = new Map(ns.map((x) => [x.loop.bodyNodeId, x]));
|
|
288
|
+
return `<div class="v3r-round${isCur ? ' cur' : ''}${verdict ? ` vd-${verdict}` : ''}">
|
|
289
|
+
<div class="v3r-round-head">
|
|
290
|
+
<span class="rn">R${iter}</span>
|
|
291
|
+
${granted ? '<span class="v3r-granted-tag">➕ 追加轮</span>' : ''}
|
|
292
|
+
${verdictChip}
|
|
293
|
+
</div>
|
|
294
|
+
${roundMiniDagSvg(ls.bodyTemplate, instOf, activeInstId)}
|
|
295
|
+
</div>`;
|
|
296
|
+
})
|
|
297
|
+
.join('');
|
|
298
|
+
const meter = `<div class="v3r-loop-meter">
|
|
299
|
+
<span class="num">第 <b>${ls.iteration}</b>${budget !== undefined ? ` / <b>${budget}</b>` : ''} 轮</span>
|
|
300
|
+
${ls.granted > 0 ? `<span class="v3r-granted-tag">含人工追加 +${ls.granted}</span>` : ''}
|
|
301
|
+
${ls.lastDecision === 'exit' ? '<span class="v3r-verdict vd-exit">✓ 已收敛</span>' : ''}
|
|
302
|
+
</div>`;
|
|
303
|
+
const cta = node.status === 'blocked'
|
|
304
|
+
? `<div class="v3r-cta">⚠ 轮数耗尽,等人追加 — 飞书卡片点「➕ 追加 1 轮」,或 <code>botmux workflow grant ${esc(runId)}</code></div>`
|
|
305
|
+
: '';
|
|
306
|
+
return `<div class="v3r-loop-sec">${meter}<div class="v3r-rounds">${rounds}</div>${cta}</div>`;
|
|
307
|
+
}
|
|
308
|
+
function renderPanel() {
|
|
309
|
+
const node = lastView?.nodes.find((n) => n.id === selected) ?? null;
|
|
310
|
+
if (!node) {
|
|
311
|
+
panelEl.innerHTML = '<p class="muted">点一个节点看详情与终端</p>';
|
|
312
|
+
panelSelected = null;
|
|
313
|
+
renderedTermSig = null;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
// (Re)build the shell only when the selected node changes — keeps the
|
|
317
|
+
// terminal slot's DOM (and any live iframe) alive across polls.
|
|
318
|
+
if (panelSelected !== node.id) {
|
|
319
|
+
panelEl.innerHTML = `
|
|
320
|
+
<div id="v3-node-meta"></div>
|
|
321
|
+
<div id="v3-inst-strip"></div>
|
|
322
|
+
<div id="v3-term-slot" class="v3r-term-slot"></div>`;
|
|
323
|
+
panelSelected = node.id;
|
|
324
|
+
renderedTermSig = null; // force a terminal (re)mount for the new selection
|
|
325
|
+
}
|
|
326
|
+
// The terminal target: the node itself, or — for a loop — the selected /
|
|
327
|
+
// live-frontier body instance (timeline stays visible above it).
|
|
328
|
+
let termNode = node;
|
|
329
|
+
let activeInstId = null;
|
|
330
|
+
if (node.isLoop) {
|
|
331
|
+
const insts = (lastView?.nodes ?? []).filter((x) => x.loop?.loopId === node.id);
|
|
332
|
+
const inst = resolveInstSel(insts);
|
|
333
|
+
termNode = inst;
|
|
334
|
+
activeInstId = inst?.id ?? null;
|
|
335
|
+
instSel = activeInstId;
|
|
336
|
+
}
|
|
337
|
+
// Cheap meta updates in place every poll (no iframe involved).
|
|
338
|
+
const head = `<div class="v3r-panel-head">
|
|
339
|
+
${node.isLoop ? '<span class="v3r-loop-mark">⟳</span>' : ''}
|
|
340
|
+
<span class="v3r-nodeid">${esc(node.id)}</span>
|
|
341
|
+
<span class="v3r-pill st-${node.status}"><i class="dot"></i>${NODE_LABEL[node.status]}</span>
|
|
342
|
+
</div>`;
|
|
343
|
+
const errLine = node.errorClass
|
|
344
|
+
? `<p class="v3r-err">原因:${esc(node.errorClass)}${node.errorCode ? ` (${esc(node.errorCode)})` : ''}${node.status === 'blocked' && !node.isLoop ? ' — 飞书卡片或 <code>botmux workflow retry</code> 可重试' : ''}</p>`
|
|
345
|
+
: '';
|
|
346
|
+
panelEl.querySelector('#v3-node-meta').innerHTML = `
|
|
347
|
+
${head}
|
|
348
|
+
${node.goal ? `<p class="v3r-goal">${esc(node.goal)}</p>` : ''}
|
|
349
|
+
${node.depends.length ? `<p class="v3r-deps">依赖 ${node.depends.map((d) => `<span class="v3r-dep">${esc(d)}</span>`).join('')}</p>` : ''}
|
|
350
|
+
${errLine}${node.isLoop ? loopTimelineHtml(node, activeInstId) : ''}`;
|
|
351
|
+
// Instance strip: which body instance the terminal below belongs to.
|
|
352
|
+
panelEl.querySelector('#v3-inst-strip').innerHTML =
|
|
353
|
+
node.isLoop && termNode
|
|
354
|
+
? `<div class="v3r-inst-strip">
|
|
355
|
+
<span class="lbl">实例终端</span>
|
|
356
|
+
<span class="v3r-nodeid sm">${esc(termNode.id)}</span>
|
|
357
|
+
<span class="v3r-pill st-${termNode.status}"><i class="dot"></i>${NODE_LABEL[termNode.status]}</span>
|
|
358
|
+
<span class="muted">第 ${termNode.loop.iteration} 轮 · ${esc(termNode.loop.bodyNodeId)}${instPinned ? '' : ' · 自动跟随'}</span>
|
|
359
|
+
</div>`
|
|
360
|
+
: '';
|
|
361
|
+
for (const el of panelEl.querySelectorAll('#v3-node-meta [data-sel]')) {
|
|
362
|
+
el.addEventListener('click', (ev) => {
|
|
363
|
+
ev.stopPropagation();
|
|
364
|
+
selectInstance(el.getAttribute('data-sel'));
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
// Only (re)mount the terminal when its target or inputs changed (instance
|
|
368
|
+
// switch / status / webPort / hasPtyLog) — otherwise leave codex's live
|
|
369
|
+
// iframe untouched (no flicker).
|
|
370
|
+
const slot = panelEl.querySelector('#v3-term-slot');
|
|
371
|
+
if (!termNode) {
|
|
372
|
+
slot.innerHTML = '';
|
|
373
|
+
renderedTermSig = null;
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
const sig = `${termNode.id}|${termSig(termNode)}`;
|
|
377
|
+
if (sig !== renderedTermSig) {
|
|
378
|
+
renderNodeTerminal(slot, runId, termNode);
|
|
379
|
+
renderedTermSig = sig;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
/** SVG for one composite loop capsule (dashed outer frame + budget dots). */
|
|
383
|
+
function loopBoxSvg(n, x, y, sel) {
|
|
384
|
+
const ls = n.loopState;
|
|
385
|
+
const maxIt = ls?.maxIterations;
|
|
386
|
+
const budget = ls && maxIt !== undefined ? maxIt + ls.granted : undefined;
|
|
387
|
+
const statusLine = ls
|
|
388
|
+
? `${NODE_LABEL[n.status]} · 第${ls.iteration}${budget !== undefined ? `/${budget}` : ''}轮`
|
|
389
|
+
: `${NODE_LABEL[n.status]} · loop`;
|
|
390
|
+
// Budget dots: ● done round / ◉ current (pulses while running) / ○ todo;
|
|
391
|
+
// slots past the authored maxIterations render in accent (granted).
|
|
392
|
+
let dots = '';
|
|
393
|
+
if (ls && budget !== undefined && budget <= MAX_DOTS) {
|
|
394
|
+
const cy = y + LOOP_H - 13;
|
|
395
|
+
for (let k = 1; k <= budget; k++) {
|
|
396
|
+
const cx = x + 14 + (k - 1) * 15;
|
|
397
|
+
const cls = k < ls.iteration ? 'v3r-dot done'
|
|
398
|
+
: k === ls.iteration ? `v3r-dot cur${n.status === 'running' ? ' live' : ''}`
|
|
399
|
+
: 'v3r-dot todo';
|
|
400
|
+
const grant = maxIt !== undefined && k > maxIt ? ' grant' : '';
|
|
401
|
+
dots += `<circle class="${cls}${grant}" cx="${cx}" cy="${cy}" r="3.6"/>`;
|
|
402
|
+
}
|
|
403
|
+
if (ls.lastDecision === 'exit') {
|
|
404
|
+
dots += `<text class="v3r-dots-verdict ok" x="${x + 14 + budget * 15 + 4}" y="${cy + 4}">✓</text>`;
|
|
405
|
+
}
|
|
406
|
+
else if (ls.lastDecision === 'exhausted' && n.status === 'blocked') {
|
|
407
|
+
dots += `<text class="v3r-dots-verdict warn" x="${x + 14 + budget * 15 + 4}" y="${cy + 4}">⚠</text>`;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
return `<g class="v3r-node v3r-loopnode st-${n.status}${sel ? ' sel' : ''}" data-node="${esc(n.id)}">
|
|
411
|
+
<rect class="v3r-cap" x="${x - 4}" y="${y - 4}" width="${NW + 8}" height="${LOOP_H + 8}" rx="14"/>
|
|
412
|
+
<rect class="v3r-box" x="${x}" y="${y}" width="${NW}" height="${LOOP_H}" rx="10"/>
|
|
413
|
+
<text class="v3r-spin" x="${x + 14}" y="${y + 21}">⟳</text>
|
|
414
|
+
<text class="v3r-nid" x="${x + 30}" y="${y + 21}">${esc(trunc(n.id, 16))}</text>
|
|
415
|
+
<text class="v3r-nstatus" x="${x + 14}" y="${y + 39}">${esc(statusLine)}</text>
|
|
416
|
+
${dots}
|
|
417
|
+
</g>`;
|
|
418
|
+
}
|
|
419
|
+
function renderGraph(view) {
|
|
420
|
+
// Loop body INSTANCES stay out of the graph (the loop node represents
|
|
421
|
+
// them; the panel's iteration timeline reaches them). Group by the
|
|
422
|
+
// structured `loop` ref — never by parsing the id.
|
|
423
|
+
const graphNodes = view.nodes.filter((n) => !n.loop);
|
|
424
|
+
const depthOf = computeDepth(graphNodes);
|
|
425
|
+
const byDepth = new Map();
|
|
426
|
+
for (const n of graphNodes) {
|
|
427
|
+
const d = depthOf.get(n.id) ?? 0;
|
|
428
|
+
(byDepth.get(d) ?? byDepth.set(d, []).get(d)).push(n);
|
|
429
|
+
}
|
|
430
|
+
// Columns stack top-aligned; rows have per-node heights (loops are taller).
|
|
431
|
+
const pos = new Map();
|
|
432
|
+
let height = 0;
|
|
433
|
+
for (const [d, ns] of byDepth) {
|
|
434
|
+
let y = PAD;
|
|
435
|
+
for (const n of ns) {
|
|
436
|
+
pos.set(n.id, { x: PAD + d * COL, y });
|
|
437
|
+
y += nodeH(n) + VGAP;
|
|
438
|
+
}
|
|
439
|
+
height = Math.max(height, y - VGAP + PAD);
|
|
440
|
+
}
|
|
441
|
+
const width = PAD * 2 + (byDepth.size === 0 ? 0 : Math.max(...byDepth.keys()) * COL + NW);
|
|
442
|
+
const byId = new Map(graphNodes.map((n) => [n.id, n]));
|
|
443
|
+
const edges = [];
|
|
444
|
+
for (const n of graphNodes) {
|
|
445
|
+
const to = pos.get(n.id);
|
|
446
|
+
if (!to)
|
|
447
|
+
continue;
|
|
448
|
+
for (const dep of n.depends) {
|
|
449
|
+
const from = pos.get(dep);
|
|
450
|
+
const fromNode = byId.get(dep);
|
|
451
|
+
if (!from || !fromNode)
|
|
452
|
+
continue;
|
|
453
|
+
const x1 = from.x + NW, y1 = from.y + nodeH(fromNode) / 2;
|
|
454
|
+
const x2 = to.x, y2 = to.y + nodeH(n) / 2;
|
|
455
|
+
const mx = (x1 + x2) / 2;
|
|
456
|
+
// Edges INTO a live node animate (marching ants) — the eye finds the
|
|
457
|
+
// active frontier of the dag instantly.
|
|
458
|
+
const live = n.status === 'running' ? ' live' : '';
|
|
459
|
+
edges.push(`<path class="v3r-edge${live}" d="M${x1},${y1} C${mx},${y1} ${mx},${y2} ${x2},${y2}" marker-end="url(#v3arrow)"/>`);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
const boxes = graphNodes.map((n) => {
|
|
463
|
+
const p = pos.get(n.id);
|
|
464
|
+
const sel = n.id === selected;
|
|
465
|
+
if (n.isLoop)
|
|
466
|
+
return loopBoxSvg(n, p.x, p.y, sel);
|
|
467
|
+
return `<g class="v3r-node st-${n.status}${sel ? ' sel' : ''}" data-node="${esc(n.id)}">
|
|
468
|
+
<rect class="v3r-box" x="${p.x}" y="${p.y}" width="${NW}" height="${PLAIN_H}" rx="10"/>
|
|
469
|
+
<rect class="v3r-bar" x="${p.x}" y="${p.y + 8}" width="3" height="${PLAIN_H - 16}" rx="1.5"/>
|
|
470
|
+
<text class="v3r-nid" x="${p.x + 14}" y="${p.y + 20}">${esc(trunc(n.id, 18))}</text>
|
|
471
|
+
<text class="v3r-nstatus" x="${p.x + 14}" y="${p.y + 37}">${NODE_LABEL[n.status]}</text>
|
|
472
|
+
</g>`;
|
|
473
|
+
});
|
|
474
|
+
graphEl.innerHTML = `<svg width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" style="display:block">
|
|
475
|
+
<defs><marker id="v3arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
|
476
|
+
<path d="M0,0 L10,5 L0,10 z" fill="var(--faint, #8b98aa)"/></marker></defs>
|
|
477
|
+
${edges.join('')}${boxes.join('')}
|
|
478
|
+
</svg>`;
|
|
479
|
+
for (const g of graphEl.querySelectorAll('[data-node]')) {
|
|
480
|
+
g.addEventListener('click', () => select(g.getAttribute('data-node')));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
async function poll() {
|
|
484
|
+
if (disposed || document.hidden)
|
|
485
|
+
return;
|
|
486
|
+
try {
|
|
487
|
+
const r = await fetch(`/api/v3/runs/${encodeURIComponent(runId)}`);
|
|
488
|
+
if (!r.ok) {
|
|
489
|
+
// Legacy fallback: old v2 detail links are #/workflows/<v2-run-id>, a URL
|
|
490
|
+
// that now routes to v3 detail. If this id is actually a v2 run, bounce to
|
|
491
|
+
// the legacy page so already-sent cards/bookmarks still open. Probe once.
|
|
492
|
+
if (r.status === 404 && !triedV2Fallback) {
|
|
493
|
+
triedV2Fallback = true;
|
|
494
|
+
try {
|
|
495
|
+
const v2 = await fetch(`/api/workflows/runs/${encodeURIComponent(runId)}/snapshot`);
|
|
496
|
+
if (v2.ok && !disposed) {
|
|
497
|
+
window.location.replace(legacyWorkflowDetailHash(runId, location.hash.split('?')[1]));
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
catch { /* fall through to not-found */ }
|
|
502
|
+
}
|
|
503
|
+
if (!disposed)
|
|
504
|
+
runStatusEl.textContent = r.status === 404 ? 'not found' : `HTTP ${r.status}`;
|
|
505
|
+
return;
|
|
506
|
+
}
|
|
507
|
+
const view = (await r.json());
|
|
508
|
+
if (disposed)
|
|
509
|
+
return;
|
|
510
|
+
lastView = view;
|
|
511
|
+
runStatusEl.textContent = view.runStatus;
|
|
512
|
+
runStatusEl.className = `v3r-pill rs-${view.runStatus}`;
|
|
513
|
+
if (selected === null && view.nodes.length)
|
|
514
|
+
selected = view.nodes[0].id;
|
|
515
|
+
renderGraph(view);
|
|
516
|
+
renderPanel();
|
|
517
|
+
// Stop polling once the run is terminal (one final render already done).
|
|
518
|
+
if (isTerminalRunStatus(view.runStatus)) {
|
|
519
|
+
if (timer !== null)
|
|
520
|
+
window.clearTimeout(timer);
|
|
521
|
+
timer = null;
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
/* transient — next tick retries */
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
function loop() {
|
|
530
|
+
if (disposed)
|
|
531
|
+
return;
|
|
532
|
+
void poll().then(() => { if (!disposed && !isTerminalRunStatus(lastView?.runStatus))
|
|
533
|
+
timer = window.setTimeout(loop, POLL_MS); });
|
|
534
|
+
}
|
|
535
|
+
loop();
|
|
536
|
+
return () => { disposed = true; if (timer !== null)
|
|
537
|
+
window.clearTimeout(timer); };
|
|
538
|
+
}
|
|
539
|
+
/** Longest-path depth from a root (no-deps node) — the node's graph column. */
|
|
540
|
+
function computeDepth(nodes) {
|
|
541
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
542
|
+
const memo = new Map();
|
|
543
|
+
const visiting = new Set();
|
|
544
|
+
function depth(id) {
|
|
545
|
+
if (memo.has(id))
|
|
546
|
+
return memo.get(id);
|
|
547
|
+
if (visiting.has(id))
|
|
548
|
+
return 0; // defensive: dag is acyclic, but never loop
|
|
549
|
+
visiting.add(id);
|
|
550
|
+
const n = byId.get(id);
|
|
551
|
+
const deps = (n?.depends ?? []).filter((d) => byId.has(d));
|
|
552
|
+
const d = deps.length ? 1 + Math.max(...deps.map(depth)) : 0;
|
|
553
|
+
visiting.delete(id);
|
|
554
|
+
memo.set(id, d);
|
|
555
|
+
return d;
|
|
556
|
+
}
|
|
557
|
+
for (const n of nodes)
|
|
558
|
+
depth(n.id);
|
|
559
|
+
return memo;
|
|
560
|
+
}
|
|
561
|
+
function trunc(s, n) {
|
|
562
|
+
return s.length > n ? s.slice(0, n - 1) + '…' : s;
|
|
563
|
+
}
|
|
564
|
+
//# sourceMappingURL=v3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/dashboard/web/v3.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"whiteboards.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/whiteboards.ts"],"names":[],"mappings":"AAmKA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"whiteboards.d.ts","sourceRoot":"","sources":["../../../src/dashboard/web/whiteboards.ts"],"names":[],"mappings":"AAmKA,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA2B5E"}
|
|
@@ -138,7 +138,16 @@ function pageHtml(enabled, rows, groupNames, selected) {
|
|
|
138
138
|
</section>`;
|
|
139
139
|
}
|
|
140
140
|
export async function renderWhiteboardsPage(root) {
|
|
141
|
-
|
|
141
|
+
// 同步渲染一个稳定的宿主容器,三次 fetch 落地后只往这个「捕获到的子节点」里
|
|
142
|
+
// 写,绝不回写共享的 #root。这与其它异步页(overview/settings/skills)一致:
|
|
143
|
+
// 若用户在 fetch 期间切走,路由会用下一个页面替换 #root,本 host 随之脱离文档
|
|
144
|
+
// 树,迟到的写入落在已脱离的节点上(不可见),不会把白板的最终渲染状态盖到当前
|
|
145
|
+
// 页面上。快速「白板→他页→白板」也安全:每次渲染各自捕获一个 host,只有最新
|
|
146
|
+
// 那次的 host 仍挂在文档里,旧渲染只会写进自己那个已脱离的 host。
|
|
147
|
+
root.innerHTML = '<div data-whiteboards-host><p class="empty">Loading whiteboards…</p></div>';
|
|
148
|
+
const host = root.querySelector('[data-whiteboards-host]');
|
|
149
|
+
if (!host)
|
|
150
|
+
return;
|
|
142
151
|
const selectedId = decodeURIComponent((location.hash.match(/^#\/whiteboards\/([^/]+)/)?.[1] ?? '').trim());
|
|
143
152
|
try {
|
|
144
153
|
const [whiteboardsRes, groupsRes] = await Promise.all([
|
|
@@ -151,12 +160,12 @@ export async function renderWhiteboardsPage(root) {
|
|
|
151
160
|
const groupNames = await loadGroupNames(groupsRes);
|
|
152
161
|
const rows = Array.isArray(body.whiteboards) ? body.whiteboards : [];
|
|
153
162
|
const selected = selectedId ? await loadSelectedBoard(selectedId, rows) : undefined;
|
|
154
|
-
|
|
155
|
-
wireBoardSelection(
|
|
156
|
-
wireDelete(
|
|
163
|
+
host.innerHTML = pageHtml(body.enabled === true, rows, groupNames, selected);
|
|
164
|
+
wireBoardSelection(host, rows, groupNames);
|
|
165
|
+
wireDelete(host, selectedId);
|
|
157
166
|
}
|
|
158
167
|
catch (err) {
|
|
159
|
-
|
|
168
|
+
host.innerHTML = `<section class="page"><p class="hint-warn">加载白板失败:${escapeHtml(err?.message ?? String(err))}</p></section>`;
|
|
160
169
|
}
|
|
161
170
|
}
|
|
162
171
|
async function loadSelectedBoard(id, rows) {
|