@coreplane/switchboard 1.267.1 → 1.267.3
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/deploy/cloudflare-memory/testDiagnostics.ts +44 -3
- package/dist/assets/deploy/cloudflare-memory/testDiagnosticsProtocol.ts +7 -0
- package/dist/assets/deploy/cloudflare-memory/testDiagnosticsReporter.ts +22 -0
- package/dist/assets/deploy/cloudflare-memory/testFetch.ts +21 -0
- package/dist/assets/deploy/cloudflare-memory/worker.ts +43 -0
- package/dist/assets/deploy/cloudflare-resident/runRegistration.ts +68 -0
- package/dist/assets/deploy/cloudflare-resident/worker.ts +379 -165
- package/dist/assets/deploy/cloudflare-sandbox/worker.ts +17 -2
- package/dist/assets/package-lock.json +51 -4
- package/dist/assets/package.json +1 -1
- package/dist/assets/source.json +3 -3
- package/dist/assets/src/agents/registry.ts +2 -2
- package/dist/assets/src/core/budgets.ts +2 -0
- package/dist/assets/src/core/publicationPush.ts +31 -9
- package/dist/assets/src/core/runEvents.ts +17 -1
- package/dist/assets/src/core/runRecord.ts +17 -1
- package/dist/assets/src/core/ship/coordinator.ts +14 -1
- package/dist/assets/src/core/trace/attrs.ts +22 -0
- package/dist/assets/src/core/trace/tracer.ts +10 -2
- package/dist/assets/src/core/trace/types.ts +12 -1
- package/dist/assets/src/core/trace/workerTrace.ts +2 -2
- package/dist/assets/src/execution/seedPlan.ts +13 -4
- package/dist/assets/web/dist/.vite/manifest.json +488 -488
- package/dist/assets/web/dist/assets/AppShell-CCPwPqvc.js +1 -0
- package/dist/assets/web/dist/assets/{CostChart-4TeDdbvJ.js → CostChart-Y5BqJKLA.js} +2 -2
- package/dist/assets/web/dist/assets/CostsPage-RRggVvIx.js +1 -0
- package/dist/assets/web/dist/assets/DeliveryPage-CuIBLfup.js +1 -0
- package/dist/assets/web/dist/assets/HomePage-BQmun9kS.js +1 -0
- package/dist/assets/web/dist/assets/InputMenu-ymuqElZB.js +1 -0
- package/dist/assets/web/dist/assets/{MetricsPage-BQBuIuYU.js → MetricsPage-D2YGsI8C.js} +1 -1
- package/dist/assets/web/dist/assets/NotFoundPage-DZ6ysTXd.js +1 -0
- package/dist/assets/web/dist/assets/PendingTurnRow-BPyJ2f-i.js +1 -0
- package/dist/assets/web/dist/assets/PlanePage-C_gVx0Dg.js +1 -0
- package/dist/assets/web/dist/assets/ResidentDetailPage-CT6v9INM.js +1 -0
- package/dist/assets/web/dist/assets/ResidentsIndexPage-CWqPFf40.js +1 -0
- package/dist/assets/web/dist/assets/{RunFoldRow-BOWvSoIv.js → RunFoldRow-C1w-mwch.js} +1 -1
- package/dist/assets/web/dist/assets/RunRoutePage-zWMHTzla.js +9 -0
- package/dist/assets/web/dist/assets/RunsIndexPage-DF16D2Gg.js +1 -0
- package/dist/assets/web/dist/assets/RunsTabs-D7rsC4GR.js +1 -0
- package/dist/assets/web/dist/assets/ScheduledPage-CyRs0fXN.js +1 -0
- package/dist/assets/web/dist/assets/SettingSelect-CEkCLVWd.js +1 -0
- package/dist/assets/web/dist/assets/SettingsPage-BRuMCHSj.js +1 -0
- package/dist/assets/web/dist/assets/SilentTurn-D4MMDjWO.js +2 -0
- package/dist/assets/web/dist/assets/SlackMark-DjJTK2tD.js +1 -0
- package/dist/assets/web/dist/assets/StatusDot-BIAQN7iD.js +1 -0
- package/dist/assets/web/dist/assets/Tooltip-CxiQ7luL.js +1 -0
- package/dist/assets/web/dist/assets/UnitRoutePage-BQB5mAAl.js +1 -0
- package/dist/assets/web/dist/assets/{angular-html-BRRhsdQE.js → angular-html-DR_aiq_z.js} +1 -1
- package/dist/assets/web/dist/assets/{angular-ts-BFj-mqCm.js → angular-ts-DdAdPC99.js} +1 -1
- package/dist/assets/web/dist/assets/{apl-CsBuy1IL.js → apl-CFsVwJXV.js} +1 -1
- package/dist/assets/web/dist/assets/{astro-cVaqxBZq.js → astro-CV6XNX3T.js} +1 -1
- package/dist/assets/web/dist/assets/{blade-MEP5mWha.js → blade-BGvEqTv7.js} +1 -1
- package/dist/assets/web/dist/assets/browser-BWnzWwaT.js +6 -0
- package/dist/assets/web/dist/assets/{c-D4NGQ_Pd.js → c-ViUYuFAx.js} +1 -1
- package/dist/assets/web/dist/assets/{chapel-BkWImsIy.js → chapel-CZ7_YbfP.js} +1 -1
- package/dist/assets/web/dist/assets/{cobol-RPDSPSET.js → cobol-Dc8kYvS6.js} +1 -1
- package/dist/assets/web/dist/assets/{coffee-DZ5z7oVX.js → coffee-C0musOvn.js} +1 -1
- package/dist/assets/web/dist/assets/{cpp-CrBtIaRZ.js → cpp-ToPQGcGd.js} +1 -1
- package/dist/assets/web/dist/assets/{crystal-DJesYi6f.js → crystal-DUJIaO8W.js} +1 -1
- package/dist/assets/web/dist/assets/{css-DO9mjau-.js → css-BTcEgWyQ.js} +1 -1
- package/dist/assets/web/dist/assets/{dist-XRrc0hhm.js → dist-I2OEmPqz.js} +2 -2
- package/dist/assets/web/dist/assets/{edge-C3VzbSW0.js → edge-D8sh1uAj.js} +1 -1
- package/dist/assets/web/dist/assets/{elixir-BkD_Vhfe.js → elixir-D5vOtHrB.js} +1 -1
- package/dist/assets/web/dist/assets/{elm-KIM0mFPk.js → elm-B_5xqTuz.js} +1 -1
- package/dist/assets/web/dist/assets/{erb-D2ruCqb2.js → erb-DBEct7_l.js} +1 -1
- package/dist/assets/web/dist/assets/{eventSource-Cv0AFlMi.js → eventSource-Ds6_gjPD.js} +1 -1
- package/dist/assets/web/dist/assets/{git-rebase-CnfbQTxA.js → git-rebase-lWoTL0Qh.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-js-CbiwFXWH.js → glimmer-js-DNfSP4GT.js} +1 -1
- package/dist/assets/web/dist/assets/{glimmer-ts-DV8ii0Pv.js → glimmer-ts-DpPeWMLs.js} +1 -1
- package/dist/assets/web/dist/assets/{glsl-CgmznWkA.js → glsl-m6Ziz8q0.js} +1 -1
- package/dist/assets/web/dist/assets/{graphql-BK-bBYfH.js → graphql-shD9521e.js} +1 -1
- package/dist/assets/web/dist/assets/{hack-DnwTTP5n.js → hack-D_Mf_2gv.js} +1 -1
- package/dist/assets/web/dist/assets/{haml-DAVJsWwT.js → haml-BuEAF47S.js} +1 -1
- package/dist/assets/web/dist/assets/{handlebars-voOVfxFL.js → handlebars-D2aiSYtv.js} +1 -1
- package/dist/assets/web/dist/assets/{html-3FTqwaPk.js → html-CRjXJZot.js} +1 -1
- package/dist/assets/web/dist/assets/{html-derivative-CSlhLDQn.js → html-derivative-CYpQX_KK.js} +1 -1
- package/dist/assets/web/dist/assets/{http-Vx4ilPfS.js → http-D6vXMpc_.js} +1 -1
- package/dist/assets/web/dist/assets/{hurl-DxyZDE3R.js → hurl-B634cMJT.js} +1 -1
- package/dist/assets/web/dist/assets/{java-nWqdjNJG.js → java-BlINdAve.js} +1 -1
- package/dist/assets/web/dist/assets/{javascript-Dr82HOwp.js → javascript-BBEMmUOu.js} +1 -1
- package/dist/assets/web/dist/assets/{jinja-BhjLrPOt.js → jinja-BIA8TYHJ.js} +1 -1
- package/dist/assets/web/dist/assets/{jison-DilGIUa8.js → jison-aepfTbzH.js} +1 -1
- package/dist/assets/web/dist/assets/{json-CrOcvmhJ.js → json-DmYZ1boG.js} +1 -1
- package/dist/assets/web/dist/assets/{jsx-BW_RX-fo.js → jsx-GkaaF4_W.js} +1 -1
- package/dist/assets/web/dist/assets/{julia-CWH071DB.js → julia-DRtMztOz.js} +1 -1
- package/dist/assets/web/dist/assets/{just-DkhhoWBy.js → just-PvoeEWoy.js} +1 -1
- package/dist/assets/web/dist/assets/{latex-cuHJ4TUT.js → latex-Bujuo3AZ.js} +1 -1
- package/dist/assets/web/dist/assets/{liquid-DG6afYQN.js → liquid-B0h0HkS3.js} +1 -1
- package/dist/assets/web/dist/assets/{lua-C5CMnCPS.js → lua-6O-O1CSO.js} +1 -1
- package/dist/assets/web/dist/assets/main-DYwKyLY5.js +28 -0
- package/dist/assets/web/dist/assets/{marko-Dy2_DuC4.js → marko-XS_tpV0N.js} +1 -1
- package/dist/assets/web/dist/assets/{mdc-BMKb_EeA.js → mdc-DxULuTS1.js} +1 -1
- package/dist/assets/web/dist/assets/{nginx-og0RgMKf.js → nginx-n2RAfCKE.js} +1 -1
- package/dist/assets/web/dist/assets/{nim-CQxTQQP0.js → nim-DJxdqD8U.js} +1 -1
- package/dist/assets/web/dist/assets/{org-b5lkq_Pl.js → org-R9yxwZ2v.js} +1 -1
- package/dist/assets/web/dist/assets/{perl-D9X5_i_J.js → perl-zKAUtl73.js} +1 -1
- package/dist/assets/web/dist/assets/{php-CwUxvLX0.js → php-Dk50x1WE.js} +1 -1
- package/dist/assets/web/dist/assets/{pug-uOIOExr0.js → pug-C3TSg8JJ.js} +1 -1
- package/dist/assets/web/dist/assets/{qml-g5RoADf-.js → qml-CL1nqvCt.js} +1 -1
- package/dist/assets/web/dist/assets/{r-5LdAopD3.js → r-Bo44Yl7y.js} +1 -1
- package/dist/assets/web/dist/assets/{razor-DwrvFLYQ.js → razor-k2Dt2qXz.js} +1 -1
- package/dist/assets/web/dist/assets/{regexp-dGtAaxC-.js → regexp-Uy_HNe6G.js} +1 -1
- package/dist/assets/web/dist/assets/{rst-wFdW0brr.js → rst-U0zcRJ60.js} +1 -1
- package/dist/assets/web/dist/assets/{ruby-DPglN6I9.js → ruby-DUyqwP37.js} +1 -1
- package/dist/assets/web/dist/assets/{sas-CUbCs40d.js → sas-CxC8bRy1.js} +1 -1
- package/dist/assets/web/dist/assets/{scss-B-DUiI1c.js → scss-DaqXj6RQ.js} +1 -1
- package/dist/assets/web/dist/assets/{shellscript-lGOV7Ram.js → shellscript-CI49IoFh.js} +1 -1
- package/dist/assets/web/dist/assets/{shellsession-BDWtAAjr.js → shellsession-DJxotIOK.js} +1 -1
- package/dist/assets/web/dist/assets/{soy-zod_dcOy.js → soy-BjVAVuCU.js} +1 -1
- package/dist/assets/web/dist/assets/{sql-BG0HSSoM.js → sql-CzxBKQd1.js} +1 -1
- package/dist/assets/web/dist/assets/{sseReplay-BsNQPLmJ.js → sseReplay-BDVyc8HX.js} +3 -3
- package/dist/assets/web/dist/assets/{stata-BVUMLp1W.js → stata-DLMv_9_X.js} +1 -1
- package/dist/assets/web/dist/assets/{surrealql-DiSUbHKF.js → surrealql-BzbVEZbO.js} +1 -1
- package/dist/assets/web/dist/assets/{svelte-DZenT45Y.js → svelte-DTSfopUt.js} +1 -1
- package/dist/assets/web/dist/assets/{templ-BWKRNM0-.js → templ-BPxFlKQj.js} +1 -1
- package/dist/assets/web/dist/assets/{tex-CbMzBlkK.js → tex-B_KndQwK.js} +1 -1
- package/dist/assets/web/dist/assets/{ts-tags-CZsVorDN.js → ts-tags-CG7tgvBl.js} +1 -1
- package/dist/assets/web/dist/assets/{tsx-R1r3mH4W.js → tsx-CN9eqIdJ.js} +1 -1
- package/dist/assets/web/dist/assets/{twig-BjPMyjKH.js → twig-CKPbB5ca.js} +1 -1
- package/dist/assets/web/dist/assets/{typescript-Bx-66VN_.js → typescript-BQAXVjHJ.js} +1 -1
- package/dist/assets/web/dist/assets/{typst-fW037Eo6.js → typst-D3EaP9Ui.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-D1V_GNxW.js → vue-DuQwQ__D.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-html-C-4pTZ-s.js → vue-html-UEJn5cLD.js} +1 -1
- package/dist/assets/web/dist/assets/{vue-vine-C-QsX3hR.js → vue-vine-DDdijZY2.js} +1 -1
- package/dist/assets/web/dist/assets/{wallClock-x0hjr0J_.js → wallClock-DaewxNMY.js} +1 -1
- package/dist/assets/web/dist/assets/{xml-BJTJXPgC.js → xml-BezNDynk.js} +1 -1
- package/dist/assets/web/dist/assets/{xsl-Xc9lukqe.js → xsl-Bzb2zIr8.js} +1 -1
- package/dist/assets/web/dist/assets/{yaml-Dw-FMsQa.js → yaml-DOccVRRG.js} +1 -1
- package/dist/cli.js +868 -226
- package/package.json +1 -1
- package/dist/assets/web/dist/assets/AppShell-DPJVhUGP.js +0 -1
- package/dist/assets/web/dist/assets/CostsPage-8HDgvuWB.js +0 -1
- package/dist/assets/web/dist/assets/DeliveryPage-QyS6_Q0m.js +0 -1
- package/dist/assets/web/dist/assets/HomePage-Bm40r0wF.js +0 -1
- package/dist/assets/web/dist/assets/InputMenu-BRV35UWP.js +0 -1
- package/dist/assets/web/dist/assets/NotFoundPage-CDjMfCcj.js +0 -1
- package/dist/assets/web/dist/assets/PendingTurnRow-BqY-EaZR.js +0 -1
- package/dist/assets/web/dist/assets/PlanePage-VmzjNLAz.js +0 -1
- package/dist/assets/web/dist/assets/ResidentDetailPage-hjX3tRCh.js +0 -1
- package/dist/assets/web/dist/assets/ResidentsIndexPage-BKGnlCEh.js +0 -1
- package/dist/assets/web/dist/assets/RunRoutePage-D9ztf-uE.js +0 -9
- package/dist/assets/web/dist/assets/RunsIndexPage-DTYQbhtr.js +0 -1
- package/dist/assets/web/dist/assets/RunsTabs-C7Ua3XX5.js +0 -1
- package/dist/assets/web/dist/assets/ScheduledPage-BAYR0tuv.js +0 -1
- package/dist/assets/web/dist/assets/SettingSelect-CEeA4iuC.js +0 -1
- package/dist/assets/web/dist/assets/SettingsPage-BlYBmCG9.js +0 -1
- package/dist/assets/web/dist/assets/SilentTurn-CHfzRQW8.js +0 -2
- package/dist/assets/web/dist/assets/SlackMark-DB0MGiF5.js +0 -1
- package/dist/assets/web/dist/assets/StatusDot-BMkUFVE8.js +0 -1
- package/dist/assets/web/dist/assets/Tooltip-Dfot16IA.js +0 -1
- package/dist/assets/web/dist/assets/UnitRoutePage-BdZoT-4d.js +0 -1
- package/dist/assets/web/dist/assets/browser-CCImen5O.js +0 -6
- package/dist/assets/web/dist/assets/main-B32N1vUh.js +0 -28
|
@@ -95,6 +95,7 @@ import {
|
|
|
95
95
|
SEED_MARKER,
|
|
96
96
|
SEED_RESTORE_MAX_MS,
|
|
97
97
|
SEED_ABANDONED_RESTORE_WAIT_MS,
|
|
98
|
+
seedDoorRemote,
|
|
98
99
|
seedFixupScript,
|
|
99
100
|
seedMarkerText,
|
|
100
101
|
type RestorePhases,
|
|
@@ -451,12 +452,25 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
451
452
|
detail: `presigned R2 transfer needs ${transfer.missing.join(", ")}`,
|
|
452
453
|
};
|
|
453
454
|
}
|
|
455
|
+
let doorRemote: string;
|
|
456
|
+
try {
|
|
457
|
+
doorRemote = seedDoorRemote(envVars.GIT_DOOR_ORIGIN ?? "", seed.slug);
|
|
458
|
+
} catch {
|
|
459
|
+
return { seeded: false, reason: "seed-failed", detail: "Git door origin unavailable", step: "fixup" };
|
|
460
|
+
}
|
|
454
461
|
// The marker names the seed this container carries — the handle and the
|
|
455
462
|
// ref and head checked out: the same seed again is the run's second
|
|
456
463
|
// request (a retry, a re-attach), and a restore over the live tree would
|
|
457
464
|
// destroy the run's work; a seed naming another ref is a new seed.
|
|
458
465
|
const marker = await this.runRoot(["cat", SEED_MARKER], 30_000);
|
|
459
466
|
if (marker.exitCode === 0 && marker.stdout.trim() === seedMarkerText(seed)) {
|
|
467
|
+
const origin = await this.runRoot(
|
|
468
|
+
["git", "-C", SEED_CHECKOUT_DIR, "remote", "set-url", "origin", doorRemote],
|
|
469
|
+
30_000,
|
|
470
|
+
envVars,
|
|
471
|
+
);
|
|
472
|
+
if (origin.exitCode !== 0)
|
|
473
|
+
return { seeded: false, reason: "seed-failed", detail: "seed origin refresh failed", step: "fixup" };
|
|
460
474
|
const head = await this.runRoot(["git", "-C", SEED_CHECKOUT_DIR, "rev-parse", "HEAD"], 30_000);
|
|
461
475
|
return {
|
|
462
476
|
seeded: true,
|
|
@@ -490,14 +504,15 @@ export class SwitchboardSandbox extends Sandbox<Env> {
|
|
|
490
504
|
t = systemClock();
|
|
491
505
|
const script = seedFixupScript({
|
|
492
506
|
slug: seed.slug,
|
|
507
|
+
doorOrigin: envVars.GIT_DOOR_ORIGIN ?? "",
|
|
493
508
|
ref: seed.ref,
|
|
494
509
|
...(seed.fetchRef ? { fetchRef: seed.fetchRef } : {}),
|
|
495
510
|
...(seed.fetchSha ? { fetchSha: seed.fetchSha } : {}),
|
|
496
511
|
checkoutDir: SEED_CHECKOUT_DIR,
|
|
497
512
|
...(seed.depsBackupId ? { depsDir: SEED_DEPS_STAGING_DIR } : {}),
|
|
498
513
|
});
|
|
499
|
-
// The fix-up's fetch authenticates through the
|
|
500
|
-
//
|
|
514
|
+
// The fix-up's fetch authenticates through the door-scoped helper using
|
|
515
|
+
// the run bearer in the command environment.
|
|
501
516
|
const fix = await this.runRoot(["bash", "-c", script], SEED_FIXUP_TIMEOUT_MS, envVars);
|
|
502
517
|
if (fix.exitCode !== 0) throw new Error(`fix-up exited ${fix.exitCode}: ${tail(fix.stderr || fix.stdout)}`);
|
|
503
518
|
steps.fixup = systemClock() - t;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.267.
|
|
3
|
+
"version": "1.267.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "switchboard",
|
|
9
|
-
"version": "1.267.
|
|
9
|
+
"version": "1.267.3",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"workspaces": [
|
|
12
12
|
"web",
|
|
@@ -21023,7 +21023,7 @@
|
|
|
21023
21023
|
},
|
|
21024
21024
|
"packages/switchboard": {
|
|
21025
21025
|
"name": "@coreplane/switchboard",
|
|
21026
|
-
"version": "1.267.
|
|
21026
|
+
"version": "1.267.3",
|
|
21027
21027
|
"license": "Apache-2.0",
|
|
21028
21028
|
"dependencies": {
|
|
21029
21029
|
"@earendil-works/pi-ai": "0.85.1",
|
|
@@ -21053,7 +21053,7 @@
|
|
|
21053
21053
|
"dependencies": {
|
|
21054
21054
|
"@nuxt/ui": "^4.11.0",
|
|
21055
21055
|
"@pierre/diffs": "1.4.2",
|
|
21056
|
-
"@vueuse/core": "^
|
|
21056
|
+
"@vueuse/core": "^15.0.0",
|
|
21057
21057
|
"vue": "^3.5.42",
|
|
21058
21058
|
"vue-router": "^5.3.1"
|
|
21059
21059
|
},
|
|
@@ -21076,6 +21076,53 @@
|
|
|
21076
21076
|
"engines": {
|
|
21077
21077
|
"node": ">=22"
|
|
21078
21078
|
}
|
|
21079
|
+
},
|
|
21080
|
+
"web/node_modules/@vueuse/core": {
|
|
21081
|
+
"version": "15.0.0",
|
|
21082
|
+
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-15.0.0.tgz",
|
|
21083
|
+
"integrity": "sha512-Zv67h5mrk7YHDJsRObx+DXaCBz6RKBUpxqLesJ+3xZNPG6yuQs7ja7kg3veFrrouYLWAqydQ7CwGEbLVxB43Ng==",
|
|
21084
|
+
"license": "MIT",
|
|
21085
|
+
"dependencies": {
|
|
21086
|
+
"@types/web-bluetooth": "^0.0.21",
|
|
21087
|
+
"@vueuse/metadata": "15.0.0",
|
|
21088
|
+
"@vueuse/shared": "15.0.0"
|
|
21089
|
+
},
|
|
21090
|
+
"engines": {
|
|
21091
|
+
"node": ">=22"
|
|
21092
|
+
},
|
|
21093
|
+
"funding": {
|
|
21094
|
+
"url": "https://github.com/sponsors/antfu"
|
|
21095
|
+
},
|
|
21096
|
+
"peerDependencies": {
|
|
21097
|
+
"vue": "^3.5.0"
|
|
21098
|
+
}
|
|
21099
|
+
},
|
|
21100
|
+
"web/node_modules/@vueuse/core/node_modules/@vueuse/shared": {
|
|
21101
|
+
"version": "15.0.0",
|
|
21102
|
+
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-15.0.0.tgz",
|
|
21103
|
+
"integrity": "sha512-vbNXhGrbV53xPOMSyOzMHQRvhMgZf7oNDw2RChUetVxwTIEkInQ1joZ9JbT0G3z2n2AJNjvmCWeM9ySYrnvogA==",
|
|
21104
|
+
"license": "MIT",
|
|
21105
|
+
"engines": {
|
|
21106
|
+
"node": ">=22"
|
|
21107
|
+
},
|
|
21108
|
+
"funding": {
|
|
21109
|
+
"url": "https://github.com/sponsors/antfu"
|
|
21110
|
+
},
|
|
21111
|
+
"peerDependencies": {
|
|
21112
|
+
"vue": "^3.5.0"
|
|
21113
|
+
}
|
|
21114
|
+
},
|
|
21115
|
+
"web/node_modules/@vueuse/metadata": {
|
|
21116
|
+
"version": "15.0.0",
|
|
21117
|
+
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-15.0.0.tgz",
|
|
21118
|
+
"integrity": "sha512-sfWE5LADAKJ0KnjtGliMdSN8Ij/L7uP6rXIZlgPloAI4zvvefbw6H5Lt7KJA6RqQOYgVqWF5Guj50HHMIzygBg==",
|
|
21119
|
+
"license": "MIT",
|
|
21120
|
+
"engines": {
|
|
21121
|
+
"node": ">=22"
|
|
21122
|
+
},
|
|
21123
|
+
"funding": {
|
|
21124
|
+
"url": "https://github.com/sponsors/antfu"
|
|
21125
|
+
}
|
|
21079
21126
|
}
|
|
21080
21127
|
}
|
|
21081
21128
|
}
|
package/dist/assets/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "switchboard",
|
|
3
|
-
"version": "1.267.
|
|
3
|
+
"version": "1.267.3",
|
|
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",
|
package/dist/assets/source.json
CHANGED
|
@@ -306,7 +306,7 @@ SCOPE FIRST — a hard rule, at most 5 tool calls: identify the target repositor
|
|
|
306
306
|
- Use \`gh search code\` / \`gh api\` for cross-repo lookups; clone at most ONE repo per task.
|
|
307
307
|
|
|
308
308
|
Workflow for shipping a PR:
|
|
309
|
-
1. Clone the repo into the workspace if it
|
|
309
|
+
1. Clone the repo into the workspace if it is not already there: \`gh repo clone <owner/name>\` or \`git clone "$GIT_DOOR_REMOTE"\` for the bound repository. A private \`github.com\` clone URL cannot use the run bearer. Orient with a few BATCHED commands (tree + the relevant files in one call), not file-by-file exploration.
|
|
310
310
|
2. Create a branch with a descriptive name.
|
|
311
311
|
3. Implement the change. Match the surrounding code's style and conventions.
|
|
312
312
|
4. Prove the change with the cheapest checks that can (CHECKS BY COST below): the linter and the tests nearest the files you touched, the documentation checks for a documentation change.
|
|
@@ -628,7 +628,7 @@ You cannot run commands, clone repositories, edit code, or review pull requests,
|
|
|
628
628
|
// preset, not a directive.
|
|
629
629
|
const EXPLORE_SYSTEM = `You are Switchboard's explore agent: a long, read-only investigation of a repository, answering a request from Slack.
|
|
630
630
|
|
|
631
|
-
You work in a fresh sandbox with a shell (bash), read_file, and a read-scoped GitHub credential:
|
|
631
|
+
You work in a fresh sandbox with a shell (bash), read_file, and a read-scoped GitHub credential: clone the bound repository first with \`gh repo clone <owner/name>\` or \`git clone "$GIT_DOOR_REMOTE"\`, then check out the ref the request names. A private \`github.com\` clone URL cannot use the run bearer. Install what you need and run whatever the investigation calls for — builds, test suites, benchmarks, \`act\`. ${SANDBOX_TOOLCHAIN} You cannot push. Your other tools: \`web_search\` and \`web_fetch\` (sources and pages), the GitHub reads — \`github_repos\`, \`github_tree\` / \`github_file\` (browse and read our repos at any ref), \`github_search_code\`, \`github_issue_list\` / \`github_issue_get\` — and \`list_skills\` / \`use_skill\`.
|
|
632
632
|
|
|
633
633
|
THE DELIVERABLE IS A CLAIM TABLE. Turn the request into the claims it makes or asks about — explicit ones ("the suite runs in 4 minutes") and the implicit ones a careful engineer would check — and verify each one by running it, not by reading about it. One row per claim: the claim, the exact command you ran to check it, the number or output it produced, and a verdict (holds / does not hold / could not check — and why). Numbers over adjectives: measure a duration, count the failures, quote the version. Say what you did not get to.
|
|
634
634
|
|
|
@@ -21,6 +21,8 @@ export const SECOND_MS = 1_000;
|
|
|
21
21
|
export const secondsToMs = (seconds: number): number => seconds * SECOND_MS;
|
|
22
22
|
|
|
23
23
|
export const MINUTE_MS = 60_000;
|
|
24
|
+
/** Allow the run's ending to release its resident workspace after its budget ends. */
|
|
25
|
+
export const RUN_REGISTRATION_GRACE_MS = MINUTE_MS;
|
|
24
26
|
|
|
25
27
|
/** Minutes → milliseconds, for a duration a request names in minutes (a drain's
|
|
26
28
|
* length): the multiplication lives here so no other file holds it. */
|
|
@@ -15,14 +15,19 @@ const refName = (s: string): boolean =>
|
|
|
15
15
|
/^[a-zA-Z0-9_/-]+(?:\.[a-zA-Z0-9_/-]+)*$/.test(s) && !s.includes("..") && !s.startsWith("-");
|
|
16
16
|
const shortRef = (s: string): string => s.replace(/^refs\/heads\//, "");
|
|
17
17
|
|
|
18
|
+
/** Broad enough to count competing Git config pushes; leasedPushCommand
|
|
19
|
+
* separately proves whether one selected call is authorized evidence. */
|
|
20
|
+
export const mentionsGitPush = (command: string): boolean => /\bgit(?:\s+-c\s+\S+)*\s+push\b/.test(command);
|
|
21
|
+
|
|
18
22
|
/** Deliberately not a shell interpreter. Only a standalone literal push can
|
|
19
23
|
* attest its result: pipelines, scripts, substitutions and compound commands
|
|
20
24
|
* cannot prove which operation produced stdout or the successful exit. */
|
|
21
25
|
export function leasedPushCommand(command: string): { ref: string; expectedHeadSha: string } | undefined {
|
|
22
26
|
if (command.length >= COMMAND_CAP) return;
|
|
23
27
|
const literal = command.replace(/[ \t]+2>&1[ \t]*$/, "");
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
const prefix = /^git(?: -c http\.postBuffer=[0-9]+)? push\s/.exec(literal);
|
|
29
|
+
if (!prefix || /[\n\r'"`$;|<>\\&]/.test(literal)) return;
|
|
30
|
+
const words = literal.slice(prefix[0].length).trim().split(/\s+/);
|
|
26
31
|
const leases = words.filter((w) => w.startsWith("--force-with-lease="));
|
|
27
32
|
if (leases.length !== 1) return;
|
|
28
33
|
const lease = /^--force-with-lease=refs\/heads\/([^:]+):([a-f0-9]{40})$/.exec(leases[0]!);
|
|
@@ -42,13 +47,14 @@ export function pairedPublicationPush(
|
|
|
42
47
|
binding: ExistingPrPublicationBinding,
|
|
43
48
|
head: string,
|
|
44
49
|
callId?: string,
|
|
50
|
+
gitDoorBaseUrl?: string,
|
|
45
51
|
): PushEvent | undefined {
|
|
46
52
|
if (!fullSha(head) || !fullSha(binding.expectedHeadSha) || head === binding.expectedHeadSha) return;
|
|
47
53
|
const calls = events.filter(
|
|
48
54
|
(e): e is Extract<RunEvent, { type: "tool_call" }> =>
|
|
49
55
|
e.type === "tool_call" &&
|
|
50
56
|
e.tool === "bash" &&
|
|
51
|
-
(callId === undefined ?
|
|
57
|
+
(callId === undefined ? mentionsGitPush(e.command ?? e.summary) : e.callId === callId),
|
|
52
58
|
);
|
|
53
59
|
if (calls.length !== 1) return;
|
|
54
60
|
const call = calls[0]!;
|
|
@@ -76,20 +82,36 @@ export function pairedPublicationPush(
|
|
|
76
82
|
)
|
|
77
83
|
return;
|
|
78
84
|
let lines = result.output.trim().split(/\r?\n/);
|
|
79
|
-
const tracking =
|
|
85
|
+
const tracking = [
|
|
86
|
+
`branch '${binding.publicationRef}' set up to track 'origin/${binding.publicationRef}'.`,
|
|
87
|
+
`Branch '${binding.publicationRef}' set up to track remote branch '${binding.publicationRef}' from 'origin'.`,
|
|
88
|
+
];
|
|
80
89
|
if (
|
|
81
90
|
/(?:^|\s)(?:-u|--set-upstream)(?:\s|$)/.test(call.command) &&
|
|
82
|
-
lines.filter((line) => line
|
|
91
|
+
lines.filter((line) => tracking.includes(line)).length === 1
|
|
83
92
|
)
|
|
84
|
-
lines = lines.filter((line) => line
|
|
93
|
+
lines = lines.filter((line) => !tracking.includes(line));
|
|
85
94
|
if (lines.length !== 2) return;
|
|
86
|
-
const
|
|
95
|
+
const githubRemote = /^To (?:https:\/\/github\.com\/|git@github\.com:|github\.com:)([^\s]+?)(?:\.git)?$/.exec(
|
|
96
|
+
lines[0]!,
|
|
97
|
+
);
|
|
98
|
+
let doorRemote: string | undefined;
|
|
99
|
+
if (gitDoorBaseUrl !== undefined) {
|
|
100
|
+
try {
|
|
101
|
+
doorRemote = `${new URL(gitDoorBaseUrl).origin}/git/${binding.repo}`;
|
|
102
|
+
} catch {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const remoteMatches =
|
|
107
|
+
doorRemote === undefined
|
|
108
|
+
? githubRemote?.[1]?.toLowerCase() === binding.repo.toLowerCase()
|
|
109
|
+
: lines[0] === `To ${doorRemote}` || lines[0] === `To ${doorRemote}.git`;
|
|
87
110
|
const update = /^\s*\+?\s*([a-f0-9]{7,40})(\.{2,3})([a-f0-9]{7,40})\s+(\S+) -> (\S+)( \(forced update\))?$/.exec(
|
|
88
111
|
lines[1]!,
|
|
89
112
|
);
|
|
90
113
|
if (
|
|
91
|
-
!
|
|
92
|
-
remote[1]!.toLowerCase() !== binding.repo.toLowerCase() ||
|
|
114
|
+
!remoteMatches ||
|
|
93
115
|
!update ||
|
|
94
116
|
!binding.expectedHeadSha.startsWith(update[1]!) ||
|
|
95
117
|
!head.startsWith(update[3]!) ||
|
|
@@ -1142,7 +1142,15 @@ export type RunEvent =
|
|
|
1142
1142
|
* `model` is the ref the run uses, resolved from a model the person
|
|
1143
1143
|
* named in plain words (the plain-words model unit) — applied at
|
|
1144
1144
|
* directive precedence. Additive: unknown → no model. */
|
|
1145
|
-
binds?: ReadonlyArray<{
|
|
1145
|
+
binds?: ReadonlyArray<{
|
|
1146
|
+
line: string;
|
|
1147
|
+
reason: string;
|
|
1148
|
+
model?: string;
|
|
1149
|
+
repo?: string;
|
|
1150
|
+
/** The admissible fact behind the model's typed repo choice. */
|
|
1151
|
+
repoSource?: "request" | "attachment" | "thread" | "channel";
|
|
1152
|
+
confirmed?: true;
|
|
1153
|
+
}>;
|
|
1146
1154
|
question?: string;
|
|
1147
1155
|
/** A question's proposed line, redacted and cut like the receipt — what
|
|
1148
1156
|
* the next turn's "yes" binds (`bindFromAnswer`). */
|
|
@@ -1160,6 +1168,14 @@ export type RunEvent =
|
|
|
1160
1168
|
* boundary. Its refusal text is always the cause-owned renderer. */
|
|
1161
1169
|
providerFailure?: ProviderFailureCause;
|
|
1162
1170
|
attempts?: ReadonlyArray<{ outcome: "accepted" | "violation"; violation?: string }>;
|
|
1171
|
+
/** Snapshot of the factual repository context the operator received. */
|
|
1172
|
+
repoContext?: {
|
|
1173
|
+
organization: string;
|
|
1174
|
+
threadRepo?: string;
|
|
1175
|
+
channelRepo?: string;
|
|
1176
|
+
candidateStatus: "skipped" | "available" | "truncated" | "empty" | "unavailable";
|
|
1177
|
+
candidateCount: number;
|
|
1178
|
+
};
|
|
1163
1179
|
intake?: { verdict: string; reason: string };
|
|
1164
1180
|
latencyMs?: number;
|
|
1165
1181
|
outputTokens?: number;
|
|
@@ -484,7 +484,14 @@ export interface RunOperatorDecision {
|
|
|
484
484
|
mode: "shadow" | "on";
|
|
485
485
|
outcome: "binds" | "question" | "refusal" | "non_decision";
|
|
486
486
|
reason: string;
|
|
487
|
-
binds?: {
|
|
487
|
+
binds?: {
|
|
488
|
+
line: string;
|
|
489
|
+
reason: string;
|
|
490
|
+
model?: string;
|
|
491
|
+
repo?: string;
|
|
492
|
+
repoSource?: "request" | "attachment" | "thread" | "channel";
|
|
493
|
+
confirmed?: true;
|
|
494
|
+
}[];
|
|
488
495
|
question?: string;
|
|
489
496
|
/** A question's proposed line — what the next turn's "yes" binds. */
|
|
490
497
|
proposal?: string;
|
|
@@ -497,6 +504,13 @@ export interface RunOperatorDecision {
|
|
|
497
504
|
/** The structured seam's attempts (record 0067): what each answer violated,
|
|
498
505
|
* or that it was accepted. */
|
|
499
506
|
attempts?: { outcome: "accepted" | "violation"; violation?: string }[];
|
|
507
|
+
repoContext?: {
|
|
508
|
+
organization: string;
|
|
509
|
+
threadRepo?: string;
|
|
510
|
+
channelRepo?: string;
|
|
511
|
+
candidateStatus: "skipped" | "available" | "truncated" | "empty" | "unavailable";
|
|
512
|
+
candidateCount: number;
|
|
513
|
+
};
|
|
500
514
|
intake?: { verdict: string; reason: string };
|
|
501
515
|
latencyMs?: number;
|
|
502
516
|
outputTokens?: number;
|
|
@@ -520,6 +534,7 @@ export function operatorOfEvents(events: readonly RunEvent[]): RunOperatorDecisi
|
|
|
520
534
|
reason: b.reason,
|
|
521
535
|
...(b.model !== undefined ? { model: b.model } : {}),
|
|
522
536
|
...(b.repo !== undefined ? { repo: b.repo } : {}),
|
|
537
|
+
...(b.repoSource !== undefined ? { repoSource: b.repoSource } : {}),
|
|
523
538
|
...(b.confirmed ? { confirmed: true as const } : {}),
|
|
524
539
|
})),
|
|
525
540
|
}
|
|
@@ -538,6 +553,7 @@ export function operatorOfEvents(events: readonly RunEvent[]): RunOperatorDecisi
|
|
|
538
553
|
})),
|
|
539
554
|
}
|
|
540
555
|
: {}),
|
|
556
|
+
...(e.repoContext ? { repoContext: { ...e.repoContext } } : {}),
|
|
541
557
|
...(e.intake ? { intake: { verdict: e.intake.verdict, reason: e.intake.reason } } : {}),
|
|
542
558
|
...(e.latencyMs !== undefined ? { latencyMs: e.latencyMs } : {}),
|
|
543
559
|
...(e.outputTokens !== undefined ? { outputTokens: e.outputTokens } : {}),
|
|
@@ -3883,9 +3883,19 @@ function renderUnitReportWithWake(
|
|
|
3883
3883
|
const rounds = `${e.reviewRounds} review round${e.reviewRounds === 1 ? "" : "s"}`;
|
|
3884
3884
|
const prUrl = s.pr?.url;
|
|
3885
3885
|
const prLine = prUrl ? ` PR: ${prUrl}` : "";
|
|
3886
|
+
const spentCapAction = (): string | undefined => {
|
|
3887
|
+
const cap = s.input.grant?.costCapUsd;
|
|
3888
|
+
if (cap === undefined || s.spendUsd === null || s.spendUsd < cap) return undefined;
|
|
3889
|
+
const next = prUrl
|
|
3890
|
+
? `If the pull request (${prUrl}) is still open, continue it only under separate authorization, then request review of its exact head. If it has merged, no recovery is needed.`
|
|
3891
|
+
: "If work remains on the saved branch, continue it only under separate authorization, then request review of its exact head.";
|
|
3892
|
+
return `Next action: $${s.spendUsd.toFixed(2)} spent against the original $${cap.toFixed(2)} cost cap; this unit cannot start another child. ${next}`;
|
|
3893
|
+
};
|
|
3886
3894
|
const nextAction = (checkpoint?: { branch: string; sha: string }): string => {
|
|
3887
3895
|
if (durableWake)
|
|
3888
3896
|
return `The unit remains live; a reply in this thread is recorded as its continuation action${prUrl ? ` from the open pull request (${prUrl})` : " from the saved branch state"}.`;
|
|
3897
|
+
const cappedAction = spentCapAction();
|
|
3898
|
+
if (cappedAction !== undefined) return cappedAction;
|
|
3889
3899
|
const reconcile = checkpoint
|
|
3890
3900
|
? `verify \`${checkpoint.sha.slice(0, 7)}\` is on \`${checkpoint.branch}\`, then reconcile the remote branch and pull request if needed`
|
|
3891
3901
|
: prUrl
|
|
@@ -4167,7 +4177,10 @@ function renderUnitReportWithWake(
|
|
|
4167
4177
|
]);
|
|
4168
4178
|
case "no_verdict":
|
|
4169
4179
|
if (!shows(verbosity, "verbose"))
|
|
4170
|
-
return
|
|
4180
|
+
return join([
|
|
4181
|
+
`⚠️ No verdict from review round ${e.round.index} — aborted after ${rounds}.${prLine}`,
|
|
4182
|
+
spentCapAction(),
|
|
4183
|
+
]);
|
|
4171
4184
|
return join([
|
|
4172
4185
|
`⚠️ Review round ${e.round.index} ended without a submitted verdict (budget, refusal, or stop) — ship never converts that into a request for changes, so no findings step ran.`,
|
|
4173
4186
|
writeUpPointer(s, e.round.kind, s.reviewRunByRound[e.round.index]),
|
|
@@ -45,6 +45,16 @@ export interface AttrDomain {
|
|
|
45
45
|
refusal: string;
|
|
46
46
|
/** The refusal's cause, from the one code→cause table in src/core/refusal.ts. */
|
|
47
47
|
cause: "request" | "policy" | "system";
|
|
48
|
+
/** The operator's decision, separate from the request's final outcome. */
|
|
49
|
+
operatorOutcome: "binds" | "question" | "refusal" | "non_decision";
|
|
50
|
+
/** Provenance of an accepted typed repository; the slug stays on the run event. */
|
|
51
|
+
operatorRepoSource: "request" | "attachment" | "thread" | "channel";
|
|
52
|
+
operatorAttempts: number;
|
|
53
|
+
operatorRepoCatalog: "skipped" | "available" | "truncated" | "empty" | "unavailable";
|
|
54
|
+
operatorRepoCandidates: number;
|
|
55
|
+
/** Final target resolution, separate from the operator's proposed repo. */
|
|
56
|
+
repoResolution: "skipped" | "resolved" | "rejected" | "unverified" | "conflict" | "unresolved";
|
|
57
|
+
repoTargetRelation: "not_proposed" | "matched" | "overridden" | "unresolved";
|
|
48
58
|
count: number;
|
|
49
59
|
backend: Backend;
|
|
50
60
|
// run.command
|
|
@@ -146,6 +156,11 @@ const IDENTIFIER_KEYS: ReadonlySet<SpanAttrKey> = new Set<SpanAttrKey>([
|
|
|
146
156
|
"outcome",
|
|
147
157
|
"refusal",
|
|
148
158
|
"cause",
|
|
159
|
+
"operatorOutcome",
|
|
160
|
+
"operatorRepoSource",
|
|
161
|
+
"operatorRepoCatalog",
|
|
162
|
+
"repoResolution",
|
|
163
|
+
"repoTargetRelation",
|
|
149
164
|
"command",
|
|
150
165
|
"route",
|
|
151
166
|
"host",
|
|
@@ -204,6 +219,13 @@ const ATTR_TYPE: Record<SpanAttrKey, "string" | "number" | "boolean"> = {
|
|
|
204
219
|
outcome: "string",
|
|
205
220
|
refusal: "string",
|
|
206
221
|
cause: "string",
|
|
222
|
+
operatorOutcome: "string",
|
|
223
|
+
operatorRepoSource: "string",
|
|
224
|
+
operatorAttempts: "number",
|
|
225
|
+
operatorRepoCatalog: "string",
|
|
226
|
+
operatorRepoCandidates: "number",
|
|
227
|
+
repoResolution: "string",
|
|
228
|
+
repoTargetRelation: "string",
|
|
207
229
|
count: "number",
|
|
208
230
|
backend: "string",
|
|
209
231
|
command: "string",
|
|
@@ -99,6 +99,7 @@ interface SpanInit {
|
|
|
99
99
|
sinks: SpanSink[];
|
|
100
100
|
startedAt: number | undefined;
|
|
101
101
|
attrs: SpanAttrs | undefined;
|
|
102
|
+
rootIdentity?: { name: string; runId?: string };
|
|
102
103
|
}
|
|
103
104
|
|
|
104
105
|
class SpanImpl implements Span {
|
|
@@ -109,6 +110,8 @@ class SpanImpl implements Span {
|
|
|
109
110
|
ended = false;
|
|
110
111
|
private readonly sinks: SpanSink[];
|
|
111
112
|
private readonly rec: SpanRecord;
|
|
113
|
+
private readonly rootIdentity: { name: string; runId?: string };
|
|
114
|
+
private readonly isLocalRoot: boolean;
|
|
112
115
|
|
|
113
116
|
constructor(
|
|
114
117
|
private readonly shared: Shared,
|
|
@@ -118,6 +121,8 @@ class SpanImpl implements Span {
|
|
|
118
121
|
this.parentId = init.parentId;
|
|
119
122
|
this.name = sanitizeSpanName(init.name);
|
|
120
123
|
this.sinks = init.sinks;
|
|
124
|
+
this.isLocalRoot = init.rootIdentity === undefined;
|
|
125
|
+
this.rootIdentity = init.rootIdentity ?? { name: this.name, runId: init.attrs?.runId };
|
|
121
126
|
this.rec = {
|
|
122
127
|
traceId: this.traceId,
|
|
123
128
|
spanId: this.id,
|
|
@@ -164,6 +169,7 @@ class SpanImpl implements Span {
|
|
|
164
169
|
sinks: this.sinks,
|
|
165
170
|
startedAt: opts?.startedAt,
|
|
166
171
|
attrs: opts?.attrs,
|
|
172
|
+
rootIdentity: this.rootIdentity,
|
|
167
173
|
});
|
|
168
174
|
child.emitStart();
|
|
169
175
|
return child;
|
|
@@ -177,6 +183,7 @@ class SpanImpl implements Span {
|
|
|
177
183
|
sinks: this.sinks,
|
|
178
184
|
startedAt: opts.startedAt,
|
|
179
185
|
attrs: opts.attrs,
|
|
186
|
+
rootIdentity: this.rootIdentity,
|
|
180
187
|
});
|
|
181
188
|
child.emitStart();
|
|
182
189
|
child.endAt(Math.max(opts.startedAt, opts.endedAt), opts.status ?? "ok", opts.errorKind, opts.errorCode);
|
|
@@ -196,7 +203,7 @@ class SpanImpl implements Span {
|
|
|
196
203
|
}
|
|
197
204
|
for (const s of this.sinks) {
|
|
198
205
|
try {
|
|
199
|
-
s.onEnd(this.record());
|
|
206
|
+
s.onEnd(this.record(), this.rootIdentity);
|
|
200
207
|
} catch (err) {
|
|
201
208
|
this.shared.warn(
|
|
202
209
|
`[trace] sink onEnd threw for ${this.name}: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -215,7 +222,7 @@ class SpanImpl implements Span {
|
|
|
215
222
|
this.rec.status = status ?? this.rec.status ?? "ok";
|
|
216
223
|
for (const s of this.sinks) {
|
|
217
224
|
try {
|
|
218
|
-
s.onEnd(this.record());
|
|
225
|
+
s.onEnd(this.record(), this.rootIdentity);
|
|
219
226
|
} catch (err) {
|
|
220
227
|
this.shared.warn(
|
|
221
228
|
`[trace] sink onEnd threw for ${this.name}: ${err instanceof Error ? err.message : String(err)}`,
|
|
@@ -239,6 +246,7 @@ class SpanImpl implements Span {
|
|
|
239
246
|
|
|
240
247
|
setAttrs(attrs: SpanAttrs): void {
|
|
241
248
|
(this.rec as { attrs: SpanAttrs }).attrs = { ...this.rec.attrs, ...attrs };
|
|
249
|
+
if (this.isLocalRoot) this.rootIdentity.runId = this.rec.attrs.runId;
|
|
242
250
|
}
|
|
243
251
|
|
|
244
252
|
record(): SpanRecord {
|
|
@@ -40,12 +40,23 @@ export interface SpanRecord {
|
|
|
40
40
|
attrs: SpanAttrs;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
/** A live, read-only view of the local root's run identity. Shared by its
|
|
44
|
+
* descendants, including late children; changing the root's runId updates
|
|
45
|
+
* the view without changing any span record. No span, sink or payload is
|
|
46
|
+
* retained here, so a bounded sink can keep it after the root ends. */
|
|
47
|
+
export interface SpanRootIdentity {
|
|
48
|
+
readonly name: string;
|
|
49
|
+
readonly runId?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
43
52
|
/** Observer of span starts and ends (Observer). Sinks are attached to a root
|
|
44
53
|
* and inherited by its whole subtree; a throwing sink never reaches traced
|
|
45
54
|
* code. */
|
|
46
55
|
export interface SpanSink {
|
|
47
56
|
onStart?(span: SpanRecord): void;
|
|
48
|
-
|
|
57
|
+
/** The tracer supplies root identity separately from the record snapshot;
|
|
58
|
+
* synthetic records supplied directly by a caller may omit it. */
|
|
59
|
+
onEnd(span: SpanRecord, root?: SpanRootIdentity): void;
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
/** The seam the no-gaps test enters through (Strategy): production is the
|
|
@@ -82,9 +82,9 @@ export function refusalFilter(sink: SpanSink): SpanSink {
|
|
|
82
82
|
const refused = (status: unknown) => status === 401 || status === 403;
|
|
83
83
|
return {
|
|
84
84
|
...(sink.onStart ? { onStart: (rec) => sink.onStart!(rec) } : {}),
|
|
85
|
-
onEnd: (rec) => {
|
|
85
|
+
onEnd: (rec, root) => {
|
|
86
86
|
if (refused(rec.attrs.httpStatus)) return;
|
|
87
|
-
sink.onEnd(rec);
|
|
87
|
+
sink.onEnd(rec, root);
|
|
88
88
|
},
|
|
89
89
|
};
|
|
90
90
|
}
|
|
@@ -106,7 +106,7 @@ export type SeedAnswer =
|
|
|
106
106
|
/** A backup id as the SDK mints it (a UUID: hex and hyphens) — it becomes a
|
|
107
107
|
* path and a glob on the container, so nothing else may. */
|
|
108
108
|
const BACKUP_ID_RE = /^(?=.*[0-9a-fA-F])[0-9a-fA-F-]{8,64}$/;
|
|
109
|
-
const SLUG_RE = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
|
|
109
|
+
const SLUG_RE = /^(?!\.{1,2}\/)[A-Za-z0-9_.-]+\/(?!\.{1,2}$)[A-Za-z0-9_.-]+$/;
|
|
110
110
|
const SHA_RE = /^[0-9a-f]{40}$/;
|
|
111
111
|
/** A branch name git accepts (`check-ref-format --branch`, in the shape a
|
|
112
112
|
* Slack thread binds): printable, no leading `-`/`/`/`.`, no `..`, no
|
|
@@ -148,8 +148,8 @@ export function parseSeed(v: unknown): { ok: true; seed: SandboxSeed } | { ok: f
|
|
|
148
148
|
* 1. the tree becomes root's — the archive came from the resident's build
|
|
149
149
|
* user, and a sandbox runs everything as root, so git would otherwise
|
|
150
150
|
* refuse the "dubious ownership" and every write would need a chown;
|
|
151
|
-
* 2. origin points at
|
|
152
|
-
* local mirror;
|
|
151
|
+
* 2. origin points at this run's exact repository on the Git door — the
|
|
152
|
+
* resident's checkout fetched from its local mirror;
|
|
153
153
|
* 3. the deps view, when one was restored, replaces whatever `node_modules`
|
|
154
154
|
* the checkout carries (older snapshots still hold one);
|
|
155
155
|
* 4. the thread's ref is fetched from origin (the credential is the exec
|
|
@@ -158,8 +158,17 @@ export function parseSeed(v: unknown): { ok: true; seed: SandboxSeed } | { ok: f
|
|
|
158
158
|
* fetch holds it, else at the fetched tip; without a thread ref the
|
|
159
159
|
* checkout stays on the snapshot's branch;
|
|
160
160
|
* 5. the head is printed last: the answer's `sha`. */
|
|
161
|
+
export function seedDoorRemote(baseUrl: string, slug: string): string {
|
|
162
|
+
if (!SLUG_RE.test(slug)) throw new Error("seed door remote needs owner/name");
|
|
163
|
+
const base = new URL(baseUrl);
|
|
164
|
+
if ((base.protocol !== "https:" && base.protocol !== "http:") || base.username || base.password)
|
|
165
|
+
throw new Error("seed door remote needs an HTTP origin");
|
|
166
|
+
return `${base.origin}/git/${slug}.git`;
|
|
167
|
+
}
|
|
168
|
+
|
|
161
169
|
export function seedFixupScript(input: {
|
|
162
170
|
slug: string;
|
|
171
|
+
doorOrigin: string;
|
|
163
172
|
ref: string;
|
|
164
173
|
fetchRef?: string;
|
|
165
174
|
fetchSha?: string;
|
|
@@ -170,7 +179,7 @@ export function seedFixupScript(input: {
|
|
|
170
179
|
"set -e",
|
|
171
180
|
`cd ${shellQuote(input.checkoutDir)}`,
|
|
172
181
|
"chown -R 0:0 .",
|
|
173
|
-
`git remote set-url origin ${shellQuote(
|
|
182
|
+
`git remote set-url origin ${shellQuote(seedDoorRemote(input.doorOrigin, input.slug))}`,
|
|
174
183
|
];
|
|
175
184
|
if (input.depsDir) {
|
|
176
185
|
lines.push("rm -rf node_modules", `mv ${shellQuote(input.depsDir)} node_modules`);
|