@coreplane/switchboard 1.202.1 → 1.203.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 (36) hide show
  1. package/dist/assets/config/config.example.yaml +10 -0
  2. package/dist/assets/deploy/cloudflare-resident/preflight.mjs +3 -3
  3. package/dist/assets/deploy/cloudflare-resident/refresh.ts +179 -93
  4. package/dist/assets/deploy/cloudflare-resident/shared.ts +15 -10
  5. package/dist/assets/deploy/cloudflare-resident/worker.ts +459 -632
  6. package/dist/assets/package-lock.json +3 -3
  7. package/dist/assets/package.json +1 -1
  8. package/dist/assets/source.json +3 -3
  9. package/dist/assets/src/core/authz/policy.ts +4 -0
  10. package/dist/assets/src/core/runRecord.ts +11 -0
  11. package/dist/assets/src/core/schedules.ts +12 -6
  12. package/dist/assets/src/core/ship/handoff.ts +246 -0
  13. package/dist/assets/src/execution/residentDisk.ts +2 -2
  14. package/dist/assets/src/execution/residentInstanceId.ts +48 -45
  15. package/dist/assets/src/execution/residentRefresh.ts +17 -66
  16. package/dist/assets/src/execution/residentState.ts +8 -9
  17. package/dist/assets/src/execution/residentStepPlan.ts +1 -1
  18. package/dist/assets/web/dist/.vite/manifest.json +55 -44
  19. package/dist/assets/web/dist/assets/{AppShell-CJLPO_aI.js → AppShell-Bw3-_TTE.js} +1 -1
  20. package/dist/assets/web/dist/assets/{CostsPage-C6ZvnAmH.js → CostsPage-XQ-fQzdz.js} +1 -1
  21. package/dist/assets/web/dist/assets/DeliveryPage-BRwcQyr7.js +1 -0
  22. package/dist/assets/web/dist/assets/{NotFoundPage-BdLcY60r.js → NotFoundPage-RjH-9dPy.js} +1 -1
  23. package/dist/assets/web/dist/assets/{ResidentDetailPage-hXWrr8T6.js → ResidentDetailPage-BRy5wkv9.js} +1 -1
  24. package/dist/assets/web/dist/assets/{ResidentsIndexPage-YLu4JwxJ.js → ResidentsIndexPage-DAEVLN8j.js} +1 -1
  25. package/dist/assets/web/dist/assets/{RunRoutePage-BDhHRJVO.js → RunRoutePage-B1KHmkZ9.js} +1 -1
  26. package/dist/assets/web/dist/assets/{RunsIndexPage-D7zZ5a0l.js → RunsIndexPage-3hUWFpFV.js} +1 -1
  27. package/dist/assets/web/dist/assets/{RunsTabs-BOUlSa2W.js → RunsTabs-Qn_5TVJU.js} +1 -1
  28. package/dist/assets/web/dist/assets/{ScheduledPage-BciOQcC-.js → ScheduledPage-DysVvVm1.js} +1 -1
  29. package/dist/assets/web/dist/assets/{StatusDot-CTDLBv92.js → StatusDot-Bug2a6T6.js} +1 -1
  30. package/dist/assets/web/dist/assets/{Tooltip-DD2v9Gxx.js → Tooltip-C2eEUbwn.js} +1 -1
  31. package/dist/assets/web/dist/assets/{favicon-C4Q8cRsP.js → favicon-CFfbl2AI.js} +1 -1
  32. package/dist/assets/web/dist/assets/{main-DrSlUcMg.js → main-0uhp-taL.js} +3 -3
  33. package/dist/assets/web/dist/assets/main-Xickfv8V.css +1 -0
  34. package/dist/cli.js +2932 -1875
  35. package/package.json +1 -1
  36. package/dist/assets/web/dist/assets/main-DoTjwE-G.css +0 -1
@@ -1,8 +1,8 @@
1
- /** The refresh cycle's rebuild decision for the resident Worker's
2
- * `onRefreshAlarm` (deploy/cloudflare-resident/worker.ts), kept pure and
3
- * dependency-free so it is unit-testable from src/ and imported across
4
- * packages by the resident Worker (like residentDetach) — the tested code IS
5
- * the shipped code.
1
+ /** The refresh cycle's rebuild decision for the resident Worker's refresh
2
+ * instance steps (`refreshInstanceFetch` and its siblings in
3
+ * deploy/cloudflare-resident/worker.ts), kept pure and dependency-free so it
4
+ * is unit-testable from src/ and imported across packages by the resident
5
+ * Worker (like residentDetach) — the tested code IS the shipped code.
6
6
  *
7
7
  * Background: the refresh used to `git clean -fdx` + `npm install` on
8
8
  * EVERY default-branch advance (a refresh over two minutes long), even
@@ -18,7 +18,7 @@
18
18
  * The disk facts come from two root-only markers the cycle writes as it
19
19
  * materializes (deps key after install, built sha after build) plus the
20
20
  * checkout's actual HEAD. A worker-code deploy resets the DO mid-cycle but
21
- * leaves the container disk alone, so the next alarm can pick up where the
21
+ * leaves the container disk alone, so the next cycle can pick up where the
22
22
  * interrupted one stopped instead of redoing the whole rebuild. */
23
23
 
24
24
  import { DISK_FULL_FREE_KIB, diskFullReason, isDiskFullMessage } from "./residentDisk.js";
@@ -119,11 +119,12 @@ export function checkoutUpdateCommand(sha: string, clean: CleanScope): string {
119
119
  * the SDK's replacement errors (stale process handle, closed supervisor).
120
120
  * Everything else is the repo's own build failing. */
121
121
  export interface RefreshFailure {
122
- /** The `degraded` reason to record. Interruptions are prefixed
123
- * `refresh-interrupted:` so the park-streak gate can exclude them by prefix,
124
- * exactly like the watchdog's stamps; a full disk is `disk-full:`
125
- * (`residentDisk.ts` — the cycle's entry gate and the recycle decision key
126
- * on it); real failures keep `<step>-failed:`. */
122
+ /** The reason. An interruption's is prefixed `refresh-interrupted:` and is
123
+ * never recorded as `degraded`: the instance step throws it to the engine,
124
+ * whose retry re-enters the step, and the row keeps its state (the word
125
+ * shows on the instance row's `lastStep` and in the log). A full disk is
126
+ * `disk-full:` (`residentDisk.ts` — the cycle's entry gate and the recycle
127
+ * decision key on it); real failures keep `<step>-failed:`. */
127
128
  reason: string;
128
129
  interrupted: boolean;
129
130
  diskFull: boolean;
@@ -196,7 +197,8 @@ const INTERRUPTION_SIGNATURE = /\bexit 143\b|Session terminated|SIGTERM/;
196
197
  * (deploy/cloudflare-resident/worker.ts) as its message-level fallback, so the
197
198
  * exec path and the refresh classifier agree on one wording list (a container
198
199
  * stop mid-snapshot produces "Process supervisor is closed"; classified as the
199
- * repo's own `snapshot-failed` it would re-arm at the full cadence). */
200
+ * repo's own `snapshot-failed` it would end the instance `degraded` instead of
201
+ * being retried into the same step). */
200
202
  export const RUNTIME_REPLACEMENT_WORDING =
201
203
  /previous runtime incarnation|interrupted because the runtime changed|runtime identity is no longer active|sandbox lifetime is no longer current|platform was updating the sandbox runtime|no longer identifies pid|process supervisor is closed|container is not running, consider calling start/i;
202
204
 
@@ -238,8 +240,9 @@ export function classifyRefreshFailure(input: {
238
240
  * was replaced under the restore (a resident Worker deploy rolled the
239
241
  * container; `RUNTIME_REPLACEMENT_WORDING`), so the disk it wrote to is gone
240
242
  * with it. That is `interrupted`, the same class a refresh step earns when a
241
- * deploy kills it: not evidence about the repo, re-armed short, and the next
242
- * wake restores again. The caller may also say the error IS a replacement
243
+ * deploy kills it: not evidence about the repo, thrown to the engine whose
244
+ * retry re-enters the step, and that attempt restores again. The caller may
245
+ * also say the error IS a replacement
243
246
  * (`runtimeReplaced`, from the Worker's typed and cause-chain check, since
244
247
  * the SDK throws typed replacement errors whose top-level message carries no
245
248
  * wording); the message check stays for the untyped ones. A message that
@@ -259,58 +262,6 @@ export function restoreFailureDisposition(
259
262
  };
260
263
  }
261
264
 
262
- /** Re-arm delay after a cycle that did not run to completion for a reason
263
- * outside the repo — an interrupted step, or an image-stale container stop.
264
- * 45 s: comfortably longer than a container restart plus rehydration
265
- * (~10–20 s observed), so the retry finds a live runtime, and an order of
266
- * magnitude under the 600 s cadence that previously left the resident
267
- * `degraded` (every run falling back cold) until the next regular alarm. */
268
- export const INTERRUPTED_REARM_S = 45;
269
-
270
- export type RefreshOutcome =
271
- /** Cycle ran (warm, or a real failure): regular cadence. */
272
- | "normal"
273
- /** A step was killed from outside (see classifyRefreshFailure). */
274
- | "interrupted"
275
- /** `reconcileImage` stopped the container so it restarts on the new image. */
276
- | "image-stale-restart"
277
- /** The disk-full recovery stopped the container so it restarts on an empty
278
- * disk and the next alarm restores from R2 (`residentDisk.ts`). */
279
- | "disk-full-restart"
280
- /** Idle gate parked the resident. */
281
- | "idle";
282
-
283
- /** How many CONSECUTIVE interrupted cycles still re-arm short. A real deploy
284
- * interrupts once, maybe twice (a deploy train); a step whose own output
285
- * happens to carry the kill signature every cycle (a test supervisor printing
286
- * `signal SIGTERM`) would otherwise retry at 45 s forever — never parking
287
- * (it is excluded from the streak) AND at 13× the cadence. Past the cap the
288
- * regular interval returns; the classification (and the streak exclusion)
289
- * stand, matching the watchdog's accepted "never parks, but at cadence". */
290
- export const INTERRUPTED_REARM_MAX_CONSECUTIVE = 3;
291
-
292
- export function nextRefreshDelayS(input: {
293
- outcome: RefreshOutcome;
294
- intervalS: number;
295
- idleIntervalS: number;
296
- /** Consecutive cycles (this one included) that ended `interrupted`; omitted = 1. */
297
- consecutiveInterrupted?: number;
298
- }): number {
299
- switch (input.outcome) {
300
- case "idle":
301
- return input.idleIntervalS;
302
- case "interrupted":
303
- return (input.consecutiveInterrupted ?? 1) > INTERRUPTED_REARM_MAX_CONSECUTIVE
304
- ? input.intervalS
305
- : INTERRUPTED_REARM_S;
306
- case "image-stale-restart":
307
- case "disk-full-restart":
308
- return INTERRUPTED_REARM_S;
309
- default:
310
- return input.intervalS;
311
- }
312
- }
313
-
314
265
  /** Bound a promise that offers no timeout of its own (the
315
266
  * Sandbox SDK's createBackup/restoreBackup take neither a timeout nor an
316
267
  * AbortSignal). On expiry, rejects with an error naming `what` and the
@@ -16,14 +16,13 @@ export const SERVICEABLE_STATES: ReadonlySet<ResidentLifecycleState> = new Set<R
16
16
  ]);
17
17
 
18
18
  // A `degraded` reason names its cause (docs/reference/specs/resident-repos.md item 7).
19
- // Only reasons that PROVE the previous checkout + dep cache are intact attach:
20
- // `github-unreachable: …` (the fetch failed before the checkout was touched)
21
- // and `alarm-missed: …` (the watchdog re-armed a dead chain; nothing ran).
22
- // `stale-mid-flight: …` does NOT qualify: it is stamped when a `refreshing`
23
- // marker was orphaned by a cycle that died mid-flight, and that death may have
24
- // been inside the rebuild lock section (after `git clean -fdx`, mid-install) —
25
- // exactly the torn checkout this gate exists to avoid; the +5s recovery cycle
26
- // rebuilds it within seconds anyway. A failure INSIDE the rebuild —
19
+ // Only a reason that PROVES the previous checkout + dep cache are intact
20
+ // attaches: `github-unreachable: …` (the fetch failed before the checkout was
21
+ // touched). `stale-mid-flight: …` does NOT qualify: it is stamped when a
22
+ // `refreshing` marker was orphaned by a cycle that died mid-flight, and that
23
+ // death may have been inside the rebuild lock section (after `git clean -fdx`,
24
+ // mid-install) — exactly the torn checkout this gate exists to avoid; the next
25
+ // refresh instance rebuilds it within one bucket anyway. A failure INSIDE the rebuild —
27
26
  // `checkout-update-failed`, `install-failed`, `build-failed`,
28
27
  // `snapshot-failed`, `refresh-failed` — leaves the checkout at a new sha with
29
28
  // absent/partial deps, and a fresh thread would hardlink that broken cache.
@@ -33,7 +32,7 @@ export const SERVICEABLE_STATES: ReadonlySet<ResidentLifecycleState> = new Set<R
33
32
  // (recorded as `github-unreachable`, every run would attach and die at
34
33
  // git-setup). Everything not on the allow-list, including unknown reasons,
35
34
  // stays cold.
36
- const SERVICEABLE_DEGRADED_REASON = /^(?:github-unreachable|alarm-missed)(?::|$)/;
35
+ const SERVICEABLE_DEGRADED_REASON = /^github-unreachable(?::|$)/;
37
36
 
38
37
  export function degradedIsServiceable(reason: string | undefined): boolean {
39
38
  return SERVICEABLE_DEGRADED_REASON.test(reason ?? "");
@@ -20,7 +20,7 @@ import { planRefresh, type CleanScope, type RefreshDisk } from "./residentRefres
20
20
  export const LAST_FETCH_KEY = "resident:lastFetch";
21
21
 
22
22
  export interface FetchRecord {
23
- /** The cycle that fetched (the alarm firing or the instance driving it). */
23
+ /** The cycle that fetched: the refresh instance driving it. */
24
24
  cycle: string;
25
25
  ref: string;
26
26
  /** The ref's tip in the mirror after that fetch. */
@@ -27,52 +27,52 @@
27
27
  "file": "assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2",
28
28
  "src": "../node_modules/@fontsource-variable/jetbrains-mono/files/jetbrains-mono-vietnamese-wght-normal.woff2"
29
29
  },
30
- "_AppShell-CJLPO_aI.js": {
31
- "file": "assets/AppShell-CJLPO_aI.js",
30
+ "_AppShell-Bw3-_TTE.js": {
31
+ "file": "assets/AppShell-Bw3-_TTE.js",
32
32
  "name": "AppShell",
33
33
  "imports": [
34
34
  "_seed-B8mQEhhw.js"
35
35
  ]
36
36
  },
37
- "_NotFoundPage-BdLcY60r.js": {
38
- "file": "assets/NotFoundPage-BdLcY60r.js",
37
+ "_NotFoundPage-RjH-9dPy.js": {
38
+ "file": "assets/NotFoundPage-RjH-9dPy.js",
39
39
  "name": "NotFoundPage",
40
40
  "imports": [
41
- "_AppShell-CJLPO_aI.js",
41
+ "_AppShell-Bw3-_TTE.js",
42
42
  "_seed-B8mQEhhw.js",
43
- "_RunsTabs-BOUlSa2W.js"
43
+ "_RunsTabs-Qn_5TVJU.js"
44
44
  ]
45
45
  },
46
- "_RunsTabs-BOUlSa2W.js": {
47
- "file": "assets/RunsTabs-BOUlSa2W.js",
46
+ "_RunsTabs-Qn_5TVJU.js": {
47
+ "file": "assets/RunsTabs-Qn_5TVJU.js",
48
48
  "name": "RunsTabs",
49
49
  "imports": [
50
- "_AppShell-CJLPO_aI.js",
50
+ "_AppShell-Bw3-_TTE.js",
51
51
  "_seed-B8mQEhhw.js"
52
52
  ]
53
53
  },
54
- "_StatusDot-CTDLBv92.js": {
55
- "file": "assets/StatusDot-CTDLBv92.js",
54
+ "_StatusDot-Bug2a6T6.js": {
55
+ "file": "assets/StatusDot-Bug2a6T6.js",
56
56
  "name": "StatusDot",
57
57
  "imports": [
58
58
  "_seed-B8mQEhhw.js",
59
- "_Tooltip-DD2v9Gxx.js"
59
+ "_Tooltip-C2eEUbwn.js"
60
60
  ]
61
61
  },
62
- "_Tooltip-DD2v9Gxx.js": {
63
- "file": "assets/Tooltip-DD2v9Gxx.js",
62
+ "_Tooltip-C2eEUbwn.js": {
63
+ "file": "assets/Tooltip-C2eEUbwn.js",
64
64
  "name": "Tooltip",
65
65
  "imports": [
66
- "_AppShell-CJLPO_aI.js",
66
+ "_AppShell-Bw3-_TTE.js",
67
67
  "_seed-B8mQEhhw.js",
68
68
  "src/main.ts"
69
69
  ]
70
70
  },
71
- "_favicon-C4Q8cRsP.js": {
72
- "file": "assets/favicon-C4Q8cRsP.js",
71
+ "_favicon-CFfbl2AI.js": {
72
+ "file": "assets/favicon-CFfbl2AI.js",
73
73
  "name": "favicon",
74
74
  "imports": [
75
- "_AppShell-CJLPO_aI.js",
75
+ "_AppShell-Bw3-_TTE.js",
76
76
  "_seed-B8mQEhhw.js",
77
77
  "_localIso-L06jV29p.js"
78
78
  ]
@@ -97,7 +97,7 @@
97
97
  ]
98
98
  },
99
99
  "src/main.ts": {
100
- "file": "assets/main-DrSlUcMg.js",
100
+ "file": "assets/main-0uhp-taL.js",
101
101
  "name": "main",
102
102
  "src": "src/main.ts",
103
103
  "isEntry": true,
@@ -111,10 +111,11 @@
111
111
  "src/pages/ResidentsIndexPage.vue",
112
112
  "src/pages/ResidentDetailPage.vue",
113
113
  "src/pages/CostsPage.vue",
114
- "_NotFoundPage-BdLcY60r.js"
114
+ "src/pages/DeliveryPage.vue",
115
+ "_NotFoundPage-RjH-9dPy.js"
115
116
  ],
116
117
  "css": [
117
- "assets/main-DoTjwE-G.css"
118
+ "assets/main-Xickfv8V.css"
118
119
  ],
119
120
  "assets": [
120
121
  "assets/instrument-sans-latin-ext-wght-normal-B5bTHO_g.woff2",
@@ -127,53 +128,63 @@
127
128
  ]
128
129
  },
129
130
  "src/pages/CostsPage.vue": {
130
- "file": "assets/CostsPage-C6ZvnAmH.js",
131
+ "file": "assets/CostsPage-XQ-fQzdz.js",
131
132
  "name": "CostsPage",
132
133
  "src": "src/pages/CostsPage.vue",
133
134
  "isDynamicEntry": true,
134
135
  "imports": [
135
- "_AppShell-CJLPO_aI.js",
136
+ "_AppShell-Bw3-_TTE.js",
137
+ "_seed-B8mQEhhw.js"
138
+ ]
139
+ },
140
+ "src/pages/DeliveryPage.vue": {
141
+ "file": "assets/DeliveryPage-BRwcQyr7.js",
142
+ "name": "DeliveryPage",
143
+ "src": "src/pages/DeliveryPage.vue",
144
+ "isDynamicEntry": true,
145
+ "imports": [
146
+ "_AppShell-Bw3-_TTE.js",
136
147
  "_seed-B8mQEhhw.js"
137
148
  ]
138
149
  },
139
150
  "src/pages/ResidentDetailPage.vue": {
140
- "file": "assets/ResidentDetailPage-hXWrr8T6.js",
151
+ "file": "assets/ResidentDetailPage-BRy5wkv9.js",
141
152
  "name": "ResidentDetailPage",
142
153
  "src": "src/pages/ResidentDetailPage.vue",
143
154
  "isDynamicEntry": true,
144
155
  "imports": [
145
- "_AppShell-CJLPO_aI.js",
156
+ "_AppShell-Bw3-_TTE.js",
146
157
  "_seed-B8mQEhhw.js",
147
- "_StatusDot-CTDLBv92.js",
158
+ "_StatusDot-Bug2a6T6.js",
148
159
  "_residentDiskBudget-BMBKlYRH.js"
149
160
  ]
150
161
  },
151
162
  "src/pages/ResidentsIndexPage.vue": {
152
- "file": "assets/ResidentsIndexPage-YLu4JwxJ.js",
163
+ "file": "assets/ResidentsIndexPage-DAEVLN8j.js",
153
164
  "name": "ResidentsIndexPage",
154
165
  "src": "src/pages/ResidentsIndexPage.vue",
155
166
  "isDynamicEntry": true,
156
167
  "imports": [
157
- "_AppShell-CJLPO_aI.js",
168
+ "_AppShell-Bw3-_TTE.js",
158
169
  "_seed-B8mQEhhw.js",
159
- "_StatusDot-CTDLBv92.js",
170
+ "_StatusDot-Bug2a6T6.js",
160
171
  "_residentDiskBudget-BMBKlYRH.js",
161
172
  "_wallClock-DI4HIEN5.js"
162
173
  ]
163
174
  },
164
175
  "src/pages/RunRoutePage.vue": {
165
- "file": "assets/RunRoutePage-BDhHRJVO.js",
176
+ "file": "assets/RunRoutePage-B1KHmkZ9.js",
166
177
  "name": "RunRoutePage",
167
178
  "src": "src/pages/RunRoutePage.vue",
168
179
  "isDynamicEntry": true,
169
180
  "imports": [
170
- "_AppShell-CJLPO_aI.js",
181
+ "_AppShell-Bw3-_TTE.js",
171
182
  "_seed-B8mQEhhw.js",
172
- "_Tooltip-DD2v9Gxx.js",
183
+ "_Tooltip-C2eEUbwn.js",
173
184
  "src/main.ts",
174
- "_NotFoundPage-BdLcY60r.js",
185
+ "_NotFoundPage-RjH-9dPy.js",
175
186
  "_wallClock-DI4HIEN5.js",
176
- "_favicon-C4Q8cRsP.js",
187
+ "_favicon-CFfbl2AI.js",
177
188
  "_localIso-L06jV29p.js"
178
189
  ],
179
190
  "css": [
@@ -181,31 +192,31 @@
181
192
  ]
182
193
  },
183
194
  "src/pages/RunsIndexPage.vue": {
184
- "file": "assets/RunsIndexPage-D7zZ5a0l.js",
195
+ "file": "assets/RunsIndexPage-3hUWFpFV.js",
185
196
  "name": "RunsIndexPage",
186
197
  "src": "src/pages/RunsIndexPage.vue",
187
198
  "isDynamicEntry": true,
188
199
  "imports": [
189
- "_AppShell-CJLPO_aI.js",
200
+ "_AppShell-Bw3-_TTE.js",
190
201
  "_seed-B8mQEhhw.js",
191
- "_Tooltip-DD2v9Gxx.js",
192
- "_RunsTabs-BOUlSa2W.js",
193
- "_StatusDot-CTDLBv92.js",
202
+ "_Tooltip-C2eEUbwn.js",
203
+ "_RunsTabs-Qn_5TVJU.js",
204
+ "_StatusDot-Bug2a6T6.js",
194
205
  "_wallClock-DI4HIEN5.js",
195
- "_favicon-C4Q8cRsP.js",
206
+ "_favicon-CFfbl2AI.js",
196
207
  "_localIso-L06jV29p.js"
197
208
  ]
198
209
  },
199
210
  "src/pages/ScheduledPage.vue": {
200
- "file": "assets/ScheduledPage-BciOQcC-.js",
211
+ "file": "assets/ScheduledPage-DysVvVm1.js",
201
212
  "name": "ScheduledPage",
202
213
  "src": "src/pages/ScheduledPage.vue",
203
214
  "isDynamicEntry": true,
204
215
  "imports": [
205
- "_AppShell-CJLPO_aI.js",
216
+ "_AppShell-Bw3-_TTE.js",
206
217
  "_seed-B8mQEhhw.js",
207
- "_Tooltip-DD2v9Gxx.js",
208
- "_RunsTabs-BOUlSa2W.js",
218
+ "_Tooltip-C2eEUbwn.js",
219
+ "_RunsTabs-Qn_5TVJU.js",
209
220
  "_wallClock-DI4HIEN5.js",
210
221
  "_localIso-L06jV29p.js"
211
222
  ]