cohorte 2.10.1 → 3.0.0-dev.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/README.md +7 -0
- package/assets/manifest.json +193 -0
- package/assets/migrations/config/README.md +12 -0
- package/assets/migrations/state/0001_init.sql +154 -0
- package/assets/prompts/README.md +11 -0
- package/assets/prompts/agents/fixer.md +7 -0
- package/assets/prompts/agents/implementer.md +12 -0
- package/assets/prompts/agents/reviewer.md +8 -0
- package/assets/prompts/agents/security-reviewer.md +7 -0
- package/assets/prompts/phases/build.md +5 -0
- package/assets/prompts/phases/review.md +5 -0
- package/assets/prompts/phases/test.md +5 -0
- package/assets/prompts/system.md +11 -0
- package/assets/schemas/agent-output.schema.json +225 -0
- package/assets/schemas/auth-status.schema.json +87 -0
- package/assets/schemas/command-result.schema.json +192 -0
- package/assets/schemas/commands.schema.json +952 -0
- package/assets/schemas/config.schema.json +2259 -0
- package/assets/schemas/doctor-report.schema.json +104 -0
- package/assets/schemas/events.schema.json +8514 -0
- package/assets/schemas/fake-script.schema.json +549 -0
- package/assets/schemas/inspect.schema.json +3133 -0
- package/assets/schemas/manifest.schema.json +79 -0
- package/assets/schemas/ownership.schema.json +52 -0
- package/assets/schemas/policy-verdict.schema.json +282 -0
- package/assets/schemas/project-model.schema.json +783 -0
- package/assets/schemas/project-status.schema.json +217 -0
- package/assets/schemas/reconcile-plan.schema.json +155 -0
- package/assets/schemas/run-diff.schema.json +177 -0
- package/assets/schemas/run-snapshot-manifest.schema.json +2104 -0
- package/assets/schemas/run-state.schema.json +1755 -0
- package/assets/schemas/runtime-capabilities.schema.json +1545 -0
- package/assets/schemas/sandbox-capabilities.schema.json +116 -0
- package/assets/schemas/skill.schema.json +87 -0
- package/assets/schemas/spec.schema.json +202 -0
- package/assets/schemas/tool-catalogue.schema.json +655 -0
- package/assets/schemas/trust-record.schema.json +38 -0
- package/assets/skills/README.md +9 -0
- package/assets/skills/inspect-surface.md +4 -0
- package/dist/agent-host.mjs +50 -0
- package/dist/agent-host.mjs.map +1 -0
- package/dist/bundle-manifest.json +102 -0
- package/dist/chunks/__host-DgGPyco9.mjs +18 -0
- package/dist/chunks/__host-DgGPyco9.mjs.map +1 -0
- package/dist/chunks/approve-Cv135UqI.mjs +19 -0
- package/dist/chunks/approve-Cv135UqI.mjs.map +1 -0
- package/dist/chunks/auth-BT5oVp76.mjs +132 -0
- package/dist/chunks/auth-BT5oVp76.mjs.map +1 -0
- package/dist/chunks/auth-D8rCVY33.mjs +84 -0
- package/dist/chunks/auth-D8rCVY33.mjs.map +1 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs +7 -0
- package/dist/chunks/brainstorm-CU10T1tm.mjs.map +1 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs +15 -0
- package/dist/chunks/cancel-KV_cpsqS.mjs.map +1 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs +57 -0
- package/dist/chunks/canonical-C8UxeEmB.mjs.map +1 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs +1549 -0
- package/dist/chunks/catalogue-BvwSrM_j.mjs.map +1 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs +7303 -0
- package/dist/chunks/compose-Cr0hP0qm.mjs.map +1 -0
- package/dist/chunks/config-DefHA2Lw.mjs +96 -0
- package/dist/chunks/config-DefHA2Lw.mjs.map +1 -0
- package/dist/chunks/contract-B0ra2-bm.mjs +295 -0
- package/dist/chunks/contract-B0ra2-bm.mjs.map +1 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs +123 -0
- package/dist/chunks/contract-vYbwYr0Y.mjs.map +1 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs +15 -0
- package/dist/chunks/deny-ClTxSvHJ.mjs.map +1 -0
- package/dist/chunks/diff-Qw0II0sj.mjs +101 -0
- package/dist/chunks/diff-Qw0II0sj.mjs.map +1 -0
- package/dist/chunks/discover-30Ciya_0.mjs +20 -0
- package/dist/chunks/discover-30Ciya_0.mjs.map +1 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs +222 -0
- package/dist/chunks/doctor-DfCrKpfT.mjs.map +1 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs +18 -0
- package/dist/chunks/fix-D4Tj8RVA.mjs.map +1 -0
- package/dist/chunks/gc-BBXlBF5w.mjs +161 -0
- package/dist/chunks/gc-BBXlBF5w.mjs.map +1 -0
- package/dist/chunks/init-Okz4mlhW.mjs +30 -0
- package/dist/chunks/init-Okz4mlhW.mjs.map +1 -0
- package/dist/chunks/inspect-Buby4cQD.mjs +22 -0
- package/dist/chunks/inspect-Buby4cQD.mjs.map +1 -0
- package/dist/chunks/logs-Giq5sUau.mjs +25 -0
- package/dist/chunks/logs-Giq5sUau.mjs.map +1 -0
- package/dist/chunks/migrate-DRoFUg53.mjs +19 -0
- package/dist/chunks/migrate-DRoFUg53.mjs.map +1 -0
- package/dist/chunks/models-Btp6X2YZ.mjs +24 -0
- package/dist/chunks/models-Btp6X2YZ.mjs.map +1 -0
- package/dist/chunks/pause-BASBfKLO.mjs +16 -0
- package/dist/chunks/pause-BASBfKLO.mjs.map +1 -0
- package/dist/chunks/policy-DUIgzF3W.mjs +139 -0
- package/dist/chunks/policy-DUIgzF3W.mjs.map +1 -0
- package/dist/chunks/providers-Dx78imKj.mjs +19 -0
- package/dist/chunks/providers-Dx78imKj.mjs.map +1 -0
- package/dist/chunks/reconcile-BfD785gd.mjs +26 -0
- package/dist/chunks/reconcile-BfD785gd.mjs.map +1 -0
- package/dist/chunks/render-DyZr5Lgi.mjs +53 -0
- package/dist/chunks/render-DyZr5Lgi.mjs.map +1 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs +15 -0
- package/dist/chunks/resume-GJQRb2Eb.mjs.map +1 -0
- package/dist/chunks/retry-CLrUem1H.mjs +15 -0
- package/dist/chunks/retry-CLrUem1H.mjs.map +1 -0
- package/dist/chunks/review-BDdjb6kB.mjs +17 -0
- package/dist/chunks/review-BDdjb6kB.mjs.map +1 -0
- package/dist/chunks/run-CgTWYlcd.mjs +82 -0
- package/dist/chunks/run-CgTWYlcd.mjs.map +1 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs +26 -0
- package/dist/chunks/run-tool-C3rAv4XF.mjs.map +1 -0
- package/dist/chunks/schema-C9QbUW4H.mjs +578 -0
- package/dist/chunks/schema-C9QbUW4H.mjs.map +1 -0
- package/dist/chunks/send-BdnJ85Nz.mjs +19 -0
- package/dist/chunks/send-BdnJ85Nz.mjs.map +1 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs +19 -0
- package/dist/chunks/ship-DTuLGoaJ.mjs.map +1 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs +15 -0
- package/dist/chunks/shutdown-BM92Iyv2.mjs.map +1 -0
- package/dist/chunks/skip-DZG5kpcH.mjs +18 -0
- package/dist/chunks/skip-DZG5kpcH.mjs.map +1 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs +20 -0
- package/dist/chunks/spec-Bq0P3jVh.mjs.map +1 -0
- package/dist/chunks/src-CId6kMsY.mjs +1689 -0
- package/dist/chunks/src-CId6kMsY.mjs.map +1 -0
- package/dist/chunks/src-CfAOde7C.mjs +5403 -0
- package/dist/chunks/src-CfAOde7C.mjs.map +1 -0
- package/dist/chunks/src-Cu3TxNob.mjs +783 -0
- package/dist/chunks/src-Cu3TxNob.mjs.map +1 -0
- package/dist/chunks/src-DcU4IXMe.mjs +117 -0
- package/dist/chunks/src-DcU4IXMe.mjs.map +1 -0
- package/dist/chunks/src-JxawBQBn.mjs +446 -0
- package/dist/chunks/src-JxawBQBn.mjs.map +1 -0
- package/dist/chunks/src-s7YRvk0t.mjs +2116 -0
- package/dist/chunks/src-s7YRvk0t.mjs.map +1 -0
- package/dist/chunks/status-SMjiOMYy.mjs +29 -0
- package/dist/chunks/status-SMjiOMYy.mjs.map +1 -0
- package/dist/chunks/tail-BuhT-2PH.mjs +25 -0
- package/dist/chunks/tail-BuhT-2PH.mjs.map +1 -0
- package/dist/chunks/update-BXIppsbi.mjs +16 -0
- package/dist/chunks/update-BXIppsbi.mjs.map +1 -0
- package/dist/cli.mjs +175 -0
- package/dist/cli.mjs.map +1 -0
- package/package.json +17 -32
- package/CHANGELOG.md +0 -1461
- package/bin/cli.js +0 -817
- package/bin/report.js +0 -141
- package/core/adapter/render.js +0 -415
- package/core/agents/implementer.template.md +0 -142
- package/core/agents/profile-reader.md +0 -50
- package/core/agents/release.md +0 -58
- package/core/agents/review.md +0 -179
- package/core/commands/cohorte-align-ds.md +0 -33
- package/core/commands/cohorte-audit.md +0 -58
- package/core/commands/cohorte-brainstorm.md +0 -70
- package/core/commands/cohorte-build.md +0 -219
- package/core/commands/cohorte-doctor.md +0 -194
- package/core/commands/cohorte-fix.md +0 -80
- package/core/commands/cohorte-fleet.md +0 -103
- package/core/commands/cohorte-init-pipeline.md +0 -31
- package/core/commands/cohorte-intake.md +0 -92
- package/core/commands/cohorte-patch.md +0 -118
- package/core/commands/cohorte-refactor.md +0 -59
- package/core/commands/cohorte-retro.md +0 -85
- package/core/commands/cohorte-review.md +0 -270
- package/core/commands/cohorte-ship.md +0 -122
- package/core/commands/cohorte-spec.md +0 -101
- package/core/commands/cohorte-update-pipeline.md +0 -165
- package/core/hooks/gate.py +0 -588
- package/core/runtimes/claude.json +0 -74
- package/core/runtimes/codex.json +0 -85
- package/core/runtimes/cursor.json +0 -76
- package/core/runtimes/gemini.json +0 -76
- package/core/runtimes/opencode.json +0 -73
- package/core/templates/agent-handoff.md +0 -34
- package/core/templates/brainstorm-return.md +0 -36
- package/core/templates/decisions.template.md +0 -42
- package/core/templates/design-brief.md +0 -44
- package/core/templates/patch.template.md +0 -86
- package/core/templates/pr-body.md +0 -25
- package/core/templates/review-feedback.md +0 -38
- package/core/templates/spec.template.md +0 -87
- package/core/templates/steps/init-pipeline/01-detect-stack.md +0 -44
- package/core/templates/steps/init-pipeline/02-interview-gaps.md +0 -55
- package/core/templates/steps/init-pipeline/03-draft-profile.md +0 -10
- package/core/templates/steps/init-pipeline/04-write-render.md +0 -145
- package/core/templates/steps/init-pipeline/05-report.md +0 -12
- package/core/workflows/audit.js +0 -240
- package/core/workflows/loop.js +0 -641
- package/core/workflows/refactor.js +0 -276
- package/core/workflows/review.js +0 -456
- package/install.ps1 +0 -103
- package/install.sh +0 -114
- package/lib/doctor.js +0 -534
- package/lib/runtime.js +0 -139
- package/lib/versions.js +0 -149
- package/lib/yaml.js +0 -129
- package/profile/PIPELINE.template.md +0 -259
- package/profile/SCHEMA.md +0 -735
- package/profile/cohorte.config.template.yaml +0 -43
- package/scripts/assert-gate-hook.mjs +0 -75
- package/scripts/kanban-move.sh +0 -313
- package/scripts/metrics/collect.mjs +0 -531
- package/scripts/metrics/prices.json +0 -42
- package/scripts/new-feature.sh.template +0 -98
- package/scripts/preflight.sh +0 -124
- package/scripts/remove-feature.sh.template +0 -56
- package/scripts/test-adapter.mjs +0 -437
- package/scripts/test-gate.mjs +0 -443
- package/scripts/test-kanban.mjs +0 -304
- package/scripts/test-lib.mjs +0 -356
- package/scripts/test-metrics.mjs +0 -156
- package/scripts/test-workflows.mjs +0 -758
- package/scripts/validate-core.mjs +0 -325
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { n as sha256Hex } from "./canonical-C8UxeEmB.mjs";
|
|
2
|
+
import "./src-Cu3TxNob.mjs";
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
//#region src/commands/diff/index.ts
|
|
7
|
+
const exec = promisify(execFile);
|
|
8
|
+
async function git(cwd, args) {
|
|
9
|
+
return (await exec("git", args, {
|
|
10
|
+
cwd,
|
|
11
|
+
maxBuffer: 4194304
|
|
12
|
+
})).stdout;
|
|
13
|
+
}
|
|
14
|
+
function parseNumstat(text) {
|
|
15
|
+
return text.split("\n").filter(Boolean).map((line) => {
|
|
16
|
+
const [addedText, removedText, ...pathParts] = line.split(" ");
|
|
17
|
+
const path = pathParts.join(" ");
|
|
18
|
+
const added = addedText === "-" ? 0 : Number(addedText);
|
|
19
|
+
const removed = removedText === "-" ? 0 : Number(removedText);
|
|
20
|
+
return {
|
|
21
|
+
path,
|
|
22
|
+
op: added === 0 && removed > 0 ? "delete" : added > 0 && removed === 0 ? "create" : "modify",
|
|
23
|
+
added,
|
|
24
|
+
removed
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
const diff = {
|
|
29
|
+
verb: "diff",
|
|
30
|
+
async run(ctx, args) {
|
|
31
|
+
const runId = args.positionals[0];
|
|
32
|
+
if (!runId) return 2;
|
|
33
|
+
const store = await ctx.openStore();
|
|
34
|
+
try {
|
|
35
|
+
const tree = await store.readRunTree(runId);
|
|
36
|
+
const run = tree.run;
|
|
37
|
+
const worktree = tree.worktrees.find((item) => item.slot === (args.positionals[1] ?? "_integration")) ?? tree.worktrees[0];
|
|
38
|
+
if (!worktree || !run.baseSha) return 3;
|
|
39
|
+
const base = run.baseSha;
|
|
40
|
+
const head = run.integrationHead ?? (await git(worktree.path, ["rev-parse", "HEAD"])).trim();
|
|
41
|
+
const rows = parseNumstat(await git(worktree.path, [
|
|
42
|
+
"diff",
|
|
43
|
+
"--numstat",
|
|
44
|
+
`${base}..${head}`
|
|
45
|
+
]));
|
|
46
|
+
const patch = await git(worktree.path, [
|
|
47
|
+
"diff",
|
|
48
|
+
"--binary",
|
|
49
|
+
`${base}..${head}`
|
|
50
|
+
]);
|
|
51
|
+
const artifactId = ctx.ids.next("art");
|
|
52
|
+
const artifactDir = `${ctx.cwd}/.cohorte/runs/${runId}/artifacts`;
|
|
53
|
+
const relativePatch = `.cohorte/runs/${runId}/artifacts/${artifactId}.patch`;
|
|
54
|
+
await mkdir(artifactDir, {
|
|
55
|
+
recursive: true,
|
|
56
|
+
mode: 448
|
|
57
|
+
});
|
|
58
|
+
await writeFile(`${ctx.cwd}/${relativePatch}`, patch, "utf8");
|
|
59
|
+
const document = {
|
|
60
|
+
documentVersion: 1,
|
|
61
|
+
runId,
|
|
62
|
+
base: {
|
|
63
|
+
branch: run.baseBranch,
|
|
64
|
+
sha: base
|
|
65
|
+
},
|
|
66
|
+
head: {
|
|
67
|
+
branch: run.integrationBranch ?? worktree.branch ?? "HEAD",
|
|
68
|
+
sha: head,
|
|
69
|
+
treeDigest: (await git(worktree.path, ["rev-parse", `${head}^{tree}`])).trim()
|
|
70
|
+
},
|
|
71
|
+
surfaces: [{
|
|
72
|
+
surface: args.positionals[1] ?? "shared",
|
|
73
|
+
files: rows.map((row) => ({
|
|
74
|
+
path: row.path,
|
|
75
|
+
op: row.op
|
|
76
|
+
})),
|
|
77
|
+
stat: {
|
|
78
|
+
added: rows.reduce((sum, row) => sum + row.added, 0),
|
|
79
|
+
removed: rows.reduce((sum, row) => sum + row.removed, 0)
|
|
80
|
+
},
|
|
81
|
+
patch: {
|
|
82
|
+
artifactId,
|
|
83
|
+
kind: "patch",
|
|
84
|
+
path: relativePatch,
|
|
85
|
+
sha256: sha256Hex(patch),
|
|
86
|
+
bytes: Buffer.byteLength(patch)
|
|
87
|
+
}
|
|
88
|
+
}]
|
|
89
|
+
};
|
|
90
|
+
if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(document)}\n`);
|
|
91
|
+
else ctx.stdio.stdout.write(patch);
|
|
92
|
+
return 0;
|
|
93
|
+
} finally {
|
|
94
|
+
await store.close();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
//#endregion
|
|
99
|
+
export { diff as default };
|
|
100
|
+
|
|
101
|
+
//# sourceMappingURL=diff-Qw0II0sj.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diff-Qw0II0sj.mjs","names":[],"sources":["../../../../apps/cli/src/commands/diff/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport { mkdir, writeFile } from 'node:fs/promises';\nimport { promisify } from 'node:util';\nimport { sha256Hex } from '@cohorte/base';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst exec = promisify(execFile);\ntype GitRow = { path: string; op: 'create' | 'modify' | 'delete'; added: number; removed: number };\n\nasync function git(cwd: string, args: string[]): Promise<string> {\n const result = await exec('git', args, { cwd, maxBuffer: 4 * 1024 * 1024 });\n return result.stdout;\n}\n\nfunction parseNumstat(text: string): GitRow[] {\n return text\n .split('\\n')\n .filter(Boolean)\n .map((line) => {\n const [addedText, removedText, ...pathParts] = line.split('\\t');\n const path = pathParts.join('\\t');\n const added = addedText === '-' ? 0 : Number(addedText);\n const removed = removedText === '-' ? 0 : Number(removedText);\n return {\n path,\n op: added === 0 && removed > 0 ? 'delete' : added > 0 && removed === 0 ? 'create' : 'modify',\n added,\n removed,\n };\n });\n}\n\nconst diff: CommandModule = {\n verb: 'diff',\n async run(ctx, args) {\n const runId = args.positionals[0];\n if (!runId) return 2;\n const store = await ctx.openStore();\n try {\n const tree = await store.readRunTree(runId as never);\n const run = tree.run;\n const worktree =\n tree.worktrees.find((item) => item.slot === (args.positionals[1] ?? '_integration')) ?? tree.worktrees[0];\n if (!worktree || !run.baseSha) return 3;\n const base = run.baseSha;\n const head = run.integrationHead ?? (await git(worktree.path, ['rev-parse', 'HEAD'])).trim();\n const rows = parseNumstat(await git(worktree.path, ['diff', '--numstat', `${base}..${head}`]));\n const patch = await git(worktree.path, ['diff', '--binary', `${base}..${head}`]);\n const artifactId = ctx.ids.next<'ArtifactId'>('art');\n const artifactDir = `${ctx.cwd}/.cohorte/runs/${runId}/artifacts`;\n const relativePatch = `.cohorte/runs/${runId}/artifacts/${artifactId}.patch`;\n await mkdir(artifactDir, { recursive: true, mode: 0o700 });\n await writeFile(`${ctx.cwd}/${relativePatch}`, patch, 'utf8');\n const document = {\n documentVersion: 1,\n runId,\n base: { branch: run.baseBranch, sha: base },\n head: {\n branch: run.integrationBranch ?? worktree.branch ?? 'HEAD',\n sha: head,\n treeDigest: (await git(worktree.path, ['rev-parse', `${head}^{tree}`])).trim(),\n },\n surfaces: [\n {\n surface: args.positionals[1] ?? 'shared',\n files: rows.map((row) => ({ path: row.path, op: row.op })),\n stat: {\n added: rows.reduce((sum, row) => sum + row.added, 0),\n removed: rows.reduce((sum, row) => sum + row.removed, 0),\n },\n patch: {\n artifactId,\n kind: 'patch',\n path: relativePatch,\n sha256: sha256Hex(patch),\n bytes: Buffer.byteLength(patch),\n },\n },\n ],\n };\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(document)}\\n`);\n else ctx.stdio.stdout.write(patch);\n return 0;\n } finally {\n await store.close();\n }\n },\n};\n\nexport default diff;\n"],"mappings":";;;;;;AAMA,MAAM,OAAO,UAAU,QAAQ;AAG/B,eAAe,IAAI,KAAa,MAAiC;CAE/D,QAAO,MADc,KAAK,OAAO,MAAM;EAAE;EAAK,WAAW;CAAgB,CAAC,EAAA,CAC5D;AAChB;AAEA,SAAS,aAAa,MAAwB;CAC5C,OAAO,KACJ,MAAM,IAAI,CAAC,CACX,OAAO,OAAO,CAAC,CACf,KAAK,SAAS;EACb,MAAM,CAAC,WAAW,aAAa,GAAG,aAAa,KAAK,MAAM,GAAI;EAC9D,MAAM,OAAO,UAAU,KAAK,GAAI;EAChC,MAAM,QAAQ,cAAc,MAAM,IAAI,OAAO,SAAS;EACtD,MAAM,UAAU,gBAAgB,MAAM,IAAI,OAAO,WAAW;EAC5D,OAAO;GACL;GACA,IAAI,UAAU,KAAK,UAAU,IAAI,WAAW,QAAQ,KAAK,YAAY,IAAI,WAAW;GACpF;GACA;EACF;CACF,CAAC;AACL;AAEA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,KAAK,YAAY;EAC/B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,QAAQ,MAAM,IAAI,UAAU;EAClC,IAAI;GACF,MAAM,OAAO,MAAM,MAAM,YAAY,KAAc;GACnD,MAAM,MAAM,KAAK;GACjB,MAAM,WACJ,KAAK,UAAU,MAAM,SAAS,KAAK,UAAU,KAAK,YAAY,MAAM,eAAe,KAAK,KAAK,UAAU;GACzG,IAAI,CAAC,YAAY,CAAC,IAAI,SAAS,OAAO;GACtC,MAAM,OAAO,IAAI;GACjB,MAAM,OAAO,IAAI,oBAAoB,MAAM,IAAI,SAAS,MAAM,CAAC,aAAa,MAAM,CAAC,EAAA,CAAG,KAAK;GAC3F,MAAM,OAAO,aAAa,MAAM,IAAI,SAAS,MAAM;IAAC;IAAQ;IAAa,GAAG,KAAK,IAAI;GAAM,CAAC,CAAC;GAC7F,MAAM,QAAQ,MAAM,IAAI,SAAS,MAAM;IAAC;IAAQ;IAAY,GAAG,KAAK,IAAI;GAAM,CAAC;GAC/E,MAAM,aAAa,IAAI,IAAI,KAAmB,KAAK;GACnD,MAAM,cAAc,GAAG,IAAI,IAAI,iBAAiB,MAAM;GACtD,MAAM,gBAAgB,iBAAiB,MAAM,aAAa,WAAW;GACrE,MAAM,MAAM,aAAa;IAAE,WAAW;IAAM,MAAM;GAAM,CAAC;GACzD,MAAM,UAAU,GAAG,IAAI,IAAI,GAAG,iBAAiB,OAAO,MAAM;GAC5D,MAAM,WAAW;IACf,iBAAiB;IACjB;IACA,MAAM;KAAE,QAAQ,IAAI;KAAY,KAAK;IAAK;IAC1C,MAAM;KACJ,QAAQ,IAAI,qBAAqB,SAAS,UAAU;KACpD,KAAK;KACL,aAAa,MAAM,IAAI,SAAS,MAAM,CAAC,aAAa,GAAG,KAAK,QAAQ,CAAC,EAAA,CAAG,KAAK;IAC/E;IACA,UAAU,CACR;KACE,SAAS,KAAK,YAAY,MAAM;KAChC,OAAO,KAAK,KAAK,SAAS;MAAE,MAAM,IAAI;MAAM,IAAI,IAAI;KAAG,EAAE;KACzD,MAAM;MACJ,OAAO,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,OAAO,CAAC;MACnD,SAAS,KAAK,QAAQ,KAAK,QAAQ,MAAM,IAAI,SAAS,CAAC;KACzD;KACA,OAAO;MACL;MACA,MAAM;MACN,MAAM;MACN,QAAQ,UAAU,KAAK;MACvB,OAAO,OAAO,WAAW,KAAK;KAChC;IACF,CACF;GACF;GACA,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,QAAQ,EAAE,GAAG;QAChE,IAAI,MAAM,OAAO,MAAM,KAAK;GACjC,OAAO;EACT,UAAU;GACR,MAAM,MAAM,MAAM;EACpB;CACF;AACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { N as errorOf, j as CohorteError } from "./src-Cu3TxNob.mjs";
|
|
2
|
+
import { t as scanRepository } from "./src-JxawBQBn.mjs";
|
|
3
|
+
//#region src/commands/discover/index.ts
|
|
4
|
+
const discover = {
|
|
5
|
+
verb: "discover",
|
|
6
|
+
async run(ctx, args) {
|
|
7
|
+
if (args.positionals.includes("--semantic")) throw new CohorteError(errorOf("configuration/phase-not-available", "semantic discovery is not available in V3.0"));
|
|
8
|
+
const model = await scanRepository(ctx.cwd, {
|
|
9
|
+
clock: ctx.clock,
|
|
10
|
+
toolVersion: "3.0.0"
|
|
11
|
+
});
|
|
12
|
+
if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(model)}\n`);
|
|
13
|
+
else ctx.stdio.stdout.write(`${JSON.stringify(model, null, 2)}\n`);
|
|
14
|
+
return 0;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { discover as default };
|
|
19
|
+
|
|
20
|
+
//# sourceMappingURL=discover-30Ciya_0.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discover-30Ciya_0.mjs","names":[],"sources":["../../../../apps/cli/src/commands/discover/index.ts"],"sourcesContent":["import { CohorteError, errorOf } from '@cohorte/base';\nimport { scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst discover: CommandModule = {\n verb: 'discover',\n async run(ctx, args) {\n if (args.positionals.includes('--semantic')) {\n throw new CohorteError(\n errorOf('configuration/phase-not-available', 'semantic discovery is not available in V3.0'),\n );\n }\n const model = await scanRepository(ctx.cwd, { clock: ctx.clock, toolVersion: '3.0.0' });\n if (args.json) ctx.stdio.stdout.write(`${JSON.stringify(model)}\\n`);\n else ctx.stdio.stdout.write(`${JSON.stringify(model, null, 2)}\\n`);\n return 0;\n },\n};\n\nexport default discover;\n"],"mappings":";;;AAIA,MAAM,WAA0B;CAC9B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,IAAI,KAAK,YAAY,SAAS,YAAY,GACxC,MAAM,IAAI,aACR,QAAQ,qCAAqC,6CAA6C,CAC5F;EAEF,MAAM,QAAQ,MAAM,eAAe,IAAI,KAAK;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACtF,IAAI,KAAK,MAAM,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,KAAK,EAAE,GAAG;OAC7D,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;EACjE,OAAO;CACT;AACF"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { n as sha256Hex } from "./canonical-C8UxeEmB.mjs";
|
|
2
|
+
import "./src-Cu3TxNob.mjs";
|
|
3
|
+
import { n as createKeyStore, r as createTrustStore } from "./auth-BT5oVp76.mjs";
|
|
4
|
+
import "./contract-B0ra2-bm.mjs";
|
|
5
|
+
import { i as resolveConfig, n as loadConfig } from "./src-DcU4IXMe.mjs";
|
|
6
|
+
import { _ as probeSandbox } from "./src-CId6kMsY.mjs";
|
|
7
|
+
import { l as verifyProjectionAgainstEvents } from "./src-CfAOde7C.mjs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { execFile } from "node:child_process";
|
|
10
|
+
import { promisify } from "node:util";
|
|
11
|
+
//#region src/contract/doctor.ts
|
|
12
|
+
/** DESIGN §9 doctor capabilities + F-1 (ripgrep backend) + F-3 (sandbox availability) + 6.3 (protected roots). */
|
|
13
|
+
const DOCTOR_CHECK_IDS = [
|
|
14
|
+
"node-version",
|
|
15
|
+
"git",
|
|
16
|
+
"sqlite",
|
|
17
|
+
"search-backend",
|
|
18
|
+
"sandbox",
|
|
19
|
+
"auth",
|
|
20
|
+
"install",
|
|
21
|
+
"config",
|
|
22
|
+
"worktree-root"
|
|
23
|
+
];
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/doctor/checks/auth/index.ts
|
|
26
|
+
const authDoctorCheck = {
|
|
27
|
+
id: "auth",
|
|
28
|
+
description: "Checks that the configured provider's authentication is usable, without reading the token.",
|
|
29
|
+
async run(ctx) {
|
|
30
|
+
try {
|
|
31
|
+
const statuses = await ctx.runtime.resolve().authStatus([]);
|
|
32
|
+
const usable = statuses.every((status) => status.state !== "absent" && status.state !== "unknown-transient");
|
|
33
|
+
const accountNote = statuses.some((status) => status.accountLabel === void 0) ? "; account: not exposed by the engine" : "";
|
|
34
|
+
return {
|
|
35
|
+
id: "auth",
|
|
36
|
+
status: usable ? "ok" : "warning",
|
|
37
|
+
summary: `${usable ? "runtime authentication is available" : "runtime authentication needs attention"}${accountNote}`
|
|
38
|
+
};
|
|
39
|
+
} catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
id: "auth",
|
|
42
|
+
status: "error",
|
|
43
|
+
summary: `authentication status unavailable: ${String(error)}`
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/doctor/index.ts
|
|
50
|
+
const exec = promisify(execFile);
|
|
51
|
+
const result = (id, summary, status = "ok") => ({
|
|
52
|
+
id,
|
|
53
|
+
summary,
|
|
54
|
+
status
|
|
55
|
+
});
|
|
56
|
+
function versionAtLeast(value, required) {
|
|
57
|
+
const [major = 0, minor = 0] = value.split(".").map((part) => Number.parseInt(part, 10));
|
|
58
|
+
return major > required[0] || major === required[0] && minor >= required[1];
|
|
59
|
+
}
|
|
60
|
+
async function runDoctor(ctx, options = {}) {
|
|
61
|
+
const checks = [
|
|
62
|
+
{
|
|
63
|
+
id: "node-version",
|
|
64
|
+
description: "Node",
|
|
65
|
+
run: async () => versionAtLeast(process.versions.node, [24, 16]) ? result("node-version", `Node ${process.versions.node}`) : result("node-version", `Node ${process.versions.node} is below 24.16`, "error")
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
id: "git",
|
|
69
|
+
description: "Git",
|
|
70
|
+
run: async () => {
|
|
71
|
+
try {
|
|
72
|
+
const { stdout } = await exec("git", ["--version"]);
|
|
73
|
+
const version = stdout.match(/(\d+\.\d+(?:\.\d+)?)/)?.[1] ?? "0.0.0";
|
|
74
|
+
return versionAtLeast(version, [2, 38]) ? result("git", `Git ${version}`) : result("git", `Git ${version} is below 2.38`, "error");
|
|
75
|
+
} catch (error) {
|
|
76
|
+
return result("git", `git unavailable: ${String(error)}`, "error");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: "sqlite",
|
|
82
|
+
description: "SQLite",
|
|
83
|
+
run: async () => {
|
|
84
|
+
try {
|
|
85
|
+
const store = await ctx.openStore();
|
|
86
|
+
const info = await store.open();
|
|
87
|
+
const migration = await store.migrate("check");
|
|
88
|
+
await store.close();
|
|
89
|
+
return migration.pending.length === 0 ? result("sqlite", `${info.kind} schema ${info.schemaVersion}`) : result("sqlite", `${migration.pending.length} migration(s) pending`, "warning");
|
|
90
|
+
} catch (error) {
|
|
91
|
+
return result("sqlite", `state store unavailable: ${String(error)}`, "error");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: "search-backend",
|
|
97
|
+
description: "Search",
|
|
98
|
+
run: async () => {
|
|
99
|
+
try {
|
|
100
|
+
const { stdout } = await exec("rg", ["--version"]);
|
|
101
|
+
return result("search-backend", `ripgrep ${stdout.split(/\s+/u)[1] ?? "available"}`);
|
|
102
|
+
} catch {
|
|
103
|
+
try {
|
|
104
|
+
await exec("git", ["grep", "--version"]);
|
|
105
|
+
return result("search-backend", "git grep fallback");
|
|
106
|
+
} catch {
|
|
107
|
+
return result("search-backend", "neither rg nor git grep is available", "error");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: "sandbox",
|
|
114
|
+
description: "Sandbox",
|
|
115
|
+
run: async () => {
|
|
116
|
+
const capabilities = await probeSandbox();
|
|
117
|
+
return result("sandbox", `level=${capabilities.level}, backend=${capabilities.backend}, filesystem=${capabilities.filesystem}, network=${capabilities.network}`, capabilities.level === "L0-process" ? "warning" : "ok");
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
authDoctorCheck,
|
|
121
|
+
{
|
|
122
|
+
id: "install",
|
|
123
|
+
description: "Install",
|
|
124
|
+
run: async () => {
|
|
125
|
+
try {
|
|
126
|
+
const verified = await ctx.assets.verify();
|
|
127
|
+
return verified.ok ? result("install", `${ctx.install.installDir()} (assets verified)`) : result("install", `asset verification failed: ${verified.error.message}`, "error");
|
|
128
|
+
} catch (error) {
|
|
129
|
+
return result("install", `asset verification unavailable: ${String(error)}`, "warning");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: "config",
|
|
135
|
+
description: "Config",
|
|
136
|
+
run: async () => {
|
|
137
|
+
try {
|
|
138
|
+
const home = ctx.env.HOME ?? ctx.cwd;
|
|
139
|
+
const keys = createKeyStore({ directory: join(home, ".cohorte", "keys") });
|
|
140
|
+
const trustStore = createTrustStore({
|
|
141
|
+
directory: join(home, ".cohorte", "trust"),
|
|
142
|
+
keys
|
|
143
|
+
});
|
|
144
|
+
const loaded = await loadConfig({
|
|
145
|
+
cwd: ctx.cwd,
|
|
146
|
+
home
|
|
147
|
+
});
|
|
148
|
+
const resolved = await resolveConfig(loaded, {
|
|
149
|
+
trustStore,
|
|
150
|
+
projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24)
|
|
151
|
+
});
|
|
152
|
+
return resolved.status === "resolved" ? result("config", `project configuration resolved at ${loaded.projectRoot}`) : result("config", `project policy requires trust: ${resolved.loosenedKeys.join(", ")}`, "warning");
|
|
153
|
+
} catch (error) {
|
|
154
|
+
return result("config", `configuration unavailable: ${String(error)}`, "error");
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
id: "worktree-root",
|
|
160
|
+
description: "Worktree",
|
|
161
|
+
run: async () => typeof process.getuid === "function" && process.getuid() === 0 ? result("worktree-root", "refusing to operate as uid 0", "error") : result("worktree-root", ctx.cwd)
|
|
162
|
+
}
|
|
163
|
+
];
|
|
164
|
+
if (options.verifyState) checks.push({
|
|
165
|
+
id: "verify-state",
|
|
166
|
+
description: "State verification",
|
|
167
|
+
run: async () => {
|
|
168
|
+
const store = await ctx.openStore();
|
|
169
|
+
try {
|
|
170
|
+
const runs = await store.listRuns({
|
|
171
|
+
limit: 1e5,
|
|
172
|
+
offset: 0
|
|
173
|
+
});
|
|
174
|
+
for (const run of runs) {
|
|
175
|
+
if (!(await store.verifyChain(run.runId)).ok) return result("verify-state", `run ${run.runId}: event chain is broken`, "error");
|
|
176
|
+
try {
|
|
177
|
+
await verifyProjectionAgainstEvents(store, run);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
return result("verify-state", `run ${run.runId}: projection mismatch (${String(error)})`, "error");
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return result("verify-state", `verified ${runs.length} run(s)`);
|
|
183
|
+
} finally {
|
|
184
|
+
await store.close();
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
const byId = new Map(checks.map((check) => [check.id, check]));
|
|
189
|
+
return Promise.all(DOCTOR_CHECK_IDS.map(async (id) => byId.get(id)?.run(ctx) ?? result(id, "check unavailable", "skipped")));
|
|
190
|
+
}
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/commands/doctor/index.ts
|
|
193
|
+
const doctor = {
|
|
194
|
+
verb: "doctor",
|
|
195
|
+
async run(ctx, args) {
|
|
196
|
+
const checks = await runDoctor(ctx, { verifyState: args.positionals.includes("--verify-state") });
|
|
197
|
+
if (args.json) {
|
|
198
|
+
let sandbox = {};
|
|
199
|
+
let runtimeCapabilities = {};
|
|
200
|
+
try {
|
|
201
|
+
sandbox = await probeSandbox();
|
|
202
|
+
} catch {}
|
|
203
|
+
try {
|
|
204
|
+
runtimeCapabilities = ctx.runtime.capabilities?.() ?? { provider: ctx.runtime.resolve().id };
|
|
205
|
+
} catch {}
|
|
206
|
+
ctx.stdio.stdout.write(`${JSON.stringify({
|
|
207
|
+
documentVersion: 1,
|
|
208
|
+
cohorteVersion: "3.0.0",
|
|
209
|
+
generatedAt: ctx.clock.now(),
|
|
210
|
+
ok: checks.every((check) => check.status === "ok" || check.status === "skipped"),
|
|
211
|
+
checks,
|
|
212
|
+
sandbox,
|
|
213
|
+
runtimeCapabilities
|
|
214
|
+
})}\n`);
|
|
215
|
+
} else for (const check of checks) ctx.stdio.stdout.write(`${check.status.padEnd(7)} ${check.id}: ${check.summary}\n`);
|
|
216
|
+
return checks.some((check) => check.status === "error") ? 1 : 0;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
//#endregion
|
|
220
|
+
export { doctor as default };
|
|
221
|
+
|
|
222
|
+
//# sourceMappingURL=doctor-DfCrKpfT.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor-DfCrKpfT.mjs","names":[],"sources":["../../../../apps/cli/src/contract/doctor.ts","../../../../apps/cli/src/doctor/checks/auth/index.ts","../../../../apps/cli/src/doctor/index.ts","../../../../apps/cli/src/commands/doctor/index.ts"],"sourcesContent":["// apps/cli/src/contract/doctor.ts — the frozen list of doctor check modules (PLAN PC-4 \"DoctorCheck + the frozen\n// list of doctor check modules\"). `doctor/index.ts` (area barrel, U4.05) runs every id of `DOCTOR_CHECK_IDS`\n// through `doctor/checks/<id>/index.ts`; only `auth` is a Wave-0 owned stub (`apps/cli/src/doctor/checks/auth`,\n// filled by U5.05) — the rest are created, under the same shape, by U4.05.\nimport type { DOCTOR_CHECK_STATUSES } from '@cohorte/protocol';\nimport type { CliContext } from './context.ts';\n\nexport type DoctorCheckStatus = (typeof DOCTOR_CHECK_STATUSES)[number];\n\nexport interface DoctorCheckResult {\n readonly id: string;\n readonly status: DoctorCheckStatus;\n readonly summary: string;\n readonly remediation?: string;\n}\n\nexport interface DoctorCheck {\n readonly id: string;\n readonly description: string;\n run(ctx: CliContext): Promise<DoctorCheckResult>;\n}\n\n/** DESIGN §9 doctor capabilities + F-1 (ripgrep backend) + F-3 (sandbox availability) + 6.3 (protected roots). */\nexport const DOCTOR_CHECK_IDS = [\n 'node-version',\n 'git',\n 'sqlite',\n 'search-backend',\n 'sandbox',\n 'auth',\n 'install',\n 'config',\n 'worktree-root',\n] as const;\nexport type DoctorCheckId = (typeof DOCTOR_CHECK_IDS)[number];\n","// apps/cli/src/doctor/checks/auth/index.ts — the `auth` doctor check (spec 10 \"auth status\"). Wave-0 typed stub:\n// filled by `U5.05`, which owns `apps/cli/src/doctor/checks/auth/**`.\nimport type { DoctorCheck } from '../../../contract/index.ts';\n\nconst authDoctorCheck: DoctorCheck = {\n id: 'auth',\n description: \"Checks that the configured provider's authentication is usable, without reading the token.\",\n async run(ctx) {\n try {\n const statuses = await ctx.runtime.resolve().authStatus([]);\n const usable = statuses.every((status) => status.state !== 'absent' && status.state !== 'unknown-transient');\n const accountNote = statuses.some((status) => status.accountLabel === undefined)\n ? '; account: not exposed by the engine'\n : '';\n return {\n id: 'auth',\n status: usable ? 'ok' : 'warning',\n summary: `${usable ? 'runtime authentication is available' : 'runtime authentication needs attention'}${accountNote}`,\n };\n } catch (error) {\n return { id: 'auth', status: 'error', summary: `authentication status unavailable: ${String(error)}` };\n }\n },\n};\n\nexport default authDoctorCheck;\n","// apps/cli/src/doctor/index.ts — AREA barrel: runs every `DOCTOR_CHECK_IDS` entry (contract/doctor.ts) through\n// `doctor/checks/<id>/index.ts` and assembles a `DoctorReport`. Wave-0 stub: filled by `U4.05`, which owns\n// `apps/cli/src/doctor/**` (this file included); only `checks/auth/**` is carved out to `U5.05`.\nimport { execFile } from 'node:child_process';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { sha256Hex } from '@cohorte/base';\nimport { loadConfig, resolveConfig } from '@cohorte/config';\nimport { verifyProjectionAgainstEvents } from '@cohorte/core';\nimport { createKeyStore, createTrustStore } from '@cohorte/security/auth';\nimport { probeSandbox } from '@cohorte/security/exec';\nimport { type CliContext, DOCTOR_CHECK_IDS, type DoctorCheck, type DoctorCheckResult } from '../contract/index.ts';\nimport authDoctorCheck from './checks/auth/index.ts';\n\nconst exec = promisify(execFile);\nconst result = (id: string, summary: string, status: DoctorCheckResult['status'] = 'ok'): DoctorCheckResult => ({\n id,\n summary,\n status,\n});\n\nfunction versionAtLeast(value: string, required: [number, number]): boolean {\n const [major = 0, minor = 0] = value.split('.').map((part) => Number.parseInt(part, 10));\n return major > required[0] || (major === required[0] && minor >= required[1]);\n}\n\nexport async function runDoctor(\n ctx: CliContext,\n options: { readonly verifyState?: boolean } = {},\n): Promise<readonly DoctorCheckResult[]> {\n const checks: DoctorCheck[] = [\n {\n id: 'node-version',\n description: 'Node',\n run: async () =>\n versionAtLeast(process.versions.node, [24, 16])\n ? result('node-version', `Node ${process.versions.node}`)\n : result('node-version', `Node ${process.versions.node} is below 24.16`, 'error'),\n },\n {\n id: 'git',\n description: 'Git',\n run: async () => {\n try {\n const { stdout } = await exec('git', ['--version']);\n const version = stdout.match(/(\\d+\\.\\d+(?:\\.\\d+)?)/)?.[1] ?? '0.0.0';\n return versionAtLeast(version, [2, 38])\n ? result('git', `Git ${version}`)\n : result('git', `Git ${version} is below 2.38`, 'error');\n } catch (error) {\n return result('git', `git unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'sqlite',\n description: 'SQLite',\n run: async () => {\n try {\n const store = await ctx.openStore();\n const info = await store.open();\n const migration = await store.migrate('check');\n await store.close();\n return migration.pending.length === 0\n ? result('sqlite', `${info.kind} schema ${info.schemaVersion}`)\n : result('sqlite', `${migration.pending.length} migration(s) pending`, 'warning');\n } catch (error) {\n return result('sqlite', `state store unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'search-backend',\n description: 'Search',\n run: async () => {\n try {\n const { stdout } = await exec('rg', ['--version']);\n return result('search-backend', `ripgrep ${stdout.split(/\\s+/u)[1] ?? 'available'}`);\n } catch {\n try {\n await exec('git', ['grep', '--version']);\n return result('search-backend', 'git grep fallback');\n } catch {\n return result('search-backend', 'neither rg nor git grep is available', 'error');\n }\n }\n },\n },\n {\n id: 'sandbox',\n description: 'Sandbox',\n run: async () => {\n const capabilities = await probeSandbox();\n return result(\n 'sandbox',\n `level=${capabilities.level}, backend=${capabilities.backend}, filesystem=${capabilities.filesystem}, network=${capabilities.network}`,\n capabilities.level === 'L0-process' ? 'warning' : 'ok',\n );\n },\n },\n authDoctorCheck,\n {\n id: 'install',\n description: 'Install',\n run: async () => {\n try {\n const verified = await ctx.assets.verify();\n return verified.ok\n ? result('install', `${ctx.install.installDir()} (assets verified)`)\n : result('install', `asset verification failed: ${verified.error.message}`, 'error');\n } catch (error) {\n return result('install', `asset verification unavailable: ${String(error)}`, 'warning');\n }\n },\n },\n {\n id: 'config',\n description: 'Config',\n run: async () => {\n try {\n const home = ctx.env.HOME ?? ctx.cwd;\n const keys = createKeyStore({ directory: join(home, '.cohorte', 'keys') });\n const trustStore = createTrustStore({ directory: join(home, '.cohorte', 'trust'), keys });\n const loaded = await loadConfig({ cwd: ctx.cwd, home });\n const resolved = await resolveConfig(loaded, {\n trustStore,\n projectKeyId: sha256Hex(loaded.projectRoot).slice(0, 24),\n });\n return resolved.status === 'resolved'\n ? result('config', `project configuration resolved at ${loaded.projectRoot}`)\n : result('config', `project policy requires trust: ${resolved.loosenedKeys.join(', ')}`, 'warning');\n } catch (error) {\n return result('config', `configuration unavailable: ${String(error)}`, 'error');\n }\n },\n },\n {\n id: 'worktree-root',\n description: 'Worktree',\n run: async () =>\n typeof process.getuid === 'function' && process.getuid() === 0\n ? result('worktree-root', 'refusing to operate as uid 0', 'error')\n : result('worktree-root', ctx.cwd),\n },\n ];\n if (options.verifyState) {\n checks.push({\n id: 'verify-state',\n description: 'State verification',\n run: async () => {\n const store = await ctx.openStore();\n try {\n const runs = await store.listRuns({ limit: 100_000, offset: 0 });\n for (const run of runs) {\n const chain = await store.verifyChain(run.runId);\n if (!chain.ok) return result('verify-state', `run ${run.runId}: event chain is broken`, 'error');\n try {\n await verifyProjectionAgainstEvents(store, run);\n } catch (error) {\n return result('verify-state', `run ${run.runId}: projection mismatch (${String(error)})`, 'error');\n }\n }\n return result('verify-state', `verified ${runs.length} run(s)`);\n } finally {\n await store.close();\n }\n },\n });\n }\n const byId = new Map(checks.map((check) => [check.id, check]));\n return Promise.all(\n DOCTOR_CHECK_IDS.map(async (id) => byId.get(id)?.run(ctx) ?? result(id, 'check unavailable', 'skipped')),\n );\n}\n","// apps/cli/src/commands/doctor/index.ts — DESIGN §9 verb `doctor` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/doctor/**`.\nimport { probeSandbox } from '@cohorte/security/exec';\nimport type { CommandModule } from '../../contract/index.ts';\nimport { runDoctor } from '../../doctor/index.ts';\n\nconst doctor: CommandModule = {\n verb: 'doctor',\n async run(ctx, args) {\n const checks = await runDoctor(ctx, { verifyState: args.positionals.includes('--verify-state') });\n if (args.json) {\n let sandbox: unknown = {};\n let runtimeCapabilities: unknown = {};\n try {\n sandbox = await probeSandbox();\n } catch {\n // The corresponding check already reports the diagnostic; keep JSON stable.\n }\n try {\n runtimeCapabilities = ctx.runtime.capabilities?.() ?? { provider: ctx.runtime.resolve().id };\n } catch {\n // A provider may be unavailable while the rest of doctor remains useful.\n }\n ctx.stdio.stdout.write(\n `${JSON.stringify({ documentVersion: 1, cohorteVersion: '3.0.0', generatedAt: ctx.clock.now(), ok: checks.every((check) => check.status === 'ok' || check.status === 'skipped'), checks, sandbox, runtimeCapabilities })}\\n`,\n );\n } else\n for (const check of checks) ctx.stdio.stdout.write(`${check.status.padEnd(7)} ${check.id}: ${check.summary}\\n`);\n return checks.some((check) => check.status === 'error') ? 1 : 0;\n },\n};\n\nexport default doctor;\n"],"mappings":";;;;;;;;;;;;AAuBA,MAAa,mBAAmB;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;;;AC7BA,MAAM,kBAA+B;CACnC,IAAI;CACJ,aAAa;CACb,MAAM,IAAI,KAAK;EACb,IAAI;GACF,MAAM,WAAW,MAAM,IAAI,QAAQ,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAC;GAC1D,MAAM,SAAS,SAAS,OAAO,WAAW,OAAO,UAAU,YAAY,OAAO,UAAU,mBAAmB;GAC3G,MAAM,cAAc,SAAS,MAAM,WAAW,OAAO,iBAAiB,KAAA,CAAS,IAC3E,yCACA;GACJ,OAAO;IACL,IAAI;IACJ,QAAQ,SAAS,OAAO;IACxB,SAAS,GAAG,SAAS,wCAAwC,2CAA2C;GAC1G;EACF,SAAS,OAAO;GACd,OAAO;IAAE,IAAI;IAAQ,QAAQ;IAAS,SAAS,sCAAsC,OAAO,KAAK;GAAI;EACvG;CACF;AACF;;;ACTA,MAAM,OAAO,UAAU,QAAQ;AAC/B,MAAM,UAAU,IAAY,SAAiB,SAAsC,UAA6B;CAC9G;CACA;CACA;AACF;AAEA,SAAS,eAAe,OAAe,UAAqC;CAC1E,MAAM,CAAC,QAAQ,GAAG,QAAQ,KAAK,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,OAAO,SAAS,MAAM,EAAE,CAAC;CACvF,OAAO,QAAQ,SAAS,MAAO,UAAU,SAAS,MAAM,SAAS,SAAS;AAC5E;AAEA,eAAsB,UACpB,KACA,UAA8C,CAAC,GACR;CACvC,MAAM,SAAwB;EAC5B;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YACH,eAAe,QAAQ,SAAS,MAAM,CAAC,IAAI,EAAE,CAAC,IAC1C,OAAO,gBAAgB,QAAQ,QAAQ,SAAS,MAAM,IACtD,OAAO,gBAAgB,QAAQ,QAAQ,SAAS,KAAK,kBAAkB,OAAO;EACtF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,IAAI;KACF,MAAM,EAAE,WAAW,MAAM,KAAK,OAAO,CAAC,WAAW,CAAC;KAClD,MAAM,UAAU,OAAO,MAAM,sBAAsB,CAAC,GAAG,MAAM;KAC7D,OAAO,eAAe,SAAS,CAAC,GAAG,EAAE,CAAC,IAClC,OAAO,OAAO,OAAO,SAAS,IAC9B,OAAO,OAAO,OAAO,QAAQ,iBAAiB,OAAO;IAC3D,SAAS,OAAO;KACd,OAAO,OAAO,OAAO,oBAAoB,OAAO,KAAK,KAAK,OAAO;IACnE;GACF;EACF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,IAAI;KACF,MAAM,QAAQ,MAAM,IAAI,UAAU;KAClC,MAAM,OAAO,MAAM,MAAM,KAAK;KAC9B,MAAM,YAAY,MAAM,MAAM,QAAQ,OAAO;KAC7C,MAAM,MAAM,MAAM;KAClB,OAAO,UAAU,QAAQ,WAAW,IAChC,OAAO,UAAU,GAAG,KAAK,KAAK,UAAU,KAAK,eAAe,IAC5D,OAAO,UAAU,GAAG,UAAU,QAAQ,OAAO,wBAAwB,SAAS;IACpF,SAAS,OAAO;KACd,OAAO,OAAO,UAAU,4BAA4B,OAAO,KAAK,KAAK,OAAO;IAC9E;GACF;EACF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,IAAI;KACF,MAAM,EAAE,WAAW,MAAM,KAAK,MAAM,CAAC,WAAW,CAAC;KACjD,OAAO,OAAO,kBAAkB,WAAW,OAAO,MAAM,MAAM,CAAC,CAAC,MAAM,aAAa;IACrF,QAAQ;KACN,IAAI;MACF,MAAM,KAAK,OAAO,CAAC,QAAQ,WAAW,CAAC;MACvC,OAAO,OAAO,kBAAkB,mBAAmB;KACrD,QAAQ;MACN,OAAO,OAAO,kBAAkB,wCAAwC,OAAO;KACjF;IACF;GACF;EACF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,MAAM,eAAe,MAAM,aAAa;IACxC,OAAO,OACL,WACA,SAAS,aAAa,MAAM,YAAY,aAAa,QAAQ,eAAe,aAAa,WAAW,YAAY,aAAa,WAC7H,aAAa,UAAU,eAAe,YAAY,IACpD;GACF;EACF;EACA;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,IAAI;KACF,MAAM,WAAW,MAAM,IAAI,OAAO,OAAO;KACzC,OAAO,SAAS,KACZ,OAAO,WAAW,GAAG,IAAI,QAAQ,WAAW,EAAE,mBAAmB,IACjE,OAAO,WAAW,8BAA8B,SAAS,MAAM,WAAW,OAAO;IACvF,SAAS,OAAO;KACd,OAAO,OAAO,WAAW,mCAAmC,OAAO,KAAK,KAAK,SAAS;IACxF;GACF;EACF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YAAY;IACf,IAAI;KACF,MAAM,OAAO,IAAI,IAAI,QAAQ,IAAI;KACjC,MAAM,OAAO,eAAe,EAAE,WAAW,KAAK,MAAM,YAAY,MAAM,EAAE,CAAC;KACzE,MAAM,aAAa,iBAAiB;MAAE,WAAW,KAAK,MAAM,YAAY,OAAO;MAAG;KAAK,CAAC;KACxF,MAAM,SAAS,MAAM,WAAW;MAAE,KAAK,IAAI;MAAK;KAAK,CAAC;KACtD,MAAM,WAAW,MAAM,cAAc,QAAQ;MAC3C;MACA,cAAc,UAAU,OAAO,WAAW,CAAC,CAAC,MAAM,GAAG,EAAE;KACzD,CAAC;KACD,OAAO,SAAS,WAAW,aACvB,OAAO,UAAU,qCAAqC,OAAO,aAAa,IAC1E,OAAO,UAAU,kCAAkC,SAAS,aAAa,KAAK,IAAI,KAAK,SAAS;IACtG,SAAS,OAAO;KACd,OAAO,OAAO,UAAU,8BAA8B,OAAO,KAAK,KAAK,OAAO;IAChF;GACF;EACF;EACA;GACE,IAAI;GACJ,aAAa;GACb,KAAK,YACH,OAAO,QAAQ,WAAW,cAAc,QAAQ,OAAO,MAAM,IACzD,OAAO,iBAAiB,gCAAgC,OAAO,IAC/D,OAAO,iBAAiB,IAAI,GAAG;EACvC;CACF;CACA,IAAI,QAAQ,aACV,OAAO,KAAK;EACV,IAAI;EACJ,aAAa;EACb,KAAK,YAAY;GACf,MAAM,QAAQ,MAAM,IAAI,UAAU;GAClC,IAAI;IACF,MAAM,OAAO,MAAM,MAAM,SAAS;KAAE,OAAO;KAAS,QAAQ;IAAE,CAAC;IAC/D,KAAK,MAAM,OAAO,MAAM;KAEtB,IAAI,EAAC,MADe,MAAM,YAAY,IAAI,KAAK,EAAA,CACpC,IAAI,OAAO,OAAO,gBAAgB,OAAO,IAAI,MAAM,0BAA0B,OAAO;KAC/F,IAAI;MACF,MAAM,8BAA8B,OAAO,GAAG;KAChD,SAAS,OAAO;MACd,OAAO,OAAO,gBAAgB,OAAO,IAAI,MAAM,yBAAyB,OAAO,KAAK,EAAE,IAAI,OAAO;KACnG;IACF;IACA,OAAO,OAAO,gBAAgB,YAAY,KAAK,OAAO,QAAQ;GAChE,UAAU;IACR,MAAM,MAAM,MAAM;GACpB;EACF;CACF,CAAC;CAEH,MAAM,OAAO,IAAI,IAAI,OAAO,KAAK,UAAU,CAAC,MAAM,IAAI,KAAK,CAAC,CAAC;CAC7D,OAAO,QAAQ,IACb,iBAAiB,IAAI,OAAO,OAAO,KAAK,IAAI,EAAE,CAAC,EAAE,IAAI,GAAG,KAAK,OAAO,IAAI,qBAAqB,SAAS,CAAC,CACzG;AACF;;;ACrKA,MAAM,SAAwB;CAC5B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,SAAS,MAAM,UAAU,KAAK,EAAE,aAAa,KAAK,YAAY,SAAS,gBAAgB,EAAE,CAAC;EAChG,IAAI,KAAK,MAAM;GACb,IAAI,UAAmB,CAAC;GACxB,IAAI,sBAA+B,CAAC;GACpC,IAAI;IACF,UAAU,MAAM,aAAa;GAC/B,QAAQ,CAER;GACA,IAAI;IACF,sBAAsB,IAAI,QAAQ,eAAe,KAAK,EAAE,UAAU,IAAI,QAAQ,QAAQ,CAAC,CAAC,GAAG;GAC7F,QAAQ,CAER;GACA,IAAI,MAAM,OAAO,MACf,GAAG,KAAK,UAAU;IAAE,iBAAiB;IAAG,gBAAgB;IAAS,aAAa,IAAI,MAAM,IAAI;IAAG,IAAI,OAAO,OAAO,UAAU,MAAM,WAAW,QAAQ,MAAM,WAAW,SAAS;IAAG;IAAQ;IAAS;GAAoB,CAAC,EAAE,GAC3N;EACF,OACE,KAAK,MAAM,SAAS,QAAQ,IAAI,MAAM,OAAO,MAAM,GAAG,MAAM,OAAO,OAAO,CAAC,EAAE,GAAG,MAAM,GAAG,IAAI,MAAM,QAAQ,GAAG;EAChH,OAAO,OAAO,MAAM,UAAU,MAAM,WAAW,OAAO,IAAI,IAAI;CAChE;AACF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
//#region src/commands/fix/index.ts
|
|
2
|
+
const fix = {
|
|
3
|
+
verb: "fix",
|
|
4
|
+
async run(ctx, args) {
|
|
5
|
+
const runId = args.positionals[0];
|
|
6
|
+
if (!runId) return 2;
|
|
7
|
+
const result = await ctx.controller.send("retry", { target: {
|
|
8
|
+
kind: "phase",
|
|
9
|
+
state: "FIX"
|
|
10
|
+
} }, { runId });
|
|
11
|
+
ctx.stdio.stdout.write(`${JSON.stringify(result)}\n`);
|
|
12
|
+
return result.status === "rejected" ? 3 : result.status === "pending" ? 4 : 0;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { fix as default };
|
|
17
|
+
|
|
18
|
+
//# sourceMappingURL=fix-D4Tj8RVA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-D4Tj8RVA.mjs","names":[],"sources":["../../../../apps/cli/src/commands/fix/index.ts"],"sourcesContent":["// apps/cli/src/commands/fix/index.ts — DESIGN §9 verb `fix` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/fix/**`.\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst fix: CommandModule = {\n verb: 'fix',\n async run(ctx, args) {\n const runId = args.positionals[0];\n if (!runId) return 2;\n const result = await ctx.controller.send('retry', { target: { kind: 'phase', state: 'FIX' } }, { runId });\n ctx.stdio.stdout.write(`${JSON.stringify(result)}\\n`);\n return result.status === 'rejected' ? 3 : result.status === 'pending' ? 4 : 0;\n },\n};\n\nexport default fix;\n"],"mappings":";AAMA,MAAM,MAAqB;CACzB,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,QAAQ,KAAK,YAAY;EAC/B,IAAI,CAAC,OAAO,OAAO;EACnB,MAAM,SAAS,MAAM,IAAI,WAAW,KAAK,SAAS,EAAE,QAAQ;GAAE,MAAM;GAAS,OAAO;EAAM,EAAE,GAAG,EAAE,MAAM,CAAC;EACxG,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,MAAM,EAAE,GAAG;EACpD,OAAO,OAAO,WAAW,aAAa,IAAI,OAAO,WAAW,YAAY,IAAI;CAC9E;AACF"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { gzip } from "node:zlib";
|
|
6
|
+
//#region src/commands/gc/index.ts
|
|
7
|
+
const gzipAsync = promisify(gzip);
|
|
8
|
+
const TERMINAL = /* @__PURE__ */ new Set(["COMPLETED", "CANCELLED"]);
|
|
9
|
+
const DEFAULT_RETENTION = {
|
|
10
|
+
transcriptsDays: 30,
|
|
11
|
+
artifactsDays: 90,
|
|
12
|
+
compressAfterDays: 7
|
|
13
|
+
};
|
|
14
|
+
async function filesUnder(root) {
|
|
15
|
+
const result = [];
|
|
16
|
+
async function visit(directory) {
|
|
17
|
+
let entries;
|
|
18
|
+
try {
|
|
19
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
if (error.code === "ENOENT") return;
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const path = join(directory, entry.name);
|
|
26
|
+
if (entry.isDirectory()) await visit(path);
|
|
27
|
+
else if (entry.isFile()) result.push(path);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await visit(root);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
function classify(path) {
|
|
34
|
+
const normalized = path.replaceAll("\\", "/").toLowerCase();
|
|
35
|
+
if (normalized.endsWith(".gz")) return void 0;
|
|
36
|
+
if (normalized.includes("/sensitive/") || normalized.endsWith(".sensitive") || normalized.endsWith(".sensitive.json")) return "sensitive";
|
|
37
|
+
if (normalized.includes("/transcripts/") || normalized.includes("/wire/") || normalized.includes("/logs/")) return "transcript";
|
|
38
|
+
if (normalized.includes("/artifacts/")) return "artifact";
|
|
39
|
+
if (normalized.includes("/spool/") || normalized.includes("/stream/")) return "spool";
|
|
40
|
+
}
|
|
41
|
+
async function readRetention(cwd) {
|
|
42
|
+
try {
|
|
43
|
+
const value = parse(await readFile(join(cwd, ".cohorte", "config.yaml"), "utf8"));
|
|
44
|
+
return {
|
|
45
|
+
...DEFAULT_RETENTION,
|
|
46
|
+
...value.retention ?? {}
|
|
47
|
+
};
|
|
48
|
+
} catch {
|
|
49
|
+
return DEFAULT_RETENTION;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
const CAS_ADDRESS = /^[a-f0-9]{64}$/;
|
|
53
|
+
async function referencedCasAddresses(runs, casRoot) {
|
|
54
|
+
const retained = /* @__PURE__ */ new Set();
|
|
55
|
+
for (const run of runs) {
|
|
56
|
+
const digest = run.snapshotDigest;
|
|
57
|
+
if (!digest || !CAS_ADDRESS.test(digest)) continue;
|
|
58
|
+
retained.add(digest);
|
|
59
|
+
const path = join(casRoot, digest.slice(0, 2), digest);
|
|
60
|
+
try {
|
|
61
|
+
const text = (await readFile(path, "utf8")).toString();
|
|
62
|
+
for (const address of text.match(/[a-f0-9]{64}/g) ?? []) retained.add(address);
|
|
63
|
+
} catch {}
|
|
64
|
+
}
|
|
65
|
+
return retained;
|
|
66
|
+
}
|
|
67
|
+
async function planGc(ctx) {
|
|
68
|
+
const store = await ctx.openStore();
|
|
69
|
+
try {
|
|
70
|
+
const runs = await store.listRuns({
|
|
71
|
+
limit: 1e5,
|
|
72
|
+
offset: 0
|
|
73
|
+
});
|
|
74
|
+
const retention = await readRetention(ctx.cwd);
|
|
75
|
+
const root = join(ctx.cwd, ".cohorte", "state", "runs");
|
|
76
|
+
const casRoot = join(ctx.cwd, ".cohorte", "state", "cas");
|
|
77
|
+
const now = Date.parse(ctx.clock.now());
|
|
78
|
+
const actions = [];
|
|
79
|
+
for (const run of runs) {
|
|
80
|
+
if (!TERMINAL.has(run.state)) continue;
|
|
81
|
+
if (run.purgeable && typeof store.purgeEvents === "function") actions.push({
|
|
82
|
+
kind: "purge-events",
|
|
83
|
+
path: `.cohorte/state/events/${run.runId}`,
|
|
84
|
+
runId: run.runId
|
|
85
|
+
});
|
|
86
|
+
for (const path of await filesUnder(join(root, run.runId))) {
|
|
87
|
+
const kind = classify(path);
|
|
88
|
+
if (!kind) continue;
|
|
89
|
+
const age = Math.max(0, now - (await stat(path)).mtimeMs);
|
|
90
|
+
if (kind === "sensitive" && age >= retention.compressAfterDays * 864e5) actions.push({
|
|
91
|
+
kind: "compress",
|
|
92
|
+
path,
|
|
93
|
+
runId: run.runId
|
|
94
|
+
});
|
|
95
|
+
else if (kind === "transcript" && age >= retention.transcriptsDays * 864e5) actions.push({
|
|
96
|
+
kind: "delete",
|
|
97
|
+
path,
|
|
98
|
+
runId: run.runId
|
|
99
|
+
});
|
|
100
|
+
else if (kind === "artifact" && age >= retention.artifactsDays * 864e5) actions.push({
|
|
101
|
+
kind: "delete",
|
|
102
|
+
path,
|
|
103
|
+
runId: run.runId
|
|
104
|
+
});
|
|
105
|
+
else if (kind === "spool" && age >= 864e5) actions.push({
|
|
106
|
+
kind: "delete",
|
|
107
|
+
path,
|
|
108
|
+
runId: run.runId
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const retainedCas = await referencedCasAddresses(runs, casRoot);
|
|
113
|
+
for (const path of await filesUnder(casRoot)) {
|
|
114
|
+
const address = path.split("/").at(-1) ?? "";
|
|
115
|
+
if (CAS_ADDRESS.test(address) && !retainedCas.has(address)) actions.push({
|
|
116
|
+
kind: "delete-cas",
|
|
117
|
+
path,
|
|
118
|
+
runId: "cas"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
return actions.sort((a, b) => a.path.localeCompare(b.path));
|
|
122
|
+
} finally {
|
|
123
|
+
await store.close();
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async function applyAction(action, store) {
|
|
127
|
+
if (action.kind === "purge-events") {
|
|
128
|
+
await store.purgeEvents?.(action.runId);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (action.kind === "delete" || action.kind === "delete-cas") return rm(action.path, { force: true });
|
|
132
|
+
const compressed = await gzipAsync(await readFile(action.path));
|
|
133
|
+
await writeFile(`${action.path}.gz`, compressed, { mode: 384 });
|
|
134
|
+
await rm(action.path, { force: true });
|
|
135
|
+
}
|
|
136
|
+
const gc = {
|
|
137
|
+
verb: "gc",
|
|
138
|
+
async run(ctx, args) {
|
|
139
|
+
const dryRun = args.positionals.includes("--dry-run");
|
|
140
|
+
const apply = args.positionals.includes("--apply");
|
|
141
|
+
if (dryRun === apply) return 2;
|
|
142
|
+
const actions = await planGc(ctx);
|
|
143
|
+
if (apply) {
|
|
144
|
+
const store = await ctx.openStore();
|
|
145
|
+
try {
|
|
146
|
+
for (const action of actions) await applyAction(action, store);
|
|
147
|
+
} finally {
|
|
148
|
+
await store.close();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
ctx.stdio.stdout.write(`${JSON.stringify({
|
|
152
|
+
mode: dryRun ? "dry-run" : "apply",
|
|
153
|
+
actions
|
|
154
|
+
})}\n`);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
//#endregion
|
|
159
|
+
export { gc as default };
|
|
160
|
+
|
|
161
|
+
//# sourceMappingURL=gc-BBXlBF5w.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gc-BBXlBF5w.mjs","names":[],"sources":["../../../../apps/cli/src/commands/gc/index.ts"],"sourcesContent":["// apps/cli/src/commands/gc/index.ts — DESIGN §9 verb `gc` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/gc/**`.\nimport type { Dirent } from 'node:fs';\nimport { readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { promisify } from 'node:util';\nimport { gzip } from 'node:zlib';\nimport type { PipelineState } from '@cohorte/protocol';\nimport { parse } from 'yaml';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst gzipAsync = promisify(gzip);\nconst TERMINAL = new Set<PipelineState>(['COMPLETED', 'CANCELLED']);\nconst DEFAULT_RETENTION = { transcriptsDays: 30, artifactsDays: 90, compressAfterDays: 7 };\ntype Action =\n | { kind: 'compress' | 'delete' | 'delete-cas'; path: string; runId: string }\n | { kind: 'purge-events'; path: string; runId: string };\n\ntype RetentionStore = Awaited<ReturnType<Parameters<CommandModule['run']>[0]['openStore']>> & {\n purgeEvents?: (runId: string) => Promise<number>;\n};\n\nasync function filesUnder(root: string): Promise<string[]> {\n const result: string[] = [];\n async function visit(directory: string): Promise<void> {\n let entries: Dirent[];\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;\n throw error;\n }\n for (const entry of entries) {\n const path = join(directory, entry.name);\n if (entry.isDirectory()) await visit(path);\n else if (entry.isFile()) result.push(path);\n }\n }\n await visit(root);\n return result;\n}\n\nfunction classify(path: string): 'sensitive' | 'transcript' | 'artifact' | 'spool' | undefined {\n const normalized = path.replaceAll('\\\\', '/').toLowerCase();\n if (normalized.endsWith('.gz')) return undefined;\n if (normalized.includes('/sensitive/') || normalized.endsWith('.sensitive') || normalized.endsWith('.sensitive.json'))\n return 'sensitive';\n if (normalized.includes('/transcripts/') || normalized.includes('/wire/') || normalized.includes('/logs/'))\n return 'transcript';\n if (normalized.includes('/artifacts/')) return 'artifact';\n if (normalized.includes('/spool/') || normalized.includes('/stream/')) return 'spool';\n return undefined;\n}\n\nasync function readRetention(cwd: string): Promise<typeof DEFAULT_RETENTION> {\n try {\n const value = parse(await readFile(join(cwd, '.cohorte', 'config.yaml'), 'utf8')) as {\n retention?: Partial<typeof DEFAULT_RETENTION>;\n };\n return { ...DEFAULT_RETENTION, ...(value.retention ?? {}) };\n } catch {\n return DEFAULT_RETENTION;\n }\n}\n\nconst CAS_ADDRESS = /^[a-f0-9]{64}$/;\n\nasync function referencedCasAddresses(\n runs: Awaited<ReturnType<RetentionStore['listRuns']>>,\n casRoot: string,\n): Promise<Set<string>> {\n const retained = new Set<string>();\n for (const run of runs) {\n const digest = run.snapshotDigest;\n if (!digest || !CAS_ADDRESS.test(digest)) continue;\n retained.add(digest);\n const path = join(casRoot, digest.slice(0, 2), digest);\n try {\n const text = (await readFile(path, 'utf8')).toString();\n for (const address of text.match(/[a-f0-9]{64}/g) ?? []) retained.add(address);\n } catch {\n // A missing snapshot is reported by resume/doctor; GC must not fail or invent a reference.\n }\n }\n return retained;\n}\n\nasync function planGc(ctx: Parameters<CommandModule['run']>[0]): Promise<Action[]> {\n const store = (await ctx.openStore()) as RetentionStore;\n try {\n const runs = await store.listRuns({ limit: 100_000, offset: 0 });\n const retention = await readRetention(ctx.cwd);\n const root = join(ctx.cwd, '.cohorte', 'state', 'runs');\n const casRoot = join(ctx.cwd, '.cohorte', 'state', 'cas');\n const now = Date.parse(ctx.clock.now());\n const actions: Action[] = [];\n for (const run of runs) {\n if (!TERMINAL.has(run.state)) continue;\n if (run.purgeable && typeof store.purgeEvents === 'function') {\n actions.push({ kind: 'purge-events', path: `.cohorte/state/events/${run.runId}`, runId: run.runId });\n }\n for (const path of await filesUnder(join(root, run.runId))) {\n const kind = classify(path);\n if (!kind) continue;\n const age = Math.max(0, now - (await stat(path)).mtimeMs);\n if (kind === 'sensitive' && age >= retention.compressAfterDays * 86_400_000)\n actions.push({ kind: 'compress', path, runId: run.runId });\n else if (kind === 'transcript' && age >= retention.transcriptsDays * 86_400_000)\n actions.push({ kind: 'delete', path, runId: run.runId });\n else if (kind === 'artifact' && age >= retention.artifactsDays * 86_400_000)\n actions.push({ kind: 'delete', path, runId: run.runId });\n else if (kind === 'spool' && age >= 86_400_000) actions.push({ kind: 'delete', path, runId: run.runId });\n }\n }\n const retainedCas = await referencedCasAddresses(runs, casRoot);\n for (const path of await filesUnder(casRoot)) {\n const address = path.split('/').at(-1) ?? '';\n if (CAS_ADDRESS.test(address) && !retainedCas.has(address))\n actions.push({ kind: 'delete-cas', path, runId: 'cas' });\n }\n return actions.sort((a, b) => a.path.localeCompare(b.path));\n } finally {\n await store.close();\n }\n}\n\nasync function applyAction(action: Action, store: RetentionStore): Promise<void> {\n if (action.kind === 'purge-events') {\n await store.purgeEvents?.(action.runId);\n return;\n }\n if (action.kind === 'delete' || action.kind === 'delete-cas') return rm(action.path, { force: true });\n const compressed = await gzipAsync(await readFile(action.path));\n await writeFile(`${action.path}.gz`, compressed, { mode: 0o600 });\n await rm(action.path, { force: true });\n}\n\nconst gc: CommandModule = {\n verb: 'gc',\n async run(ctx, args) {\n const dryRun = args.positionals.includes('--dry-run');\n const apply = args.positionals.includes('--apply');\n if (dryRun === apply) return 2;\n const actions = await planGc(ctx);\n if (apply) {\n const store = (await ctx.openStore()) as RetentionStore;\n try {\n for (const action of actions) await applyAction(action, store);\n } finally {\n await store.close();\n }\n }\n ctx.stdio.stdout.write(`${JSON.stringify({ mode: dryRun ? 'dry-run' : 'apply', actions })}\\n`);\n return 0;\n },\n};\n\nexport default gc;\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { i as planInit, r as applyInit, t as scanRepository } from "./src-JxawBQBn.mjs";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
//#region src/commands/init/index.ts
|
|
4
|
+
const init = {
|
|
5
|
+
verb: "init",
|
|
6
|
+
async run(ctx, args) {
|
|
7
|
+
const requestedRoot = args.positionals.find((value) => !value.startsWith("--"));
|
|
8
|
+
const root = requestedRoot === void 0 ? ctx.cwd : resolve(ctx.cwd, requestedRoot);
|
|
9
|
+
const model = await scanRepository(root, {
|
|
10
|
+
clock: ctx.clock,
|
|
11
|
+
toolVersion: "3.0.0"
|
|
12
|
+
});
|
|
13
|
+
const plan = await planInit({
|
|
14
|
+
root,
|
|
15
|
+
model,
|
|
16
|
+
cohorteVersion: "3.0.0"
|
|
17
|
+
});
|
|
18
|
+
if (args.positionals.includes("--plan") || args.json) {
|
|
19
|
+
ctx.stdio.stdout.write(`${JSON.stringify(plan)}\n`);
|
|
20
|
+
return 0;
|
|
21
|
+
}
|
|
22
|
+
const result = await applyInit(plan);
|
|
23
|
+
ctx.stdio.stdout.write(`written: ${result.written.join(", ") || "none"}\n`);
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
//#endregion
|
|
28
|
+
export { init as default };
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=init-Okz4mlhW.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init-Okz4mlhW.mjs","names":[],"sources":["../../../../apps/cli/src/commands/init/index.ts"],"sourcesContent":["// apps/cli/src/commands/init/index.ts — DESIGN §9 verb `init` (PLAN §3 rule 3 \"later units fill\n// stub files that lie inside their owned paths; they never edit ... the registry\"). Wave-0 stub: exits with the\n// documented not-available error (spec 24 `configuration/phase-not-available`); filled by the Wave-4/5\n// unit that owns `apps/cli/src/commands/init/**`.\nimport { resolve } from 'node:path';\nimport { applyInit, planInit, scanRepository } from '@cohorte/project-model';\nimport type { CommandModule } from '../../contract/index.ts';\n\nconst init: CommandModule = {\n verb: 'init',\n async run(ctx, args) {\n const requestedRoot = args.positionals.find((value) => !value.startsWith('--'));\n const root = requestedRoot === undefined ? ctx.cwd : resolve(ctx.cwd, requestedRoot);\n const model = await scanRepository(root, { clock: ctx.clock, toolVersion: '3.0.0' });\n const plan = await planInit({ root, model, cohorteVersion: '3.0.0' });\n if (args.positionals.includes('--plan') || args.json) {\n ctx.stdio.stdout.write(`${JSON.stringify(plan)}\\n`);\n return 0;\n }\n const result = await applyInit(plan);\n ctx.stdio.stdout.write(`written: ${result.written.join(', ') || 'none'}\\n`);\n return 0;\n },\n};\n\nexport default init;\n"],"mappings":";;;AAQA,MAAM,OAAsB;CAC1B,MAAM;CACN,MAAM,IAAI,KAAK,MAAM;EACnB,MAAM,gBAAgB,KAAK,YAAY,MAAM,UAAU,CAAC,MAAM,WAAW,IAAI,CAAC;EAC9E,MAAM,OAAO,kBAAkB,KAAA,IAAY,IAAI,MAAM,QAAQ,IAAI,KAAK,aAAa;EACnF,MAAM,QAAQ,MAAM,eAAe,MAAM;GAAE,OAAO,IAAI;GAAO,aAAa;EAAQ,CAAC;EACnF,MAAM,OAAO,MAAM,SAAS;GAAE;GAAM;GAAO,gBAAgB;EAAQ,CAAC;EACpE,IAAI,KAAK,YAAY,SAAS,QAAQ,KAAK,KAAK,MAAM;GACpD,IAAI,MAAM,OAAO,MAAM,GAAG,KAAK,UAAU,IAAI,EAAE,GAAG;GAClD,OAAO;EACT;EACA,MAAM,SAAS,MAAM,UAAU,IAAI;EACnC,IAAI,MAAM,OAAO,MAAM,YAAY,OAAO,QAAQ,KAAK,IAAI,KAAK,OAAO,GAAG;EAC1E,OAAO;CACT;AACF"}
|