dorfl 0.14.2 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/advance-ci-template.d.ts +17 -16
- package/dist/advance-ci-template.d.ts.map +1 -1
- package/dist/advance-ci-template.js +42 -53
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-drivers.d.ts +8 -0
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +28 -4
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +3 -2
- package/dist/advance-isolated.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts +41 -40
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +309 -489
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +3 -2
- package/dist/advance-loop-driver.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +31 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +40 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +63 -3
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +80 -12
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +9 -0
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +21 -1
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-env.d.ts +61 -0
- package/dist/agent-env.d.ts.map +1 -0
- package/dist/agent-env.js +144 -0
- package/dist/agent-env.js.map +1 -0
- package/dist/agent-stop.d.ts +35 -0
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +62 -0
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-merge-action.d.ts +76 -34
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +339 -129
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-stuck-action.js +2 -2
- package/dist/apply-stuck-action.js.map +1 -1
- package/dist/ci-agent-result.d.ts +151 -0
- package/dist/ci-agent-result.d.ts.map +1 -0
- package/dist/ci-agent-result.js +314 -0
- package/dist/ci-agent-result.js.map +1 -0
- package/dist/ci-handoff-apply.d.ts +162 -0
- package/dist/ci-handoff-apply.d.ts.map +1 -0
- package/dist/ci-handoff-apply.js +497 -0
- package/dist/ci-handoff-apply.js.map +1 -0
- package/dist/ci-handoff-format.d.ts +369 -0
- package/dist/ci-handoff-format.d.ts.map +1 -0
- package/dist/ci-handoff-format.js +646 -0
- package/dist/ci-handoff-format.js.map +1 -0
- package/dist/ci-handoff-lfs.d.ts +146 -0
- package/dist/ci-handoff-lfs.d.ts.map +1 -0
- package/dist/ci-handoff-lfs.js +397 -0
- package/dist/ci-handoff-lfs.js.map +1 -0
- package/dist/ci-handoff.d.ts +99 -0
- package/dist/ci-handoff.d.ts.map +1 -0
- package/dist/ci-handoff.js +277 -0
- package/dist/ci-handoff.js.map +1 -0
- package/dist/ci-lock-outputs.d.ts +93 -0
- package/dist/ci-lock-outputs.d.ts.map +1 -0
- package/dist/ci-lock-outputs.js +223 -0
- package/dist/ci-lock-outputs.js.map +1 -0
- package/dist/ci-phase-build.d.ts +178 -0
- package/dist/ci-phase-build.d.ts.map +1 -0
- package/dist/ci-phase-build.js +989 -0
- package/dist/ci-phase-build.js.map +1 -0
- package/dist/ci-phase-driver.d.ts +247 -0
- package/dist/ci-phase-driver.d.ts.map +1 -0
- package/dist/ci-phase-driver.js +370 -0
- package/dist/ci-phase-driver.js.map +1 -0
- package/dist/ci-phase-intake.d.ts +172 -0
- package/dist/ci-phase-intake.d.ts.map +1 -0
- package/dist/ci-phase-intake.js +555 -0
- package/dist/ci-phase-intake.js.map +1 -0
- package/dist/ci-phase-tasking.d.ts +205 -0
- package/dist/ci-phase-tasking.d.ts.map +1 -0
- package/dist/ci-phase-tasking.js +906 -0
- package/dist/ci-phase-tasking.js.map +1 -0
- package/dist/ci-phase-treeless.d.ts +224 -0
- package/dist/ci-phase-treeless.d.ts.map +1 -0
- package/dist/ci-phase-treeless.js +1482 -0
- package/dist/ci-phase-treeless.js.map +1 -0
- package/dist/ci-read-token.d.ts +47 -0
- package/dist/ci-read-token.d.ts.map +1 -0
- package/dist/ci-read-token.js +80 -0
- package/dist/ci-read-token.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +447 -22
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +10 -1
- package/dist/close-job-template.d.ts.map +1 -1
- package/dist/close-job-template.js +46 -14
- package/dist/close-job-template.js.map +1 -1
- package/dist/close-job.d.ts +20 -5
- package/dist/close-job.d.ts.map +1 -1
- package/dist/close-job.js +65 -12
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +20 -1
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +41 -44
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +63 -15
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +75 -8
- package/dist/continue-branch.js.map +1 -1
- package/dist/crashed-run-save.d.ts +73 -0
- package/dist/crashed-run-save.d.ts.map +1 -0
- package/dist/crashed-run-save.js +86 -0
- package/dist/crashed-run-save.js.map +1 -0
- package/dist/cwd-section.d.ts.map +1 -1
- package/dist/cwd-section.js +10 -2
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +10 -0
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-config.d.ts +2 -24
- package/dist/do-config.d.ts.map +1 -1
- package/dist/do-config.js +1 -33
- package/dist/do-config.js.map +1 -1
- package/dist/do.d.ts +149 -1
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +115 -25
- package/dist/do.js.map +1 -1
- package/dist/dorfl-item-template.d.ts +98 -0
- package/dist/dorfl-item-template.d.ts.map +1 -0
- package/dist/dorfl-item-template.js +504 -0
- package/dist/dorfl-item-template.js.map +1 -0
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +7 -5
- package/dist/env-config.js.map +1 -1
- package/dist/frontmatter.d.ts +52 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +136 -2
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +13 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +16 -2
- package/dist/gc.js.map +1 -1
- package/dist/git.d.ts +18 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +70 -5
- package/dist/git.js.map +1 -1
- package/dist/harness.d.ts +44 -5
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +24 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/install-ci-action-pins.d.ts +123 -0
- package/dist/install-ci-action-pins.d.ts.map +1 -0
- package/dist/install-ci-action-pins.js +190 -0
- package/dist/install-ci-action-pins.js.map +1 -0
- package/dist/install-ci-actions-pr-creation.d.ts +70 -0
- package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
- package/dist/install-ci-actions-pr-creation.js +106 -0
- package/dist/install-ci-actions-pr-creation.js.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.js +26 -0
- package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
- package/dist/install-ci-core.d.ts +155 -37
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +304 -200
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +47 -2
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +113 -1
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-ci.d.ts +25 -6
- package/dist/install-ci.d.ts.map +1 -1
- package/dist/install-ci.js +72 -36
- package/dist/install-ci.js.map +1 -1
- package/dist/intake-marker.d.ts +6 -1
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-marker.js.map +1 -1
- package/dist/intake-triage.d.ts.map +1 -1
- package/dist/intake-triage.js +6 -2
- package/dist/intake-triage.js.map +1 -1
- package/dist/intake-trigger-template.d.ts +31 -25
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +107 -292
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +186 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +533 -416
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +154 -1
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +759 -486
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +12 -0
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +21 -0
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +12 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +19 -8
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +93 -11
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +233 -65
- package/dist/item-lock.js.map +1 -1
- package/dist/land-question-residue.d.ts +58 -0
- package/dist/land-question-residue.d.ts.map +1 -0
- package/dist/land-question-residue.js +95 -0
- package/dist/land-question-residue.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +16 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +54 -6
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/merge-answer.d.ts +41 -0
- package/dist/merge-answer.d.ts.map +1 -0
- package/dist/merge-answer.js +60 -0
- package/dist/merge-answer.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +105 -30
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +202 -91
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/merge-question-tick.d.ts +64 -0
- package/dist/merge-question-tick.d.ts.map +1 -0
- package/dist/merge-question-tick.js +96 -0
- package/dist/merge-question-tick.js.map +1 -0
- package/dist/mint-adr.d.ts.map +1 -1
- package/dist/mint-adr.js +20 -2
- package/dist/mint-adr.js.map +1 -1
- package/dist/needs-attention.d.ts +72 -2
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +275 -38
- package/dist/needs-attention.js.map +1 -1
- package/dist/phase-recorder.d.ts +168 -0
- package/dist/phase-recorder.d.ts.map +1 -0
- package/dist/phase-recorder.js +218 -0
- package/dist/phase-recorder.js.map +1 -0
- package/dist/phase.d.ts +94 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +148 -0
- package/dist/phase.js.map +1 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +19 -6
- package/dist/pi-harness.js.map +1 -1
- package/dist/prepare.d.ts.map +1 -1
- package/dist/prepare.js +4 -0
- package/dist/prepare.js.map +1 -1
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +48 -1
- package/dist/prompt.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +1 -1
- package/dist/reap-branches.d.ts +48 -0
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +64 -0
- package/dist/reap-branches.js.map +1 -1
- package/dist/ref-write.d.ts +198 -0
- package/dist/ref-write.d.ts.map +1 -0
- package/dist/ref-write.js +79 -0
- package/dist/ref-write.js.map +1 -0
- package/dist/repo-config.js +8 -8
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts +36 -0
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +91 -1
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.d.ts +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/run.d.ts +7 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +28 -4
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +19 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +21 -8
- package/dist/scan.js.map +1 -1
- package/dist/sidecar.d.ts +14 -0
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +21 -0
- package/dist/sidecar.js.map +1 -1
- package/dist/single-job-warning.d.ts +63 -0
- package/dist/single-job-warning.d.ts.map +1 -0
- package/dist/single-job-warning.js +110 -0
- package/dist/single-job-warning.js.map +1 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
- package/dist/spec-complete.d.ts +13 -6
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +23 -11
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +47 -14
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +12 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -3
- package/dist/status.js.map +1 -1
- package/dist/tasking.d.ts +246 -1
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +452 -358
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +21 -2
- package/dist/triage-persist.js.map +1 -1
- package/dist/verify-workflow-template.d.ts.map +1 -1
- package/dist/verify-workflow-template.js +8 -4
- package/dist/verify-workflow-template.js.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +4 -0
- package/dist/verify.js.map +1 -1
- package/dist/watch-session.d.ts +42 -13
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +52 -23
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +10 -4
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +11 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +65 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +159 -11
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
- package/src/advance-ci-template.ts +80 -81
- package/src/advance-drivers.ts +45 -6
- package/src/advance-isolated.ts +3 -5
- package/src/advance-lifecycle-template.ts +375 -538
- package/src/advance-loop-driver.ts +3 -5
- package/src/advance-treeless-publish.ts +61 -0
- package/src/advance.ts +145 -15
- package/src/advancing-lock.ts +36 -1
- package/src/agent-env.ts +174 -0
- package/src/agent-stop.ts +81 -0
- package/src/apply-merge-action.ts +401 -154
- package/src/apply-stuck-action.ts +2 -2
- package/src/ci-agent-result.ts +421 -0
- package/src/ci-handoff-apply.ts +737 -0
- package/src/ci-handoff-format.ts +926 -0
- package/src/ci-handoff-lfs.ts +519 -0
- package/src/ci-handoff.ts +392 -0
- package/src/ci-lock-outputs.ts +273 -0
- package/src/ci-phase-build.ts +1328 -0
- package/src/ci-phase-driver.ts +548 -0
- package/src/ci-phase-intake.ts +861 -0
- package/src/ci-phase-tasking.ts +1255 -0
- package/src/ci-phase-treeless.ts +2028 -0
- package/src/ci-read-token.ts +90 -0
- package/src/cli.ts +591 -29
- package/src/close-job-template.ts +52 -15
- package/src/close-job.ts +77 -12
- package/src/complete.ts +19 -1
- package/src/config.ts +43 -48
- package/src/continue-branch.ts +121 -16
- package/src/crashed-run-save.ts +162 -0
- package/src/cwd-section.ts +10 -0
- package/src/decision-engine.ts +10 -0
- package/src/do-config.ts +2 -57
- package/src/do.ts +170 -28
- package/src/dorfl-item-template.ts +556 -0
- package/src/env-config.ts +7 -5
- package/src/frontmatter.ts +155 -2
- package/src/gc.ts +27 -2
- package/src/git.ts +83 -5
- package/src/harness.ts +58 -6
- package/src/index.ts +50 -1
- package/src/install-ci-action-pins.ts +209 -0
- package/src/install-ci-actions-pr-creation.ts +186 -0
- package/src/install-ci-capabilities/dorfl-item.ts +33 -0
- package/src/install-ci-core.ts +388 -233
- package/src/install-ci-github.ts +149 -1
- package/src/install-ci.ts +83 -37
- package/src/intake-marker.ts +6 -1
- package/src/intake-triage.ts +7 -3
- package/src/intake-trigger-template.ts +126 -327
- package/src/intake.ts +725 -544
- package/src/integration-core.ts +975 -523
- package/src/integrator.ts +28 -0
- package/src/isolation.ts +29 -10
- package/src/item-lock.ts +308 -92
- package/src/land-question-residue.ts +122 -0
- package/src/lifecycle-gather.ts +82 -10
- package/src/merge-answer.ts +82 -0
- package/src/merge-question-surfacer.ts +320 -112
- package/src/merge-question-tick.ts +182 -0
- package/src/mint-adr.ts +28 -2
- package/src/needs-attention.ts +350 -42
- package/src/phase-recorder.ts +350 -0
- package/src/phase.ts +177 -0
- package/src/pi-harness.ts +25 -5
- package/src/prepare.ts +4 -0
- package/src/prompt.ts +68 -1
- package/src/reap-branches.ts +107 -0
- package/src/ref-write.ts +324 -0
- package/src/repo-config.ts +8 -8
- package/src/repo-mirror.ts +107 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +47 -6
- package/src/scan.ts +37 -7
- package/src/sidecar.ts +36 -0
- package/src/single-job-warning.ts +135 -0
- package/src/spec-complete.ts +36 -14
- package/src/start.ts +55 -16
- package/src/status.ts +29 -3
- package/src/tasking.ts +593 -378
- package/src/triage-persist.ts +24 -2
- package/src/verify-workflow-template.ts +8 -4
- package/src/verify.ts +4 -0
- package/src/watch-session.ts +86 -28
- package/src/work-layout.ts +11 -3
- package/src/workspace.ts +210 -14
package/dist/install-ci-core.js
CHANGED
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
* This module is the PROVIDER-AGNOSTIC half: the config model (`ProviderEntry` /
|
|
11
11
|
* `AuthMode` / `CIConfigFile`), the `models.json` builder, the secret-orchestration
|
|
12
12
|
* LOGIC (which secrets, dedup, prompt-or-take-from-config), the `--export-config`
|
|
13
|
-
* (+ `--include-secrets`) round-trip, the composite-setup-action
|
|
14
|
-
* script generators, the `--fake` snapshot mechanism (write to `.fake/` instead of
|
|
13
|
+
* (+ `--include-secrets`) round-trip, the composite-setup-action generators, the `--fake` snapshot mechanism (write to `.fake/` instead of
|
|
15
14
|
* `.github/`), and the capability-emitter REGISTRY seam. It imports NOTHING
|
|
16
15
|
* GitHub-specific: the thin GitHub adapter (`github-ci.ts`) plugs into the
|
|
17
16
|
* {@link CIProviderContext} seam, so a second provider could slot in WITHOUT
|
|
@@ -24,9 +23,18 @@
|
|
|
24
23
|
* policy is the SAME engine gate family resolved via the generated workflow's
|
|
25
24
|
* `DORFL_*` env block, NOT a new config knob minted here.
|
|
26
25
|
*/
|
|
27
|
-
import { readFileSync, writeFileSync, mkdirSync, readdirSync } from 'node:fs';
|
|
26
|
+
import { readFileSync, writeFileSync, mkdirSync, readdirSync, existsSync, } from 'node:fs';
|
|
28
27
|
import { dirname, join, relative } from 'node:path';
|
|
29
28
|
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { ACTION_PINS, pinnedUses, preserveExistingPins, } from './install-ci-action-pins.js';
|
|
30
|
+
/**
|
|
31
|
+
* The refusal a config naming the removed `auth-json` mode gets: it says the
|
|
32
|
+
* mode is gone and names the replacement (`models-json` + a proxy `baseUrl`).
|
|
33
|
+
*/
|
|
34
|
+
export const AUTH_JSON_REMOVED_MESSAGE = 'config file "authMode": "auth-json" is no longer supported (the mode was ' +
|
|
35
|
+
'removed: its OAuth refresh needed a write-capable GH_PAT in the agent job). ' +
|
|
36
|
+
'Use "authMode": "models-json" and point a provider\'s "baseUrl" at a proxy ' +
|
|
37
|
+
'that handles credential rotation outside GitHub.';
|
|
30
38
|
/** Parse a provider's visibility answer (`PUBLIC`, `private`, ...); unknown ⇒ `undefined`. */
|
|
31
39
|
export function parseRepoVisibility(raw) {
|
|
32
40
|
const v = raw.trim().toLowerCase();
|
|
@@ -125,15 +133,20 @@ function pathToFileUrl(absPath) {
|
|
|
125
133
|
// ─── models.json generation ──────────────────────────────────────────────────
|
|
126
134
|
/**
|
|
127
135
|
* Build the harness `models.json` object from the providers. Built-in providers
|
|
128
|
-
* (`anthropic`/`openai`) get `{apiKey:
|
|
129
|
-
* var at runtime); a custom provider carries its `baseUrl`/`api`/`models`/`compat`.
|
|
136
|
+
* (`anthropic`/`openai`) get `{apiKey: "$<ENV_VAR>"}` (the harness resolves the
|
|
137
|
+
* env var at runtime); a custom provider carries its `baseUrl`/`api`/`models`/`compat`.
|
|
130
138
|
* Mirrors whitesmith's `buildModelsJson`.
|
|
139
|
+
*
|
|
140
|
+
* The `apiKey` is an env-var REFERENCE (see {@link modelsJsonEnvRef}), never the
|
|
141
|
+
* bare env-var name: pi treats a bare value as a LITERAL key.
|
|
131
142
|
*/
|
|
132
143
|
export function buildModelsJson(providers) {
|
|
133
144
|
const providersObj = {};
|
|
134
145
|
for (const p of providers) {
|
|
135
146
|
if (p.builtin) {
|
|
136
|
-
const entry = {
|
|
147
|
+
const entry = {
|
|
148
|
+
apiKey: modelsJsonEnvRef(p.apiKeyEnvVar),
|
|
149
|
+
};
|
|
137
150
|
if (p.baseUrl)
|
|
138
151
|
entry.baseUrl = p.baseUrl;
|
|
139
152
|
providersObj[p.name] = entry;
|
|
@@ -142,7 +155,7 @@ export function buildModelsJson(providers) {
|
|
|
142
155
|
const entry = {
|
|
143
156
|
baseUrl: p.baseUrl,
|
|
144
157
|
api: p.api,
|
|
145
|
-
apiKey: p.apiKeyEnvVar,
|
|
158
|
+
apiKey: modelsJsonEnvRef(p.apiKeyEnvVar),
|
|
146
159
|
models: p.models,
|
|
147
160
|
};
|
|
148
161
|
if (p.compat)
|
|
@@ -152,18 +165,25 @@ export function buildModelsJson(providers) {
|
|
|
152
165
|
}
|
|
153
166
|
return { providers: providersObj };
|
|
154
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* The `models.json` config value that makes the pi harness read `envVarName`
|
|
170
|
+
* from the environment: `"$NAME"`. The pinned harness
|
|
171
|
+
* (`@earendil-works/pi-coding-agent`, `dist/core/resolve-config-value.js`)
|
|
172
|
+
* resolves a config value as a shell command when it starts with `!`, as an env
|
|
173
|
+
* var when it is `$NAME` / `${NAME}`, and as a LITERAL otherwise. Writing the bare
|
|
174
|
+
* name therefore sent the string `ANTHROPIC_API_KEY` as the token (`401
|
|
175
|
+
* Unauthorized` on every CI launch).
|
|
176
|
+
*/
|
|
177
|
+
export function modelsJsonEnvRef(envVarName) {
|
|
178
|
+
return `$${envVarName}`;
|
|
179
|
+
}
|
|
155
180
|
// ─── secret-orchestration LOGIC (which secrets, dedup) ───────────────────────
|
|
156
181
|
/**
|
|
157
182
|
* The DEDUPLICATED set of secret env-var names the config requires, in first-seen
|
|
158
|
-
* order
|
|
159
|
-
* for `auth-json` mode it is the fixed `PI_AUTH_JSON` + `GH_PAT` pair (the sharp
|
|
160
|
-
* edge). This is the pure secret-orchestration LOGIC the adapter's setter
|
|
183
|
+
* order: one per distinct provider `apiKeyEnvVar`. This is the pure secret-orchestration LOGIC the adapter's setter
|
|
161
184
|
* consumes — which secrets, deduped — separated from any provider I/O.
|
|
162
185
|
*/
|
|
163
186
|
export function requiredSecretNames(config) {
|
|
164
|
-
if (config.authMode === 'auth-json') {
|
|
165
|
-
return ['PI_AUTH_JSON', 'GH_PAT'];
|
|
166
|
-
}
|
|
167
187
|
const seen = new Set();
|
|
168
188
|
const names = [];
|
|
169
189
|
for (const p of config.providers) {
|
|
@@ -176,13 +196,13 @@ export function requiredSecretNames(config) {
|
|
|
176
196
|
}
|
|
177
197
|
/**
|
|
178
198
|
* The GitHub secret the propose/merge CI legs read as `GH_TOKEN` to open PRs
|
|
179
|
-
* under a CHOSEN identity (a PAT / App token).
|
|
180
|
-
* distinct purpose from `auth-json` mode's `GH_PAT` (which exists only to rotate
|
|
181
|
-
* the OAuth `PI_AUTH_JSON` secret). The generated workflows reference it as
|
|
199
|
+
* under a CHOSEN identity (a PAT / App token). The generated workflows reference it as
|
|
182
200
|
* `${{ secrets.DORFL_GH_TOKEN || secrets.GITHUB_TOKEN }}` so it is
|
|
183
201
|
* OPTIONAL: unset ⇒ the legs fall back to the built-in `GITHUB_TOKEN` (PRs are
|
|
184
|
-
* `github-actions[bot]
|
|
185
|
-
*
|
|
202
|
+
* `github-actions[bot]`, and GitHub holds their `on: pull_request` runs in
|
|
203
|
+
* `action_required` until someone approves them); set ⇒ PRs carry the token's
|
|
204
|
+
* identity and trigger those workflows on their own. `intake.yml` never passes
|
|
205
|
+
* it, so intake PRs always need the approval (`docs/ci/README.md`).
|
|
186
206
|
*/
|
|
187
207
|
export const PR_IDENTITY_SECRET_NAME = 'DORFL_GH_TOKEN';
|
|
188
208
|
/**
|
|
@@ -207,13 +227,13 @@ export function optionalSecretNames(config) {
|
|
|
207
227
|
* entry). Each entry maps the action input (named identically to the secret) to
|
|
208
228
|
* `${{ secrets.<NAME> }}`.
|
|
209
229
|
*
|
|
210
|
-
* Empty string when there are no provider keys (
|
|
230
|
+
* Empty string when there are no provider keys (no providers),
|
|
211
231
|
* so the `uses:` line is emitted bare and unchanged. This is the SINGLE place the
|
|
212
232
|
* provider→`with:` shape lives, shared by every capability workflow generator, so
|
|
213
233
|
* no template re-derives it.
|
|
214
234
|
*/
|
|
215
235
|
export function providerSecretsWithBlock(config) {
|
|
216
|
-
const names =
|
|
236
|
+
const names = providerSecretNames(config);
|
|
217
237
|
if (names.length === 0) {
|
|
218
238
|
return '';
|
|
219
239
|
}
|
|
@@ -222,6 +242,16 @@ export function providerSecretsWithBlock(config) {
|
|
|
222
242
|
.join('\n');
|
|
223
243
|
return `\n with:\n${entries}`;
|
|
224
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* The provider API key secrets the AGENT-running jobs forward to the setup
|
|
247
|
+
* action: one per distinct provider key (the same set as
|
|
248
|
+
* {@link requiredSecretNames}). The split item workflow declares exactly these (plus
|
|
249
|
+
* {@link PR_IDENTITY_SECRET_NAME}) as its `workflow_call` secrets, and hands
|
|
250
|
+
* them to its agent job only.
|
|
251
|
+
*/
|
|
252
|
+
export function providerSecretNames(config) {
|
|
253
|
+
return requiredSecretNames(config);
|
|
254
|
+
}
|
|
225
255
|
/**
|
|
226
256
|
* Orchestrate the required secrets through the provider seam: for each
|
|
227
257
|
* DEDUPLICATED required name, take its value from `knownSecrets` (the
|
|
@@ -289,10 +319,17 @@ export function loadCIConfigFile(filePath) {
|
|
|
289
319
|
throw new CIConfigError(`could not read/parse config file ${filePath}: ` +
|
|
290
320
|
(err instanceof Error ? err.message : String(err)));
|
|
291
321
|
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
322
|
+
// `authMode` is kept as a key (so existing configs still parse) but the only
|
|
323
|
+
// accepted value is `models-json`. The removed `auth-json` mode gets a refusal
|
|
324
|
+
// that names its replacement rather than the generic "must be" message.
|
|
325
|
+
const rawAuthMode = data.authMode;
|
|
326
|
+
if (rawAuthMode === 'auth-json') {
|
|
327
|
+
throw new CIConfigError(AUTH_JSON_REMOVED_MESSAGE);
|
|
295
328
|
}
|
|
329
|
+
if (rawAuthMode !== undefined && rawAuthMode !== 'models-json') {
|
|
330
|
+
throw new CIConfigError(`config file "authMode" must be "models-json"`);
|
|
331
|
+
}
|
|
332
|
+
const authMode = 'models-json';
|
|
296
333
|
if (!data.defaultProvider) {
|
|
297
334
|
throw new CIConfigError('config file must contain "defaultProvider"');
|
|
298
335
|
}
|
|
@@ -313,16 +350,14 @@ export function loadCIConfigFile(filePath) {
|
|
|
313
350
|
'the per-provider payload is OPAQUE to the core');
|
|
314
351
|
}
|
|
315
352
|
}
|
|
316
|
-
if (
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
throw new CIConfigError('config file (models-json) must contain a non-empty "providers" array');
|
|
321
|
-
}
|
|
353
|
+
if (!data.providers ||
|
|
354
|
+
!Array.isArray(data.providers) ||
|
|
355
|
+
data.providers.length === 0) {
|
|
356
|
+
throw new CIConfigError('config file (models-json) must contain a non-empty "providers" array');
|
|
322
357
|
}
|
|
323
358
|
return {
|
|
324
359
|
authMode,
|
|
325
|
-
providers: data.providers
|
|
360
|
+
providers: data.providers,
|
|
326
361
|
defaultProvider: data.defaultProvider,
|
|
327
362
|
defaultModel: data.defaultModel,
|
|
328
363
|
harness: data.harness,
|
|
@@ -372,7 +407,7 @@ export function exportCIConfig(config, secrets) {
|
|
|
372
407
|
}
|
|
373
408
|
return JSON.stringify(file, null, 2) + '\n';
|
|
374
409
|
}
|
|
375
|
-
// ─── composite setup action
|
|
410
|
+
// ─── composite setup action generation ───────────────────────────────────────
|
|
376
411
|
/** Indent every non-blank line of `text` by `spaces` spaces. */
|
|
377
412
|
function indent(text, spaces) {
|
|
378
413
|
const pad = ' '.repeat(spaces);
|
|
@@ -411,6 +446,48 @@ export const PI_HARNESS_PACKAGE = '@earendil-works/pi-coding-agent';
|
|
|
411
446
|
* `node-version: '22'` satisfies (setup-node resolves the latest 22.x).
|
|
412
447
|
*/
|
|
413
448
|
export const PI_HARNESS_VERSION = '0.80.6';
|
|
449
|
+
/**
|
|
450
|
+
* The `pi` packages the harness LOADS besides itself, each pinned to an exact
|
|
451
|
+
* version that is known to load with {@link PI_HARNESS_VERSION}.
|
|
452
|
+
*
|
|
453
|
+
* `pi-coding-agent` declares its sibling packages with a caret range
|
|
454
|
+
* (`@earendil-works/pi-ai: ^0.80.6`), which on a `0.x` version floats within
|
|
455
|
+
* the minor. `pi-ai@0.80.10` removed the `getOAuthApiKey` export that
|
|
456
|
+
* `pi-coding-agent@0.80.6` imports, so a resolver that honours the caret kills
|
|
457
|
+
* `pi` on start. npm happens to be protected by the `npm-shrinkwrap.json` the
|
|
458
|
+
* package publishes; pnpm ignores that file and was broken. Pinning the family
|
|
459
|
+
* explicitly (as npm `overrides`, see {@link piHarnessManifest}) makes the
|
|
460
|
+
* install reproducible whatever the resolver.
|
|
461
|
+
*
|
|
462
|
+
* The set is `pi-coding-agent`'s own `@earendil-works/*` dependencies plus
|
|
463
|
+
* theirs (`npm view @earendil-works/pi-coding-agent@<v> dependencies`,
|
|
464
|
+
* recursively): at 0.80.6 that is pi-ai, pi-agent-core (which depends on pi-ai)
|
|
465
|
+
* and pi-tui. Re-derive it on every {@link PI_HARNESS_VERSION} bump: a newer
|
|
466
|
+
* harness may add a package (0.87 adds `@earendil-works/chord`).
|
|
467
|
+
*/
|
|
468
|
+
export const PI_HARNESS_PINNED_DEPENDENCIES = {
|
|
469
|
+
'@earendil-works/pi-agent-core': PI_HARNESS_VERSION,
|
|
470
|
+
'@earendil-works/pi-ai': PI_HARNESS_VERSION,
|
|
471
|
+
'@earendil-works/pi-tui': PI_HARNESS_VERSION,
|
|
472
|
+
};
|
|
473
|
+
/**
|
|
474
|
+
* The throwaway `package.json` the setup action installs the harness from: the
|
|
475
|
+
* harness at {@link PI_HARNESS_VERSION} as the only dependency, and every pi
|
|
476
|
+
* package it loads forced to its exact pin through npm `overrides`.
|
|
477
|
+
*
|
|
478
|
+
* A project-local install (not `npm install -g`) because `overrides` only apply
|
|
479
|
+
* to the ROOT project, and a global install makes each package its own root. The
|
|
480
|
+
* same file is used in `registry` and `workspace` mode: pnpm 11 also isolates
|
|
481
|
+
* every global package, so listing the pins next to the harness in
|
|
482
|
+
* `pnpm add -g` would not constrain the harness's own tree either.
|
|
483
|
+
*/
|
|
484
|
+
export function piHarnessManifest() {
|
|
485
|
+
return {
|
|
486
|
+
private: true,
|
|
487
|
+
dependencies: { [PI_HARNESS_PACKAGE]: PI_HARNESS_VERSION },
|
|
488
|
+
overrides: { ...PI_HARNESS_PINNED_DEPENDENCIES },
|
|
489
|
+
};
|
|
490
|
+
}
|
|
414
491
|
/**
|
|
415
492
|
* The version of THIS dorfl package, read at runtime from its own
|
|
416
493
|
* `package.json` (the single source changesets bumps on release). The composite
|
|
@@ -442,25 +519,47 @@ export function dorflPackageVersion() {
|
|
|
442
519
|
}
|
|
443
520
|
return version;
|
|
444
521
|
}
|
|
522
|
+
/** Where the setup action installs the harness (under the job's temp dir). */
|
|
523
|
+
const PI_HARNESS_DIR = '$RUNNER_TEMP/dorfl-harness';
|
|
445
524
|
/**
|
|
446
|
-
* The install
|
|
447
|
-
*
|
|
448
|
-
*
|
|
449
|
-
* `$GITHUB_PATH
|
|
525
|
+
* The install + smoke steps for the configured harness (the `pi` CLI; `''` ⇒
|
|
526
|
+
* none). Identical in `registry` and `workspace` mode: the harness is installed
|
|
527
|
+
* with npm from {@link piHarnessManifest} into a job-local directory whose
|
|
528
|
+
* `node_modules/.bin` is added to `$GITHUB_PATH`, so every pi package it loads
|
|
529
|
+
* is pinned whatever package manager the repository uses. The smoke step then
|
|
530
|
+
* runs `pi --version`, which imports the harness's whole module graph, so a
|
|
531
|
+
* broken install fails setup with a clear message instead of failing inside a
|
|
532
|
+
* dorfl agent launch.
|
|
450
533
|
*/
|
|
451
|
-
function harnessInstallStep(harness
|
|
534
|
+
function harnessInstallStep(harness) {
|
|
452
535
|
if (harness === 'pi') {
|
|
453
|
-
const
|
|
454
|
-
const run = installSource === 'workspace'
|
|
455
|
-
? `pnpm add -g ${spec}`
|
|
456
|
-
: `npm install -g ${spec}`;
|
|
536
|
+
const manifest = JSON.stringify(piHarnessManifest(), null, 2);
|
|
457
537
|
return `
|
|
458
538
|
# Pinned to the exact harness version this dorfl release declares
|
|
459
|
-
# (PI_HARNESS_VERSION)
|
|
460
|
-
#
|
|
539
|
+
# (PI_HARNESS_VERSION), with every pi package it loads pinned too (npm
|
|
540
|
+
# \`overrides\`; a caret range on a 0.x version floats within the minor): the
|
|
541
|
+
# job holds a provider key, so no version may move on its own. Re-run
|
|
542
|
+
# \`dorfl install-ci\` to upgrade.
|
|
461
543
|
- name: Install agent harness (pi)
|
|
462
544
|
shell: bash
|
|
463
|
-
run:
|
|
545
|
+
run: |
|
|
546
|
+
mkdir -p "${PI_HARNESS_DIR}"
|
|
547
|
+
cd "${PI_HARNESS_DIR}"
|
|
548
|
+
cat > package.json << 'HARNESS_EOF'
|
|
549
|
+
${indent(manifest, 8)}
|
|
550
|
+
HARNESS_EOF
|
|
551
|
+
npm install --no-audit --no-fund
|
|
552
|
+
echo "${PI_HARNESS_DIR}/node_modules/.bin" >> "$GITHUB_PATH"
|
|
553
|
+
|
|
554
|
+
# \`pi --version\` loads the harness's module graph, so an install whose pi
|
|
555
|
+
# packages do not load together fails HERE, not inside a dorfl agent launch.
|
|
556
|
+
- name: Check the agent harness loads (pi)
|
|
557
|
+
shell: bash
|
|
558
|
+
run: |
|
|
559
|
+
if ! pi --version; then
|
|
560
|
+
echo "::error title=dorfl-setup::The agent harness (pi) is installed but cannot start, so its pi packages do not load together. Re-run dorfl install-ci with a dorfl release that pins a working harness."
|
|
561
|
+
exit 1
|
|
562
|
+
fi`;
|
|
464
563
|
}
|
|
465
564
|
return '';
|
|
466
565
|
}
|
|
@@ -472,14 +571,11 @@ function harnessInstallStep(harness, installSource) {
|
|
|
472
571
|
* `uses: ./.github/actions/dorfl-setup`. The advance-loop seed template
|
|
473
572
|
* already references this exact action name + path (`docs/ci/README.md`).
|
|
474
573
|
*
|
|
475
|
-
* The auth step
|
|
476
|
-
* `~/.pi/agent/models.json` inline (the conservative default) AND exports each
|
|
574
|
+
* The auth step writes a generated `~/.pi/agent/models.json` inline AND exports each
|
|
477
575
|
* configured provider's API key from a same-named ACTION INPUT to `$GITHUB_ENV`,
|
|
478
576
|
* so the agent step (`pi`) can resolve the env var `models.json` references; the
|
|
479
577
|
* workflow passes the secrets ONCE via `with:` on the `uses:` line (no
|
|
480
|
-
* per-step / per-capability provider enumeration).
|
|
481
|
-
* `~/.pi/agent/auth.json` from `$PI_AUTH_JSON` + runs the OAuth-refresh script
|
|
482
|
-
* (the sharp edge). Deterministic: the same config produces byte-identical output.
|
|
578
|
+
* per-step / per-capability provider enumeration). Deterministic: the same config produces byte-identical output.
|
|
483
579
|
*/
|
|
484
580
|
export function generateSetupAction(config,
|
|
485
581
|
/**
|
|
@@ -491,43 +587,24 @@ export function generateSetupAction(config,
|
|
|
491
587
|
* baseline (the load-bearing absent-⇒-identical contract).
|
|
492
588
|
*/
|
|
493
589
|
projectSetupSteps) {
|
|
494
|
-
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
echo "$
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
# CI→repo mutation in the design; needs GH_PAT to rotate PI_AUTH_JSON.
|
|
513
|
-
- name: Refresh OAuth token
|
|
514
|
-
shell: bash
|
|
515
|
-
run: node .github/scripts/refresh-oauth-token.mjs`;
|
|
516
|
-
}
|
|
517
|
-
else {
|
|
518
|
-
const modelsJsonStr = JSON.stringify(buildModelsJson(config.providers), null, 2);
|
|
519
|
-
// Map each provider-key INPUT into the step env, then append the non-empty
|
|
520
|
-
// ones to `$GITHUB_ENV` so the agent step inherits them. Values flow via env
|
|
521
|
-
// (never interpolated into the script body), and Actions masks the secret in
|
|
522
|
-
// logs. `models.json`'s `apiKey` is the env-var NAME; this puts the value there.
|
|
523
|
-
const exportEnvLines = providerKeyNames
|
|
524
|
-
.map((name) => ` ${name}: \${{ inputs.${name} }}`)
|
|
525
|
-
.join('\n');
|
|
526
|
-
const exportRunLines = providerKeyNames
|
|
527
|
-
.map((name) => ` if [ -n "\${${name}}" ]; then echo "${name}=\${${name}}" >> "$GITHUB_ENV"; fi`)
|
|
528
|
-
.join('\n');
|
|
529
|
-
const exportStep = providerKeyNames.length > 0
|
|
530
|
-
? `\n
|
|
590
|
+
// models-json mode (the only mode) declares one ACTION INPUT per distinct
|
|
591
|
+
// provider key (named identically to the secret / env var) and exports it to
|
|
592
|
+
// `$GITHUB_ENV`, so every later step inherits it.
|
|
593
|
+
const providerKeyNames = providerSecretNames(config);
|
|
594
|
+
const modelsJsonStr = JSON.stringify(buildModelsJson(config.providers), null, 2);
|
|
595
|
+
// Map each provider-key INPUT into the step env, then append the non-empty
|
|
596
|
+
// ones to `$GITHUB_ENV` so the agent step inherits them. Values flow via env
|
|
597
|
+
// (never interpolated into the script body), and Actions masks the secret in
|
|
598
|
+
// logs. `models.json`'s `apiKey` is a `$NAME` env-var REFERENCE (pi resolves it at
|
|
599
|
+
// runtime; a bare name would be a literal key); this puts the value there.
|
|
600
|
+
const exportEnvLines = providerKeyNames
|
|
601
|
+
.map((name) => ` ${name}: \${{ inputs.${name} }}`)
|
|
602
|
+
.join('\n');
|
|
603
|
+
const exportRunLines = providerKeyNames
|
|
604
|
+
.map((name) => ` if [ -n "\${${name}}" ]; then echo "${name}=\${${name}}" >> "$GITHUB_ENV"; fi`)
|
|
605
|
+
.join('\n');
|
|
606
|
+
const exportStep = providerKeyNames.length > 0
|
|
607
|
+
? `\n
|
|
531
608
|
# Surface the configured provider API key(s) to the environment so the agent
|
|
532
609
|
# step (\`pi\`) can resolve the env var \`models.json\` references. The workflow
|
|
533
610
|
# passes the secret(s) via \`with:\` on the \`uses:\` line; we forward the
|
|
@@ -538,8 +615,8 @@ projectSetupSteps) {
|
|
|
538
615
|
${exportEnvLines}
|
|
539
616
|
run: |
|
|
540
617
|
${exportRunLines}`
|
|
541
|
-
|
|
542
|
-
|
|
618
|
+
: '';
|
|
619
|
+
const authStep = `\
|
|
543
620
|
- name: Configure agent models (models.json)
|
|
544
621
|
shell: bash
|
|
545
622
|
run: |
|
|
@@ -547,20 +624,19 @@ ${exportRunLines}`
|
|
|
547
624
|
cat > ~/.pi/agent/models.json << 'MODELS_EOF'
|
|
548
625
|
${indent(modelsJsonStr, 8)}
|
|
549
626
|
MODELS_EOF${exportStep}`;
|
|
550
|
-
|
|
551
|
-
const installHarness = harnessInstallStep(config.harness, config.installSource);
|
|
627
|
+
const installHarness = harnessInstallStep(config.harness);
|
|
552
628
|
// The CLI-install block branches on installSource. `registry` installs the
|
|
553
629
|
// published CLI via `npm install -g dorfl` (the default for every
|
|
554
630
|
// consumer repo). `workspace` builds the CLI from the checked-out source and
|
|
555
631
|
// links it onto PATH — for the self-hosting dorfl monorepo, which is
|
|
556
632
|
// not published under that npm name. We add pnpm's global bin to $GITHUB_PATH
|
|
557
|
-
// so the linked `dorfl`
|
|
558
|
-
//
|
|
633
|
+
// so the linked `dorfl` is on PATH in all subsequent steps (the harness adds
|
|
634
|
+
// its own bin dir, the same way in both modes); we always rebuild because source changes per commit.
|
|
559
635
|
let installSteps;
|
|
560
636
|
if (config.installSource === 'workspace') {
|
|
561
637
|
installSteps = `\
|
|
562
638
|
- name: Setup pnpm
|
|
563
|
-
uses:
|
|
639
|
+
uses: ${pinnedUses(ACTION_PINS.pnpmSetup)}
|
|
564
640
|
|
|
565
641
|
- name: Add pnpm global bin to PATH
|
|
566
642
|
shell: bash
|
|
@@ -609,10 +685,9 @@ ${indent(modelsJsonStr, 8)}
|
|
|
609
685
|
// would exec the local bin which then forwards AGAIN — a confusing
|
|
610
686
|
// double-resolution. The install step below leaves `dorfl` on `$PATH` as the
|
|
611
687
|
// bootstrap; the forward does the pinning.
|
|
612
|
-
// One optional ACTION INPUT per provider key
|
|
613
|
-
//
|
|
614
|
-
//
|
|
615
|
-
// step skips an empty value. auth-json mode declares no inputs.
|
|
688
|
+
// One optional ACTION INPUT per provider key, named identically to the
|
|
689
|
+
// secret/env var. Optional + default '' so a workflow that does not pass it is
|
|
690
|
+
// valid; the export step skips an empty value.
|
|
616
691
|
const inputsBlock = providerKeyNames.length > 0
|
|
617
692
|
? `inputs:
|
|
618
693
|
${providerKeyNames
|
|
@@ -648,15 +723,16 @@ runs:
|
|
|
648
723
|
using: composite
|
|
649
724
|
steps:
|
|
650
725
|
${projectSetupBlock} - name: Setup Node.js
|
|
651
|
-
uses:
|
|
726
|
+
uses: ${pinnedUses(ACTION_PINS.setupNode)}
|
|
652
727
|
with:
|
|
653
728
|
node-version: '22'
|
|
654
|
-
# setup-node
|
|
655
|
-
#
|
|
656
|
-
# before that manager
|
|
657
|
-
# locate executable file").
|
|
658
|
-
#
|
|
659
|
-
#
|
|
729
|
+
# setup-node (v5 and later) auto-enables package-manager caching. In v5
|
|
730
|
+
# it probed whatever manager package.json's packageManager named, during
|
|
731
|
+
# its own run and before that manager was on PATH, and crashed ("Unable
|
|
732
|
+
# to locate executable file"). Since v6 it only fires when package.json
|
|
733
|
+
# declares npm, and then fails if there is no npm lockfile. Either way
|
|
734
|
+
# it is the project's cache, not dorfl's: dorfl provisions ONLY its own
|
|
735
|
+
# runtime (toolchain boundary), so disable it explicitly.
|
|
660
736
|
package-manager-cache: false
|
|
661
737
|
|
|
662
738
|
- name: Configure git identity
|
|
@@ -670,109 +746,125 @@ ${installSteps}
|
|
|
670
746
|
${authStep}
|
|
671
747
|
`;
|
|
672
748
|
}
|
|
749
|
+
/** The local path a workflow step `uses:` for the agent-role setup action. */
|
|
750
|
+
export const SETUP_ACTION_USES = './.github/actions/dorfl-setup';
|
|
751
|
+
/** The local path a workflow step `uses:` for the writer-role setup action. */
|
|
752
|
+
export const WRITER_SETUP_ACTION_USES = './.github/actions/dorfl-setup-writer';
|
|
673
753
|
/**
|
|
674
|
-
*
|
|
675
|
-
* `
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
*
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
*
|
|
754
|
+
* Generate the WRITER-ROLE composite setup action (`dorfl-setup-writer`, spec
|
|
755
|
+
* `ci-agent-job-without-write-token` §9, ADR
|
|
756
|
+
* `ci-agent-job-holds-no-write-token`; task `ci-split-generate-workflows`).
|
|
757
|
+
*
|
|
758
|
+
* The jobs that hold a write token and run no agent (the split item
|
|
759
|
+
* workflow's lock and apply jobs, `reap-merged-branches`, `close-job`, and the
|
|
760
|
+
* read-only `enumerate`) install Node and dorfl ONLY: no harness, no provider
|
|
761
|
+
* key, no `models.json`, no project-setup hook, no install of the project's
|
|
762
|
+
* dependencies, and no Actions cache. In `registry` mode the dorfl install runs
|
|
763
|
+
* from `$RUNNER_TEMP`, outside the checkout, with `--ignore-scripts`, so a
|
|
764
|
+
* repository `.npmrc` cannot redirect the registry and no install script runs.
|
|
684
765
|
*
|
|
685
|
-
*
|
|
686
|
-
*
|
|
687
|
-
*
|
|
688
|
-
*
|
|
766
|
+
* In `workspace` mode (dorfl's own repository) dorfl is built from source
|
|
767
|
+
* (decision 8): the tree of `source-ref` (default the checkout's `HEAD`) is
|
|
768
|
+
* exported with `git archive` into `$RUNNER_TEMP` and built there with
|
|
769
|
+
* `--ignore-scripts`. In merge mode that is code an earlier agent landed on
|
|
770
|
+
* `main`, which is the trust merge mode already implies.
|
|
689
771
|
*
|
|
690
|
-
*
|
|
772
|
+
* It is a SIBLING action rather than a `role` input on `dorfl-setup`: the
|
|
773
|
+
* project-setup hook is an opaque fragment spliced first into `dorfl-setup`
|
|
774
|
+
* (ADR `install-ci-project-provisioning-native-passthrough`), and a composite
|
|
775
|
+
* action cannot skip an opaque block of steps by input. `dorfl-setup` stays the
|
|
776
|
+
* agent role, byte for byte.
|
|
691
777
|
*/
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
const cred = auth.anthropic;
|
|
702
|
-
|
|
703
|
-
if (!cred || cred.type !== "oauth") {
|
|
704
|
-
console.log("No OAuth credentials for anthropic, skipping refresh");
|
|
705
|
-
process.exit(0);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (Date.now() < cred.expires) {
|
|
709
|
-
console.log("Token still valid until", new Date(cred.expires).toISOString());
|
|
710
|
-
process.exit(0);
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
console.log(
|
|
714
|
-
"Token expired at",
|
|
715
|
-
new Date(cred.expires).toISOString(),
|
|
716
|
-
"- refreshing..."
|
|
717
|
-
);
|
|
778
|
+
export function generateWriterSetupAction(config) {
|
|
779
|
+
let installSteps;
|
|
780
|
+
let inputsBlock = '';
|
|
781
|
+
if (config.installSource === 'workspace') {
|
|
782
|
+
inputsBlock = `inputs:
|
|
783
|
+
source-ref:
|
|
784
|
+
description: 'The commit to build dorfl from (workspace mode). Blank: the checkout HEAD.'
|
|
785
|
+
required: false
|
|
786
|
+
default: ''
|
|
718
787
|
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
Accept: "application/json",
|
|
724
|
-
},
|
|
725
|
-
body: new URLSearchParams({
|
|
726
|
-
grant_type: "refresh_token",
|
|
727
|
-
client_id: ANTHROPIC_CLIENT_ID,
|
|
728
|
-
refresh_token: cred.refresh,
|
|
729
|
-
}).toString(),
|
|
730
|
-
signal: AbortSignal.timeout(30_000),
|
|
731
|
-
});
|
|
788
|
+
`;
|
|
789
|
+
installSteps = `\
|
|
790
|
+
- name: Setup pnpm
|
|
791
|
+
uses: ${pinnedUses(ACTION_PINS.pnpmSetup)}
|
|
732
792
|
|
|
733
|
-
|
|
793
|
+
- name: Add pnpm global bin to PATH
|
|
794
|
+
shell: bash
|
|
795
|
+
run: |
|
|
796
|
+
pnpm setup
|
|
797
|
+
echo "$HOME/.local/share/pnpm" >> "$GITHUB_PATH"
|
|
734
798
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
799
|
+
# Workspace mode (dorfl's own repository, decision 8 of ADR
|
|
800
|
+
# ci-agent-job-holds-no-write-token): build dorfl from the tree of
|
|
801
|
+
# \`source-ref\`, exported into $RUNNER_TEMP (outside the checkout), with
|
|
802
|
+
# \`--ignore-scripts\`. In merge mode this is code an earlier agent landed on
|
|
803
|
+
# main running in a write job, the trust merge mode already implies.
|
|
804
|
+
# Only dorfl and its workspace dependencies (\`--filter 'dorfl...'\`) are
|
|
805
|
+
# installed and built: the other workspace packages (the website) are not
|
|
806
|
+
# needed to run dorfl, so they do not run next to the write token.
|
|
807
|
+
- name: Build dorfl from the base commit (outside the checkout)
|
|
808
|
+
shell: bash
|
|
809
|
+
env:
|
|
810
|
+
SOURCE_REF: \${{ inputs.source-ref }}
|
|
811
|
+
run: |
|
|
812
|
+
set -euo pipefail
|
|
813
|
+
src="$RUNNER_TEMP/dorfl-src"
|
|
814
|
+
rm -rf "$src"
|
|
815
|
+
mkdir -p "$src"
|
|
816
|
+
git archive "$(git rev-parse --verify "\${SOURCE_REF:-HEAD}^{commit}")" | tar -x -C "$src"
|
|
817
|
+
cd "$src"
|
|
818
|
+
pnpm install --frozen-lockfile --ignore-scripts --filter 'dorfl...'
|
|
819
|
+
pnpm --filter 'dorfl...' build
|
|
820
|
+
cd packages/dorfl && pnpm link --global`;
|
|
821
|
+
}
|
|
822
|
+
else {
|
|
823
|
+
installSteps = `\
|
|
824
|
+
# Pinned to the dorfl version that generated this action. Installed from
|
|
825
|
+
# $RUNNER_TEMP (outside the checkout, so a repository .npmrc cannot redirect
|
|
826
|
+
# the registry) with --ignore-scripts (no install script runs next to the
|
|
827
|
+
# write token).
|
|
828
|
+
- name: Install dorfl (outside the checkout, no install scripts)
|
|
829
|
+
shell: bash
|
|
830
|
+
run: |
|
|
831
|
+
cd "$RUNNER_TEMP"
|
|
832
|
+
npm install -g --ignore-scripts dorfl@${dorflPackageVersion()}`;
|
|
833
|
+
}
|
|
834
|
+
return `\
|
|
835
|
+
name: Setup dorfl (writer role)
|
|
836
|
+
description: Install Node.js and dorfl only, for the CI jobs that run no agent (lock, apply, reap, close, enumerate)
|
|
739
837
|
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
838
|
+
${inputsBlock}# THE WRITER ROLE (ADR ci-agent-job-holds-no-write-token): the jobs that use
|
|
839
|
+
# this action hold a write token (or run next to one) and run NO agent and NO
|
|
840
|
+
# repository code. So it installs Node.js and dorfl ONLY: no agent harness, no
|
|
841
|
+
# provider API key, no models.json, no project-setup hook, no install of the
|
|
842
|
+
# project's dependencies, and no Actions cache (an agent can write
|
|
843
|
+
# default-branch cache entries, so a restore here would run its bytes with
|
|
844
|
+
# write access). The agent role is ./.github/actions/dorfl-setup.
|
|
845
|
+
runs:
|
|
846
|
+
using: composite
|
|
847
|
+
steps:
|
|
848
|
+
- name: Setup Node.js
|
|
849
|
+
uses: ${pinnedUses(ACTION_PINS.setupNode)}
|
|
850
|
+
with:
|
|
851
|
+
node-version: '22'
|
|
852
|
+
# No cache in the writer role (see above).
|
|
853
|
+
package-manager-cache: false
|
|
746
854
|
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
);
|
|
855
|
+
- name: Configure git identity
|
|
856
|
+
shell: bash
|
|
857
|
+
run: |
|
|
858
|
+
git config user.name "dorfl[bot]"
|
|
859
|
+
git config user.email "dorfl[bot]@users.noreply.github.com"
|
|
753
860
|
|
|
754
|
-
|
|
755
|
-
const repo = process.env.GITHUB_REPOSITORY;
|
|
756
|
-
const token = process.env.GH_PAT;
|
|
757
|
-
if (repo && token) {
|
|
758
|
-
try {
|
|
759
|
-
execSync(\`gh secret set PI_AUTH_JSON --repo "\${repo}"\`, {
|
|
760
|
-
input: JSON.stringify(auth),
|
|
761
|
-
env: { ...process.env, GH_TOKEN: token },
|
|
762
|
-
stdio: ["pipe", "pipe", "pipe"],
|
|
763
|
-
});
|
|
764
|
-
console.log("PI_AUTH_JSON secret updated");
|
|
765
|
-
} catch (err) {
|
|
766
|
-
console.warn("Failed to update secret (non-fatal):", err.stderr?.toString() || err.message);
|
|
767
|
-
}
|
|
768
|
-
} else {
|
|
769
|
-
console.log("Skipping secret update (no GH_PAT or GITHUB_REPOSITORY)");
|
|
770
|
-
}
|
|
861
|
+
${installSteps}
|
|
771
862
|
`;
|
|
863
|
+
}
|
|
772
864
|
// ─── the --fake snapshot mechanism + artifact assembly ───────────────────────
|
|
773
865
|
/**
|
|
774
|
-
* The setup ARTIFACTS this task generates: the composite setup
|
|
775
|
-
*
|
|
866
|
+
* The setup ARTIFACTS this task generates: the composite setup actions, plus
|
|
867
|
+
* any selected capabilities'
|
|
776
868
|
* workflow files. This task emits NO capability workflow itself (those are the
|
|
777
869
|
* sibling tasks); `capabilities` defaults to none.
|
|
778
870
|
*/
|
|
@@ -789,13 +881,11 @@ options) {
|
|
|
789
881
|
path: join('actions', 'dorfl-setup', 'action.yml'),
|
|
790
882
|
content: generateSetupAction(config, options?.projectSetupSteps),
|
|
791
883
|
},
|
|
884
|
+
{
|
|
885
|
+
path: join('actions', 'dorfl-setup-writer', 'action.yml'),
|
|
886
|
+
content: generateWriterSetupAction(config),
|
|
887
|
+
},
|
|
792
888
|
];
|
|
793
|
-
if (config.authMode === 'auth-json') {
|
|
794
|
-
files.push({
|
|
795
|
-
path: join('scripts', 'refresh-oauth-token.mjs'),
|
|
796
|
-
content: REFRESH_OAUTH_SCRIPT,
|
|
797
|
-
});
|
|
798
|
-
}
|
|
799
889
|
for (const cap of capabilities) {
|
|
800
890
|
files.push(...cap.emit(config));
|
|
801
891
|
}
|
|
@@ -814,14 +904,28 @@ export function outputBaseName(fake) {
|
|
|
814
904
|
* Write the artifacts under `<workDir>/<base>/` (base = `.fake` when `fake`, else
|
|
815
905
|
* `.github`). Returns the repo-relative paths actually written. Pure filesystem
|
|
816
906
|
* I/O — no secrets, no git. Creates parent dirs as needed.
|
|
907
|
+
*
|
|
908
|
+
* PIN PRESERVATION. Before writing a YAML artifact, any `uses:` of an action the
|
|
909
|
+
* repository's EXISTING `.github/<path>` already pins to a full commit SHA keeps
|
|
910
|
+
* that reference and its trailing comment (see {@link preserveExistingPins}),
|
|
911
|
+
* so a Dependabot bump survives regeneration and a pin is never swapped for a
|
|
912
|
+
* tag. The existing file is always read from `.github/`, also in `--fake` mode,
|
|
913
|
+
* so the `.fake/` preview shows what a real run would write.
|
|
817
914
|
*/
|
|
818
915
|
export function writeArtifacts(options) {
|
|
819
916
|
const base = outputBaseName(options.fake);
|
|
820
917
|
const written = [];
|
|
821
918
|
for (const file of options.files) {
|
|
822
919
|
const abs = join(options.workDir, base, file.path);
|
|
920
|
+
let content = file.content;
|
|
921
|
+
if (/\.ya?ml$/.test(file.path)) {
|
|
922
|
+
const current = join(options.workDir, outputBaseName(false), file.path);
|
|
923
|
+
if (existsSync(current)) {
|
|
924
|
+
content = preserveExistingPins(content, readFileSync(current, 'utf8'));
|
|
925
|
+
}
|
|
926
|
+
}
|
|
823
927
|
mkdirSync(dirname(abs), { recursive: true });
|
|
824
|
-
writeFileSync(abs,
|
|
928
|
+
writeFileSync(abs, content, 'utf8');
|
|
825
929
|
written.push(relative(options.workDir, abs));
|
|
826
930
|
}
|
|
827
931
|
return written;
|