@biffo/cli 0.295.2 → 0.296.1
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 +183 -110
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8647,8 +8647,30 @@ function printEntry(entry) {
|
|
|
8647
8647
|
const summary = entry.ui_components.map((c) => `${c.label} (${c.type})`).join(", ");
|
|
8648
8648
|
console.log(` UI components: ${summary}`);
|
|
8649
8649
|
}
|
|
8650
|
+
printScopeSeamEntitlement(entry.name);
|
|
8650
8651
|
console.log("");
|
|
8651
8652
|
}
|
|
8653
|
+
function printScopeSeamEntitlement(name) {
|
|
8654
|
+
console.log("");
|
|
8655
|
+
console.log(chalk14.bold(" Scope-seam entitlement"));
|
|
8656
|
+
console.log(" Declared by the INSTANCE, never by this plugin (ADR-0029, #1653).");
|
|
8657
|
+
console.log(" A plugin manifest cannot grant it \u2014 biffo.plugin.json is not consulted.");
|
|
8658
|
+
console.log(" To let this plugin call GET /internal/scopes or POST /internal/scope-check,");
|
|
8659
|
+
console.log(" the instance operator adds it beside their own scope authorizer:");
|
|
8660
|
+
console.log("");
|
|
8661
|
+
console.log(chalk14.dim(" register_scope_authorizer("));
|
|
8662
|
+
console.log(chalk14.dim(" authorizer,"));
|
|
8663
|
+
console.log(
|
|
8664
|
+
chalk14.dim(` entitlements={"system:${name}": frozenset({"<permission_code>"})},`)
|
|
8665
|
+
);
|
|
8666
|
+
console.log(chalk14.dim(" )"));
|
|
8667
|
+
console.log("");
|
|
8668
|
+
console.log(
|
|
8669
|
+
chalk14.dim(
|
|
8670
|
+
" <permission_code> is one of the INSTANCE's own codes, so ask the operator which\n one covers this plugin's surface. This command cannot report what an instance\n has actually entitled: the map is evaluated in the instance's API at import\n time, and re-deriving it here would be a second authority that can drift from\n the one enforcing the 403."
|
|
8671
|
+
)
|
|
8672
|
+
);
|
|
8673
|
+
}
|
|
8652
8674
|
|
|
8653
8675
|
// src/commands/plugin-install.ts
|
|
8654
8676
|
import { cpSync as cpSync5, existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync23, statSync as statSync7 } from "fs";
|
|
@@ -11678,8 +11700,8 @@ async function runOwnershipCheck(argv) {
|
|
|
11678
11700
|
const { stdout } = await execa14("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
11679
11701
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11680
11702
|
if (messageFile) {
|
|
11681
|
-
const { readFileSync: readFileSync45, existsSync:
|
|
11682
|
-
if (
|
|
11703
|
+
const { readFileSync: readFileSync45, existsSync: existsSync54 } = await import("fs");
|
|
11704
|
+
if (existsSync54(messageFile)) commitMessage = readFileSync45(messageFile, "utf8");
|
|
11683
11705
|
}
|
|
11684
11706
|
} else {
|
|
11685
11707
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -11993,16 +12015,56 @@ async function runEventBridgeLogPermissionCheck() {
|
|
|
11993
12015
|
console.log(`\u2713 EventBridge log permission guard: ${report.summary}`);
|
|
11994
12016
|
}
|
|
11995
12017
|
|
|
11996
|
-
// src/scripts/check-
|
|
12018
|
+
// src/scripts/check-instance-adoption.ts
|
|
12019
|
+
import { existsSync as existsSync43 } from "fs";
|
|
12020
|
+
import { join as join48 } from "path";
|
|
11997
12021
|
import { execa as execa16 } from "execa";
|
|
12022
|
+
async function runInstanceAdoptionCheck(opts = {}) {
|
|
12023
|
+
if (!opts.instanceDir) {
|
|
12024
|
+
console.error(
|
|
12025
|
+
"\u2717 instance-adoption guard: --instance-dir is required \u2014 the real instance tree to check adoption against. There is no self-check default: this repo IS the template, it has no instance tree of its own to examine, and a fabricated one would prove nothing about a real gap."
|
|
12026
|
+
);
|
|
12027
|
+
process.exit(2);
|
|
12028
|
+
}
|
|
12029
|
+
if (!existsSync43(opts.instanceDir)) {
|
|
12030
|
+
console.error(
|
|
12031
|
+
`\u2717 instance-adoption guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it is adopted, and that is not the same as a clean pass.`
|
|
12032
|
+
);
|
|
12033
|
+
process.exit(2);
|
|
12034
|
+
}
|
|
12035
|
+
const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12036
|
+
const theirsDir = opts.theirsDir ?? root;
|
|
12037
|
+
const instanceLabel = opts.instance ?? join48(opts.instanceDir).split("/").filter(Boolean).pop();
|
|
12038
|
+
const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
|
|
12039
|
+
console.log(
|
|
12040
|
+
`examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
|
|
12041
|
+
);
|
|
12042
|
+
const unadopted = report.findings.filter((f) => f.status === "unadopted");
|
|
12043
|
+
if (unadopted.length === 0) {
|
|
12044
|
+
console.log(`\u2713 instance-adoption guard (${instanceLabel}): no adoption gaps`);
|
|
12045
|
+
return;
|
|
12046
|
+
}
|
|
12047
|
+
console.error(
|
|
12048
|
+
`\u2717 instance-adoption guard (${instanceLabel}): ${unadopted.length} adoption gap(s) \u2014 shipped but not consumed (#1538/#1570):`
|
|
12049
|
+
);
|
|
12050
|
+
for (const f of unadopted) {
|
|
12051
|
+
console.error(` ${f.pair.id} \u2014 ${f.pair.userFile} does not consume ${f.pair.templateFile}`);
|
|
12052
|
+
console.error(` ${f.pair.remedy}`);
|
|
12053
|
+
}
|
|
12054
|
+
console.error("\nSee biffo-template#1538/#1570/#1609.");
|
|
12055
|
+
process.exit(1);
|
|
12056
|
+
}
|
|
12057
|
+
|
|
12058
|
+
// src/scripts/check-lambda-output.ts
|
|
12059
|
+
import { execa as execa17 } from "execa";
|
|
11998
12060
|
|
|
11999
12061
|
// src/lib/lambda-output-guard.ts
|
|
12000
12062
|
import { readFileSync as readFileSync36 } from "fs";
|
|
12001
|
-
import { join as
|
|
12063
|
+
import { join as join50 } from "path";
|
|
12002
12064
|
|
|
12003
12065
|
// src/lib/terraform-input-guard.ts
|
|
12004
|
-
import { existsSync as
|
|
12005
|
-
import { join as
|
|
12066
|
+
import { existsSync as existsSync44, readdirSync as readdirSync21, readFileSync as readFileSync35, statSync as statSync13 } from "fs";
|
|
12067
|
+
import { join as join49 } from "path";
|
|
12006
12068
|
var GUARDED_SUBCOMMANDS = [
|
|
12007
12069
|
"init",
|
|
12008
12070
|
"plan",
|
|
@@ -12016,7 +12078,7 @@ function stripComments2(source) {
|
|
|
12016
12078
|
return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
|
|
12017
12079
|
}
|
|
12018
12080
|
function vendoredPluginServiceDirs(repoRoot) {
|
|
12019
|
-
const servicesDir =
|
|
12081
|
+
const servicesDir = join49(repoRoot, "services");
|
|
12020
12082
|
const result = /* @__PURE__ */ new Set();
|
|
12021
12083
|
let entries;
|
|
12022
12084
|
try {
|
|
@@ -12025,9 +12087,9 @@ function vendoredPluginServiceDirs(repoRoot) {
|
|
|
12025
12087
|
return result;
|
|
12026
12088
|
}
|
|
12027
12089
|
for (const entry of entries) {
|
|
12028
|
-
const full =
|
|
12029
|
-
if (!
|
|
12030
|
-
if (
|
|
12090
|
+
const full = join49(servicesDir, entry);
|
|
12091
|
+
if (!existsSync44(full) || !statSync13(full).isDirectory()) continue;
|
|
12092
|
+
if (existsSync44(join49(full, "biffo.plugin.json"))) {
|
|
12031
12093
|
result.add(entry);
|
|
12032
12094
|
}
|
|
12033
12095
|
}
|
|
@@ -12048,7 +12110,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
12048
12110
|
if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
|
|
12049
12111
|
continue;
|
|
12050
12112
|
}
|
|
12051
|
-
const full =
|
|
12113
|
+
const full = join49(dir, entry);
|
|
12052
12114
|
const rel = relative11 ? `${relative11}/${entry}` : entry;
|
|
12053
12115
|
if (statSync13(full).isDirectory()) {
|
|
12054
12116
|
walk2(full, rel);
|
|
@@ -12092,7 +12154,7 @@ function checkWorkflowSource(file, rawSource) {
|
|
|
12092
12154
|
}
|
|
12093
12155
|
function checkTerraformInput(repoRoot) {
|
|
12094
12156
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
12095
|
-
(file) => checkWorkflowSource(file, readFileSync35(
|
|
12157
|
+
(file) => checkWorkflowSource(file, readFileSync35(join49(repoRoot, file), "utf8"))
|
|
12096
12158
|
);
|
|
12097
12159
|
}
|
|
12098
12160
|
|
|
@@ -12150,13 +12212,13 @@ function checkWorkflowSource2(file, rawSource) {
|
|
|
12150
12212
|
}
|
|
12151
12213
|
function checkLambdaOutput(repoRoot) {
|
|
12152
12214
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
12153
|
-
(file) => checkWorkflowSource2(file, readFileSync36(
|
|
12215
|
+
(file) => checkWorkflowSource2(file, readFileSync36(join50(repoRoot, file), "utf8"))
|
|
12154
12216
|
);
|
|
12155
12217
|
}
|
|
12156
12218
|
|
|
12157
12219
|
// src/scripts/check-lambda-output.ts
|
|
12158
12220
|
async function runLambdaOutputCheck() {
|
|
12159
|
-
const root = (await
|
|
12221
|
+
const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12160
12222
|
const files = findWorkflowFiles(root);
|
|
12161
12223
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
12162
12224
|
if (files.length === 0) {
|
|
@@ -12178,9 +12240,9 @@ async function runLambdaOutputCheck() {
|
|
|
12178
12240
|
}
|
|
12179
12241
|
|
|
12180
12242
|
// src/scripts/check-migration-body-change.ts
|
|
12181
|
-
import { execa as
|
|
12182
|
-
import { existsSync as
|
|
12183
|
-
import { join as
|
|
12243
|
+
import { execa as execa18 } from "execa";
|
|
12244
|
+
import { existsSync as existsSync45 } from "fs";
|
|
12245
|
+
import { join as join51 } from "path";
|
|
12184
12246
|
|
|
12185
12247
|
// src/lib/migration-body-change-guard.ts
|
|
12186
12248
|
function checkMigrationBodyChangeMarkers(diffs) {
|
|
@@ -12235,18 +12297,18 @@ var GREEN = "\x1B[32m";
|
|
|
12235
12297
|
var YELLOW2 = "\x1B[33m";
|
|
12236
12298
|
var OFF2 = "\x1B[0m";
|
|
12237
12299
|
async function showAt(ref, path, cwd) {
|
|
12238
|
-
const result = await
|
|
12300
|
+
const result = await execa18("git", ["show", `${ref}:${path}`], { cwd, reject: false });
|
|
12239
12301
|
return result.exitCode === 0 ? result.stdout : null;
|
|
12240
12302
|
}
|
|
12241
12303
|
async function runMigrationBodyChangeCheck(argv) {
|
|
12242
|
-
const root = (await
|
|
12304
|
+
const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12243
12305
|
if (isInstanceRepo(root)) {
|
|
12244
12306
|
console.log(
|
|
12245
12307
|
`\u2713 migration body-change guard: skipped \u2014 this is an instance (${INSTANCE_CORE_FILE} present). Its migrations/versions/ is a carried, user-owned copy, not the template source this guard protects.`
|
|
12246
12308
|
);
|
|
12247
12309
|
return;
|
|
12248
12310
|
}
|
|
12249
|
-
if (!
|
|
12311
|
+
if (!existsSync45(join51(root, MIGRATIONS_VERSIONS_DIR))) {
|
|
12250
12312
|
console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
|
|
12251
12313
|
return;
|
|
12252
12314
|
}
|
|
@@ -12255,8 +12317,8 @@ async function runMigrationBodyChangeCheck(argv) {
|
|
|
12255
12317
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
12256
12318
|
process.exit(2);
|
|
12257
12319
|
}
|
|
12258
|
-
await
|
|
12259
|
-
const { stdout } = await
|
|
12320
|
+
await execa18("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
|
|
12321
|
+
const { stdout } = await execa18(
|
|
12260
12322
|
"git",
|
|
12261
12323
|
[
|
|
12262
12324
|
"diff",
|
|
@@ -12349,8 +12411,8 @@ ${BOLD2}What to do${OFF2}
|
|
|
12349
12411
|
|
|
12350
12412
|
// src/scripts/check-pipe-trap.ts
|
|
12351
12413
|
import { readFileSync as readFileSync37, readdirSync as readdirSync22 } from "fs";
|
|
12352
|
-
import { join as
|
|
12353
|
-
import { execa as
|
|
12414
|
+
import { join as join52, relative as relative9 } from "path";
|
|
12415
|
+
import { execa as execa19 } from "execa";
|
|
12354
12416
|
|
|
12355
12417
|
// src/lib/pipe-trap-guard.ts
|
|
12356
12418
|
var STATUS_BEARING = [
|
|
@@ -12446,7 +12508,7 @@ function findPipeTraps(source) {
|
|
|
12446
12508
|
function shellFiles(root) {
|
|
12447
12509
|
const out = [];
|
|
12448
12510
|
for (const dir of ["scripts", ".githooks"]) {
|
|
12449
|
-
const full =
|
|
12511
|
+
const full = join52(root, dir);
|
|
12450
12512
|
let entries;
|
|
12451
12513
|
try {
|
|
12452
12514
|
entries = readdirSync22(full, { withFileTypes: true });
|
|
@@ -12456,13 +12518,13 @@ function shellFiles(root) {
|
|
|
12456
12518
|
for (const entry of entries) {
|
|
12457
12519
|
if (!entry.isFile()) continue;
|
|
12458
12520
|
if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
|
|
12459
|
-
out.push(
|
|
12521
|
+
out.push(join52(full, entry.name));
|
|
12460
12522
|
}
|
|
12461
12523
|
}
|
|
12462
12524
|
return out;
|
|
12463
12525
|
}
|
|
12464
12526
|
async function runPipeTrapCheck() {
|
|
12465
|
-
const root = (await
|
|
12527
|
+
const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12466
12528
|
const files = shellFiles(root);
|
|
12467
12529
|
console.log(`audited ${files.length} shell file(s) under scripts/ and .githooks/ under ${root}`);
|
|
12468
12530
|
if (files.length === 0) {
|
|
@@ -12489,11 +12551,11 @@ async function runPipeTrapCheck() {
|
|
|
12489
12551
|
}
|
|
12490
12552
|
|
|
12491
12553
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
12492
|
-
import { execa as
|
|
12554
|
+
import { execa as execa20 } from "execa";
|
|
12493
12555
|
|
|
12494
12556
|
// src/lib/plugin-allowlist-convention.ts
|
|
12495
12557
|
import { readFileSync as readFileSync38 } from "fs";
|
|
12496
|
-
import { join as
|
|
12558
|
+
import { join as join53 } from "path";
|
|
12497
12559
|
var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
|
|
12498
12560
|
var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
|
|
12499
12561
|
var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
|
|
@@ -12504,7 +12566,7 @@ var PLUGIN = "<plugin>";
|
|
|
12504
12566
|
var ACCOUNT = "<account>";
|
|
12505
12567
|
function read(repoRoot, relative11) {
|
|
12506
12568
|
try {
|
|
12507
|
-
return readFileSync38(
|
|
12569
|
+
return readFileSync38(join53(repoRoot, relative11), "utf8");
|
|
12508
12570
|
} catch {
|
|
12509
12571
|
throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
|
|
12510
12572
|
}
|
|
@@ -12600,7 +12662,7 @@ Plugins would be rejected by require_service_principal (ADR-0009). Fix the glob,
|
|
|
12600
12662
|
|
|
12601
12663
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
12602
12664
|
async function runPluginAllowlistConventionCheck() {
|
|
12603
|
-
const root = (await
|
|
12665
|
+
const root = (await execa20("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12604
12666
|
let violations;
|
|
12605
12667
|
try {
|
|
12606
12668
|
violations = checkAllowlistConvention(root);
|
|
@@ -12625,33 +12687,33 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
12625
12687
|
}
|
|
12626
12688
|
|
|
12627
12689
|
// src/scripts/check-plugin-collisions.ts
|
|
12628
|
-
import { existsSync as
|
|
12629
|
-
import { join as
|
|
12630
|
-
import { execa as
|
|
12690
|
+
import { existsSync as existsSync47 } from "fs";
|
|
12691
|
+
import { join as join55 } from "path";
|
|
12692
|
+
import { execa as execa21 } from "execa";
|
|
12631
12693
|
|
|
12632
12694
|
// src/lib/plugin-collision-guard.ts
|
|
12633
|
-
import { existsSync as
|
|
12634
|
-
import { join as
|
|
12695
|
+
import { existsSync as existsSync46, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
|
|
12696
|
+
import { join as join54 } from "path";
|
|
12635
12697
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
12636
12698
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
12637
12699
|
function subdirectories(dir) {
|
|
12638
|
-
if (!
|
|
12700
|
+
if (!existsSync46(dir)) return [];
|
|
12639
12701
|
return readdirSync23(dir).filter((entry) => {
|
|
12640
12702
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
12641
12703
|
try {
|
|
12642
|
-
return statSync14(
|
|
12704
|
+
return statSync14(join54(dir, entry)).isDirectory();
|
|
12643
12705
|
} catch {
|
|
12644
12706
|
return false;
|
|
12645
12707
|
}
|
|
12646
12708
|
});
|
|
12647
12709
|
}
|
|
12648
12710
|
function regularPackagesOf(pluginDir2) {
|
|
12649
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
12711
|
+
return subdirectories(pluginDir2).filter((name) => existsSync46(join54(pluginDir2, name, "__init__.py"))).sort();
|
|
12650
12712
|
}
|
|
12651
12713
|
function bareTestModulesOf(pluginDir2) {
|
|
12652
|
-
const testsDir =
|
|
12653
|
-
if (!
|
|
12654
|
-
if (
|
|
12714
|
+
const testsDir = join54(pluginDir2, "tests");
|
|
12715
|
+
if (!existsSync46(testsDir)) return [];
|
|
12716
|
+
if (existsSync46(join54(testsDir, "__init__.py"))) return [];
|
|
12655
12717
|
return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
12656
12718
|
}
|
|
12657
12719
|
function findCollisions(servicesDir, pluginDirs) {
|
|
@@ -12660,7 +12722,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
12660
12722
|
const gather = (kind, namesOf) => {
|
|
12661
12723
|
const claims = /* @__PURE__ */ new Map();
|
|
12662
12724
|
for (const plugin of plugins) {
|
|
12663
|
-
for (const name of namesOf(
|
|
12725
|
+
for (const name of namesOf(join54(servicesDir, plugin))) {
|
|
12664
12726
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
12665
12727
|
}
|
|
12666
12728
|
}
|
|
@@ -12697,9 +12759,9 @@ function formatCollisions(collisions) {
|
|
|
12697
12759
|
|
|
12698
12760
|
// src/scripts/check-plugin-collisions.ts
|
|
12699
12761
|
async function runPluginCollisionCheck() {
|
|
12700
|
-
const root = (await
|
|
12701
|
-
const servicesDir =
|
|
12702
|
-
if (!
|
|
12762
|
+
const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12763
|
+
const servicesDir = join55(root, "services");
|
|
12764
|
+
if (!existsSync47(servicesDir)) {
|
|
12703
12765
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
12704
12766
|
return;
|
|
12705
12767
|
}
|
|
@@ -12716,11 +12778,11 @@ async function runPluginCollisionCheck() {
|
|
|
12716
12778
|
}
|
|
12717
12779
|
|
|
12718
12780
|
// src/scripts/check-plugin-terraform.ts
|
|
12719
|
-
import { execa as
|
|
12781
|
+
import { execa as execa22 } from "execa";
|
|
12720
12782
|
|
|
12721
12783
|
// src/lib/plugin-terraform-guard.ts
|
|
12722
|
-
import { existsSync as
|
|
12723
|
-
import { dirname as dirname10, join as
|
|
12784
|
+
import { existsSync as existsSync48, readFileSync as readFileSync39, readdirSync as readdirSync24 } from "fs";
|
|
12785
|
+
import { dirname as dirname10, join as join56, relative as relative10, sep as sep4 } from "path";
|
|
12724
12786
|
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
12725
12787
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
12726
12788
|
function findPluginManifests(root) {
|
|
@@ -12735,9 +12797,9 @@ function findPluginManifests(root) {
|
|
|
12735
12797
|
for (const entry of entries) {
|
|
12736
12798
|
if (entry.isDirectory()) {
|
|
12737
12799
|
if (SKIP_DIRS3.has(entry.name)) continue;
|
|
12738
|
-
walk2(
|
|
12800
|
+
walk2(join56(dir, entry.name));
|
|
12739
12801
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
12740
|
-
found.push(relative10(root,
|
|
12802
|
+
found.push(relative10(root, join56(dir, entry.name)).split(sep4).join("/"));
|
|
12741
12803
|
}
|
|
12742
12804
|
}
|
|
12743
12805
|
};
|
|
@@ -12762,14 +12824,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
12762
12824
|
}
|
|
12763
12825
|
function checkPluginTerraform(root) {
|
|
12764
12826
|
const violations = [];
|
|
12765
|
-
const coreManifest =
|
|
12827
|
+
const coreManifest = existsSync48(join56(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
12766
12828
|
for (const manifest of findPluginManifests(root)) {
|
|
12767
12829
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
12768
|
-
const absManifest =
|
|
12830
|
+
const absManifest = join56(root, manifest);
|
|
12769
12831
|
const subscriptions = readSubscriptions(absManifest);
|
|
12770
12832
|
if (subscriptions === null) continue;
|
|
12771
12833
|
const pluginDir2 = dirname10(absManifest);
|
|
12772
|
-
if (
|
|
12834
|
+
if (existsSync48(join56(pluginDir2, "terraform"))) continue;
|
|
12773
12835
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
12774
12836
|
violations.push({
|
|
12775
12837
|
manifest,
|
|
@@ -12789,7 +12851,7 @@ function formatViolations(violations) {
|
|
|
12789
12851
|
|
|
12790
12852
|
// src/scripts/check-plugin-terraform.ts
|
|
12791
12853
|
async function runPluginTerraformCheck() {
|
|
12792
|
-
const root = (await
|
|
12854
|
+
const root = (await execa22("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12793
12855
|
const violations = checkPluginTerraform(root);
|
|
12794
12856
|
if (violations.length > 0) {
|
|
12795
12857
|
console.error("\u2717 plugin Terraform guard: event subscriptions with no infrastructure\n");
|
|
@@ -12800,13 +12862,13 @@ async function runPluginTerraformCheck() {
|
|
|
12800
12862
|
}
|
|
12801
12863
|
|
|
12802
12864
|
// src/scripts/check-plugin-tool-supply.ts
|
|
12803
|
-
import { existsSync as
|
|
12804
|
-
import { join as
|
|
12805
|
-
import { execa as
|
|
12865
|
+
import { existsSync as existsSync50 } from "fs";
|
|
12866
|
+
import { join as join58 } from "path";
|
|
12867
|
+
import { execa as execa23 } from "execa";
|
|
12806
12868
|
|
|
12807
12869
|
// src/lib/plugin-tool-supply-audit.ts
|
|
12808
|
-
import { existsSync as
|
|
12809
|
-
import { join as
|
|
12870
|
+
import { existsSync as existsSync49, readFileSync as readFileSync40, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
|
|
12871
|
+
import { join as join57 } from "path";
|
|
12810
12872
|
|
|
12811
12873
|
// src/lib/openrouter-model-snapshot.ts
|
|
12812
12874
|
var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
|
|
@@ -13223,7 +13285,7 @@ function listDirs(root) {
|
|
|
13223
13285
|
}
|
|
13224
13286
|
return entries.filter((e) => {
|
|
13225
13287
|
try {
|
|
13226
|
-
return statSync15(
|
|
13288
|
+
return statSync15(join57(root, e)).isDirectory();
|
|
13227
13289
|
} catch {
|
|
13228
13290
|
return false;
|
|
13229
13291
|
}
|
|
@@ -13239,7 +13301,7 @@ function walkFiles2(root, accept, skipDir) {
|
|
|
13239
13301
|
return;
|
|
13240
13302
|
}
|
|
13241
13303
|
for (const entry of entries) {
|
|
13242
|
-
const p =
|
|
13304
|
+
const p = join57(dir, entry);
|
|
13243
13305
|
let st;
|
|
13244
13306
|
try {
|
|
13245
13307
|
st = statSync15(p);
|
|
@@ -13265,14 +13327,14 @@ function pluginPythonFiles(pluginDir2) {
|
|
|
13265
13327
|
);
|
|
13266
13328
|
}
|
|
13267
13329
|
function pluginTerraformFiles(pluginDir2) {
|
|
13268
|
-
const tfDir =
|
|
13330
|
+
const tfDir = join57(pluginDir2, "terraform");
|
|
13269
13331
|
let entries;
|
|
13270
13332
|
try {
|
|
13271
13333
|
entries = readdirSync25(tfDir);
|
|
13272
13334
|
} catch {
|
|
13273
13335
|
return [];
|
|
13274
13336
|
}
|
|
13275
|
-
return entries.filter((e) => e.endsWith(".tf")).map((e) =>
|
|
13337
|
+
return entries.filter((e) => e.endsWith(".tf")).map((e) => join57(tfDir, e)).sort();
|
|
13276
13338
|
}
|
|
13277
13339
|
function extractManifestTools(manifestText) {
|
|
13278
13340
|
let parsed;
|
|
@@ -13524,8 +13586,8 @@ function isSnapshotStale(fetchedAt, now) {
|
|
|
13524
13586
|
function normalizeModelId(id) {
|
|
13525
13587
|
return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
|
|
13526
13588
|
}
|
|
13527
|
-
var CONFIG_PY_PATH =
|
|
13528
|
-
var ORCHESTRATION_SCHEMA_PATH =
|
|
13589
|
+
var CONFIG_PY_PATH = join57("services", "api", "src", "api", "config.py");
|
|
13590
|
+
var ORCHESTRATION_SCHEMA_PATH = join57(
|
|
13529
13591
|
"services",
|
|
13530
13592
|
"api",
|
|
13531
13593
|
"src",
|
|
@@ -13537,10 +13599,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13537
13599
|
const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
|
|
13538
13600
|
const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
|
|
13539
13601
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
13540
|
-
const configPath =
|
|
13541
|
-
const orchestrationPath =
|
|
13542
|
-
const configMissing = !
|
|
13543
|
-
const orchestrationSchemaMissing = !
|
|
13602
|
+
const configPath = join57(repoRoot, CONFIG_PY_PATH);
|
|
13603
|
+
const orchestrationPath = join57(repoRoot, ORCHESTRATION_SCHEMA_PATH);
|
|
13604
|
+
const configMissing = !existsSync49(configPath);
|
|
13605
|
+
const orchestrationSchemaMissing = !existsSync49(orchestrationPath);
|
|
13544
13606
|
const knownSet = new Set(knownModelIds);
|
|
13545
13607
|
const snapshotEmpty = knownModelIds.length === 0;
|
|
13546
13608
|
const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
|
|
@@ -13611,7 +13673,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13611
13673
|
function discoverPluginDirs(pluginsRoot) {
|
|
13612
13674
|
return listDirs(pluginsRoot).filter((name) => {
|
|
13613
13675
|
try {
|
|
13614
|
-
return statSync15(
|
|
13676
|
+
return statSync15(join57(pluginsRoot, name, "biffo.plugin.json")).isFile();
|
|
13615
13677
|
} catch {
|
|
13616
13678
|
return false;
|
|
13617
13679
|
}
|
|
@@ -13624,8 +13686,8 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13624
13686
|
let terraformBlind = false;
|
|
13625
13687
|
let totalDeclaredTools = 0;
|
|
13626
13688
|
for (const name of pluginNames) {
|
|
13627
|
-
const pluginDir2 =
|
|
13628
|
-
const manifestText = readFileSync40(
|
|
13689
|
+
const pluginDir2 = join57(pluginsRoot, name);
|
|
13690
|
+
const manifestText = readFileSync40(join57(pluginDir2, "biffo.plugin.json"), "utf8");
|
|
13629
13691
|
const manifest = extractManifestTools(manifestText);
|
|
13630
13692
|
if (manifest.parseError) {
|
|
13631
13693
|
findings.push({
|
|
@@ -13723,7 +13785,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13723
13785
|
requiredEnvVars: envResult.envVars,
|
|
13724
13786
|
missingEnvVars: anyWired ? [] : envResult.envVars,
|
|
13725
13787
|
status: anyWired ? "ok" : "missing-env",
|
|
13726
|
-
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${
|
|
13788
|
+
detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join57(pluginDir2, "terraform")}, so this deployment can never supply it`
|
|
13727
13789
|
});
|
|
13728
13790
|
}
|
|
13729
13791
|
}
|
|
@@ -13754,10 +13816,10 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13754
13816
|
|
|
13755
13817
|
// src/scripts/check-plugin-tool-supply.ts
|
|
13756
13818
|
async function runPluginToolSupplyCheck() {
|
|
13757
|
-
const root = (await
|
|
13819
|
+
const root = (await execa23("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13758
13820
|
let allOk = true;
|
|
13759
|
-
const pluginsRoot =
|
|
13760
|
-
if (!
|
|
13821
|
+
const pluginsRoot = join58(root, "services", "_plugins");
|
|
13822
|
+
if (!existsSync50(pluginsRoot)) {
|
|
13761
13823
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
13762
13824
|
} else {
|
|
13763
13825
|
const report = auditPluginToolSupply(pluginsRoot);
|
|
@@ -13787,8 +13849,8 @@ async function runPluginToolSupplyCheck() {
|
|
|
13787
13849
|
console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
|
|
13788
13850
|
}
|
|
13789
13851
|
}
|
|
13790
|
-
const servicesApiRoot =
|
|
13791
|
-
if (!
|
|
13852
|
+
const servicesApiRoot = join58(root, "services", "api");
|
|
13853
|
+
if (!existsSync50(servicesApiRoot)) {
|
|
13792
13854
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
13793
13855
|
} else {
|
|
13794
13856
|
const modelReport = auditDeclaredModelIds(root);
|
|
@@ -13834,7 +13896,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
13834
13896
|
}
|
|
13835
13897
|
|
|
13836
13898
|
// src/scripts/check-release-subject.ts
|
|
13837
|
-
import { execa as
|
|
13899
|
+
import { execa as execa24 } from "execa";
|
|
13838
13900
|
|
|
13839
13901
|
// src/lib/release-version.ts
|
|
13840
13902
|
var MINOR_TYPES = /* @__PURE__ */ new Set(["feat"]);
|
|
@@ -13871,7 +13933,7 @@ async function fetchPrTitleViaGh({
|
|
|
13871
13933
|
PR_NUMBER,
|
|
13872
13934
|
GH_REPO
|
|
13873
13935
|
}) {
|
|
13874
|
-
const { stdout } = await
|
|
13936
|
+
const { stdout } = await execa24(
|
|
13875
13937
|
"gh",
|
|
13876
13938
|
["pr", "view", PR_NUMBER, "--repo", GH_REPO, "--json", "title", "--jq", ".title"],
|
|
13877
13939
|
{ env: { ...process.env, GH_TOKEN } }
|
|
@@ -13907,7 +13969,7 @@ async function resolveReleaseSubject({
|
|
|
13907
13969
|
);
|
|
13908
13970
|
}
|
|
13909
13971
|
}
|
|
13910
|
-
return (await
|
|
13972
|
+
return (await execa24("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
|
|
13911
13973
|
}
|
|
13912
13974
|
async function runReleaseSubjectCheck(argv) {
|
|
13913
13975
|
const base = process.env["GITHUB_BASE_REF"] ?? argv[0];
|
|
@@ -13915,9 +13977,9 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
13915
13977
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
13916
13978
|
process.exit(2);
|
|
13917
13979
|
}
|
|
13918
|
-
const root = (await
|
|
13919
|
-
await
|
|
13920
|
-
const { stdout } = await
|
|
13980
|
+
const root = (await execa24("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13981
|
+
await execa24("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
|
|
13982
|
+
const { stdout } = await execa24("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
|
|
13921
13983
|
cwd: root
|
|
13922
13984
|
});
|
|
13923
13985
|
const changedFiles = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
@@ -14168,13 +14230,13 @@ async function runSharedFileReductionCheck(args) {
|
|
|
14168
14230
|
}
|
|
14169
14231
|
|
|
14170
14232
|
// src/scripts/check-skeleton-drift.ts
|
|
14171
|
-
import { existsSync as
|
|
14172
|
-
import { join as
|
|
14173
|
-
import { execa as
|
|
14233
|
+
import { existsSync as existsSync51, readdirSync as readdirSync27 } from "fs";
|
|
14234
|
+
import { join as join60 } from "path";
|
|
14235
|
+
import { execa as execa25 } from "execa";
|
|
14174
14236
|
|
|
14175
14237
|
// src/lib/skeleton-drift-guard.ts
|
|
14176
14238
|
import { readFileSync as readFileSync43, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
|
|
14177
|
-
import { join as
|
|
14239
|
+
import { join as join59 } from "path";
|
|
14178
14240
|
var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
|
|
14179
14241
|
var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
|
|
14180
14242
|
var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
|
|
@@ -14238,7 +14300,7 @@ function walk(dir, base = dir) {
|
|
|
14238
14300
|
}
|
|
14239
14301
|
for (const entry of entries) {
|
|
14240
14302
|
if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
|
|
14241
|
-
const abs =
|
|
14303
|
+
const abs = join59(dir, entry);
|
|
14242
14304
|
let isDir;
|
|
14243
14305
|
try {
|
|
14244
14306
|
isDir = statSync16(abs).isDirectory();
|
|
@@ -14260,7 +14322,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
|
|
|
14260
14322
|
if (!rule.appliesTo(rel)) continue;
|
|
14261
14323
|
let contents;
|
|
14262
14324
|
try {
|
|
14263
|
-
contents = readFileSync43(
|
|
14325
|
+
contents = readFileSync43(join59(skeletonRoot, rel), "utf8");
|
|
14264
14326
|
} catch {
|
|
14265
14327
|
continue;
|
|
14266
14328
|
}
|
|
@@ -14289,23 +14351,23 @@ function formatViolations2(violations) {
|
|
|
14289
14351
|
|
|
14290
14352
|
// src/scripts/check-skeleton-drift.ts
|
|
14291
14353
|
function discoverSkeletons(root) {
|
|
14292
|
-
const skeletonsDir =
|
|
14354
|
+
const skeletonsDir = join60(root, "_skeletons");
|
|
14293
14355
|
let entries;
|
|
14294
14356
|
try {
|
|
14295
14357
|
entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
14296
14358
|
} catch {
|
|
14297
14359
|
return [];
|
|
14298
14360
|
}
|
|
14299
|
-
return entries.filter((name) =>
|
|
14361
|
+
return entries.filter((name) => existsSync51(join60(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
14300
14362
|
}
|
|
14301
14363
|
async function runSkeletonDriftCheck() {
|
|
14302
|
-
const root = (await
|
|
14364
|
+
const root = (await execa25("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
14303
14365
|
const skeletons = discoverSkeletons(root);
|
|
14304
14366
|
let filesConsidered = 0;
|
|
14305
14367
|
for (const name of skeletons) {
|
|
14306
|
-
const skeletonRoot =
|
|
14368
|
+
const skeletonRoot = join60(root, "_skeletons", name);
|
|
14307
14369
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
14308
|
-
if (
|
|
14370
|
+
if (existsSync51(join60(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
14309
14371
|
filesConsidered += 1;
|
|
14310
14372
|
}
|
|
14311
14373
|
}
|
|
@@ -14319,7 +14381,7 @@ async function runSkeletonDriftCheck() {
|
|
|
14319
14381
|
process.exit(1);
|
|
14320
14382
|
}
|
|
14321
14383
|
const violations = skeletons.flatMap(
|
|
14322
|
-
(name) => auditSkeleton(
|
|
14384
|
+
(name) => auditSkeleton(join60(root, "_skeletons", name), name)
|
|
14323
14385
|
);
|
|
14324
14386
|
if (violations.length > 0) {
|
|
14325
14387
|
console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
|
|
@@ -14331,9 +14393,9 @@ async function runSkeletonDriftCheck() {
|
|
|
14331
14393
|
}
|
|
14332
14394
|
|
|
14333
14395
|
// src/scripts/check-terraform-input.ts
|
|
14334
|
-
import { execa as
|
|
14396
|
+
import { execa as execa26 } from "execa";
|
|
14335
14397
|
async function runTerraformInputCheck() {
|
|
14336
|
-
const root = (await
|
|
14398
|
+
const root = (await execa26("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
14337
14399
|
const files = findWorkflowFiles(root);
|
|
14338
14400
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
14339
14401
|
if (files.length === 0) {
|
|
@@ -14356,7 +14418,7 @@ async function runTerraformInputCheck() {
|
|
|
14356
14418
|
|
|
14357
14419
|
// src/commands/check.ts
|
|
14358
14420
|
var checkCommand = new Command24("check").description(
|
|
14359
|
-
"Repo guards (ownership, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, 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)"
|
|
14421
|
+
"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)"
|
|
14360
14422
|
);
|
|
14361
14423
|
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 () => {
|
|
14362
14424
|
await runOwnershipCheck(rawArgsAfter("ownership"));
|
|
@@ -14408,6 +14470,17 @@ checkCommand.command("core-direct-paths").description(
|
|
|
14408
14470
|
await runCoreDirectPathsCheck(opts);
|
|
14409
14471
|
}
|
|
14410
14472
|
);
|
|
14473
|
+
checkCommand.command("instance-adoption").description(
|
|
14474
|
+
"Refuse a real instance tree that has not consumed a registered adoption channel its target template ships (#1538/#1570/#1609) \u2014 checkInstanceAdoption previously ran only inside `biffo core upgrade`, so a gap in an instance nobody happened to be upgrading went undetected for days (keiranholloway/biffo-platform, PR #174). --instance-dir is REQUIRED and has no self-check default: this repo is the template, not an instance."
|
|
14475
|
+
).option("--instance <name>", "Label for the report (defaults to the basename of --instance-dir)").option(
|
|
14476
|
+
"--instance-dir <dir>",
|
|
14477
|
+
"REQUIRED: the real instance tree to check adoption against (oursDir) \u2014 no self-check default exists (exits 2, cannot-tell, when omitted)"
|
|
14478
|
+
).option(
|
|
14479
|
+
"--theirs-dir <dir>",
|
|
14480
|
+
"Template tree that ships the channel (theirsDir); defaults to this repo root"
|
|
14481
|
+
).action(async (opts) => {
|
|
14482
|
+
await runInstanceAdoptionCheck(opts);
|
|
14483
|
+
});
|
|
14411
14484
|
checkCommand.command("cognito-invite-template").description(
|
|
14412
14485
|
"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"
|
|
14413
14486
|
).action(async () => {
|
|
@@ -14474,8 +14547,8 @@ function rawArgsAfter(subcommand) {
|
|
|
14474
14547
|
}
|
|
14475
14548
|
|
|
14476
14549
|
// src/commands/doctor.ts
|
|
14477
|
-
import { existsSync as
|
|
14478
|
-
import { join as
|
|
14550
|
+
import { existsSync as existsSync52, readFileSync as readFileSync44 } from "fs";
|
|
14551
|
+
import { join as join61, resolve as resolve20 } from "path";
|
|
14479
14552
|
import chalk21 from "chalk";
|
|
14480
14553
|
import { Command as Command25 } from "commander";
|
|
14481
14554
|
|
|
@@ -14654,8 +14727,8 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
14654
14727
|
return runDoctorChecks(facts);
|
|
14655
14728
|
}
|
|
14656
14729
|
function readLocalCoreVersion(cwd) {
|
|
14657
|
-
const path =
|
|
14658
|
-
if (!
|
|
14730
|
+
const path = join61(cwd, INSTANCE_CORE_FILE);
|
|
14731
|
+
if (!existsSync52(path)) return null;
|
|
14659
14732
|
try {
|
|
14660
14733
|
return extractVersionField(readFileSync44(path, "utf8"));
|
|
14661
14734
|
} catch {
|
|
@@ -14677,8 +14750,8 @@ function extractVersionField(contents) {
|
|
|
14677
14750
|
return match?.[1] ?? null;
|
|
14678
14751
|
}
|
|
14679
14752
|
function readFossil(cwd) {
|
|
14680
|
-
const path =
|
|
14681
|
-
if (!
|
|
14753
|
+
const path = join61(cwd, CORE_VERSION_FILE);
|
|
14754
|
+
if (!existsSync52(path)) return null;
|
|
14682
14755
|
try {
|
|
14683
14756
|
const value = readFileSync44(path, "utf8").trim();
|
|
14684
14757
|
return value === "" ? null : value;
|
|
@@ -15129,13 +15202,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
15129
15202
|
import { Command as Command27 } from "commander";
|
|
15130
15203
|
|
|
15131
15204
|
// src/lib/packaged-scripts.ts
|
|
15132
|
-
import { existsSync as
|
|
15133
|
-
import { dirname as dirname11, join as
|
|
15205
|
+
import { existsSync as existsSync53 } from "fs";
|
|
15206
|
+
import { dirname as dirname11, join as join62 } from "path";
|
|
15134
15207
|
function findPackagedScript(startDir, relativePath) {
|
|
15135
15208
|
let dir = startDir;
|
|
15136
15209
|
for (; ; ) {
|
|
15137
|
-
const candidate =
|
|
15138
|
-
if (
|
|
15210
|
+
const candidate = join62(dir, relativePath);
|
|
15211
|
+
if (existsSync53(candidate)) return candidate;
|
|
15139
15212
|
const parent = dirname11(dir);
|
|
15140
15213
|
if (parent === dir) return null;
|
|
15141
15214
|
dir = parent;
|