@gobing-ai/spur 0.3.94 → 0.3.96
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/.claude-plugin/marketplace.json +1 -1
- package/README.md +42 -0
- package/board/index.d.ts +38 -0
- package/config/plugin-scripts.json +3 -52
- package/config/rules/boundary/sp-script-placement.yaml +19 -0
- package/config/rules/strict/runtime-boundaries.yaml +5 -0
- package/config/rules/structure/test-location.yaml +2 -0
- package/config/rules/typescript/no-syscall-emulation-in-boundary-mock.yaml +1 -1
- package/config/rules/typescript/output-boundaries.yaml +15 -2
- package/config/script-placement-baseline.json +51 -0
- package/config/workflows/feature-verification.yaml +7 -4
- package/config/workflows/history-anatomy.yaml +23 -11
- package/config/workflows/idea-pipeline.yaml +87 -86
- package/config/workflows/pr-review.yaml +18 -9
- package/config/workflows/task-pipeline.yaml +84 -109
- package/config/workflows/wrapup-pipeline.yaml +173 -46
- package/package.json +15 -11
- package/plugins/sp/README.md +8 -10
- package/plugins/sp/agents/super-reviewer.md +43 -18
- package/plugins/sp/commands/dev-fixgha.md +83 -0
- package/plugins/sp/commands/dev-gitmsg.md +8 -6
- package/plugins/sp/commands/dev-gtd.md +2 -2
- package/plugins/sp/commands/dev-idea.md +22 -12
- package/plugins/sp/commands/dev-plan.md +8 -9
- package/plugins/sp/commands/dev-review.md +22 -13
- package/plugins/sp/commands/dev-verify.md +7 -4
- package/plugins/sp/commands/dev-verifyall.md +5 -4
- package/plugins/sp/commands/dev-wrap.md +1 -1
- package/plugins/sp/commands/spur-init.md +2 -2
- package/plugins/sp/hooks/context-post-tool.ts +2 -2
- package/plugins/sp/hooks/context-session-start.ts +2 -1
- package/plugins/sp/hooks/context-session-stop.ts +22 -14
- package/plugins/sp/hooks/pi/guard-extension.ts +109 -150
- package/plugins/sp/lib/history-anatomy.generated.d.mts +112 -0
- package/plugins/sp/lib/history-anatomy.generated.mjs +686 -0
- package/plugins/sp/lib/idea-handoff.generated.mjs +6 -5
- package/plugins/sp/lib/inline-run.generated.d.mts +13 -2
- package/plugins/sp/lib/inline-run.generated.mjs +33 -16
- package/plugins/sp/lib/quality-gate.generated.d.mts +104 -0
- package/plugins/sp/lib/quality-gate.generated.mjs +438 -0
- package/plugins/sp/lib/residual-scan.generated.d.mts +62 -0
- package/plugins/sp/lib/residual-scan.generated.mjs +210 -0
- package/plugins/sp/lib/spur-bin.ts +36 -0
- package/plugins/sp/lib/step-profile.generated.d.mts +71 -0
- package/plugins/sp/lib/step-profile.generated.mjs +174 -0
- package/plugins/sp/plugin.json +1 -1
- package/plugins/sp/references/roles.md +1 -1
- package/plugins/sp/scripts/daily-summary/daily-summary.mjs +3 -3
- package/plugins/sp/scripts/daily-summary/daily-summary.ts +3 -3
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.mjs +2 -0
- package/plugins/sp/scripts/dogfood-testing/detect-pipeline-driving.ts +1 -0
- package/plugins/sp/scripts/dogfood-testing/validate-report.mjs +9 -7
- package/plugins/sp/scripts/dogfood-testing/validate-report.ts +8 -7
- package/plugins/sp/scripts/feature-verification-steps.ts +6 -6
- package/plugins/sp/scripts/history-anatomy-cache.mjs +23 -22
- package/plugins/sp/scripts/history-anatomy-cache.ts +23 -928
- package/plugins/sp/scripts/inline-run-setup.mjs +90 -315
- package/plugins/sp/scripts/inline-run-setup.ts +109 -639
- package/plugins/sp/scripts/pr-reviewing.mjs +2 -0
- package/plugins/sp/scripts/pr-reviewing.ts +1 -0
- package/plugins/sp/scripts/quality-gate.mjs +38 -21
- package/plugins/sp/scripts/quality-gate.ts +20 -660
- package/plugins/sp/scripts/residual-scan.mjs +138 -159
- package/plugins/sp/scripts/residual-scan.ts +102 -499
- package/plugins/sp/scripts/script-root.mjs +129 -0
- package/plugins/sp/scripts/script-root.ts +199 -0
- package/plugins/sp/scripts/workflow-step-profile.mjs +27 -16
- package/plugins/sp/scripts/workflow-step-profile.ts +22 -314
- package/plugins/sp/scripts/wrapup-drift-probe.mjs +12 -3
- package/plugins/sp/scripts/wrapup-drift-probe.ts +6 -3
- package/plugins/sp/scripts/wrapup-steps.mjs +42 -40
- package/plugins/sp/scripts/wrapup-steps.ts +64 -46
- package/plugins/sp/skills/code-improvement/SKILL.md +5 -4
- package/plugins/sp/skills/code-verification/SKILL.md +46 -16
- package/plugins/sp/skills/code-verification/references/verdict-schema.md +13 -2
- package/plugins/sp/skills/functional-review/SKILL.md +8 -5
- package/plugins/sp/skills/functional-review/references/verdict-schema.md +1 -1
- package/plugins/sp/skills/history-anatomy/references/modes.md +2 -1
- package/plugins/sp/skills/next-feature/references/handoff-routing.md +1 -1
- package/plugins/sp/skills/next-router/references/routing-table.md +2 -2
- package/plugins/sp/skills/spur-cli/SKILL.md +3 -3
- package/plugins/sp/skills/spur-cli/references/agent.md +10 -10
- package/plugins/sp/skills/spur-cli/references/features.md +17 -6
- package/plugins/sp/skills/spur-cli/references/init.md +17 -16
- package/plugins/sp/skills/spur-cli/references/self.md +3 -2
- package/plugins/sp/skills/spur-cli/references/serve.md +10 -10
- package/plugins/sp/skills/spur-cli/references/tasks/verbs.md +28 -5
- package/plugins/sp/skills/spur-cli/references/tasks.md +14 -8
- package/plugins/sp/skills/spur-dev/references/ac-style-guide.md +4 -3
- package/plugins/sp/skills/spur-dev/references/cross-cutting.md +11 -9
- package/plugins/sp/skills/spur-dev/references/decision-brief.md +1 -1
- package/plugins/sp/skills/spur-dev/references/dev-operations.md +86 -70
- package/plugins/sp/skills/spur-dev/references/done-housekeeping.md +3 -3
- package/plugins/sp/skills/spur-dev/references/execution-batch.md +107 -44
- package/plugins/sp/skills/spur-dev/references/execution-workflow.md +5 -9
- package/plugins/sp/skills/spur-dev/references/feature-link-helper.md +3 -3
- package/plugins/sp/skills/spur-dev/references/flag-glossary.md +31 -21
- package/plugins/sp/skills/spur-dev/references/gate-checklists.md +19 -19
- package/plugins/sp/skills/spur-dev/references/idea-evaluation.md +4 -3
- package/plugins/sp/skills/spur-dev/references/inline-pipeline-driver.md +35 -6
- package/plugins/sp/skills/spur-doctor/SKILL.md +1 -1
- package/plugins/sp/skills/sys-architecture/SKILL.md +3 -2
- package/plugins/sp/tsconfig.json +8 -0
- package/spur.js +6638 -4775
- package/web/_astro/BoardApp.BIjMatT1.js +1 -0
- package/web/_astro/{BoardApp.FTEs3-N8.js → BoardApp.GvjIe9Z6.js} +150 -153
- package/web/_astro/TaskDetail.BZM3EAFx.js +1 -0
- package/web/_astro/_commonjsHelpers.CqkleIqs.js +1 -0
- package/web/_astro/{arc.uuAf51IT.js → arc.HkRiZnoI.js} +1 -1
- package/web/_astro/architectureDiagram-3BPJPVTR.BDdK-tgZ.js +36 -0
- package/web/_astro/{blockDiagram-GPEHLZMM.D1mGCq3p.js → blockDiagram-GPEHLZMM.CZ9kOBxx.js} +1 -1
- package/web/_astro/board-facade-react-dom-client.B2U5wMvk.js +1 -0
- package/web/_astro/board-facade-react-dom.Cd2MuMhF.js +1 -0
- package/web/_astro/board-facade-react-jsx-dev-runtime.B_L3JzfC.js +1 -0
- package/web/_astro/board-facade-react-jsx-runtime.BY2yfHRi.js +1 -0
- package/web/_astro/board-facade-react-router-dom.Dy5pjzOw.js +1 -0
- package/web/_astro/board-facade-react-router.DjjqU27_.js +1 -0
- package/web/_astro/board-facade-react.Dgf4GRyj.js +1 -0
- package/web/_astro/{c4Diagram-AAUBKEIU.CMsolcde.js → c4Diagram-AAUBKEIU.DXK9qeRD.js} +1 -1
- package/web/_astro/channel.DlhX2MEt.js +1 -0
- package/web/_astro/{chunk-2J33WTMH.CrGA3fik.js → chunk-2J33WTMH.BTD2WeX5.js} +1 -1
- package/web/_astro/{chunk-4BX2VUAB.DsLVVla0.js → chunk-4BX2VUAB.Ci6Qbcvk.js} +1 -1
- package/web/_astro/{chunk-55IACEB6.Dxc59Tfi.js → chunk-55IACEB6.yl3zsj7p.js} +1 -1
- package/web/_astro/{chunk-727SXJPM.CaEVE2Wy.js → chunk-727SXJPM.ClXZsfyR.js} +1 -1
- package/web/_astro/{chunk-AQP2D5EJ.BiJ4HXeI.js → chunk-AQP2D5EJ.BmKWZBcP.js} +1 -1
- package/web/_astro/{chunk-FMBD7UC4.Mxf1fru5.js → chunk-FMBD7UC4.By30fcb7.js} +1 -1
- package/web/_astro/chunk-JMJ3UQ3L.BQJEJHu4.js +34 -0
- package/web/_astro/{chunk-ND2GUHAM.pyOWQixH.js → chunk-ND2GUHAM.24NmrD-k.js} +1 -1
- package/web/_astro/{chunk-QZHKN3VN.BmEsg4vr.js → chunk-QZHKN3VN.BN4sKdcS.js} +1 -1
- package/web/_astro/chunk-YNUBSHFH.CxeAygfi.js +7 -0
- package/web/_astro/{classDiagram-4FO5ZUOK.BHhhMFTO.js → classDiagram-4FO5ZUOK.CJpoMPb5.js} +1 -1
- package/web/_astro/{classDiagram-v2-Q7XG4LA2.BHhhMFTO.js → classDiagram-v2-Q7XG4LA2.CJpoMPb5.js} +1 -1
- package/web/_astro/client.BhluLQqe.js +1 -0
- package/web/_astro/client.DBpSRLnx.js +9 -0
- package/web/_astro/cose-bilkent-S5V4N54A.BraxQ2Nt.js +1 -0
- package/web/_astro/{cynefin-OW5HDTMX.C2j1_lKL.js → cynefin-OW5HDTMX.gzoU73oL.js} +1 -1
- package/web/_astro/{dagre-BM42HDAG.hZ2NCTdT.js → dagre-BM42HDAG.wyDfWySU.js} +1 -1
- package/web/_astro/{diagram-2AECGRRQ.Bfw5_EzK.js → diagram-2AECGRRQ.h2_EbMxV.js} +1 -1
- package/web/_astro/{diagram-5GNKFQAL.Bt1V_Tmk.js → diagram-5GNKFQAL.Db6dcs89.js} +1 -1
- package/web/_astro/{diagram-KO2AKTUF.DEk-YFwp.js → diagram-KO2AKTUF.NVwzxu_U.js} +1 -1
- package/web/_astro/{diagram-LMA3HP47.CDjndtQm.js → diagram-LMA3HP47.B-eNgDQk.js} +1 -1
- package/web/_astro/{diagram-OG6HWLK6.cFnUHScG.js → diagram-OG6HWLK6.Bsa8ZRbJ.js} +1 -1
- package/web/_astro/{erDiagram-TEJ5UH35.DlhYp7NV.js → erDiagram-TEJ5UH35.BospRE5Q.js} +1 -1
- package/web/_astro/{flowDiagram-I6XJVG4X.DNTpsxfx.js → flowDiagram-I6XJVG4X.DMzvHwAA.js} +1 -1
- package/web/_astro/ganttDiagram-6RSMTGT7.Dmdxgd-M.js +292 -0
- package/web/_astro/{gitGraphDiagram-PVQCEYII.DkEqNI0P.js → gitGraphDiagram-PVQCEYII.CaqdO0rL.js} +1 -1
- package/web/_astro/index.5dn-Zm-2.js +1 -0
- package/web/_astro/index.C-IqPpW2.js +1 -0
- package/web/_astro/index.Cyc_GA4f.js +1 -0
- package/web/_astro/index.EoZlzLK-.css +1 -0
- package/web/_astro/index.tZfqwSut.js +1 -0
- package/web/_astro/{infoDiagram-5YYISTIA.CrjioCTG.js → infoDiagram-5YYISTIA.DDVD9Wn7.js} +1 -1
- package/web/_astro/{ishikawaDiagram-YF4QCWOH.BcK0CN8n.js → ishikawaDiagram-YF4QCWOH.EKyCUFsL.js} +1 -1
- package/web/_astro/{journeyDiagram-JHISSGLW.p0CbBDX1.js → journeyDiagram-JHISSGLW.By2-RkYB.js} +1 -1
- package/web/_astro/jsx-runtime.DWOiEOII.js +1 -0
- package/web/_astro/{kanban-definition-UN3LZRKU.puHIFt6J.js → kanban-definition-UN3LZRKU.ar-WmlyR.js} +1 -1
- package/web/_astro/{linear.Bb-3a1d7.js → linear.BnzMBgo_.js} +1 -1
- package/web/_astro/{mermaid.core.CJDgXOJs.js → mermaid.core.DSqeFu2Y.js} +4 -4
- package/web/_astro/{mindmap-definition-RKZ34NQL.BoAZEI9t.js → mindmap-definition-RKZ34NQL.1XcF8Wh2.js} +1 -1
- package/web/_astro/{pieDiagram-4H26LBE5.CJNSdqY7.js → pieDiagram-4H26LBE5.DnItHOdK.js} +1 -1
- package/web/_astro/{quadrantDiagram-W4KKPZXB.DtTy7_0Z.js → quadrantDiagram-W4KKPZXB.BQQyrnBt.js} +1 -1
- package/web/_astro/{requirementDiagram-4Y6WPE33.BNYV98Xg.js → requirementDiagram-4Y6WPE33.Dmzxun82.js} +1 -1
- package/web/_astro/{sankeyDiagram-5OEKKPKP.DRq9DGHp.js → sankeyDiagram-5OEKKPKP.BBIeRKYB.js} +1 -1
- package/web/_astro/{sequenceDiagram-3UESZ5HK.B7KdBFCm.js → sequenceDiagram-3UESZ5HK.CUNVdBBf.js} +1 -1
- package/web/_astro/{stateDiagram-AJRCARHV.CD62ZJ2H.js → stateDiagram-AJRCARHV.DlFf2sJm.js} +1 -1
- package/web/_astro/{stateDiagram-v2-BHNVJYJU.CeVk1TAj.js → stateDiagram-v2-BHNVJYJU.DXR7I1H5.js} +1 -1
- package/web/_astro/{timeline-definition-PNZ67QCA.C_ZPA5tT.js → timeline-definition-PNZ67QCA.BcO-GyX2.js} +1 -1
- package/web/_astro/{vennDiagram-CIIHVFJN.CrThO_FQ.js → vennDiagram-CIIHVFJN.ChCECmmW.js} +1 -1
- package/web/_astro/{wardleyDiagram-YWT4CUSO.DSZCA5nl.js → wardleyDiagram-YWT4CUSO.CRaT12mD.js} +1 -1
- package/web/_astro/{xychartDiagram-2RQKCTM6.KI7baTuk.js → xychartDiagram-2RQKCTM6.B3lsHVpj.js} +1 -1
- package/web/board-runtime.json +69 -0
- package/web/index.html +2 -2
- package/plugins/sp/lib/artifact-digest.generated.d.mts +0 -7
- package/plugins/sp/lib/artifact-digest.generated.mjs +0 -48
- package/plugins/sp/scripts/feature-sync-bounded.mjs +0 -298
- package/plugins/sp/scripts/feature-sync-bounded.ts +0 -479
- package/plugins/sp/scripts/idea-coverage-check.ts +0 -168
- package/plugins/sp/scripts/inline-pipeline-parity-check.ts +0 -298
- package/plugins/sp/scripts/record-feature-sync.mjs +0 -63
- package/plugins/sp/scripts/record-feature-sync.ts +0 -84
- package/plugins/sp/scripts/script-contract-check.ts +0 -377
- package/plugins/sp/scripts/stage-registry-adapter.ts +0 -1533
- package/plugins/sp/scripts/surface-drift-inventory.ts +0 -989
- package/plugins/sp/scripts/task-evidence-precheck.ts +0 -187
- package/plugins/sp/scripts/task-size-precheck.ts +0 -210
- package/plugins/sp/scripts/transition-shim-check.ts +0 -238
- package/plugins/sp/scripts/validate-commands.ts +0 -689
- package/plugins/sp/scripts/validate-flag-contracts.ts +0 -878
- package/plugins/sp/scripts/verify-answer-lint.ts +0 -547
- package/web/_astro/BoardApp.C02hAHPO.js +0 -1
- package/web/_astro/TaskDetail.C-GdsS-t.js +0 -1
- package/web/_astro/architectureDiagram-3BPJPVTR.CGe629A1.js +0 -36
- package/web/_astro/channel.fsgl7o5j.js +0 -1
- package/web/_astro/client.yhYJvxCU.js +0 -9
- package/web/_astro/cose-bilkent-S5V4N54A.2fH4YOlp.js +0 -1
- package/web/_astro/ganttDiagram-6RSMTGT7.DC_p36PI.js +0 -292
- package/web/_astro/index.De90oHcH.js +0 -1
- package/web/_astro/index.Hjbr15fG.css +0 -1
|
@@ -1,176 +1,56 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
|
-
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* Unlike the subprocess path (`spur workflow run`), the interactive inline driver
|
|
6
|
-
* allocated a run id but never persisted an authoritative `runs` row, so bound
|
|
7
|
-
* `run.artifact` registration (0785 R3) correctly refused every inline record. This
|
|
8
|
-
* script is the thin delegate the driver now runs at Run setup: it loads the source
|
|
9
|
-
* app service when the SPUR_BIN chain identifies a checkout, otherwise its generated bundle
|
|
10
|
-
* (`createOrAttachInlineRun` / `openInlineRunProjectDb` from packages/app), and lets it
|
|
11
|
-
* resolve the SAME project/registered/shared definition the engine would launch, compute the
|
|
12
|
-
* canonical definition digest with the exported hash machinery, and create-or-attach the
|
|
13
|
-
* run row through the existing engine persistence adapter.
|
|
14
|
-
*
|
|
15
|
-
* The script itself contains NO direct SQL, NO second hasher and NO persistence policy —
|
|
16
|
-
* every rule lives in packages/app (0804 D1). On a bundle-only install, the existing
|
|
17
|
-
* workflow-show projection selects the CLI's definition; the bundled app revalidates its
|
|
18
|
-
* schema and digest before recording the selected layer. It never creates an unbound run.
|
|
19
|
-
*
|
|
20
|
-
* The setup outcome is written into the two-file run record (task 0927 R1) — machine state
|
|
21
|
-
* at `.spur/run/<run-id>.state.json` (atomic replace) plus the `.spur/run/<run-id>.md`
|
|
22
|
-
* run-start header — so the driver can seed the inline var overlay (`__runId`,
|
|
23
|
-
* `__definitionDigest`) that proof capture and bound registration verify against. The
|
|
24
|
-
* pre-0927 `<run-id>-inline-setup.json` sidecar is no longer written. Exit 0 = authoritative
|
|
25
|
-
* identity ready (created or idempotently attached); exit 1 = fail closed, the driver must stop.
|
|
26
|
-
*
|
|
27
|
-
* Standard script (ADR-065): the generated Node-runnable twin re-enters Bun for the
|
|
28
|
-
* existing SQLite runtime. Bun on PATH is required; a monorepo checkout is not.
|
|
29
|
-
*
|
|
30
|
-
* Usage:
|
|
31
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]
|
|
32
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]
|
|
33
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> \
|
|
34
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]
|
|
35
|
-
* --status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]
|
|
36
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]
|
|
37
|
-
* bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--spur-bin <path>]
|
|
38
|
-
*
|
|
39
|
-
* The `--fingerprint` mode prints the engine's proof-input digest for the given spec files and
|
|
40
|
-
* creates nothing (task 0862 R5).
|
|
41
|
-
*
|
|
42
|
-
* The `--action` / `--close` modes are the inline driver's ADR-117 emission boundary (task
|
|
43
|
-
* 0868): `--action` records one completed action boundary as an `action_runs` row through the
|
|
44
|
-
* shared `WorkflowActionTraceWriter`, `--close` marks the run row terminal through the same
|
|
45
|
-
* writer. `--action` is best-effort: a persistence failure is appended to
|
|
46
|
-
* `.spur/run/<run-id>.md` and the script still exits 0 with `{"ok":false}` on stdout, so
|
|
47
|
-
* observation never wedges the run. `--close` is NOT best-effort: the run-row closure is
|
|
48
|
-
* bookkeeping, so a missing run row or a persistence failure exits 1 with a named error.
|
|
49
|
-
* The `--decide` mode (task 0941 R5) executes the non-pausing decide action through the SAME
|
|
50
|
-
* app runner the engine registers (`runDecideForInlineRun`), writing the same resultFile row
|
|
51
|
-
* and recording the same `action_runs` trace row (`kind=decide`, best-effort). A degraded
|
|
52
|
-
* decision is still `ok: true` — the run continues — so exit 0 covers every model-level
|
|
53
|
-
* outcome; only an invalid options schema exits 1 (fail closed).
|
|
54
|
-
*
|
|
55
|
-
* The `--persist-out --from <worktree-path>` mode (task 0975 R1) is the WT-4a step a
|
|
56
|
-
* `--worktree` run executes BEFORE teardown: it copies the worktree's run provenance — the
|
|
57
|
-
* `runs`/`action_runs`/`phase_runs`/`transition_runs`/`workflow_states` rows and the two-file
|
|
58
|
-
* run records — into THIS tree's project DB and `.spur/run/` (cwd = the invoking tree).
|
|
59
|
-
* Idempotent; collisions are reported as `skipped[{id,reason}]`, never overwrites. Exit 0 =
|
|
60
|
-
* persisted (possibly with skips); exit 1 = fail closed — the driver routes to WT-5 and
|
|
61
|
-
* retains the worktree; usage errors exit 2.
|
|
62
|
-
*
|
|
63
|
-
* Exit 2 is reserved for usage errors.
|
|
64
|
-
*
|
|
65
|
-
* Env: SPUR_BIN
|
|
66
|
-
*/
|
|
67
|
-
|
|
2
|
+
/** Inline run setup driver (ADR-117/1006 R3): thin glue — argv/env, app-entry resolution, the installed-CLI
|
|
3
|
+
* handshake, mode dispatch. Mode bodies live in the bundled app service (`runInlineRun*` exports); the `.mjs`
|
|
4
|
+
* twin re-enters Bun in `main()` (SQLite) and imports the bundle dynamically, so bare `node` never loads app code. */
|
|
68
5
|
import { spawnSync } from 'node:child_process';
|
|
69
|
-
import {
|
|
70
|
-
appendFileSync,
|
|
71
|
-
closeSync,
|
|
72
|
-
existsSync,
|
|
73
|
-
mkdirSync,
|
|
74
|
-
openSync,
|
|
75
|
-
readFileSync,
|
|
76
|
-
renameSync,
|
|
77
|
-
unlinkSync,
|
|
78
|
-
writeFileSync,
|
|
79
|
-
writeSync,
|
|
80
|
-
} from 'node:fs';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
81
7
|
import { dirname, join, resolve } from 'node:path';
|
|
82
8
|
import { fileURLToPath } from 'node:url';
|
|
9
|
+
// Type-only namespace import (erased before bundling; the smoke scanner exempts `import type`).
|
|
10
|
+
import type * as spurApp from '@gobing-ai/spur-app';
|
|
83
11
|
import { getEnvVar } from '../lib/env';
|
|
84
12
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
readonly runId: string;
|
|
89
|
-
readonly attached?: boolean;
|
|
90
|
-
readonly definitionDigest?: string;
|
|
91
|
-
readonly workflowName?: string;
|
|
92
|
-
readonly workflowVersion?: string | null;
|
|
93
|
-
readonly resolvedPath?: string;
|
|
94
|
-
readonly layer?: string;
|
|
95
|
-
readonly workdir?: string;
|
|
96
|
-
readonly status?: string;
|
|
97
|
-
readonly error?: string;
|
|
98
|
-
}
|
|
13
|
+
// EMBEDDED_SPUR_SCHEMAS: declared by the bundle twin, never exported by the barrel — optional
|
|
14
|
+
// intersection member, exactly what the previous structural cast expressed.
|
|
15
|
+
type InlineApp = typeof spurApp & { EMBEDDED_SPUR_SCHEMAS?: ReadonlyMap<string, string> };
|
|
99
16
|
|
|
100
17
|
function usage(): never {
|
|
101
18
|
console.error(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
'
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
' bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]',
|
|
113
|
-
);
|
|
114
|
-
console.error(
|
|
115
|
-
' bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--spur-bin <path>]',
|
|
116
|
-
);
|
|
117
|
-
console.error(
|
|
118
|
-
' terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, ' +
|
|
119
|
-
'failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted',
|
|
120
|
-
);
|
|
121
|
-
console.error(
|
|
122
|
-
' bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]',
|
|
19
|
+
[
|
|
20
|
+
'Usage: bun plugins/sp/scripts/inline-run-setup.ts --run-id <id> --file <definition> [--spur-bin <path>]',
|
|
21
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --fingerprint --task-file <path> [--feature-file <path>] [--spur-bin <path>]',
|
|
22
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --action --run-id <id> --node <state> --kind <kind> --status <done|failed> --ok <true|false> --duration-ms <n> [--spur-bin <path>]',
|
|
23
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --actions-file <json-file> --run-id <id> [--spur-bin <path>] (1007 R5 batch trace emission)',
|
|
24
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --close --run-id <id> --status <done|failed|paused> [--reason <terminal-reason>] [--spur-bin <path>]',
|
|
25
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --persist-out --from <worktree-path> [--task-file <path>]... [--spur-bin <path>]',
|
|
26
|
+
' terminal-reason is a closed enum (0937 R2): done, paused-operator, failed-check, failed-agent, failed-timeout, failed-guard, cancelled, interrupted, retry-exhausted',
|
|
27
|
+
' bun plugins/sp/scripts/inline-run-setup.ts --decide --run-id <id> --node <state> --options-json <file> [--spur-bin <path>]',
|
|
28
|
+
].join('\n'),
|
|
123
29
|
);
|
|
124
30
|
process.exit(2);
|
|
125
31
|
}
|
|
126
32
|
|
|
127
|
-
|
|
128
|
-
* The run id becomes filenames under `.spur/run/` (`<run-id>.state.json` + `<run-id>.md`),
|
|
129
|
-
* so it must be a single safe filename component before anything is written — the same guard class the
|
|
130
|
-
* task-pipeline.yaml route-reason action applies to `$__runId` (task 0804 R8). The allowlist
|
|
131
|
-
* refuses path separators, dot traversal (leading `.`), unresolved interpolation (`$`/`{`/`}`) and
|
|
132
|
-
* every other shell/unspecified metachar; valid UUID/timestamp-slug ids pass.
|
|
133
|
-
*/
|
|
33
|
+
// Run ids become filenames under `.spur/run/` — single safe component only (0804 R8).
|
|
134
34
|
const SAFE_RUN_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
|
|
135
35
|
|
|
136
36
|
function refuseUnsafeRunId(runId: string): never {
|
|
137
|
-
// Refuse BEFORE any outcome write: an unsafe id must never reach
|
|
138
|
-
// `.spur/run/<run-id>.state.json` / `.md` (no traversal, no unintended file).
|
|
139
37
|
console.error(`inline-run-setup: refusing unsafe run id: ${runId}`);
|
|
140
38
|
console.error(
|
|
141
|
-
' The run id must be a single safe filename component (alphanumeric/._-, no leading dot, '
|
|
142
|
-
'no path separators, interpolation or traversal; same class as the task-pipeline ' +
|
|
143
|
-
'route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.',
|
|
39
|
+
' The run id must be a single safe filename component (alphanumeric/._-, no leading dot, no path separators, interpolation or traversal; same class as the task-pipeline route-reason guard, task 0804 R8). Allocate a fresh run id (uuid or timestamp slug) and retry.',
|
|
144
40
|
);
|
|
145
41
|
process.exit(1);
|
|
146
42
|
}
|
|
147
43
|
|
|
148
|
-
/**
|
|
149
|
-
* Resolve the spur repo checkout the same way the other prechecks resolve the CLI
|
|
150
|
-
* (--spur-bin > SPUR_BIN > monorepo-local CLI entry > PATH `spur`), then derive the repo
|
|
151
|
-
* root from the resolved main module. `bun <repo>/apps/cli/src/index.ts` → repo root is
|
|
152
|
-
* three levels up. A bundle-only install (`spur` on PATH, a bundled `spur.js`, or a spur
|
|
153
|
-
* binary without the app workspace) uses the adjacent generated application bundle.
|
|
154
|
-
*/
|
|
44
|
+
/** Resolve the app entry: --spur-bin/SPUR_BIN main module if it proves a repo checkout, else the adjacent bundle. */
|
|
155
45
|
function resolveAppEntry(spurBin: string): { entry: string; portable: boolean } {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
candidates = [spurBin];
|
|
159
|
-
} else {
|
|
160
|
-
// scripts/ -> plugins/sp/ -> <repo>/apps/cli/src/index.ts (fileURLToPath — raw
|
|
161
|
-
// pathname breaks on %-encoded paths, e.g. spaces in the checkout directory).
|
|
162
|
-
candidates = [fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url))];
|
|
163
|
-
}
|
|
46
|
+
const fallback = fileURLToPath(new URL('../../../apps/cli/src/index.ts', import.meta.url));
|
|
47
|
+
const candidates = spurBin !== '' ? [spurBin] : [fallback];
|
|
164
48
|
for (const candidate of candidates) {
|
|
165
49
|
const tokens = candidate.split(/\s+/).filter(Boolean);
|
|
166
|
-
//
|
|
167
|
-
// `bun run <path>` lead tokens). Only a TypeScript source entry proves a repo
|
|
168
|
-
// checkout; a bundled `spur.js` or a bare `spur` binary does not.
|
|
50
|
+
// Main module = last path-like token; only a .ts source entry proves a repo checkout.
|
|
169
51
|
const mainModule = [...tokens].reverse().find((t) => t.endsWith('.ts'));
|
|
170
52
|
if (mainModule === undefined || !existsSync(mainModule)) continue;
|
|
171
|
-
|
|
172
|
-
const srcDir = dirname(mainModule);
|
|
173
|
-
const repoRoot = resolve(srcDir, '..', '..', '..');
|
|
53
|
+
const repoRoot = resolve(dirname(mainModule), '..', '..', '..');
|
|
174
54
|
const appEntry = join(repoRoot, 'packages', 'app', 'src', 'index.ts');
|
|
175
55
|
if (existsSync(appEntry)) return { entry: appEntry, portable: false };
|
|
176
56
|
}
|
|
@@ -204,139 +84,12 @@ async function readInstalledInventory(file: string, spurBin: string): Promise<un
|
|
|
204
84
|
}
|
|
205
85
|
const value: unknown = JSON.parse(result.stdout);
|
|
206
86
|
// Honor the existing optional JSON envelope without inventing another projection format.
|
|
207
|
-
if (value && typeof value === 'object' && 'ok' in value && 'data' in value && value.ok === true)
|
|
208
|
-
return value.data;
|
|
209
|
-
}
|
|
87
|
+
if (value && typeof value === 'object' && 'ok' in value && 'data' in value && value.ok === true) return value.data;
|
|
210
88
|
return value;
|
|
211
89
|
}
|
|
212
90
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
* merges into `.spur/run/<run-id>.state.json` (atomic same-directory temp + rename, the
|
|
216
|
-
* 0925 R1 pattern) and `.spur/run/<run-id>.md` receives its run-start header exactly once.
|
|
217
|
-
* A re-setup of the same run id rewrites the state from the current outcome but keeps the
|
|
218
|
-
* prior `startedAt` instead of resurrecting setup time. Identity/provenance fields only —
|
|
219
|
-
* never prompt bodies — so the record carries no secret-bearing content by construction
|
|
220
|
-
* (0927 AC2). The retired `<run-id>-inline-setup.json` sidecar is not written (0927 R4);
|
|
221
|
-
* its readers were migrated to the pair state in the same change.
|
|
222
|
-
*/
|
|
223
|
-
function writeOutcome(runId: string, outcome: SetupOutcome): void {
|
|
224
|
-
const runDir = join(process.cwd(), '.spur', 'run');
|
|
225
|
-
if (!existsSync(runDir)) mkdirSync(runDir, { recursive: true });
|
|
226
|
-
const statePath = join(runDir, `${runId}.state.json`);
|
|
227
|
-
const markdownPath = join(runDir, `${runId}.md`);
|
|
228
|
-
let prior: Record<string, unknown> = {};
|
|
229
|
-
try {
|
|
230
|
-
const parsed: unknown = JSON.parse(readFileSync(statePath, 'utf8'));
|
|
231
|
-
if (parsed !== null && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
232
|
-
prior = parsed as Record<string, unknown>;
|
|
233
|
-
}
|
|
234
|
-
} catch {
|
|
235
|
-
// No prior state (new run) or unreadable file — the outcome alone defines the state.
|
|
236
|
-
}
|
|
237
|
-
const at = new Date().toISOString();
|
|
238
|
-
const state = {
|
|
239
|
-
schemaVersion: 1 as const,
|
|
240
|
-
runId,
|
|
241
|
-
...(outcome.workflowName !== undefined ? { workflowName: outcome.workflowName } : {}),
|
|
242
|
-
...(outcome.status !== undefined ? { status: outcome.status } : {}),
|
|
243
|
-
startedAt: typeof prior.startedAt === 'string' ? prior.startedAt : at,
|
|
244
|
-
updatedAt: at,
|
|
245
|
-
...(outcome.attached !== undefined ? { attached: outcome.attached } : {}),
|
|
246
|
-
...(outcome.definitionDigest !== undefined ? { definitionDigest: outcome.definitionDigest } : {}),
|
|
247
|
-
...(outcome.workflowVersion !== undefined ? { workflowVersion: outcome.workflowVersion } : {}),
|
|
248
|
-
...(outcome.resolvedPath !== undefined ? { resolvedPath: outcome.resolvedPath } : {}),
|
|
249
|
-
...(outcome.layer !== undefined ? { layer: outcome.layer } : {}),
|
|
250
|
-
...(outcome.workdir !== undefined ? { workdir: outcome.workdir } : {}),
|
|
251
|
-
// 0948 R7: project the setup outcome itself, so the retired `-inline-setup.json`
|
|
252
|
-
// sidecar's `ok` is still readable from the pair, and a SUCCESSFUL re-setup
|
|
253
|
-
// explicitly drops any prior `error` — a stale failure message must not outlive
|
|
254
|
-
// the failure it described (the state object is rebuilt, never merged with `prior`).
|
|
255
|
-
ok: outcome.ok,
|
|
256
|
-
...(outcome.ok === false && outcome.error !== undefined ? { error: outcome.error } : {}),
|
|
257
|
-
};
|
|
258
|
-
const temp = `${statePath}.tmp`;
|
|
259
|
-
try {
|
|
260
|
-
writeFileSync(temp, `${JSON.stringify(state, null, 4)}\n`);
|
|
261
|
-
renameSync(temp, statePath);
|
|
262
|
-
} catch {
|
|
263
|
-
// Best-effort: a failing record write must not wedge setup reporting — and never
|
|
264
|
-
// leaves `.tmp` residue behind (0926 R1).
|
|
265
|
-
try {
|
|
266
|
-
unlinkSync(temp);
|
|
267
|
-
} catch {
|
|
268
|
-
// Nothing to clean (temp was never created).
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
// 0948 R7: create the header with `wx` (O_EXCL) instead of `existsSync` → `appendFileSync`.
|
|
272
|
-
// The check-then-append pair could append a SECOND header when two setups raced, and it
|
|
273
|
-
// kept "header present" as a separate fact from the identity write. One exclusive create
|
|
274
|
-
// makes exactly-one-header atomic; EEXIST just means it is already there.
|
|
275
|
-
let headerFd: number | undefined;
|
|
276
|
-
try {
|
|
277
|
-
headerFd = openSync(markdownPath, 'wx');
|
|
278
|
-
writeSync(
|
|
279
|
-
headerFd,
|
|
280
|
-
`# spur inline run ${runId} — ${outcome.workflowName ?? 'unknown workflow'} — setup ${at}\n`,
|
|
281
|
-
);
|
|
282
|
-
} catch {
|
|
283
|
-
// EEXIST (already written) or an unwritable path — the human header is not the setup
|
|
284
|
-
// identity, so this stays best-effort exactly like the state write above.
|
|
285
|
-
} finally {
|
|
286
|
-
if (headerFd !== undefined) closeSync(headerFd);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* `--fingerprint` mode (task 0862 R5): print the engine's proof-input digest for a task file
|
|
292
|
-
* (plus optional feature file) instead of creating a run, so the inline driver can capture the
|
|
293
|
-
* fresh digest bound `run.artifact` registration checks against.
|
|
294
|
-
*
|
|
295
|
-
* The digest itself comes from the exported app functions — never reimplemented here — and the
|
|
296
|
-
* options mirror `ProofFingerprintActionRunner` (cwd, spec contents, fileSystem; no `--expect`).
|
|
297
|
-
* Returns the process exit code: 0 printed, 1 read/resolve failure.
|
|
298
|
-
*/
|
|
299
|
-
async function printFingerprint(taskFile: string, featureFile: string, spurBin: string): Promise<number> {
|
|
300
|
-
const { entry } = resolveAppEntry(spurBin);
|
|
301
|
-
const app = (await import(entry)) as {
|
|
302
|
-
computeProofInputFingerprint: (options: Record<string, unknown>) => Promise<string>;
|
|
303
|
-
readProofInputContents: (
|
|
304
|
-
fileSystem: unknown,
|
|
305
|
-
workdir: string,
|
|
306
|
-
options: { taskFile?: unknown; featureFile?: unknown },
|
|
307
|
-
) => Promise<{ ok: true; taskContent?: string; featureContent?: string } | { ok: false; error: string }>;
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
const workdir = process.cwd();
|
|
311
|
-
// `undefined` fs takes readProofInputContents' node-filesystem default — the same default its
|
|
312
|
-
// sibling createGitAlternateTree applies, and the same Node FS the CLI's runner injects.
|
|
313
|
-
const inputs = await app.readProofInputContents(undefined, workdir, {
|
|
314
|
-
taskFile,
|
|
315
|
-
...(featureFile.trim() !== '' ? { featureFile } : {}),
|
|
316
|
-
});
|
|
317
|
-
if (!inputs.ok) {
|
|
318
|
-
console.error(`inline-run-setup: FAIL — ${inputs.error}`);
|
|
319
|
-
return 1;
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
const digest = await app.computeProofInputFingerprint({
|
|
323
|
-
cwd: workdir,
|
|
324
|
-
...(inputs.taskContent !== undefined ? { taskContent: inputs.taskContent } : {}),
|
|
325
|
-
...(inputs.featureContent !== undefined ? { featureContent: inputs.featureContent } : {}),
|
|
326
|
-
});
|
|
327
|
-
process.stdout.write(`${digest}\n`);
|
|
328
|
-
return 0;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
/** Terminal statuses the inline driver may declare when closing its run row. */
|
|
332
|
-
const CLOSE_STATUSES = new Set(['done', 'failed', 'paused']);
|
|
333
|
-
|
|
334
|
-
/**
|
|
335
|
-
* 0937 R2: closed terminal-reason vocabulary the driver may declare on `--close`.
|
|
336
|
-
* COPIED from packages/app/src/workflow/terminal-reason.ts — the plugin standalone
|
|
337
|
-
* contract forbids a value import of app code; the parity test asserts the copy
|
|
338
|
-
* equals the app export.
|
|
339
|
-
*/
|
|
91
|
+
// 0937 R2 closed terminal-reason vocabulary for `--close`. COPIED from
|
|
92
|
+
// packages/app/src/workflow/terminal-reason.ts (no value import); parity test asserts equality.
|
|
340
93
|
const TERMINAL_REASONS = new Set([
|
|
341
94
|
'done',
|
|
342
95
|
'paused-operator',
|
|
@@ -349,257 +102,6 @@ const TERMINAL_REASONS = new Set([
|
|
|
349
102
|
'retry-exhausted',
|
|
350
103
|
]);
|
|
351
104
|
|
|
352
|
-
/** Finalize statuses — a finish emission is terminal, so only done|failed are valid (0868 #4). */
|
|
353
|
-
const ACTION_STATUSES = new Set(['done', 'failed']);
|
|
354
|
-
|
|
355
|
-
/** Input for the ADR-117 emission modes (`--action` / `--close`). */
|
|
356
|
-
import type { WorkflowActionTraceWriter } from '@gobing-ai/spur-app';
|
|
357
|
-
|
|
358
|
-
interface TraceModeInput {
|
|
359
|
-
readonly runId: string;
|
|
360
|
-
readonly close: boolean;
|
|
361
|
-
readonly node: string;
|
|
362
|
-
readonly kind: string;
|
|
363
|
-
/** Declared terminal reason (0937 R2) — validated against TERMINAL_REASONS before this point. */
|
|
364
|
-
readonly reason?: string;
|
|
365
|
-
readonly status: string;
|
|
366
|
-
readonly ok: boolean;
|
|
367
|
-
readonly durationMs: number;
|
|
368
|
-
readonly spurBin: string;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
/**
|
|
372
|
-
* The run-record file for appended driver lines (task 0927 R1): `.spur/run/<run-id>.md`
|
|
373
|
-
* for pair-based runs. A legacy run that predates the pair keeps appending to its declared
|
|
374
|
-
* `.spur/run/<run-id>.log` — the same precedence as `readWorkflowRunRecord` (the pair wins,
|
|
375
|
-
* legacy stays readable in place).
|
|
376
|
-
*/
|
|
377
|
-
function runRecordLogPath(runDir: string, runId: string): string {
|
|
378
|
-
const markdownPath = join(runDir, `${runId}.md`);
|
|
379
|
-
const legacyLogPath = join(runDir, `${runId}.log`);
|
|
380
|
-
if (existsSync(legacyLogPath) && !existsSync(markdownPath)) return legacyLogPath;
|
|
381
|
-
return markdownPath;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Append one emission-failure line to the run record markdown — `.spur/run/<run-id>.md`,
|
|
386
|
-
* the run log the inline driver already owns. Best-effort and synchronous (the process may
|
|
387
|
-
* exit immediately after), and never throws: an unwritable log must not wedge the run
|
|
388
|
-
* (ADR-117 R3).
|
|
389
|
-
*/
|
|
390
|
-
function appendRunLogLine(runId: string, detail: string): void {
|
|
391
|
-
try {
|
|
392
|
-
const runDir = join(process.cwd(), '.spur', 'run');
|
|
393
|
-
if (!existsSync(runDir)) mkdirSync(runDir, { recursive: true });
|
|
394
|
-
const safeRunId = runId.replace(/[^A-Za-z0-9._-]/g, '_');
|
|
395
|
-
const stamp = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
396
|
-
appendFileSync(runRecordLogPath(runDir, safeRunId), `[${stamp}] ${detail}\n`);
|
|
397
|
-
} catch {
|
|
398
|
-
// Best-effort (R3): the run continues even when the failure cannot be recorded.
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/**
|
|
403
|
-
* Emit one trace write through the SHARED `WorkflowActionTraceWriter` (task 0868 R5/R7).
|
|
404
|
-
* `--action` is best-effort: an emission failure is recorded to the run log and reported
|
|
405
|
-
* on stdout as `{"ok":false}`, and the script exits 0 so the run still reaches its declared
|
|
406
|
-
* terminal state (R3/R12). `--close` is bookkeeping, so a missing run row or a persistence
|
|
407
|
-
* failure fails loudly with exit 1 (review findings #1/#4).
|
|
408
|
-
*/
|
|
409
|
-
async function runTraceMode(input: TraceModeInput): Promise<number> {
|
|
410
|
-
const operation = input.close ? 'run.close' : 'action.finish';
|
|
411
|
-
const fail = (error: string): number => {
|
|
412
|
-
appendRunLogLine(
|
|
413
|
-
input.runId,
|
|
414
|
-
`trace-emission-failed operation=${operation} run=${input.runId}` +
|
|
415
|
-
`${input.node === '' ? '' : ` node=${input.node}`}${input.kind === '' ? '' : ` kind=${input.kind}`}: ${error}`,
|
|
416
|
-
);
|
|
417
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}\n`);
|
|
418
|
-
// The action boundary is best-effort (exit 0); the run-row closure fails loudly (exit 1).
|
|
419
|
-
return input.close ? 1 : 0;
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
// Compile-time link (0868 finding #5): the writer half is typed by the real packages/app
|
|
423
|
-
// export (type-only import, erased at runtime) so a signature drift breaks THIS file's
|
|
424
|
-
// typecheck instead of hiding behind the hand-declared cast.
|
|
425
|
-
let projectDb: { adapter: unknown; close: () => void } | undefined;
|
|
426
|
-
try {
|
|
427
|
-
const { entry } = resolveAppEntry(input.spurBin);
|
|
428
|
-
const app = (await import(entry)) as {
|
|
429
|
-
openInlineRunProjectDb: (workdir: string) => Promise<{ adapter: unknown; close: () => void }>;
|
|
430
|
-
createWorkflowActionTraceWriter: (
|
|
431
|
-
db: unknown,
|
|
432
|
-
recordFailure?: (failure: unknown) => void,
|
|
433
|
-
) => WorkflowActionTraceWriter;
|
|
434
|
-
};
|
|
435
|
-
|
|
436
|
-
projectDb = await app.openInlineRunProjectDb(process.cwd());
|
|
437
|
-
const writer = app.createWorkflowActionTraceWriter(projectDb.adapter, (failure: unknown) => {
|
|
438
|
-
const detail = failure as { operation?: string; error?: string };
|
|
439
|
-
appendRunLogLine(
|
|
440
|
-
input.runId,
|
|
441
|
-
`trace-emission-failed operation=${detail.operation ?? operation} run=${input.runId}: ${detail.error ?? 'unknown error'}`,
|
|
442
|
-
);
|
|
443
|
-
});
|
|
444
|
-
const result = (
|
|
445
|
-
input.close
|
|
446
|
-
? await writer.closeRun(input.runId, input.status, undefined, input.reason)
|
|
447
|
-
: await writer.recordAction({
|
|
448
|
-
runId: input.runId,
|
|
449
|
-
node: input.node,
|
|
450
|
-
kind: input.kind,
|
|
451
|
-
status: input.status,
|
|
452
|
-
ok: input.ok,
|
|
453
|
-
durationMs: input.durationMs,
|
|
454
|
-
})
|
|
455
|
-
) as Record<string, unknown>;
|
|
456
|
-
if (result.ok !== true && result.failure !== undefined) {
|
|
457
|
-
// One stdout shape for emission failures (0868 finding #1): flatten the guard's
|
|
458
|
-
// nested failure object to the same `{ok, runId, error}` the direct paths emit.
|
|
459
|
-
const failure = result.failure as { error?: string };
|
|
460
|
-
return fail(failure.error ?? 'unknown trace emission failure');
|
|
461
|
-
}
|
|
462
|
-
if (input.close && input.status === 'done' && result.actionRows === 0) {
|
|
463
|
-
// A run finalized `done` with ZERO recorded action rows is a bookkeeping defect
|
|
464
|
-
// (task 0975 R2): the row is already terminal — closeRun ran above — but the
|
|
465
|
-
// driver must surface this instead of reporting a clean close, and must never
|
|
466
|
-
// backfill rows. Exit 1 with the named code; the run record carries the finding.
|
|
467
|
-
const error = `run ${input.runId} closed done with zero action_runs rows`;
|
|
468
|
-
appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${error}`);
|
|
469
|
-
process.stdout.write(
|
|
470
|
-
`${JSON.stringify({ ok: false, runId: input.runId, error, code: 'NO_ACTION_ROWS', actionRows: 0 })}\n`,
|
|
471
|
-
);
|
|
472
|
-
return 1;
|
|
473
|
-
}
|
|
474
|
-
process.stdout.write(`${JSON.stringify({ ...result, runId: input.runId })}\n`);
|
|
475
|
-
return 0;
|
|
476
|
-
} catch (error) {
|
|
477
|
-
if (input.close && (error as { name?: string }).name === 'RunRowNotFoundError') {
|
|
478
|
-
// The run row must exist before --close can mark it terminal (R6); a missing row
|
|
479
|
-
// is a loud correctness failure, not a best-effort emission failure (finding #4).
|
|
480
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
481
|
-
appendRunLogLine(input.runId, `trace-close-failed run=${input.runId}: ${message}`);
|
|
482
|
-
process.stdout.write(
|
|
483
|
-
`${JSON.stringify({ ok: false, runId: input.runId, error: message, code: 'RUN_NOT_FOUND' })}\n`,
|
|
484
|
-
);
|
|
485
|
-
return 1;
|
|
486
|
-
}
|
|
487
|
-
return fail(error instanceof Error ? error.message : String(error));
|
|
488
|
-
} finally {
|
|
489
|
-
projectDb?.close();
|
|
490
|
-
}
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
/**
|
|
494
|
-
* `--decide` mode (task 0941 R5): execute the non-pausing decide action through the same app
|
|
495
|
-
* function the engine registers (`runDecideForInlineRun` → `DecideActionRunner`), then record
|
|
496
|
-
* the `action_runs` trace row through the shared writer. The decide-enabled switch
|
|
497
|
-
* (`workflow.decideDecisionMaker`) is resolved HERE at the driver boundary (ADR-082) through
|
|
498
|
-
* the bridged facade derivation and passed into the app runner as an explicit parameter —
|
|
499
|
-
* app services never load Spur config. Degraded outcomes (`disabled`,
|
|
500
|
-
* `no-backend`, `error`, `timeout`, `low-confidence`) are normal — the value is the declared
|
|
501
|
-
* default and the run continues — so they print `ok: true` and exit 0. Only an invalid options
|
|
502
|
-
* schema, an unreadable options file, or a failed config load fails closed with exit 1.
|
|
503
|
-
*/
|
|
504
|
-
async function runDecideMode(input: {
|
|
505
|
-
runId: string;
|
|
506
|
-
node: string;
|
|
507
|
-
optionsFile: string;
|
|
508
|
-
spurBin: string;
|
|
509
|
-
}): Promise<number> {
|
|
510
|
-
type DecideOutcome = {
|
|
511
|
-
ok: boolean;
|
|
512
|
-
error?: string;
|
|
513
|
-
value?: string;
|
|
514
|
-
degraded?: boolean;
|
|
515
|
-
reason?: string;
|
|
516
|
-
source?: 'model' | 'default';
|
|
517
|
-
backend?: string | null;
|
|
518
|
-
confidence?: number | null;
|
|
519
|
-
resultFile?: string;
|
|
520
|
-
durationMs?: number;
|
|
521
|
-
};
|
|
522
|
-
const decideFailed = (error: string): number => {
|
|
523
|
-
process.stdout.write(`${JSON.stringify({ ok: false, runId: input.runId, error })}\n`);
|
|
524
|
-
return 1;
|
|
525
|
-
};
|
|
526
|
-
let outcome: DecideOutcome;
|
|
527
|
-
try {
|
|
528
|
-
const { entry, portable } = resolveAppEntry(input.spurBin);
|
|
529
|
-
const app = (await import(entry)) as {
|
|
530
|
-
runDecideForInlineRun: (input2: {
|
|
531
|
-
workdir: string;
|
|
532
|
-
optionsFile: string;
|
|
533
|
-
enabled: boolean;
|
|
534
|
-
}) => Promise<DecideOutcome>;
|
|
535
|
-
};
|
|
536
|
-
// Config switch at the driver boundary (ADR-082 / task 0941 gate fix): the flag comes
|
|
537
|
-
// from the bridged facade derivation in the generated lib — the same committed bundle
|
|
538
|
-
// the inventory flow imports — never a @gobing-ai/* value import. Embedded schemas
|
|
539
|
-
// ride along only in the portable layout, mirroring the setup mode's load posture.
|
|
540
|
-
const lib = (await import(
|
|
541
|
-
fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url))
|
|
542
|
-
)) as typeof import('../lib/inline-run.generated.mjs');
|
|
543
|
-
const enabled = await lib.resolveDecideDecisionMakerEnabled(
|
|
544
|
-
process.cwd(),
|
|
545
|
-
portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined,
|
|
546
|
-
);
|
|
547
|
-
outcome = await app.runDecideForInlineRun({
|
|
548
|
-
workdir: process.cwd(),
|
|
549
|
-
optionsFile: input.optionsFile,
|
|
550
|
-
enabled,
|
|
551
|
-
});
|
|
552
|
-
} catch (error) {
|
|
553
|
-
return decideFailed(error instanceof Error ? error.message : String(error));
|
|
554
|
-
}
|
|
555
|
-
if (!outcome.ok) return decideFailed(outcome.error ?? 'decide failed without an error message');
|
|
556
|
-
process.stdout.write(`${JSON.stringify({ ok: true, runId: input.runId, node: input.node, ...outcome })}\n`);
|
|
557
|
-
// 0976 R2: the run log names the decision's provenance, so a declared-default fallback is
|
|
558
|
-
// never read as a model decision. Best-effort through the same run-log appender.
|
|
559
|
-
appendRunLogLine(
|
|
560
|
-
input.runId,
|
|
561
|
-
`decide node=${input.node} value=${outcome.value ?? ''} source=${outcome.source ?? 'default'} reason=${outcome.reason ?? ''}`,
|
|
562
|
-
);
|
|
563
|
-
// Trace row is best-effort, exactly like --action: an emission failure never wedges the run.
|
|
564
|
-
return await runTraceMode({
|
|
565
|
-
runId: input.runId,
|
|
566
|
-
close: false,
|
|
567
|
-
node: input.node,
|
|
568
|
-
kind: 'decide',
|
|
569
|
-
status: 'done',
|
|
570
|
-
ok: true,
|
|
571
|
-
durationMs: outcome.durationMs ?? 0,
|
|
572
|
-
spurBin: input.spurBin,
|
|
573
|
-
});
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* `--persist-out --from <worktree-path>` (task 0975 R1): resolve the app through the shared
|
|
578
|
-
* chain and copy the worktree's inline-run provenance into THIS tree. No persistence policy
|
|
579
|
-
* lives here — the app's `persistWorktreeRuns` (→ domain `transferRunTables`) owns the DB
|
|
580
|
-
* transfer, the idempotence and the conflict skips. Success prints
|
|
581
|
-
* `{ok:true,persisted:<n>,skipped:[…]}` and exits 0; any failure prints `{ok:false,error}`
|
|
582
|
-
* and exits 1 (the driver routes to WT-5 and retains the worktree).
|
|
583
|
-
*/
|
|
584
|
-
async function runPersistOutMode(input: { from: string; spurBin: string }): Promise<number> {
|
|
585
|
-
try {
|
|
586
|
-
const { entry } = resolveAppEntry(input.spurBin);
|
|
587
|
-
const app = (await import(entry)) as {
|
|
588
|
-
persistWorktreeRuns: (i: {
|
|
589
|
-
fromWorkdir: string;
|
|
590
|
-
toWorkdir: string;
|
|
591
|
-
}) => Promise<{ ok: true; persisted: number; skipped: ReadonlyArray<{ id: string; reason: string }> }>;
|
|
592
|
-
};
|
|
593
|
-
const result = await app.persistWorktreeRuns({ fromWorkdir: input.from, toWorkdir: process.cwd() });
|
|
594
|
-
process.stdout.write(`${JSON.stringify({ ok: true, persisted: result.persisted, skipped: result.skipped })}\n`);
|
|
595
|
-
return 0;
|
|
596
|
-
} catch (error) {
|
|
597
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
598
|
-
process.stdout.write(`${JSON.stringify({ ok: false, error: message })}\n`);
|
|
599
|
-
return 1;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
105
|
async function main(): Promise<void> {
|
|
604
106
|
// The portable Node twin has no workspace imports; SQLite still uses Spur's existing Bun runtime.
|
|
605
107
|
if (!process.versions.bun) {
|
|
@@ -609,89 +111,95 @@ async function main(): Promise<void> {
|
|
|
609
111
|
if (child.error) console.error(`inline-run-setup requires Bun on PATH: ${child.error.message}`);
|
|
610
112
|
process.exit(child.status ?? 1);
|
|
611
113
|
}
|
|
612
|
-
|
|
613
|
-
let
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
let decide = false;
|
|
620
|
-
let persistOut = false;
|
|
621
|
-
let from = '';
|
|
622
|
-
let optionsJson = '';
|
|
623
|
-
let node = '';
|
|
624
|
-
let kind = '';
|
|
625
|
-
let status = '';
|
|
626
|
-
let reason = '';
|
|
627
|
-
let okRaw = '';
|
|
628
|
-
let durationRaw = '';
|
|
114
|
+
const flags = new Map<string, string>();
|
|
115
|
+
let fingerprint = false,
|
|
116
|
+
action = false,
|
|
117
|
+
close = false,
|
|
118
|
+
decide = false,
|
|
119
|
+
persistOut = false;
|
|
120
|
+
const taskFiles: string[] = [];
|
|
629
121
|
let spurBin = getEnvVar('SPUR_BIN') ?? '';
|
|
630
122
|
const argv = process.argv.slice(2);
|
|
631
123
|
for (let i = 0; i < argv.length; i++) {
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
else if (
|
|
635
|
-
else if (
|
|
636
|
-
else if (
|
|
637
|
-
else if (
|
|
638
|
-
else if (
|
|
639
|
-
else if (
|
|
640
|
-
else if (argv[i]
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
124
|
+
const flag = argv[i];
|
|
125
|
+
if (flag === '--fingerprint') fingerprint = true;
|
|
126
|
+
else if (flag === '--action') action = true;
|
|
127
|
+
else if (flag === '--close') close = true;
|
|
128
|
+
else if (flag === '--decide') decide = true;
|
|
129
|
+
else if (flag === '--persist-out') persistOut = true;
|
|
130
|
+
else if (flag === '--task-file') taskFiles.push(argv[++i] ?? '');
|
|
131
|
+
else if (flag === '--spur-bin') spurBin = argv[++i] ?? spurBin;
|
|
132
|
+
else if (flag !== undefined) flags.set(flag, argv[++i] ?? '');
|
|
133
|
+
}
|
|
134
|
+
// Repeated value flags: last occurrence wins (Map.set), matching the previous assignment chain.
|
|
135
|
+
const runId = flags.get('--run-id') ?? '';
|
|
136
|
+
const file = flags.get('--file') ?? '';
|
|
137
|
+
const featureFile = flags.get('--feature-file') ?? '';
|
|
138
|
+
const from = flags.get('--from') ?? '';
|
|
139
|
+
const optionsJson = flags.get('--options-json') ?? '';
|
|
140
|
+
const node = flags.get('--node') ?? '';
|
|
141
|
+
const kind = flags.get('--kind') ?? '';
|
|
142
|
+
const status = flags.get('--status') ?? '';
|
|
143
|
+
const reason = flags.get('--reason') ?? '';
|
|
144
|
+
const okRaw = flags.get('--ok') ?? '';
|
|
145
|
+
const durationRaw = flags.get('--duration-ms') ?? '';
|
|
146
|
+
const actionsFile = flags.get('--actions-file') ?? '';
|
|
651
147
|
|
|
652
|
-
// Two mutually exclusive modes share this entry point: create/attach a run (run-id + file), or
|
|
653
|
-
// print the proof digest for the inline driver (task 0862 R5). Mixing them is a usage error.
|
|
654
148
|
if (fingerprint) {
|
|
655
|
-
if (runId !== '' || file !== '' ||
|
|
656
|
-
|
|
149
|
+
if (runId !== '' || file !== '' || taskFiles.length !== 1 || (taskFiles[0] ?? '').trim() === '') usage();
|
|
150
|
+
const app = (await import(resolveAppEntry(spurBin).entry)) as InlineApp;
|
|
151
|
+
process.exit(await app.runInlineRunFingerprint({ taskFile: taskFiles[0] ?? '', featureFile }));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (actionsFile !== '') {
|
|
155
|
+
if (action || close || fingerprint || decide || persistOut || file !== '' || taskFiles.length > 0) usage();
|
|
156
|
+
if (runId.trim() === '' || status !== '' || node !== '' || kind !== '') usage();
|
|
157
|
+
if (okRaw !== '' || durationRaw !== '') usage();
|
|
158
|
+
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
159
|
+
const app = (await import(resolveAppEntry(spurBin).entry)) as InlineApp;
|
|
160
|
+
process.exit(await app.runInlineRunTraceBatch({ runId, actionsFile }));
|
|
657
161
|
}
|
|
658
162
|
|
|
659
|
-
// ADR-117 emission modes (task 0868): the inline driver reports one completed action
|
|
660
|
-
// boundary, or closes its run row at the declared terminal state. Both share the run-id
|
|
661
|
-
// filename guard, the app-entry resolution chain and the best-effort failure contract.
|
|
662
|
-
// Non-pausing decide action (0941 R5): --decide is mutually exclusive with the run-setup,
|
|
663
|
-
// fingerprint, and trace modes; it requires the run id (filename-guarded), the state id
|
|
664
|
-
// for the trace row, and the options JSON file.
|
|
665
163
|
if (decide) {
|
|
666
|
-
if (action || close || fingerprint || file !== '' ||
|
|
164
|
+
if (action || close || fingerprint || file !== '' || taskFiles.length > 0) usage();
|
|
667
165
|
if (runId.trim() === '' || node.trim() === '' || optionsJson.trim() === '') usage();
|
|
668
166
|
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
669
|
-
|
|
167
|
+
try {
|
|
168
|
+
const { entry, portable } = resolveAppEntry(spurBin);
|
|
169
|
+
const app = (await import(entry)) as InlineApp;
|
|
170
|
+
const bundlePath = fileURLToPath(new URL('../lib/inline-run.generated.mjs', import.meta.url));
|
|
171
|
+
const lib = (await import(bundlePath)) as typeof import('../lib/inline-run.generated.mjs');
|
|
172
|
+
// Decide-enabled switch resolved at the driver boundary (ADR-082), passed as a param.
|
|
173
|
+
const enabled = await lib.resolveDecideDecisionMakerEnabled(
|
|
174
|
+
process.cwd(),
|
|
175
|
+
portable ? { embeddedSchemas: lib.EMBEDDED_SPUR_SCHEMAS } : undefined,
|
|
176
|
+
);
|
|
177
|
+
process.exit(await app.runInlineRunDecide({ runId, node, optionsFile: optionsJson, enabled }));
|
|
178
|
+
} catch (error) {
|
|
179
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
180
|
+
process.stdout.write(`${JSON.stringify({ ok: false, runId, error: message })}\n`);
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
670
183
|
}
|
|
671
184
|
|
|
672
|
-
// Worktree provenance persist-out (task 0975 R1): mutually exclusive with every other
|
|
673
|
-
// mode; requires the source worktree path. Runs with cwd = the invoking tree.
|
|
674
185
|
if (persistOut) {
|
|
675
|
-
if (fingerprint || decide || action || close || runId !== '' || file !== ''
|
|
676
|
-
if (from.trim() === '') usage();
|
|
677
|
-
|
|
186
|
+
if (fingerprint || decide || action || close || runId !== '' || file !== '') usage();
|
|
187
|
+
if (from.trim() === '' || taskFiles.some((taskFile) => taskFile.trim() === '')) usage();
|
|
188
|
+
const app = (await import(resolveAppEntry(spurBin).entry)) as InlineApp;
|
|
189
|
+
process.exit(await app.runInlineRunPersistOut({ from, taskFiles }));
|
|
678
190
|
}
|
|
679
191
|
|
|
680
192
|
if (action || close) {
|
|
681
193
|
if (action && close) usage();
|
|
682
194
|
if (runId.trim() === '' || status.trim() === '') usage();
|
|
683
195
|
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
196
|
+
const app = (await import(resolveAppEntry(spurBin).entry)) as InlineApp;
|
|
684
197
|
if (close) {
|
|
685
|
-
if (!
|
|
686
|
-
// 0937 R2:
|
|
687
|
-
|
|
688
|
-
if (reason.trim() === '') {
|
|
689
|
-
if (status === 'failed') usage();
|
|
690
|
-
} else if (!TERMINAL_REASONS.has(reason)) {
|
|
691
|
-
usage();
|
|
692
|
-
}
|
|
198
|
+
if (!app.isInlineRunCloseStatus(status)) usage();
|
|
199
|
+
// 0937 R2: failed close needs a declared closed-enum reason — before any write.
|
|
200
|
+
if (reason.trim() === '' ? status === 'failed' : !TERMINAL_REASONS.has(reason)) usage();
|
|
693
201
|
process.exit(
|
|
694
|
-
await
|
|
202
|
+
await app.runInlineRunTrace({
|
|
695
203
|
runId,
|
|
696
204
|
close: true,
|
|
697
205
|
node: '',
|
|
@@ -699,79 +207,41 @@ async function main(): Promise<void> {
|
|
|
699
207
|
status,
|
|
700
208
|
ok: true,
|
|
701
209
|
durationMs: 0,
|
|
702
|
-
spurBin,
|
|
703
210
|
...(reason.trim() === '' ? {} : { reason }),
|
|
704
211
|
}),
|
|
705
212
|
);
|
|
706
213
|
}
|
|
707
214
|
if (node.trim() === '' || kind.trim() === '') usage();
|
|
708
|
-
if (!
|
|
709
|
-
// `--ok` and `--duration-ms` are required and exact
|
|
710
|
-
// (review finding #2): a miscased `--ok True` or an omitted `--duration-ms`
|
|
711
|
-
// must be a loud usage error, never a silently-defaulted `ok=0` /
|
|
712
|
-
// `duration_ms=0` row.
|
|
215
|
+
if (!app.isInlineRunActionStatus(status)) usage();
|
|
216
|
+
// `--ok` and `--duration-ms` are required and exact (0868 #2): no silent defaults.
|
|
713
217
|
if (okRaw !== 'true' && okRaw !== 'false') usage();
|
|
714
|
-
const ok = okRaw === 'true';
|
|
715
218
|
const durationMs = Number(durationRaw);
|
|
716
219
|
if (durationRaw.trim() === '' || !Number.isFinite(durationMs) || durationMs < 0) usage();
|
|
717
|
-
process.exit(
|
|
220
|
+
process.exit(
|
|
221
|
+
await app.runInlineRunTrace({ runId, close: false, node, kind, status, ok: okRaw === 'true', durationMs }),
|
|
222
|
+
);
|
|
718
223
|
}
|
|
719
224
|
|
|
720
225
|
if (runId.trim() === '' || file.trim() === '') usage();
|
|
721
226
|
if (!SAFE_RUN_ID_RE.test(runId)) refuseUnsafeRunId(runId);
|
|
722
|
-
|
|
723
227
|
const { entry, portable } = resolveAppEntry(spurBin);
|
|
724
|
-
|
|
725
|
-
// Dynamic import by absolute path: the app source graph resolves its own workspace
|
|
726
|
-
// dependencies from the repo checkout, never from this plugin script's location.
|
|
727
|
-
const app = (await import(entry)) as {
|
|
728
|
-
createOrAttachInlineRun: (input: {
|
|
729
|
-
workdir: string;
|
|
730
|
-
getDb: () => Promise<unknown>;
|
|
731
|
-
file: string;
|
|
732
|
-
runId: string;
|
|
733
|
-
inventory?: unknown;
|
|
734
|
-
embeddedSchemas?: ReadonlyMap<string, string>;
|
|
735
|
-
}) => Promise<SetupOutcome & { ok: boolean }>;
|
|
736
|
-
openInlineRunProjectDb: (workdir: string) => Promise<{ adapter: unknown; close: () => void }>;
|
|
737
|
-
EMBEDDED_SPUR_SCHEMAS?: ReadonlyMap<string, string>;
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
const workdir = process.cwd();
|
|
228
|
+
const app = (await import(entry)) as InlineApp;
|
|
741
229
|
let inventory: unknown;
|
|
742
230
|
try {
|
|
743
231
|
inventory = await readInstalledInventory(file, spurBin);
|
|
744
232
|
} catch (error) {
|
|
745
233
|
const message = `could not resolve the workflow definition: ${error instanceof Error ? error.message : String(error)}`;
|
|
746
|
-
|
|
234
|
+
app.writeInlineRunOutcome(runId, { ok: false, runId, error: message });
|
|
747
235
|
throw new Error(message);
|
|
748
236
|
}
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
try {
|
|
752
|
-
const result = await app.createOrAttachInlineRun({
|
|
753
|
-
workdir,
|
|
754
|
-
getDb: async () => projectDb.adapter,
|
|
755
|
-
file,
|
|
237
|
+
process.exit(
|
|
238
|
+
await app.runInlineRunSetup({
|
|
756
239
|
runId,
|
|
240
|
+
file,
|
|
757
241
|
inventory,
|
|
758
242
|
...(portable ? { embeddedSchemas: app.EMBEDDED_SPUR_SCHEMAS } : {}),
|
|
759
|
-
})
|
|
760
|
-
|
|
761
|
-
if (!result.ok) {
|
|
762
|
-
console.error(`inline-run-setup: FAIL for run ${runId}`);
|
|
763
|
-
console.error(` ${result.error}`);
|
|
764
|
-
exitCode = 1;
|
|
765
|
-
} else {
|
|
766
|
-
console.error(
|
|
767
|
-
`inline-run-setup: ${result.attached ? 'attached' : 'created'} run ${runId} ` +
|
|
768
|
-
`(${result.workflowName}, layer ${result.layer}, digest ${result.definitionDigest}, status ${result.status})`,
|
|
769
|
-
);
|
|
770
|
-
}
|
|
771
|
-
} finally {
|
|
772
|
-
projectDb.close();
|
|
773
|
-
}
|
|
774
|
-
process.exit(exitCode);
|
|
243
|
+
}),
|
|
244
|
+
);
|
|
775
245
|
}
|
|
776
246
|
|
|
777
247
|
main().catch((e: unknown) => {
|