botmux 2.33.0 → 2.33.1
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 +12 -1
- package/README.md +45 -1
- package/dist/adapters/cli/claude-code.d.ts.map +1 -1
- package/dist/adapters/cli/claude-code.js +11 -0
- package/dist/adapters/cli/claude-code.js.map +1 -1
- package/dist/cli/bots-list-output.d.ts +21 -0
- package/dist/cli/bots-list-output.d.ts.map +1 -0
- package/dist/cli/bots-list-output.js +23 -0
- package/dist/cli/bots-list-output.js.map +1 -0
- package/dist/cli/workflow.d.ts +13 -0
- package/dist/cli/workflow.d.ts.map +1 -0
- package/dist/cli/workflow.js +781 -0
- package/dist/cli/workflow.js.map +1 -0
- package/dist/cli.js +69 -14
- package/dist/cli.js.map +1 -1
- package/dist/core/command-handler.d.ts.map +1 -1
- package/dist/core/command-handler.js +211 -4
- package/dist/core/command-handler.js.map +1 -1
- package/dist/core/session-manager.d.ts +6 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +22 -12
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/worker-pool.d.ts +13 -0
- package/dist/core/worker-pool.d.ts.map +1 -1
- package/dist/core/worker-pool.js +100 -6
- package/dist/core/worker-pool.js.map +1 -1
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +884 -3
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard/auth.d.ts +36 -0
- package/dist/dashboard/auth.d.ts.map +1 -1
- package/dist/dashboard/auth.js +22 -0
- package/dist/dashboard/auth.js.map +1 -1
- package/dist/dashboard/web/app.js +20 -1
- package/dist/dashboard/web/app.js.map +1 -1
- package/dist/dashboard/web/i18n.d.ts.map +1 -1
- package/dist/dashboard/web/i18n.js +356 -0
- package/dist/dashboard/web/i18n.js.map +1 -1
- package/dist/dashboard/web/workflow-catalog.d.ts +2 -0
- package/dist/dashboard/web/workflow-catalog.d.ts.map +1 -0
- package/dist/dashboard/web/workflow-catalog.js +323 -0
- package/dist/dashboard/web/workflow-catalog.js.map +1 -0
- package/dist/dashboard/web/workflows.d.ts +2 -0
- package/dist/dashboard/web/workflows.d.ts.map +1 -0
- package/dist/dashboard/web/workflows.js +1618 -0
- package/dist/dashboard/web/workflows.js.map +1 -0
- package/dist/dashboard/workflow-api.d.ts +23 -0
- package/dist/dashboard/workflow-api.d.ts.map +1 -0
- package/dist/dashboard/workflow-api.js +463 -0
- package/dist/dashboard/workflow-api.js.map +1 -0
- package/dist/dashboard-web/app.js +494 -199
- package/dist/dashboard-web/index.html +1 -0
- package/dist/dashboard-web/style.css +160 -6
- package/dist/dashboard-web/terminal-replay.html +227 -0
- package/dist/dashboard.js +29 -12
- package/dist/dashboard.js.map +1 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +12 -0
- package/dist/i18n/en.js.map +1 -1
- package/dist/i18n/zh.d.ts.map +1 -1
- package/dist/i18n/zh.js +12 -0
- package/dist/i18n/zh.js.map +1 -1
- package/dist/im/lark/card-handler.d.ts +3 -0
- package/dist/im/lark/card-handler.d.ts.map +1 -1
- package/dist/im/lark/card-handler.js +27 -1
- package/dist/im/lark/card-handler.js.map +1 -1
- package/dist/im/lark/client.d.ts +19 -2
- package/dist/im/lark/client.d.ts.map +1 -1
- package/dist/im/lark/client.js +21 -2
- package/dist/im/lark/client.js.map +1 -1
- package/dist/im/lark/workflow-card-handler.d.ts +50 -0
- package/dist/im/lark/workflow-card-handler.d.ts.map +1 -0
- package/dist/im/lark/workflow-card-handler.js +152 -0
- package/dist/im/lark/workflow-card-handler.js.map +1 -0
- package/dist/im/lark/workflow-cards.d.ts +46 -0
- package/dist/im/lark/workflow-cards.d.ts.map +1 -0
- package/dist/im/lark/workflow-cards.js +226 -0
- package/dist/im/lark/workflow-cards.js.map +1 -0
- package/dist/im/lark/workflow-progress-card.d.ts +76 -0
- package/dist/im/lark/workflow-progress-card.d.ts.map +1 -0
- package/dist/im/lark/workflow-progress-card.js +279 -0
- package/dist/im/lark/workflow-progress-card.js.map +1 -0
- package/dist/im/lark/workflow-slash-command.d.ts +92 -0
- package/dist/im/lark/workflow-slash-command.d.ts.map +1 -0
- package/dist/im/lark/workflow-slash-command.js +185 -0
- package/dist/im/lark/workflow-slash-command.js.map +1 -0
- package/dist/services/group-creator.d.ts.map +1 -1
- package/dist/services/group-creator.js +17 -4
- package/dist/services/group-creator.js.map +1 -1
- package/dist/services/groups-store.d.ts +11 -0
- package/dist/services/groups-store.d.ts.map +1 -1
- package/dist/services/groups-store.js +26 -0
- package/dist/services/groups-store.js.map +1 -1
- package/dist/services/jsonl-cursor.d.ts +12 -0
- package/dist/services/jsonl-cursor.d.ts.map +1 -0
- package/dist/services/jsonl-cursor.js +45 -0
- package/dist/services/jsonl-cursor.js.map +1 -0
- package/dist/services/schedule-store.d.ts +35 -0
- package/dist/services/schedule-store.d.ts.map +1 -1
- package/dist/services/schedule-store.js +108 -1
- package/dist/services/schedule-store.js.map +1 -1
- package/dist/skills/definitions.d.ts.map +1 -1
- package/dist/skills/definitions.js +399 -0
- package/dist/skills/definitions.js.map +1 -1
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.d.ts.map +1 -1
- package/dist/utils/cli-usage-limit.js +4 -0
- package/dist/utils/cli-usage-limit.js.map +1 -1
- package/dist/worker.js +118 -14
- package/dist/worker.js.map +1 -1
- package/dist/workflows/attempt-resume.d.ts +114 -0
- package/dist/workflows/attempt-resume.d.ts.map +1 -0
- package/dist/workflows/attempt-resume.js +385 -0
- package/dist/workflows/attempt-resume.js.map +1 -0
- package/dist/workflows/attempt-terminal.d.ts +21 -0
- package/dist/workflows/attempt-terminal.d.ts.map +1 -0
- package/dist/workflows/attempt-terminal.js +7 -0
- package/dist/workflows/attempt-terminal.js.map +1 -0
- package/dist/workflows/blob.d.ts +27 -0
- package/dist/workflows/blob.d.ts.map +1 -0
- package/dist/workflows/blob.js +39 -0
- package/dist/workflows/blob.js.map +1 -0
- package/dist/workflows/cancel-run.d.ts +45 -0
- package/dist/workflows/cancel-run.d.ts.map +1 -0
- package/dist/workflows/cancel-run.js +99 -0
- package/dist/workflows/cancel-run.js.map +1 -0
- package/dist/workflows/cancel.d.ts +111 -0
- package/dist/workflows/cancel.d.ts.map +1 -0
- package/dist/workflows/cancel.js +120 -0
- package/dist/workflows/cancel.js.map +1 -0
- package/dist/workflows/catalog.d.ts +60 -0
- package/dist/workflows/catalog.d.ts.map +1 -0
- package/dist/workflows/catalog.js +119 -0
- package/dist/workflows/catalog.js.map +1 -0
- package/dist/workflows/cold-attach.d.ts +30 -0
- package/dist/workflows/cold-attach.d.ts.map +1 -0
- package/dist/workflows/cold-attach.js +40 -0
- package/dist/workflows/cold-attach.js.map +1 -0
- package/dist/workflows/cold-scan.d.ts +21 -0
- package/dist/workflows/cold-scan.d.ts.map +1 -0
- package/dist/workflows/cold-scan.js +70 -0
- package/dist/workflows/cold-scan.js.map +1 -0
- package/dist/workflows/daemon-spawn.d.ts +117 -0
- package/dist/workflows/daemon-spawn.d.ts.map +1 -0
- package/dist/workflows/daemon-spawn.js +551 -0
- package/dist/workflows/daemon-spawn.js.map +1 -0
- package/dist/workflows/definition.d.ts +1309 -0
- package/dist/workflows/definition.d.ts.map +1 -0
- package/dist/workflows/definition.js +334 -0
- package/dist/workflows/definition.js.map +1 -0
- package/dist/workflows/effect-input.d.ts +4 -0
- package/dist/workflows/effect-input.d.ts.map +1 -0
- package/dist/workflows/effect-input.js +18 -0
- package/dist/workflows/effect-input.js.map +1 -0
- package/dist/workflows/events/append.d.ts +77 -0
- package/dist/workflows/events/append.d.ts.map +1 -0
- package/dist/workflows/events/append.js +214 -0
- package/dist/workflows/events/append.js.map +1 -0
- package/dist/workflows/events/idempotency.d.ts +77 -0
- package/dist/workflows/events/idempotency.d.ts.map +1 -0
- package/dist/workflows/events/idempotency.js +116 -0
- package/dist/workflows/events/idempotency.js.map +1 -0
- package/dist/workflows/events/index.d.ts +7 -0
- package/dist/workflows/events/index.d.ts.map +1 -0
- package/dist/workflows/events/index.js +7 -0
- package/dist/workflows/events/index.js.map +1 -0
- package/dist/workflows/events/payloads.d.ts +917 -0
- package/dist/workflows/events/payloads.d.ts.map +1 -0
- package/dist/workflows/events/payloads.js +337 -0
- package/dist/workflows/events/payloads.js.map +1 -0
- package/dist/workflows/events/replay.d.ts +238 -0
- package/dist/workflows/events/replay.d.ts.map +1 -0
- package/dist/workflows/events/replay.js +608 -0
- package/dist/workflows/events/replay.js.map +1 -0
- package/dist/workflows/events/schema.d.ts +5242 -0
- package/dist/workflows/events/schema.d.ts.map +1 -0
- package/dist/workflows/events/schema.js +295 -0
- package/dist/workflows/events/schema.js.map +1 -0
- package/dist/workflows/events/types.d.ts +34 -0
- package/dist/workflows/events/types.d.ts.map +1 -0
- package/dist/workflows/events/types.js +2 -0
- package/dist/workflows/events/types.js.map +1 -0
- package/dist/workflows/fanout.d.ts +36 -0
- package/dist/workflows/fanout.d.ts.map +1 -0
- package/dist/workflows/fanout.js +114 -0
- package/dist/workflows/fanout.js.map +1 -0
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts +41 -0
- package/dist/workflows/hostExecutors/botmux-schedule.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/botmux-schedule.js +121 -0
- package/dist/workflows/hostExecutors/botmux-schedule.js.map +1 -0
- package/dist/workflows/hostExecutors/feishu-im.d.ts +12 -0
- package/dist/workflows/hostExecutors/feishu-im.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/feishu-im.js +49 -0
- package/dist/workflows/hostExecutors/feishu-im.js.map +1 -0
- package/dist/workflows/hostExecutors/feishu-reply.d.ts +24 -0
- package/dist/workflows/hostExecutors/feishu-reply.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/feishu-reply.js +88 -0
- package/dist/workflows/hostExecutors/feishu-reply.js.map +1 -0
- package/dist/workflows/hostExecutors/feishu-send.d.ts +23 -0
- package/dist/workflows/hostExecutors/feishu-send.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/feishu-send.js +124 -0
- package/dist/workflows/hostExecutors/feishu-send.js.map +1 -0
- package/dist/workflows/hostExecutors/index.d.ts +8 -0
- package/dist/workflows/hostExecutors/index.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/index.js +8 -0
- package/dist/workflows/hostExecutors/index.js.map +1 -0
- package/dist/workflows/hostExecutors/protocol.d.ts +42 -0
- package/dist/workflows/hostExecutors/protocol.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/protocol.js +181 -0
- package/dist/workflows/hostExecutors/protocol.js.map +1 -0
- package/dist/workflows/hostExecutors/registry.d.ts +10 -0
- package/dist/workflows/hostExecutors/registry.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/registry.js +36 -0
- package/dist/workflows/hostExecutors/registry.js.map +1 -0
- package/dist/workflows/hostExecutors/types.d.ts +78 -0
- package/dist/workflows/hostExecutors/types.d.ts.map +1 -0
- package/dist/workflows/hostExecutors/types.js +2 -0
- package/dist/workflows/hostExecutors/types.js.map +1 -0
- package/dist/workflows/loader.d.ts +16 -0
- package/dist/workflows/loader.d.ts.map +1 -0
- package/dist/workflows/loader.js +56 -0
- package/dist/workflows/loader.js.map +1 -0
- package/dist/workflows/loop.d.ts +50 -0
- package/dist/workflows/loop.d.ts.map +1 -0
- package/dist/workflows/loop.js +350 -0
- package/dist/workflows/loop.js.map +1 -0
- package/dist/workflows/ops-projection.d.ts +168 -0
- package/dist/workflows/ops-projection.d.ts.map +1 -0
- package/dist/workflows/ops-projection.js +707 -0
- package/dist/workflows/ops-projection.js.map +1 -0
- package/dist/workflows/orchestrator.d.ts +107 -0
- package/dist/workflows/orchestrator.d.ts.map +1 -0
- package/dist/workflows/orchestrator.js +197 -0
- package/dist/workflows/orchestrator.js.map +1 -0
- package/dist/workflows/output-binding.d.ts +70 -0
- package/dist/workflows/output-binding.d.ts.map +1 -0
- package/dist/workflows/output-binding.js +265 -0
- package/dist/workflows/output-binding.js.map +1 -0
- package/dist/workflows/params.d.ts +61 -0
- package/dist/workflows/params.d.ts.map +1 -0
- package/dist/workflows/params.js +195 -0
- package/dist/workflows/params.js.map +1 -0
- package/dist/workflows/resume.d.ts +263 -0
- package/dist/workflows/resume.d.ts.map +1 -0
- package/dist/workflows/resume.js +808 -0
- package/dist/workflows/resume.js.map +1 -0
- package/dist/workflows/run-id.d.ts +2 -0
- package/dist/workflows/run-id.d.ts.map +1 -0
- package/dist/workflows/run-id.js +7 -0
- package/dist/workflows/run-id.js.map +1 -0
- package/dist/workflows/run-init.d.ts +48 -0
- package/dist/workflows/run-init.d.ts.map +1 -0
- package/dist/workflows/run-init.js +99 -0
- package/dist/workflows/run-init.js.map +1 -0
- package/dist/workflows/runs-dir.d.ts +4 -0
- package/dist/workflows/runs-dir.d.ts.map +1 -0
- package/dist/workflows/runs-dir.js +15 -0
- package/dist/workflows/runs-dir.js.map +1 -0
- package/dist/workflows/runtime.d.ts +211 -0
- package/dist/workflows/runtime.d.ts.map +1 -0
- package/dist/workflows/runtime.js +594 -0
- package/dist/workflows/runtime.js.map +1 -0
- package/dist/workflows/spawn-bot.d.ts +165 -0
- package/dist/workflows/spawn-bot.d.ts.map +1 -0
- package/dist/workflows/spawn-bot.js +215 -0
- package/dist/workflows/spawn-bot.js.map +1 -0
- package/dist/workflows/system.d.ts +49 -0
- package/dist/workflows/system.d.ts.map +1 -0
- package/dist/workflows/system.js +48 -0
- package/dist/workflows/system.js.map +1 -0
- package/dist/workflows/trigger-run.d.ts +70 -0
- package/dist/workflows/trigger-run.d.ts.map +1 -0
- package/dist/workflows/trigger-run.js +88 -0
- package/dist/workflows/trigger-run.js.map +1 -0
- package/dist/workflows/wait.d.ts +120 -0
- package/dist/workflows/wait.d.ts.map +1 -0
- package/dist/workflows/wait.js +181 -0
- package/dist/workflows/wait.js.map +1 -0
- package/package.json +3 -3
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
<nav class="sidebar-nav" aria-label="Dashboard">
|
|
20
20
|
<a href="#/" data-route="overview" data-i18n="nav.overview">总览</a>
|
|
21
21
|
<a href="#/sessions" data-route="sessions" data-i18n="nav.sessions">会话</a>
|
|
22
|
+
<a href="#/workflows" data-route="workflows" data-i18n="nav.workflows">工作流(beta)</a>
|
|
22
23
|
<a href="#/groups" data-route="groups" data-i18n="nav.groups">群组</a>
|
|
23
24
|
<a href="#/schedules" data-route="schedules" data-i18n="nav.schedules">定时</a>
|
|
24
25
|
<a href="#/bot-defaults" data-route="bot-defaults" data-i18n="nav.botDefaults">默认 Bot</a>
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
--warning-soft: #fff7df;
|
|
20
20
|
--danger: #dc2626;
|
|
21
21
|
--danger-soft: #feecec;
|
|
22
|
+
--on-accent: #ffffff;
|
|
23
|
+
--on-danger: #ffffff;
|
|
24
|
+
--modal-shadow: 0 24px 80px rgba(0, 0, 0, 0.25);
|
|
25
|
+
--modal-backdrop: rgba(10, 14, 18, 0.48);
|
|
26
|
+
--qr-surface: #ffffff;
|
|
22
27
|
--shadow: 0 18px 44px rgba(15, 23, 42, 0.08);
|
|
23
28
|
--radius: 8px;
|
|
24
29
|
--sidebar-width: 236px;
|
|
@@ -46,6 +51,11 @@
|
|
|
46
51
|
--warning-soft: rgba(199, 142, 39, 0.16);
|
|
47
52
|
--danger: #ff7a7a;
|
|
48
53
|
--danger-soft: rgba(220, 38, 38, 0.16);
|
|
54
|
+
--on-accent: #111418;
|
|
55
|
+
--on-danger: #111418;
|
|
56
|
+
--modal-shadow: 0 24px 80px rgba(0, 0, 0, 0.52);
|
|
57
|
+
--modal-backdrop: rgba(0, 0, 0, 0.62);
|
|
58
|
+
--qr-surface: #ffffff;
|
|
49
59
|
--shadow: 0 18px 44px rgba(0, 0, 0, 0.28);
|
|
50
60
|
}
|
|
51
61
|
|
|
@@ -375,6 +385,20 @@ button,
|
|
|
375
385
|
font: inherit;
|
|
376
386
|
}
|
|
377
387
|
|
|
388
|
+
input::placeholder {
|
|
389
|
+
color: var(--faint);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
button:focus-visible,
|
|
393
|
+
.btn-link:focus-visible,
|
|
394
|
+
input:focus-visible,
|
|
395
|
+
select:focus-visible,
|
|
396
|
+
textarea:focus-visible,
|
|
397
|
+
a:focus-visible {
|
|
398
|
+
outline: 2px solid var(--accent);
|
|
399
|
+
outline-offset: 2px;
|
|
400
|
+
}
|
|
401
|
+
|
|
378
402
|
.filters input[type=search],
|
|
379
403
|
.filters input[type=text],
|
|
380
404
|
.filters select,
|
|
@@ -541,14 +565,14 @@ button:disabled {
|
|
|
541
565
|
|
|
542
566
|
button.contrast {
|
|
543
567
|
background: var(--danger);
|
|
544
|
-
color:
|
|
568
|
+
color: var(--on-danger);
|
|
545
569
|
border-color: var(--danger);
|
|
546
570
|
}
|
|
547
571
|
|
|
548
572
|
.btn-link.primary,
|
|
549
573
|
button.primary {
|
|
550
574
|
background: var(--accent);
|
|
551
|
-
color:
|
|
575
|
+
color: var(--on-accent);
|
|
552
576
|
border-color: var(--accent);
|
|
553
577
|
font-weight: 700;
|
|
554
578
|
}
|
|
@@ -590,6 +614,7 @@ button.primary {
|
|
|
590
614
|
:root[data-theme="dark"] .cli-aiden,
|
|
591
615
|
:root[data-theme="dark"] .cli-coco {
|
|
592
616
|
background: var(--surface-muted);
|
|
617
|
+
color: var(--fg);
|
|
593
618
|
border-color: var(--border);
|
|
594
619
|
}
|
|
595
620
|
|
|
@@ -609,11 +634,11 @@ dialog {
|
|
|
609
634
|
padding: 0;
|
|
610
635
|
background: var(--surface);
|
|
611
636
|
color: var(--fg);
|
|
612
|
-
box-shadow:
|
|
637
|
+
box-shadow: var(--modal-shadow);
|
|
613
638
|
}
|
|
614
639
|
|
|
615
640
|
dialog::backdrop {
|
|
616
|
-
background:
|
|
641
|
+
background: var(--modal-backdrop);
|
|
617
642
|
backdrop-filter: blur(3px);
|
|
618
643
|
}
|
|
619
644
|
|
|
@@ -773,9 +798,9 @@ input.row-select,
|
|
|
773
798
|
.qr-image {
|
|
774
799
|
width: min(260px, 74vw);
|
|
775
800
|
height: auto;
|
|
776
|
-
border: 10px solid
|
|
801
|
+
border: 10px solid var(--qr-surface);
|
|
777
802
|
border-radius: 8px;
|
|
778
|
-
background:
|
|
803
|
+
background: var(--qr-surface);
|
|
779
804
|
}
|
|
780
805
|
|
|
781
806
|
.onboarding-link {
|
|
@@ -931,3 +956,132 @@ input.row-select,
|
|
|
931
956
|
overflow-x: auto;
|
|
932
957
|
}
|
|
933
958
|
}
|
|
959
|
+
|
|
960
|
+
/* ─── Workflow runtime: list, detail, parallel timeline, IO cards ─── */
|
|
961
|
+
/* Status pills + dangling badges piggyback on the global semantic tokens
|
|
962
|
+
(--accent / --success / --warning / --danger + their *-soft pairs) so
|
|
963
|
+
light <-> dark switches just by flipping data-theme on <html>. */
|
|
964
|
+
.wf-status { padding: 0.05rem 0.4rem; border-radius: 999px; font-size: 11px; font-variant: small-caps; background: var(--bg-soft); color: var(--muted); }
|
|
965
|
+
.wf-status-pending { background: var(--bg-soft); color: var(--muted); }
|
|
966
|
+
.wf-status-running { background: var(--accent-soft); color: var(--accent); }
|
|
967
|
+
.wf-status-waiting { background: var(--warning-soft); color: var(--warning); }
|
|
968
|
+
.wf-status-succeeded { background: var(--success-soft); color: var(--success); }
|
|
969
|
+
.wf-status-failed { background: var(--danger-soft); color: var(--danger); }
|
|
970
|
+
.wf-status-cancelled { background: var(--bg-soft); color: var(--muted); }
|
|
971
|
+
.wf-dangling.has { color: var(--danger); font-weight: 600; }
|
|
972
|
+
.wf-dangling.none { color: var(--muted); }
|
|
973
|
+
.wf-run-error { margin-top: 0.25rem; max-width: 360px; white-space: normal; overflow-wrap: anywhere; color: var(--muted); font-size: 12px; }
|
|
974
|
+
|
|
975
|
+
.wf-subnav { display: flex; gap: 0.4rem; margin-bottom: 0.8rem; border-bottom: 1px solid var(--border); padding-bottom: 0; }
|
|
976
|
+
.wf-subnav a { padding: 0.35rem 0.7rem; color: var(--muted); text-decoration: none; border-bottom: 2px solid transparent; margin-bottom: -1px; }
|
|
977
|
+
.wf-subnav a.active { color: var(--fg); border-bottom-color: var(--fg); font-weight: 600; }
|
|
978
|
+
.wf-subnav a:hover { color: var(--fg); }
|
|
979
|
+
|
|
980
|
+
.wf-detail-head { display: flex; align-items: flex-start; gap: 0.8rem; margin-bottom: 0.8rem; }
|
|
981
|
+
.wf-detail-head h2 { margin: 0 0 0.15rem; font-size: 18px; line-height: 1.25; }
|
|
982
|
+
.wf-detail-head #wf-cancel-run { margin-left: auto; }
|
|
983
|
+
.wf-detail-head #wf-detail-refresh { white-space: nowrap; }
|
|
984
|
+
.wf-summary-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 0.6rem; margin-bottom: 0.8rem; }
|
|
985
|
+
.wf-summary-item { border: 1px solid var(--border); border-radius: 6px; padding: 0.5rem 0.6rem; min-width: 0; background: var(--surface); }
|
|
986
|
+
.wf-summary-item span { display: block; color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: 0; margin-bottom: 0.2rem; }
|
|
987
|
+
.wf-summary-item strong { display: block; font-weight: 600; overflow-wrap: anywhere; }
|
|
988
|
+
.wf-summary-item .wf-status { display: inline-block; }
|
|
989
|
+
.wf-panel { border: 1px solid var(--border); border-radius: 6px; padding: 0.7rem; margin-bottom: 0.8rem; background: var(--surface); }
|
|
990
|
+
.wf-panel-title { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.5rem; }
|
|
991
|
+
.wf-panel-title h3 { margin: 0; font-size: 15px; }
|
|
992
|
+
.wf-panel-title button { margin-left: auto; }
|
|
993
|
+
.wf-dangling-panel.has { border-color: var(--danger); background: var(--danger-soft); }
|
|
994
|
+
.wf-dangling-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 0.7rem; }
|
|
995
|
+
.wf-dangling-grid ul { margin: 0.25rem 0 0; padding-left: 1.1rem; }
|
|
996
|
+
.wf-dangling-grid li { margin: 0.15rem 0; overflow-wrap: anywhere; }
|
|
997
|
+
.wf-table-scroll { overflow-x: auto; }
|
|
998
|
+
.wf-timeline-scroll { max-height: 520px; overflow: auto; border: 1px solid var(--border); border-radius: 4px; }
|
|
999
|
+
.wf-timeline-scroll table th { position: sticky; top: 0; z-index: 1; }
|
|
1000
|
+
.wf-parallel-axis { display: flex; justify-content: space-between; color: var(--muted); font-size: 12px; margin: 0 0 0.35rem 170px; }
|
|
1001
|
+
.wf-parallel-list { display: grid; gap: 0.45rem; }
|
|
1002
|
+
.wf-parallel-row { display: grid; grid-template-columns: 160px minmax(220px, 1fr); gap: 0.6rem; align-items: center; }
|
|
1003
|
+
.wf-parallel-label { min-width: 0; }
|
|
1004
|
+
.wf-parallel-label code { display: block; overflow-wrap: anywhere; }
|
|
1005
|
+
.wf-parallel-label .muted { display: block; margin-top: 0.1rem; font-size: 11px; overflow-wrap: anywhere; }
|
|
1006
|
+
.wf-parallel-track { position: relative; height: 28px; border: 1px solid var(--border); border-radius: 4px; background: linear-gradient(90deg, var(--bg-soft), var(--surface)); overflow: hidden; }
|
|
1007
|
+
/* Coloured bars sit on top of any theme; foreground stays #fff because the
|
|
1008
|
+
bars carry the strong fill themselves and the contrast holds in both modes. */
|
|
1009
|
+
.wf-parallel-bar { position: absolute; top: 4px; bottom: 4px; min-width: 3px; border-radius: 3px; display: flex; align-items: center; padding: 0 0.35rem; color: #fff; font-size: 11px; line-height: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
1010
|
+
.wf-parallel-running, .wf-parallel-effectAttempting { background: var(--accent); }
|
|
1011
|
+
.wf-parallel-waiting { background: var(--warning); }
|
|
1012
|
+
.wf-parallel-pending { background: #8250df; }
|
|
1013
|
+
.wf-parallel-succeeded { background: var(--success); }
|
|
1014
|
+
.wf-parallel-failed, .wf-parallel-timedOut { background: var(--danger); }
|
|
1015
|
+
.wf-parallel-cancelled { background: var(--faint); }
|
|
1016
|
+
:root[data-theme="dark"] .wf-parallel-running,
|
|
1017
|
+
:root[data-theme="dark"] .wf-parallel-effectAttempting { color: var(--on-accent); }
|
|
1018
|
+
:root[data-theme="dark"] .wf-parallel-waiting,
|
|
1019
|
+
:root[data-theme="dark"] .wf-parallel-succeeded,
|
|
1020
|
+
:root[data-theme="dark"] .wf-parallel-cancelled { color: var(--bg); }
|
|
1021
|
+
:root[data-theme="dark"] .wf-parallel-failed,
|
|
1022
|
+
:root[data-theme="dark"] .wf-parallel-timedOut { color: var(--on-danger); }
|
|
1023
|
+
.wf-io-list { display: grid; gap: 0.7rem; }
|
|
1024
|
+
.wf-io-card { border: 1px solid var(--border); border-radius: 6px; padding: 0.65rem; background: var(--surface); }
|
|
1025
|
+
.wf-io-card.is-focused { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
|
|
1026
|
+
.wf-io-card header { display: flex; justify-content: space-between; gap: 0.8rem; align-items: flex-start; }
|
|
1027
|
+
.wf-io-card header > div:first-child { min-width: 0; }
|
|
1028
|
+
.wf-io-card header code, .wf-io-meta code { overflow-wrap: anywhere; }
|
|
1029
|
+
.wf-io-card header .muted { display: block; margin-top: 0.1rem; overflow-wrap: anywhere; }
|
|
1030
|
+
.wf-io-meta { margin-top: 0.35rem; color: var(--muted); font-size: 12px; overflow-wrap: anywhere; }
|
|
1031
|
+
.wf-approval-box { margin-top: 0.6rem; padding: 0.55rem; border: 1px solid var(--warning); border-radius: 4px; background: var(--warning-soft); }
|
|
1032
|
+
.wf-approval-box label span { display: block; margin-bottom: 0.25rem; color: var(--muted); font-size: 12px; }
|
|
1033
|
+
.wf-approval-comment { width: 100%; resize: vertical; min-height: 48px; padding: 0.35rem 0.45rem; border: 1px solid var(--border); border-radius: 4px; font: inherit; background: var(--surface); color: var(--fg); }
|
|
1034
|
+
.wf-approval-actions { display: flex; align-items: center; gap: 0.45rem; margin-top: 0.45rem; flex-wrap: wrap; }
|
|
1035
|
+
.wf-approval-status { margin: 0.5rem 0 0; }
|
|
1036
|
+
.wf-io-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 0.55rem; margin-top: 0.6rem; }
|
|
1037
|
+
.wf-io-block { border: 1px solid var(--border); border-radius: 4px; background: var(--surface-muted); min-width: 0; }
|
|
1038
|
+
.wf-io-block summary { cursor: pointer; padding: 0.4rem 0.5rem; font-weight: 600; }
|
|
1039
|
+
.wf-io-block summary .muted { margin-left: 0.3rem; font-weight: 400; }
|
|
1040
|
+
.wf-io-pre { margin: 0; padding: 0.5rem; max-height: 340px; overflow: auto; white-space: pre-wrap; overflow-wrap: anywhere; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; background: var(--surface); color: var(--fg); border-top: 1px solid var(--border); }
|
|
1041
|
+
.wf-io-empty { padding: 0.5rem; }
|
|
1042
|
+
.wf-terminal-block { grid-column: 1 / -1; }
|
|
1043
|
+
.wf-terminal-actions { padding: 0.45rem 0.5rem; border-top: 1px solid var(--border); background: var(--surface); display: flex; gap: 0.75rem; flex-wrap: wrap; align-items: center; }
|
|
1044
|
+
.wf-resume-status { padding: 0.35rem 0.5rem; border-top: 1px solid var(--border); }
|
|
1045
|
+
/* Terminal iframe content is itself a CLI surface (Tokyo Night theme), so
|
|
1046
|
+
the wrapper stays dark in both modes to avoid a white flash before xterm
|
|
1047
|
+
boots. */
|
|
1048
|
+
.wf-terminal-frame { display: block; width: 100%; height: 460px; border: 0; border-top: 1px solid var(--border); background: #0d1117; }
|
|
1049
|
+
.wf-error-msg { display: block; margin-top: 0.15rem; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 12px; }
|
|
1050
|
+
|
|
1051
|
+
/* Workflow catalog */
|
|
1052
|
+
.catalog-head, .catalog-detail-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.8rem; margin-bottom: 0.8rem; }
|
|
1053
|
+
.catalog-head h2, .catalog-detail-head h2 { margin: 0 0 0.15rem; font-size: 18px; line-height: 1.25; }
|
|
1054
|
+
.catalog-run-form { display: grid; gap: 0.65rem; max-width: 900px; }
|
|
1055
|
+
.catalog-run-form label span, .catalog-param header { display: block; margin-bottom: 0.25rem; color: var(--muted); font-size: 12px; }
|
|
1056
|
+
.catalog-run-form textarea, .catalog-run-form input[type=text] { width: 100%; padding: 0.4rem 0.5rem; border: 1px solid var(--border); border-radius: 4px; font: 12px/1.45 ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--fg); background: var(--surface); }
|
|
1057
|
+
.catalog-run-form textarea { resize: vertical; min-height: 150px; }
|
|
1058
|
+
.catalog-chat-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.65rem; }
|
|
1059
|
+
.catalog-param-errors { background: var(--warning-soft); border-left: 3px solid var(--warning); padding: 0.45rem 0.65rem; border-radius: 3px; }
|
|
1060
|
+
.catalog-param-errors ul { margin: 0.35rem 0 0; padding-left: 1.2rem; }
|
|
1061
|
+
.catalog-param-list { display: grid; gap: 0.55rem; }
|
|
1062
|
+
.catalog-param { border: 1px solid var(--border); border-radius: 4px; padding: 0.55rem; background: var(--surface-muted); }
|
|
1063
|
+
.catalog-param header { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; margin-bottom: 0.35rem; }
|
|
1064
|
+
|
|
1065
|
+
@media (max-width: 720px) {
|
|
1066
|
+
.wf-detail-head { flex-wrap: wrap; align-items: center; }
|
|
1067
|
+
.wf-detail-head > div { min-width: 0; flex: 1 1 220px; }
|
|
1068
|
+
.wf-detail-head h2 code { overflow-wrap: anywhere; }
|
|
1069
|
+
.wf-detail-head #wf-cancel-run { margin-left: 0; }
|
|
1070
|
+
.wf-summary-grid { grid-template-columns: 1fr 1fr; }
|
|
1071
|
+
.wf-panel { padding: 0.55rem; }
|
|
1072
|
+
.wf-parallel-axis { margin-left: 0; }
|
|
1073
|
+
.wf-parallel-row { grid-template-columns: 1fr; gap: 0.25rem; }
|
|
1074
|
+
.wf-io-card header { flex-direction: column; }
|
|
1075
|
+
.wf-io-grid { grid-template-columns: 1fr; }
|
|
1076
|
+
.wf-terminal-frame { height: 360px; }
|
|
1077
|
+
.wf-approval-actions button { flex: 1 1 120px; }
|
|
1078
|
+
.wf-timeline-scroll { max-height: 420px; }
|
|
1079
|
+
.catalog-head, .catalog-detail-head { flex-direction: column; }
|
|
1080
|
+
.catalog-chat-grid { grid-template-columns: 1fr; }
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
@media (max-width: 460px) {
|
|
1084
|
+
.wf-summary-grid { grid-template-columns: 1fr; }
|
|
1085
|
+
.wf-approval-actions { flex-direction: column; align-items: stretch; }
|
|
1086
|
+
.wf-approval-actions button { width: 100%; }
|
|
1087
|
+
}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<title>Workflow attempt terminal replay</title>
|
|
6
|
+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/css/xterm.css">
|
|
7
|
+
<style>
|
|
8
|
+
html, body {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
background: #0d1117;
|
|
12
|
+
color: #c9d1d9;
|
|
13
|
+
height: 100%;
|
|
14
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
15
|
+
font-size: 13px;
|
|
16
|
+
}
|
|
17
|
+
#chrome {
|
|
18
|
+
display: flex;
|
|
19
|
+
gap: 1rem;
|
|
20
|
+
align-items: center;
|
|
21
|
+
padding: 0.45rem 0.75rem;
|
|
22
|
+
border-bottom: 1px solid #30363d;
|
|
23
|
+
background: #161b22;
|
|
24
|
+
color: #8b949e;
|
|
25
|
+
font-size: 12px;
|
|
26
|
+
}
|
|
27
|
+
#chrome a {
|
|
28
|
+
color: #58a6ff;
|
|
29
|
+
text-decoration: none;
|
|
30
|
+
}
|
|
31
|
+
#chrome a:hover { text-decoration: underline; }
|
|
32
|
+
#info { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
33
|
+
#progress { color: #6e7681; margin-left: auto; }
|
|
34
|
+
#term {
|
|
35
|
+
height: calc(100% - 33px);
|
|
36
|
+
padding: 0.25rem;
|
|
37
|
+
box-sizing: border-box;
|
|
38
|
+
}
|
|
39
|
+
#term :focus { outline: none; }
|
|
40
|
+
.mode-toggle {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
border: 1px solid #30363d;
|
|
43
|
+
border-radius: 4px;
|
|
44
|
+
overflow: hidden;
|
|
45
|
+
}
|
|
46
|
+
.mode-toggle button {
|
|
47
|
+
background: transparent;
|
|
48
|
+
color: #8b949e;
|
|
49
|
+
border: 0;
|
|
50
|
+
padding: 0.2rem 0.6rem;
|
|
51
|
+
font: inherit;
|
|
52
|
+
cursor: pointer;
|
|
53
|
+
}
|
|
54
|
+
.mode-toggle button.active {
|
|
55
|
+
background: #1f6feb;
|
|
56
|
+
color: #ffffff;
|
|
57
|
+
}
|
|
58
|
+
.mode-toggle button:disabled {
|
|
59
|
+
color: #484f58;
|
|
60
|
+
cursor: not-allowed;
|
|
61
|
+
}
|
|
62
|
+
.mode-toggle button:not(.active):not(:disabled):hover {
|
|
63
|
+
background: #21262d;
|
|
64
|
+
color: #c9d1d9;
|
|
65
|
+
}
|
|
66
|
+
</style>
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
<div id="chrome">
|
|
70
|
+
<span id="info">Loading…</span>
|
|
71
|
+
<span class="mode-toggle" id="mode-toggle">
|
|
72
|
+
<button type="button" data-mode="pty" id="btn-pty">Terminal</button>
|
|
73
|
+
<button type="button" data-mode="diag" id="btn-diag">Diagnostic</button>
|
|
74
|
+
</span>
|
|
75
|
+
<span id="progress"></span>
|
|
76
|
+
<a id="download-link" href="#" target="_blank" rel="noopener" style="display:none">Download full log</a>
|
|
77
|
+
</div>
|
|
78
|
+
<div id="term"></div>
|
|
79
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5.5.0/lib/xterm.js"></script>
|
|
80
|
+
<script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0.10.0/lib/addon-fit.js"></script>
|
|
81
|
+
<script>
|
|
82
|
+
(function () {
|
|
83
|
+
'use strict';
|
|
84
|
+
var params = new URLSearchParams(window.location.search);
|
|
85
|
+
var runId = params.get('runId');
|
|
86
|
+
var activityId = params.get('activityId');
|
|
87
|
+
var attemptId = params.get('attemptId');
|
|
88
|
+
// hasPtyLog=1 → projection saw a non-empty pty.log; default mode is the
|
|
89
|
+
// raw PTY cinema. Without it the diag button is the only one enabled
|
|
90
|
+
// (older attempt → no pty.log on disk → /raw?stream=pty would 404).
|
|
91
|
+
var hasPty = params.get('hasPtyLog') === '1';
|
|
92
|
+
var info = document.getElementById('info');
|
|
93
|
+
var progress = document.getElementById('progress');
|
|
94
|
+
var dl = document.getElementById('download-link');
|
|
95
|
+
var btnPty = document.getElementById('btn-pty');
|
|
96
|
+
var btnDiag = document.getElementById('btn-diag');
|
|
97
|
+
if (!runId || !activityId || !attemptId) {
|
|
98
|
+
info.textContent = 'Missing runId/activityId/attemptId query params';
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
var Term = window.Terminal;
|
|
102
|
+
var FitAddon = window.FitAddon ? window.FitAddon.FitAddon : null;
|
|
103
|
+
if (!Term) {
|
|
104
|
+
info.textContent = 'xterm.js failed to load';
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
var term = new Term({
|
|
108
|
+
convertEol: true,
|
|
109
|
+
scrollback: 100000,
|
|
110
|
+
fontSize: 13,
|
|
111
|
+
theme: { background: '#0d1117', foreground: '#c9d1d9' },
|
|
112
|
+
disableStdin: true,
|
|
113
|
+
});
|
|
114
|
+
var fit = FitAddon ? new FitAddon() : null;
|
|
115
|
+
if (fit) term.loadAddon(fit);
|
|
116
|
+
term.open(document.getElementById('term'));
|
|
117
|
+
if (fit) {
|
|
118
|
+
try { fit.fit(); } catch (e) { /* initial fit can race layout */ }
|
|
119
|
+
window.addEventListener('resize', function () {
|
|
120
|
+
try { fit.fit(); } catch (e) { /* ignore */ }
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
var apiBase = '/api/workflows/runs/' + encodeURIComponent(runId)
|
|
125
|
+
+ '/attempts/' + encodeURIComponent(activityId)
|
|
126
|
+
+ '/' + encodeURIComponent(attemptId)
|
|
127
|
+
+ '/terminal-log/raw';
|
|
128
|
+
|
|
129
|
+
var activeFetchToken = 0;
|
|
130
|
+
|
|
131
|
+
function setMode(mode) {
|
|
132
|
+
btnPty.classList.toggle('active', mode === 'pty');
|
|
133
|
+
btnDiag.classList.toggle('active', mode === 'diag');
|
|
134
|
+
var query = mode === 'pty' ? '?stream=pty' : '';
|
|
135
|
+
dl.href = apiBase + (query ? query + '&download=1' : '?download=1');
|
|
136
|
+
dl.style.display = '';
|
|
137
|
+
// Bump token so any in-flight earlier fetch doesn't write into the
|
|
138
|
+
// terminal after we've already reset for the new mode.
|
|
139
|
+
var myToken = ++activeFetchToken;
|
|
140
|
+
term.reset();
|
|
141
|
+
progress.textContent = '';
|
|
142
|
+
info.textContent = 'Loading…';
|
|
143
|
+
fetchAndRender(query, myToken);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!hasPty) {
|
|
147
|
+
btnPty.disabled = true;
|
|
148
|
+
btnPty.title = 'This attempt has no raw PTY log (older run or empty session)';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
btnPty.addEventListener('click', function () {
|
|
152
|
+
if (btnPty.disabled || btnPty.classList.contains('active')) return;
|
|
153
|
+
setMode('pty');
|
|
154
|
+
});
|
|
155
|
+
btnDiag.addEventListener('click', function () {
|
|
156
|
+
if (btnDiag.classList.contains('active')) return;
|
|
157
|
+
setMode('diag');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Initial load — PTY when available, diag otherwise.
|
|
161
|
+
setMode(hasPty ? 'pty' : 'diag');
|
|
162
|
+
|
|
163
|
+
function fetchAndRender(query, token) {
|
|
164
|
+
fetch(apiBase + query).then(function (res) {
|
|
165
|
+
if (token !== activeFetchToken) return null;
|
|
166
|
+
if (!res.ok) {
|
|
167
|
+
if (res.status === 404) {
|
|
168
|
+
info.textContent = 'No log on disk for this attempt';
|
|
169
|
+
} else {
|
|
170
|
+
info.textContent = 'Failed to load log: HTTP ' + res.status;
|
|
171
|
+
}
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
var totalBytes = Number(res.headers.get('x-botmux-log-bytes') || 0);
|
|
175
|
+
var servedBytes = Number(res.headers.get('x-botmux-served-bytes') || 0);
|
|
176
|
+
var truncated = res.headers.get('x-botmux-truncated') === '1';
|
|
177
|
+
info.textContent = truncated
|
|
178
|
+
? 'Showing last ' + formatBytes(servedBytes) + ' of ' + formatBytes(totalBytes) + ' (truncated — Download for full)'
|
|
179
|
+
: 'Showing ' + formatBytes(totalBytes);
|
|
180
|
+
return res.text();
|
|
181
|
+
}).then(function (text) {
|
|
182
|
+
if (token !== activeFetchToken) return;
|
|
183
|
+
if (text == null) return;
|
|
184
|
+
if (text.length === 0) {
|
|
185
|
+
// header has already filled in totals; nothing to render
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Chunk writes through requestAnimationFrame so the browser stays
|
|
189
|
+
// responsive on multi-MB logs. 64 KB / frame ≈ a few seconds for
|
|
190
|
+
// a 10 MB tail at 60 fps.
|
|
191
|
+
var CHUNK = 64 * 1024;
|
|
192
|
+
var offset = 0;
|
|
193
|
+
var total = text.length;
|
|
194
|
+
function writeNext() {
|
|
195
|
+
// Abort the chunk loop if the user switched modes mid-render.
|
|
196
|
+
if (token !== activeFetchToken) return;
|
|
197
|
+
if (offset >= total) {
|
|
198
|
+
progress.textContent = '';
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
var slice = text.slice(offset, offset + CHUNK);
|
|
202
|
+
offset += slice.length;
|
|
203
|
+
term.write(slice);
|
|
204
|
+
progress.textContent = formatBytes(Math.min(offset, total)) + ' / ' + formatBytes(total);
|
|
205
|
+
if (typeof window.requestAnimationFrame === 'function') {
|
|
206
|
+
window.requestAnimationFrame(writeNext);
|
|
207
|
+
} else {
|
|
208
|
+
setTimeout(writeNext, 16);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
writeNext();
|
|
212
|
+
}).catch(function (err) {
|
|
213
|
+
if (token !== activeFetchToken) return;
|
|
214
|
+
info.textContent = 'Failed to load log: ' + (err && err.message ? err.message : err);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function formatBytes(n) {
|
|
219
|
+
if (!isFinite(n)) return '0 B';
|
|
220
|
+
if (n < 1024) return n + ' B';
|
|
221
|
+
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + ' KB';
|
|
222
|
+
return (n / (1024 * 1024)).toFixed(2) + ' MB';
|
|
223
|
+
}
|
|
224
|
+
})();
|
|
225
|
+
</script>
|
|
226
|
+
</body>
|
|
227
|
+
</html>
|
package/dist/dashboard.js
CHANGED
|
@@ -6,10 +6,12 @@ import { homedir } from 'node:os';
|
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
7
|
import { logger } from './utils/logger.js';
|
|
8
8
|
import { config } from './config.js';
|
|
9
|
-
import { generateToken, parseCookie, buildSetCookie, verifyHmac, } from './dashboard/auth.js';
|
|
9
|
+
import { generateToken, parseCookie, buildSetCookie, verifyHmac, decideDashboardAuth, } from './dashboard/auth.js';
|
|
10
10
|
import { DaemonRegistry } from './dashboard/registry.js';
|
|
11
11
|
import { Aggregator, subscribeDaemon } from './dashboard/aggregator.js';
|
|
12
12
|
import { pickCreatorForGroup } from './dashboard/operator-selector.js';
|
|
13
|
+
import { handleWorkflowApi, jsonRes } from './dashboard/workflow-api.js';
|
|
14
|
+
import { getRunsDir } from './workflows/runs-dir.js';
|
|
13
15
|
import { BotOnboardingManager } from './dashboard/bot-onboarding.js';
|
|
14
16
|
const SECRET_PATH = join(homedir(), '.botmux', '.dashboard-secret');
|
|
15
17
|
const BOTS_JSON_PATH = join(homedir(), '.botmux', 'bots.json');
|
|
@@ -131,10 +133,6 @@ function authedToken(req, url) {
|
|
|
131
133
|
return q;
|
|
132
134
|
return parseCookie(req.headers.cookie);
|
|
133
135
|
}
|
|
134
|
-
function jsonRes(res, status, body) {
|
|
135
|
-
res.writeHead(status, { 'content-type': 'application/json' });
|
|
136
|
-
res.end(JSON.stringify(body));
|
|
137
|
-
}
|
|
138
136
|
async function proxyToDaemon(larkAppId, daemonPath, init) {
|
|
139
137
|
const d = registry.getByAppId(larkAppId);
|
|
140
138
|
if (!d) {
|
|
@@ -196,18 +194,23 @@ const server = createServer(async (req, res) => {
|
|
|
196
194
|
const fullUrl = `http://${config.dashboard.externalHost}:${config.dashboard.port}/?t=${activeToken}`;
|
|
197
195
|
return jsonRes(res, 200, { url: fullUrl });
|
|
198
196
|
}
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
197
|
+
const presentedToken = authedToken(req, url);
|
|
198
|
+
const decision = decideDashboardAuth({
|
|
199
|
+
method: req.method ?? 'GET',
|
|
200
|
+
pathname: url.pathname,
|
|
201
|
+
hasTokenParam: url.searchParams.has('t'),
|
|
202
|
+
presentedToken,
|
|
203
|
+
activeToken: activeToken ?? '',
|
|
204
|
+
});
|
|
205
|
+
if (decision.kind === 'deny401') {
|
|
202
206
|
res.writeHead(401, { 'content-type': 'text/html; charset=utf-8' });
|
|
203
207
|
res.end('<h1>Token expired</h1><p>Run <code>botmux dashboard</code> to get a fresh URL.</p>');
|
|
204
208
|
return;
|
|
205
209
|
}
|
|
206
|
-
|
|
207
|
-
if (url.searchParams.has('t')) {
|
|
210
|
+
if (decision.kind === 'allow+set-cookie') {
|
|
208
211
|
res.writeHead(302, {
|
|
209
|
-
'set-cookie': buildSetCookie(
|
|
210
|
-
'location':
|
|
212
|
+
'set-cookie': buildSetCookie(decision.token),
|
|
213
|
+
'location': decision.redirectTo,
|
|
211
214
|
});
|
|
212
215
|
res.end();
|
|
213
216
|
return;
|
|
@@ -262,6 +265,20 @@ const server = createServer(async (req, res) => {
|
|
|
262
265
|
res.end(await upstream.text());
|
|
263
266
|
return;
|
|
264
267
|
}
|
|
268
|
+
// ─── Workflows (D0 read-only + D1 cancel mutation) ───────────────────────
|
|
269
|
+
//
|
|
270
|
+
// Dashboard reads runsDir directly (single-process; cross-daemon ownership
|
|
271
|
+
// doesn't matter for read-only). All readers in `ops-projection` are
|
|
272
|
+
// pure: no mkdir, no EventLog instantiation. Unknown / corrupt run → 404.
|
|
273
|
+
// Mutations are intentionally proxied to the owner daemon from
|
|
274
|
+
// chat-binding.larkAppId so only the daemon with live workflow runtime
|
|
275
|
+
// context writes the event log.
|
|
276
|
+
if (await handleWorkflowApi(req, res, url, {
|
|
277
|
+
runsDir: getRunsDir(),
|
|
278
|
+
proxyToDaemon,
|
|
279
|
+
})) {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
265
282
|
// ─── Groups (Phase B) ────────────────────────────────────────────────────
|
|
266
283
|
if (req.method === 'GET' && url.pathname === '/api/groups') {
|
|
267
284
|
// Fan out: each online daemon returns the chats its bot is in.
|