@biffo/cli 0.283.3 → 0.284.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_skeletons/plugin-template/AGENTS.md +18 -3
- package/_skeletons/sibling-template/AGENTS.md +18 -3
- package/dist/index.js +424 -178
- package/package.json +1 -1
- package/scripts/claim.sh +119 -9
package/dist/index.js
CHANGED
|
@@ -9693,6 +9693,7 @@ import { cpSync as cpSync6, existsSync as existsSync35, mkdirSync as mkdirSync12
|
|
|
9693
9693
|
import { join as join37, relative as relative7, resolve as resolve17 } from "path";
|
|
9694
9694
|
import chalk19 from "chalk";
|
|
9695
9695
|
import { Command as Command20 } from "commander";
|
|
9696
|
+
import { execa as execa7 } from "execa";
|
|
9696
9697
|
import inquirer7 from "inquirer";
|
|
9697
9698
|
var pluginUpgradeCommand = new Command20("upgrade").description(
|
|
9698
9699
|
"Upgrade an installed plugin to a new minor version (biffo plugin upgrade <name>@<new-minor>) or refresh it in place from a local, unpublished checkout (biffo plugin upgrade --local <path>)"
|
|
@@ -9794,6 +9795,7 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
9794
9795
|
refuseIfModuleStillReferenced(options.cwd, modulesDir, entry.name);
|
|
9795
9796
|
}
|
|
9796
9797
|
const previousProvenance = readProvenance(targetDir);
|
|
9798
|
+
const previousPyproject = readPyprojectIfPresent(targetDir);
|
|
9797
9799
|
rmSync10(targetDir, { recursive: true, force: true });
|
|
9798
9800
|
mkdirSync12(targetDir, { recursive: true });
|
|
9799
9801
|
cpSync6(tmpDir, targetDir, { recursive: true });
|
|
@@ -9804,6 +9806,7 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
9804
9806
|
);
|
|
9805
9807
|
writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
|
|
9806
9808
|
applyWorkspaceSources(targetDir, options.cwd, `services/${entry.name}`);
|
|
9809
|
+
const newPyproject = readPyprojectIfPresent(targetDir);
|
|
9807
9810
|
const stagePaths = [`services/${entry.name}`];
|
|
9808
9811
|
if (existsSync35(modulesDir)) {
|
|
9809
9812
|
rmSync10(modulesDir, { recursive: true, force: true });
|
|
@@ -9835,12 +9838,29 @@ async function runPluginUpgrade(target, options, deps) {
|
|
|
9835
9838
|
if (seedResult.vendored) {
|
|
9836
9839
|
stagePaths.push(seedResult.stagedPath);
|
|
9837
9840
|
}
|
|
9841
|
+
const lockOutcomes = await relockIfDependenciesChanged(
|
|
9842
|
+
options.cwd,
|
|
9843
|
+
`services/${entry.name}/pyproject.toml`,
|
|
9844
|
+
previousPyproject,
|
|
9845
|
+
newPyproject,
|
|
9846
|
+
deps
|
|
9847
|
+
);
|
|
9848
|
+
for (const outcome of lockOutcomes) {
|
|
9849
|
+
if (outcome.ok) stagePaths.push(outcome.trigger.lockfile);
|
|
9850
|
+
}
|
|
9838
9851
|
const label = currentVersion ? `${entry.name} ${currentVersion} -> ${entry.version}` : `${entry.name} to ${entry.version}`;
|
|
9839
9852
|
const commitMessage = `feat(plugins): upgrade ${label}`;
|
|
9840
9853
|
await deps.git.add(options.cwd, stagePaths);
|
|
9841
9854
|
await deps.git.commit(options.cwd, commitMessage);
|
|
9842
9855
|
log.success(`Committed: ${commitMessage}`);
|
|
9843
|
-
|
|
9856
|
+
const lockFailures = describeFailures(lockOutcomes);
|
|
9857
|
+
if (lockFailures.length > 0) {
|
|
9858
|
+
console.log(chalk19.yellow.bold("\n \u26A0 Plugin upgraded, but uv.lock needs attention\n"));
|
|
9859
|
+
for (const failure of lockFailures) console.log(chalk19.yellow(` ${failure}`));
|
|
9860
|
+
console.log();
|
|
9861
|
+
} else {
|
|
9862
|
+
console.log(chalk19.bold("\n Plugin upgraded!\n"));
|
|
9863
|
+
}
|
|
9844
9864
|
console.log(` ${entry.name}@${entry.version} is committed at services/${entry.name}/`);
|
|
9845
9865
|
console.log(" Push and redeploy to apply its updated tables and routes:");
|
|
9846
9866
|
console.log(chalk19.dim(` git push`));
|
|
@@ -9889,6 +9909,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
|
|
|
9889
9909
|
refuseIfModuleStillReferenced(options.cwd, modulesDir, source.name);
|
|
9890
9910
|
}
|
|
9891
9911
|
const previousProvenance = readProvenance(targetDir);
|
|
9912
|
+
const previousPyproject = readPyprojectIfPresent(targetDir);
|
|
9892
9913
|
if (inTreeSource) {
|
|
9893
9914
|
log.info(
|
|
9894
9915
|
`services/${source.name}/ is already the local checkout \u2014 nothing to copy; re-syncing its Terraform module and checking for a migration.`
|
|
@@ -9902,6 +9923,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
|
|
|
9902
9923
|
const nextProvenance = inTreeSource ? inTreePluginProvenance(`services/${source.name}`) : await resolveLocalProvenance(source.sourceDir, source.origin);
|
|
9903
9924
|
writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
|
|
9904
9925
|
applyWorkspaceSources(targetDir, options.cwd, `services/${source.name}`);
|
|
9926
|
+
const newPyproject = readPyprojectIfPresent(targetDir);
|
|
9905
9927
|
const stagePaths = [`services/${source.name}`];
|
|
9906
9928
|
if (existsSync35(modulesDir)) {
|
|
9907
9929
|
rmSync10(modulesDir, { recursive: true, force: true });
|
|
@@ -9935,6 +9957,16 @@ async function runLocalPluginRefresh(localPath, options, deps) {
|
|
|
9935
9957
|
if (seedResult.vendored) {
|
|
9936
9958
|
stagePaths.push(seedResult.stagedPath);
|
|
9937
9959
|
}
|
|
9960
|
+
const lockOutcomes = await relockIfDependenciesChanged(
|
|
9961
|
+
options.cwd,
|
|
9962
|
+
`services/${source.name}/pyproject.toml`,
|
|
9963
|
+
previousPyproject,
|
|
9964
|
+
newPyproject,
|
|
9965
|
+
deps
|
|
9966
|
+
);
|
|
9967
|
+
for (const outcome of lockOutcomes) {
|
|
9968
|
+
if (outcome.ok) stagePaths.push(outcome.trigger.lockfile);
|
|
9969
|
+
}
|
|
9938
9970
|
await deps.git.add(options.cwd, stagePaths);
|
|
9939
9971
|
if (!await deps.git.hasUncommittedChanges(options.cwd)) {
|
|
9940
9972
|
log.warn(`services/${source.name}/ already matches ${source.origin} \u2014 nothing to commit.`);
|
|
@@ -9943,7 +9975,14 @@ async function runLocalPluginRefresh(localPath, options, deps) {
|
|
|
9943
9975
|
const commitMessage = `chore(plugins): refresh ${source.name} from local checkout`;
|
|
9944
9976
|
await deps.git.commit(options.cwd, commitMessage);
|
|
9945
9977
|
log.success(`Committed: ${commitMessage}`);
|
|
9946
|
-
|
|
9978
|
+
const lockFailures = describeFailures(lockOutcomes);
|
|
9979
|
+
if (lockFailures.length > 0) {
|
|
9980
|
+
console.log(chalk19.yellow.bold("\n \u26A0 Plugin refreshed, but uv.lock needs attention\n"));
|
|
9981
|
+
for (const failure of lockFailures) console.log(chalk19.yellow(` ${failure}`));
|
|
9982
|
+
console.log();
|
|
9983
|
+
} else {
|
|
9984
|
+
console.log(chalk19.bold("\n Plugin refreshed!\n"));
|
|
9985
|
+
}
|
|
9947
9986
|
console.log(` services/${source.name}/ now matches ${source.origin}`);
|
|
9948
9987
|
console.log(" Push and redeploy to apply any updated tables and routes:");
|
|
9949
9988
|
console.log(chalk19.dim(` git push`));
|
|
@@ -9964,6 +10003,62 @@ ${refList}
|
|
|
9964
10003
|
A plugin repo without terraform/ is missing the directory, not declaring that the module should go, and a wrong destructive action here is worse than no action. Either remove the reference(s) above yourself, or run 'biffo plugin uninstall ${name}', which removes the module and unwires the reference together.`
|
|
9965
10004
|
);
|
|
9966
10005
|
}
|
|
10006
|
+
async function relockIfDependenciesChanged(cwd, relPyprojectPath, previousPyproject, newPyproject, deps) {
|
|
10007
|
+
if (!dependenciesChanged(previousPyproject, newPyproject)) return [];
|
|
10008
|
+
const triggers = lockfilesNeedingRefresh([relPyprojectPath], cwd);
|
|
10009
|
+
if (triggers.length === 0) return [];
|
|
10010
|
+
const run = deps.runCommand ?? defaultRunCommand2;
|
|
10011
|
+
const outcomes = await refreshLockfiles(cwd, triggers, run);
|
|
10012
|
+
const refreshed = outcomes.filter((o) => o.ok);
|
|
10013
|
+
if (refreshed.length > 0) {
|
|
10014
|
+
log.success(
|
|
10015
|
+
`Refreshed ${refreshed.map((o) => o.trigger.lockfile).join(", ")} \u2014 this refresh changed a dependency it locks.`
|
|
10016
|
+
);
|
|
10017
|
+
}
|
|
10018
|
+
for (const message of describeFailures(outcomes)) log.warn(message);
|
|
10019
|
+
return outcomes;
|
|
10020
|
+
}
|
|
10021
|
+
var defaultRunCommand2 = async (command, cwd) => {
|
|
10022
|
+
const [bin, ...args] = command;
|
|
10023
|
+
if (!bin) return { ok: false, error: "empty command" };
|
|
10024
|
+
try {
|
|
10025
|
+
await execa7(bin, args, { cwd });
|
|
10026
|
+
return { ok: true };
|
|
10027
|
+
} catch (err) {
|
|
10028
|
+
const cause = err;
|
|
10029
|
+
const detail = cause.stderr?.trim() || cause.shortMessage || cause.message || "failed";
|
|
10030
|
+
return { ok: false, error: detail.split("\n")[0] ?? "failed" };
|
|
10031
|
+
}
|
|
10032
|
+
};
|
|
10033
|
+
function readPyprojectIfPresent(targetDir) {
|
|
10034
|
+
const path = join37(targetDir, "pyproject.toml");
|
|
10035
|
+
return existsSync35(path) ? readFileSync26(path, "utf8") : null;
|
|
10036
|
+
}
|
|
10037
|
+
function dependenciesChanged(before, after) {
|
|
10038
|
+
if (before === after) return false;
|
|
10039
|
+
const beforeItems = before ? dependencySurface(before) : [];
|
|
10040
|
+
const afterItems = after ? dependencySurface(after) : [];
|
|
10041
|
+
return JSON.stringify(beforeItems) !== JSON.stringify(afterItems);
|
|
10042
|
+
}
|
|
10043
|
+
function dependencySurface(text) {
|
|
10044
|
+
const items = [...readTomlStringArray(text, "dependencies")];
|
|
10045
|
+
for (const header of ["dependency-groups", "project.optional-dependencies"]) {
|
|
10046
|
+
const body = tomlTableBody(text, header);
|
|
10047
|
+
if (!body) continue;
|
|
10048
|
+
for (const m of body.matchAll(/^([A-Za-z0-9_.-]+)\s*=\s*\[/gm)) {
|
|
10049
|
+
items.push(...readTomlStringArray(body, m[1]));
|
|
10050
|
+
}
|
|
10051
|
+
}
|
|
10052
|
+
return items.sort();
|
|
10053
|
+
}
|
|
10054
|
+
function tomlTableBody(text, header) {
|
|
10055
|
+
const escaped = header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
10056
|
+
const headerMatch = new RegExp(`^\\[${escaped}\\]\\s*$`, "m").exec(text);
|
|
10057
|
+
if (!headerMatch) return null;
|
|
10058
|
+
const rest = text.slice(headerMatch.index + headerMatch[0].length);
|
|
10059
|
+
const nextHeader = /^\[/m.exec(rest);
|
|
10060
|
+
return nextHeader ? rest.slice(0, nextHeader.index) : rest;
|
|
10061
|
+
}
|
|
9967
10062
|
function readInstalledVersion2(targetDir) {
|
|
9968
10063
|
const manifestPath = join37(targetDir, "biffo.plugin.json");
|
|
9969
10064
|
if (!existsSync35(manifestPath)) return void 0;
|
|
@@ -10314,7 +10409,7 @@ import { Command as Command24 } from "commander";
|
|
|
10314
10409
|
// src/scripts/check-adr-numbering.ts
|
|
10315
10410
|
import { existsSync as existsSync38 } from "fs";
|
|
10316
10411
|
import { join as join39 } from "path";
|
|
10317
|
-
import { execa as
|
|
10412
|
+
import { execa as execa8 } from "execa";
|
|
10318
10413
|
|
|
10319
10414
|
// src/lib/adr-numbering-guard.ts
|
|
10320
10415
|
import { existsSync as existsSync37, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "fs";
|
|
@@ -10388,7 +10483,7 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
|
|
|
10388
10483
|
|
|
10389
10484
|
// src/scripts/check-adr-numbering.ts
|
|
10390
10485
|
async function runAdrNumberingCheck() {
|
|
10391
|
-
const root = (await
|
|
10486
|
+
const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
10392
10487
|
const adrDir = join39(root, "docs", "ADR");
|
|
10393
10488
|
if (!existsSync38(adrDir)) {
|
|
10394
10489
|
console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
|
|
@@ -10429,7 +10524,7 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
|
|
|
10429
10524
|
|
|
10430
10525
|
// src/scripts/check-branch-protection.ts
|
|
10431
10526
|
import { Octokit as Octokit2 } from "@octokit/rest";
|
|
10432
|
-
import { execa as
|
|
10527
|
+
import { execa as execa9 } from "execa";
|
|
10433
10528
|
|
|
10434
10529
|
// src/lib/branch-protection-apply.ts
|
|
10435
10530
|
var CONTEXT_CONSISTENCY_THRESHOLD = 2 / 3;
|
|
@@ -10556,7 +10651,7 @@ async function resolveRepo(explicit) {
|
|
|
10556
10651
|
}
|
|
10557
10652
|
return { owner, repo };
|
|
10558
10653
|
}
|
|
10559
|
-
const { stdout } = await
|
|
10654
|
+
const { stdout } = await execa9("git", ["remote", "get-url", "origin"]);
|
|
10560
10655
|
const m = /github\.com[:/]([^/]+)\/(.+?)(?:\.git)?$/.exec(stdout.trim());
|
|
10561
10656
|
if (!m?.[1] || !m[2]) {
|
|
10562
10657
|
console.error(
|
|
@@ -10687,14 +10782,160 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
|
|
|
10687
10782
|
console.log(`\u2713 branch-protection guard: ${owner}/${repo} (${audited.join(", ")}) OK`);
|
|
10688
10783
|
}
|
|
10689
10784
|
|
|
10785
|
+
// src/scripts/check-claim-invocation.ts
|
|
10786
|
+
import { execa as execa10 } from "execa";
|
|
10787
|
+
|
|
10788
|
+
// src/lib/claim-invocation-parity.ts
|
|
10789
|
+
import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync16 } from "fs";
|
|
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
|
+
|
|
10690
10931
|
// src/scripts/check-codeql-suppression.ts
|
|
10691
|
-
import { existsSync as
|
|
10692
|
-
import { join as
|
|
10693
|
-
import { execa as
|
|
10932
|
+
import { existsSync as existsSync40 } from "fs";
|
|
10933
|
+
import { join as join42, relative as relative8 } from "path";
|
|
10934
|
+
import { execa as execa11 } from "execa";
|
|
10694
10935
|
|
|
10695
10936
|
// src/lib/codeql-suppression-guard.ts
|
|
10696
|
-
import { readdirSync as
|
|
10697
|
-
import { join as
|
|
10937
|
+
import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync9 } from "fs";
|
|
10938
|
+
import { join as join41 } from "path";
|
|
10698
10939
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
10699
10940
|
".git",
|
|
10700
10941
|
".worktrees",
|
|
@@ -10720,12 +10961,12 @@ function walkSourceFiles(root) {
|
|
|
10720
10961
|
const walk2 = (dir) => {
|
|
10721
10962
|
let entries;
|
|
10722
10963
|
try {
|
|
10723
|
-
entries =
|
|
10964
|
+
entries = readdirSync17(dir);
|
|
10724
10965
|
} catch {
|
|
10725
10966
|
return;
|
|
10726
10967
|
}
|
|
10727
10968
|
for (const entry of entries) {
|
|
10728
|
-
const p =
|
|
10969
|
+
const p = join41(dir, entry);
|
|
10729
10970
|
let st;
|
|
10730
10971
|
try {
|
|
10731
10972
|
st = statSync9(p);
|
|
@@ -10751,7 +10992,7 @@ function countSourceFiles(root) {
|
|
|
10751
10992
|
function sweepCodeqlSuppressionComments(root) {
|
|
10752
10993
|
const hits = [];
|
|
10753
10994
|
for (const path of walkSourceFiles(root)) {
|
|
10754
|
-
const text =
|
|
10995
|
+
const text = readFileSync30(path, "utf8");
|
|
10755
10996
|
for (const line of findCodeqlSuppressionComments(text)) {
|
|
10756
10997
|
hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
|
|
10757
10998
|
}
|
|
@@ -10761,9 +11002,9 @@ function sweepCodeqlSuppressionComments(root) {
|
|
|
10761
11002
|
|
|
10762
11003
|
// src/scripts/check-codeql-suppression.ts
|
|
10763
11004
|
async function runCodeqlSuppressionCheck() {
|
|
10764
|
-
const root = (await
|
|
10765
|
-
const scanRoot =
|
|
10766
|
-
if (!
|
|
11005
|
+
const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11006
|
+
const scanRoot = join42(root, "cli", "src");
|
|
11007
|
+
if (!existsSync40(scanRoot)) {
|
|
10767
11008
|
console.log(
|
|
10768
11009
|
"\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
|
|
10769
11010
|
);
|
|
@@ -10787,11 +11028,11 @@ async function runCodeqlSuppressionCheck() {
|
|
|
10787
11028
|
}
|
|
10788
11029
|
|
|
10789
11030
|
// src/scripts/check-cognito-invite-template.ts
|
|
10790
|
-
import { execa as
|
|
11031
|
+
import { execa as execa12 } from "execa";
|
|
10791
11032
|
|
|
10792
11033
|
// src/lib/cognito-invite-template-guard.ts
|
|
10793
|
-
import { readdirSync as
|
|
10794
|
-
import { join as
|
|
11034
|
+
import { readdirSync as readdirSync18, readFileSync as readFileSync31, statSync as statSync10 } from "fs";
|
|
11035
|
+
import { join as join43 } from "path";
|
|
10795
11036
|
var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
|
|
10796
11037
|
var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
|
|
10797
11038
|
var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
|
|
@@ -10868,13 +11109,13 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
10868
11109
|
const walk2 = (dir, relative11) => {
|
|
10869
11110
|
let entries;
|
|
10870
11111
|
try {
|
|
10871
|
-
entries =
|
|
11112
|
+
entries = readdirSync18(dir);
|
|
10872
11113
|
} catch {
|
|
10873
11114
|
return;
|
|
10874
11115
|
}
|
|
10875
11116
|
for (const entry of entries) {
|
|
10876
11117
|
if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
|
|
10877
|
-
const full =
|
|
11118
|
+
const full = join43(dir, entry);
|
|
10878
11119
|
const rel = `${relative11}/${entry}`;
|
|
10879
11120
|
if (statSync10(full).isDirectory()) {
|
|
10880
11121
|
walk2(full, rel);
|
|
@@ -10883,18 +11124,18 @@ function findModuleTerraformFiles(repoRoot) {
|
|
|
10883
11124
|
}
|
|
10884
11125
|
}
|
|
10885
11126
|
};
|
|
10886
|
-
walk2(
|
|
11127
|
+
walk2(join43(repoRoot, "modules"), "modules");
|
|
10887
11128
|
return found.sort();
|
|
10888
11129
|
}
|
|
10889
11130
|
function checkCognitoInviteTemplates(repoRoot) {
|
|
10890
11131
|
return findModuleTerraformFiles(repoRoot).flatMap(
|
|
10891
|
-
(file) => checkInviteTemplateSource(file,
|
|
11132
|
+
(file) => checkInviteTemplateSource(file, readFileSync31(join43(repoRoot, file), "utf8"))
|
|
10892
11133
|
);
|
|
10893
11134
|
}
|
|
10894
11135
|
|
|
10895
11136
|
// src/scripts/check-cognito-invite-template.ts
|
|
10896
11137
|
async function runCognitoInviteTemplateCheck() {
|
|
10897
|
-
const root = (await
|
|
11138
|
+
const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
10898
11139
|
const files = findModuleTerraformFiles(root);
|
|
10899
11140
|
console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
|
|
10900
11141
|
if (files.length === 0) {
|
|
@@ -10916,12 +11157,12 @@ async function runCognitoInviteTemplateCheck() {
|
|
|
10916
11157
|
}
|
|
10917
11158
|
|
|
10918
11159
|
// src/scripts/check-core-direct-paths.ts
|
|
10919
|
-
import { join as
|
|
10920
|
-
import { execa as
|
|
11160
|
+
import { join as join45 } from "path";
|
|
11161
|
+
import { execa as execa13 } from "execa";
|
|
10921
11162
|
|
|
10922
11163
|
// src/lib/core-direct-paths-audit.ts
|
|
10923
|
-
import { existsSync as
|
|
10924
|
-
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";
|
|
10925
11166
|
var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
|
|
10926
11167
|
var API_ROUTE_PREFIX = "/api/v1";
|
|
10927
11168
|
var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
|
|
@@ -11080,12 +11321,12 @@ function walkFiles(root, accept, skipDir) {
|
|
|
11080
11321
|
const walk2 = (dir) => {
|
|
11081
11322
|
let entries;
|
|
11082
11323
|
try {
|
|
11083
|
-
entries =
|
|
11324
|
+
entries = readdirSync19(dir);
|
|
11084
11325
|
} catch {
|
|
11085
11326
|
return;
|
|
11086
11327
|
}
|
|
11087
11328
|
for (const entry of entries) {
|
|
11088
|
-
const p =
|
|
11329
|
+
const p = join44(dir, entry);
|
|
11089
11330
|
let st;
|
|
11090
11331
|
try {
|
|
11091
11332
|
st = statSync11(p);
|
|
@@ -11115,7 +11356,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
|
|
|
11115
11356
|
const extracted = [];
|
|
11116
11357
|
let rawTotal = 0;
|
|
11117
11358
|
for (const file of files) {
|
|
11118
|
-
const text =
|
|
11359
|
+
const text = readFileSync32(file, "utf8");
|
|
11119
11360
|
rawTotal += countRawExternalOccurrences(text, externalBases);
|
|
11120
11361
|
extracted.push(...extractCoreDirectPaths(text, file, externalBases));
|
|
11121
11362
|
}
|
|
@@ -11164,7 +11405,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
|
|
|
11164
11405
|
const prefixSet = /* @__PURE__ */ new Set();
|
|
11165
11406
|
let rawApiRouterCount = 0;
|
|
11166
11407
|
for (const file of files) {
|
|
11167
|
-
const text =
|
|
11408
|
+
const text = readFileSync32(file, "utf8");
|
|
11168
11409
|
const extraction = extractCoreRoutePrefixes(text);
|
|
11169
11410
|
rawApiRouterCount += extraction.rawApiRouterCount;
|
|
11170
11411
|
for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
|
|
@@ -11180,10 +11421,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
|
|
|
11180
11421
|
}
|
|
11181
11422
|
function resolveSiblingCoreSrc(params) {
|
|
11182
11423
|
const { estateDir, sibling } = params;
|
|
11183
|
-
const configPath =
|
|
11424
|
+
const configPath = join44(estateDir, sibling, "biffo.sibling.json");
|
|
11184
11425
|
let raw;
|
|
11185
11426
|
try {
|
|
11186
|
-
raw =
|
|
11427
|
+
raw = readFileSync32(configPath, "utf8");
|
|
11187
11428
|
} catch (err) {
|
|
11188
11429
|
throw new Error(
|
|
11189
11430
|
`cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
|
|
@@ -11203,8 +11444,8 @@ function resolveSiblingCoreSrc(params) {
|
|
|
11203
11444
|
`cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
|
|
11204
11445
|
);
|
|
11205
11446
|
}
|
|
11206
|
-
const coreApiSrcDir =
|
|
11207
|
-
if (!
|
|
11447
|
+
const coreApiSrcDir = join44(estateDir, coreProject, "services", "api", "src");
|
|
11448
|
+
if (!existsSync41(coreApiSrcDir)) {
|
|
11208
11449
|
throw new Error(
|
|
11209
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.`
|
|
11210
11451
|
);
|
|
@@ -11245,9 +11486,9 @@ function auditSiblingCoreDirectPaths(params) {
|
|
|
11245
11486
|
|
|
11246
11487
|
// src/scripts/check-core-direct-paths.ts
|
|
11247
11488
|
async function runCoreDirectPathsCheck(opts = {}) {
|
|
11248
|
-
const root = (await
|
|
11489
|
+
const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11249
11490
|
const sibling = opts.sibling ?? "sibling-template (self-check)";
|
|
11250
|
-
const frontendSrcDir = opts.frontendSrc ??
|
|
11491
|
+
const frontendSrcDir = opts.frontendSrc ?? join45(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
|
|
11251
11492
|
let coreApiSrcDir;
|
|
11252
11493
|
let coreProject = null;
|
|
11253
11494
|
if (opts.coreSrc) {
|
|
@@ -11263,7 +11504,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
|
|
|
11263
11504
|
coreApiSrcDir = resolution.coreApiSrcDir;
|
|
11264
11505
|
coreProject = resolution.coreProject;
|
|
11265
11506
|
} else {
|
|
11266
|
-
coreApiSrcDir =
|
|
11507
|
+
coreApiSrcDir = join45(root, "services", "api", "src");
|
|
11267
11508
|
}
|
|
11268
11509
|
const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
|
|
11269
11510
|
console.log(
|
|
@@ -11299,7 +11540,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
|
|
|
11299
11540
|
}
|
|
11300
11541
|
|
|
11301
11542
|
// src/scripts/check-core-ownership.ts
|
|
11302
|
-
import { execa as
|
|
11543
|
+
import { execa as execa14 } from "execa";
|
|
11303
11544
|
var BOLD = "\x1B[1m";
|
|
11304
11545
|
var DIM = "\x1B[2m";
|
|
11305
11546
|
var RED = "\x1B[31m";
|
|
@@ -11310,7 +11551,7 @@ async function runOwnershipCheck(argv) {
|
|
|
11310
11551
|
const stagedFlag = args.indexOf("--staged");
|
|
11311
11552
|
const staged = stagedFlag !== -1;
|
|
11312
11553
|
const messageFile = staged ? args[stagedFlag + 1] : void 0;
|
|
11313
|
-
const root = (await
|
|
11554
|
+
const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11314
11555
|
const ownership = classifyRepoOwnership(root);
|
|
11315
11556
|
if (ownership === "template") {
|
|
11316
11557
|
console.log("\u2713 core ownership guard: skipped \u2014 this is the template, which owns these paths.");
|
|
@@ -11326,11 +11567,11 @@ async function runOwnershipCheck(argv) {
|
|
|
11326
11567
|
let deletedFiles = [];
|
|
11327
11568
|
let commitMessage = "";
|
|
11328
11569
|
if (staged) {
|
|
11329
|
-
const { stdout } = await
|
|
11570
|
+
const { stdout } = await execa14("git", ["diff", "--cached", "--name-status"], { cwd: root });
|
|
11330
11571
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11331
11572
|
if (messageFile) {
|
|
11332
|
-
const { readFileSync:
|
|
11333
|
-
if (
|
|
11573
|
+
const { readFileSync: readFileSync42, existsSync: existsSync51 } = await import("fs");
|
|
11574
|
+
if (existsSync51(messageFile)) commitMessage = readFileSync42(messageFile, "utf8");
|
|
11334
11575
|
}
|
|
11335
11576
|
} else {
|
|
11336
11577
|
const base = process.env["GITHUB_BASE_REF"] ?? args[0];
|
|
@@ -11338,18 +11579,18 @@ async function runOwnershipCheck(argv) {
|
|
|
11338
11579
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
11339
11580
|
process.exit(2);
|
|
11340
11581
|
}
|
|
11341
|
-
await
|
|
11342
|
-
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`], {
|
|
11343
11584
|
cwd: root
|
|
11344
11585
|
});
|
|
11345
11586
|
({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
|
|
11346
|
-
const { stdout: log2 } = await
|
|
11587
|
+
const { stdout: log2 } = await execa14("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
|
|
11347
11588
|
cwd: root,
|
|
11348
11589
|
reject: false
|
|
11349
11590
|
});
|
|
11350
11591
|
commitMessage = log2;
|
|
11351
11592
|
}
|
|
11352
|
-
const { stdout: gitBranch } = await
|
|
11593
|
+
const { stdout: gitBranch } = await execa14("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
11353
11594
|
cwd: root,
|
|
11354
11595
|
reject: false
|
|
11355
11596
|
});
|
|
@@ -11431,11 +11672,11 @@ ${BOLD}If the divergence is deliberate${OFF}
|
|
|
11431
11672
|
}
|
|
11432
11673
|
|
|
11433
11674
|
// src/scripts/check-eventbridge-log-permissions.ts
|
|
11434
|
-
import { execa as
|
|
11675
|
+
import { execa as execa15 } from "execa";
|
|
11435
11676
|
|
|
11436
11677
|
// src/lib/eventbridge-log-permission-guard.ts
|
|
11437
|
-
import { readFileSync as
|
|
11438
|
-
import { join as
|
|
11678
|
+
import { readFileSync as readFileSync33, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
|
|
11679
|
+
import { join as join46 } from "path";
|
|
11439
11680
|
var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
|
|
11440
11681
|
var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
|
|
11441
11682
|
var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
|
|
@@ -11507,12 +11748,12 @@ function walkTerraformFiles(root) {
|
|
|
11507
11748
|
const walk2 = (dir) => {
|
|
11508
11749
|
let entries;
|
|
11509
11750
|
try {
|
|
11510
|
-
entries =
|
|
11751
|
+
entries = readdirSync20(dir);
|
|
11511
11752
|
} catch {
|
|
11512
11753
|
return;
|
|
11513
11754
|
}
|
|
11514
11755
|
for (const entry of entries) {
|
|
11515
|
-
const p =
|
|
11756
|
+
const p = join46(dir, entry);
|
|
11516
11757
|
let st;
|
|
11517
11758
|
try {
|
|
11518
11759
|
st = statSync12(p);
|
|
@@ -11555,7 +11796,7 @@ function auditEventBridgeLogPermissions(root) {
|
|
|
11555
11796
|
let rawEventTargetCount = 0;
|
|
11556
11797
|
let rawLogPolicyCount = 0;
|
|
11557
11798
|
for (const file of files) {
|
|
11558
|
-
const text =
|
|
11799
|
+
const text = readFileSync33(file, "utf8");
|
|
11559
11800
|
rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
|
|
11560
11801
|
rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
|
|
11561
11802
|
eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
|
|
@@ -11608,7 +11849,7 @@ function auditEventBridgeLogPermissions(root) {
|
|
|
11608
11849
|
|
|
11609
11850
|
// src/scripts/check-eventbridge-log-permissions.ts
|
|
11610
11851
|
async function runEventBridgeLogPermissionCheck() {
|
|
11611
|
-
const root = (await
|
|
11852
|
+
const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11612
11853
|
let report;
|
|
11613
11854
|
try {
|
|
11614
11855
|
report = auditEventBridgeLogPermissions(root);
|
|
@@ -11645,15 +11886,15 @@ async function runEventBridgeLogPermissionCheck() {
|
|
|
11645
11886
|
}
|
|
11646
11887
|
|
|
11647
11888
|
// src/scripts/check-lambda-output.ts
|
|
11648
|
-
import { execa as
|
|
11889
|
+
import { execa as execa16 } from "execa";
|
|
11649
11890
|
|
|
11650
11891
|
// src/lib/lambda-output-guard.ts
|
|
11651
|
-
import { readFileSync as
|
|
11652
|
-
import { join as
|
|
11892
|
+
import { readFileSync as readFileSync35 } from "fs";
|
|
11893
|
+
import { join as join48 } from "path";
|
|
11653
11894
|
|
|
11654
11895
|
// src/lib/terraform-input-guard.ts
|
|
11655
|
-
import { existsSync as
|
|
11656
|
-
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";
|
|
11657
11898
|
var GUARDED_SUBCOMMANDS = [
|
|
11658
11899
|
"init",
|
|
11659
11900
|
"plan",
|
|
@@ -11667,18 +11908,18 @@ function stripComments2(source) {
|
|
|
11667
11908
|
return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
|
|
11668
11909
|
}
|
|
11669
11910
|
function vendoredPluginServiceDirs(repoRoot) {
|
|
11670
|
-
const servicesDir =
|
|
11911
|
+
const servicesDir = join47(repoRoot, "services");
|
|
11671
11912
|
const result = /* @__PURE__ */ new Set();
|
|
11672
11913
|
let entries;
|
|
11673
11914
|
try {
|
|
11674
|
-
entries =
|
|
11915
|
+
entries = readdirSync21(servicesDir);
|
|
11675
11916
|
} catch {
|
|
11676
11917
|
return result;
|
|
11677
11918
|
}
|
|
11678
11919
|
for (const entry of entries) {
|
|
11679
|
-
const full =
|
|
11680
|
-
if (!
|
|
11681
|
-
if (
|
|
11920
|
+
const full = join47(servicesDir, entry);
|
|
11921
|
+
if (!existsSync42(full) || !statSync13(full).isDirectory()) continue;
|
|
11922
|
+
if (existsSync42(join47(full, "biffo.plugin.json"))) {
|
|
11682
11923
|
result.add(entry);
|
|
11683
11924
|
}
|
|
11684
11925
|
}
|
|
@@ -11690,7 +11931,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
11690
11931
|
const walk2 = (dir, relative11) => {
|
|
11691
11932
|
let entries;
|
|
11692
11933
|
try {
|
|
11693
|
-
entries =
|
|
11934
|
+
entries = readdirSync21(dir);
|
|
11694
11935
|
} catch {
|
|
11695
11936
|
return;
|
|
11696
11937
|
}
|
|
@@ -11699,7 +11940,7 @@ function findWorkflowFiles(repoRoot) {
|
|
|
11699
11940
|
if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
|
|
11700
11941
|
continue;
|
|
11701
11942
|
}
|
|
11702
|
-
const full =
|
|
11943
|
+
const full = join47(dir, entry);
|
|
11703
11944
|
const rel = relative11 ? `${relative11}/${entry}` : entry;
|
|
11704
11945
|
if (statSync13(full).isDirectory()) {
|
|
11705
11946
|
walk2(full, rel);
|
|
@@ -11743,7 +11984,7 @@ function checkWorkflowSource(file, rawSource) {
|
|
|
11743
11984
|
}
|
|
11744
11985
|
function checkTerraformInput(repoRoot) {
|
|
11745
11986
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
11746
|
-
(file) => checkWorkflowSource(file,
|
|
11987
|
+
(file) => checkWorkflowSource(file, readFileSync34(join47(repoRoot, file), "utf8"))
|
|
11747
11988
|
);
|
|
11748
11989
|
}
|
|
11749
11990
|
|
|
@@ -11801,13 +12042,13 @@ function checkWorkflowSource2(file, rawSource) {
|
|
|
11801
12042
|
}
|
|
11802
12043
|
function checkLambdaOutput(repoRoot) {
|
|
11803
12044
|
return findWorkflowFiles(repoRoot).flatMap(
|
|
11804
|
-
(file) => checkWorkflowSource2(file,
|
|
12045
|
+
(file) => checkWorkflowSource2(file, readFileSync35(join48(repoRoot, file), "utf8"))
|
|
11805
12046
|
);
|
|
11806
12047
|
}
|
|
11807
12048
|
|
|
11808
12049
|
// src/scripts/check-lambda-output.ts
|
|
11809
12050
|
async function runLambdaOutputCheck() {
|
|
11810
|
-
const root = (await
|
|
12051
|
+
const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11811
12052
|
const files = findWorkflowFiles(root);
|
|
11812
12053
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
11813
12054
|
if (files.length === 0) {
|
|
@@ -11829,9 +12070,9 @@ async function runLambdaOutputCheck() {
|
|
|
11829
12070
|
}
|
|
11830
12071
|
|
|
11831
12072
|
// src/scripts/check-pipe-trap.ts
|
|
11832
|
-
import { readFileSync as
|
|
11833
|
-
import { join as
|
|
11834
|
-
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";
|
|
11835
12076
|
|
|
11836
12077
|
// src/lib/pipe-trap-guard.ts
|
|
11837
12078
|
var STATUS_BEARING = [
|
|
@@ -11927,23 +12168,23 @@ function findPipeTraps(source) {
|
|
|
11927
12168
|
function shellFiles(root) {
|
|
11928
12169
|
const out = [];
|
|
11929
12170
|
for (const dir of ["scripts", ".githooks"]) {
|
|
11930
|
-
const full =
|
|
12171
|
+
const full = join49(root, dir);
|
|
11931
12172
|
let entries;
|
|
11932
12173
|
try {
|
|
11933
|
-
entries =
|
|
12174
|
+
entries = readdirSync22(full, { withFileTypes: true });
|
|
11934
12175
|
} catch {
|
|
11935
12176
|
continue;
|
|
11936
12177
|
}
|
|
11937
12178
|
for (const entry of entries) {
|
|
11938
12179
|
if (!entry.isFile()) continue;
|
|
11939
12180
|
if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
|
|
11940
|
-
out.push(
|
|
12181
|
+
out.push(join49(full, entry.name));
|
|
11941
12182
|
}
|
|
11942
12183
|
}
|
|
11943
12184
|
return out;
|
|
11944
12185
|
}
|
|
11945
12186
|
async function runPipeTrapCheck() {
|
|
11946
|
-
const root = (await
|
|
12187
|
+
const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
11947
12188
|
const files = shellFiles(root);
|
|
11948
12189
|
console.log(`audited ${files.length} shell file(s) under scripts/ and .githooks/ under ${root}`);
|
|
11949
12190
|
if (files.length === 0) {
|
|
@@ -11953,7 +12194,7 @@ async function runPipeTrapCheck() {
|
|
|
11953
12194
|
process.exit(1);
|
|
11954
12195
|
}
|
|
11955
12196
|
const findings = files.flatMap(
|
|
11956
|
-
(file) => findPipeTraps(
|
|
12197
|
+
(file) => findPipeTraps(readFileSync36(file, "utf8")).map(
|
|
11957
12198
|
(t) => `${relative9(root, file)}:${t.line} ${t.text}
|
|
11958
12199
|
${t.reason}`
|
|
11959
12200
|
)
|
|
@@ -11970,11 +12211,11 @@ async function runPipeTrapCheck() {
|
|
|
11970
12211
|
}
|
|
11971
12212
|
|
|
11972
12213
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
11973
|
-
import { execa as
|
|
12214
|
+
import { execa as execa18 } from "execa";
|
|
11974
12215
|
|
|
11975
12216
|
// src/lib/plugin-allowlist-convention.ts
|
|
11976
|
-
import { readFileSync as
|
|
11977
|
-
import { join as
|
|
12217
|
+
import { readFileSync as readFileSync37 } from "fs";
|
|
12218
|
+
import { join as join50 } from "path";
|
|
11978
12219
|
var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
|
|
11979
12220
|
var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
|
|
11980
12221
|
var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
|
|
@@ -11985,7 +12226,7 @@ var PLUGIN = "<plugin>";
|
|
|
11985
12226
|
var ACCOUNT = "<account>";
|
|
11986
12227
|
function read(repoRoot, relative11) {
|
|
11987
12228
|
try {
|
|
11988
|
-
return
|
|
12229
|
+
return readFileSync37(join50(repoRoot, relative11), "utf8");
|
|
11989
12230
|
} catch {
|
|
11990
12231
|
throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
|
|
11991
12232
|
}
|
|
@@ -12081,7 +12322,7 @@ Plugins would be rejected by require_service_principal (ADR-0009). Fix the glob,
|
|
|
12081
12322
|
|
|
12082
12323
|
// src/scripts/check-plugin-allowlist-convention.ts
|
|
12083
12324
|
async function runPluginAllowlistConventionCheck() {
|
|
12084
|
-
const root = (await
|
|
12325
|
+
const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12085
12326
|
let violations;
|
|
12086
12327
|
try {
|
|
12087
12328
|
violations = checkAllowlistConvention(root);
|
|
@@ -12106,34 +12347,34 @@ async function runPluginAllowlistConventionCheck() {
|
|
|
12106
12347
|
}
|
|
12107
12348
|
|
|
12108
12349
|
// src/scripts/check-plugin-collisions.ts
|
|
12109
|
-
import { existsSync as
|
|
12110
|
-
import { join as
|
|
12111
|
-
import { execa as
|
|
12350
|
+
import { existsSync as existsSync44 } from "fs";
|
|
12351
|
+
import { join as join52 } from "path";
|
|
12352
|
+
import { execa as execa19 } from "execa";
|
|
12112
12353
|
|
|
12113
12354
|
// src/lib/plugin-collision-guard.ts
|
|
12114
|
-
import { existsSync as
|
|
12115
|
-
import { join as
|
|
12355
|
+
import { existsSync as existsSync43, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
|
|
12356
|
+
import { join as join51 } from "path";
|
|
12116
12357
|
var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
|
|
12117
12358
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
|
|
12118
12359
|
function subdirectories(dir) {
|
|
12119
|
-
if (!
|
|
12120
|
-
return
|
|
12360
|
+
if (!existsSync43(dir)) return [];
|
|
12361
|
+
return readdirSync23(dir).filter((entry) => {
|
|
12121
12362
|
if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
|
|
12122
12363
|
try {
|
|
12123
|
-
return statSync14(
|
|
12364
|
+
return statSync14(join51(dir, entry)).isDirectory();
|
|
12124
12365
|
} catch {
|
|
12125
12366
|
return false;
|
|
12126
12367
|
}
|
|
12127
12368
|
});
|
|
12128
12369
|
}
|
|
12129
12370
|
function regularPackagesOf(pluginDir2) {
|
|
12130
|
-
return subdirectories(pluginDir2).filter((name) =>
|
|
12371
|
+
return subdirectories(pluginDir2).filter((name) => existsSync43(join51(pluginDir2, name, "__init__.py"))).sort();
|
|
12131
12372
|
}
|
|
12132
12373
|
function bareTestModulesOf(pluginDir2) {
|
|
12133
|
-
const testsDir =
|
|
12134
|
-
if (!
|
|
12135
|
-
if (
|
|
12136
|
-
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();
|
|
12137
12378
|
}
|
|
12138
12379
|
function findCollisions(servicesDir, pluginDirs) {
|
|
12139
12380
|
const plugins = (pluginDirs ?? subdirectories(servicesDir)).filter((name) => !name.startsWith("_")).filter((name) => name !== "api").sort();
|
|
@@ -12141,7 +12382,7 @@ function findCollisions(servicesDir, pluginDirs) {
|
|
|
12141
12382
|
const gather = (kind, namesOf) => {
|
|
12142
12383
|
const claims = /* @__PURE__ */ new Map();
|
|
12143
12384
|
for (const plugin of plugins) {
|
|
12144
|
-
for (const name of namesOf(
|
|
12385
|
+
for (const name of namesOf(join51(servicesDir, plugin))) {
|
|
12145
12386
|
claims.set(name, [...claims.get(name) ?? [], plugin]);
|
|
12146
12387
|
}
|
|
12147
12388
|
}
|
|
@@ -12178,9 +12419,9 @@ function formatCollisions(collisions) {
|
|
|
12178
12419
|
|
|
12179
12420
|
// src/scripts/check-plugin-collisions.ts
|
|
12180
12421
|
async function runPluginCollisionCheck() {
|
|
12181
|
-
const root = (await
|
|
12182
|
-
const servicesDir =
|
|
12183
|
-
if (!
|
|
12422
|
+
const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12423
|
+
const servicesDir = join52(root, "services");
|
|
12424
|
+
if (!existsSync44(servicesDir)) {
|
|
12184
12425
|
console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
|
|
12185
12426
|
return;
|
|
12186
12427
|
}
|
|
@@ -12197,11 +12438,11 @@ async function runPluginCollisionCheck() {
|
|
|
12197
12438
|
}
|
|
12198
12439
|
|
|
12199
12440
|
// src/scripts/check-plugin-terraform.ts
|
|
12200
|
-
import { execa as
|
|
12441
|
+
import { execa as execa20 } from "execa";
|
|
12201
12442
|
|
|
12202
12443
|
// src/lib/plugin-terraform-guard.ts
|
|
12203
|
-
import { existsSync as
|
|
12204
|
-
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";
|
|
12205
12446
|
var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
|
|
12206
12447
|
var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
|
|
12207
12448
|
function findPluginManifests(root) {
|
|
@@ -12209,16 +12450,16 @@ function findPluginManifests(root) {
|
|
|
12209
12450
|
const walk2 = (dir) => {
|
|
12210
12451
|
let entries;
|
|
12211
12452
|
try {
|
|
12212
|
-
entries =
|
|
12453
|
+
entries = readdirSync24(dir, { withFileTypes: true });
|
|
12213
12454
|
} catch {
|
|
12214
12455
|
return;
|
|
12215
12456
|
}
|
|
12216
12457
|
for (const entry of entries) {
|
|
12217
12458
|
if (entry.isDirectory()) {
|
|
12218
12459
|
if (SKIP_DIRS3.has(entry.name)) continue;
|
|
12219
|
-
walk2(
|
|
12460
|
+
walk2(join53(dir, entry.name));
|
|
12220
12461
|
} else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
|
|
12221
|
-
found.push(relative10(root,
|
|
12462
|
+
found.push(relative10(root, join53(dir, entry.name)).split(sep4).join("/"));
|
|
12222
12463
|
}
|
|
12223
12464
|
}
|
|
12224
12465
|
};
|
|
@@ -12228,7 +12469,7 @@ function findPluginManifests(root) {
|
|
|
12228
12469
|
function readSubscriptions(absManifestPath) {
|
|
12229
12470
|
let parsed;
|
|
12230
12471
|
try {
|
|
12231
|
-
parsed = JSON.parse(
|
|
12472
|
+
parsed = JSON.parse(readFileSync38(absManifestPath, "utf8"));
|
|
12232
12473
|
} catch {
|
|
12233
12474
|
return null;
|
|
12234
12475
|
}
|
|
@@ -12243,14 +12484,14 @@ function readSubscriptions(absManifestPath) {
|
|
|
12243
12484
|
}
|
|
12244
12485
|
function checkPluginTerraform(root) {
|
|
12245
12486
|
const violations = [];
|
|
12246
|
-
const coreManifest =
|
|
12487
|
+
const coreManifest = existsSync45(join53(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
|
|
12247
12488
|
for (const manifest of findPluginManifests(root)) {
|
|
12248
12489
|
if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
|
|
12249
|
-
const absManifest =
|
|
12490
|
+
const absManifest = join53(root, manifest);
|
|
12250
12491
|
const subscriptions = readSubscriptions(absManifest);
|
|
12251
12492
|
if (subscriptions === null) continue;
|
|
12252
12493
|
const pluginDir2 = dirname10(absManifest);
|
|
12253
|
-
if (
|
|
12494
|
+
if (existsSync45(join53(pluginDir2, "terraform"))) continue;
|
|
12254
12495
|
const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
|
|
12255
12496
|
violations.push({
|
|
12256
12497
|
manifest,
|
|
@@ -12270,7 +12511,7 @@ function formatViolations(violations) {
|
|
|
12270
12511
|
|
|
12271
12512
|
// src/scripts/check-plugin-terraform.ts
|
|
12272
12513
|
async function runPluginTerraformCheck() {
|
|
12273
|
-
const root = (await
|
|
12514
|
+
const root = (await execa20("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
12274
12515
|
const violations = checkPluginTerraform(root);
|
|
12275
12516
|
if (violations.length > 0) {
|
|
12276
12517
|
console.error("\u2717 plugin Terraform guard: event subscriptions with no infrastructure\n");
|
|
@@ -12281,13 +12522,13 @@ async function runPluginTerraformCheck() {
|
|
|
12281
12522
|
}
|
|
12282
12523
|
|
|
12283
12524
|
// src/scripts/check-plugin-tool-supply.ts
|
|
12284
|
-
import { existsSync as
|
|
12285
|
-
import { join as
|
|
12286
|
-
import { execa as
|
|
12525
|
+
import { existsSync as existsSync47 } from "fs";
|
|
12526
|
+
import { join as join55 } from "path";
|
|
12527
|
+
import { execa as execa21 } from "execa";
|
|
12287
12528
|
|
|
12288
12529
|
// src/lib/plugin-tool-supply-audit.ts
|
|
12289
|
-
import { existsSync as
|
|
12290
|
-
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";
|
|
12291
12532
|
|
|
12292
12533
|
// src/lib/openrouter-model-snapshot.ts
|
|
12293
12534
|
var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
|
|
@@ -12698,13 +12939,13 @@ var OPENROUTER_MODEL_IDS = [
|
|
|
12698
12939
|
function listDirs(root) {
|
|
12699
12940
|
let entries;
|
|
12700
12941
|
try {
|
|
12701
|
-
entries =
|
|
12942
|
+
entries = readdirSync25(root);
|
|
12702
12943
|
} catch {
|
|
12703
12944
|
return [];
|
|
12704
12945
|
}
|
|
12705
12946
|
return entries.filter((e) => {
|
|
12706
12947
|
try {
|
|
12707
|
-
return statSync15(
|
|
12948
|
+
return statSync15(join54(root, e)).isDirectory();
|
|
12708
12949
|
} catch {
|
|
12709
12950
|
return false;
|
|
12710
12951
|
}
|
|
@@ -12715,12 +12956,12 @@ function walkFiles2(root, accept, skipDir) {
|
|
|
12715
12956
|
const walk2 = (dir) => {
|
|
12716
12957
|
let entries;
|
|
12717
12958
|
try {
|
|
12718
|
-
entries =
|
|
12959
|
+
entries = readdirSync25(dir);
|
|
12719
12960
|
} catch {
|
|
12720
12961
|
return;
|
|
12721
12962
|
}
|
|
12722
12963
|
for (const entry of entries) {
|
|
12723
|
-
const p =
|
|
12964
|
+
const p = join54(dir, entry);
|
|
12724
12965
|
let st;
|
|
12725
12966
|
try {
|
|
12726
12967
|
st = statSync15(p);
|
|
@@ -12746,14 +12987,14 @@ function pluginPythonFiles(pluginDir2) {
|
|
|
12746
12987
|
);
|
|
12747
12988
|
}
|
|
12748
12989
|
function pluginTerraformFiles(pluginDir2) {
|
|
12749
|
-
const tfDir =
|
|
12990
|
+
const tfDir = join54(pluginDir2, "terraform");
|
|
12750
12991
|
let entries;
|
|
12751
12992
|
try {
|
|
12752
|
-
entries =
|
|
12993
|
+
entries = readdirSync25(tfDir);
|
|
12753
12994
|
} catch {
|
|
12754
12995
|
return [];
|
|
12755
12996
|
}
|
|
12756
|
-
return entries.filter((e) => e.endsWith(".tf")).map((e) =>
|
|
12997
|
+
return entries.filter((e) => e.endsWith(".tf")).map((e) => join54(tfDir, e)).sort();
|
|
12757
12998
|
}
|
|
12758
12999
|
function extractManifestTools(manifestText) {
|
|
12759
13000
|
let parsed;
|
|
@@ -13005,8 +13246,8 @@ function isSnapshotStale(fetchedAt, now) {
|
|
|
13005
13246
|
function normalizeModelId(id) {
|
|
13006
13247
|
return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
|
|
13007
13248
|
}
|
|
13008
|
-
var CONFIG_PY_PATH =
|
|
13009
|
-
var ORCHESTRATION_SCHEMA_PATH =
|
|
13249
|
+
var CONFIG_PY_PATH = join54("services", "api", "src", "api", "config.py");
|
|
13250
|
+
var ORCHESTRATION_SCHEMA_PATH = join54(
|
|
13010
13251
|
"services",
|
|
13011
13252
|
"api",
|
|
13012
13253
|
"src",
|
|
@@ -13018,10 +13259,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13018
13259
|
const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
|
|
13019
13260
|
const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
|
|
13020
13261
|
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
13021
|
-
const configPath =
|
|
13022
|
-
const orchestrationPath =
|
|
13023
|
-
const configMissing = !
|
|
13024
|
-
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);
|
|
13025
13266
|
const knownSet = new Set(knownModelIds);
|
|
13026
13267
|
const snapshotEmpty = knownModelIds.length === 0;
|
|
13027
13268
|
const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
|
|
@@ -13039,13 +13280,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13039
13280
|
};
|
|
13040
13281
|
let settingsBlind = false;
|
|
13041
13282
|
if (!configMissing) {
|
|
13042
|
-
const settingsFields = extractSettingsModelFields(
|
|
13283
|
+
const settingsFields = extractSettingsModelFields(readFileSync39(configPath, "utf8"));
|
|
13043
13284
|
if (settingsFields.length === 0) settingsBlind = true;
|
|
13044
13285
|
for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
|
|
13045
13286
|
}
|
|
13046
13287
|
let curatedFieldsBlind = false;
|
|
13047
13288
|
if (!orchestrationSchemaMissing) {
|
|
13048
|
-
const curated = extractCuratedModelFields(
|
|
13289
|
+
const curated = extractCuratedModelFields(readFileSync39(orchestrationPath, "utf8"));
|
|
13049
13290
|
if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
|
|
13050
13291
|
curatedFieldsBlind = true;
|
|
13051
13292
|
}
|
|
@@ -13092,7 +13333,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
|
|
|
13092
13333
|
function discoverPluginDirs(pluginsRoot) {
|
|
13093
13334
|
return listDirs(pluginsRoot).filter((name) => {
|
|
13094
13335
|
try {
|
|
13095
|
-
return statSync15(
|
|
13336
|
+
return statSync15(join54(pluginsRoot, name, "biffo.plugin.json")).isFile();
|
|
13096
13337
|
} catch {
|
|
13097
13338
|
return false;
|
|
13098
13339
|
}
|
|
@@ -13105,8 +13346,8 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13105
13346
|
let terraformBlind = false;
|
|
13106
13347
|
let totalDeclaredTools = 0;
|
|
13107
13348
|
for (const name of pluginNames) {
|
|
13108
|
-
const pluginDir2 =
|
|
13109
|
-
const manifestText =
|
|
13349
|
+
const pluginDir2 = join54(pluginsRoot, name);
|
|
13350
|
+
const manifestText = readFileSync39(join54(pluginDir2, "biffo.plugin.json"), "utf8");
|
|
13110
13351
|
const manifest = extractManifestTools(manifestText);
|
|
13111
13352
|
if (manifest.parseError) {
|
|
13112
13353
|
findings.push({
|
|
@@ -13124,13 +13365,13 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13124
13365
|
totalDeclaredTools += manifest.tools.length;
|
|
13125
13366
|
const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
|
|
13126
13367
|
file: f,
|
|
13127
|
-
text:
|
|
13368
|
+
text: readFileSync39(f, "utf8")
|
|
13128
13369
|
}));
|
|
13129
13370
|
const resolver = buildSymbolResolver(pySources);
|
|
13130
13371
|
const registry = extractToolRegistryEntries(pySources, resolver);
|
|
13131
13372
|
if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
|
|
13132
13373
|
const tfFiles = pluginTerraformFiles(pluginDir2);
|
|
13133
|
-
const tfText = tfFiles.map((f) =>
|
|
13374
|
+
const tfText = tfFiles.map((f) => readFileSync39(f, "utf8")).join("\n");
|
|
13134
13375
|
const terraform = extractTerraformEnvKeys(tfText);
|
|
13135
13376
|
if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
|
|
13136
13377
|
for (const toolName of manifest.tools) {
|
|
@@ -13204,7 +13445,7 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13204
13445
|
requiredEnvVars: envResult.envVars,
|
|
13205
13446
|
missingEnvVars: anyWired ? [] : envResult.envVars,
|
|
13206
13447
|
status: anyWired ? "ok" : "missing-env",
|
|
13207
|
-
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`
|
|
13208
13449
|
});
|
|
13209
13450
|
}
|
|
13210
13451
|
}
|
|
@@ -13235,10 +13476,10 @@ function auditPluginToolSupply(pluginsRoot) {
|
|
|
13235
13476
|
|
|
13236
13477
|
// src/scripts/check-plugin-tool-supply.ts
|
|
13237
13478
|
async function runPluginToolSupplyCheck() {
|
|
13238
|
-
const root = (await
|
|
13479
|
+
const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13239
13480
|
let allOk = true;
|
|
13240
|
-
const pluginsRoot =
|
|
13241
|
-
if (!
|
|
13481
|
+
const pluginsRoot = join55(root, "services", "_plugins");
|
|
13482
|
+
if (!existsSync47(pluginsRoot)) {
|
|
13242
13483
|
console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
|
|
13243
13484
|
} else {
|
|
13244
13485
|
const report = auditPluginToolSupply(pluginsRoot);
|
|
@@ -13268,8 +13509,8 @@ async function runPluginToolSupplyCheck() {
|
|
|
13268
13509
|
console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
|
|
13269
13510
|
}
|
|
13270
13511
|
}
|
|
13271
|
-
const servicesApiRoot =
|
|
13272
|
-
if (!
|
|
13512
|
+
const servicesApiRoot = join55(root, "services", "api");
|
|
13513
|
+
if (!existsSync47(servicesApiRoot)) {
|
|
13273
13514
|
console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
|
|
13274
13515
|
} else {
|
|
13275
13516
|
const modelReport = auditDeclaredModelIds(root);
|
|
@@ -13315,7 +13556,7 @@ async function runPluginToolSupplyCheck() {
|
|
|
13315
13556
|
}
|
|
13316
13557
|
|
|
13317
13558
|
// src/scripts/check-release-subject.ts
|
|
13318
|
-
import { execa as
|
|
13559
|
+
import { execa as execa22 } from "execa";
|
|
13319
13560
|
|
|
13320
13561
|
// src/lib/release-version.ts
|
|
13321
13562
|
var MINOR_TYPES = /* @__PURE__ */ new Set(["feat"]);
|
|
@@ -13352,7 +13593,7 @@ async function fetchPrTitleViaGh({
|
|
|
13352
13593
|
PR_NUMBER,
|
|
13353
13594
|
GH_REPO
|
|
13354
13595
|
}) {
|
|
13355
|
-
const { stdout } = await
|
|
13596
|
+
const { stdout } = await execa22(
|
|
13356
13597
|
"gh",
|
|
13357
13598
|
["pr", "view", PR_NUMBER, "--repo", GH_REPO, "--json", "title", "--jq", ".title"],
|
|
13358
13599
|
{ env: { ...process.env, GH_TOKEN } }
|
|
@@ -13388,7 +13629,7 @@ async function resolveReleaseSubject({
|
|
|
13388
13629
|
);
|
|
13389
13630
|
}
|
|
13390
13631
|
}
|
|
13391
|
-
return (await
|
|
13632
|
+
return (await execa22("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
|
|
13392
13633
|
}
|
|
13393
13634
|
async function runReleaseSubjectCheck(argv) {
|
|
13394
13635
|
const base = process.env["GITHUB_BASE_REF"] ?? argv[0];
|
|
@@ -13396,9 +13637,9 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
13396
13637
|
console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
|
|
13397
13638
|
process.exit(2);
|
|
13398
13639
|
}
|
|
13399
|
-
const root = (await
|
|
13400
|
-
await
|
|
13401
|
-
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`], {
|
|
13402
13643
|
cwd: root
|
|
13403
13644
|
});
|
|
13404
13645
|
const changedFiles = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
@@ -13446,13 +13687,13 @@ async function runReleaseSubjectCheck(argv) {
|
|
|
13446
13687
|
}
|
|
13447
13688
|
|
|
13448
13689
|
// src/scripts/check-skeleton-drift.ts
|
|
13449
|
-
import { existsSync as
|
|
13450
|
-
import { join as
|
|
13451
|
-
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";
|
|
13452
13693
|
|
|
13453
13694
|
// src/lib/skeleton-drift-guard.ts
|
|
13454
|
-
import { readFileSync as
|
|
13455
|
-
import { join as
|
|
13695
|
+
import { readFileSync as readFileSync40, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
|
|
13696
|
+
import { join as join56 } from "path";
|
|
13456
13697
|
var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
|
|
13457
13698
|
var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
|
|
13458
13699
|
var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
|
|
@@ -13510,13 +13751,13 @@ function walk(dir, base = dir) {
|
|
|
13510
13751
|
const out = [];
|
|
13511
13752
|
let entries;
|
|
13512
13753
|
try {
|
|
13513
|
-
entries =
|
|
13754
|
+
entries = readdirSync26(dir);
|
|
13514
13755
|
} catch {
|
|
13515
13756
|
return out;
|
|
13516
13757
|
}
|
|
13517
13758
|
for (const entry of entries) {
|
|
13518
13759
|
if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
|
|
13519
|
-
const abs =
|
|
13760
|
+
const abs = join56(dir, entry);
|
|
13520
13761
|
let isDir;
|
|
13521
13762
|
try {
|
|
13522
13763
|
isDir = statSync16(abs).isDirectory();
|
|
@@ -13538,7 +13779,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
|
|
|
13538
13779
|
if (!rule.appliesTo(rel)) continue;
|
|
13539
13780
|
let contents;
|
|
13540
13781
|
try {
|
|
13541
|
-
contents =
|
|
13782
|
+
contents = readFileSync40(join56(skeletonRoot, rel), "utf8");
|
|
13542
13783
|
} catch {
|
|
13543
13784
|
continue;
|
|
13544
13785
|
}
|
|
@@ -13567,23 +13808,23 @@ function formatViolations2(violations) {
|
|
|
13567
13808
|
|
|
13568
13809
|
// src/scripts/check-skeleton-drift.ts
|
|
13569
13810
|
function discoverSkeletons(root) {
|
|
13570
|
-
const skeletonsDir =
|
|
13811
|
+
const skeletonsDir = join57(root, "_skeletons");
|
|
13571
13812
|
let entries;
|
|
13572
13813
|
try {
|
|
13573
|
-
entries =
|
|
13814
|
+
entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
13574
13815
|
} catch {
|
|
13575
13816
|
return [];
|
|
13576
13817
|
}
|
|
13577
|
-
return entries.filter((name) =>
|
|
13818
|
+
return entries.filter((name) => existsSync48(join57(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
|
|
13578
13819
|
}
|
|
13579
13820
|
async function runSkeletonDriftCheck() {
|
|
13580
|
-
const root = (await
|
|
13821
|
+
const root = (await execa23("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13581
13822
|
const skeletons = discoverSkeletons(root);
|
|
13582
13823
|
let filesConsidered = 0;
|
|
13583
13824
|
for (const name of skeletons) {
|
|
13584
|
-
const skeletonRoot =
|
|
13825
|
+
const skeletonRoot = join57(root, "_skeletons", name);
|
|
13585
13826
|
filesConsidered += findWorkflowFiles(skeletonRoot).length;
|
|
13586
|
-
if (
|
|
13827
|
+
if (existsSync48(join57(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
|
|
13587
13828
|
filesConsidered += 1;
|
|
13588
13829
|
}
|
|
13589
13830
|
}
|
|
@@ -13597,7 +13838,7 @@ async function runSkeletonDriftCheck() {
|
|
|
13597
13838
|
process.exit(1);
|
|
13598
13839
|
}
|
|
13599
13840
|
const violations = skeletons.flatMap(
|
|
13600
|
-
(name) => auditSkeleton(
|
|
13841
|
+
(name) => auditSkeleton(join57(root, "_skeletons", name), name)
|
|
13601
13842
|
);
|
|
13602
13843
|
if (violations.length > 0) {
|
|
13603
13844
|
console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
|
|
@@ -13609,9 +13850,9 @@ async function runSkeletonDriftCheck() {
|
|
|
13609
13850
|
}
|
|
13610
13851
|
|
|
13611
13852
|
// src/scripts/check-terraform-input.ts
|
|
13612
|
-
import { execa as
|
|
13853
|
+
import { execa as execa24 } from "execa";
|
|
13613
13854
|
async function runTerraformInputCheck() {
|
|
13614
|
-
const root = (await
|
|
13855
|
+
const root = (await execa24("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
|
|
13615
13856
|
const files = findWorkflowFiles(root);
|
|
13616
13857
|
console.log(`audited ${files.length} workflow file(s) under ${root}`);
|
|
13617
13858
|
if (files.length === 0) {
|
|
@@ -13701,6 +13942,11 @@ checkCommand.command("codeql-suppression").description(
|
|
|
13701
13942
|
).action(async () => {
|
|
13702
13943
|
await runCodeqlSuppressionCheck();
|
|
13703
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
|
+
});
|
|
13704
13950
|
checkCommand.command("skeleton-drift").description(
|
|
13705
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"
|
|
13706
13952
|
).action(async () => {
|
|
@@ -13735,8 +13981,8 @@ function rawArgsAfter(subcommand) {
|
|
|
13735
13981
|
}
|
|
13736
13982
|
|
|
13737
13983
|
// src/commands/doctor.ts
|
|
13738
|
-
import { existsSync as
|
|
13739
|
-
import { join as
|
|
13984
|
+
import { existsSync as existsSync49, readFileSync as readFileSync41 } from "fs";
|
|
13985
|
+
import { join as join58, resolve as resolve20 } from "path";
|
|
13740
13986
|
import chalk21 from "chalk";
|
|
13741
13987
|
import { Command as Command25 } from "commander";
|
|
13742
13988
|
|
|
@@ -13915,10 +14161,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
|
|
|
13915
14161
|
return runDoctorChecks(facts);
|
|
13916
14162
|
}
|
|
13917
14163
|
function readLocalCoreVersion(cwd) {
|
|
13918
|
-
const path =
|
|
13919
|
-
if (!
|
|
14164
|
+
const path = join58(cwd, INSTANCE_CORE_FILE);
|
|
14165
|
+
if (!existsSync49(path)) return null;
|
|
13920
14166
|
try {
|
|
13921
|
-
return extractVersionField(
|
|
14167
|
+
return extractVersionField(readFileSync41(path, "utf8"));
|
|
13922
14168
|
} catch {
|
|
13923
14169
|
return null;
|
|
13924
14170
|
}
|
|
@@ -13938,10 +14184,10 @@ function extractVersionField(contents) {
|
|
|
13938
14184
|
return match?.[1] ?? null;
|
|
13939
14185
|
}
|
|
13940
14186
|
function readFossil(cwd) {
|
|
13941
|
-
const path =
|
|
13942
|
-
if (!
|
|
14187
|
+
const path = join58(cwd, CORE_VERSION_FILE);
|
|
14188
|
+
if (!existsSync49(path)) return null;
|
|
13943
14189
|
try {
|
|
13944
|
-
const value =
|
|
14190
|
+
const value = readFileSync41(path, "utf8").trim();
|
|
13945
14191
|
return value === "" ? null : value;
|
|
13946
14192
|
} catch {
|
|
13947
14193
|
return null;
|
|
@@ -14390,13 +14636,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
|
|
|
14390
14636
|
import { Command as Command27 } from "commander";
|
|
14391
14637
|
|
|
14392
14638
|
// src/lib/packaged-scripts.ts
|
|
14393
|
-
import { existsSync as
|
|
14394
|
-
import { dirname as dirname11, join as
|
|
14639
|
+
import { existsSync as existsSync50 } from "fs";
|
|
14640
|
+
import { dirname as dirname11, join as join59 } from "path";
|
|
14395
14641
|
function findPackagedScript(startDir, relativePath) {
|
|
14396
14642
|
let dir = startDir;
|
|
14397
14643
|
for (; ; ) {
|
|
14398
|
-
const candidate =
|
|
14399
|
-
if (
|
|
14644
|
+
const candidate = join59(dir, relativePath);
|
|
14645
|
+
if (existsSync50(candidate)) return candidate;
|
|
14400
14646
|
const parent = dirname11(dir);
|
|
14401
14647
|
if (parent === dir) return null;
|
|
14402
14648
|
dir = parent;
|