patchwork-os 1.2.0-beta.2.canary.764 → 1.2.0-beta.2.canary.766
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/claudeOrchestrator.d.ts +12 -0
- package/dist/claudeOrchestrator.js +154 -12
- package/dist/claudeOrchestrator.js.map +1 -1
- package/dist/deploymentFreshness.d.ts +18 -0
- package/dist/deploymentFreshness.js +94 -0
- package/dist/deploymentFreshness.js.map +1 -1
- package/dist/index.js +75 -102
- package/dist/index.js.map +1 -1
- package/dist/maintenance/prOutcomeLedger.d.ts +12 -1
- package/dist/maintenance/prOutcomeLedger.js +28 -0
- package/dist/maintenance/prOutcomeLedger.js.map +1 -1
- package/dist/maintenance/sweep.d.ts +98 -0
- package/dist/maintenance/sweep.js +197 -0
- package/dist/maintenance/sweep.js.map +1 -0
- package/dist/maintenance/sweepCollect.d.ts +33 -0
- package/dist/maintenance/sweepCollect.js +109 -0
- package/dist/maintenance/sweepCollect.js.map +1 -0
- package/dist/privacy/boundaryReceiptLog.d.ts +33 -0
- package/dist/privacy/boundaryReceiptLog.js +32 -0
- package/dist/privacy/boundaryReceiptLog.js.map +1 -1
- package/dist/privacy/boundaryReceipts.d.ts +8 -0
- package/dist/privacy/boundaryReceipts.js +8 -0
- package/dist/privacy/boundaryReceipts.js.map +1 -1
- package/dist/privacy/shadowLog.d.ts +33 -8
- package/dist/privacy/shadowLog.js +11 -0
- package/dist/privacy/shadowLog.js.map +1 -1
- package/dist/privacy/undeclaredSteps.d.ts +14 -0
- package/dist/privacy/undeclaredSteps.js +44 -0
- package/dist/privacy/undeclaredSteps.js.map +1 -1
- package/dist/recipes/agentExecutor.d.ts +8 -1
- package/dist/recipes/agentExecutor.js +5 -0
- package/dist/recipes/agentExecutor.js.map +1 -1
- package/dist/recipes/yamlRunner.js +11 -21
- package/dist/recipes/yamlRunner.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -179,6 +179,7 @@ async function downloadVsixFromOpenVsx() {
|
|
|
179
179
|
const KNOWN_SUBCOMMANDS = [
|
|
180
180
|
"init",
|
|
181
181
|
"evidence",
|
|
182
|
+
"sweep",
|
|
182
183
|
"patchwork-init",
|
|
183
184
|
"start-all",
|
|
184
185
|
"install-extension",
|
|
@@ -4189,30 +4190,13 @@ if (process.argv[2] === "pr-outcomes") {
|
|
|
4189
4190
|
process.exit(0);
|
|
4190
4191
|
}
|
|
4191
4192
|
(async () => {
|
|
4192
|
-
const {
|
|
4193
|
+
const { appendFileSync } = await import("node:fs");
|
|
4193
4194
|
const { join } = await import("node:path");
|
|
4194
4195
|
const { patchworkPath } = await import("./patchworkHome.js");
|
|
4195
4196
|
const { execFileSync } = await import("node:child_process");
|
|
4196
|
-
const { toObservation, dedupeAgainst, summarise, formatLedgerSummary } = await import("./maintenance/prOutcomeLedger.js");
|
|
4197
|
+
const { toObservation, dedupeAgainst, readObservations, summarise, formatLedgerSummary, } = await import("./maintenance/prOutcomeLedger.js");
|
|
4197
4198
|
const file = join(patchworkPath(), "pr_outcomes.jsonl");
|
|
4198
|
-
const read = () =>
|
|
4199
|
-
if (!existsSync(file))
|
|
4200
|
-
return [];
|
|
4201
|
-
const out = [];
|
|
4202
|
-
for (const line of readFileSync(file, "utf-8").split("\n")) {
|
|
4203
|
-
if (line.trim() === "")
|
|
4204
|
-
continue;
|
|
4205
|
-
try {
|
|
4206
|
-
out.push(JSON.parse(line));
|
|
4207
|
-
}
|
|
4208
|
-
catch {
|
|
4209
|
-
// A corrupt line is skipped, never repaired: rewriting an
|
|
4210
|
-
// append-only evidence file to make it parse is how evidence
|
|
4211
|
-
// quietly becomes fiction.
|
|
4212
|
-
}
|
|
4213
|
-
}
|
|
4214
|
-
return out;
|
|
4215
|
-
};
|
|
4199
|
+
const read = () => readObservations(file);
|
|
4216
4200
|
const sub = args[0] ?? "show";
|
|
4217
4201
|
if (sub === "show") {
|
|
4218
4202
|
const rows = read();
|
|
@@ -4331,6 +4315,71 @@ if (process.argv[2] === "pr-outcomes") {
|
|
|
4331
4315
|
process.exit(2);
|
|
4332
4316
|
})();
|
|
4333
4317
|
}
|
|
4318
|
+
if (process.argv[2] === "sweep") {
|
|
4319
|
+
const args = process.argv.slice(3);
|
|
4320
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
4321
|
+
process.stdout.write("Usage: patchwork sweep [--dir <path>] [--expect-running [N]] [--no-write] [--json]\n\n" +
|
|
4322
|
+
"What MOVED since the last sweep. Composes doctor, workers validate,\n" +
|
|
4323
|
+
"evidence, privacy undeclared and pr-outcomes into one reading, appends it\n" +
|
|
4324
|
+
"to sweep_snapshots.jsonl, and diffs it against the previous one.\n\n" +
|
|
4325
|
+
"Two of the readings are GATES and can fail this command: deployment\n" +
|
|
4326
|
+
"freshness and worker-manifest validity. Everything else is drift and is\n" +
|
|
4327
|
+
"reported without failing — the evidence ratios fall by construction as\n" +
|
|
4328
|
+
"ledgers accrue rows, and an undeclared agent step is ADR-0021's documented\n" +
|
|
4329
|
+
"default. Wiring those to an exit code would make sweep permanently red,\n" +
|
|
4330
|
+
"which is how a real warning gets ignored.\n\n" +
|
|
4331
|
+
"A first run is a BASELINE, never 'no changes': nothing was observed to\n" +
|
|
4332
|
+
"change, and there was nothing to compare against, are different facts.\n\n" +
|
|
4333
|
+
"The snapshot holds COUNTS ONLY — no recipe name, no path, no id — because\n" +
|
|
4334
|
+
"two of its inputs return operator data and a health check is the last\n" +
|
|
4335
|
+
"place anyone thinks to look for accumulated secrets.\n\n" +
|
|
4336
|
+
" --dir <path> ledger directory (default: $PATCHWORK_HOME)\n" +
|
|
4337
|
+
" --expect-running [N] bridges that ought to be up (default: no expectation)\n" +
|
|
4338
|
+
" --no-write report without appending a snapshot\n" +
|
|
4339
|
+
" --json emit the reading and the delta\n\n" +
|
|
4340
|
+
"Exits 1 only when a gate flipped healthy -> unhealthy.\n");
|
|
4341
|
+
process.exit(0);
|
|
4342
|
+
}
|
|
4343
|
+
(async () => {
|
|
4344
|
+
try {
|
|
4345
|
+
const { collectSweep } = await import("./maintenance/sweepCollect.js");
|
|
4346
|
+
const { appendSnapshot, diffReadings, formatSweep, readLastSnapshot } = await import("./maintenance/sweep.js");
|
|
4347
|
+
const { patchworkHome } = await import("./patchworkHome.js");
|
|
4348
|
+
const dirIdx = args.indexOf("--dir");
|
|
4349
|
+
const dir = dirIdx !== -1 && args[dirIdx + 1] ? args[dirIdx + 1] : patchworkHome();
|
|
4350
|
+
const expectIdx = args.indexOf("--expect-running");
|
|
4351
|
+
let expectRunning;
|
|
4352
|
+
if (expectIdx !== -1) {
|
|
4353
|
+
const raw = args[expectIdx + 1];
|
|
4354
|
+
const parsed = raw !== undefined && !raw.startsWith("--") ? Number(raw) : 1;
|
|
4355
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
4356
|
+
process.stderr.write("[sweep] --expect-running takes a non-negative integer\n");
|
|
4357
|
+
process.exit(2);
|
|
4358
|
+
}
|
|
4359
|
+
expectRunning = parsed;
|
|
4360
|
+
}
|
|
4361
|
+
// Read the previous snapshot BEFORE appending this one, or the diff
|
|
4362
|
+
// compares the reading against itself and every sweep reports nothing.
|
|
4363
|
+
const previous = readLastSnapshot(dir);
|
|
4364
|
+
const reading = collectSweep({
|
|
4365
|
+
dir: dir,
|
|
4366
|
+
...(expectRunning !== undefined ? { expectRunning } : {}),
|
|
4367
|
+
});
|
|
4368
|
+
const delta = diffReadings(previous, reading);
|
|
4369
|
+
const wrote = !args.includes("--no-write");
|
|
4370
|
+
if (wrote)
|
|
4371
|
+
appendSnapshot(dir, reading);
|
|
4372
|
+
process.stdout.write(args.includes("--json")
|
|
4373
|
+
? `${JSON.stringify({ reading, delta }, null, 2)}\n`
|
|
4374
|
+
: formatSweep(reading, delta, { wrote }));
|
|
4375
|
+
process.exit(delta.regressed ? 1 : 0);
|
|
4376
|
+
}
|
|
4377
|
+
catch (err) {
|
|
4378
|
+
process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
4379
|
+
process.exit(1);
|
|
4380
|
+
}
|
|
4381
|
+
})();
|
|
4382
|
+
}
|
|
4334
4383
|
if (process.argv[2] === "evidence") {
|
|
4335
4384
|
const args = process.argv.slice(3);
|
|
4336
4385
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -4790,56 +4839,16 @@ if (process.argv[2] === "doctor" && process.argv[3] !== "health") {
|
|
|
4790
4839
|
process.exit(0);
|
|
4791
4840
|
}
|
|
4792
4841
|
(async () => {
|
|
4793
|
-
const {
|
|
4794
|
-
const { join } = await import("node:path");
|
|
4795
|
-
const os = await import("node:os");
|
|
4796
|
-
const { fileURLToPath } = await import("node:url");
|
|
4797
|
-
const { assessDeploymentFreshness, formatFreshness } = await import("./deploymentFreshness.js");
|
|
4842
|
+
const { assessDeploymentFreshness, discoverLocks, formatFreshness, installedBuildTimeMs, } = await import("./deploymentFreshness.js");
|
|
4798
4843
|
// The installed build we are comparing against. Falls back to this
|
|
4799
4844
|
// process's own dist when the global package is absent (running from a
|
|
4800
4845
|
// checkout), which is the honest reference in that case.
|
|
4801
|
-
|
|
4802
|
-
for (const candidate of [
|
|
4803
|
-
join(process.env.npm_config_prefix ?? "/opt/homebrew", "lib/node_modules/patchwork-os/dist/index.js"),
|
|
4804
|
-
// `fileURLToPath`, NOT `.pathname`. On Windows a file URL's pathname is
|
|
4805
|
-
// `/D:/a/...` — a leading slash before the drive letter — which statSync
|
|
4806
|
-
// cannot open, so this fallback never resolved there and `patchwork
|
|
4807
|
-
// doctor` from a checkout always exited 2 with "could not locate an
|
|
4808
|
-
// installed build". Found by the dispatch test added alongside this, on
|
|
4809
|
-
// the Windows CI cells only.
|
|
4810
|
-
fileURLToPath(new URL("./index.js", import.meta.url)),
|
|
4811
|
-
]) {
|
|
4812
|
-
try {
|
|
4813
|
-
buildTimeMs = statSync(candidate).mtimeMs;
|
|
4814
|
-
break;
|
|
4815
|
-
}
|
|
4816
|
-
catch {
|
|
4817
|
-
// try the next candidate
|
|
4818
|
-
}
|
|
4819
|
-
}
|
|
4846
|
+
const buildTimeMs = installedBuildTimeMs();
|
|
4820
4847
|
if (buildTimeMs === undefined) {
|
|
4821
4848
|
process.stderr.write("[deployment] could not locate an installed build to compare against\n");
|
|
4822
4849
|
process.exit(2);
|
|
4823
4850
|
}
|
|
4824
|
-
const
|
|
4825
|
-
const locks = [];
|
|
4826
|
-
try {
|
|
4827
|
-
for (const f of readdirSync(dir)) {
|
|
4828
|
-
if (!f.endsWith(".lock"))
|
|
4829
|
-
continue;
|
|
4830
|
-
try {
|
|
4831
|
-
const d = JSON.parse(readFileSync(join(dir, f), "utf-8"));
|
|
4832
|
-
if (typeof d?.pid === "number")
|
|
4833
|
-
locks.push({ ...d, file: f });
|
|
4834
|
-
}
|
|
4835
|
-
catch {
|
|
4836
|
-
// An unparseable lock is not this command's problem to solve.
|
|
4837
|
-
}
|
|
4838
|
-
}
|
|
4839
|
-
}
|
|
4840
|
-
catch {
|
|
4841
|
-
// No lock dir ⇒ no bridges ⇒ reported as "nothing running".
|
|
4842
|
-
}
|
|
4851
|
+
const locks = discoverLocks();
|
|
4843
4852
|
// `--expect-running [N]` (#1481). Only the caller knows how many bridges
|
|
4844
4853
|
// ought to be up — this command cannot discover it — so the expectation is
|
|
4845
4854
|
// stated rather than inferred. Bare `--expect-running` means "at least one",
|
|
@@ -4998,55 +5007,19 @@ if (process.argv[2] === "privacy") {
|
|
|
4998
5007
|
// every installed recipe, which agent steps declare no data_policy, and
|
|
4999
5008
|
// what flows into them".
|
|
5000
5009
|
if (args[0] === "undeclared") {
|
|
5001
|
-
const {
|
|
5002
|
-
const pathMod = await import("node:path");
|
|
5003
|
-
const { parse: parseYaml } = await import("yaml");
|
|
5004
|
-
const { undeclaredInRecipe, formatUndeclared } = await import("./privacy/undeclaredSteps.js");
|
|
5010
|
+
const { scanRecipeDir, formatUndeclared } = await import("./privacy/undeclaredSteps.js");
|
|
5005
5011
|
const dirIdx = args.indexOf("--dir");
|
|
5006
5012
|
const dir = dirIdx !== -1 && args[dirIdx + 1]
|
|
5007
5013
|
? args[dirIdx + 1]
|
|
5008
5014
|
: patchworkPath("recipes");
|
|
5009
|
-
let
|
|
5015
|
+
let report;
|
|
5010
5016
|
try {
|
|
5011
|
-
|
|
5017
|
+
report = scanRecipeDir(dir);
|
|
5012
5018
|
}
|
|
5013
5019
|
catch {
|
|
5014
5020
|
process.stderr.write(`[privacy] cannot read ${dir}\n`);
|
|
5015
5021
|
process.exit(1);
|
|
5016
5022
|
}
|
|
5017
|
-
let agentSteps = 0;
|
|
5018
|
-
let declared = 0;
|
|
5019
|
-
const undeclared = [];
|
|
5020
|
-
const unreadable = [];
|
|
5021
|
-
let recipesScanned = 0;
|
|
5022
|
-
for (const f of entries.sort()) {
|
|
5023
|
-
if (!/\.ya?ml$/i.test(f))
|
|
5024
|
-
continue;
|
|
5025
|
-
recipesScanned++;
|
|
5026
|
-
let parsed;
|
|
5027
|
-
try {
|
|
5028
|
-
parsed = parseYaml(readFileSync(pathMod.join(dir, f), "utf-8"));
|
|
5029
|
-
}
|
|
5030
|
-
catch {
|
|
5031
|
-
unreadable.push(f);
|
|
5032
|
-
continue;
|
|
5033
|
-
}
|
|
5034
|
-
const name = parsed?.name &&
|
|
5035
|
-
typeof parsed.name === "string"
|
|
5036
|
-
? parsed.name
|
|
5037
|
-
: f;
|
|
5038
|
-
const r = undeclaredInRecipe(name, parsed);
|
|
5039
|
-
agentSteps += r.agentSteps;
|
|
5040
|
-
declared += r.declared;
|
|
5041
|
-
undeclared.push(...r.steps);
|
|
5042
|
-
}
|
|
5043
|
-
const report = {
|
|
5044
|
-
recipesScanned,
|
|
5045
|
-
agentSteps,
|
|
5046
|
-
declared,
|
|
5047
|
-
undeclared,
|
|
5048
|
-
unreadable,
|
|
5049
|
-
};
|
|
5050
5023
|
process.stdout.write(args.includes("--json")
|
|
5051
5024
|
? `${JSON.stringify(report, null, 2)}\n`
|
|
5052
5025
|
: formatUndeclared(report));
|