@coreplane/switchboard 1.234.0 → 1.236.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/dist/assets/Dockerfile +11 -0
- package/dist/assets/config/config.example.yaml +9 -4
- package/dist/assets/deploy/cloudflare-memory/worker.ts +12 -0
- package/dist/assets/deploy/cloudflare-resident/Dockerfile +18 -0
- package/dist/assets/deploy/cloudflare-resident/gc.ts +6 -9
- package/dist/assets/deploy/cloudflare-resident/worker.ts +225 -119
- package/dist/assets/deploy/cloudflare-sandbox/Dockerfile +11 -0
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +143 -13
- package/dist/assets/package-lock.json +212 -3
- package/dist/assets/package.json +4 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/core/coordinator/contract.ts +24 -4
- package/dist/assets/src/core/coordinator/driver.ts +31 -3
- package/dist/assets/src/core/runEvents.ts +43 -1
- package/dist/assets/src/core/runFriction.ts +1 -0
- package/dist/assets/src/core/ship/contract.ts +3 -1
- package/dist/assets/src/core/ship/coordinator.ts +213 -29
- package/dist/assets/src/core/trace/types.ts +5 -0
- package/dist/assets/src/execution/residentCleanliness.ts +24 -0
- package/dist/assets/src/execution/residentDisk.ts +21 -10
- package/dist/assets/src/execution/residentDiskBudget.ts +7 -6
- package/dist/assets/src/execution/sandboxErrors.ts +77 -0
- package/dist/assets/src/execution/sandboxIdle.ts +220 -0
- package/dist/assets/src/execution/sandboxStart.ts +90 -0
- package/dist/assets/web/dist/.vite/manifest.json +437 -422
- package/dist/assets/web/dist/assets/{AppShell-lYVcj-k7.js → AppShell-DZdz_sl7.js} +1 -1
- package/dist/assets/web/dist/assets/{CostsPage-D-v8am88.js → CostsPage-BfJsol9y.js} +1 -1
- package/dist/assets/web/dist/assets/{DeliveryPage-CvlWP7Eq.js → DeliveryPage-CvInfu7y.js} +1 -1
- package/dist/assets/web/dist/assets/{NotFoundPage-BzWS4Ca1.js → NotFoundPage-d37EP8hS.js} +1 -1
- package/dist/assets/web/dist/assets/ResidentDetailPage-DQSD0SZD.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-CZUGLen6.js +1 -0
- package/dist/assets/web/dist/assets/RunFoldRow-Bngn4GjF.js +9 -0
- package/dist/assets/web/dist/assets/RunRoutePage-a-tMrirB.js +6 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-s0VKmRda.js +1 -0
- package/dist/assets/web/dist/assets/{RunsTabs-DEICQ4FY.js → RunsTabs-CyCdOhJ2.js} +1 -1
- package/dist/assets/web/dist/assets/ScheduledPage-ByCmyrDs.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-DhKEokGK.js +1 -0
- package/dist/assets/web/dist/assets/{StatusDot-Ct6AtHhS.js → StatusDot-D-BPSPpw.js} +1 -1
- package/dist/assets/web/dist/assets/{Tooltip-qol5Boof.js → Tooltip-DPDx5tXT.js} +1 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-0eiekTHQ.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-Cw130Zyi.js → angular-html-Ni1Xil2G.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-DY1m4C2s.js → angular-ts-oCgo6p05.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-W6Ty7v05.js → apl-CiWYl0gh.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-DlLVHwVk.js → astro-B-VeKZ_J.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-Bco3tDh4.js → blade-BIWEISKd.js} +1 -1
- package/dist/assets/web/dist/assets/{c-1LaFY_cj.js → c-DHxA5Byp.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-B-OvrOL5.js → chapel-D4qGLw-Q.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-Ch3EPlHu.js → cobol-BznX5Q-u.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-hZuITnto.js → coffee-CVr8Idlj.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-B8iRVo8m.js → cpp-wrk6NfW4.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-DKveI7lr.js → crystal-CGYWMeH1.js} +1 -1
- package/dist/assets/web/dist/assets/{css-_2_CD-rr.js → css-BgGytMsy.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-DwtJj-ou.js → dist-DSkVB16z.js} +2 -2
- package/dist/assets/web/dist/assets/durationTone-DYogEPEk.js +1 -0
- package/dist/assets/web/dist/assets/{edge-BenMDPct.js → edge-CdR6AU9v.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-CBbdEqG6.js → elixir-CQRb3PZx.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-DMub_2lY.js → elm-CpuA6W8B.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-CI9He_Up.js → erb-CvlrblqH.js} +1 -1
- package/dist/assets/web/dist/assets/{favicon-BQsePYv5.js → favicon-CSt-qDvc.js} +1 -1
- package/dist/assets/web/dist/assets/{git-rebase-BBfhnyF8.js → git-rebase-COnovCVc.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-CR9Fxrau.js → glimmer-js-C-DS4g_J.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-CwFwX_XS.js → glimmer-ts-DTkorWRI.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-Daiodp1r.js → glsl-CH51-JW7.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-DZvrsEkB.js → graphql-BO-XymNv.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-BaXOzdg7.js → hack-C-N0oicf.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-CMskq_VA.js → haml-BfmdtWoP.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-Dhw_f7jD.js → handlebars-BqhO_te-.js} +1 -1
- package/dist/assets/web/dist/assets/{html-C9k99z0z.js → html-DUEZlTKV.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-VckPItXN.js → html-derivative-DOUJTS8A.js} +1 -1
- package/dist/assets/web/dist/assets/{http-DXU_3h9v.js → http-j7UH0Cqv.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-KU24JNdp.js → hurl-D6ZaBLJk.js} +1 -1
- package/dist/assets/web/dist/assets/indexRow-BjXBzI5Z.js +1 -0
- package/dist/assets/web/dist/assets/{java-BbZjTNgf.js → java-dugWx3wi.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-DXCdqcZZ.js → javascript-CK4GC4JO.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-pDlsjTVp.js → jinja-DrLKIyKG.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-DsXDk66e.js → jison-CS1Th-TO.js} +1 -1
- package/dist/assets/web/dist/assets/{json-tCxXfRgG.js → json-CkBqdVys.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-B0ZYkCmi.js → jsx-CCxdg7n3.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-BLzzeI6x.js → julia-BalKW2hA.js} +1 -1
- package/dist/assets/web/dist/assets/{just-DvD2_60c.js → just-BKw1G40D.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-511zn3h7.js → latex-Dfad6ZN-.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-BD0BkMox.js → liquid-f_CMGBvd.js} +1 -1
- package/dist/assets/web/dist/assets/{indexFormat-B-pd4r7Y.js → localIso-CNA-bhS-.js} +1 -1
- package/dist/assets/web/dist/assets/{lua-NFicKm6N.js → lua-BcMK_uKM.js} +1 -1
- package/dist/assets/web/dist/assets/main-B77vAZVg.css +1 -0
- package/dist/assets/web/dist/assets/main-DR8iKpNt.js +28 -0
- package/dist/assets/web/dist/assets/{marko-1-G_nEXK.js → marko-B0oNLx2i.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-DUe3AQLp.js → mdc-Bc_MmIyq.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-Dg379_bA.js → nginx-Cmxv8RIt.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-BnFq97ZO.js → nim-BtgRfaY0.js} +1 -1
- package/dist/assets/web/dist/assets/{org-F0uiwGvq.js → org-Dl9EzlHh.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-B0euczkl.js → perl-BmF7x7BB.js} +1 -1
- package/dist/assets/web/dist/assets/{php-DkL3k_n7.js → php-rH6CVptQ.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-L_OjjZP4.js → pug-CIN5Ccck.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-BMjB00Zz.js → qml-BpLS3RFr.js} +1 -1
- package/dist/assets/web/dist/assets/{r-DoeLdnqR.js → r-BUMF3B-H.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-BzYNkAWy.js → razor-B7sTBPdT.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-CPmElMk3.js → regexp-h2SvwrsJ.js} +1 -1
- package/dist/assets/web/dist/assets/residentsModel-VSJEepL1.js +1 -0
- package/dist/assets/web/dist/assets/{rst-DrsjgfI7.js → rst-DLOWEsD2.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-cB42ppvx.js → ruby-Cl_-I4k3.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-R5Y3NM_I.js → sas-Cm4M-BvV.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-C-zWxV9s.js → scss-CEFCEQUF.js} +1 -1
- package/dist/assets/web/dist/assets/{shellscript-oJF96aAU.js → shellscript-CE6zb5eS.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-CznECKyi.js → shellsession-B4fDVzrM.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-wjHLSRag.js → soy-DwXCMpjE.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-C_BM8IOW.js → sql-DrnAKnyD.js} +1 -1
- package/dist/assets/web/dist/assets/{stata-D31HO8aQ.js → stata-BzlwPWD6.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-CKCLyplA.js → surrealql-CeUieZXd.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-3geWk2iu.js → svelte-BERIpIKR.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-DiJr_hTD.js → templ-6FSuUDwg.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-BEJGWyeF.js → tex-SWCyMwNX.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-C8Mzdpxx.js → ts-tags-VWj7gWgY.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-UQiSm_p3.js → tsx-B6dzfL8L.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-_Pvzkeo9.js → twig-BDXsr482.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-BWMkINKK.js → typescript-s100Dt8y.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-Bdg9m-e7.js → typst-WfyEAUdt.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-CDZrrAZ8.js → vue-CkXOOJ-6.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-tAlpNhfN.js → vue-html-CEpzdcau.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-C27UF_rh.js → vue-vine-D9dsfiFA.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-CxeDr9Zh.js → xml-iBDofbJq.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-Bpi0uUnM.js → xsl-CqbJF4hD.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-BtUgZ1GN.js → yaml-D9i6OxYe.js} +1 -1
- package/dist/cli.js +2336 -1192
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/ResidentDetailPage-C_WNBUao.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-D1sTyaZ_.js +0 -1
- package/dist/assets/web/dist/assets/RunFoldRow-C_bcTSjA.js +0 -5
- package/dist/assets/web/dist/assets/RunRoutePage-BWTi_6PG.js +0 -10
- package/dist/assets/web/dist/assets/RunsIndexPage-H6GkSv0a.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-BLcPzm1S.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-CIhTEwN7.js +0 -1
- package/dist/assets/web/dist/assets/indexRow-Bnj883ii.js +0 -1
- package/dist/assets/web/dist/assets/localIso-L06jV29p.js +0 -1
- package/dist/assets/web/dist/assets/main-CRtlGxRm.js +0 -28
- package/dist/assets/web/dist/assets/main-Dm11o0hc.css +0 -1
- package/dist/assets/web/dist/assets/residentDiskBudget-BMBKlYRH.js +0 -1
|
@@ -43,6 +43,8 @@ import {
|
|
|
43
43
|
recycledMidCommandMessage,
|
|
44
44
|
} from "../../src/execution/sandboxLifecycle.js";
|
|
45
45
|
import { envFromRequest } from "../../src/execution/sandboxEnv.js";
|
|
46
|
+
import { IdleGuard, type IdleGuardHost } from "../../src/execution/sandboxIdle.js";
|
|
47
|
+
import { StartGate, type StartGateHost, type StartingCause } from "../../src/execution/sandboxStart.js";
|
|
46
48
|
import { RUNTIME_REPLACEMENT_WORDING, isRuntimeUnreachableSignal } from "../../src/execution/residentRefresh.js";
|
|
47
49
|
import {
|
|
48
50
|
fleetBusyAnswer,
|
|
@@ -52,6 +54,8 @@ import {
|
|
|
52
54
|
runtimeUnreachableAnswer,
|
|
53
55
|
runtimeUnreachableExecAnswer,
|
|
54
56
|
SandboxRuntimeUnreachableError,
|
|
57
|
+
sandboxStartingAnswer,
|
|
58
|
+
sandboxStartingExecAnswer,
|
|
55
59
|
thrownShape,
|
|
56
60
|
thrownText,
|
|
57
61
|
} from "../../src/execution/sandboxErrors.js";
|
|
@@ -166,16 +170,93 @@ interface FileRefusal {
|
|
|
166
170
|
reason?: string;
|
|
167
171
|
}
|
|
168
172
|
|
|
173
|
+
/** The SDK's own limit on the warm-up command (`true`): generous, since the
|
|
174
|
+
* SDK's start — an instance grant up to 30 s, the runtime's port up to 90 s
|
|
175
|
+
* by its defaults — happens inside this one call. The executor's wait for
|
|
176
|
+
* the `sandbox-starting` token is bounded on its own side. */
|
|
177
|
+
const WARM_UP_BACKSTOP_MS = 4 * 60_000;
|
|
178
|
+
|
|
179
|
+
/** Durable Object storage key of the idle ledger's served-time. */
|
|
180
|
+
const IDLE_LEDGER_KEY = "switchboard.idle.lastServedAt";
|
|
181
|
+
/** The scheduled-callback name of the idle sweep (a method below). */
|
|
182
|
+
const IDLE_SWEEP_CALLBACK = "idleSweep";
|
|
183
|
+
|
|
169
184
|
export class SwitchboardSandbox extends Sandbox<Env> {
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
//
|
|
175
|
-
// 5 minutes
|
|
176
|
-
//
|
|
185
|
+
// The SDK's idle setting (its own default is 10 min), kept so its alarm
|
|
186
|
+
// loop calls `onActivityExpired` on this cadence — but on the 0.13 line that
|
|
187
|
+
// hook is a question to the runtime ("anything still running?"), not a
|
|
188
|
+
// deadline, and the answer kept twenty-five containers awake for 16 hours.
|
|
189
|
+
// The deadline itself is the guard's (docs/reference/specs/execution.md
|
|
190
|
+
// item 22): 5 minutes after the last request this object served, the
|
|
191
|
+
// container is destroyed, whatever runs inside; the shell-level `timeout`
|
|
192
|
+
// stays the one deadline a command can hit (item 2), since a request in
|
|
193
|
+
// flight is service.
|
|
177
194
|
sleepAfter = SANDBOX_SLEEP_AFTER;
|
|
178
195
|
|
|
196
|
+
private readonly idle: IdleGuard;
|
|
197
|
+
private readonly gate: StartGate;
|
|
198
|
+
|
|
199
|
+
constructor(...args: ConstructorParameters<typeof Sandbox<Env>>) {
|
|
200
|
+
super(...args);
|
|
201
|
+
this.idle = new IdleGuard(this.idleHost());
|
|
202
|
+
this.gate = new StartGate(this.startHost());
|
|
203
|
+
// Every wake, including the one the SDK's own alarm causes on a leaked
|
|
204
|
+
// container: the baseline is read and a sweep armed before any request.
|
|
205
|
+
this.ctx.blockConcurrencyWhile(() => this.idle.wake());
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** The guard's view of this object: the platform's running flag, the SDK's
|
|
209
|
+
* schedule table and clean destroy, the platform's kill, and storage. */
|
|
210
|
+
private idleHost(): IdleGuardHost {
|
|
211
|
+
return {
|
|
212
|
+
now: systemClock,
|
|
213
|
+
containerRunning: () => this.ctx.container?.running,
|
|
214
|
+
sweepScheduled: async () => (await this.listSchedules(IDLE_SWEEP_CALLBACK)).length > 0,
|
|
215
|
+
scheduleSweep: async (delayMs) => {
|
|
216
|
+
await this.schedule(Math.ceil(delayMs / 1000), IDLE_SWEEP_CALLBACK);
|
|
217
|
+
},
|
|
218
|
+
destroySandbox: () => this.destroy(),
|
|
219
|
+
killContainer: async () => {
|
|
220
|
+
await this.ctx.container?.destroy();
|
|
221
|
+
},
|
|
222
|
+
loadLastServedAt: () => this.ctx.storage.get<number>(IDLE_LEDGER_KEY),
|
|
223
|
+
saveLastServedAt: (at) => this.ctx.storage.put(IDLE_LEDGER_KEY, at),
|
|
224
|
+
log: (event) => console.log(JSON.stringify({ ...event, thread: this.ctx.id.name ?? this.ctx.id.toString() })),
|
|
225
|
+
wait: (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/** The start gate's view of this object (docs/reference/specs/execution.md
|
|
230
|
+
* item 23): the platform's running flag, and a warm-up that is one trivial
|
|
231
|
+
* command through the SDK — which does the start (instance grant, image
|
|
232
|
+
* pull, boot, the runtime's port) and answers when the runtime does. The
|
|
233
|
+
* warm-up runs inside the idle ledger so the guard sees a request in flight
|
|
234
|
+
* for as long as the start takes. */
|
|
235
|
+
private startHost(): StartGateHost {
|
|
236
|
+
return {
|
|
237
|
+
now: systemClock,
|
|
238
|
+
containerRunning: () => this.ctx.container?.running,
|
|
239
|
+
warmUp: () =>
|
|
240
|
+
this.idle.served(async () => {
|
|
241
|
+
const proc = await createExtensionProcessSandbox(this).exec(["true"], { timeout: WARM_UP_BACKSTOP_MS });
|
|
242
|
+
await proc.output({ encoding: "utf8", timeout: WARM_UP_BACKSTOP_MS });
|
|
243
|
+
}),
|
|
244
|
+
log: (event) => console.log(JSON.stringify({ ...event, thread: this.ctx.id.name ?? this.ctx.id.toString() })),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The idle sweep, scheduled by the guard through the SDK's schedule table
|
|
249
|
+
* and re-armed by it while a container is running. */
|
|
250
|
+
async idleSweep(): Promise<void> {
|
|
251
|
+
await this.idle.sweep();
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The SDK's activity expiry, answered by the guard's verdict instead of the
|
|
255
|
+
* runtime's process probes. */
|
|
256
|
+
override async onActivityExpired(): Promise<void> {
|
|
257
|
+
await this.idle.expired();
|
|
258
|
+
}
|
|
259
|
+
|
|
179
260
|
/** One command as one supervised process (docs/reference/specs/execution.md
|
|
180
261
|
* item 4): `timeout -k 10 <secs> bash -c 'mkdir -p /workspace && cd
|
|
181
262
|
* /workspace && <command>'`, the caller's env on the process alone (item 5),
|
|
@@ -188,6 +269,28 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
188
269
|
command: string,
|
|
189
270
|
execTimeoutSecs: number,
|
|
190
271
|
envVars: Record<string, string>,
|
|
272
|
+
): Promise<ExecAnswer | ExecFailure> {
|
|
273
|
+
const startedAt = systemClock();
|
|
274
|
+
return this.idle.served(async () => {
|
|
275
|
+
try {
|
|
276
|
+
return await this.gate.through(
|
|
277
|
+
() => this.execute(command, execTimeoutSecs, envVars),
|
|
278
|
+
(cause) => sandboxStartingExecAnswer(cause),
|
|
279
|
+
);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
// The warm-up's own failure, handed on by the gate: a full fleet or a
|
|
282
|
+
// silent control port keeps its name; anything else propagates.
|
|
283
|
+
return this.execFailure(err, startedAt);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/** `runCommand` without the ledger entry: the body, and the internal caller
|
|
289
|
+
* (`readBase64`'s stat) that is already inside a served request. */
|
|
290
|
+
private async execute(
|
|
291
|
+
command: string,
|
|
292
|
+
execTimeoutSecs: number,
|
|
293
|
+
envVars: Record<string, string>,
|
|
191
294
|
): Promise<ExecAnswer | ExecFailure> {
|
|
192
295
|
const startedAt = systemClock();
|
|
193
296
|
const full = `mkdir -p ${WORKDIR} && cd ${WORKDIR} && ${command}`;
|
|
@@ -276,8 +379,20 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
276
379
|
}
|
|
277
380
|
}
|
|
278
381
|
|
|
382
|
+
/** A file route's answer while the container starts: HTTP 503 with the
|
|
383
|
+
* token, like a full fleet's. */
|
|
384
|
+
private startingRefusal(cause: StartingCause): FileRefusal {
|
|
385
|
+
const { error, reason } = sandboxStartingAnswer(cause);
|
|
386
|
+
return { error, status: 503, reason };
|
|
387
|
+
}
|
|
388
|
+
|
|
279
389
|
async readText(path: string): Promise<{ content: string } | FileRefusal> {
|
|
280
|
-
return this.
|
|
390
|
+
return this.idle.served(() =>
|
|
391
|
+
this.gate.through(
|
|
392
|
+
() => this.fileOp(async () => ({ content: (await this.readFile(path, { encoding: "utf-8" })).content })),
|
|
393
|
+
(cause) => this.startingRefusal(cause),
|
|
394
|
+
),
|
|
395
|
+
);
|
|
281
396
|
}
|
|
282
397
|
|
|
283
398
|
/** `encoding: "base64"` (src/execution/binaryRead.ts): the size first, from
|
|
@@ -285,7 +400,16 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
285
400
|
* decoded bytes to it — an SDK read that came back short would otherwise
|
|
286
401
|
* pass as the file. A file over the cap is refused by name inside a 200. */
|
|
287
402
|
async readBase64(path: string): Promise<Base64ReadAnswer | FileRefusal> {
|
|
288
|
-
|
|
403
|
+
return this.idle.served(() =>
|
|
404
|
+
this.gate.through(
|
|
405
|
+
() => this.readBase64Now(path),
|
|
406
|
+
(cause) => this.startingRefusal(cause),
|
|
407
|
+
),
|
|
408
|
+
);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private async readBase64Now(path: string): Promise<Base64ReadAnswer | FileRefusal> {
|
|
412
|
+
const stat = await this.execute(statCommandFor(path), 60, {});
|
|
289
413
|
if ("error" in stat) {
|
|
290
414
|
// The stat's own named failure — a full fleet, a silent control port —
|
|
291
415
|
// is the read's, token included, so the executor waits as it would
|
|
@@ -309,10 +433,16 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
309
433
|
}
|
|
310
434
|
|
|
311
435
|
async write(path: string, content: string): Promise<{ ok: true } | FileRefusal> {
|
|
312
|
-
return this.
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
436
|
+
return this.idle.served(() =>
|
|
437
|
+
this.gate.through(
|
|
438
|
+
() =>
|
|
439
|
+
this.fileOp(async () => {
|
|
440
|
+
await this.writeFile(path, content);
|
|
441
|
+
return { ok: true as const };
|
|
442
|
+
}),
|
|
443
|
+
(cause) => this.startingRefusal(cause),
|
|
444
|
+
),
|
|
445
|
+
);
|
|
316
446
|
}
|
|
317
447
|
}
|
|
318
448
|
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.236.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.
|
|
9
|
+
"version": "1.236.0",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"web",
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
36
36
|
"@eslint/js": "^10.0.1",
|
|
37
|
+
"@opencode/protocol": "2.0.3",
|
|
38
|
+
"@opencode/schema": "2.0.3",
|
|
37
39
|
"@types/node": "^24.0.0",
|
|
40
|
+
"effect": "4.0.0-rc.112",
|
|
38
41
|
"eslint": "^10.10.0",
|
|
39
42
|
"eslint-config-prettier": "^10.1.8",
|
|
40
43
|
"eslint-plugin-vue": "^10.11.0",
|
|
@@ -5863,6 +5866,90 @@
|
|
|
5863
5866
|
"@chevrotain/types": "~11.1.2"
|
|
5864
5867
|
}
|
|
5865
5868
|
},
|
|
5869
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": {
|
|
5870
|
+
"version": "3.0.4",
|
|
5871
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.4.tgz",
|
|
5872
|
+
"integrity": "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ==",
|
|
5873
|
+
"cpu": [
|
|
5874
|
+
"arm64"
|
|
5875
|
+
],
|
|
5876
|
+
"dev": true,
|
|
5877
|
+
"license": "MIT",
|
|
5878
|
+
"optional": true,
|
|
5879
|
+
"os": [
|
|
5880
|
+
"darwin"
|
|
5881
|
+
]
|
|
5882
|
+
},
|
|
5883
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": {
|
|
5884
|
+
"version": "3.0.4",
|
|
5885
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.4.tgz",
|
|
5886
|
+
"integrity": "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w==",
|
|
5887
|
+
"cpu": [
|
|
5888
|
+
"x64"
|
|
5889
|
+
],
|
|
5890
|
+
"dev": true,
|
|
5891
|
+
"license": "MIT",
|
|
5892
|
+
"optional": true,
|
|
5893
|
+
"os": [
|
|
5894
|
+
"darwin"
|
|
5895
|
+
]
|
|
5896
|
+
},
|
|
5897
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": {
|
|
5898
|
+
"version": "3.0.4",
|
|
5899
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.4.tgz",
|
|
5900
|
+
"integrity": "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw==",
|
|
5901
|
+
"cpu": [
|
|
5902
|
+
"arm"
|
|
5903
|
+
],
|
|
5904
|
+
"dev": true,
|
|
5905
|
+
"license": "MIT",
|
|
5906
|
+
"optional": true,
|
|
5907
|
+
"os": [
|
|
5908
|
+
"linux"
|
|
5909
|
+
]
|
|
5910
|
+
},
|
|
5911
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": {
|
|
5912
|
+
"version": "3.0.4",
|
|
5913
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.4.tgz",
|
|
5914
|
+
"integrity": "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw==",
|
|
5915
|
+
"cpu": [
|
|
5916
|
+
"arm64"
|
|
5917
|
+
],
|
|
5918
|
+
"dev": true,
|
|
5919
|
+
"license": "MIT",
|
|
5920
|
+
"optional": true,
|
|
5921
|
+
"os": [
|
|
5922
|
+
"linux"
|
|
5923
|
+
]
|
|
5924
|
+
},
|
|
5925
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": {
|
|
5926
|
+
"version": "3.0.4",
|
|
5927
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.4.tgz",
|
|
5928
|
+
"integrity": "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ==",
|
|
5929
|
+
"cpu": [
|
|
5930
|
+
"x64"
|
|
5931
|
+
],
|
|
5932
|
+
"dev": true,
|
|
5933
|
+
"license": "MIT",
|
|
5934
|
+
"optional": true,
|
|
5935
|
+
"os": [
|
|
5936
|
+
"linux"
|
|
5937
|
+
]
|
|
5938
|
+
},
|
|
5939
|
+
"node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": {
|
|
5940
|
+
"version": "3.0.4",
|
|
5941
|
+
"resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.4.tgz",
|
|
5942
|
+
"integrity": "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ==",
|
|
5943
|
+
"cpu": [
|
|
5944
|
+
"x64"
|
|
5945
|
+
],
|
|
5946
|
+
"dev": true,
|
|
5947
|
+
"license": "MIT",
|
|
5948
|
+
"optional": true,
|
|
5949
|
+
"os": [
|
|
5950
|
+
"win32"
|
|
5951
|
+
]
|
|
5952
|
+
},
|
|
5866
5953
|
"node_modules/@napi-rs/lzma-linux-x64-gnu": {
|
|
5867
5954
|
"version": "1.5.1",
|
|
5868
5955
|
"resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz",
|
|
@@ -6155,6 +6242,28 @@
|
|
|
6155
6242
|
"dev": true,
|
|
6156
6243
|
"license": "MIT"
|
|
6157
6244
|
},
|
|
6245
|
+
"node_modules/@opencode/protocol": {
|
|
6246
|
+
"version": "2.0.3",
|
|
6247
|
+
"resolved": "https://registry.npmjs.org/@opencode/protocol/-/protocol-2.0.3.tgz",
|
|
6248
|
+
"integrity": "sha512-DNHbkLDTuAyMsmVdnjuKulcfoDbllW2dxt4W19r2aJEYvswJpEO9168CSK6Sjbx1FOh/B9yL7SQgoDRMd4K5Vw==",
|
|
6249
|
+
"dev": true,
|
|
6250
|
+
"license": "MIT",
|
|
6251
|
+
"dependencies": {
|
|
6252
|
+
"@opencode/schema": "2.0.3",
|
|
6253
|
+
"effect": "4.0.0-rc.112"
|
|
6254
|
+
}
|
|
6255
|
+
},
|
|
6256
|
+
"node_modules/@opencode/schema": {
|
|
6257
|
+
"version": "2.0.3",
|
|
6258
|
+
"resolved": "https://registry.npmjs.org/@opencode/schema/-/schema-2.0.3.tgz",
|
|
6259
|
+
"integrity": "sha512-thPeBbqw4+SkZ/0LOuqfahSRGuWogRa/wwfIVyiQYsoE0vzLx8XUpHQ+FSUWkdz6llDtP0+ZsyBqXUMpXmPgbg==",
|
|
6260
|
+
"dev": true,
|
|
6261
|
+
"license": "MIT",
|
|
6262
|
+
"dependencies": {
|
|
6263
|
+
"@standard-schema/spec": "1.1.0",
|
|
6264
|
+
"effect": "4.0.0-rc.112"
|
|
6265
|
+
}
|
|
6266
|
+
},
|
|
6158
6267
|
"node_modules/@oxc-project/types": {
|
|
6159
6268
|
"version": "0.148.0",
|
|
6160
6269
|
"resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.148.0.tgz",
|
|
@@ -11840,6 +11949,17 @@
|
|
|
11840
11949
|
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
|
|
11841
11950
|
"license": "MIT"
|
|
11842
11951
|
},
|
|
11952
|
+
"node_modules/effect": {
|
|
11953
|
+
"version": "4.0.0-rc.112",
|
|
11954
|
+
"resolved": "https://registry.npmjs.org/effect/-/effect-4.0.0-rc.112.tgz",
|
|
11955
|
+
"integrity": "sha512-wXxwuh1Ywnv4cPRM3Wfa0vDwuOHnZ1TsTgHJkG9XgzND6inhBH9n1vBxhg3iIXOia/OrpmvVmd3lrD4vq6bF3A==",
|
|
11956
|
+
"dev": true,
|
|
11957
|
+
"license": "MIT",
|
|
11958
|
+
"dependencies": {
|
|
11959
|
+
"fast-check": "^4.9.0",
|
|
11960
|
+
"msgpackr": "^2.0.5"
|
|
11961
|
+
}
|
|
11962
|
+
},
|
|
11843
11963
|
"node_modules/embla-carousel": {
|
|
11844
11964
|
"version": "8.6.0",
|
|
11845
11965
|
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
|
@@ -12447,6 +12567,29 @@
|
|
|
12447
12567
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
|
12448
12568
|
"license": "MIT"
|
|
12449
12569
|
},
|
|
12570
|
+
"node_modules/fast-check": {
|
|
12571
|
+
"version": "4.10.1",
|
|
12572
|
+
"resolved": "https://registry.npmjs.org/fast-check/-/fast-check-4.10.1.tgz",
|
|
12573
|
+
"integrity": "sha512-sB5Vghiu8MyCyToHoBVGsT0baZg3sZWNIY+a6Ct2EDrQJlT4YdH6MC1BSLNe3kX1k5i5g0q1O52XFgcKK/rGHg==",
|
|
12574
|
+
"dev": true,
|
|
12575
|
+
"funding": [
|
|
12576
|
+
{
|
|
12577
|
+
"type": "individual",
|
|
12578
|
+
"url": "https://github.com/sponsors/dubzzz"
|
|
12579
|
+
},
|
|
12580
|
+
{
|
|
12581
|
+
"type": "opencollective",
|
|
12582
|
+
"url": "https://opencollective.com/fast-check"
|
|
12583
|
+
}
|
|
12584
|
+
],
|
|
12585
|
+
"license": "MIT",
|
|
12586
|
+
"dependencies": {
|
|
12587
|
+
"pure-rand": "^8.0.0"
|
|
12588
|
+
},
|
|
12589
|
+
"engines": {
|
|
12590
|
+
"node": ">=12.17.0"
|
|
12591
|
+
}
|
|
12592
|
+
},
|
|
12450
12593
|
"node_modules/fast-deep-equal": {
|
|
12451
12594
|
"version": "3.1.3",
|
|
12452
12595
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
|
@@ -15349,6 +15492,39 @@
|
|
|
15349
15492
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
|
15350
15493
|
"license": "MIT"
|
|
15351
15494
|
},
|
|
15495
|
+
"node_modules/msgpackr": {
|
|
15496
|
+
"version": "2.1.0",
|
|
15497
|
+
"resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-2.1.0.tgz",
|
|
15498
|
+
"integrity": "sha512-p/pBCVO63CsvvpkomUnNNag6+n38rULuDA6HHe70o2gtC8ODI52foF/4ko2qQcp6OiErJXTmrZeXmsGGHsIQNQ==",
|
|
15499
|
+
"dev": true,
|
|
15500
|
+
"license": "MIT",
|
|
15501
|
+
"optionalDependencies": {
|
|
15502
|
+
"msgpackr-extract": "^3.0.4"
|
|
15503
|
+
}
|
|
15504
|
+
},
|
|
15505
|
+
"node_modules/msgpackr-extract": {
|
|
15506
|
+
"version": "3.0.4",
|
|
15507
|
+
"resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.4.tgz",
|
|
15508
|
+
"integrity": "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw==",
|
|
15509
|
+
"dev": true,
|
|
15510
|
+
"hasInstallScript": true,
|
|
15511
|
+
"license": "MIT",
|
|
15512
|
+
"optional": true,
|
|
15513
|
+
"dependencies": {
|
|
15514
|
+
"node-gyp-build-optional-packages": "5.2.2"
|
|
15515
|
+
},
|
|
15516
|
+
"bin": {
|
|
15517
|
+
"download-msgpackr-prebuilds": "bin/download-prebuilds.js"
|
|
15518
|
+
},
|
|
15519
|
+
"optionalDependencies": {
|
|
15520
|
+
"@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4",
|
|
15521
|
+
"@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4",
|
|
15522
|
+
"@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4",
|
|
15523
|
+
"@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4",
|
|
15524
|
+
"@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4",
|
|
15525
|
+
"@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4"
|
|
15526
|
+
}
|
|
15527
|
+
},
|
|
15352
15528
|
"node_modules/muggle-string": {
|
|
15353
15529
|
"version": "0.4.1",
|
|
15354
15530
|
"resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
|
|
@@ -15453,6 +15629,22 @@
|
|
|
15453
15629
|
"integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==",
|
|
15454
15630
|
"license": "MIT"
|
|
15455
15631
|
},
|
|
15632
|
+
"node_modules/node-gyp-build-optional-packages": {
|
|
15633
|
+
"version": "5.2.2",
|
|
15634
|
+
"resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz",
|
|
15635
|
+
"integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==",
|
|
15636
|
+
"dev": true,
|
|
15637
|
+
"license": "MIT",
|
|
15638
|
+
"optional": true,
|
|
15639
|
+
"dependencies": {
|
|
15640
|
+
"detect-libc": "^2.0.1"
|
|
15641
|
+
},
|
|
15642
|
+
"bin": {
|
|
15643
|
+
"node-gyp-build-optional-packages": "bin.js",
|
|
15644
|
+
"node-gyp-build-optional-packages-optional": "optional.js",
|
|
15645
|
+
"node-gyp-build-optional-packages-test": "build-test.js"
|
|
15646
|
+
}
|
|
15647
|
+
},
|
|
15456
15648
|
"node_modules/node-mock-http": {
|
|
15457
15649
|
"version": "1.0.5",
|
|
15458
15650
|
"resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.5.tgz",
|
|
@@ -16269,6 +16461,23 @@
|
|
|
16269
16461
|
"node": ">=6"
|
|
16270
16462
|
}
|
|
16271
16463
|
},
|
|
16464
|
+
"node_modules/pure-rand": {
|
|
16465
|
+
"version": "8.4.2",
|
|
16466
|
+
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-8.4.2.tgz",
|
|
16467
|
+
"integrity": "sha512-vvuOGgcuPJAirlHvuQw1TrOiw7ptaIXXmIbNuiNOY6lNGJJH49PQ1Kj4nd783nPdQhQdicgOjVI2yI/9BD6/Ng==",
|
|
16468
|
+
"dev": true,
|
|
16469
|
+
"funding": [
|
|
16470
|
+
{
|
|
16471
|
+
"type": "individual",
|
|
16472
|
+
"url": "https://github.com/sponsors/dubzzz"
|
|
16473
|
+
},
|
|
16474
|
+
{
|
|
16475
|
+
"type": "opencollective",
|
|
16476
|
+
"url": "https://opencollective.com/fast-check"
|
|
16477
|
+
}
|
|
16478
|
+
],
|
|
16479
|
+
"license": "MIT"
|
|
16480
|
+
},
|
|
16272
16481
|
"node_modules/qified": {
|
|
16273
16482
|
"version": "0.10.1",
|
|
16274
16483
|
"resolved": "https://registry.npmjs.org/qified/-/qified-0.10.1.tgz",
|
|
@@ -20032,7 +20241,7 @@
|
|
|
20032
20241
|
},
|
|
20033
20242
|
"packages/switchboard": {
|
|
20034
20243
|
"name": "@coreplane/switchboard",
|
|
20035
|
-
"version": "1.
|
|
20244
|
+
"version": "1.236.0",
|
|
20036
20245
|
"license": "Apache-2.0",
|
|
20037
20246
|
"dependencies": {
|
|
20038
20247
|
"@earendil-works/pi-ai": "0.85.1",
|
package/dist/assets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.236.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"description": "Mention it in Slack and an agent reviews the PR, ships the fix, or answers the question — on the model you choose, with its tools running where you decide.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -91,7 +91,10 @@
|
|
|
91
91
|
"devDependencies": {
|
|
92
92
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
93
93
|
"@eslint/js": "^10.0.1",
|
|
94
|
+
"@opencode/protocol": "2.0.3",
|
|
95
|
+
"@opencode/schema": "2.0.3",
|
|
94
96
|
"@types/node": "^24.0.0",
|
|
97
|
+
"effect": "4.0.0-rc.112",
|
|
95
98
|
"eslint": "^10.10.0",
|
|
96
99
|
"eslint-config-prettier": "^10.1.8",
|
|
97
100
|
"eslint-plugin-vue": "^10.11.0",
|
package/dist/assets/source.json
CHANGED
|
@@ -55,6 +55,18 @@ export function parseUnitKey(key: string): { instanceId: string; unit: string }
|
|
|
55
55
|
return { instanceId: key.slice(0, at), unit: key.slice(at + 1) };
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/** The unit an idempotency key names — the head of its step
|
|
59
|
+
* (`<instance>:<unit>/<round>/<kind>` for the plan runner), or undefined for
|
|
60
|
+
* a key whose step carries no unit prefix the unit pattern accepts. What the
|
|
61
|
+
* `coordinator_tag` run event stamps as `unit`, so a run's stream names the
|
|
62
|
+
* unit it ran for without parsing the key back. */
|
|
63
|
+
export function unitOfIdempotencyKey(key: string): string | undefined {
|
|
64
|
+
if (!IDEMPOTENCY_KEY_PATTERN.test(key)) return undefined;
|
|
65
|
+
const step = key.slice(key.indexOf(":") + 1);
|
|
66
|
+
const head = step.split("/")[0];
|
|
67
|
+
return UNIT_PATTERN.test(head) ? head : undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
/** The event a child's terminal record sends its parent: the type carries the
|
|
59
71
|
* run id, so each `waitForEvent` matches its own child and a duplicate is
|
|
60
72
|
* buffered harmlessly. An event type is the platform's alphabet — letters,
|
|
@@ -85,10 +97,12 @@ export interface CoordinatorTag {
|
|
|
85
97
|
* (`CoordinatorInstance.base`), set by the spawn when the instance knows it.
|
|
86
98
|
* A coordinator's child is dispatched AT its unit branch so the resident
|
|
87
99
|
* attaches there, which makes the binding ref the branch itself — no base
|
|
88
|
-
* the post-step could resolve from the thread — so the spawn says it.
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
100
|
+
* the post-step could resolve from the thread — so the spawn says it.
|
|
101
|
+
* Published as the run's own `coordinator_tag` event at dispatch
|
|
102
|
+
* (run-history item 48a), so a run re-attached after a bot roll — whose
|
|
103
|
+
* dispatch options are gone with the process that spawned it — reads it
|
|
104
|
+
* back off its ledger events; `coordinatorFields` still leaves it off the
|
|
105
|
+
* rows, so rows and records keep the shape written before it existed. */
|
|
92
106
|
base?: string;
|
|
93
107
|
}
|
|
94
108
|
|
|
@@ -182,6 +196,11 @@ export interface CoordinatorUnit {
|
|
|
182
196
|
* — no pre-check, no branch, no round 0. A task string's row only; written by
|
|
183
197
|
* the hand-off, read by the driver into the machine's input. */
|
|
184
198
|
resume?: { pr: number; headSha?: string; url?: string };
|
|
199
|
+
/** The head the unit's own coding child last pushed, recorded when the unit
|
|
200
|
+
* ended `review_pending` (the wall clock capped after the pull request was
|
|
201
|
+
* opened or updated): the next attempt's pre-check starts at the review
|
|
202
|
+
* round when the open pull request still heads exactly here. */
|
|
203
|
+
lastPush?: string;
|
|
185
204
|
/** The round boundaries the coordinator reported, oldest first (the `ship_round` vocabulary). */
|
|
186
205
|
rounds: Array<{ index: number; agent: string; outcome: string; at: number }>;
|
|
187
206
|
/** How the unit ended: the ending's kind and the thread's report, when it has. */
|
|
@@ -244,6 +263,7 @@ export function isCoordinatorUnit(v: unknown): v is CoordinatorUnit {
|
|
|
244
263
|
if (r.issue !== undefined && !isFinite(r.issue)) return false;
|
|
245
264
|
if (r.pr !== undefined && !isPr(r.pr)) return false;
|
|
246
265
|
if (r.resume !== undefined && !isResume(r.resume)) return false;
|
|
266
|
+
if (r.lastPush !== undefined && !isText(r.lastPush)) return false;
|
|
247
267
|
if (
|
|
248
268
|
!Array.isArray(r.rounds) ||
|
|
249
269
|
r.rounds.length > MAX_ROUNDS ||
|
|
@@ -274,7 +274,18 @@ function readRecordReturn(step: string, a: BotAnswer): StepReturn {
|
|
|
274
274
|
|
|
275
275
|
function prCheckReturn(step: string, a: BotAnswer): StepReturn {
|
|
276
276
|
const { ok, state, prNumber, url, headSha, sha, mergedAt, at } = a.body;
|
|
277
|
-
if (ok === true && state === "none")
|
|
277
|
+
if (ok === true && state === "none") {
|
|
278
|
+
const { unrecovered } = a.body;
|
|
279
|
+
return {
|
|
280
|
+
type: "pr-check",
|
|
281
|
+
step,
|
|
282
|
+
pr: {
|
|
283
|
+
state: "none",
|
|
284
|
+
...(unrecovered === "no_commits" || unrecovered === "no_base" ? { unrecovered } : {}),
|
|
285
|
+
},
|
|
286
|
+
at,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
278
289
|
if (ok === true && state === "open" && typeof prNumber === "number" && typeof url === "string")
|
|
279
290
|
return {
|
|
280
291
|
type: "pr-check",
|
|
@@ -389,9 +400,16 @@ async function perform(
|
|
|
389
400
|
),
|
|
390
401
|
);
|
|
391
402
|
case "pr-check":
|
|
403
|
+
// `recover` rides only after a dead coding child: the bot opens the pull
|
|
404
|
+
// request from the pushed branch itself instead of answering `none`.
|
|
392
405
|
return prCheckReturn(
|
|
393
406
|
action.step,
|
|
394
|
-
answerOf(
|
|
407
|
+
answerOf(
|
|
408
|
+
"pr-check",
|
|
409
|
+
await step.do(action.step, STEP_CONFIG, () =>
|
|
410
|
+
call(bot, "pr-check", { ...tag, ...(action.recover !== undefined ? { recover: action.recover } : {}) }),
|
|
411
|
+
),
|
|
412
|
+
),
|
|
395
413
|
);
|
|
396
414
|
case "sleep":
|
|
397
415
|
await step.sleep(action.step, action.ms);
|
|
@@ -426,7 +444,11 @@ async function runUnit(
|
|
|
426
444
|
// A resume at review (agent-ship item 10) rides the unit's row: the pull
|
|
427
445
|
// request of ship's own the requester named opens the pipeline at its first
|
|
428
446
|
// review round, with no pre-check, no branch and no round 0.
|
|
429
|
-
const
|
|
447
|
+
const row = plan.units.find((u) => u.unit === unit);
|
|
448
|
+
const resume = row?.resume;
|
|
449
|
+
// A previous attempt's `review_pending` head: the machine's pre-check starts
|
|
450
|
+
// at the review round when the open pull request still heads exactly there.
|
|
451
|
+
const lastPush = row?.lastPush;
|
|
430
452
|
let state: UnitPipelineState = openUnitPipeline(
|
|
431
453
|
{
|
|
432
454
|
unit: { id: unit, branch: node.branch },
|
|
@@ -441,6 +463,7 @@ async function runUnit(
|
|
|
441
463
|
merge: plan.merge,
|
|
442
464
|
generated: plan.generated,
|
|
443
465
|
...(resume !== undefined ? { resume } : {}),
|
|
466
|
+
...(lastPush !== undefined ? { lastPush } : {}),
|
|
444
467
|
},
|
|
445
468
|
start.at,
|
|
446
469
|
);
|
|
@@ -486,6 +509,11 @@ async function runUnit(
|
|
|
486
509
|
...tag,
|
|
487
510
|
ending: { kind: note.ending.kind, report: renderUnitReport(state, endFacts) },
|
|
488
511
|
...(state.pr !== undefined ? { pr: state.pr } : {}),
|
|
512
|
+
// A review_pending ending names the child's own last push so the next
|
|
513
|
+
// attempt's pre-check can start at the review round (the row's lastPush).
|
|
514
|
+
...(note.ending.kind === "review_pending" && note.ending.headSha !== undefined
|
|
515
|
+
? { headSha: note.ending.headSha }
|
|
516
|
+
: {}),
|
|
489
517
|
...(state.lastCodingRunId !== undefined ? { codingRunId: state.lastCodingRunId } : {}),
|
|
490
518
|
};
|
|
491
519
|
await step.do(`${unit}/end`, STEP_CONFIG, () => call(bot, "unit-end", body));
|