@adonis-agora/durable 0.26.0 → 0.28.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.
Files changed (193) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/spa/assets/index-97QpkU1f.css +1 -0
  3. package/dist/assets/spa/assets/index-Db5aPtlJ.js +16 -0
  4. package/dist/assets/spa/assets/index-Db5aPtlJ.js.map +1 -0
  5. package/dist/assets/spa/index.html +2 -2
  6. package/dist/commands/durable_worker.d.ts.map +1 -1
  7. package/dist/commands/durable_worker.js +1 -3
  8. package/dist/commands/durable_worker.js.map +1 -1
  9. package/dist/commands/main.d.ts.map +1 -1
  10. package/dist/commands/make_workflow.d.ts.map +1 -1
  11. package/dist/commands/make_workflow.js +1 -1
  12. package/dist/commands/make_workflow.js.map +1 -1
  13. package/dist/commands/retry.d.ts.map +1 -1
  14. package/dist/commands/retry.js +1 -1
  15. package/dist/commands/retry.js.map +1 -1
  16. package/dist/commands/runs.d.ts.map +1 -1
  17. package/dist/commands/runs.js +1 -1
  18. package/dist/commands/runs.js.map +1 -1
  19. package/dist/commands/work.d.ts.map +1 -1
  20. package/dist/commands/work.js.map +1 -1
  21. package/dist/providers/dashboard_provider.d.ts +3 -9
  22. package/dist/providers/dashboard_provider.d.ts.map +1 -1
  23. package/dist/providers/dashboard_provider.js +4 -19
  24. package/dist/providers/dashboard_provider.js.map +1 -1
  25. package/dist/providers/durable_provider.d.ts.map +1 -1
  26. package/dist/providers/durable_provider.js +2 -2
  27. package/dist/providers/durable_provider.js.map +1 -1
  28. package/dist/src/admission-redis/redis-admission-backend.d.ts.map +1 -1
  29. package/dist/src/admission.d.ts.map +1 -1
  30. package/dist/src/admission.js.map +1 -1
  31. package/dist/src/admissions/factory.d.ts.map +1 -1
  32. package/dist/src/admissions/factory.js.map +1 -1
  33. package/dist/src/base-workflow.d.ts +1 -1
  34. package/dist/src/base-workflow.d.ts.map +1 -1
  35. package/dist/src/base-workflow.js.map +1 -1
  36. package/dist/src/codec-state-store.d.ts.map +1 -1
  37. package/dist/src/codec-state-store.js.map +1 -1
  38. package/dist/src/commands/index.d.ts +2 -2
  39. package/dist/src/commands/index.d.ts.map +1 -1
  40. package/dist/src/commands/index.js +1 -1
  41. package/dist/src/commands/index.js.map +1 -1
  42. package/dist/src/commands/runs.js.map +1 -1
  43. package/dist/src/commands/worker.d.ts.map +1 -1
  44. package/dist/src/commands/worker.js.map +1 -1
  45. package/dist/src/control-plane-redis/redis-control-plane.d.ts.map +1 -1
  46. package/dist/src/control-planes/factory.d.ts.map +1 -1
  47. package/dist/src/control-planes/factory.js.map +1 -1
  48. package/dist/src/dashboard/compat-view.d.ts +1 -1
  49. package/dist/src/dashboard/compat-view.d.ts.map +1 -1
  50. package/dist/src/dashboard/compat-view.js +1 -1
  51. package/dist/src/dashboard/compat-view.js.map +1 -1
  52. package/dist/src/dashboard/define_config.js.map +1 -1
  53. package/dist/src/dashboard/diagnostics-recorder.d.ts.map +1 -1
  54. package/dist/src/dashboard/diagnostics-recorder.js.map +1 -1
  55. package/dist/src/dashboard/handlers.d.ts +3 -3
  56. package/dist/src/dashboard/handlers.d.ts.map +1 -1
  57. package/dist/src/dashboard/handlers.js +2 -2
  58. package/dist/src/dashboard/index.d.ts +13 -14
  59. package/dist/src/dashboard/index.d.ts.map +1 -1
  60. package/dist/src/dashboard/index.js +8 -9
  61. package/dist/src/dashboard/index.js.map +1 -1
  62. package/dist/src/dashboard/login_page.d.ts +1 -1
  63. package/dist/src/dashboard/login_page.js +1 -1
  64. package/dist/src/dashboard/spa.d.ts +21 -4
  65. package/dist/src/dashboard/spa.d.ts.map +1 -1
  66. package/dist/src/dashboard/spa.js +19 -5
  67. package/dist/src/dashboard/spa.js.map +1 -1
  68. package/dist/src/define_config.d.ts +6 -6
  69. package/dist/src/define_config.d.ts.map +1 -1
  70. package/dist/src/define_config.js +1 -1
  71. package/dist/src/define_config.js.map +1 -1
  72. package/dist/src/dispatch-routing.d.ts.map +1 -1
  73. package/dist/src/dispatch-routing.js.map +1 -1
  74. package/dist/src/engine.d.ts.map +1 -1
  75. package/dist/src/engine.js +2 -2
  76. package/dist/src/engine.js.map +1 -1
  77. package/dist/src/entities.d.ts.map +1 -1
  78. package/dist/src/entities.js.map +1 -1
  79. package/dist/src/errors.d.ts.map +1 -1
  80. package/dist/src/errors.js.map +1 -1
  81. package/dist/src/event-accumulators.d.ts.map +1 -1
  82. package/dist/src/event-accumulators.js.map +1 -1
  83. package/dist/src/event-trigger-bridge.d.ts.map +1 -1
  84. package/dist/src/event-trigger-bridge.js.map +1 -1
  85. package/dist/src/handshake/descriptor.js.map +1 -1
  86. package/dist/src/handshake/negotiate.d.ts.map +1 -1
  87. package/dist/src/handshake/negotiate.js.map +1 -1
  88. package/dist/src/handshake/routing.d.ts.map +1 -1
  89. package/dist/src/handshake/routing.js.map +1 -1
  90. package/dist/src/hooks/steps.d.ts.map +1 -1
  91. package/dist/src/hooks/steps.js.map +1 -1
  92. package/dist/src/hooks/workflows.d.ts.map +1 -1
  93. package/dist/src/hooks/workflows.js.map +1 -1
  94. package/dist/src/index.d.ts +46 -46
  95. package/dist/src/index.d.ts.map +1 -1
  96. package/dist/src/index.js +50 -50
  97. package/dist/src/index.js.map +1 -1
  98. package/dist/src/otel/durable-otel.d.ts.map +1 -1
  99. package/dist/src/otel/durable-otel.js +1 -1
  100. package/dist/src/otel/durable-otel.js.map +1 -1
  101. package/dist/src/otel/index.d.ts +2 -2
  102. package/dist/src/otel/index.d.ts.map +1 -1
  103. package/dist/src/otel/index.js +1 -1
  104. package/dist/src/otel/index.js.map +1 -1
  105. package/dist/src/otel/traceparent.js.map +1 -1
  106. package/dist/src/pollers.d.ts.map +1 -1
  107. package/dist/src/protocol.d.ts.map +1 -1
  108. package/dist/src/queue.d.ts.map +1 -1
  109. package/dist/src/queue.js.map +1 -1
  110. package/dist/src/remote-workflow-executor.d.ts.map +1 -1
  111. package/dist/src/remote-workflow-executor.js.map +1 -1
  112. package/dist/src/run-gateway/proxy-run-gateway.d.ts.map +1 -1
  113. package/dist/src/run-gateway/run-request-responder.d.ts.map +1 -1
  114. package/dist/src/run-gateway/run-request-responder.js.map +1 -1
  115. package/dist/src/run-gateway/store-run-gateway.d.ts.map +1 -1
  116. package/dist/src/run-gateway/store-run-gateway.js.map +1 -1
  117. package/dist/src/scheduler.d.ts +28 -0
  118. package/dist/src/scheduler.d.ts.map +1 -1
  119. package/dist/src/scheduler.js +43 -7
  120. package/dist/src/scheduler.js.map +1 -1
  121. package/dist/src/search-attributes.d.ts +1 -2
  122. package/dist/src/search-attributes.d.ts.map +1 -1
  123. package/dist/src/search-attributes.js.map +1 -1
  124. package/dist/src/services/booted_app.js.map +1 -1
  125. package/dist/src/singleton-gate.d.ts.map +1 -1
  126. package/dist/src/singleton-gate.js.map +1 -1
  127. package/dist/src/stores/factory.d.ts.map +1 -1
  128. package/dist/src/stores/factory.js.map +1 -1
  129. package/dist/src/stores/lucid-schema.d.ts +6 -6
  130. package/dist/src/stores/lucid-schema.d.ts.map +1 -1
  131. package/dist/src/stores/lucid-schema.js.map +1 -1
  132. package/dist/src/stores/lucid.d.ts.map +1 -1
  133. package/dist/src/stores/lucid.js +1 -1
  134. package/dist/src/stores/lucid.js.map +1 -1
  135. package/dist/src/telescope/dashboard.js.map +1 -1
  136. package/dist/src/telescope/extension.js.map +1 -1
  137. package/dist/src/telescope/index.d.ts +1 -1
  138. package/dist/src/telescope/index.d.ts.map +1 -1
  139. package/dist/src/telescope/index.js +1 -1
  140. package/dist/src/telescope/index.js.map +1 -1
  141. package/dist/src/testing/in-memory-state-store.d.ts.map +1 -1
  142. package/dist/src/testing/in-memory-state-store.js.map +1 -1
  143. package/dist/src/testing/in-memory-transport.d.ts.map +1 -1
  144. package/dist/src/testing/in-memory-transport.js.map +1 -1
  145. package/dist/src/testing-kit/harness.d.ts.map +1 -1
  146. package/dist/src/testing-kit/harness.js.map +1 -1
  147. package/dist/src/testing-kit/state-store-conformance.d.ts.map +1 -1
  148. package/dist/src/testing-kit/state-store-conformance.js.map +1 -1
  149. package/dist/src/testing-kit/steps.js.map +1 -1
  150. package/dist/src/transport-pool.d.ts.map +1 -1
  151. package/dist/src/transport-pool.js.map +1 -1
  152. package/dist/src/transports/bullmq/bullmq-transport.d.ts.map +1 -1
  153. package/dist/src/transports/bullmq/bullmq-transport.js +1 -1
  154. package/dist/src/transports/bullmq/bullmq-transport.js.map +1 -1
  155. package/dist/src/transports/bullmq/index.d.ts +1 -1
  156. package/dist/src/transports/bullmq/index.d.ts.map +1 -1
  157. package/dist/src/transports/bullmq/index.js.map +1 -1
  158. package/dist/src/transports/bullmq/serialization.js.map +1 -1
  159. package/dist/src/transports/db-schema.d.ts +4 -4
  160. package/dist/src/transports/db-schema.d.ts.map +1 -1
  161. package/dist/src/transports/db.d.ts.map +1 -1
  162. package/dist/src/transports/db.js +1 -1
  163. package/dist/src/transports/db.js.map +1 -1
  164. package/dist/src/transports/event-emitter.d.ts.map +1 -1
  165. package/dist/src/transports/event-emitter.js.map +1 -1
  166. package/dist/src/transports/factory.d.ts.map +1 -1
  167. package/dist/src/transports/factory.js.map +1 -1
  168. package/dist/src/transports/queue-mock-adapter.d.ts.map +1 -1
  169. package/dist/src/transports/queue.d.ts.map +1 -1
  170. package/dist/src/transports/queue.js.map +1 -1
  171. package/dist/src/worker-runtime/index.d.ts +6 -6
  172. package/dist/src/worker-runtime/index.d.ts.map +1 -1
  173. package/dist/src/worker-runtime/index.js +6 -6
  174. package/dist/src/worker-runtime/index.js.map +1 -1
  175. package/dist/src/worker-runtime/registry.d.ts.map +1 -1
  176. package/dist/src/worker-runtime/registry.js.map +1 -1
  177. package/dist/src/worker-runtime/worker-runtime.d.ts +2 -2
  178. package/dist/src/worker-runtime/worker-runtime.d.ts.map +1 -1
  179. package/dist/src/worker-runtime/worker-runtime.js +1 -1
  180. package/dist/src/worker-runtime/worker-runtime.js.map +1 -1
  181. package/dist/src/workflow-discovery.js.map +1 -1
  182. package/dist/src/workflow-turn.d.ts.map +1 -1
  183. package/dist/src/workflow-turn.js.map +1 -1
  184. package/dist/stubs/main.js.map +1 -1
  185. package/package.json +24 -23
  186. package/dist/assets/dashboard.html +0 -559
  187. package/dist/assets/spa/assets/index-ClD9G7pa.js +0 -67
  188. package/dist/assets/spa/assets/index-ClD9G7pa.js.map +0 -1
  189. package/dist/assets/spa/assets/index-Ds6dCWMa.css +0 -1
  190. package/dist/src/dashboard/html.d.ts +0 -7
  191. package/dist/src/dashboard/html.d.ts.map +0 -1
  192. package/dist/src/dashboard/html.js +0 -24
  193. package/dist/src/dashboard/html.js.map +0 -1
@@ -1,559 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>Durable · Workflow Runs</title>
7
- <style>
8
- :root {
9
- --bg: #0d1117;
10
- --panel: #161b22;
11
- --panel-2: #1c2128;
12
- --border: #30363d;
13
- --text: #e6edf3;
14
- --muted: #8b949e;
15
- --accent: #58a6ff;
16
- --green: #3fb950;
17
- --red: #f85149;
18
- --amber: #d29922;
19
- --purple: #bc8cff;
20
- --grey: #6e7681;
21
- }
22
- * { box-sizing: border-box; }
23
- html, body {
24
- margin: 0;
25
- background: var(--bg);
26
- color: var(--text);
27
- font: 14px/1.5 ui-sans-serif, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
28
- }
29
- a { color: var(--accent); text-decoration: none; }
30
- a:hover { text-decoration: underline; }
31
- code, .mono {
32
- font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
33
- font-size: 12.5px;
34
- }
35
- header {
36
- display: flex;
37
- align-items: center;
38
- gap: 16px;
39
- padding: 14px 22px;
40
- border-bottom: 1px solid var(--border);
41
- background: var(--panel);
42
- position: sticky;
43
- top: 0;
44
- z-index: 10;
45
- }
46
- header h1 {
47
- font-size: 16px;
48
- margin: 0;
49
- font-weight: 600;
50
- letter-spacing: 0.3px;
51
- }
52
- header .dot { color: var(--accent); }
53
- .spacer { flex: 1; }
54
- main { padding: 22px; max-width: 1180px; margin: 0 auto; }
55
- .controls {
56
- display: flex;
57
- gap: 10px;
58
- align-items: center;
59
- flex-wrap: wrap;
60
- margin-bottom: 16px;
61
- }
62
- select, input, button {
63
- background: var(--panel-2);
64
- color: var(--text);
65
- border: 1px solid var(--border);
66
- border-radius: 6px;
67
- padding: 7px 11px;
68
- font-size: 13px;
69
- }
70
- button { cursor: pointer; transition: border-color 0.15s, background 0.15s; }
71
- button:hover { border-color: var(--accent); }
72
- button:disabled { opacity: 0.5; cursor: not-allowed; }
73
- button.primary { background: #1f6feb; border-color: #1f6feb; color: #fff; }
74
- button.primary:hover { background: #2a7fff; }
75
- button.danger:hover { border-color: var(--red); color: var(--red); }
76
- table { width: 100%; border-collapse: collapse; }
77
- th, td {
78
- text-align: left;
79
- padding: 9px 12px;
80
- border-bottom: 1px solid var(--border);
81
- vertical-align: middle;
82
- }
83
- th {
84
- color: var(--muted);
85
- font-weight: 500;
86
- font-size: 12px;
87
- text-transform: uppercase;
88
- letter-spacing: 0.5px;
89
- }
90
- tbody tr { cursor: pointer; transition: background 0.1s; }
91
- tbody tr:hover { background: var(--panel); }
92
- .badge {
93
- display: inline-block;
94
- padding: 2px 9px;
95
- border-radius: 999px;
96
- font-size: 11.5px;
97
- font-weight: 600;
98
- text-transform: capitalize;
99
- border: 1px solid transparent;
100
- }
101
- .s-completed { color: var(--green); background: rgba(63,185,80,0.12); border-color: rgba(63,185,80,0.3); }
102
- .s-failed, .s-dead { color: var(--red); background: rgba(248,81,73,0.12); border-color: rgba(248,81,73,0.3); }
103
- .s-running, .s-pending { color: var(--accent); background: rgba(88,166,255,0.12); border-color: rgba(88,166,255,0.3); }
104
- .s-suspended { color: var(--amber); background: rgba(210,153,34,0.12); border-color: rgba(210,153,34,0.3); }
105
- .s-cancelled { color: var(--grey); background: rgba(110,118,129,0.15); border-color: rgba(110,118,129,0.3); }
106
- .muted { color: var(--muted); }
107
- .empty { color: var(--muted); padding: 40px; text-align: center; }
108
- .panel {
109
- background: var(--panel);
110
- border: 1px solid var(--border);
111
- border-radius: 8px;
112
- padding: 18px 20px;
113
- margin-bottom: 18px;
114
- }
115
- .panel h2 { font-size: 14px; margin: 0 0 14px; font-weight: 600; }
116
- .kv { display: grid; grid-template-columns: 150px 1fr; gap: 6px 14px; }
117
- .kv dt { color: var(--muted); }
118
- .kv dd { margin: 0; word-break: break-word; }
119
- pre {
120
- background: var(--panel-2);
121
- border: 1px solid var(--border);
122
- border-radius: 6px;
123
- padding: 10px 12px;
124
- overflow: auto;
125
- margin: 4px 0 0;
126
- max-height: 240px;
127
- }
128
- .timeline { display: flex; flex-direction: column; gap: 0; }
129
- .step {
130
- display: flex;
131
- gap: 14px;
132
- padding: 12px 0;
133
- border-bottom: 1px solid var(--border);
134
- }
135
- .step:last-child { border-bottom: none; }
136
- .step .marker { width: 10px; padding-top: 5px; }
137
- .step .marker .pip { width: 10px; height: 10px; border-radius: 50%; }
138
- .step .body { flex: 1; min-width: 0; }
139
- .step .row1 { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
140
- .step .name { font-weight: 600; }
141
- .step .meta { color: var(--muted); font-size: 12px; display: flex; gap: 14px; flex-wrap: wrap; margin-top: 4px; }
142
- .kind {
143
- font-size: 10.5px;
144
- text-transform: uppercase;
145
- letter-spacing: 0.5px;
146
- color: var(--purple);
147
- border: 1px solid rgba(188,140,255,0.3);
148
- background: rgba(188,140,255,0.1);
149
- border-radius: 4px;
150
- padding: 1px 6px;
151
- }
152
- .back { margin-bottom: 14px; display: inline-block; }
153
- .actions { display: flex; gap: 10px; margin: 14px 0 0; }
154
- .toast {
155
- position: fixed;
156
- bottom: 22px;
157
- right: 22px;
158
- background: var(--panel-2);
159
- border: 1px solid var(--border);
160
- border-radius: 8px;
161
- padding: 12px 16px;
162
- max-width: 360px;
163
- box-shadow: 0 8px 24px rgba(0,0,0,0.4);
164
- opacity: 0;
165
- transform: translateY(8px);
166
- transition: opacity 0.2s, transform 0.2s;
167
- pointer-events: none;
168
- }
169
- .toast.show { opacity: 1; transform: translateY(0); }
170
- .toast.err { border-color: var(--red); }
171
- .health { display: flex; gap: 12px; flex-wrap: wrap; }
172
- .health .h-card {
173
- border: 1px solid var(--border);
174
- border-radius: 6px;
175
- padding: 8px 14px;
176
- background: var(--panel-2);
177
- font-size: 12.5px;
178
- }
179
- .health .h-card.stalled { border-color: var(--red); }
180
- .nav { display: flex; gap: 16px; }
181
- .nav a { color: var(--muted); font-size: 13px; }
182
- .nav a:hover, .nav a.active { color: var(--text); }
183
- /* Fleet-health / protocol-compat badges — mirror src/dashboard/compat-view.ts outcomeClass(). */
184
- .badge.c-ok { color: var(--green); background: rgba(63,185,80,0.12); border-color: rgba(63,185,80,0.3); }
185
- .badge.c-degraded { color: var(--amber); background: rgba(210,153,34,0.12); border-color: rgba(210,153,34,0.3); }
186
- .badge.c-bad { color: var(--red); background: rgba(248,81,73,0.12); border-color: rgba(248,81,73,0.3); }
187
- .panel.flagged-panel { border-color: var(--red); }
188
- tr.flagged { background: rgba(248,81,73,0.06); }
189
- .flag-reason { color: var(--red); }
190
- </style>
191
- </head>
192
- <body>
193
- <header>
194
- <h1><span class="dot">●</span> Durable</h1>
195
- <span class="muted mono" id="crumb">runs</span>
196
- <nav class="nav">
197
- <a href="#" id="nav-runs">Runs</a>
198
- <a href="#/health" id="nav-health">Fleet health</a>
199
- </nav>
200
- <span class="spacer"></span>
201
- <span class="muted" id="health-strip"></span>
202
- </header>
203
- <main id="app"><div class="empty">Loading…</div></main>
204
- <div class="toast" id="toast"></div>
205
-
206
- <script>
207
- // The API base path is injected by the provider at serve time.
208
- const API_BASE = '__API_BASE__';
209
-
210
- const el = (tag, attrs = {}, children = []) => {
211
- const n = document.createElement(tag);
212
- for (const [k, v] of Object.entries(attrs)) {
213
- if (k === 'class') n.className = v;
214
- else if (k === 'html') n.innerHTML = v;
215
- else if (k.startsWith('on') && typeof v === 'function') n.addEventListener(k.slice(2), v);
216
- else if (v != null) n.setAttribute(k, v);
217
- }
218
- for (const c of [].concat(children)) {
219
- if (c == null) continue;
220
- n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c);
221
- }
222
- return n;
223
- };
224
-
225
- const toast = (msg, isErr) => {
226
- const t = document.getElementById('toast');
227
- t.textContent = msg;
228
- t.className = 'toast show' + (isErr ? ' err' : '');
229
- setTimeout(() => { t.className = 'toast'; }, 3200);
230
- };
231
-
232
- const api = async (path, opts) => {
233
- const res = await fetch(API_BASE + path, {
234
- headers: { 'Content-Type': 'application/json' },
235
- ...opts,
236
- });
237
- const body = await res.json().catch(() => ({}));
238
- if (!res.ok) throw new Error(body.error || ('HTTP ' + res.status));
239
- return body;
240
- };
241
-
242
- const badge = (status) =>
243
- el('span', { class: 'badge s-' + status }, status);
244
-
245
- const fmt = (iso) => {
246
- if (!iso) return '—';
247
- const d = new Date(iso);
248
- return d.toLocaleString();
249
- };
250
- const dur = (ms) => {
251
- if (ms == null) return '—';
252
- if (ms < 1000) return ms + 'ms';
253
- if (ms < 60000) return (ms / 1000).toFixed(1) + 's';
254
- return (ms / 60000).toFixed(1) + 'm';
255
- };
256
-
257
- const PIP = {
258
- completed: 'var(--green)', failed: 'var(--red)', running: 'var(--accent)',
259
- pending: 'var(--accent)', suspended: 'var(--amber)', cancelled: 'var(--grey)',
260
- dead: 'var(--red)',
261
- };
262
-
263
- let state = { status: '', workflow: '' };
264
-
265
- async function refreshHealth() {
266
- try {
267
- const { groups } = await api('/health');
268
- const strip = document.getElementById('health-strip');
269
- if (!groups.length) { strip.textContent = ''; return; }
270
- const stalled = groups.filter((g) => g.stalled).length;
271
- strip.innerHTML = stalled
272
- ? '<span style="color:var(--red)">⚠ ' + stalled + ' stalled group(s)</span>'
273
- : groups.length + ' worker group(s)';
274
- } catch (_) { /* health is best-effort */ }
275
- }
276
-
277
- async function renderList() {
278
- document.getElementById('crumb').textContent = 'runs';
279
- const app = document.getElementById('app');
280
- const params = new URLSearchParams();
281
- if (state.status) params.set('status', state.status);
282
- if (state.workflow) params.set('workflow', state.workflow);
283
- let data;
284
- try {
285
- data = await api('/runs?' + params.toString());
286
- } catch (e) {
287
- app.innerHTML = '';
288
- app.appendChild(el('div', { class: 'empty' }, 'Failed to load runs: ' + e.message));
289
- return;
290
- }
291
-
292
- const statusSel = el('select', {
293
- onchange: (e) => { state.status = e.target.value; renderList(); },
294
- }, [el('option', { value: '' }, 'All statuses')].concat(
295
- data.statuses.map((s) =>
296
- el('option', { value: s, ...(s === state.status ? { selected: '' } : {}) }, s)),
297
- ));
298
-
299
- const wfInput = el('input', {
300
- placeholder: 'Filter by workflow…',
301
- value: state.workflow,
302
- onkeydown: (e) => { if (e.key === 'Enter') { state.workflow = e.target.value.trim(); renderList(); } },
303
- });
304
-
305
- const controls = el('div', { class: 'controls' }, [
306
- statusSel,
307
- wfInput,
308
- el('button', { onclick: () => renderList() }, '↻ Refresh'),
309
- ]);
310
-
311
- const rows = data.runs.map((r) =>
312
- el('tr', { onclick: () => navigate(r.id) }, [
313
- el('td', {}, el('code', {}, r.id)),
314
- el('td', {}, r.workflow + (r.workflowVersion ? ' @' + r.workflowVersion : '')),
315
- el('td', {}, badge(r.status)),
316
- el('td', { class: 'muted mono' }, fmt(r.createdAt)),
317
- el('td', { class: 'muted mono' }, fmt(r.updatedAt)),
318
- ]));
319
-
320
- const table = el('table', {}, [
321
- el('thead', {}, el('tr', {}, [
322
- el('th', {}, 'Run id'), el('th', {}, 'Workflow'),
323
- el('th', {}, 'Status'), el('th', {}, 'Started'), el('th', {}, 'Updated'),
324
- ])),
325
- el('tbody', {}, rows.length ? rows : []),
326
- ]);
327
-
328
- app.innerHTML = '';
329
- app.appendChild(controls);
330
- if (rows.length) app.appendChild(table);
331
- else app.appendChild(el('div', { class: 'empty' }, 'No runs match.'));
332
- }
333
-
334
- async function renderDetail(id) {
335
- document.getElementById('crumb').textContent = 'runs / ' + id;
336
- const app = document.getElementById('app');
337
- let data;
338
- try {
339
- data = await api('/runs/' + encodeURIComponent(id));
340
- } catch (e) {
341
- app.innerHTML = '';
342
- app.appendChild(el('a', { class: 'back', href: '#' }, '← back'));
343
- app.appendChild(el('div', { class: 'empty' }, e.message));
344
- return;
345
- }
346
- const run = data.run;
347
-
348
- const canRetry = ['failed', 'dead', 'cancelled', 'suspended'].includes(run.status);
349
- const canCancel = ['pending', 'running', 'suspended'].includes(run.status);
350
-
351
- const actions = el('div', { class: 'actions' }, [
352
- el('button', {
353
- class: 'primary', disabled: canRetry ? null : '',
354
- onclick: () => act('retry', id),
355
- }, '↻ Retry'),
356
- el('button', {
357
- class: 'danger', disabled: canCancel ? null : '',
358
- onclick: () => act('cancel', id),
359
- }, '✕ Cancel'),
360
- ]);
361
-
362
- const overview = el('div', { class: 'panel' }, [
363
- el('h2', {}, 'Overview'),
364
- el('dl', { class: 'kv' }, [
365
- el('dt', {}, 'Status'), el('dd', {}, badge(run.status)),
366
- el('dt', {}, 'Workflow'), el('dd', {}, run.workflow + ' @' + run.workflowVersion),
367
- el('dt', {}, 'Run id'), el('dd', {}, el('code', {}, run.id)),
368
- el('dt', {}, 'Tags'), el('dd', { class: 'muted' }, (run.tags || []).join(', ') || '—'),
369
- el('dt', {}, 'Started'), el('dd', { class: 'mono muted' }, fmt(run.createdAt)),
370
- el('dt', {}, 'Updated'), el('dd', { class: 'mono muted' }, fmt(run.updatedAt)),
371
- ]),
372
- actions,
373
- run.input !== undefined ? el('div', {}, [
374
- el('div', { class: 'muted', style: 'margin-top:14px' }, 'Input'),
375
- el('pre', {}, el('code', {}, JSON.stringify(run.input, null, 2))),
376
- ]) : null,
377
- run.error ? el('div', {}, [
378
- el('div', { class: 'muted', style: 'margin-top:14px;color:var(--red)' }, 'Error'),
379
- el('pre', {}, el('code', {}, JSON.stringify(run.error, null, 2))),
380
- ]) : null,
381
- run.output !== undefined && run.output !== null ? el('div', {}, [
382
- el('div', { class: 'muted', style: 'margin-top:14px' }, 'Output'),
383
- el('pre', {}, el('code', {}, JSON.stringify(run.output, null, 2))),
384
- ]) : null,
385
- ]);
386
-
387
- const steps = data.timeline.map((cp) =>
388
- el('div', { class: 'step' }, [
389
- el('div', { class: 'marker' },
390
- el('div', { class: 'pip', style: 'background:' + (PIP[cp.status] || 'var(--grey)') })),
391
- el('div', { class: 'body' }, [
392
- el('div', { class: 'row1' }, [
393
- el('span', { class: 'name' }, cp.name),
394
- el('span', { class: 'kind' }, cp.kind),
395
- badge(cp.status),
396
- ]),
397
- el('div', { class: 'meta' }, [
398
- el('span', {}, 'attempts: ' + cp.attempts),
399
- el('span', {}, 'duration: ' + dur(cp.durationMs)),
400
- cp.queueMs ? el('span', {}, 'queued: ' + dur(cp.queueMs)) : null,
401
- cp.workerGroup ? el('span', {}, 'group: ' + cp.workerGroup) : null,
402
- el('span', { class: 'mono' }, fmt(cp.startedAt)),
403
- ]),
404
- cp.error ? el('pre', {}, el('code', {}, JSON.stringify(cp.error, null, 2))) : null,
405
- ]),
406
- ]));
407
-
408
- const timeline = el('div', { class: 'panel' }, [
409
- el('h2', {}, 'Timeline (' + data.timeline.length + ' steps)'),
410
- steps.length ? el('div', { class: 'timeline' }, steps)
411
- : el('div', { class: 'muted' }, 'No checkpoints yet.'),
412
- ]);
413
-
414
- const childrenPanel = data.children.length ? el('div', { class: 'panel' }, [
415
- el('h2', {}, 'Children'),
416
- el('div', {}, data.children.map((c) =>
417
- el('div', {}, el('a', { href: '#', onclick: (e) => { e.preventDefault(); navigate(c); } },
418
- el('code', {}, c))))),
419
- ]) : null;
420
-
421
- app.innerHTML = '';
422
- app.appendChild(el('a', {
423
- class: 'back', href: '#',
424
- onclick: (e) => { e.preventDefault(); navigate(''); },
425
- }, '← all runs'));
426
- app.appendChild(overview);
427
- app.appendChild(timeline);
428
- if (childrenPanel) app.appendChild(childrenPanel);
429
- }
430
-
431
- // Mirrors src/dashboard/compat-view.ts — keep the two in lockstep (unit-tested there).
432
- const OUTCOME = {
433
- compatible: { cls: 'c-ok', label: 'compatible' },
434
- degraded: { cls: 'c-degraded', label: 'degraded' },
435
- incompatible: { cls: 'c-bad', label: 'incompatible' },
436
- };
437
- const protoRange = (r) =>
438
- !r ? '—' : (r[0] === r[1] ? 'v' + r[0] : 'v' + r[0] + '–' + r[1]);
439
-
440
- async function renderCompat() {
441
- document.getElementById('crumb').textContent = 'fleet health';
442
- const app = document.getElementById('app');
443
- let data;
444
- try {
445
- data = await api('/compat');
446
- } catch (e) {
447
- app.innerHTML = '';
448
- app.appendChild(el('div', { class: 'empty' }, 'Failed to load fleet health: ' + e.message));
449
- return;
450
- }
451
-
452
- const cp = data.controlPlane;
453
- const cpPanel = el('div', { class: 'panel' }, [
454
- el('h2', {}, 'Control plane'),
455
- el('dl', { class: 'kv' }, [
456
- el('dt', {}, 'Instance'), el('dd', {}, el('code', {}, cp.instanceId)),
457
- el('dt', {}, 'Protocol'), el('dd', {}, protoRange(cp.protocolRange)),
458
- el('dt', {}, 'Capabilities'),
459
- el('dd', { class: 'muted' }, (cp.capabilities || []).join(', ') || '—'),
460
- ]),
461
- ]);
462
-
463
- const groupPanels = data.groups.map((g) => {
464
- const rows = g.pods.map((p) => {
465
- const o = OUTCOME[p.outcome] || { cls: '', label: p.outcome };
466
- return el('tr', p.incompatible ? { class: 'flagged' } : {}, [
467
- el('td', {}, el('code', {}, p.instanceId)),
468
- el('td', { class: 'muted' }, p.runtime),
469
- el('td', {}, protoRange(p.protocolRange)),
470
- el('td', {}, el('span', { class: 'badge ' + o.cls }, o.label)),
471
- el('td', { class: p.incompatible ? 'flag-reason' : 'muted' }, p.reason || '—'),
472
- ]);
473
- });
474
- return el('div', { class: 'panel' + (g.incompatible ? ' flagged-panel' : '') }, [
475
- el('h2', {}, (g.incompatible ? '⚠ ' : '') + g.token),
476
- el('table', {}, [
477
- el('thead', {}, el('tr', {}, [
478
- el('th', {}, 'Pod'), el('th', {}, 'Runtime'), el('th', {}, 'Protocol'),
479
- el('th', {}, 'Negotiated'), el('th', {}, 'Reason'),
480
- ])),
481
- el('tbody', {}, rows),
482
- ]),
483
- ]);
484
- });
485
-
486
- const blockedRows = data.blocked.map((b) =>
487
- el('tr', { onclick: () => navigate(b.id) }, [
488
- el('td', {}, el('code', {}, b.id)),
489
- el('td', {}, b.workflow || '—'),
490
- el('td', { class: 'flag-reason' }, b.reason),
491
- el('td', { class: 'muted' }, (b.requires && b.requires.length) ? b.requires.join(', ') : '—'),
492
- el('td', { class: 'muted mono' }, fmt(b.updatedAt)),
493
- ]));
494
- const blockedPanel = el('div', { class: 'panel' + (data.blocked.length ? ' flagged-panel' : '') }, [
495
- el('h2', {}, (data.blocked.length ? '⚠ ' : '') + 'Blocked runs (' + data.blocked.length + ')'),
496
- data.blocked.length
497
- ? el('table', {}, [
498
- el('thead', {}, el('tr', {}, [
499
- el('th', {}, 'Run id'), el('th', {}, 'Workflow'), el('th', {}, 'Reason'),
500
- el('th', {}, 'Requires'), el('th', {}, 'Updated'),
501
- ])),
502
- el('tbody', {}, blockedRows),
503
- ])
504
- : el('div', { class: 'muted' }, 'No blocked runs — every run has a compatible worker.'),
505
- ]);
506
-
507
- app.innerHTML = '';
508
- app.appendChild(el('a', {
509
- class: 'back', href: '#',
510
- onclick: (e) => { e.preventDefault(); navigate(''); },
511
- }, '← all runs'));
512
- app.appendChild(cpPanel);
513
- for (const panel of (data.groups.length ? groupPanels
514
- : [el('div', { class: 'panel' }, [
515
- el('h2', {}, 'Workers'),
516
- el('div', { class: 'muted' }, 'No live worker descriptors advertised.'),
517
- ])])) {
518
- app.appendChild(panel);
519
- }
520
- app.appendChild(blockedPanel);
521
- }
522
-
523
- async function act(action, id) {
524
- try {
525
- await api('/runs/' + encodeURIComponent(id) + '/' + action, {
526
- method: 'POST',
527
- body: JSON.stringify({}),
528
- });
529
- toast(action === 'retry' ? 'Run re-enqueued' : 'Run cancelled');
530
- setTimeout(() => renderDetail(id), 250);
531
- } catch (e) {
532
- toast('Failed: ' + e.message, true);
533
- }
534
- }
535
-
536
- function navigate(id) {
537
- location.hash = id ? '/runs/' + id : '';
538
- }
539
-
540
- function route() {
541
- const hash = location.hash.replace(/^#/, '');
542
- const onHealth = hash === '/health';
543
- document.getElementById('nav-health').className = onHealth ? 'active' : '';
544
- document.getElementById('nav-runs').className = onHealth ? '' : 'active';
545
- if (onHealth) {
546
- renderCompat();
547
- } else {
548
- const m = /^\/runs\/(.+)$/.exec(hash);
549
- if (m) renderDetail(decodeURIComponent(m[1]));
550
- else renderList();
551
- }
552
- refreshHealth();
553
- }
554
-
555
- window.addEventListener('hashchange', route);
556
- route();
557
- </script>
558
- </body>
559
- </html>