@deftai/directive-runtime 0.127.0 → 0.128.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/.githooks/_directive-run.sh +30 -0
- package/.githooks/pre-commit +4 -24
- package/.githooks/pre-push +11 -17
- package/Taskfile.yml +16 -15
- package/contracts/design-critique.md +5 -0
- package/dist/cli-router/route-argv.js +1 -0
- package/dist/coverage-hotspots.js +2 -2
- package/dist/dispatch.d.ts +1 -1
- package/dist/dispatch.js +7 -0
- package/dist/policy.d.ts +4 -2
- package/dist/policy.js +101 -2
- package/dist/pre-pr-run.d.ts +2 -0
- package/dist/pre-pr-run.js +40 -0
- package/dist/render-cli/capability-map-cli.d.ts +3 -0
- package/dist/render-cli/capability-map-cli.js +6 -0
- package/dist/setup-wiring.d.ts +2 -0
- package/dist/setup-wiring.js +14 -0
- package/docs/getting-started.md +2 -0
- package/docs/known-limitations.md +1 -0
- package/docs/launcher.md +10 -0
- package/package.json +1 -1
- package/packs/skills/skills-pack-0.1.json +1 -1
- package/skills/deft-directive-pre-pr/SKILL.md +13 -3
- package/tasks/agent.yml +0 -2
- package/tasks/cache.yml +0 -16
- package/tasks/capacity.yml +0 -4
- package/tasks/change.yml +0 -4
- package/tasks/codebase.yml +0 -10
- package/tasks/commit.yml +0 -2
- package/tasks/coverage.yml +0 -2
- package/tasks/decision.yml +0 -4
- package/tasks/directive.yml +0 -1
- package/tasks/docs.yml +6 -10
- package/tasks/engine-invoke.cjs +79 -0
- package/tasks/engine-invoke.test.cjs +84 -0
- package/tasks/engine.yml +27 -88
- package/tasks/eval.yml +0 -8
- package/tasks/feedback.yml +0 -2
- package/tasks/install.yml +0 -4
- package/tasks/issue.yml +0 -3
- package/tasks/lifecycle.yml +0 -1
- package/tasks/materialize.cjs +246 -0
- package/tasks/materialize.test.cjs +103 -0
- package/tasks/materialize.yml +13 -0
- package/tasks/migrate.yml +0 -6
- package/tasks/packs.yml +0 -16
- package/tasks/plan-sequence.yml +0 -8
- package/tasks/policy.yml +0 -24
- package/tasks/pr.yml +0 -6
- package/tasks/prd.yml +0 -2
- package/tasks/product-signal.yml +0 -10
- package/tasks/project.yml +0 -8
- package/tasks/reconcile.yml +0 -2
- package/tasks/review-monitor.yml +0 -4
- package/tasks/roadmap.yml +0 -4
- package/tasks/scm.yml +0 -32
- package/tasks/scope.yml +0 -30
- package/tasks/slice.yml +0 -4
- package/tasks/spec.yml +0 -6
- package/tasks/subagent.yml +0 -4
- package/tasks/swarm.yml +0 -7
- package/tasks/toolchain.yml +0 -4
- package/tasks/triage-actions.yml +0 -8
- package/tasks/triage-bootstrap.yml +0 -1
- package/tasks/triage-bulk.yml +0 -5
- package/tasks/triage-classify.yml +0 -2
- package/tasks/triage-evaluate.yml +0 -1
- package/tasks/triage-metrics.yml +0 -2
- package/tasks/triage-queue.yml +0 -3
- package/tasks/triage-reconcile.yml +0 -1
- package/tasks/triage-scope-drift.yml +0 -1
- package/tasks/triage-scope.yml +0 -1
- package/tasks/triage-smoketest.yml +0 -1
- package/tasks/triage-subscribe.yml +0 -2
- package/tasks/tutorial.yml +0 -20
- package/tasks/umbrella.yml +0 -2
- package/tasks/value.yml +0 -2
- package/tasks/vbrief.yml +0 -12
- package/tasks/verify.yml +0 -140
- package/tasks/xbrief.yml +0 -6
- package/templates/design-critique-brief.md +3 -0
- package/vendor/directive-content/.githooks/_directive-run.sh +30 -0
- package/vendor/directive-content/.githooks/pre-commit +4 -24
- package/vendor/directive-content/.githooks/pre-push +11 -17
- package/vendor/directive-content/Taskfile.yml +16 -15
- package/vendor/directive-content/contracts/design-critique.md +5 -0
- package/vendor/directive-content/docs/getting-started.md +2 -0
- package/vendor/directive-content/docs/known-limitations.md +1 -0
- package/vendor/directive-content/docs/launcher.md +10 -0
- package/vendor/directive-content/packs/skills/skills-pack-0.1.json +1 -1
- package/vendor/directive-content/skills/deft-directive-pre-pr/SKILL.md +13 -3
- package/vendor/directive-content/tasks/agent.yml +0 -2
- package/vendor/directive-content/tasks/cache.yml +0 -16
- package/vendor/directive-content/tasks/capacity.yml +0 -4
- package/vendor/directive-content/tasks/change.yml +0 -4
- package/vendor/directive-content/tasks/codebase.yml +0 -10
- package/vendor/directive-content/tasks/commit.yml +0 -2
- package/vendor/directive-content/tasks/coverage.yml +0 -2
- package/vendor/directive-content/tasks/decision.yml +0 -4
- package/vendor/directive-content/tasks/directive.yml +0 -1
- package/vendor/directive-content/tasks/docs.yml +6 -10
- package/vendor/directive-content/tasks/engine-invoke.cjs +79 -0
- package/vendor/directive-content/tasks/engine-invoke.test.cjs +84 -0
- package/vendor/directive-content/tasks/engine.yml +27 -88
- package/vendor/directive-content/tasks/eval.yml +0 -8
- package/vendor/directive-content/tasks/feedback.yml +0 -2
- package/vendor/directive-content/tasks/install.yml +0 -4
- package/vendor/directive-content/tasks/issue.yml +0 -3
- package/vendor/directive-content/tasks/lifecycle.yml +0 -1
- package/vendor/directive-content/tasks/materialize.cjs +246 -0
- package/vendor/directive-content/tasks/materialize.test.cjs +103 -0
- package/vendor/directive-content/tasks/materialize.yml +13 -0
- package/vendor/directive-content/tasks/migrate.yml +0 -6
- package/vendor/directive-content/tasks/packs.yml +0 -16
- package/vendor/directive-content/tasks/plan-sequence.yml +0 -8
- package/vendor/directive-content/tasks/policy.yml +0 -24
- package/vendor/directive-content/tasks/pr.yml +0 -6
- package/vendor/directive-content/tasks/prd.yml +0 -2
- package/vendor/directive-content/tasks/product-signal.yml +0 -10
- package/vendor/directive-content/tasks/project.yml +0 -8
- package/vendor/directive-content/tasks/reconcile.yml +0 -2
- package/vendor/directive-content/tasks/review-monitor.yml +0 -4
- package/vendor/directive-content/tasks/roadmap.yml +0 -4
- package/vendor/directive-content/tasks/scm.yml +0 -32
- package/vendor/directive-content/tasks/scope.yml +0 -30
- package/vendor/directive-content/tasks/slice.yml +0 -4
- package/vendor/directive-content/tasks/spec.yml +0 -6
- package/vendor/directive-content/tasks/subagent.yml +0 -4
- package/vendor/directive-content/tasks/swarm.yml +0 -7
- package/vendor/directive-content/tasks/toolchain.yml +0 -4
- package/vendor/directive-content/tasks/triage-actions.yml +0 -8
- package/vendor/directive-content/tasks/triage-bootstrap.yml +0 -1
- package/vendor/directive-content/tasks/triage-bulk.yml +0 -5
- package/vendor/directive-content/tasks/triage-classify.yml +0 -2
- package/vendor/directive-content/tasks/triage-evaluate.yml +0 -1
- package/vendor/directive-content/tasks/triage-metrics.yml +0 -2
- package/vendor/directive-content/tasks/triage-queue.yml +0 -3
- package/vendor/directive-content/tasks/triage-reconcile.yml +0 -1
- package/vendor/directive-content/tasks/triage-scope-drift.yml +0 -1
- package/vendor/directive-content/tasks/triage-scope.yml +0 -1
- package/vendor/directive-content/tasks/triage-smoketest.yml +0 -1
- package/vendor/directive-content/tasks/triage-subscribe.yml +0 -2
- package/vendor/directive-content/tasks/tutorial.yml +0 -20
- package/vendor/directive-content/tasks/umbrella.yml +0 -2
- package/vendor/directive-content/tasks/value.yml +0 -2
- package/vendor/directive-content/tasks/vbrief.yml +0 -12
- package/vendor/directive-content/tasks/verify.yml +0 -140
- package/vendor/directive-content/tasks/xbrief.yml +0 -6
- package/vendor/directive-content/templates/design-critique-brief.md +3 -0
- package/vendor/directive-core/dist/.tsbuildinfo +1 -1
- package/vendor/directive-core/dist/content-root.d.ts.map +1 -1
- package/vendor/directive-core/dist/content-root.js +8 -1
- package/vendor/directive-core/dist/content-root.js.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts +13 -5
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js +60 -2
- package/vendor/directive-core/dist/coverage-hotspots/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/doctor/checks.d.ts.map +1 -1
- package/vendor/directive-core/dist/doctor/checks.js +5 -3
- package/vendor/directive-core/dist/doctor/checks.js.map +1 -1
- package/vendor/directive-core/dist/index.d.ts +1 -0
- package/vendor/directive-core/dist/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/index.js +1 -0
- package/vendor/directive-core/dist/index.js.map +1 -1
- package/vendor/directive-core/dist/init-deposit/scaffold.d.ts +1 -1
- package/vendor/directive-core/dist/init-deposit/scaffold.d.ts.map +1 -1
- package/vendor/directive-core/dist/init-deposit/scaffold.js +1 -1
- package/vendor/directive-core/dist/init-deposit/scaffold.js.map +1 -1
- package/vendor/directive-core/dist/policy/coverage-mode.d.ts +84 -0
- package/vendor/directive-core/dist/policy/coverage-mode.d.ts.map +1 -0
- package/vendor/directive-core/dist/policy/coverage-mode.js +226 -0
- package/vendor/directive-core/dist/policy/coverage-mode.js.map +1 -0
- package/vendor/directive-core/dist/policy/index.d.ts +2 -0
- package/vendor/directive-core/dist/policy/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/policy/index.js +60 -40
- package/vendor/directive-core/dist/policy/index.js.map +1 -1
- package/vendor/directive-core/dist/pr-closing-keywords/main.d.ts.map +1 -1
- package/vendor/directive-core/dist/pr-closing-keywords/main.js +137 -7
- package/vendor/directive-core/dist/pr-closing-keywords/main.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.d.ts +2 -0
- package/vendor/directive-core/dist/pre-pr-controller/controller.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/controller.js +109 -14
- package/vendor/directive-core/dist/pre-pr-controller/controller.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/evaluate.d.ts +2 -0
- package/vendor/directive-core/dist/pre-pr-controller/evaluate.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/evaluate.js +11 -1
- package/vendor/directive-core/dist/pre-pr-controller/evaluate.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.d.ts +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.js +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/index.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/phases.d.ts +10 -0
- package/vendor/directive-core/dist/pre-pr-controller/phases.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/phases.js +15 -1
- package/vendor/directive-core/dist/pre-pr-controller/phases.js.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/types.d.ts +20 -1
- package/vendor/directive-core/dist/pre-pr-controller/types.d.ts.map +1 -1
- package/vendor/directive-core/dist/pre-pr-controller/types.js +2 -0
- package/vendor/directive-core/dist/pre-pr-controller/types.js.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts +40 -3
- package/vendor/directive-core/dist/preflight-gh/pre-push.d.ts.map +1 -1
- package/vendor/directive-core/dist/preflight-gh/pre-push.js +340 -27
- package/vendor/directive-core/dist/preflight-gh/pre-push.js.map +1 -1
- package/vendor/directive-core/dist/render/project-render.d.ts.map +1 -1
- package/vendor/directive-core/dist/render/project-render.js +1 -0
- package/vendor/directive-core/dist/render/project-render.js.map +1 -1
- package/vendor/directive-core/dist/scope/file-scope-coverage-warn.d.ts +21 -0
- package/vendor/directive-core/dist/scope/file-scope-coverage-warn.d.ts.map +1 -0
- package/vendor/directive-core/dist/scope/file-scope-coverage-warn.js +95 -0
- package/vendor/directive-core/dist/scope/file-scope-coverage-warn.js.map +1 -0
- package/vendor/directive-core/dist/swarm/launch-cli.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/launch-cli.js +12 -0
- package/vendor/directive-core/dist/swarm/launch-cli.js.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.d.ts +6 -0
- package/vendor/directive-core/dist/swarm/launch.d.ts.map +1 -1
- package/vendor/directive-core/dist/swarm/launch.js +11 -0
- package/vendor/directive-core/dist/swarm/launch.js.map +1 -1
- package/vendor/directive-core/dist/swarm/leftover-before-activation.d.ts +27 -0
- package/vendor/directive-core/dist/swarm/leftover-before-activation.d.ts.map +1 -0
- package/vendor/directive-core/dist/swarm/leftover-before-activation.js +31 -0
- package/vendor/directive-core/dist/swarm/leftover-before-activation.js.map +1 -0
- package/vendor/directive-core/dist/verify-env/verify-hooks-installed.d.ts +3 -3
- package/vendor/directive-core/dist/verify-env/verify-hooks-installed.d.ts.map +1 -1
- package/vendor/directive-core/dist/verify-env/verify-hooks-installed.js +7 -11
- package/vendor/directive-core/dist/verify-env/verify-hooks-installed.js.map +1 -1
- package/vendor/directive-core/dist/verify-source/project-definition-mutation-boundary.d.ts.map +1 -1
- package/vendor/directive-core/dist/verify-source/project-definition-mutation-boundary.js +1 -0
- package/vendor/directive-core/dist/verify-source/project-definition-mutation-boundary.js.map +1 -1
- package/.githooks/_deft-run.sh +0 -47
- package/vendor/directive-content/.githooks/_deft-run.sh +0 -47
|
@@ -533,3 +533,87 @@ describe("buildSpawnPlan — CodeQL absolute-path isolation (#3175 / alert #74)"
|
|
|
533
533
|
assert.deepEqual(plan.args, [String.raw`C:\repo\packages\cli\dist\bin.js`, "session:start"]);
|
|
534
534
|
});
|
|
535
535
|
});
|
|
536
|
+
|
|
537
|
+
describe("framework pin exclusive invoke (#5805)", () => {
|
|
538
|
+
const {
|
|
539
|
+
resolveFrameworkPinBin,
|
|
540
|
+
hasFrameworkPinBin,
|
|
541
|
+
resolveFrameworkSourceInvoke,
|
|
542
|
+
PIN_INSTALL_REMEDIATION,
|
|
543
|
+
PIN_MATERIALIZE_REMEDIATION,
|
|
544
|
+
} = require("./engine-invoke.cjs");
|
|
545
|
+
const pinRoots = [];
|
|
546
|
+
function pinTempRoot() {
|
|
547
|
+
const root = mkdtempSync(join(tmpdir(), "pin-inv-"));
|
|
548
|
+
pinRoots.push(root);
|
|
549
|
+
return root;
|
|
550
|
+
}
|
|
551
|
+
after(() => {
|
|
552
|
+
for (const root of pinRoots) {
|
|
553
|
+
try {
|
|
554
|
+
rmSync(root, { recursive: true, force: true });
|
|
555
|
+
} catch {
|
|
556
|
+
/* ignore */
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
it("resolveFrameworkPinBin joins checkout pin path", () => {
|
|
562
|
+
const pin = resolveFrameworkPinBin(join("C:", "repo"));
|
|
563
|
+
assert.match(pin.replace(/\\/g, "/"), /node_modules\/@deftai\/directive-runtime\/dist\/bin\.js$/);
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it("hasFrameworkPinBin is false when missing", () => {
|
|
567
|
+
const root = pinTempRoot();
|
|
568
|
+
assert.equal(hasFrameworkPinBin(root), false);
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it("hasFrameworkPinBin is true when pin bin exists", () => {
|
|
572
|
+
const root = pinTempRoot();
|
|
573
|
+
const pin = resolveFrameworkPinBin(root);
|
|
574
|
+
mkdirSync(require("node:path").dirname(pin), { recursive: true });
|
|
575
|
+
writeFileSync(pin, "module.exports={}\n");
|
|
576
|
+
assert.equal(hasFrameworkPinBin(root), true);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("framework-source with pin selects vendored pin (not tip dist)", () => {
|
|
580
|
+
const plan = resolveFrameworkSourceInvoke({
|
|
581
|
+
hasBin: true,
|
|
582
|
+
isBuildableSource: true,
|
|
583
|
+
hasPinBin: true,
|
|
584
|
+
});
|
|
585
|
+
assert.deepEqual(plan, { action: "vendored", binKind: "pin" });
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
it("framework-source missing pin fails closed even when tip dist / global exist", () => {
|
|
589
|
+
const plan = resolveFrameworkSourceInvoke({
|
|
590
|
+
hasBin: true,
|
|
591
|
+
isBuildableSource: true,
|
|
592
|
+
hasPinBin: false,
|
|
593
|
+
hasGlobalCli: true,
|
|
594
|
+
isRuntimeVerb: true,
|
|
595
|
+
});
|
|
596
|
+
assert.equal(plan.action, "fail-closed");
|
|
597
|
+
assert.equal(plan.exitCode, 2);
|
|
598
|
+
assert.deepEqual(plan.remediations, [PIN_INSTALL_REMEDIATION, PIN_MATERIALIZE_REMEDIATION]);
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
it("deposit path keeps prior resolveInvokeDispatch behavior", () => {
|
|
602
|
+
const plan = resolveFrameworkSourceInvoke({
|
|
603
|
+
hasBin: false,
|
|
604
|
+
isBuildableSource: false,
|
|
605
|
+
hasPinBin: false,
|
|
606
|
+
hasGlobalCli: true,
|
|
607
|
+
isRuntimeVerb: false,
|
|
608
|
+
});
|
|
609
|
+
assert.deepEqual(plan, { action: "global" });
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
it("pin-bin CLI exits 2 when missing", () => {
|
|
613
|
+
const root = pinTempRoot();
|
|
614
|
+
const script = join(__dirname, "engine-invoke.cjs");
|
|
615
|
+
const r = spawnSync(process.execPath, [script, "pin-bin", root], { encoding: "utf8" });
|
|
616
|
+
assert.equal(r.status, 2);
|
|
617
|
+
assert.match(r.stderr, /missing checkout pin/);
|
|
618
|
+
});
|
|
619
|
+
});
|
package/tasks/engine.yml
CHANGED
|
@@ -2,11 +2,13 @@ version: '3'
|
|
|
2
2
|
|
|
3
3
|
# Consumer engine resolution (#2022 Phase 3).
|
|
4
4
|
#
|
|
5
|
-
# Framework-source checkouts invoke the
|
|
5
|
+
# Framework-source checkouts invoke the checkout pin
|
|
6
|
+
# `node_modules/@deftai/directive-runtime/dist/bin.js` exclusively (#5805 / AC9.9).
|
|
7
|
+
# Tip dogfood without publish is `task materialize:runtime`, not packages/runtime/dist.
|
|
6
8
|
# npm consumer deposits copy @deftai/directive-content only — no bundled CLI —
|
|
7
|
-
# so tasks fall back to
|
|
9
|
+
# so tasks fall back to OVPR DEFT_ROOT/dist/bin.js or global `deft` / `directive`.
|
|
8
10
|
# A deposit marker (package.json deftConsumerDeposit / .deft-consumer-deposit /
|
|
9
|
-
# @deftai/directive-content name) forces that
|
|
11
|
+
# @deftai/directive-content name) forces that deposit path for every verb (#3324).
|
|
10
12
|
|
|
11
13
|
vars:
|
|
12
14
|
DEFT_ROOT: '{{joinPath .TASKFILE_DIR ".."}}'
|
|
@@ -59,7 +61,7 @@ tasks:
|
|
|
59
61
|
|
|
60
62
|
invoke:
|
|
61
63
|
internal: true
|
|
62
|
-
desc: "Run a deft-ts verb from
|
|
64
|
+
desc: "Run a deft-ts verb from the checkout pin (framework-source #5805) or OVPR/global deposit path (#2409 / #3324). Ordinary framework-source verbs never fall through to packages/runtime/dist."
|
|
63
65
|
# Run from the operator project root so deft verbs resolve USER_WORKING_DIR
|
|
64
66
|
# correctly; without this, included engine.yml defaults cwd to tasks/ (#2022).
|
|
65
67
|
dir: '{{.USER_WORKING_DIR}}'
|
|
@@ -71,23 +73,18 @@ tasks:
|
|
|
71
73
|
cmds:
|
|
72
74
|
- |
|
|
73
75
|
set -eu
|
|
74
|
-
#
|
|
75
|
-
# Framework source / legacy .deft/core: DEFT_ROOT/packages/runtime/dist/bin.js.
|
|
76
|
-
bin=""
|
|
77
|
-
if [ -f "{{.DEFT_ROOT}}/dist/bin.js" ]; then
|
|
78
|
-
bin="{{.DEFT_ROOT}}/dist/bin.js"
|
|
79
|
-
elif [ -f "{{.DEFT_ROOT}}/packages/runtime/dist/bin.js" ]; then
|
|
80
|
-
bin="{{.DEFT_ROOT}}/packages/runtime/dist/bin.js"
|
|
81
|
-
fi
|
|
82
|
-
root_pkg="{{.DEFT_ROOT}}/package.json"
|
|
83
|
-
# #3324: deposit marker (package.json field / file / content-package
|
|
84
|
-
# name) forces is_buildable_source=0 so every verb uses global CLI.
|
|
76
|
+
# #3324: deposit marker forces is_buildable_source=0 (consumer / OVPR path).
|
|
85
77
|
is_buildable_source=0
|
|
86
78
|
if node "{{.TASKFILE_DIR}}/engine-invoke.cjs" is-buildable-source "{{.DEFT_ROOT}}"; then
|
|
87
79
|
is_buildable_source=1
|
|
88
80
|
fi
|
|
89
|
-
if [
|
|
90
|
-
|
|
81
|
+
if [ "$is_buildable_source" = 1 ]; then
|
|
82
|
+
# #5805 Prefer-A Bound: framework-source ordinary invoke is pin-exclusive.
|
|
83
|
+
# No packages/runtime/dist fallthrough; missing pin fails closed.
|
|
84
|
+
# Resolve pin from DEFT_ROOT so subdirectory cwd still finds the checkout pin (#5805).
|
|
85
|
+
pin_bin=$(node "{{.TASKFILE_DIR}}/engine-invoke.cjs" pin-bin "{{.DEFT_ROOT}}" ) || exit $?
|
|
86
|
+
root_pkg="{{.DEFT_ROOT}}/package.json"
|
|
87
|
+
if [ -f "$root_pkg" ]; then
|
|
91
88
|
node -e "
|
|
92
89
|
const fs=require('fs');
|
|
93
90
|
const pkg=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));
|
|
@@ -101,80 +98,22 @@ tasks:
|
|
|
101
98
|
}
|
|
102
99
|
" "$root_pkg" || exit $?
|
|
103
100
|
fi
|
|
104
|
-
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" vendored "$
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
"
|
|
110
|
-
" session:ready "|" session-ready "|\
|
|
111
|
-
" occupancy:steal "|" occupancy-steal "|\
|
|
112
|
-
" occupancy:release "|" occupancy-release "|\
|
|
113
|
-
" occupancy:heartbeat "|" occupancy-heartbeat "|\
|
|
114
|
-
" occupancy:grant "|" occupancy-grant "|\
|
|
115
|
-
" session:end "|" session-end "|\
|
|
116
|
-
" lifecycle:event "|" lifecycle-event "|\
|
|
117
|
-
" verify:session-ritual "|" verify-session-ritual "|\
|
|
118
|
-
" verify:tools "|" verify-tools "|\
|
|
119
|
-
" triage:summary "|" triage-summary "|\
|
|
120
|
-
" triage:welcome "|" triage-welcome "|\
|
|
121
|
-
" verify:cache-fresh "|" verify-cache-fresh "|\
|
|
122
|
-
" preflight-cache "|\
|
|
123
|
-
" ownership:doctor "|" ownership-doctor "|\
|
|
124
|
-
" ownership:fix "|" ownership-fix "|\
|
|
125
|
-
" verify:ownership "|" verify-ownership ")
|
|
126
|
-
is_runtime_verb=1
|
|
127
|
-
;;
|
|
128
|
-
esac
|
|
129
|
-
if [ "${DEFT_USE_GLOBAL_CLI:-}" = "1" ]; then
|
|
130
|
-
is_runtime_verb=1
|
|
101
|
+
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" vendored "$pin_bin"
|
|
102
|
+
else
|
|
103
|
+
# OVPR consumer / deposit: DEFT_ROOT is often the runtime package itself.
|
|
104
|
+
bin=""
|
|
105
|
+
if [ -f "{{.DEFT_ROOT}}/dist/bin.js" ]; then
|
|
106
|
+
bin="{{.DEFT_ROOT}}/dist/bin.js"
|
|
131
107
|
fi
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
108
|
+
if [ -n "$bin" ] && [ -f "$bin" ]; then
|
|
109
|
+
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" vendored "$bin"
|
|
110
|
+
elif command -v deft >/dev/null 2>&1; then
|
|
111
|
+
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" global deft
|
|
135
112
|
elif command -v directive >/dev/null 2>&1; then
|
|
136
|
-
|
|
137
|
-
fi
|
|
138
|
-
if [ "$is_runtime_verb" = 1 ] && [ -n "$global_cli" ]; then
|
|
139
|
-
node -e "
|
|
140
|
-
const {execFileSync}=require('child_process');
|
|
141
|
-
const root=process.argv[1];
|
|
142
|
-
const cli=process.argv[2];
|
|
143
|
-
let srcVer='0.0.0-dev';
|
|
144
|
-
try{
|
|
145
|
-
if(process.env.DEFT_RELEASE_VERSION){
|
|
146
|
-
srcVer=process.env.DEFT_RELEASE_VERSION;
|
|
147
|
-
}else{
|
|
148
|
-
srcVer=execFileSync('git',['describe','--tags','--abbrev=0'],{cwd:root,encoding:'utf8'}).trim().replace(/^v/,'');
|
|
149
|
-
}
|
|
150
|
-
}catch{}
|
|
151
|
-
let globalVer='unknown';
|
|
152
|
-
try{
|
|
153
|
-
globalVer=execFileSync(cli,['--version'],{encoding:'utf8'}).trim();
|
|
154
|
-
}catch{}
|
|
155
|
-
const m=globalVer.match(/@([0-9]+\\.[0-9]+\\.[0-9]+)/)||globalVer.match(/([0-9]+\\.[0-9]+\\.[0-9]+)/);
|
|
156
|
-
const gv=m?m[1]:globalVer;
|
|
157
|
-
if(srcVer!=='0.0.0-dev'&&gv!=='unknown'&&gv!==srcVer){
|
|
158
|
-
console.error('deft: using global '+cli+' ('+gv+'); source checkout is v'+srcVer+' — run task build for a local engine match.');
|
|
159
|
-
}
|
|
160
|
-
" "{{.DEFT_ROOT}}" "$global_cli" >&2 || true
|
|
161
|
-
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" global "$global_cli"
|
|
113
|
+
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" global directive
|
|
162
114
|
else
|
|
163
|
-
echo "deft:
|
|
164
|
-
|
|
165
|
-
echo " Install global deft: npm i -g @deftai/directive" >&2
|
|
166
|
-
echo " Or run \`task build\` first (framework source checkout)." >&2
|
|
167
|
-
else
|
|
168
|
-
echo " Run \`task build\` first (framework source checkout)." >&2
|
|
169
|
-
fi
|
|
115
|
+
echo "deft: neither OVPR bin.js under {{.DEFT_ROOT}} nor a global deft command is available." >&2
|
|
116
|
+
echo " Install with: npm i -g @deftai/directive" >&2
|
|
170
117
|
exit 2
|
|
171
118
|
fi
|
|
172
|
-
elif command -v deft >/dev/null 2>&1; then
|
|
173
|
-
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" global deft
|
|
174
|
-
elif command -v directive >/dev/null 2>&1; then
|
|
175
|
-
node "{{.TASKFILE_DIR}}/engine-invoke.cjs" global directive
|
|
176
|
-
else
|
|
177
|
-
echo "deft: neither OVPR/legacy bin.js under {{.DEFT_ROOT}} nor a global deft command is available." >&2
|
|
178
|
-
echo " Install with: npm i -g @deftai/directive" >&2
|
|
179
|
-
exit 2
|
|
180
119
|
fi
|
package/tasks/eval.yml
CHANGED
|
@@ -6,8 +6,6 @@ vars:
|
|
|
6
6
|
tasks:
|
|
7
7
|
health:
|
|
8
8
|
desc: "Tier 0 static self-consistency aggregation -- versioned framework health score (#1703). -- task eval:health [-- --json] [--no-persist] [--project-root PATH]"
|
|
9
|
-
deps:
|
|
10
|
-
- task: :engine:_ts-build
|
|
11
9
|
cmds:
|
|
12
10
|
- task: :engine:invoke
|
|
13
11
|
vars:
|
|
@@ -15,8 +13,6 @@ tasks:
|
|
|
15
13
|
|
|
16
14
|
run:
|
|
17
15
|
desc: "Tier 2 golden corpus champion-challenger eval (#1703). -- task eval:run -- --model MODEL [--seed N] [--directive-version V] [--harness NAME] [--json] [--no-persist] [--project-root PATH]"
|
|
18
|
-
deps:
|
|
19
|
-
- task: :engine:_ts-build
|
|
20
16
|
cmds:
|
|
21
17
|
- task: :engine:invoke
|
|
22
18
|
vars:
|
|
@@ -24,8 +20,6 @@ tasks:
|
|
|
24
20
|
|
|
25
21
|
report:
|
|
26
22
|
desc: "Tier 2 version-diff report with significance (#1703). -- task eval:report -- --champion V --challenger V --model MODEL [--json] [--project-root PATH]"
|
|
27
|
-
deps:
|
|
28
|
-
- task: :engine:_ts-build
|
|
29
23
|
cmds:
|
|
30
24
|
- task: :engine:invoke
|
|
31
25
|
vars:
|
|
@@ -33,8 +27,6 @@ tasks:
|
|
|
33
27
|
|
|
34
28
|
triggers:
|
|
35
29
|
desc: "Trigger routing coverage for AGENTS.md / Skills Index (#1586). Offline skill-pi-trigger-eval compatible eval over evals/trigger-cases.jsonl vs REFERENCES.md. -- task eval:triggers [-- --json] [--project-root PATH]"
|
|
36
|
-
deps:
|
|
37
|
-
- task: :engine:_ts-build
|
|
38
30
|
cmds:
|
|
39
31
|
- task: :engine:invoke
|
|
40
32
|
vars:
|
package/tasks/feedback.yml
CHANGED
|
@@ -6,8 +6,6 @@ vars:
|
|
|
6
6
|
tasks:
|
|
7
7
|
file:
|
|
8
8
|
desc: "Draft or file a deduped framework-gap issue upstream (#1709 / #3713). -- task feedback:file -- [--summary TEXT | positional] [--context ...] [--blocker] [--confirm] [--dry-run] [--json]"
|
|
9
|
-
deps:
|
|
10
|
-
- task: :engine:_ts-build
|
|
11
9
|
cmds:
|
|
12
10
|
- task: :engine:invoke
|
|
13
11
|
vars:
|
package/tasks/install.yml
CHANGED
|
@@ -20,8 +20,6 @@ tasks:
|
|
|
20
20
|
uninstall:
|
|
21
21
|
desc: Remove deft entry from AGENTS.md
|
|
22
22
|
dir: '{{.USER_WORKING_DIR}}'
|
|
23
|
-
deps:
|
|
24
|
-
- task: :engine:_ts-build
|
|
25
23
|
cmds:
|
|
26
24
|
- task: :engine:invoke
|
|
27
25
|
vars:
|
|
@@ -62,8 +60,6 @@ tasks:
|
|
|
62
60
|
# `task upgrade` failed on the primary npm consumer workflow. Framework-root is
|
|
63
61
|
# intentionally omitted: install-upgrade auto-detects the consumer's
|
|
64
62
|
# `.deft/core/VERSION` (#2053) when run from the global engine.
|
|
65
|
-
deps:
|
|
66
|
-
- task: :engine:_ts-build
|
|
67
63
|
cmds:
|
|
68
64
|
- task: :engine:invoke
|
|
69
65
|
vars:
|
package/tasks/issue.yml
CHANGED
|
@@ -22,8 +22,6 @@ tasks:
|
|
|
22
22
|
ingest:
|
|
23
23
|
desc: "Ingest GitHub issues as scope vBRIEFs (single <N> or --all [--label L] [--status S] [--dry-run])"
|
|
24
24
|
dir: '{{.USER_WORKING_DIR}}'
|
|
25
|
-
deps:
|
|
26
|
-
- task: :engine:_ts-build
|
|
27
25
|
cmds:
|
|
28
26
|
- task: :engine:invoke
|
|
29
27
|
vars:
|
|
@@ -39,7 +37,6 @@ tasks:
|
|
|
39
37
|
emit:
|
|
40
38
|
desc: "File GitHub issue(s) from scope vBRIEF(s) (<path> | --umbrella <glob> | --per-vbrief <glob> [--dry-run])"
|
|
41
39
|
dir: '{{.USER_WORKING_DIR}}'
|
|
42
|
-
deps: [":engine:_ts-build"]
|
|
43
40
|
env:
|
|
44
41
|
PYTHONUTF8: "1"
|
|
45
42
|
cmds:
|
package/tasks/lifecycle.yml
CHANGED
|
@@ -27,7 +27,6 @@ tasks:
|
|
|
27
27
|
stats:
|
|
28
28
|
desc: "Local xBRIEF lifecycle folder counts for process rollups (#2995). -- task lifecycle:stats -- --since=7d [--json] [--project-root <path>]"
|
|
29
29
|
dir: '{{.USER_WORKING_DIR}}'
|
|
30
|
-
deps: [':engine:_ts-build']
|
|
31
30
|
env:
|
|
32
31
|
PYTHONUTF8: "1"
|
|
33
32
|
cmds:
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Thin tip→pin materialize (#5805 Prefer-A Bound P3/P4).
|
|
6
|
+
* Stages + packs via existing scripts, then replaces this checkout's
|
|
7
|
+
* node_modules/@deftai/directive-runtime only. No package.json/lockfile
|
|
8
|
+
* location-spec mutation. Serializes via a lockfile under .deft-scratch.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { spawnSync } = require("node:child_process");
|
|
12
|
+
const fs = require("node:fs");
|
|
13
|
+
const os = require("node:os");
|
|
14
|
+
const path = require("node:path");
|
|
15
|
+
|
|
16
|
+
const PIN_PKG = path.join("node_modules", "@deftai", "directive-runtime");
|
|
17
|
+
const LOCK_REL = path.join(".deft-scratch", "materialize-runtime.lock");
|
|
18
|
+
const OVERLAY_REL = path.join(".deft-scratch", "materialize-runtime-overlay.json");
|
|
19
|
+
|
|
20
|
+
function fail(msg, code = 2) {
|
|
21
|
+
console.error(`materialize:runtime: ${msg}`);
|
|
22
|
+
process.exit(code);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function runNode(script, args, opts = {}) {
|
|
26
|
+
const r = spawnSync(process.execPath, [script, ...args], {
|
|
27
|
+
cwd: opts.cwd,
|
|
28
|
+
encoding: "utf8",
|
|
29
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
30
|
+
env: process.env,
|
|
31
|
+
});
|
|
32
|
+
if (r.status !== 0) {
|
|
33
|
+
const err = new Error(
|
|
34
|
+
`${path.basename(script)} failed (exit ${r.status}): ${r.stderr || r.stdout || ""}`.trim(),
|
|
35
|
+
);
|
|
36
|
+
err.code = r.status || 2;
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
return (r.stdout || "").trim();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function acquireLock(projectRoot) {
|
|
43
|
+
const lockPath = path.join(projectRoot, LOCK_REL);
|
|
44
|
+
fs.mkdirSync(path.dirname(lockPath), { recursive: true });
|
|
45
|
+
try {
|
|
46
|
+
const fd = fs.openSync(lockPath, "wx");
|
|
47
|
+
fs.writeFileSync(fd, `${process.pid}\n${new Date().toISOString()}\n`);
|
|
48
|
+
fs.closeSync(fd);
|
|
49
|
+
return lockPath;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
if (err && err.code === "EEXIST") {
|
|
52
|
+
const locked = new Error(
|
|
53
|
+
`another materialize is in progress (lock ${lockPath}); reject second writer`,
|
|
54
|
+
);
|
|
55
|
+
locked.code = "EEXIST";
|
|
56
|
+
throw locked;
|
|
57
|
+
}
|
|
58
|
+
throw err;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function releaseLock(lockPath) {
|
|
63
|
+
try {
|
|
64
|
+
fs.unlinkSync(lockPath);
|
|
65
|
+
} catch {
|
|
66
|
+
/* ignore */
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** True when dir is safe to replace: missing, or a real directory whose realpath is under checkoutRoot. */
|
|
71
|
+
function isCheckoutOwnedDir(dir, checkoutRoot) {
|
|
72
|
+
try {
|
|
73
|
+
const st = fs.lstatSync(dir);
|
|
74
|
+
if (st.isSymbolicLink()) {
|
|
75
|
+
// pnpm pin is often a symlink into the checkout store — allow when realpath stays in-tree.
|
|
76
|
+
if (!checkoutRoot) return false;
|
|
77
|
+
let real;
|
|
78
|
+
try {
|
|
79
|
+
real = fs.realpathSync(dir);
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
const rootReal = fs.realpathSync(checkoutRoot);
|
|
84
|
+
const rel = path.relative(rootReal, real);
|
|
85
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
86
|
+
}
|
|
87
|
+
if (!st.isDirectory()) return false;
|
|
88
|
+
if (!checkoutRoot) return true;
|
|
89
|
+
let real;
|
|
90
|
+
try {
|
|
91
|
+
real = fs.realpathSync(dir);
|
|
92
|
+
} catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const rootReal = fs.realpathSync(checkoutRoot);
|
|
96
|
+
const rel = path.relative(rootReal, real);
|
|
97
|
+
return rel === "" || (!rel.startsWith("..") && !path.isAbsolute(rel));
|
|
98
|
+
} catch {
|
|
99
|
+
return true; // missing is fine; we will create
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function rmrf(target) {
|
|
104
|
+
fs.rmSync(target, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function movePath(src, dest) {
|
|
108
|
+
try {
|
|
109
|
+
fs.renameSync(src, dest);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
if (err && err.code === "EXDEV") {
|
|
112
|
+
fs.cpSync(src, dest, { recursive: true, force: true });
|
|
113
|
+
rmrf(src);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function materialize(projectRoot, opts = {}) {
|
|
121
|
+
const root = path.resolve(projectRoot);
|
|
122
|
+
const stageScript = path.join(root, "packages", "runtime", "scripts", "stage-bundle.mjs");
|
|
123
|
+
const packScript = path.join(root, "packages", "runtime", "scripts", "pack-staged-runtime.mjs");
|
|
124
|
+
if (!fs.existsSync(stageScript) || !fs.existsSync(packScript)) {
|
|
125
|
+
throw new Error("stage-bundle/pack-staged-runtime scripts missing; run from framework-source checkout");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const pinDir = path.join(root, PIN_PKG);
|
|
129
|
+
if (fs.existsSync(pinDir) && !isCheckoutOwnedDir(pinDir, root)) {
|
|
130
|
+
throw new Error(`refusing to mutate linked/shared pin at ${pinDir}`);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const lockPath = acquireLock(root);
|
|
134
|
+
const priorPinUsable = fs.existsSync(path.join(pinDir, "dist", "bin.js"));
|
|
135
|
+
let priorBackup = null;
|
|
136
|
+
let stageDir;
|
|
137
|
+
let packDir;
|
|
138
|
+
let extractDir;
|
|
139
|
+
try {
|
|
140
|
+
stageDir = fs.mkdtempSync(path.join(os.tmpdir(), "deft-materialize-stage-"));
|
|
141
|
+
packDir = fs.mkdtempSync(path.join(os.tmpdir(), "deft-materialize-pack-"));
|
|
142
|
+
runNode(stageScript, [stageDir], { cwd: root });
|
|
143
|
+
const tgz = runNode(packScript, [stageDir, packDir], { cwd: root });
|
|
144
|
+
if (!tgz || !fs.existsSync(tgz)) fail(`pack did not emit tgz (got ${JSON.stringify(tgz)})`);
|
|
145
|
+
|
|
146
|
+
extractDir = fs.mkdtempSync(path.join(os.tmpdir(), "deft-materialize-extract-"));
|
|
147
|
+
const tar = spawnSync(
|
|
148
|
+
process.platform === "win32" ? "tar.exe" : "tar",
|
|
149
|
+
["-xzf", tgz, "-C", extractDir],
|
|
150
|
+
{ encoding: "utf8" },
|
|
151
|
+
);
|
|
152
|
+
if (tar.status !== 0) {
|
|
153
|
+
throw Object.assign(new Error(`tar extract failed: ${tar.stderr || tar.stdout || ""}`.trim()), { code: tar.status || 2 });
|
|
154
|
+
}
|
|
155
|
+
const entries = fs.readdirSync(extractDir);
|
|
156
|
+
if (entries.length !== 1) throw new Error(`expected one package folder in tgz, got ${entries.join(",")}`);
|
|
157
|
+
const extracted = path.join(extractDir, entries[0]);
|
|
158
|
+
|
|
159
|
+
const parent = path.dirname(pinDir);
|
|
160
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
161
|
+
const stagingPin = path.join(parent, `.directive-runtime-next-${process.pid}`);
|
|
162
|
+
rmrf(stagingPin);
|
|
163
|
+
movePath(extracted, stagingPin);
|
|
164
|
+
if (!fs.existsSync(path.join(stagingPin, "dist", "bin.js"))) {
|
|
165
|
+
throw new Error("staged pin bin missing before replacement");
|
|
166
|
+
}
|
|
167
|
+
// Keep recoverable backup until replacement succeeds.
|
|
168
|
+
if (fs.existsSync(pinDir)) {
|
|
169
|
+
priorBackup = path.join(parent, `.directive-runtime-prev-${process.pid}`);
|
|
170
|
+
rmrf(priorBackup);
|
|
171
|
+
movePath(pinDir, priorBackup);
|
|
172
|
+
}
|
|
173
|
+
try {
|
|
174
|
+
movePath(stagingPin, pinDir);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
if (priorBackup && fs.existsSync(priorBackup)) {
|
|
177
|
+
try {
|
|
178
|
+
movePath(priorBackup, pinDir);
|
|
179
|
+
priorBackup = null;
|
|
180
|
+
} catch {
|
|
181
|
+
/* best-effort restore */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
if (priorBackup) {
|
|
187
|
+
rmrf(priorBackup);
|
|
188
|
+
priorBackup = null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let head = "unknown";
|
|
192
|
+
try {
|
|
193
|
+
const g = spawnSync("git", ["rev-parse", "HEAD"], { cwd: root, encoding: "utf8" });
|
|
194
|
+
if (g.status === 0) head = g.stdout.trim();
|
|
195
|
+
} catch {
|
|
196
|
+
/* ignore */
|
|
197
|
+
}
|
|
198
|
+
const overlay = {
|
|
199
|
+
schema: "deft.materialize.runtime.overlay.v1",
|
|
200
|
+
writtenAt: new Date().toISOString(),
|
|
201
|
+
projectRoot: root,
|
|
202
|
+
pinPath: pinDir,
|
|
203
|
+
sourceRevision: head,
|
|
204
|
+
tarball: path.basename(tgz),
|
|
205
|
+
note: "Local tip overlay into checkout pin; package.json/lockfile pins unchanged. Normal PM install restores registry pin.",
|
|
206
|
+
};
|
|
207
|
+
fs.writeFileSync(path.join(root, OVERLAY_REL), `${JSON.stringify(overlay, null, 2)}\n`);
|
|
208
|
+
if (!fs.existsSync(path.join(pinDir, "dist", "bin.js"))) {
|
|
209
|
+
throw new Error("materialize completed but pin bin missing");
|
|
210
|
+
}
|
|
211
|
+
process.stdout.write(`materialize:runtime: installed tip into ${pinDir}\n`);
|
|
212
|
+
process.stdout.write(`materialize:runtime: overlay ${path.join(root, OVERLAY_REL)}\n`);
|
|
213
|
+
return overlay;
|
|
214
|
+
} catch (err) {
|
|
215
|
+
if (priorPinUsable) {
|
|
216
|
+
console.error("materialize:runtime: failed after stage/pack; prior pin left in place");
|
|
217
|
+
}
|
|
218
|
+
throw err;
|
|
219
|
+
} finally {
|
|
220
|
+
releaseLock(lockPath);
|
|
221
|
+
if (stageDir) rmrf(stageDir);
|
|
222
|
+
if (packDir) rmrf(packDir);
|
|
223
|
+
if (extractDir) rmrf(extractDir);
|
|
224
|
+
if (priorBackup) rmrf(priorBackup);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (require.main === module) {
|
|
229
|
+
const root = process.argv[2] || process.cwd();
|
|
230
|
+
try {
|
|
231
|
+
materialize(root);
|
|
232
|
+
} catch (err) {
|
|
233
|
+
fail(err && err.message ? err.message : String(err), err && err.code ? Number(err.code) || 2 : 2);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
module.exports = {
|
|
238
|
+
materialize,
|
|
239
|
+
acquireLock,
|
|
240
|
+
releaseLock,
|
|
241
|
+
isCheckoutOwnedDir,
|
|
242
|
+
movePath,
|
|
243
|
+
PIN_PKG,
|
|
244
|
+
LOCK_REL,
|
|
245
|
+
OVERLAY_REL,
|
|
246
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const assert = require("node:assert/strict");
|
|
5
|
+
const {
|
|
6
|
+
mkdtempSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
symlinkSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
existsSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
} = require("node:fs");
|
|
14
|
+
const { tmpdir } = require("node:os");
|
|
15
|
+
const { join } = require("node:path");
|
|
16
|
+
const { describe, it, after } = require("node:test");
|
|
17
|
+
const {
|
|
18
|
+
acquireLock,
|
|
19
|
+
releaseLock,
|
|
20
|
+
isCheckoutOwnedDir,
|
|
21
|
+
movePath,
|
|
22
|
+
materialize,
|
|
23
|
+
LOCK_REL,
|
|
24
|
+
} = require("./materialize.cjs");
|
|
25
|
+
|
|
26
|
+
const roots = [];
|
|
27
|
+
function tempRoot() {
|
|
28
|
+
const root = mkdtempSync(join(tmpdir(), "mat-test-"));
|
|
29
|
+
roots.push(root);
|
|
30
|
+
return root;
|
|
31
|
+
}
|
|
32
|
+
after(() => {
|
|
33
|
+
for (const root of roots) {
|
|
34
|
+
try {
|
|
35
|
+
rmSync(root, { recursive: true, force: true });
|
|
36
|
+
} catch {
|
|
37
|
+
/* ignore */
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("materialize isolation (#5805 P4)", () => {
|
|
43
|
+
it("acquireLock rejects a second writer", () => {
|
|
44
|
+
const root = tempRoot();
|
|
45
|
+
const lock = acquireLock(root);
|
|
46
|
+
assert.ok(existsSync(join(root, LOCK_REL)));
|
|
47
|
+
assert.throws(() => acquireLock(root), /another materialize|second writer|EEXIST|in progress/i);
|
|
48
|
+
releaseLock(lock);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("isCheckoutOwnedDir is false for a symlink outside the checkout", () => {
|
|
52
|
+
const root = tempRoot();
|
|
53
|
+
const outside = tempRoot();
|
|
54
|
+
const real = join(outside, "real-pkg");
|
|
55
|
+
const link = join(root, "linked-pkg");
|
|
56
|
+
mkdirSync(real, { recursive: true });
|
|
57
|
+
try {
|
|
58
|
+
symlinkSync(real, link, "junction");
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
assert.equal(isCheckoutOwnedDir(link, root), false);
|
|
63
|
+
assert.equal(isCheckoutOwnedDir(real, outside), true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("isCheckoutOwnedDir allows in-tree symlink (pnpm-style)", () => {
|
|
67
|
+
const root = tempRoot();
|
|
68
|
+
const store = join(root, ".pnpm", "pkg");
|
|
69
|
+
const link = join(root, "node_modules", "@deftai", "directive-runtime");
|
|
70
|
+
mkdirSync(store, { recursive: true });
|
|
71
|
+
mkdirSync(join(root, "node_modules", "@deftai"), { recursive: true });
|
|
72
|
+
try {
|
|
73
|
+
symlinkSync(store, link, "junction");
|
|
74
|
+
} catch {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
assert.equal(isCheckoutOwnedDir(link, root), true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("missing destination is treated as checkout-owned (creatable)", () => {
|
|
81
|
+
const root = tempRoot();
|
|
82
|
+
assert.equal(isCheckoutOwnedDir(join(root, "missing"), root), true);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("movePath falls back when rename would need copy semantics", () => {
|
|
86
|
+
const root = tempRoot();
|
|
87
|
+
const src = join(root, "src-dir");
|
|
88
|
+
const dest = join(root, "dest-dir");
|
|
89
|
+
mkdirSync(src, { recursive: true });
|
|
90
|
+
writeFileSync(join(src, "f.txt"), "ok");
|
|
91
|
+
movePath(src, dest);
|
|
92
|
+
assert.equal(readFileSync(join(dest, "f.txt"), "utf8"), "ok");
|
|
93
|
+
assert.equal(existsSync(src), false);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("failed materialize releases the lock so retries can proceed", () => {
|
|
97
|
+
const root = tempRoot();
|
|
98
|
+
assert.throws(() => materialize(root), /stage-bundle|pack-staged|missing/i);
|
|
99
|
+
assert.equal(existsSync(join(root, LOCK_REL)), false);
|
|
100
|
+
const lock = acquireLock(root);
|
|
101
|
+
releaseLock(lock);
|
|
102
|
+
});
|
|
103
|
+
});
|