@biffo/cli 0.298.22 → 0.298.23
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/index.js +141 -27
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -371,6 +371,39 @@ function isTemplateOwned(relPath, manifest) {
|
|
|
371
371
|
const u = longestMatch(relPath, manifest.userOwned);
|
|
372
372
|
return t > u;
|
|
373
373
|
}
|
|
374
|
+
function leadingSegmentOverlap(relPath, pattern) {
|
|
375
|
+
const pathSegs = relPath.split("/");
|
|
376
|
+
const patternSegs = pattern.replace(/\/$/, "").split("/");
|
|
377
|
+
let shared = 0;
|
|
378
|
+
for (let i = 0; i < Math.min(pathSegs.length, patternSegs.length); i++) {
|
|
379
|
+
if (patternSegs[i] !== pathSegs[i]) break;
|
|
380
|
+
shared++;
|
|
381
|
+
}
|
|
382
|
+
return shared;
|
|
383
|
+
}
|
|
384
|
+
function explainOwnership(relPath, manifest) {
|
|
385
|
+
let templateOwnedMatch = null;
|
|
386
|
+
let bestTemplateLen = -1;
|
|
387
|
+
for (const p of manifest.templateOwned) {
|
|
388
|
+
const len = matchLength(relPath, p);
|
|
389
|
+
if (len > bestTemplateLen) {
|
|
390
|
+
bestTemplateLen = len;
|
|
391
|
+
templateOwnedMatch = p;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
let bestShared = 0;
|
|
395
|
+
let nearestUserOwnedEntries = [];
|
|
396
|
+
for (const p of manifest.userOwned) {
|
|
397
|
+
const shared = leadingSegmentOverlap(relPath, p);
|
|
398
|
+
if (shared > bestShared) {
|
|
399
|
+
bestShared = shared;
|
|
400
|
+
nearestUserOwnedEntries = [p];
|
|
401
|
+
} else if (shared === bestShared && shared > 0) {
|
|
402
|
+
nearestUserOwnedEntries.push(p);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
return { templateOwnedMatch, nearestUserOwnedEntries };
|
|
406
|
+
}
|
|
374
407
|
function toPosix(p) {
|
|
375
408
|
return sep === "/" ? p : p.split(sep).join("/");
|
|
376
409
|
}
|
|
@@ -11755,8 +11788,8 @@ async function runOwnershipCheck(argv) {
|
|
|
11755
11788
|
const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
11756
11789
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11757
11790
|
if (messageFile) {
|
|
11758
|
-
const { readFileSync: readFileSync45, existsSync:
|
|
11759
|
-
if (
|
|
11791
|
+
const { readFileSync: readFileSync45, existsSync: existsSync55 } = await import("fs");
|
|
11792
|
+
if (existsSync55(messageFile)) commitMessage = readFileSync45(messageFile, "utf8");
|
|
11760
11793
|
}
|
|
11761
11794
|
} else {
|
|
11762
11795
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -12463,6 +12496,71 @@ ${BOLD2}What to do${OFF2}
|
|
|
12463
12496
|
process.exit(1);
|
|
12464
12497
|
}
|
|
12465
12498
|
|
|
12499
|
+
// src/scripts/check-orphan-ratchet.ts
|
|
12500
|
+
import { existsSync as existsSync46 } from "fs";
|
|
12501
|
+
import { basename as basename2 } from "path";
|
|
12502
|
+
function reportOrphan(entry, manifest) {
|
|
12503
|
+
console.error(` ${entry.path}`);
|
|
12504
|
+
const { templateOwnedMatch, nearestUserOwnedEntries } = explainOwnership(entry.path, manifest);
|
|
12505
|
+
console.error(
|
|
12506
|
+
` claimed by templateOwned entry: ${templateOwnedMatch ?? "(none \u2014 unexpected)"}`
|
|
12507
|
+
);
|
|
12508
|
+
if (nearestUserOwnedEntries.length > 0) {
|
|
12509
|
+
console.error(` nearest sanctioned carve-out(s): ${nearestUserOwnedEntries.join(", ")}`);
|
|
12510
|
+
console.error(
|
|
12511
|
+
" \u2014 move it under one of those, OR add it to biffo.divergence.json with a Core-Divergence trailer if it belongs there permanently."
|
|
12512
|
+
);
|
|
12513
|
+
} else {
|
|
12514
|
+
console.error(
|
|
12515
|
+
" no userOwned entry shares any leading path segment with this file \u2014 core-manifest.json has no carve-out anywhere near it today."
|
|
12516
|
+
);
|
|
12517
|
+
console.error(
|
|
12518
|
+
" Either this is a core capability that belongs upstream in biffo-template, or it needs a new carve-out declared in core-manifest.json. classify()/isTemplateOwned() cannot tell those apart automatically (#1714) \u2014 that judgement is the author's."
|
|
12519
|
+
);
|
|
12520
|
+
}
|
|
12521
|
+
}
|
|
12522
|
+
async function runOrphanRatchetCheck(opts = {}) {
|
|
12523
|
+
if (opts.instanceDir !== void 0 && !existsSync46(opts.instanceDir)) {
|
|
12524
|
+
console.error(
|
|
12525
|
+
`\u2717 orphan-ratchet guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it carries unsanctioned files, and that is not the same as a clean pass.`
|
|
12526
|
+
);
|
|
12527
|
+
process.exit(2);
|
|
12528
|
+
return;
|
|
12529
|
+
}
|
|
12530
|
+
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12531
|
+
const theirsDir = opts.theirsDir ?? root;
|
|
12532
|
+
const instanceDir = opts.instanceDir ?? theirsDir;
|
|
12533
|
+
const baseDir = opts.baseDir ?? theirsDir;
|
|
12534
|
+
const label = opts.label ?? (basename2(instanceDir) || instanceDir);
|
|
12535
|
+
if (instanceDir === theirsDir && baseDir === theirsDir) {
|
|
12536
|
+
console.log(
|
|
12537
|
+
"self-check mode: --instance-dir was not given, so it defaulted to --theirs-dir (this repo's own root) alongside --base-dir \u2014 all three trees are the same, so this run can only ever find zero orphans by construction. See this script's doc comment. Pass a real instance tree with --instance-dir for a check that can actually find something. biffo-template#1714."
|
|
12538
|
+
);
|
|
12539
|
+
}
|
|
12540
|
+
const manifest = readCoreManifest(theirsDir);
|
|
12541
|
+
const plan = await planCoreUpgrade({ baseDir, oursDir: instanceDir, theirsDir, manifest });
|
|
12542
|
+
const baseline = readOrphanBaseline(instanceDir);
|
|
12543
|
+
const ratchet = checkOrphanRatchet(plan.orphaned.length, baseline);
|
|
12544
|
+
console.log(
|
|
12545
|
+
`examined ${label}: ${plan.orphaned.length} unsanctioned file(s) under a template-owned path (baseline ${baseline === null ? "none recorded" : String(baseline.count)}), against template tree ${theirsDir}`
|
|
12546
|
+
);
|
|
12547
|
+
if (plan.orphaned.length === 0) {
|
|
12548
|
+
console.log(`\u2713 orphan-ratchet guard (${label}): no unsanctioned files`);
|
|
12549
|
+
return;
|
|
12550
|
+
}
|
|
12551
|
+
console.error(
|
|
12552
|
+
`${ratchet.increased ? "\u2717" : "\xB7"} orphan-ratchet guard (${label}): ${String(plan.orphaned.length)} unsanctioned instance file(s) under a template-owned path (#1026)${ratchet.increased ? " \u2014 INCREASED over the recorded baseline" : ""}:`
|
|
12553
|
+
);
|
|
12554
|
+
for (const entry of plan.orphaned) reportOrphan(entry, manifest);
|
|
12555
|
+
if (ratchet.increased) {
|
|
12556
|
+
console.error(
|
|
12557
|
+
`
|
|
12558
|
+
baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biffo-template#1026 and biffo-template#1714.`
|
|
12559
|
+
);
|
|
12560
|
+
process.exit(1);
|
|
12561
|
+
}
|
|
12562
|
+
}
|
|
12563
|
+
|
|
12466
12564
|
// src/scripts/check-pipe-trap.ts
|
|
12467
12565
|
import { readFileSync as readFileSync37, readdirSync as readdirSync22 } from "fs";
|
|
12468
12566
|
import { join as join52, relative as relative9 } from "path";
|
|
@@ -12737,16 +12835,16 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
12737
12835
|
}
|
|
12738
12836
|
|
|
12739
12837
|
// src/scripts/check-plugin-collisions.ts
|
|
12740
|
-
import { existsSync as
|
|
12838
|
+
import { existsSync as existsSync48 } from "fs";
|
|
12741
12839
|
import { join as join55 } from "path";
|
|
12742
12840
|
|
|
12743
12841
|
// src/lib/plugin-collision-guard.ts
|
|
12744
|
-
import { existsSync as
|
|
12842
|
+
import { existsSync as existsSync47, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
|
|
12745
12843
|
import { join as join54 } from "path";
|
|
12746
12844
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
12747
12845
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
12748
12846
|
function subdirectories(dir) {
|
|
12749
|
-
if (!
|
|
12847
|
+
if (!existsSync47(dir)) return [];
|
|
12750
12848
|
return readdirSync23(dir).filter((entry) => {
|
|
12751
12849
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
12752
12850
|
try {
|
|
@@ -12757,12 +12855,12 @@ function subdirectories(dir) {
|
|
|
12757
12855
|
});
|
|
12758
12856
|
}
|
|
12759
12857
|
function regularPackagesOf(pluginDir2) {
|
|
12760
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
12858
|
+
return subdirectories(pluginDir2).filter((name) => existsSync47(join54(pluginDir2, name, "__init__.py"))).sort();
|
|
12761
12859
|
}
|
|
12762
12860
|
function bareTestModulesOf(pluginDir2) {
|
|
12763
12861
|
const testsDir = join54(pluginDir2, "tests");
|
|
12764
|
-
if (!
|
|
12765
|
-
if (
|
|
12862
|
+
if (!existsSync47(testsDir)) return [];
|
|
12863
|
+
if (existsSync47(join54(testsDir, "__init__.py"))) return [];
|
|
12766
12864
|
return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
12767
12865
|
}
|
|
12768
12866
|
function findCollisions(servicesDir, pluginDirs) {
|
|
@@ -12810,7 +12908,7 @@ function formatCollisions(collisions) {
|
|
|
12810
12908
|
async function runPluginCollisionCheck() {
|
|
12811
12909
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12812
12910
|
const servicesDir = join55(root, "services");
|
|
12813
|
-
if (!
|
|
12911
|
+
if (!existsSync48(servicesDir)) {
|
|
12814
12912
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
12815
12913
|
return;
|
|
12816
12914
|
}
|
|
@@ -12827,7 +12925,7 @@ async function runPluginCollisionCheck() {
|
|
|
12827
12925
|
}
|
|
12828
12926
|
|
|
12829
12927
|
// src/lib/plugin-terraform-guard.ts
|
|
12830
|
-
import { existsSync as
|
|
12928
|
+
import { existsSync as existsSync49, readFileSync as readFileSync39, readdirSync as readdirSync24 } from "fs";
|
|
12831
12929
|
import { dirname as dirname10, join as join56, relative as relative10, sep as sep4 } from "path";
|
|
12832
12930
|
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
12833
12931
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
@@ -12870,14 +12968,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
12870
12968
|
}
|
|
12871
12969
|
function checkPluginTerraform(root) {
|
|
12872
12970
|
const violations = [];
|
|
12873
|
-
const coreManifest =
|
|
12971
|
+
const coreManifest = existsSync49(join56(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
12874
12972
|
for (const manifest of findPluginManifests(root)) {
|
|
12875
12973
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
12876
12974
|
const absManifest = join56(root, manifest);
|
|
12877
12975
|
const subscriptions = readSubscriptions(absManifest);
|
|
12878
12976
|
if (subscriptions === null) continue;
|
|
12879
12977
|
const pluginDir2 = dirname10(absManifest);
|
|
12880
|
-
if (
|
|
12978
|
+
if (existsSync49(join56(pluginDir2, "terraform"))) continue;
|
|
12881
12979
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
12882
12980
|
violations.push({
|
|
12883
12981
|
manifest,
|
|
@@ -12908,11 +13006,11 @@ async function runPluginTerraformCheck() {
|
|
|
12908
13006
|
}
|
|
12909
13007
|
|
|
12910
13008
|
// src/scripts/check-plugin-tool-supply.ts
|
|
12911
|
-
import { existsSync as
|
|
13009
|
+
import { existsSync as existsSync51 } from "fs";
|
|
12912
13010
|
import { join as join58 } from "path";
|
|
12913
13011
|
|
|
12914
13012
|
// src/lib/plugin-tool-supply-audit.ts
|
|
12915
|
-
import { existsSync as
|
|
13013
|
+
import { existsSync as existsSync50, readFileSync as readFileSync40, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
|
|
12916
13014
|
import { join as join57 } from "path";
|
|
12917
13015
|
|
|
12918
13016
|
// src/lib/openrouter-model-snapshot.ts
|
|
@@ -13646,8 +13744,8 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13646
13744
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
13647
13745
|
const configPath = join57(repoRoot, CONFIG_PY_PATH);
|
|
13648
13746
|
const orchestrationPath = join57(repoRoot, ORCHESTRATION_SCHEMA_PATH);
|
|
13649
|
-
const configMissing = !
|
|
13650
|
-
const orchestrationSchemaMissing = !
|
|
13747
|
+
const configMissing = !existsSync50(configPath);
|
|
13748
|
+
const orchestrationSchemaMissing = !existsSync50(orchestrationPath);
|
|
13651
13749
|
const knownSet = new Set(knownModelIds);
|
|
13652
13750
|
const snapshotEmpty = knownModelIds.length === 0;
|
|
13653
13751
|
const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
|
|
@@ -13864,7 +13962,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
13864
13962
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13865
13963
|
let allOk = true;
|
|
13866
13964
|
const pluginsRoot = join58(root, "services", "_plugins");
|
|
13867
|
-
if (!
|
|
13965
|
+
if (!existsSync51(pluginsRoot)) {
|
|
13868
13966
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
13869
13967
|
} else {
|
|
13870
13968
|
const report = auditPluginToolSupply(pluginsRoot);
|
|
@@ -13895,7 +13993,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
13895
13993
|
}
|
|
13896
13994
|
}
|
|
13897
13995
|
const servicesApiRoot = join58(root, "services", "api");
|
|
13898
|
-
if (!
|
|
13996
|
+
if (!existsSync51(servicesApiRoot)) {
|
|
13899
13997
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
13900
13998
|
} else {
|
|
13901
13999
|
const modelReport = auditDeclaredModelIds(root);
|
|
@@ -14272,7 +14370,7 @@ async function runSharedFileReductionCheck(args) {
|
|
|
14272
14370
|
}
|
|
14273
14371
|
|
|
14274
14372
|
// src/scripts/check-skeleton-drift.ts
|
|
14275
|
-
import { existsSync as
|
|
14373
|
+
import { existsSync as existsSync52, readdirSync as readdirSync27 } from "fs";
|
|
14276
14374
|
import { join as join60 } from "path";
|
|
14277
14375
|
|
|
14278
14376
|
// src/lib/skeleton-drift-guard.ts
|
|
@@ -14399,7 +14497,7 @@ function discoverSkeletons(root) {
|
|
|
14399
14497
|
} catch {
|
|
14400
14498
|
return [];
|
|
14401
14499
|
}
|
|
14402
|
-
return entries.filter((name) =>
|
|
14500
|
+
return entries.filter((name) => existsSync52(join60(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
14403
14501
|
}
|
|
14404
14502
|
async function runSkeletonDriftCheck() {
|
|
14405
14503
|
const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
@@ -14408,7 +14506,7 @@ async function runSkeletonDriftCheck() {
|
|
|
14408
14506
|
for (const name of skeletons) {
|
|
14409
14507
|
const skeletonRoot = join60(root, "_skeletons", name);
|
|
14410
14508
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
14411
|
-
if (
|
|
14509
|
+
if (existsSync52(join60(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
14412
14510
|
filesConsidered += 1;
|
|
14413
14511
|
}
|
|
14414
14512
|
}
|
|
@@ -14458,7 +14556,7 @@ async function runTerraformInputCheck() {
|
|
|
14458
14556
|
|
|
14459
14557
|
// src/commands/check.ts
|
|
14460
14558
|
var checkCommand = new Command24("check").description(
|
|
14461
|
-
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
14559
|
+
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
|
|
14462
14560
|
);
|
|
14463
14561
|
checkCommand.command("ownership").description("Refuse changes to template-owned paths in an instance (#370)").argument("[base]", "Base branch to diff against; defaults to $GITHUB_BASE_REF").option("--staged <messageFile>", "Check staged changes instead of a branch diff (commit hook)").allowExcessArguments(true).action(async () => {
|
|
14464
14562
|
await runOwnershipCheck(rawArgsAfter("ownership"));
|
|
@@ -14521,6 +14619,22 @@ checkCommand.command("instance-adoption").description(
|
|
|
14521
14619
|
).action(async (opts) => {
|
|
14522
14620
|
await runInstanceAdoptionCheck(opts);
|
|
14523
14621
|
});
|
|
14622
|
+
checkCommand.command("orphan-ratchet").description(
|
|
14623
|
+
"Refuse an instance-written file under a template-owned path with no sanctioned carve-out (#1026), reusing planCoreUpgrade's classify() and checkOrphanRatchet \u2014 previously reachable only from inside `biffo core upgrade`, so drift was discovered only ~90 core versions later by the upgrade it blocks (#1714). --instance-dir defaults to a SELF-CHECK (this repo's own tree, unlike instance-adoption above) that can only ever report zero \u2014 see the entrypoint's own doc comment for why that default exists anyway. Pass a real instance checkout for a check that can actually find something."
|
|
14624
|
+
).option(
|
|
14625
|
+
"--instance-dir <dir>",
|
|
14626
|
+
"The real instance tree to check (oursDir). Defaults to a self-check of this repo's own root, which always reports 0 orphans (#1714) \u2014 pass a real instance checkout for a meaningful result"
|
|
14627
|
+
).option(
|
|
14628
|
+
"--theirs-dir <dir>",
|
|
14629
|
+
"Template tree that defines ownership (theirsDir); defaults to this repo root"
|
|
14630
|
+
).option(
|
|
14631
|
+
"--base-dir <dir>",
|
|
14632
|
+
"Merge-base template tree (baseDir) \u2014 the template at the instance's CURRENT core version; defaults to --theirs-dir, which is only correct when the instance is already on the latest version"
|
|
14633
|
+
).option("--label <name>", "Label for the report; defaults to the basename of --instance-dir").action(
|
|
14634
|
+
async (opts) => {
|
|
14635
|
+
await runOrphanRatchetCheck(opts);
|
|
14636
|
+
}
|
|
14637
|
+
);
|
|
14524
14638
|
checkCommand.command("cognito-invite-template").description(
|
|
14525
14639
|
"Refuse a Cognito invite_message_template missing a required member or placeholder (#356) \u2014 terraform validate is silent on this, so a fresh deploy fails on its first apply"
|
|
14526
14640
|
).action(async () => {
|
|
@@ -14587,7 +14701,7 @@ function rawArgsAfter(subcommand) {
|
|
|
14587
14701
|
}
|
|
14588
14702
|
|
|
14589
14703
|
// src/commands/doctor.ts
|
|
14590
|
-
import { existsSync as
|
|
14704
|
+
import { existsSync as existsSync53, readFileSync as readFileSync44 } from "fs";
|
|
14591
14705
|
import { join as join61, resolve as resolve20 } from "path";
|
|
14592
14706
|
import chalk21 from "chalk";
|
|
14593
14707
|
import { Command as Command25 } from "commander";
|
|
@@ -14768,7 +14882,7 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
14768
14882
|
}
|
|
14769
14883
|
function readLocalCoreVersion(cwd) {
|
|
14770
14884
|
const path = join61(cwd, INSTANCE_CORE_FILE);
|
|
14771
|
-
if (!
|
|
14885
|
+
if (!existsSync53(path)) return null;
|
|
14772
14886
|
try {
|
|
14773
14887
|
return extractVersionField(readFileSync44(path, "utf8"));
|
|
14774
14888
|
} catch {
|
|
@@ -14791,7 +14905,7 @@ function extractVersionField(contents) {
|
|
|
14791
14905
|
}
|
|
14792
14906
|
function readFossil(cwd) {
|
|
14793
14907
|
const path = join61(cwd, CORE_VERSION_FILE);
|
|
14794
|
-
if (!
|
|
14908
|
+
if (!existsSync53(path)) return null;
|
|
14795
14909
|
try {
|
|
14796
14910
|
const value = readFileSync44(path, "utf8").trim();
|
|
14797
14911
|
return value === "" ? null : value;
|
|
@@ -15242,13 +15356,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
15242
15356
|
import { Command as Command27 } from "commander";
|
|
15243
15357
|
|
|
15244
15358
|
// src/lib/packaged-scripts.ts
|
|
15245
|
-
import { existsSync as
|
|
15359
|
+
import { existsSync as existsSync54 } from "fs";
|
|
15246
15360
|
import { dirname as dirname11, join as join62 } from "path";
|
|
15247
15361
|
function findPackagedScript(startDir, relativePath) {
|
|
15248
15362
|
let dir = startDir;
|
|
15249
15363
|
for (; ; ) {
|
|
15250
15364
|
const candidate = join62(dir, relativePath);
|
|
15251
|
-
if (
|
|
15365
|
+
if (existsSync54(candidate)) return candidate;
|
|
15252
15366
|
const parent = dirname11(dir);
|
|
15253
15367
|
if (parent === dir) return null;
|
|
15254
15368
|
dir = parent;
|