@boardwalk-labs/runner 0.2.17 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/runtime/agent/budget.d.ts +43 -24
- package/dist/runtime/agent/budget.js +108 -57
- package/dist/runtime/agent/events.d.ts +1 -1
- package/dist/runtime/broker_child_dispatcher.d.ts +5 -2
- package/dist/runtime/broker_child_dispatcher.js +24 -6
- package/dist/runtime/budget_gate.d.ts +58 -21
- package/dist/runtime/budget_gate.js +227 -46
- package/dist/runtime/host_capabilities.d.ts +4 -0
- package/dist/runtime/host_capabilities.js +25 -0
- package/dist/runtime/host_server.d.ts +137 -0
- package/dist/runtime/host_server.js +562 -0
- package/dist/runtime/index.js +45 -12
- package/dist/runtime/leaf_executor.d.ts +7 -4
- package/dist/runtime/leaf_executor.js +11 -6
- package/dist/runtime/program_runner.d.ts +62 -42
- package/dist/runtime/program_runner.js +156 -101
- package/dist/runtime/program_worker.d.ts +22 -11
- package/dist/runtime/program_worker.js +26 -48
- package/dist/runtime/python_program.d.ts +68 -0
- package/dist/runtime/python_program.js +270 -0
- package/dist/runtime/run_context.d.ts +13 -0
- package/dist/runtime/run_context.js +77 -0
- package/dist/runtime/runner_control_client.d.ts +23 -0
- package/dist/runtime/runner_control_client.js +69 -147
- package/dist/runtime/shell_exec.d.ts +17 -0
- package/dist/runtime/shell_exec.js +144 -0
- package/dist/runtime/support/index.d.ts +6 -0
- package/dist/runtime/support/index.js +14 -0
- package/dist/runtime/wire/inference_proxy.js +1 -1
- package/dist/runtime/wire/run.d.ts +5 -2
- package/dist/runtime/workflow_host.d.ts +53 -7
- package/dist/runtime/workflow_host.js +82 -42
- package/package.json +4 -3
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AgentOptions, ArtifactBody, ArtifactRef, BrowserSession, BrowserSessionOptions, CallOptions, HumanInputOptions, HumanInputResult, PhaseOptions, ShellResult, SleepArg, UsageSnapshot } from "@boardwalk-labs/workflow/runtime";
|
|
2
|
+
import type { ShellOptions } from "@boardwalk-labs/workflow";
|
|
2
3
|
import type { BrowserSessionManager } from "./browser_session.js";
|
|
3
4
|
import { type LeafResume } from "@boardwalk-labs/engine/core";
|
|
4
5
|
import { type SuspendSignal } from "./suspension.js";
|
|
@@ -30,11 +31,19 @@ export interface ScheduleOptions {
|
|
|
30
31
|
timezone?: string;
|
|
31
32
|
idempotencyKey?: string;
|
|
32
33
|
}
|
|
33
|
-
/** A child run's terminal-relevant state, as the start/poll seams return it.
|
|
34
|
+
/** A child run's terminal-relevant state, as the start/poll seams return it. `outputSchema` is
|
|
35
|
+
* the callee's PINNED version's stored `output_schema` (what lets the SDK revive a typed child's
|
|
36
|
+
* return); null = untyped callee or a payload that predates the field. */
|
|
34
37
|
export interface ChildResult {
|
|
35
38
|
childRunId: string;
|
|
36
39
|
status: string;
|
|
37
40
|
output: unknown;
|
|
41
|
+
outputSchema: Record<string, unknown> | null;
|
|
42
|
+
}
|
|
43
|
+
/** What `workflows.call` resolves: the completed child's output + the callee's output schema. */
|
|
44
|
+
export interface ChildCallOutput {
|
|
45
|
+
output: unknown;
|
|
46
|
+
outputSchema: Record<string, unknown> | null;
|
|
38
47
|
}
|
|
39
48
|
/** Dispatches child runs: `call` holds in-process + returns the completed child's output (the
|
|
40
49
|
* hold path); `start`/`poll` back the snapshot-substrate callWorkflow seam (start once, freeze
|
|
@@ -42,7 +51,11 @@ export interface ChildResult {
|
|
|
42
51
|
* provisions a durable future/recurring run → schedule id. The `signal` lets a hold/start abort
|
|
43
52
|
* promptly when the parent run is cancelled. */
|
|
44
53
|
export interface ChildDispatcher {
|
|
45
|
-
call(slug: string, input: unknown, opts: CallOptions | undefined, signal?: AbortSignal): Promise<
|
|
54
|
+
call(slug: string, input: unknown, opts: CallOptions | undefined, signal?: AbortSignal): Promise<ChildCallOutput>;
|
|
55
|
+
/** Poll a child run's current state (used by the freeze-wake path to fetch the callee's
|
|
56
|
+
* output schema — the wake payload carries the output but not the schema); null = not this
|
|
57
|
+
* run's child. */
|
|
58
|
+
poll(childRunId: string): Promise<ChildResult | null>;
|
|
46
59
|
/** Start (or idempotently re-attach to) a child run; resolves its current state. */
|
|
47
60
|
start(slug: string, input: unknown, opts: CallOptions | undefined, signal?: AbortSignal): Promise<ChildResult>;
|
|
48
61
|
run(slug: string, input: unknown, opts: CallOptions | undefined): Promise<string>;
|
|
@@ -145,8 +158,26 @@ export interface WorkerWorkflowHostDeps {
|
|
|
145
158
|
heldInput?: HeldInputPort;
|
|
146
159
|
/** Poll interval for a held gate's answer (default 3s). */
|
|
147
160
|
heldPollIntervalMs?: number;
|
|
161
|
+
/** Backs the protocol's `shell` capability (shell_exec's runShell in production). Absent ⇒
|
|
162
|
+
* `shell()` is unsupported in this runtime and rejects clearly. */
|
|
163
|
+
shell?: (cmd: string, opts: ShellOptions | undefined) => Promise<ShellResult>;
|
|
164
|
+
/** Live budget state for `usage.get` (the BudgetMeter's usageSnapshot in production). Absent ⇒
|
|
165
|
+
* `usage.get()` is unsupported in this runtime and rejects clearly. */
|
|
166
|
+
usage?: () => UsageSnapshot;
|
|
167
|
+
/**
|
|
168
|
+
* Budget clearance (docs/SUSPEND_POLICY.md Decision 3), awaited at the host's blocking/spending
|
|
169
|
+
* capability seams — `sleep`, `shell`, `workflows.call` — in addition to the leaf executor's
|
|
170
|
+
* `streamModel` seam. `usd`/`tokens` only move at model calls, but `max_compute_seconds` burns
|
|
171
|
+
* continuously, so a breach between model calls parks at whichever capability the program touches
|
|
172
|
+
* next (the park-at-next-seam contract; see budget_gate.ts). Runs INSIDE `guarded()` so the park's
|
|
173
|
+
* `budgetClearance` work-accounting (endWork around the freeze wait) balances, exactly like the
|
|
174
|
+
* leaf seam. Absent ⇒ no host-seam parks (tests / legacy paths).
|
|
175
|
+
*/
|
|
176
|
+
budgetGate?: {
|
|
177
|
+
clear(): Promise<void>;
|
|
178
|
+
};
|
|
148
179
|
}
|
|
149
|
-
export declare class WorkerWorkflowHost
|
|
180
|
+
export declare class WorkerWorkflowHost {
|
|
150
181
|
private readonly deps;
|
|
151
182
|
private readonly sleeper;
|
|
152
183
|
private readonly now;
|
|
@@ -207,10 +238,16 @@ export declare class WorkerWorkflowHost implements WorkflowHost {
|
|
|
207
238
|
* host always implements it. */
|
|
208
239
|
humanInput(opts: HumanInputOptions): Promise<HumanInputResult>;
|
|
209
240
|
private humanInputSeam;
|
|
241
|
+
/** Wait a registered gate out on whichever substrate the run has: freeze in place on the
|
|
242
|
+
* snapshot fleet, or (self-hosted runner / local dev) hold the live process and poll until a
|
|
243
|
+
* person answers. The one place the freeze-vs-hold decision for a gate is made. */
|
|
244
|
+
private awaitGate;
|
|
210
245
|
/**
|
|
211
246
|
* Park the run at a BUDGET gate and resolve with the responder's answer (docs/SUSPEND_POLICY.md
|
|
212
|
-
* Decision 3). Called
|
|
213
|
-
* breached
|
|
247
|
+
* Decision 3). Called when any budget cap (`max_usd` / `max_tokens` / `max_compute_seconds`) is
|
|
248
|
+
* breached — by the leaf executor's `streamModel` seam (from INSIDE an in-flight `agent()`) and by
|
|
249
|
+
* the host's own `sleep`/`shell`/`workflows.call` seams (from inside their `guarded()` bodies).
|
|
250
|
+
* Either way the caller is already work-tracked, which drives two deliberate differences from
|
|
214
251
|
* {@link humanInputSeam}:
|
|
215
252
|
*
|
|
216
253
|
* - **No `guarded()` wrapper.** The enclosing `agent()` seam already counted this leaf as work via
|
|
@@ -232,7 +269,7 @@ export declare class WorkerWorkflowHost implements WorkflowHost {
|
|
|
232
269
|
}): Promise<HumanInputResult>;
|
|
233
270
|
/** Absolute wake time for a `humanInput({ timeout })`, or null when there is none / it's unparseable. */
|
|
234
271
|
private timeoutExpiry;
|
|
235
|
-
callWorkflow(slug: string, input: unknown, opts: CallOptions | undefined): Promise<
|
|
272
|
+
callWorkflow(slug: string, input: unknown, opts: CallOptions | undefined): Promise<ChildCallOutput>;
|
|
236
273
|
/** The `workflows.call` seam: start the child once (idempotently — the child's run row is the
|
|
237
274
|
* durable memo, so a crash-restarted parent re-attaches instead of re-spawning). A non-terminal
|
|
238
275
|
* child suspends the parent `waiting_for_child` on the snapshot substrate (the wake carries the
|
|
@@ -260,6 +297,15 @@ export declare class WorkerWorkflowHost implements WorkflowHost {
|
|
|
260
297
|
* wake resolves this very await, heap intact. Without a freeze substrate EVERY sleep holds,
|
|
261
298
|
* whatever its length (the no-substrate rule: snapshot or hold, never replay). */
|
|
262
299
|
private sleepSeam;
|
|
300
|
+
/** The protocol's `shell` capability. Runs under the same abort/freeze gate as every hook, so
|
|
301
|
+
* a freeze never snapshots around an unguarded seam and an aborted run rejects promptly. */
|
|
302
|
+
shell(cmd: string, opts: ShellOptions | undefined): Promise<ShellResult>;
|
|
303
|
+
/** Live budget state for `usage.get` — every dimension `{spent, cap, remaining}`. */
|
|
304
|
+
usage(): Promise<UsageSnapshot>;
|
|
305
|
+
/** `auth.idToken(audience)` — minted per call by the broker via {@link RuntimeContext}. */
|
|
306
|
+
idToken(audience: string): Promise<string>;
|
|
307
|
+
/** `auth.apiToken()` — the run's short-lived, manifest-scoped public-API bearer. */
|
|
308
|
+
apiToken(): Promise<string>;
|
|
263
309
|
/** Resolve any {@link SleepArg} shape to a millisecond duration from now. */
|
|
264
310
|
private resolveSleepMs;
|
|
265
311
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// WorkerWorkflowHost — the
|
|
2
|
-
//
|
|
1
|
+
// WorkerWorkflowHost — the runner's capability layer behind the host protocol (the
|
|
2
|
+
// workflow-format redesign). The protocol server's `HostCapabilities` seam delegates here:
|
|
3
3
|
//
|
|
4
4
|
// agent(prompt, opts) → an ephemeral agent leaf (the demoted agent loop)
|
|
5
5
|
// sleep(arg) → an IN-PROCESS hold (hold-and-pay; no checkpoint, no exit)
|
|
@@ -281,11 +281,11 @@ export class WorkerWorkflowHost {
|
|
|
281
281
|
humanInput(opts) {
|
|
282
282
|
return this.guarded(() => this.humanInputSeam(opts));
|
|
283
283
|
}
|
|
284
|
-
|
|
284
|
+
humanInputSeam(opts) {
|
|
285
285
|
const seq = this.seq.next();
|
|
286
286
|
const key = opts.key ?? `seam-${String(seq)}`;
|
|
287
287
|
const expiresAt = this.timeoutExpiry(opts.timeout);
|
|
288
|
-
|
|
288
|
+
return this.awaitGate({
|
|
289
289
|
reason: "human_input",
|
|
290
290
|
seq,
|
|
291
291
|
humanInput: {
|
|
@@ -296,18 +296,24 @@ export class WorkerWorkflowHost {
|
|
|
296
296
|
// A timeout only matters with a wake to fire at; carry onTimeout alongside expiresAt.
|
|
297
297
|
...(expiresAt !== null ? { expiresAt, onTimeout: opts.onTimeout ?? "fail" } : {}),
|
|
298
298
|
},
|
|
299
|
-
};
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
/** Wait a registered gate out on whichever substrate the run has: freeze in place on the
|
|
302
|
+
* snapshot fleet, or (self-hosted runner / local dev) hold the live process and poll until a
|
|
303
|
+
* person answers. The one place the freeze-vs-hold decision for a gate is made. */
|
|
304
|
+
async awaitGate(signal) {
|
|
300
305
|
const freeze = this.deps.freeze;
|
|
301
306
|
if (freeze !== undefined) {
|
|
302
|
-
return await this.freezeHumanInput(freeze, signal, key);
|
|
307
|
+
return await this.freezeHumanInput(freeze, signal, signal.humanInput.key);
|
|
303
308
|
}
|
|
304
|
-
|
|
305
|
-
return normalizeHumanInputResult(await this.holdForAnswer(seq, signal.humanInput));
|
|
309
|
+
return normalizeHumanInputResult(await this.holdForAnswer(signal.seq, signal.humanInput));
|
|
306
310
|
}
|
|
307
311
|
/**
|
|
308
312
|
* Park the run at a BUDGET gate and resolve with the responder's answer (docs/SUSPEND_POLICY.md
|
|
309
|
-
* Decision 3). Called
|
|
310
|
-
* breached
|
|
313
|
+
* Decision 3). Called when any budget cap (`max_usd` / `max_tokens` / `max_compute_seconds`) is
|
|
314
|
+
* breached — by the leaf executor's `streamModel` seam (from INSIDE an in-flight `agent()`) and by
|
|
315
|
+
* the host's own `sleep`/`shell`/`workflows.call` seams (from inside their `guarded()` bodies).
|
|
316
|
+
* Either way the caller is already work-tracked, which drives two deliberate differences from
|
|
311
317
|
* {@link humanInputSeam}:
|
|
312
318
|
*
|
|
313
319
|
* - **No `guarded()` wrapper.** The enclosing `agent()` seam already counted this leaf as work via
|
|
@@ -323,20 +329,12 @@ export class WorkerWorkflowHost {
|
|
|
323
329
|
* the inbox, and is answered by the same machinery as any other gate. No timeout: an unanswered
|
|
324
330
|
* budget gate is aged out by the control plane's inactive-cancel reaper, not by a wake we schedule.
|
|
325
331
|
*/
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
const key = BUDGET_GATE_KEY;
|
|
329
|
-
const signal = {
|
|
332
|
+
budgetClearance(gate) {
|
|
333
|
+
return this.awaitGate({
|
|
330
334
|
reason: "budget",
|
|
331
|
-
seq,
|
|
332
|
-
humanInput: { key, prompt: gate.prompt, inputSpec: gate.inputSpec },
|
|
333
|
-
};
|
|
334
|
-
const freeze = this.deps.freeze;
|
|
335
|
-
if (freeze !== undefined) {
|
|
336
|
-
return await this.freezeHumanInput(freeze, signal, key);
|
|
337
|
-
}
|
|
338
|
-
// No freeze substrate (self-hosted runner / local dev): hold the live process until answered.
|
|
339
|
-
return normalizeHumanInputResult(await this.holdForAnswer(seq, signal.humanInput));
|
|
335
|
+
seq: this.seq.next(),
|
|
336
|
+
humanInput: { key: BUDGET_GATE_KEY, prompt: gate.prompt, inputSpec: gate.inputSpec },
|
|
337
|
+
});
|
|
340
338
|
}
|
|
341
339
|
/** Absolute wake time for a `humanInput({ timeout })`, or null when there is none / it's unparseable. */
|
|
342
340
|
timeoutExpiry(timeout) {
|
|
@@ -344,7 +342,12 @@ export class WorkerWorkflowHost {
|
|
|
344
342
|
return ms === null ? null : this.now() + ms;
|
|
345
343
|
}
|
|
346
344
|
callWorkflow(slug, input, opts) {
|
|
347
|
-
return this.guarded(() =>
|
|
345
|
+
return this.guarded(async () => {
|
|
346
|
+
// Park BEFORE dispatching the child: a budget-breached parent doesn't get to spawn more
|
|
347
|
+
// (org-billed) work until a responder clears the breach.
|
|
348
|
+
await this.deps.budgetGate?.clear();
|
|
349
|
+
return this.callWorkflowSeam(slug, input, opts);
|
|
350
|
+
});
|
|
348
351
|
}
|
|
349
352
|
/** The `workflows.call` seam: start the child once (idempotently — the child's run row is the
|
|
350
353
|
* durable memo, so a crash-restarted parent re-attaches instead of re-spawning). A non-terminal
|
|
@@ -358,7 +361,7 @@ export class WorkerWorkflowHost {
|
|
|
358
361
|
}
|
|
359
362
|
const child = await this.deps.children.start(slug, input, opts, this.deps.signal);
|
|
360
363
|
if (child.status === "completed")
|
|
361
|
-
return child.output;
|
|
364
|
+
return { output: child.output, outputSchema: child.outputSchema };
|
|
362
365
|
if (child.status === "failed" || child.status === "cancelled") {
|
|
363
366
|
throw new AppError(ErrorCode.VALIDATION_FAILED, `Called workflow "${slug}" ${child.status} (run ${child.childRunId})`, { childRunId: child.childRunId, status: child.status });
|
|
364
367
|
}
|
|
@@ -377,38 +380,39 @@ export class WorkerWorkflowHost {
|
|
|
377
380
|
if (outcome.wake.kind !== "workflow_call" || woken === undefined) {
|
|
378
381
|
throw new AppError(ErrorCode.INTERNAL_ERROR, `Wake value does not carry the awaited child run (got kind "${outcome.wake.kind}")`, { kind: "wake_mismatch", childRunId: child.childRunId });
|
|
379
382
|
}
|
|
380
|
-
if (woken.status === "completed")
|
|
381
|
-
|
|
383
|
+
if (woken.status === "completed") {
|
|
384
|
+
// The wake payload carries the finalized child's OUTPUT but not the callee's schema; one
|
|
385
|
+
// post-wake poll fetches it so a typed child's return still revives. Best-effort: a poll
|
|
386
|
+
// failure degrades to plain JSON (null schema), never fails the parent.
|
|
387
|
+
let outputSchema = null;
|
|
388
|
+
try {
|
|
389
|
+
outputSchema = (await this.deps.children.poll(woken.run_id))?.outputSchema ?? null;
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
outputSchema = null;
|
|
393
|
+
}
|
|
394
|
+
return { output: woken.output, outputSchema };
|
|
395
|
+
}
|
|
382
396
|
throw new AppError(ErrorCode.VALIDATION_FAILED, `Called workflow "${slug}" ${woken.status} (run ${woken.run_id})`, { childRunId: woken.run_id, status: woken.status });
|
|
383
397
|
}
|
|
384
398
|
/** Fire-and-forget trigger of another workflow; resolves to the new run's id (no hold/poll). */
|
|
385
399
|
runWorkflow(slug, input, opts) {
|
|
386
|
-
return this.guarded(
|
|
387
|
-
const id = await this.deps.children.run(slug, input, opts);
|
|
388
|
-
return id;
|
|
389
|
-
});
|
|
400
|
+
return this.guarded(() => this.deps.children.run(slug, input, opts));
|
|
390
401
|
}
|
|
391
402
|
/** Provision a durable schedule (one-shot/recurring) that fires the target later; resolves to the
|
|
392
403
|
* new schedule's id WITHOUT running it now. Satisfies the SDK's optional `scheduleWorkflow`. */
|
|
393
404
|
scheduleWorkflow(slug, input, opts) {
|
|
394
|
-
return this.guarded(
|
|
395
|
-
const id = await this.deps.children.schedule(slug, input, opts);
|
|
396
|
-
return id;
|
|
397
|
-
});
|
|
405
|
+
return this.guarded(() => this.deps.children.schedule(slug, input, opts));
|
|
398
406
|
}
|
|
399
407
|
getSecret(name) {
|
|
400
|
-
return this.guarded(
|
|
401
|
-
const value = await this.deps.secrets.get(name);
|
|
402
|
-
return value;
|
|
403
|
-
});
|
|
408
|
+
return this.guarded(() => this.deps.secrets.get(name));
|
|
404
409
|
}
|
|
405
410
|
writeArtifact(name, contentType, body, metadata) {
|
|
406
411
|
return this.guarded(async () => {
|
|
407
412
|
if (this.deps.writeArtifact === undefined) {
|
|
408
413
|
throw new AppError(ErrorCode.VALIDATION_FAILED, "artifacts.write is not available in this runtime");
|
|
409
414
|
}
|
|
410
|
-
|
|
411
|
-
return ref;
|
|
415
|
+
return await this.deps.writeArtifact(name, contentType, body, metadata);
|
|
412
416
|
});
|
|
413
417
|
}
|
|
414
418
|
/** `computer.openBrowser()`: open a program-owned, in-VM browser session (the browser tier of
|
|
@@ -438,7 +442,12 @@ export class WorkerWorkflowHost {
|
|
|
438
442
|
return { ...rest, mcp: [...(rest.mcp ?? []), ref] };
|
|
439
443
|
}
|
|
440
444
|
sleep(arg) {
|
|
441
|
-
return this.guarded(() =>
|
|
445
|
+
return this.guarded(async () => {
|
|
446
|
+
// A compute-breached run parks HERE rather than sleeping first and parking later — no forward
|
|
447
|
+
// progress while a budget cap is breached (the budget-gate park-at-next-seam contract).
|
|
448
|
+
await this.deps.budgetGate?.clear();
|
|
449
|
+
return this.sleepSeam(arg);
|
|
450
|
+
});
|
|
442
451
|
}
|
|
443
452
|
/** A short sleep HOLDS the task in-process (cheaper than a snapshot round-trip); a long one
|
|
444
453
|
* (≥ {@link SUSPEND_THRESHOLD_MS}) SUSPENDS on the snapshot substrate — the VM freezes and the
|
|
@@ -476,6 +485,37 @@ export class WorkerWorkflowHost {
|
|
|
476
485
|
await this.deps.onBeforeSleep();
|
|
477
486
|
await this.sleeper.hold(holdMs, this.deps.signal);
|
|
478
487
|
}
|
|
488
|
+
/** The protocol's `shell` capability. Runs under the same abort/freeze gate as every hook, so
|
|
489
|
+
* a freeze never snapshots around an unguarded seam and an aborted run rejects promptly. */
|
|
490
|
+
shell(cmd, opts) {
|
|
491
|
+
return this.guarded(async () => {
|
|
492
|
+
if (this.deps.shell === undefined) {
|
|
493
|
+
throw new AppError(ErrorCode.VALIDATION_FAILED, "shell is not available in this runtime");
|
|
494
|
+
}
|
|
495
|
+
// Compute burns hardest in long shell commands — a breach parks before starting another one.
|
|
496
|
+
await this.deps.budgetGate?.clear();
|
|
497
|
+
return await this.deps.shell(cmd, opts);
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
/** Live budget state for `usage.get` — every dimension `{spent, cap, remaining}`. */
|
|
501
|
+
usage() {
|
|
502
|
+
return this.guarded(async () => {
|
|
503
|
+
if (this.deps.usage === undefined) {
|
|
504
|
+
throw new AppError(ErrorCode.VALIDATION_FAILED, "usage.get is not available in this runtime");
|
|
505
|
+
}
|
|
506
|
+
// Promise.resolve keeps the arrow's async-without-await acceptable to require-await; the
|
|
507
|
+
// arrow stays async so the guard above REJECTS (guarded's contract) rather than throwing.
|
|
508
|
+
return Promise.resolve(this.deps.usage());
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
/** `auth.idToken(audience)` — minted per call by the broker via {@link RuntimeContext}. */
|
|
512
|
+
idToken(audience) {
|
|
513
|
+
return this.guarded(() => this.deps.runtime.idToken(audience));
|
|
514
|
+
}
|
|
515
|
+
/** `auth.apiToken()` — the run's short-lived, manifest-scoped public-API bearer. */
|
|
516
|
+
apiToken() {
|
|
517
|
+
return this.guarded(() => this.deps.runtime.apiToken());
|
|
518
|
+
}
|
|
479
519
|
/** Resolve any {@link SleepArg} shape to a millisecond duration from now. */
|
|
480
520
|
resolveSleepMs(arg) {
|
|
481
521
|
if (typeof arg === "number")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@boardwalk-labs/runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
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": {
|
|
@@ -53,9 +53,10 @@
|
|
|
53
53
|
"prebuild": "prebuildify --napi --strip -t 24.0.0"
|
|
54
54
|
},
|
|
55
55
|
"dependencies": {
|
|
56
|
-
"@boardwalk-labs/engine": "0.
|
|
57
|
-
"@boardwalk-labs/workflow": "0.
|
|
56
|
+
"@boardwalk-labs/engine": "0.3.0",
|
|
57
|
+
"@boardwalk-labs/workflow": "0.3.0",
|
|
58
58
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
59
|
+
"ajv": "^8.20.0",
|
|
59
60
|
"node-gyp-build": "^4.8.4",
|
|
60
61
|
"tar": "^7.5.16",
|
|
61
62
|
"undici": "^6.27.0",
|