@biffo/cli 0.283.4 → 0.284.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/_skeletons/plugin-template/AGENTS.md +18 -3
- package/_skeletons/sibling-template/AGENTS.md +18 -3
- package/dist/index.js +323 -172
- package/package.json +1 -1
- package/scripts/claim.sh +119 -9
package/dist/index.js
CHANGED
|
@@ -10782,14 +10782,160 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
|
|
|
10782
10782
|
console.log(`\u2713 branch-protection guard: ${owner}/${repo} (${audited.join(", ")}) OK`);
|
|
10783
10783
|
}
|
|
10784
10784
|
|
|
10785
|
-
// src/scripts/check-
|
|
10786
|
-
import { existsSync as existsSync39 } from "fs";
|
|
10787
|
-
import { join as join41, relative as relative8 } from "path";
|
|
10785
|
+
// src/scripts/check-claim-invocation.ts
|
|
10788
10786
|
import { execa as execa10 } from "execa";
|
|
10789
10787
|
|
|
10790
|
-
// src/lib/
|
|
10791
|
-
import {
|
|
10788
|
+
// src/lib/claim-invocation-parity.ts
|
|
10789
|
+
import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync16 } from "fs";
|
|
10792
10790
|
import { join as join40 } from "path";
|
|
10791
|
+
function distributedAgentsDocs(root) {
|
|
10792
|
+
const docs = [];
|
|
10793
|
+
const own = join40(root, "AGENTS.md");
|
|
10794
|
+
if (existsSync39(own)) docs.push({ path: "AGENTS.md", text: readFileSync29(own, "utf8") });
|
|
10795
|
+
const skeletons = join40(root, "_skeletons");
|
|
10796
|
+
if (existsSync39(skeletons)) {
|
|
10797
|
+
for (const name of readdirSync16(skeletons).sort()) {
|
|
10798
|
+
const abs = join40(skeletons, name, "AGENTS.md");
|
|
10799
|
+
if (!existsSync39(abs)) continue;
|
|
10800
|
+
docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync29(abs, "utf8") });
|
|
10801
|
+
}
|
|
10802
|
+
}
|
|
10803
|
+
return docs;
|
|
10804
|
+
}
|
|
10805
|
+
function isClaimInvocation(line) {
|
|
10806
|
+
return /\b(?:biffo\.sh|claim\.sh)\s+claim\b|\bclaim\.sh\s+\d|\bclaim\s+<issue-number>/.test(line);
|
|
10807
|
+
}
|
|
10808
|
+
function claimBlock(text) {
|
|
10809
|
+
const lines = text.split("\n");
|
|
10810
|
+
const out = [];
|
|
10811
|
+
let fenced = false;
|
|
10812
|
+
for (const raw of lines) {
|
|
10813
|
+
if (/^\s*```/.test(raw)) {
|
|
10814
|
+
fenced = !fenced;
|
|
10815
|
+
continue;
|
|
10816
|
+
}
|
|
10817
|
+
if (!fenced) continue;
|
|
10818
|
+
if (isClaimInvocation(raw)) out.push(raw.trimEnd());
|
|
10819
|
+
}
|
|
10820
|
+
return out;
|
|
10821
|
+
}
|
|
10822
|
+
function claimInvocations(text) {
|
|
10823
|
+
const found = [];
|
|
10824
|
+
let fenced = false;
|
|
10825
|
+
for (const raw of text.split("\n")) {
|
|
10826
|
+
if (/^\s*```/.test(raw)) {
|
|
10827
|
+
fenced = !fenced;
|
|
10828
|
+
continue;
|
|
10829
|
+
}
|
|
10830
|
+
if (fenced) {
|
|
10831
|
+
if (isClaimInvocation(raw)) found.push(raw.trimEnd());
|
|
10832
|
+
continue;
|
|
10833
|
+
}
|
|
10834
|
+
for (const match of raw.matchAll(/`([^`]+)`/g)) {
|
|
10835
|
+
const span = match[1];
|
|
10836
|
+
if (span !== void 0 && isClaimInvocation(span)) found.push(span.trim());
|
|
10837
|
+
}
|
|
10838
|
+
}
|
|
10839
|
+
return found;
|
|
10840
|
+
}
|
|
10841
|
+
function isTokened(invocation) {
|
|
10842
|
+
return /--as\b|--release\b|--guard\b/.test(invocation);
|
|
10843
|
+
}
|
|
10844
|
+
function auditClaimInvocationParity(docs) {
|
|
10845
|
+
const violations = [];
|
|
10846
|
+
if (docs.length === 0) {
|
|
10847
|
+
return [
|
|
10848
|
+
{
|
|
10849
|
+
rule: "no-copies",
|
|
10850
|
+
path: "(none)",
|
|
10851
|
+
detail: "no distributed AGENTS.md found \u2014 a guard with an empty input set passes against anything"
|
|
10852
|
+
}
|
|
10853
|
+
];
|
|
10854
|
+
}
|
|
10855
|
+
const [canonical, ...rest] = docs;
|
|
10856
|
+
const canonicalBlock = claimBlock(canonical.text);
|
|
10857
|
+
if (canonicalBlock.length === 0) {
|
|
10858
|
+
violations.push({
|
|
10859
|
+
rule: "missing-form",
|
|
10860
|
+
path: canonical.path,
|
|
10861
|
+
detail: "documents no claim invocation at all"
|
|
10862
|
+
});
|
|
10863
|
+
}
|
|
10864
|
+
for (const doc of rest) {
|
|
10865
|
+
const block = claimBlock(doc.text);
|
|
10866
|
+
if (block.join("\n") !== canonicalBlock.join("\n")) {
|
|
10867
|
+
violations.push({
|
|
10868
|
+
rule: "block-drift",
|
|
10869
|
+
path: doc.path,
|
|
10870
|
+
detail: `claim block differs from ${canonical.path}
|
|
10871
|
+
${canonical.path}:
|
|
10872
|
+
${canonicalBlock.map((l) => ` ${l}`).join("\n")}
|
|
10873
|
+
${doc.path}:
|
|
10874
|
+
${block.map((l) => ` ${l}`).join("\n")}`
|
|
10875
|
+
});
|
|
10876
|
+
}
|
|
10877
|
+
}
|
|
10878
|
+
for (const doc of docs) {
|
|
10879
|
+
const invocations = claimInvocations(doc.text);
|
|
10880
|
+
for (const invocation of invocations) {
|
|
10881
|
+
if (!isTokened(invocation)) {
|
|
10882
|
+
violations.push({
|
|
10883
|
+
rule: "untokened-form",
|
|
10884
|
+
path: doc.path,
|
|
10885
|
+
detail: `documents a claim with no --as token: ${invocation.trim()}`
|
|
10886
|
+
});
|
|
10887
|
+
}
|
|
10888
|
+
}
|
|
10889
|
+
if (!invocations.some((i) => /--as\b/.test(i))) {
|
|
10890
|
+
violations.push({
|
|
10891
|
+
rule: "missing-form",
|
|
10892
|
+
path: doc.path,
|
|
10893
|
+
detail: "never documents `claim <issue-number> --as <token>`"
|
|
10894
|
+
});
|
|
10895
|
+
}
|
|
10896
|
+
if (!invocations.some((i) => /--release\b/.test(i))) {
|
|
10897
|
+
violations.push({
|
|
10898
|
+
rule: "missing-form",
|
|
10899
|
+
path: doc.path,
|
|
10900
|
+
detail: "never documents `claim <issue-number> --release <token>`"
|
|
10901
|
+
});
|
|
10902
|
+
}
|
|
10903
|
+
}
|
|
10904
|
+
return violations;
|
|
10905
|
+
}
|
|
10906
|
+
function formatParityViolations(violations) {
|
|
10907
|
+
return violations.map((v) => ` [${v.rule}] ${v.path}: ${v.detail}`).join("\n");
|
|
10908
|
+
}
|
|
10909
|
+
|
|
10910
|
+
// src/scripts/check-claim-invocation.ts
|
|
10911
|
+
async function runClaimInvocationCheck() {
|
|
10912
|
+
const root = (await execa10("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
10913
|
+
const docs = distributedAgentsDocs(root);
|
|
10914
|
+
console.log(
|
|
10915
|
+
`audited ${docs.length} distributed AGENTS.md (${docs.map((d) => d.path).join(", ") || "none"}) under ${root}`
|
|
10916
|
+
);
|
|
10917
|
+
const violations = auditClaimInvocationParity(docs);
|
|
10918
|
+
if (violations.length > 0) {
|
|
10919
|
+
console.error("\u2717 Claim-invocation guard: the distributed AGENTS.md copies disagree\n");
|
|
10920
|
+
console.error(formatParityViolations(violations));
|
|
10921
|
+
console.error(
|
|
10922
|
+
"\nEvery copy must document the same invocation, and `--as <token>` is mandatory (#1562). Fix the skeletons too \u2014 they are what satellites receive."
|
|
10923
|
+
);
|
|
10924
|
+
process.exit(1);
|
|
10925
|
+
}
|
|
10926
|
+
console.log(
|
|
10927
|
+
"\u2713 Claim-invocation guard: every distributed AGENTS.md documents the same, tokened, claim"
|
|
10928
|
+
);
|
|
10929
|
+
}
|
|
10930
|
+
|
|
10931
|
+
// src/scripts/check-codeql-suppression.ts
|
|
10932
|
+
import { existsSync as existsSync40 } from "fs";
|
|
10933
|
+
import { join as join42, relative as relative8 } from "path";
|
|
10934
|
+
import { execa as execa11 } from "execa";
|
|
10935
|
+
|
|
10936
|
+
// src/lib/codeql-suppression-guard.ts
|
|
10937
|
+
import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync9 } from "fs";
|
|
10938
|
+
import { join as join41 } from "path";
|
|
10793
10939
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
10794
10940
|
".git",
|
|
10795
10941
|
".worktrees",
|
|
@@ -10815,12 +10961,12 @@ function walkSourceFiles(root) {
|
|
|
10815
10961
|
const walk2 = (dir) => {
|
|
10816
10962
|
let entries;
|
|
10817
10963
|
try {
|
|
10818
|
-
entries =
|
|
10964
|
+
entries = readdirSync17(dir);
|
|
10819
10965
|
} catch {
|
|
10820
10966
|
return;
|
|
10821
10967
|
}
|
|
10822
10968
|
for (const entry of entries) {
|
|
10823
|
-
const p =
|
|
10969
|
+
const p = join41(dir, entry);
|
|
10824
10970
|
let st;
|
|
10825
10971
|
try {
|
|
10826
10972
|
st = statSync9(p);
|
|
@@ -10846,7 +10992,7 @@ function countSourceFiles(root) {
|
|
|
10846
10992
|
function sweepCodeqlSuppressionComments(root) {
|
|
10847
10993
|
const hits = [];
|
|
10848
10994
|
for (const path of walkSourceFiles(root)) {
|
|
10849
|
-
const text =
|
|
10995
|
+
const text = readFileSync30(path, "utf8");
|
|
10850
10996
|
for (const line of findCodeqlSuppressionComments(text)) {
|
|
10851
10997
|
hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
|
|
10852
10998
|
}
|
|
@@ -10856,9 +11002,9 @@ function sweepCodeqlSuppressionComments(root) {
|
|
|
10856
11002
|
|
|
10857
11003
|
// src/scripts/check-codeql-suppression.ts
|
|
10858
11004
|
async function runCodeqlSuppressionCheck() {
|
|
10859
|
-
const root = (await
|
|
10860
|
-
const scanRoot =
|
|
10861
|
-
if (!
|
|
11005
|
+
const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11006
|
+
const scanRoot = join42(root, "cli", "src");
|
|
11007
|
+
if (!existsSync40(scanRoot)) {
|
|
10862
11008
|
console.log(
|
|
10863
11009
|
"\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
|
|
10864
11010
|
);
|
|
@@ -10882,11 +11028,11 @@ async function runCodeqlSuppressionCheck() {
|
|
|
10882
11028
|
}
|
|
10883
11029
|
|
|
10884
11030
|
// src/scripts/check-cognito-invite-template.ts
|
|
10885
|
-
import { execa as
|
|
11031
|
+
import { execa as execa12 } from "execa";
|
|
10886
11032
|
|
|
10887
11033
|
// src/lib/cognito-invite-template-guard.ts
|
|
10888
|
-
import { readdirSync as
|
|
10889
|
-
import { join as
|
|
11034
|
+
import { readdirSync as readdirSync18, readFileSync as readFileSync31, statSync as statSync10 } from "fs";
|
|
11035
|
+
import { join as join43 } from "path";
|
|
10890
11036
|
var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
|
|
10891
11037
|
var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
|
|
10892
11038
|
var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
|
|
@@ -10963,13 +11109,13 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
10963
11109
|
const walk2 = (dir, relative11) => {
|
|
10964
11110
|
let entries;
|
|
10965
11111
|
try {
|
|
10966
|
-
entries =
|
|
11112
|
+
entries = readdirSync18(dir);
|
|
10967
11113
|
} catch {
|
|
10968
11114
|
return;
|
|
10969
11115
|
}
|
|
10970
11116
|
for (const entry of entries) {
|
|
10971
11117
|
if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
|
|
10972
|
-
const full =
|
|
11118
|
+
const full = join43(dir, entry);
|
|
10973
11119
|
const rel = `${relative11}/${entry}`;
|
|
10974
11120
|
if (statSync10(full).isDirectory()) {
|
|
10975
11121
|
walk2(full, rel);
|
|
@@ -10978,18 +11124,18 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
10978
11124
|
}
|
|
10979
11125
|
}
|
|
10980
11126
|
};
|
|
10981
|
-
walk2(
|
|
11127
|
+
walk2(join43(repoRoot, "modules"), "modules");
|
|
10982
11128
|
return found.sort();
|
|
10983
11129
|
}
|
|
10984
11130
|
function checkCognitoInviteTemplates(repoRoot) {
|
|
10985
11131
|
return findModuleTerraformFiles(repoRoot).flatMap(
|
|
10986
|
-
(file) => checkInviteTemplateSource(file,
|
|
11132
|
+
(file) => checkInviteTemplateSource(file, readFileSync31(join43(repoRoot, file), "utf8"))
|
|
10987
11133
|
);
|
|
10988
11134
|
}
|
|
10989
11135
|
|
|
10990
11136
|
// src/scripts/check-cognito-invite-template.ts
|
|
10991
11137
|
async function runCognitoInviteTemplateCheck() {
|
|
10992
|
-
const root = (await
|
|
11138
|
+
const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
10993
11139
|
const files = findModuleTerraformFiles(root);
|
|
10994
11140
|
console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
|
|
10995
11141
|
if (files.length === 0) {
|
|
@@ -11011,12 +11157,12 @@ async function runCognitoInviteTemplateCheck() {
|
|
|
11011
11157
|
}
|
|
11012
11158
|
|
|
11013
11159
|
// src/scripts/check-core-direct-paths.ts
|
|
11014
|
-
import { join as
|
|
11015
|
-
import { execa as
|
|
11160
|
+
import { join as join45 } from "path";
|
|
11161
|
+
import { execa as execa13 } from "execa";
|
|
11016
11162
|
|
|
11017
11163
|
// src/lib/core-direct-paths-audit.ts
|
|
11018
|
-
import { existsSync as
|
|
11019
|
-
import { join as
|
|
11164
|
+
import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
|
|
11165
|
+
import { join as join44 } from "path";
|
|
11020
11166
|
var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
|
|
11021
11167
|
var API_ROUTE_PREFIX = "/api/v1";
|
|
11022
11168
|
var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
|
|
@@ -11175,12 +11321,12 @@ function walkFiles(root, accept, skipDir) {
|
|
|
11175
11321
|
const walk2 = (dir) => {
|
|
11176
11322
|
let entries;
|
|
11177
11323
|
try {
|
|
11178
|
-
entries =
|
|
11324
|
+
entries = readdirSync19(dir);
|
|
11179
11325
|
} catch {
|
|
11180
11326
|
return;
|
|
11181
11327
|
}
|
|
11182
11328
|
for (const entry of entries) {
|
|
11183
|
-
const p =
|
|
11329
|
+
const p = join44(dir, entry);
|
|
11184
11330
|
let st;
|
|
11185
11331
|
try {
|
|
11186
11332
|
st = statSync11(p);
|
|
@@ -11210,7 +11356,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
|
|
|
11210
11356
|
const extracted = [];
|
|
11211
11357
|
let rawTotal = 0;
|
|
11212
11358
|
for (const file of files) {
|
|
11213
|
-
const text =
|
|
11359
|
+
const text = readFileSync32(file, "utf8");
|
|
11214
11360
|
rawTotal += countRawExternalOccurrences(text, externalBases);
|
|
11215
11361
|
extracted.push(...extractCoreDirectPaths(text, file, externalBases));
|
|
11216
11362
|
}
|
|
@@ -11259,7 +11405,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
|
|
|
11259
11405
|
const prefixSet = /* @__PURE__ */ new Set();
|
|
11260
11406
|
let rawApiRouterCount = 0;
|
|
11261
11407
|
for (const file of files) {
|
|
11262
|
-
const text =
|
|
11408
|
+
const text = readFileSync32(file, "utf8");
|
|
11263
11409
|
const extraction = extractCoreRoutePrefixes(text);
|
|
11264
11410
|
rawApiRouterCount += extraction.rawApiRouterCount;
|
|
11265
11411
|
for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
|
|
@@ -11275,10 +11421,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
|
|
|
11275
11421
|
}
|
|
11276
11422
|
function resolveSiblingCoreSrc(params) {
|
|
11277
11423
|
const { estateDir, sibling } = params;
|
|
11278
|
-
const configPath =
|
|
11424
|
+
const configPath = join44(estateDir, sibling, "biffo.sibling.json");
|
|
11279
11425
|
let raw;
|
|
11280
11426
|
try {
|
|
11281
|
-
raw =
|
|
11427
|
+
raw = readFileSync32(configPath, "utf8");
|
|
11282
11428
|
} catch (err) {
|
|
11283
11429
|
throw new Error(
|
|
11284
11430
|
`cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
|
|
@@ -11298,8 +11444,8 @@ function resolveSiblingCoreSrc(params) {
|
|
|
11298
11444
|
`cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
|
|
11299
11445
|
);
|
|
11300
11446
|
}
|
|
11301
|
-
const coreApiSrcDir =
|
|
11302
|
-
if (!
|
|
11447
|
+
const coreApiSrcDir = join44(estateDir, coreProject, "services", "api", "src");
|
|
11448
|
+
if (!existsSync41(coreApiSrcDir)) {
|
|
11303
11449
|
throw new Error(
|
|
11304
11450
|
`cannot resolve ${sibling}'s core: biffo.sibling.json names core_project "${coreProject}", but ${coreApiSrcDir} does not exist -- the instance is missing from this estate checkout, not merely unmatched. Refusing to silently skip ${sibling} and shrink the audit's denominator.`
|
|
11305
11451
|
);
|
|
@@ -11340,9 +11486,9 @@ function auditSiblingCoreDirectPaths(params) {
|
|
|
11340
11486
|
|
|
11341
11487
|
// src/scripts/check-core-direct-paths.ts
|
|
11342
11488
|
async function runCoreDirectPathsCheck(opts = {}) {
|
|
11343
|
-
const root = (await
|
|
11489
|
+
const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11344
11490
|
const sibling = opts.sibling ?? "sibling-template (self-check)";
|
|
11345
|
-
const frontendSrcDir = opts.frontendSrc ??
|
|
11491
|
+
const frontendSrcDir = opts.frontendSrc ?? join45(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
|
|
11346
11492
|
let coreApiSrcDir;
|
|
11347
11493
|
let coreProject = null;
|
|
11348
11494
|
if (opts.coreSrc) {
|
|
@@ -11358,7 +11504,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
|
|
|
11358
11504
|
coreApiSrcDir = resolution.coreApiSrcDir;
|
|
11359
11505
|
coreProject = resolution.coreProject;
|
|
11360
11506
|
} else {
|
|
11361
|
-
coreApiSrcDir =
|
|
11507
|
+
coreApiSrcDir = join45(root, "services", "api", "src");
|
|
11362
11508
|
}
|
|
11363
11509
|
const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
|
|
11364
11510
|
console.log(
|
|
@@ -11394,7 +11540,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
|
|
|
11394
11540
|
}
|
|
11395
11541
|
|
|
11396
11542
|
// src/scripts/check-core-ownership.ts
|
|
11397
|
-
import { execa as
|
|
11543
|
+
import { execa as execa14 } from "execa";
|
|
11398
11544
|
var BOLD = "\x1B[1m";
|
|
11399
11545
|
var DIM = "\x1B[2m";
|
|
11400
11546
|
var RED = "\x1B[31m";
|
|
@@ -11405,7 +11551,7 @@ async function runOwnershipCheck(argv) {
|
|
|
11405
11551
|
const stagedFlag = args.indexOf("--staged");
|
|
11406
11552
|
const staged = stagedFlag !== -1;
|
|
11407
11553
|
const messageFile = staged ? args[stagedFlag + 1] : void 0;
|
|
11408
|
-
const root = (await
|
|
11554
|
+
const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11409
11555
|
const ownership = classifyRepoOwnership(root);
|
|
11410
11556
|
if (ownership === "template") {
|
|
11411
11557
|
console.log("\u2713 core ownership guard: skipped \u2014 this is the template, which owns these paths.");
|
|
@@ -11421,11 +11567,11 @@ async function runOwnershipCheck(argv) {
|
|
|
11421
11567
|
let deletedFiles = [];
|
|
11422
11568
|
let commitMessage = "";
|
|
11423
11569
|
if (staged) {
|
|
11424
|
-
const { stdout } = await
|
|
11570
|
+
const { stdout } = await execa14("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
11425
11571
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11426
11572
|
if (messageFile) {
|
|
11427
|
-
const { readFileSync:
|
|
11428
|
-
if (
|
|
11573
|
+
const { readFileSync: readFileSync42, existsSync: existsSync51 } = await import("fs");
|
|
11574
|
+
if (existsSync51(messageFile)) commitMessage = readFileSync42(messageFile, "utf8");
|
|
11429
11575
|
}
|
|
11430
11576
|
} else {
|
|
11431
11577
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -11433,18 +11579,18 @@ async function runOwnershipCheck(argv) {
|
|
|
11433
11579
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
11434
11580
|
process.exit(2);
|
|
11435
11581
|
}
|
|
11436
|
-
await
|
|
11437
|
-
const { stdout } = await
|
|
11582
|
+
await execa14("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
|
|
11583
|
+
const { stdout } = await execa14("git", ["diff", "--name-status", `origin/${base}...HEAD`], {
|
|
11438
11584
|
cwd: root
|
|
11439
11585
|
});
|
|
11440
11586
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11441
|
-
const { stdout: log2 } = await
|
|
11587
|
+
const { stdout: log2 } = await execa14("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
|
|
11442
11588
|
cwd: root,
|
|
11443
11589
|
reject: false
|
|
11444
11590
|
});
|
|
11445
11591
|
commitMessage = log2;
|
|
11446
11592
|
}
|
|
11447
|
-
const { stdout: gitBranch } = await
|
|
11593
|
+
const { stdout: gitBranch } = await execa14("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
11448
11594
|
cwd: root,
|
|
11449
11595
|
reject: false
|
|
11450
11596
|
});
|
|
@@ -11526,11 +11672,11 @@ ${BOLD}If the divergence is deliberate${OFF}
|
|
|
11526
11672
|
}
|
|
11527
11673
|
|
|
11528
11674
|
// src/scripts/check-eventbridge-log-permissions.ts
|
|
11529
|
-
import { execa as
|
|
11675
|
+
import { execa as execa15 } from "execa";
|
|
11530
11676
|
|
|
11531
11677
|
// src/lib/eventbridge-log-permission-guard.ts
|
|
11532
|
-
import { readFileSync as
|
|
11533
|
-
import { join as
|
|
11678
|
+
import { readFileSync as readFileSync33, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
|
|
11679
|
+
import { join as join46 } from "path";
|
|
11534
11680
|
var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
|
|
11535
11681
|
var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
|
|
11536
11682
|
var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
|
|
@@ -11602,12 +11748,12 @@ function walkTerraformFiles(root) {
|
|
|
11602
11748
|
const walk2 = (dir) => {
|
|
11603
11749
|
let entries;
|
|
11604
11750
|
try {
|
|
11605
|
-
entries =
|
|
11751
|
+
entries = readdirSync20(dir);
|
|
11606
11752
|
} catch {
|
|
11607
11753
|
return;
|
|
11608
11754
|
}
|
|
11609
11755
|
for (const entry of entries) {
|
|
11610
|
-
const p =
|
|
11756
|
+
const p = join46(dir, entry);
|
|
11611
11757
|
let st;
|
|
11612
11758
|
try {
|
|
11613
11759
|
st = statSync12(p);
|
|
@@ -11650,7 +11796,7 @@ function auditEventBridgeLogPermissions(root) {
|
|
|
11650
11796
|
let rawEventTargetCount = 0;
|
|
11651
11797
|
let rawLogPolicyCount = 0;
|
|
11652
11798
|
for (const file of files) {
|
|
11653
|
-
const text =
|
|
11799
|
+
const text = readFileSync33(file, "utf8");
|
|
11654
11800
|
rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
|
|
11655
11801
|
rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
|
|
11656
11802
|
eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
|
|
@@ -11703,7 +11849,7 @@ function auditEventBridgeLogPermissions(root) {
|
|
|
11703
11849
|
|
|
11704
11850
|
// src/scripts/check-eventbridge-log-permissions.ts
|
|
11705
11851
|
async function runEventBridgeLogPermissionCheck() {
|
|
11706
|
-
const root = (await
|
|
11852
|
+
const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11707
11853
|
let report;
|
|
11708
11854
|
try {
|
|
11709
11855
|
report = auditEventBridgeLogPermissions(root);
|
|
@@ -11740,15 +11886,15 @@ async function runEventBridgeLogPermissionCheck() {
|
|
|
11740
11886
|
}
|
|
11741
11887
|
|
|
11742
11888
|
// src/scripts/check-lambda-output.ts
|
|
11743
|
-
import { execa as
|
|
11889
|
+
import { execa as execa16 } from "execa";
|
|
11744
11890
|
|
|
11745
11891
|
// src/lib/lambda-output-guard.ts
|
|
11746
|
-
import { readFileSync as
|
|
11747
|
-
import { join as
|
|
11892
|
+
import { readFileSync as readFileSync35 } from "fs";
|
|
11893
|
+
import { join as join48 } from "path";
|
|
11748
11894
|
|
|
11749
11895
|
// src/lib/terraform-input-guard.ts
|
|
11750
|
-
import { existsSync as
|
|
11751
|
-
import { join as
|
|
11896
|
+
import { existsSync as existsSync42, readdirSync as readdirSync21, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
|
|
11897
|
+
import { join as join47 } from "path";
|
|
11752
11898
|
var GUARDED_SUBCOMMANDS = [
|
|
11753
11899
|
"init",
|
|
11754
11900
|
"plan",
|
|
@@ -11762,18 +11908,18 @@ function stripComments2(source) {
|
|
|
11762
11908
|
return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
|
|
11763
11909
|
}
|
|
11764
11910
|
function vendoredPluginServiceDirs(repoRoot) {
|
|
11765
|
-
const servicesDir =
|
|
11911
|
+
const servicesDir = join47(repoRoot, "services");
|
|
11766
11912
|
const result = /* @__PURE__ */ new Set();
|
|
11767
11913
|
let entries;
|
|
11768
11914
|
try {
|
|
11769
|
-
entries =
|
|
11915
|
+
entries = readdirSync21(servicesDir);
|
|
11770
11916
|
} catch {
|
|
11771
11917
|
return result;
|
|
11772
11918
|
}
|
|
11773
11919
|
for (const entry of entries) {
|
|
11774
|
-
const full =
|
|
11775
|
-
if (!
|
|
11776
|
-
if (
|
|
11920
|
+
const full = join47(servicesDir, entry);
|
|
11921
|
+
if (!existsSync42(full) || !statSync13(full).isDirectory()) continue;
|
|
11922
|
+
if (existsSync42(join47(full, "biffo.plugin.json"))) {
|
|
11777
11923
|
result.add(entry);
|
|
11778
11924
|
}
|
|
11779
11925
|
}
|
|
@@ -11785,7 +11931,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
11785
11931
|
const walk2 = (dir, relative11) => {
|
|
11786
11932
|
let entries;
|
|
11787
11933
|
try {
|
|
11788
|
-
entries =
|
|
11934
|
+
entries = readdirSync21(dir);
|
|
11789
11935
|
} catch {
|
|
11790
11936
|
return;
|
|
11791
11937
|
}
|
|
@@ -11794,7 +11940,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
11794
11940
|
if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
|
|
11795
11941
|
continue;
|
|
11796
11942
|
}
|
|
11797
|
-
const full =
|
|
11943
|
+
const full = join47(dir, entry);
|
|
11798
11944
|
const rel = relative11 ? `${relative11}/${entry}` : entry;
|
|
11799
11945
|
if (statSync13(full).isDirectory()) {
|
|
11800
11946
|
walk2(full, rel);
|
|
@@ -11838,7 +11984,7 @@ function checkWorkflowSource(file, rawSource) {
|
|
|
11838
11984
|
}
|
|
11839
11985
|
function checkTerraformInput(repoRoot) {
|
|
11840
11986
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
11841
|
-
(file) => checkWorkflowSource(file,
|
|
11987
|
+
(file) => checkWorkflowSource(file, readFileSync34(join47(repoRoot, file), "utf8"))
|
|
11842
11988
|
);
|
|
11843
11989
|
}
|
|
11844
11990
|
|
|
@@ -11896,13 +12042,13 @@ function checkWorkflowSource2(file, rawSource) {
|
|
|
11896
12042
|
}
|
|
11897
12043
|
function checkLambdaOutput(repoRoot) {
|
|
11898
12044
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
11899
|
-
(file) => checkWorkflowSource2(file,
|
|
12045
|
+
(file) => checkWorkflowSource2(file, readFileSync35(join48(repoRoot, file), "utf8"))
|
|
11900
12046
|
);
|
|
11901
12047
|
}
|
|
11902
12048
|
|
|
11903
12049
|
// src/scripts/check-lambda-output.ts
|
|
11904
12050
|
async function runLambdaOutputCheck() {
|
|
11905
|
-
const root = (await
|
|
12051
|
+
const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11906
12052
|
const files = findWorkflowFiles(root);
|
|
11907
12053
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
11908
12054
|
if (files.length === 0) {
|
|
@@ -11924,9 +12070,9 @@ async function runLambdaOutputCheck() {
|
|
|
11924
12070
|
}
|
|
11925
12071
|
|
|
11926
12072
|
// src/scripts/check-pipe-trap.ts
|
|
11927
|
-
import { readFileSync as
|
|
11928
|
-
import { join as
|
|
11929
|
-
import { execa as
|
|
12073
|
+
import { readFileSync as readFileSync36, readdirSync as readdirSync22 } from "fs";
|
|
12074
|
+
import { join as join49, relative as relative9 } from "path";
|
|
12075
|
+
import { execa as execa17 } from "execa";
|
|
11930
12076
|
|
|
11931
12077
|
// src/lib/pipe-trap-guard.ts
|
|
11932
12078
|
var STATUS_BEARING = [
|
|
@@ -12022,23 +12168,23 @@ function findPipeTraps(source) {
|
|
|
12022
12168
|
function shellFiles(root) {
|
|
12023
12169
|
const out = [];
|
|
12024
12170
|
for (const dir of ["scripts", ".githooks"]) {
|
|
12025
|
-
const full =
|
|
12171
|
+
const full = join49(root, dir);
|
|
12026
12172
|
let entries;
|
|
12027
12173
|
try {
|
|
12028
|
-
entries =
|
|
12174
|
+
entries = readdirSync22(full, { withFileTypes: true });
|
|
12029
12175
|
} catch {
|
|
12030
12176
|
continue;
|
|
12031
12177
|
}
|
|
12032
12178
|
for (const entry of entries) {
|
|
12033
12179
|
if (!entry.isFile()) continue;
|
|
12034
12180
|
if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
|
|
12035
|
-
out.push(
|
|
12181
|
+
out.push(join49(full, entry.name));
|
|
12036
12182
|
}
|
|
12037
12183
|
}
|
|
12038
12184
|
return out;
|
|
12039
12185
|
}
|
|
12040
12186
|
async function runPipeTrapCheck() {
|
|
12041
|
-
const root = (await
|
|
12187
|
+
const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12042
12188
|
const files = shellFiles(root);
|
|
12043
12189
|
console.log(`audited ${files.length} shell file(s) under scripts/ and .githooks/ under ${root}`);
|
|
12044
12190
|
if (files.length === 0) {
|
|
@@ -12048,7 +12194,7 @@ async function runPipeTrapCheck() {
|
|
|
12048
12194
|
process.exit(1);
|
|
12049
12195
|
}
|
|
12050
12196
|
const findings = files.flatMap(
|
|
12051
|
-
(file) => findPipeTraps(
|
|
12197
|
+
(file) => findPipeTraps(readFileSync36(file, "utf8")).map(
|
|
12052
12198
|
(t) => `${relative9(root, file)}:${t.line} ${t.text}
|
|
12053
12199
|
${t.reason}`
|
|
12054
12200
|
)
|
|
@@ -12065,11 +12211,11 @@ async function runPipeTrapCheck() {
|
|
|
12065
12211
|
}
|
|
12066
12212
|
|
|
12067
12213
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
12068
|
-
import { execa as
|
|
12214
|
+
import { execa as execa18 } from "execa";
|
|
12069
12215
|
|
|
12070
12216
|
// src/lib/plugin-allowlist-convention.ts
|
|
12071
|
-
import { readFileSync as
|
|
12072
|
-
import { join as
|
|
12217
|
+
import { readFileSync as readFileSync37 } from "fs";
|
|
12218
|
+
import { join as join50 } from "path";
|
|
12073
12219
|
var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
|
|
12074
12220
|
var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
|
|
12075
12221
|
var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
|
|
@@ -12080,7 +12226,7 @@ var PLUGIN = "<plugin>";
|
|
|
12080
12226
|
var ACCOUNT = "<account>";
|
|
12081
12227
|
function read(repoRoot, relative11) {
|
|
12082
12228
|
try {
|
|
12083
|
-
return
|
|
12229
|
+
return readFileSync37(join50(repoRoot, relative11), "utf8");
|
|
12084
12230
|
} catch {
|
|
12085
12231
|
throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
|
|
12086
12232
|
}
|
|
@@ -12176,7 +12322,7 @@ Plugins would be rejected by require_service_principal (ADR-0009). Fix the glob,
|
|
|
12176
12322
|
|
|
12177
12323
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
12178
12324
|
async function runPluginAllowlistConventionCheck() {
|
|
12179
|
-
const root = (await
|
|
12325
|
+
const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12180
12326
|
let violations;
|
|
12181
12327
|
try {
|
|
12182
12328
|
violations = checkAllowlistConvention(root);
|
|
@@ -12201,34 +12347,34 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
12201
12347
|
}
|
|
12202
12348
|
|
|
12203
12349
|
// src/scripts/check-plugin-collisions.ts
|
|
12204
|
-
import { existsSync as
|
|
12205
|
-
import { join as
|
|
12206
|
-
import { execa as
|
|
12350
|
+
import { existsSync as existsSync44 } from "fs";
|
|
12351
|
+
import { join as join52 } from "path";
|
|
12352
|
+
import { execa as execa19 } from "execa";
|
|
12207
12353
|
|
|
12208
12354
|
// src/lib/plugin-collision-guard.ts
|
|
12209
|
-
import { existsSync as
|
|
12210
|
-
import { join as
|
|
12355
|
+
import { existsSync as existsSync43, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
|
|
12356
|
+
import { join as join51 } from "path";
|
|
12211
12357
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
12212
12358
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
12213
12359
|
function subdirectories(dir) {
|
|
12214
|
-
if (!
|
|
12215
|
-
return
|
|
12360
|
+
if (!existsSync43(dir)) return [];
|
|
12361
|
+
return readdirSync23(dir).filter((entry) => {
|
|
12216
12362
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
12217
12363
|
try {
|
|
12218
|
-
return statSync14(
|
|
12364
|
+
return statSync14(join51(dir, entry)).isDirectory();
|
|
12219
12365
|
} catch {
|
|
12220
12366
|
return false;
|
|
12221
12367
|
}
|
|
12222
12368
|
});
|
|
12223
12369
|
}
|
|
12224
12370
|
function regularPackagesOf(pluginDir2) {
|
|
12225
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
12371
|
+
return subdirectories(pluginDir2).filter((name) => existsSync43(join51(pluginDir2, name, "__init__.py"))).sort();
|
|
12226
12372
|
}
|
|
12227
12373
|
function bareTestModulesOf(pluginDir2) {
|
|
12228
|
-
const testsDir =
|
|
12229
|
-
if (!
|
|
12230
|
-
if (
|
|
12231
|
-
return
|
|
12374
|
+
const testsDir = join51(pluginDir2, "tests");
|
|
12375
|
+
if (!existsSync43(testsDir)) return [];
|
|
12376
|
+
if (existsSync43(join51(testsDir, "__init__.py"))) return [];
|
|
12377
|
+
return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
|
|
12232
12378
|
}
|
|
12233
12379
|
function findCollisions(servicesDir, pluginDirs) {
|
|
12234
12380
|
const plugins = (pluginDirs ?? subdirectories(servicesDir)).filter((name) => !name.startsWith("_")).filter((name) => name !== "api").sort();
|
|
@@ -12236,7 +12382,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
12236
12382
|
const gather = (kind, namesOf) => {
|
|
12237
12383
|
const claims = /* @__PURE__ */ new Map();
|
|
12238
12384
|
for (const plugin of plugins) {
|
|
12239
|
-
for (const name of namesOf(
|
|
12385
|
+
for (const name of namesOf(join51(servicesDir, plugin))) {
|
|
12240
12386
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
12241
12387
|
}
|
|
12242
12388
|
}
|
|
@@ -12273,9 +12419,9 @@ function formatCollisions(collisions) {
|
|
|
12273
12419
|
|
|
12274
12420
|
// src/scripts/check-plugin-collisions.ts
|
|
12275
12421
|
async function runPluginCollisionCheck() {
|
|
12276
|
-
const root = (await
|
|
12277
|
-
const servicesDir =
|
|
12278
|
-
if (!
|
|
12422
|
+
const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12423
|
+
const servicesDir = join52(root, "services");
|
|
12424
|
+
if (!existsSync44(servicesDir)) {
|
|
12279
12425
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
12280
12426
|
return;
|
|
12281
12427
|
}
|
|
@@ -12292,11 +12438,11 @@ async function runPluginCollisionCheck() {
|
|
|
12292
12438
|
}
|
|
12293
12439
|
|
|
12294
12440
|
// src/scripts/check-plugin-terraform.ts
|
|
12295
|
-
import { execa as
|
|
12441
|
+
import { execa as execa20 } from "execa";
|
|
12296
12442
|
|
|
12297
12443
|
// src/lib/plugin-terraform-guard.ts
|
|
12298
|
-
import { existsSync as
|
|
12299
|
-
import { dirname as dirname10, join as
|
|
12444
|
+
import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24 } from "fs";
|
|
12445
|
+
import { dirname as dirname10, join as join53, relative as relative10, sep as sep4 } from "path";
|
|
12300
12446
|
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
12301
12447
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
12302
12448
|
function findPluginManifests(root) {
|
|
@@ -12304,16 +12450,16 @@ function findPluginManifests(root) {
|
|
|
12304
12450
|
const walk2 = (dir) => {
|
|
12305
12451
|
let entries;
|
|
12306
12452
|
try {
|
|
12307
|
-
entries =
|
|
12453
|
+
entries = readdirSync24(dir, { withFileTypes: true });
|
|
12308
12454
|
} catch {
|
|
12309
12455
|
return;
|
|
12310
12456
|
}
|
|
12311
12457
|
for (const entry of entries) {
|
|
12312
12458
|
if (entry.isDirectory()) {
|
|
12313
12459
|
if (SKIP_DIRS3.has(entry.name)) continue;
|
|
12314
|
-
walk2(
|
|
12460
|
+
walk2(join53(dir, entry.name));
|
|
12315
12461
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
12316
|
-
found.push(relative10(root,
|
|
12462
|
+
found.push(relative10(root, join53(dir, entry.name)).split(sep4).join("/"));
|
|
12317
12463
|
}
|
|
12318
12464
|
}
|
|
12319
12465
|
};
|
|
@@ -12323,7 +12469,7 @@ function findPluginManifests(root) {
|
|
|
12323
12469
|
function readSubscriptions(absManifestPath) {
|
|
12324
12470
|
let parsed;
|
|
12325
12471
|
try {
|
|
12326
|
-
parsed = JSON.parse(
|
|
12472
|
+
parsed = JSON.parse(readFileSync38(absManifestPath, "utf8"));
|
|
12327
12473
|
} catch {
|
|
12328
12474
|
return null;
|
|
12329
12475
|
}
|
|
@@ -12338,14 +12484,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
12338
12484
|
}
|
|
12339
12485
|
function checkPluginTerraform(root) {
|
|
12340
12486
|
const violations = [];
|
|
12341
|
-
const coreManifest =
|
|
12487
|
+
const coreManifest = existsSync45(join53(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
12342
12488
|
for (const manifest of findPluginManifests(root)) {
|
|
12343
12489
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
12344
|
-
const absManifest =
|
|
12490
|
+
const absManifest = join53(root, manifest);
|
|
12345
12491
|
const subscriptions = readSubscriptions(absManifest);
|
|
12346
12492
|
if (subscriptions === null) continue;
|
|
12347
12493
|
const pluginDir2 = dirname10(absManifest);
|
|
12348
|
-
if (
|
|
12494
|
+
if (existsSync45(join53(pluginDir2, "terraform"))) continue;
|
|
12349
12495
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
12350
12496
|
violations.push({
|
|
12351
12497
|
manifest,
|
|
@@ -12365,7 +12511,7 @@ function formatViolations(violations) {
|
|
|
12365
12511
|
|
|
12366
12512
|
// src/scripts/check-plugin-terraform.ts
|
|
12367
12513
|
async function runPluginTerraformCheck() {
|
|
12368
|
-
const root = (await
|
|
12514
|
+
const root = (await execa20("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12369
12515
|
const violations = checkPluginTerraform(root);
|
|
12370
12516
|
if (violations.length > 0) {
|
|
12371
12517
|
console.error("\u2717 plugin Terraform guard: event subscriptions with no infrastructure\n");
|
|
@@ -12376,13 +12522,13 @@ async function runPluginTerraformCheck() {
|
|
|
12376
12522
|
}
|
|
12377
12523
|
|
|
12378
12524
|
// src/scripts/check-plugin-tool-supply.ts
|
|
12379
|
-
import { existsSync as
|
|
12380
|
-
import { join as
|
|
12381
|
-
import { execa as
|
|
12525
|
+
import { existsSync as existsSync47 } from "fs";
|
|
12526
|
+
import { join as join55 } from "path";
|
|
12527
|
+
import { execa as execa21 } from "execa";
|
|
12382
12528
|
|
|
12383
12529
|
// src/lib/plugin-tool-supply-audit.ts
|
|
12384
|
-
import { existsSync as
|
|
12385
|
-
import { join as
|
|
12530
|
+
import { existsSync as existsSync46, readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
|
|
12531
|
+
import { join as join54 } from "path";
|
|
12386
12532
|
|
|
12387
12533
|
// src/lib/openrouter-model-snapshot.ts
|
|
12388
12534
|
var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
|
|
@@ -12793,13 +12939,13 @@ var OPENROUTER_MODEL_IDS = [
|
|
|
12793
12939
|
function listDirs(root) {
|
|
12794
12940
|
let entries;
|
|
12795
12941
|
try {
|
|
12796
|
-
entries =
|
|
12942
|
+
entries = readdirSync25(root);
|
|
12797
12943
|
} catch {
|
|
12798
12944
|
return [];
|
|
12799
12945
|
}
|
|
12800
12946
|
return entries.filter((e) => {
|
|
12801
12947
|
try {
|
|
12802
|
-
return statSync15(
|
|
12948
|
+
return statSync15(join54(root, e)).isDirectory();
|
|
12803
12949
|
} catch {
|
|
12804
12950
|
return false;
|
|
12805
12951
|
}
|
|
@@ -12810,12 +12956,12 @@ function walkFiles2(root, accept, skipDir) {
|
|
|
12810
12956
|
const walk2 = (dir) => {
|
|
12811
12957
|
let entries;
|
|
12812
12958
|
try {
|
|
12813
|
-
entries =
|
|
12959
|
+
entries = readdirSync25(dir);
|
|
12814
12960
|
} catch {
|
|
12815
12961
|
return;
|
|
12816
12962
|
}
|
|
12817
12963
|
for (const entry of entries) {
|
|
12818
|
-
const p =
|
|
12964
|
+
const p = join54(dir, entry);
|
|
12819
12965
|
let st;
|
|
12820
12966
|
try {
|
|
12821
12967
|
st = statSync15(p);
|
|
@@ -12841,14 +12987,14 @@ function pluginPythonFiles(pluginDir2) {
|
|
|
12841
12987
|
);
|
|
12842
12988
|
}
|
|
12843
12989
|
function pluginTerraformFiles(pluginDir2) {
|
|
12844
|
-
const tfDir =
|
|
12990
|
+
const tfDir = join54(pluginDir2, "terraform");
|
|
12845
12991
|
let entries;
|
|
12846
12992
|
try {
|
|
12847
|
-
entries =
|
|
12993
|
+
entries = readdirSync25(tfDir);
|
|
12848
12994
|
} catch {
|
|
12849
12995
|
return [];
|
|
12850
12996
|
}
|
|
12851
|
-
return entries.filter((e) => e.endsWith(".tf")).map((e) =>
|
|
12997
|
+
return entries.filter((e) => e.endsWith(".tf")).map((e) => join54(tfDir, e)).sort();
|
|
12852
12998
|
}
|
|
12853
12999
|
function extractManifestTools(manifestText) {
|
|
12854
13000
|
let parsed;
|
|
@@ -13100,8 +13246,8 @@ function isSnapshotStale(fetchedAt, now) {
|
|
|
13100
13246
|
function normalizeModelId(id) {
|
|
13101
13247
|
return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
|
|
13102
13248
|
}
|
|
13103
|
-
var CONFIG_PY_PATH =
|
|
13104
|
-
var ORCHESTRATION_SCHEMA_PATH =
|
|
13249
|
+
var CONFIG_PY_PATH = join54("services", "api", "src", "api", "config.py");
|
|
13250
|
+
var ORCHESTRATION_SCHEMA_PATH = join54(
|
|
13105
13251
|
"services",
|
|
13106
13252
|
"api",
|
|
13107
13253
|
"src",
|
|
@@ -13113,10 +13259,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13113
13259
|
const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
|
|
13114
13260
|
const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
|
|
13115
13261
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
13116
|
-
const configPath =
|
|
13117
|
-
const orchestrationPath =
|
|
13118
|
-
const configMissing = !
|
|
13119
|
-
const orchestrationSchemaMissing = !
|
|
13262
|
+
const configPath = join54(repoRoot, CONFIG_PY_PATH);
|
|
13263
|
+
const orchestrationPath = join54(repoRoot, ORCHESTRATION_SCHEMA_PATH);
|
|
13264
|
+
const configMissing = !existsSync46(configPath);
|
|
13265
|
+
const orchestrationSchemaMissing = !existsSync46(orchestrationPath);
|
|
13120
13266
|
const knownSet = new Set(knownModelIds);
|
|
13121
13267
|
const snapshotEmpty = knownModelIds.length === 0;
|
|
13122
13268
|
const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
|
|
@@ -13134,13 +13280,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13134
13280
|
};
|
|
13135
13281
|
let settingsBlind = false;
|
|
13136
13282
|
if (!configMissing) {
|
|
13137
|
-
const settingsFields = extractSettingsModelFields(
|
|
13283
|
+
const settingsFields = extractSettingsModelFields(readFileSync39(configPath, "utf8"));
|
|
13138
13284
|
if (settingsFields.length === 0) settingsBlind = true;
|
|
13139
13285
|
for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
|
|
13140
13286
|
}
|
|
13141
13287
|
let curatedFieldsBlind = false;
|
|
13142
13288
|
if (!orchestrationSchemaMissing) {
|
|
13143
|
-
const curated = extractCuratedModelFields(
|
|
13289
|
+
const curated = extractCuratedModelFields(readFileSync39(orchestrationPath, "utf8"));
|
|
13144
13290
|
if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
|
|
13145
13291
|
curatedFieldsBlind = true;
|
|
13146
13292
|
}
|
|
@@ -13187,7 +13333,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13187
13333
|
function discoverPluginDirs(pluginsRoot) {
|
|
13188
13334
|
return listDirs(pluginsRoot).filter((name) => {
|
|
13189
13335
|
try {
|
|
13190
|
-
return statSync15(
|
|
13336
|
+
return statSync15(join54(pluginsRoot, name, "biffo.plugin.json")).isFile();
|
|
13191
13337
|
} catch {
|
|
13192
13338
|
return false;
|
|
13193
13339
|
}
|
|
@@ -13200,8 +13346,8 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13200
13346
|
let terraformBlind = false;
|
|
13201
13347
|
let totalDeclaredTools = 0;
|
|
13202
13348
|
for (const name of pluginNames) {
|
|
13203
|
-
const pluginDir2 =
|
|
13204
|
-
const manifestText =
|
|
13349
|
+
const pluginDir2 = join54(pluginsRoot, name);
|
|
13350
|
+
const manifestText = readFileSync39(join54(pluginDir2, "biffo.plugin.json"), "utf8");
|
|
13205
13351
|
const manifest = extractManifestTools(manifestText);
|
|
13206
13352
|
if (manifest.parseError) {
|
|
13207
13353
|
findings.push({
|
|
@@ -13219,13 +13365,13 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13219
13365
|
totalDeclaredTools += manifest.tools.length;
|
|
13220
13366
|
const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
|
|
13221
13367
|
file: f,
|
|
13222
|
-
text:
|
|
13368
|
+
text: readFileSync39(f, "utf8")
|
|
13223
13369
|
}));
|
|
13224
13370
|
const resolver = buildSymbolResolver(pySources);
|
|
13225
13371
|
const registry = extractToolRegistryEntries(pySources, resolver);
|
|
13226
13372
|
if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
|
|
13227
13373
|
const tfFiles = pluginTerraformFiles(pluginDir2);
|
|
13228
|
-
const tfText = tfFiles.map((f) =>
|
|
13374
|
+
const tfText = tfFiles.map((f) => readFileSync39(f, "utf8")).join("\n");
|
|
13229
13375
|
const terraform = extractTerraformEnvKeys(tfText);
|
|
13230
13376
|
if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
|
|
13231
13377
|
for (const toolName of manifest.tools) {
|
|
@@ -13299,7 +13445,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13299
13445
|
requiredEnvVars: envResult.envVars,
|
|
13300
13446
|
missingEnvVars: anyWired ? [] : envResult.envVars,
|
|
13301
13447
|
status: anyWired ? "ok" : "missing-env",
|
|
13302
|
-
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 ${
|
|
13448
|
+
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 ${join54(pluginDir2, "terraform")}, so this deployment can never supply it`
|
|
13303
13449
|
});
|
|
13304
13450
|
}
|
|
13305
13451
|
}
|
|
@@ -13330,10 +13476,10 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13330
13476
|
|
|
13331
13477
|
// src/scripts/check-plugin-tool-supply.ts
|
|
13332
13478
|
async function runPluginToolSupplyCheck() {
|
|
13333
|
-
const root = (await
|
|
13479
|
+
const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13334
13480
|
let allOk = true;
|
|
13335
|
-
const pluginsRoot =
|
|
13336
|
-
if (!
|
|
13481
|
+
const pluginsRoot = join55(root, "services", "_plugins");
|
|
13482
|
+
if (!existsSync47(pluginsRoot)) {
|
|
13337
13483
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
13338
13484
|
} else {
|
|
13339
13485
|
const report = auditPluginToolSupply(pluginsRoot);
|
|
@@ -13363,8 +13509,8 @@ async function runPluginToolSupplyCheck() {
|
|
|
13363
13509
|
console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
|
|
13364
13510
|
}
|
|
13365
13511
|
}
|
|
13366
|
-
const servicesApiRoot =
|
|
13367
|
-
if (!
|
|
13512
|
+
const servicesApiRoot = join55(root, "services", "api");
|
|
13513
|
+
if (!existsSync47(servicesApiRoot)) {
|
|
13368
13514
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
13369
13515
|
} else {
|
|
13370
13516
|
const modelReport = auditDeclaredModelIds(root);
|
|
@@ -13410,7 +13556,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
13410
13556
|
}
|
|
13411
13557
|
|
|
13412
13558
|
// src/scripts/check-release-subject.ts
|
|
13413
|
-
import { execa as
|
|
13559
|
+
import { execa as execa22 } from "execa";
|
|
13414
13560
|
|
|
13415
13561
|
// src/lib/release-version.ts
|
|
13416
13562
|
var MINOR_TYPES = /* @__PURE__ */ new Set(["feat"]);
|
|
@@ -13447,7 +13593,7 @@ async function fetchPrTitleViaGh({
|
|
|
13447
13593
|
PR_NUMBER,
|
|
13448
13594
|
GH_REPO
|
|
13449
13595
|
}) {
|
|
13450
|
-
const { stdout } = await
|
|
13596
|
+
const { stdout } = await execa22(
|
|
13451
13597
|
"gh",
|
|
13452
13598
|
["pr", "view", PR_NUMBER, "--repo", GH_REPO, "--json", "title", "--jq", ".title"],
|
|
13453
13599
|
{ env: { ...process.env, GH_TOKEN } }
|
|
@@ -13483,7 +13629,7 @@ async function resolveReleaseSubject({
|
|
|
13483
13629
|
);
|
|
13484
13630
|
}
|
|
13485
13631
|
}
|
|
13486
|
-
return (await
|
|
13632
|
+
return (await execa22("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
|
|
13487
13633
|
}
|
|
13488
13634
|
async function runReleaseSubjectCheck(argv) {
|
|
13489
13635
|
const base = process.env["GITHUB_BASE_REF"] ?? argv[0];
|
|
@@ -13491,9 +13637,9 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
13491
13637
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
13492
13638
|
process.exit(2);
|
|
13493
13639
|
}
|
|
13494
|
-
const root = (await
|
|
13495
|
-
await
|
|
13496
|
-
const { stdout } = await
|
|
13640
|
+
const root = (await execa22("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13641
|
+
await execa22("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
|
|
13642
|
+
const { stdout } = await execa22("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
|
|
13497
13643
|
cwd: root
|
|
13498
13644
|
});
|
|
13499
13645
|
const changedFiles = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
@@ -13541,13 +13687,13 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
13541
13687
|
}
|
|
13542
13688
|
|
|
13543
13689
|
// src/scripts/check-skeleton-drift.ts
|
|
13544
|
-
import { existsSync as
|
|
13545
|
-
import { join as
|
|
13546
|
-
import { execa as
|
|
13690
|
+
import { existsSync as existsSync48, readdirSync as readdirSync27 } from "fs";
|
|
13691
|
+
import { join as join57 } from "path";
|
|
13692
|
+
import { execa as execa23 } from "execa";
|
|
13547
13693
|
|
|
13548
13694
|
// src/lib/skeleton-drift-guard.ts
|
|
13549
|
-
import { readFileSync as
|
|
13550
|
-
import { join as
|
|
13695
|
+
import { readFileSync as readFileSync40, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
|
|
13696
|
+
import { join as join56 } from "path";
|
|
13551
13697
|
var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
|
|
13552
13698
|
var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
|
|
13553
13699
|
var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
|
|
@@ -13605,13 +13751,13 @@ function walk(dir, base = dir) {
|
|
|
13605
13751
|
const out = [];
|
|
13606
13752
|
let entries;
|
|
13607
13753
|
try {
|
|
13608
|
-
entries =
|
|
13754
|
+
entries = readdirSync26(dir);
|
|
13609
13755
|
} catch {
|
|
13610
13756
|
return out;
|
|
13611
13757
|
}
|
|
13612
13758
|
for (const entry of entries) {
|
|
13613
13759
|
if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
|
|
13614
|
-
const abs =
|
|
13760
|
+
const abs = join56(dir, entry);
|
|
13615
13761
|
let isDir;
|
|
13616
13762
|
try {
|
|
13617
13763
|
isDir = statSync16(abs).isDirectory();
|
|
@@ -13633,7 +13779,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
|
|
|
13633
13779
|
if (!rule.appliesTo(rel)) continue;
|
|
13634
13780
|
let contents;
|
|
13635
13781
|
try {
|
|
13636
|
-
contents =
|
|
13782
|
+
contents = readFileSync40(join56(skeletonRoot, rel), "utf8");
|
|
13637
13783
|
} catch {
|
|
13638
13784
|
continue;
|
|
13639
13785
|
}
|
|
@@ -13662,23 +13808,23 @@ function formatViolations2(violations) {
|
|
|
13662
13808
|
|
|
13663
13809
|
// src/scripts/check-skeleton-drift.ts
|
|
13664
13810
|
function discoverSkeletons(root) {
|
|
13665
|
-
const skeletonsDir =
|
|
13811
|
+
const skeletonsDir = join57(root, "_skeletons");
|
|
13666
13812
|
let entries;
|
|
13667
13813
|
try {
|
|
13668
|
-
entries =
|
|
13814
|
+
entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
13669
13815
|
} catch {
|
|
13670
13816
|
return [];
|
|
13671
13817
|
}
|
|
13672
|
-
return entries.filter((name) =>
|
|
13818
|
+
return entries.filter((name) => existsSync48(join57(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
13673
13819
|
}
|
|
13674
13820
|
async function runSkeletonDriftCheck() {
|
|
13675
|
-
const root = (await
|
|
13821
|
+
const root = (await execa23("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13676
13822
|
const skeletons = discoverSkeletons(root);
|
|
13677
13823
|
let filesConsidered = 0;
|
|
13678
13824
|
for (const name of skeletons) {
|
|
13679
|
-
const skeletonRoot =
|
|
13825
|
+
const skeletonRoot = join57(root, "_skeletons", name);
|
|
13680
13826
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
13681
|
-
if (
|
|
13827
|
+
if (existsSync48(join57(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
13682
13828
|
filesConsidered += 1;
|
|
13683
13829
|
}
|
|
13684
13830
|
}
|
|
@@ -13692,7 +13838,7 @@ async function runSkeletonDriftCheck() {
|
|
|
13692
13838
|
process.exit(1);
|
|
13693
13839
|
}
|
|
13694
13840
|
const violations = skeletons.flatMap(
|
|
13695
|
-
(name) => auditSkeleton(
|
|
13841
|
+
(name) => auditSkeleton(join57(root, "_skeletons", name), name)
|
|
13696
13842
|
);
|
|
13697
13843
|
if (violations.length > 0) {
|
|
13698
13844
|
console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
|
|
@@ -13704,9 +13850,9 @@ async function runSkeletonDriftCheck() {
|
|
|
13704
13850
|
}
|
|
13705
13851
|
|
|
13706
13852
|
// src/scripts/check-terraform-input.ts
|
|
13707
|
-
import { execa as
|
|
13853
|
+
import { execa as execa24 } from "execa";
|
|
13708
13854
|
async function runTerraformInputCheck() {
|
|
13709
|
-
const root = (await
|
|
13855
|
+
const root = (await execa24("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13710
13856
|
const files = findWorkflowFiles(root);
|
|
13711
13857
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
13712
13858
|
if (files.length === 0) {
|
|
@@ -13796,6 +13942,11 @@ checkCommand.command("codeql-suppression").description(
|
|
|
13796
13942
|
).action(async () => {
|
|
13797
13943
|
await runCodeqlSuppressionCheck();
|
|
13798
13944
|
});
|
|
13945
|
+
checkCommand.command("claim-invocation").description(
|
|
13946
|
+
"Refuse a distributed AGENTS.md that documents a different claim invocation from the others, or an untokened `claim <issue>` (#1562) \u2014 `--as` reached one of three copies, so it was documented in zero satellites while working perfectly"
|
|
13947
|
+
).action(async () => {
|
|
13948
|
+
await runClaimInvocationCheck();
|
|
13949
|
+
});
|
|
13799
13950
|
checkCommand.command("skeleton-drift").description(
|
|
13800
13951
|
"Refuse a fix this repo made for itself that never reached _skeletons/ \u2014 a hardcoded runner, the paid gitleaks action, an unhardened dependency audit, a hard-coded app title"
|
|
13801
13952
|
).action(async () => {
|
|
@@ -13830,8 +13981,8 @@ function rawArgsAfter(subcommand) {
|
|
|
13830
13981
|
}
|
|
13831
13982
|
|
|
13832
13983
|
// src/commands/doctor.ts
|
|
13833
|
-
import { existsSync as
|
|
13834
|
-
import { join as
|
|
13984
|
+
import { existsSync as existsSync49, readFileSync as readFileSync41 } from "fs";
|
|
13985
|
+
import { join as join58, resolve as resolve20 } from "path";
|
|
13835
13986
|
import chalk21 from "chalk";
|
|
13836
13987
|
import { Command as Command25 } from "commander";
|
|
13837
13988
|
|
|
@@ -14010,10 +14161,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
14010
14161
|
return runDoctorChecks(facts);
|
|
14011
14162
|
}
|
|
14012
14163
|
function readLocalCoreVersion(cwd) {
|
|
14013
|
-
const path =
|
|
14014
|
-
if (!
|
|
14164
|
+
const path = join58(cwd, INSTANCE_CORE_FILE);
|
|
14165
|
+
if (!existsSync49(path)) return null;
|
|
14015
14166
|
try {
|
|
14016
|
-
return extractVersionField(
|
|
14167
|
+
return extractVersionField(readFileSync41(path, "utf8"));
|
|
14017
14168
|
} catch {
|
|
14018
14169
|
return null;
|
|
14019
14170
|
}
|
|
@@ -14033,10 +14184,10 @@ function extractVersionField(contents) {
|
|
|
14033
14184
|
return match?.[1] ?? null;
|
|
14034
14185
|
}
|
|
14035
14186
|
function readFossil(cwd) {
|
|
14036
|
-
const path =
|
|
14037
|
-
if (!
|
|
14187
|
+
const path = join58(cwd, CORE_VERSION_FILE);
|
|
14188
|
+
if (!existsSync49(path)) return null;
|
|
14038
14189
|
try {
|
|
14039
|
-
const value =
|
|
14190
|
+
const value = readFileSync41(path, "utf8").trim();
|
|
14040
14191
|
return value === "" ? null : value;
|
|
14041
14192
|
} catch {
|
|
14042
14193
|
return null;
|
|
@@ -14485,13 +14636,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
14485
14636
|
import { Command as Command27 } from "commander";
|
|
14486
14637
|
|
|
14487
14638
|
// src/lib/packaged-scripts.ts
|
|
14488
|
-
import { existsSync as
|
|
14489
|
-
import { dirname as dirname11, join as
|
|
14639
|
+
import { existsSync as existsSync50 } from "fs";
|
|
14640
|
+
import { dirname as dirname11, join as join59 } from "path";
|
|
14490
14641
|
function findPackagedScript(startDir, relativePath) {
|
|
14491
14642
|
let dir = startDir;
|
|
14492
14643
|
for (; ; ) {
|
|
14493
|
-
const candidate =
|
|
14494
|
-
if (
|
|
14644
|
+
const candidate = join59(dir, relativePath);
|
|
14645
|
+
if (existsSync50(candidate)) return candidate;
|
|
14495
14646
|
const parent = dirname11(dir);
|
|
14496
14647
|
if (parent === dir) return null;
|
|
14497
14648
|
dir = parent;
|