@boardwalk-labs/runner 0.1.20 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/daemon/daemon.js
CHANGED
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
// per-run workspace, then tears the workspace down (contract: cleanup "always").
|
|
8
8
|
import { mkdir, rm } from "node:fs/promises";
|
|
9
9
|
import * as path from "node:path";
|
|
10
|
-
import * as os from "node:os";
|
|
11
10
|
import { createLogger } from "../runtime/support/index.js";
|
|
12
11
|
/** Assignment-lease heartbeat cadence — comfortably beats the 300s lease. */
|
|
13
12
|
const HEARTBEAT_MS = 60_000;
|
|
@@ -23,7 +22,10 @@ export function runProcessEnv(claim, opts) {
|
|
|
23
22
|
BOARDWALK_CONTROL_PLANE_URL: claim.control_plane.base_url,
|
|
24
23
|
BOARDWALK_RUN_TOKEN: claim.control_plane.run_token,
|
|
25
24
|
BOARDWALK_API_KEY: claim.control_plane.api_token,
|
|
26
|
-
BOARDWALK_TASK_CPU_UNITS:
|
|
25
|
+
// BOARDWALK_TASK_CPU_UNITS is deliberately ABSENT: self-hosted compute is Boardwalk-unbilled
|
|
26
|
+
// (SELF_HOSTED_RUNNERS.md D5), so the runtime's wall-clock fallback (1 vCPU) is the right
|
|
27
|
+
// display metric. Stamping the host's core count here billed a run at cores × the compute rate
|
|
28
|
+
// for hardware the org owns.
|
|
27
29
|
// The org's BYO inference providers, as data (the runner-direct BYO design) — consumed by the runtime's direct
|
|
28
30
|
// model path. Names + endpoints + secret NAMES only; never a credential value.
|
|
29
31
|
BOARDWALK_BYO_PROVIDERS: JSON.stringify(claim.byo_providers),
|
|
@@ -65,6 +65,11 @@ export interface FreezeCoordinatorHooks {
|
|
|
65
65
|
/** Runs when a wake lands, before the seam resolves: swap the run/api tokens onto the
|
|
66
66
|
* broker client and rebase the runtime meter past the frozen window. */
|
|
67
67
|
onAfterWake?: (wake: WakePayload) => void | Promise<void>;
|
|
68
|
+
/** Runs when a REQUESTED freeze aborts (`suspend_abort` — snapshot/store failure) before the
|
|
69
|
+
* parked seam resolves: undo onBeforeFreeze's reversible effects (resume the paused runtime
|
|
70
|
+
* flusher) since the run now HOLDS in-process instead of freezing. Not called for a failure
|
|
71
|
+
* inside onBeforeFreeze itself — that hook owns its own unwind. Must not throw. */
|
|
72
|
+
onFreezeAborted?: () => void;
|
|
68
73
|
}
|
|
69
74
|
export interface FreezeCoordinatorDeps {
|
|
70
75
|
channel: RelayChannel;
|
|
@@ -237,6 +237,9 @@ export class FreezeCoordinator {
|
|
|
237
237
|
}
|
|
238
238
|
const resolve = this.parked;
|
|
239
239
|
this.parked = null;
|
|
240
|
+
// The freeze is off — the seam will hold in-process, so onBeforeFreeze's reversible prep
|
|
241
|
+
// (the paused runtime flusher) must unwind before the hold starts metering-blind.
|
|
242
|
+
this.hooks.onFreezeAborted?.();
|
|
240
243
|
resolve({ kind: "aborted", reason });
|
|
241
244
|
}
|
|
242
245
|
awaitQuiescence(abort) {
|
package/dist/runtime/index.js
CHANGED
|
@@ -406,6 +406,16 @@ export function assembleWorkerDeps(runtime) {
|
|
|
406
406
|
// The recorder never spans a snapshot: finalize + upload the in-flight segment before the
|
|
407
407
|
// freeze (SCREEN_CAPTURE §4.3). Bounded internally, so a slow upload delays, not blocks, it.
|
|
408
408
|
await capture?.stopAndFlush();
|
|
409
|
+
// Pause LAST (nothing after it can throw, so a failed persist never strands a paused
|
|
410
|
+
// flusher): with the timer off, no tick can land in the post-wake sliver between the
|
|
411
|
+
// guest clock resync and excludeIdle below — a tick there would compute its delta over
|
|
412
|
+
// the whole frozen window and bill suspended time. Resumed on wake and on suspend_abort.
|
|
413
|
+
activeFlusher?.pause();
|
|
414
|
+
},
|
|
415
|
+
// The freeze died (snapshot/store failure) — the seam holds in-process, which must keep
|
|
416
|
+
// metering: undo the pre-freeze pause.
|
|
417
|
+
onFreezeAborted: () => {
|
|
418
|
+
activeFlusher?.resume();
|
|
409
419
|
},
|
|
410
420
|
// On wake: reseed the userspace CSPRNG (clause 3 — a suspend snapshot restored more than
|
|
411
421
|
// once, e.g. a re-dispatch retry, would otherwise repeat its post-wake `crypto.*` draws),
|
|
@@ -426,6 +436,8 @@ export function assembleWorkerDeps(runtime) {
|
|
|
426
436
|
redactor.record(wake.api_token);
|
|
427
437
|
}
|
|
428
438
|
activeFlusher?.excludeIdle(wake.wall_clock_ms - freezeWallMs);
|
|
439
|
+
// Only now that the frozen window is excluded may the flush timer run again.
|
|
440
|
+
activeFlusher?.resume();
|
|
429
441
|
// Resume capture in a fresh segment epoch (a suspend/resume boundary is a segment boundary).
|
|
430
442
|
void capture?.startFresh();
|
|
431
443
|
},
|
|
@@ -38,6 +38,17 @@ export declare class RuntimeFlusher {
|
|
|
38
38
|
excludeIdle(ms: number): void;
|
|
39
39
|
/** Begin periodic delta flushing. */
|
|
40
40
|
start(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Suspend the periodic timer across a VM freeze — the pre-freeze hook pauses AFTER the tail flush
|
|
43
|
+
* (as its last, non-throwing step), and the wake path resumes AFTER {@link excludeIdle}. Without
|
|
44
|
+
* this, a tick landing in the sliver between the guest clock resync and the idle rebase would
|
|
45
|
+
* compute its delta over the whole frozen window and bill suspended time. Reversible, unlike
|
|
46
|
+
* {@link stop}; a freeze that aborts (snapshot failure → the seam holds in-process) must resume so
|
|
47
|
+
* a long hold keeps metering.
|
|
48
|
+
*/
|
|
49
|
+
pause(): void;
|
|
50
|
+
/** Restart periodic flushing after {@link pause} (wake, or a freeze abort). No-op once stopped. */
|
|
51
|
+
resume(): void;
|
|
41
52
|
/** Stop the periodic timer and drain any in-flight flush. Does NOT book the final tail — call
|
|
42
53
|
* {@link flushFinal} for that on a clean terminal. Idempotent. */
|
|
43
54
|
stop(): Promise<void>;
|
|
@@ -58,6 +58,26 @@ export class RuntimeFlusher {
|
|
|
58
58
|
// Don't keep the worker process alive solely for the flush timer.
|
|
59
59
|
this.timer.unref();
|
|
60
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Suspend the periodic timer across a VM freeze — the pre-freeze hook pauses AFTER the tail flush
|
|
63
|
+
* (as its last, non-throwing step), and the wake path resumes AFTER {@link excludeIdle}. Without
|
|
64
|
+
* this, a tick landing in the sliver between the guest clock resync and the idle rebase would
|
|
65
|
+
* compute its delta over the whole frozen window and bill suspended time. Reversible, unlike
|
|
66
|
+
* {@link stop}; a freeze that aborts (snapshot failure → the seam holds in-process) must resume so
|
|
67
|
+
* a long hold keeps metering.
|
|
68
|
+
*/
|
|
69
|
+
pause() {
|
|
70
|
+
if (this.timer === null)
|
|
71
|
+
return;
|
|
72
|
+
clearInterval(this.timer);
|
|
73
|
+
this.timer = null;
|
|
74
|
+
}
|
|
75
|
+
/** Restart periodic flushing after {@link pause} (wake, or a freeze abort). No-op once stopped. */
|
|
76
|
+
resume() {
|
|
77
|
+
if (this.stopped)
|
|
78
|
+
return;
|
|
79
|
+
this.start();
|
|
80
|
+
}
|
|
61
81
|
/** Stop the periodic timer and drain any in-flight flush. Does NOT book the final tail — call
|
|
62
82
|
* {@link flushFinal} for that on a clean terminal. Idempotent. */
|
|
63
83
|
async stop() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boardwalk-labs/runner",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "Boardwalk self-hosted runner: execute cloud-scheduled runs on your own machines. Currently: the canonical runner contract types.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|