@lannguyensi/harness 0.40.0 → 0.42.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/CHANGELOG.md +68 -0
- package/README.md +9 -0
- package/dist/cli/adopt/derive.d.ts +15 -0
- package/dist/cli/adopt/derive.js +34 -7
- package/dist/cli/adopt/derive.js.map +1 -1
- package/dist/cli/adopt/index.d.ts +30 -0
- package/dist/cli/adopt/index.js +23 -3
- package/dist/cli/adopt/index.js.map +1 -1
- package/dist/cli/apply/generate-settings.d.ts +14 -1
- package/dist/cli/apply/generate-settings.js +7 -3
- package/dist/cli/apply/generate-settings.js.map +1 -1
- package/dist/cli/approve/stdin-report.js +2 -0
- package/dist/cli/approve/stdin-report.js.map +1 -1
- package/dist/cli/approve/understanding.js +28 -0
- package/dist/cli/approve/understanding.js.map +1 -1
- package/dist/cli/doctor/claude-mcp.d.ts +50 -0
- package/dist/cli/doctor/claude-mcp.js +170 -0
- package/dist/cli/doctor/claude-mcp.js.map +1 -0
- package/dist/cli/doctor/codex.d.ts +8 -0
- package/dist/cli/doctor/codex.js +8 -1
- package/dist/cli/doctor/codex.js.map +1 -1
- package/dist/cli/doctor/format.js +60 -11
- package/dist/cli/doctor/format.js.map +1 -1
- package/dist/cli/doctor/index.d.ts +46 -0
- package/dist/cli/doctor/index.js +155 -29
- package/dist/cli/doctor/index.js.map +1 -1
- package/dist/cli/doctor/npm-bin-path.js +42 -0
- package/dist/cli/doctor/npm-bin-path.js.map +1 -1
- package/dist/cli/doctor/types.d.ts +37 -0
- package/dist/cli/doctor/types.js.map +1 -1
- package/dist/cli/dry-run.js +5 -0
- package/dist/cli/dry-run.js.map +1 -1
- package/dist/cli/explain.js +16 -3
- package/dist/cli/explain.js.map +1 -1
- package/dist/cli/index.js +328 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/agent-tasks-auth.js +32 -0
- package/dist/cli/init/agent-tasks-auth.js.map +1 -1
- package/dist/cli/init/composer.js +13 -40
- package/dist/cli/init/composer.js.map +1 -1
- package/dist/cli/init/dependencies.js +21 -0
- package/dist/cli/init/dependencies.js.map +1 -1
- package/dist/cli/init/detect.d.ts +20 -3
- package/dist/cli/init/detect.js +34 -36
- package/dist/cli/init/detect.js.map +1 -1
- package/dist/cli/init/index.d.ts +18 -0
- package/dist/cli/init/index.js +28 -2
- package/dist/cli/init/index.js.map +1 -1
- package/dist/cli/init/interactive.d.ts +41 -0
- package/dist/cli/init/interactive.js +421 -6
- package/dist/cli/init/interactive.js.map +1 -1
- package/dist/cli/init/profiles.d.ts +2 -2
- package/dist/cli/init/profiles.js +10 -1
- package/dist/cli/init/profiles.js.map +1 -1
- package/dist/cli/init/templates.d.ts +1 -1
- package/dist/cli/init/templates.js +217 -8
- package/dist/cli/init/templates.js.map +1 -1
- package/dist/cli/list.js +3 -1
- package/dist/cli/list.js.map +1 -1
- package/dist/cli/main.js +10 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/pack/hook-bootstrap.d.ts +24 -0
- package/dist/cli/pack/hook-bootstrap.js +47 -1
- package/dist/cli/pack/hook-bootstrap.js.map +1 -1
- package/dist/cli/pack/hook-branch-protection.js +9 -1
- package/dist/cli/pack/hook-branch-protection.js.map +1 -1
- package/dist/cli/pack/hook-codex-post-tool-use.d.ts +25 -0
- package/dist/cli/pack/hook-codex-post-tool-use.js +160 -0
- package/dist/cli/pack/hook-codex-post-tool-use.js.map +1 -0
- package/dist/cli/pack/hook-codex-pre-tool-use.d.ts +1 -0
- package/dist/cli/pack/hook-codex-pre-tool-use.js +43 -10
- package/dist/cli/pack/hook-codex-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-codex-stop.js +1 -8
- package/dist/cli/pack/hook-codex-stop.js.map +1 -1
- package/dist/cli/pack/hook-post-merge-gate-record.d.ts +30 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js +153 -0
- package/dist/cli/pack/hook-post-merge-gate-record.js.map +1 -0
- package/dist/cli/pack/hook-post-merge-gate.d.ts +28 -0
- package/dist/cli/pack/hook-post-merge-gate.js +244 -0
- package/dist/cli/pack/hook-post-merge-gate.js.map +1 -0
- package/dist/cli/pack/hook-post-tool-use.js +16 -131
- package/dist/cli/pack/hook-post-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-pre-tool-use.js +54 -2
- package/dist/cli/pack/hook-pre-tool-use.js.map +1 -1
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js +148 -2
- package/dist/cli/pack/hook-solution-acceptance-writeguard.js.map +1 -1
- package/dist/cli/pack/hook-stay-in-scope.js +63 -20
- package/dist/cli/pack/hook-stay-in-scope.js.map +1 -1
- package/dist/cli/pack/hook-track-active-claim.js +33 -9
- package/dist/cli/pack/hook-track-active-claim.js.map +1 -1
- package/dist/cli/pack/index.d.ts +2 -1
- package/dist/cli/pack/index.js +2 -1
- package/dist/cli/pack/index.js.map +1 -1
- package/dist/cli/pack/mutate.d.ts +19 -0
- package/dist/cli/pack/mutate.js +38 -0
- package/dist/cli/pack/mutate.js.map +1 -1
- package/dist/cli/pack/reseed.d.ts +19 -0
- package/dist/cli/pack/reseed.js +118 -0
- package/dist/cli/pack/reseed.js.map +1 -0
- package/dist/cli/pack/understanding-report-schema-hint.js +1 -1
- package/dist/cli/pack/understanding-report-schema-hint.js.map +1 -1
- package/dist/cli/pause/index.js +12 -3
- package/dist/cli/pause/index.js.map +1 -1
- package/dist/cli/record/index.d.ts +64 -0
- package/dist/cli/record/index.js +211 -0
- package/dist/cli/record/index.js.map +1 -0
- package/dist/cli/session-start/index.d.ts +21 -0
- package/dist/cli/session-start/index.js +160 -25
- package/dist/cli/session-start/index.js.map +1 -1
- package/dist/cli/session-start/toolchain-parity.d.ts +149 -0
- package/dist/cli/session-start/toolchain-parity.js +618 -0
- package/dist/cli/session-start/toolchain-parity.js.map +1 -0
- package/dist/cli/uninstall/index.d.ts +73 -1
- package/dist/cli/uninstall/index.js +134 -2
- package/dist/cli/uninstall/index.js.map +1 -1
- package/dist/cli/validate/checks.js +8 -0
- package/dist/cli/validate/checks.js.map +1 -1
- package/dist/io/claude-mcp.d.ts +258 -0
- package/dist/io/claude-mcp.js +544 -0
- package/dist/io/claude-mcp.js.map +1 -0
- package/dist/policies/ledger-client.d.ts +34 -1
- package/dist/policies/ledger-client.js +21 -16
- package/dist/policies/ledger-client.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection-runtime.d.ts +4 -2
- package/dist/policy-packs/builtin/branch-protection-runtime.js +4 -2
- package/dist/policy-packs/builtin/branch-protection-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/branch-protection.d.ts +10 -1
- package/dist/policy-packs/builtin/branch-protection.js +17 -0
- package/dist/policy-packs/builtin/branch-protection.js.map +1 -1
- package/dist/policy-packs/builtin/post-merge-gate-runtime.d.ts +195 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js +290 -0
- package/dist/policy-packs/builtin/post-merge-gate-runtime.js.map +1 -0
- package/dist/policy-packs/builtin/post-merge-gate.d.ts +56 -0
- package/dist/policy-packs/builtin/post-merge-gate.js +291 -0
- package/dist/policy-packs/builtin/post-merge-gate.js.map +1 -0
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js +28 -13
- package/dist/policy-packs/builtin/solution-acceptance-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.d.ts +197 -24
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js +342 -40
- package/dist/policy-packs/builtin/understanding-before-execution-runtime.js.map +1 -1
- package/dist/policy-packs/builtin/understanding-before-execution.d.ts +26 -1
- package/dist/policy-packs/builtin/understanding-before-execution.js +302 -17
- package/dist/policy-packs/builtin/understanding-before-execution.js.map +1 -1
- package/dist/policy-packs/registry.d.ts +23 -2
- package/dist/policy-packs/registry.js +31 -2
- package/dist/policy-packs/registry.js.map +1 -1
- package/dist/policy-packs/ux-compare.d.ts +14 -0
- package/dist/policy-packs/ux-compare.js +58 -0
- package/dist/policy-packs/ux-compare.js.map +1 -0
- package/dist/policy-packs/ux-drift-check.d.ts +27 -0
- package/dist/policy-packs/ux-drift-check.js +78 -0
- package/dist/policy-packs/ux-drift-check.js.map +1 -0
- package/dist/probes/mcp.d.ts +13 -0
- package/dist/probes/mcp.js +57 -0
- package/dist/probes/mcp.js.map +1 -1
- package/dist/runtime/approval-signing.d.ts +82 -0
- package/dist/runtime/approval-signing.js +275 -0
- package/dist/runtime/approval-signing.js.map +1 -0
- package/dist/runtime/git-context.d.ts +50 -0
- package/dist/runtime/git-context.js +108 -1
- package/dist/runtime/git-context.js.map +1 -1
- package/dist/runtime/hermetic-spawn-guard.d.ts +25 -0
- package/dist/runtime/hermetic-spawn-guard.js +136 -0
- package/dist/runtime/hermetic-spawn-guard.js.map +1 -0
- package/dist/runtime/intercept.js +62 -5
- package/dist/runtime/intercept.js.map +1 -1
- package/dist/runtime/ledger-writer.d.ts +27 -0
- package/dist/runtime/ledger-writer.js +46 -0
- package/dist/runtime/ledger-writer.js.map +1 -0
- package/dist/runtime/pause-sentinel.js +14 -3
- package/dist/runtime/pause-sentinel.js.map +1 -1
- package/dist/runtime/read-only-bash.js +118 -0
- package/dist/runtime/read-only-bash.js.map +1 -1
- package/dist/runtime/recovery-git-commit.d.ts +22 -0
- package/dist/runtime/recovery-git-commit.js +284 -0
- package/dist/runtime/recovery-git-commit.js.map +1 -0
- package/dist/runtime/tool-name-aliases.d.ts +1 -0
- package/dist/runtime/tool-name-aliases.js +7 -1
- package/dist/runtime/tool-name-aliases.js.map +1 -1
- package/dist/schema/index.d.ts +94 -42
- package/dist/schema/index.js +5 -0
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/policies.d.ts +56 -44
- package/dist/schema/policies.js +79 -9
- package/dist/schema/policies.js.map +1 -1
- package/dist/schema/toolchain-parity.d.ts +18 -0
- package/dist/schema/toolchain-parity.js +35 -0
- package/dist/schema/toolchain-parity.js.map +1 -0
- package/package.json +4 -2
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Structural comparison + safe-parse helpers for `config.ux` /
|
|
2
|
+
// `config.producers` (task 68b9ad9c). Shared by `checkPolicyPackUxDrift`
|
|
3
|
+
// (the `harness doctor` read-side warning, ux-drift-check.ts) and
|
|
4
|
+
// `harness pack reseed` (the opt-in write-side fix, src/cli/pack/reseed.ts)
|
|
5
|
+
// so the two can never independently drift on what "matches the shipped
|
|
6
|
+
// template" means.
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
import { PolicyUxSchema, ProducerSchema } from "../schema/policies.js";
|
|
9
|
+
const ProducersArraySchema = z.array(ProducerSchema);
|
|
10
|
+
function stringArrayEqual(a, b) {
|
|
11
|
+
if (a.length !== b.length)
|
|
12
|
+
return false;
|
|
13
|
+
return a.every((v, i) => v === b[i]);
|
|
14
|
+
}
|
|
15
|
+
export function uxEqual(a, b) {
|
|
16
|
+
return (a.cannot === b.cannot &&
|
|
17
|
+
stringArrayEqual(a.required, b.required) &&
|
|
18
|
+
stringArrayEqual(a.run, b.run));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Structural equality over a `producers:` array. Compares each entry
|
|
22
|
+
* field-by-field per its discriminated `kind` rather than a generic deep
|
|
23
|
+
* equal — the shape is small and fixed (bash/mcp/ask), and this keeps the
|
|
24
|
+
* comparison legible instead of round-tripping through JSON.stringify
|
|
25
|
+
* (which would also be sensitive to incidental key ordering).
|
|
26
|
+
*/
|
|
27
|
+
export function producersEqual(a, b) {
|
|
28
|
+
if (a.length !== b.length)
|
|
29
|
+
return false;
|
|
30
|
+
return a.every((entryA, i) => {
|
|
31
|
+
const entryB = b[i];
|
|
32
|
+
if (!entryB || entryA.kind !== entryB.kind)
|
|
33
|
+
return false;
|
|
34
|
+
if (entryA.kind === "bash" && entryB.kind === "bash") {
|
|
35
|
+
return entryA.command === entryB.command && entryA.description === entryB.description;
|
|
36
|
+
}
|
|
37
|
+
if (entryA.kind === "ask" && entryB.kind === "ask") {
|
|
38
|
+
return entryA.command === entryB.command && entryA.description === entryB.description;
|
|
39
|
+
}
|
|
40
|
+
if (entryA.kind === "mcp" && entryB.kind === "mcp") {
|
|
41
|
+
return (entryA.verb === entryB.verb &&
|
|
42
|
+
entryA.example === entryB.example &&
|
|
43
|
+
entryA.description === entryB.description);
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
/** Parses an unknown `config.ux` value; returns null on any schema rejection. */
|
|
49
|
+
export function safeParseUx(raw) {
|
|
50
|
+
const result = PolicyUxSchema.safeParse(raw);
|
|
51
|
+
return result.success ? result.data : null;
|
|
52
|
+
}
|
|
53
|
+
/** Parses an unknown `config.producers` value; returns null on any schema rejection. */
|
|
54
|
+
export function safeParseProducers(raw) {
|
|
55
|
+
const result = ProducersArraySchema.safeParse(raw);
|
|
56
|
+
return result.success ? result.data : null;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=ux-compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ux-compare.js","sourceRoot":"","sources":["../../src/policy-packs/ux-compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Manifest } from "../schema/index.js";
|
|
2
|
+
export type PolicyPackUxDriftField = "ux" | "producers";
|
|
3
|
+
export interface PolicyPackUxDrift {
|
|
4
|
+
packIndex: number;
|
|
5
|
+
packName: string;
|
|
6
|
+
/** Which sub-field(s) diverge from the shipped template. */
|
|
7
|
+
fields: PolicyPackUxDriftField[];
|
|
8
|
+
message: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Walks `manifest.policy_packs` in declared order. For each enabled
|
|
12
|
+
* builtin pack that has a registered shipped default (see
|
|
13
|
+
* `resolveBuiltinDefaultConfig`), compares the operator's declared
|
|
14
|
+
* `config.ux` / `config.producers` (when present) against that default
|
|
15
|
+
* and reports a divergence for each sub-field that differs.
|
|
16
|
+
*
|
|
17
|
+
* A pack that never declared `config.ux` (or `config.producers`) at all
|
|
18
|
+
* is NOT flagged here: that is a distinct, pre-existing gap (the pack
|
|
19
|
+
* falls back to its legacy engine-vocabulary deny text) rather than a
|
|
20
|
+
* stale COPY of the shipped text, and is out of scope for this check —
|
|
21
|
+
* see task 68b9ad9c's framing (an operator whose manifest predates a
|
|
22
|
+
* wording fix has a STALE `ux.run`, not a MISSING one). A malformed
|
|
23
|
+
* `config.ux` / `config.producers` (already reported separately by
|
|
24
|
+
* `checkPolicyPackConfigs`) is treated as diverging here too, rather
|
|
25
|
+
* than being silently skipped.
|
|
26
|
+
*/
|
|
27
|
+
export declare function checkPolicyPackUxDrift(manifest: Manifest): PolicyPackUxDrift[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// Per-pack `config.ux` / `config.producers` divergence check (task
|
|
2
|
+
// 68b9ad9c). Used by `harness doctor` to warn when an enabled builtin
|
|
3
|
+
// pack's agent-facing deny-message text no longer matches the shipped
|
|
4
|
+
// template for that pack.
|
|
5
|
+
//
|
|
6
|
+
// Motivation (dogfood 2026-07-10, harness 0.40.0): the understanding-gate
|
|
7
|
+
// deny message is entirely driven by `config.ux` when the operator has
|
|
8
|
+
// declared one — the init templates (Solo/Team/Full/Custom) were fixed to
|
|
9
|
+
// teach a new submission form, but that fix only reaches manifests
|
|
10
|
+
// generated AFTER the fix landed. An operator who installed before the fix
|
|
11
|
+
// has an on-disk `config.ux` that still teaches the old wording, and
|
|
12
|
+
// nothing short of a hand-edit ever updated it: `harness apply` projects
|
|
13
|
+
// the manifest OUT to settings.json, it never re-seeds pack config FROM
|
|
14
|
+
// the shipped template back INTO the manifest.
|
|
15
|
+
//
|
|
16
|
+
// This check is the read side of that gap (the warning); `harness pack
|
|
17
|
+
// reseed <name>` (src/cli/pack/reseed.ts) is the opt-in write side. Both
|
|
18
|
+
// consume the same canonical default via `resolveBuiltinDefaultConfig`,
|
|
19
|
+
// and the same comparison helpers (ux-compare.ts), so they can never
|
|
20
|
+
// independently drift on what "the shipped template" means.
|
|
21
|
+
//
|
|
22
|
+
// Deliberately doctor-only (not `harness validate`), mirroring
|
|
23
|
+
// `checkPolicyPackVersions`: this is an informational nudge about
|
|
24
|
+
// wording that still functions, not a schema violation.
|
|
25
|
+
import { resolveBuiltinDefaultConfig } from "./registry.js";
|
|
26
|
+
import { producersEqual, safeParseProducers, safeParseUx, uxEqual } from "./ux-compare.js";
|
|
27
|
+
/**
|
|
28
|
+
* Walks `manifest.policy_packs` in declared order. For each enabled
|
|
29
|
+
* builtin pack that has a registered shipped default (see
|
|
30
|
+
* `resolveBuiltinDefaultConfig`), compares the operator's declared
|
|
31
|
+
* `config.ux` / `config.producers` (when present) against that default
|
|
32
|
+
* and reports a divergence for each sub-field that differs.
|
|
33
|
+
*
|
|
34
|
+
* A pack that never declared `config.ux` (or `config.producers`) at all
|
|
35
|
+
* is NOT flagged here: that is a distinct, pre-existing gap (the pack
|
|
36
|
+
* falls back to its legacy engine-vocabulary deny text) rather than a
|
|
37
|
+
* stale COPY of the shipped text, and is out of scope for this check —
|
|
38
|
+
* see task 68b9ad9c's framing (an operator whose manifest predates a
|
|
39
|
+
* wording fix has a STALE `ux.run`, not a MISSING one). A malformed
|
|
40
|
+
* `config.ux` / `config.producers` (already reported separately by
|
|
41
|
+
* `checkPolicyPackConfigs`) is treated as diverging here too, rather
|
|
42
|
+
* than being silently skipped.
|
|
43
|
+
*/
|
|
44
|
+
export function checkPolicyPackUxDrift(manifest) {
|
|
45
|
+
const out = [];
|
|
46
|
+
manifest.policy_packs.forEach((pack, packIndex) => {
|
|
47
|
+
if (!pack.enabled)
|
|
48
|
+
return;
|
|
49
|
+
const canonical = resolveBuiltinDefaultConfig(pack);
|
|
50
|
+
if (!canonical)
|
|
51
|
+
return;
|
|
52
|
+
const fields = [];
|
|
53
|
+
const currentUx = pack.config["ux"];
|
|
54
|
+
if (canonical.ux && currentUx !== undefined) {
|
|
55
|
+
const parsed = safeParseUx(currentUx);
|
|
56
|
+
if (!parsed || !uxEqual(parsed, canonical.ux))
|
|
57
|
+
fields.push("ux");
|
|
58
|
+
}
|
|
59
|
+
const currentProducers = pack.config["producers"];
|
|
60
|
+
if (canonical.producers && currentProducers !== undefined) {
|
|
61
|
+
const parsed = safeParseProducers(currentProducers);
|
|
62
|
+
if (!parsed || !producersEqual(parsed, canonical.producers))
|
|
63
|
+
fields.push("producers");
|
|
64
|
+
}
|
|
65
|
+
if (fields.length === 0)
|
|
66
|
+
return;
|
|
67
|
+
out.push({
|
|
68
|
+
packIndex,
|
|
69
|
+
packName: pack.name,
|
|
70
|
+
fields,
|
|
71
|
+
message: `pack "${pack.name}" config.${fields.join(" / config.")} diverges from the shipped ` +
|
|
72
|
+
`builtin template; a supported update exists. Review with ` +
|
|
73
|
+
`\`harness pack reseed ${pack.name} --dry-run\` and apply with \`harness pack reseed ${pack.name}\`.`,
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=ux-drift-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ux-drift-check.js","sourceRoot":"","sources":["../../src/policy-packs/ux-drift-check.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,sEAAsE;AACtE,sEAAsE;AACtE,0BAA0B;AAC1B,EAAE;AACF,0EAA0E;AAC1E,uEAAuE;AACvE,0EAA0E;AAC1E,mEAAmE;AACnE,2EAA2E;AAC3E,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,+CAA+C;AAC/C,EAAE;AACF,uEAAuE;AACvE,yEAAyE;AACzE,wEAAwE;AACxE,qEAAqE;AACrE,4DAA4D;AAC5D,EAAE;AACF,+DAA+D;AAC/D,kEAAkE;AAClE,wDAAwD;AAExD,OAAO,EAAE,2BAA2B,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAa3F;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,sBAAsB,CAAC,QAAkB;IACvD,MAAM,GAAG,GAAwB,EAAE,CAAC;IACpC,QAAQ,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE;QAChD,IAAI,CAAC,IAAI,CAAC,OAAO;YAAE,OAAO;QAC1B,MAAM,SAAS,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,SAAS;YAAE,OAAO;QAEvB,MAAM,MAAM,GAA6B,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,SAAS,CAAC,EAAE,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5C,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,CAAC;QACD,MAAM,gBAAgB,GAAG,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAClD,IAAI,SAAS,CAAC,SAAS,IAAI,gBAAgB,KAAK,SAAS,EAAE,CAAC;YAC1D,MAAM,MAAM,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,CAAC;YACpD,IAAI,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,CAAC;gBAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACxF,CAAC;QACD,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAEhC,GAAG,CAAC,IAAI,CAAC;YACP,SAAS;YACT,QAAQ,EAAE,IAAI,CAAC,IAAI;YACnB,MAAM;YACN,OAAO,EACL,SAAS,IAAI,CAAC,IAAI,YAAY,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,6BAA6B;gBACpF,2DAA2D;gBAC3D,yBAAyB,IAAI,CAAC,IAAI,qDAAqD,IAAI,CAAC,IAAI,KAAK;SACxG,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC"}
|
package/dist/probes/mcp.d.ts
CHANGED
|
@@ -6,6 +6,19 @@ export type McpProbeOutcome = {
|
|
|
6
6
|
kind: "error";
|
|
7
7
|
latencyMs: number;
|
|
8
8
|
message: string;
|
|
9
|
+
/**
|
|
10
|
+
* Set when the failure is a `spawn` `ENOENT` (the declared binary
|
|
11
|
+
* could not be resolved at all, as opposed to the process starting
|
|
12
|
+
* and then misbehaving). Doctor uses this to decide whether a
|
|
13
|
+
* PATH-shadow remediation hint applies (harness/7f8fb4bc).
|
|
14
|
+
*/
|
|
15
|
+
enoent?: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* One-line PATH-shadow remediation, populated by the doctor layer
|
|
18
|
+
* (which knows the resolved npm global bin dir) when an `enoent`
|
|
19
|
+
* binary is found to actually exist there, just not on PATH.
|
|
20
|
+
*/
|
|
21
|
+
pathHint?: string;
|
|
9
22
|
}
|
|
10
23
|
/**
|
|
11
24
|
* Server exited with code 0 BEFORE the doctor received a JSON-RPC
|
package/dist/probes/mcp.js
CHANGED
|
@@ -15,6 +15,29 @@ function commandToArgs(server) {
|
|
|
15
15
|
const args = list.slice(1).map(expandHomePath);
|
|
16
16
|
return { exe, args };
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Build the `error` outcome for a spawn-time failure (as opposed to a
|
|
20
|
+
* failure after the process started). `ENOENT` — the binary is not
|
|
21
|
+
* resolvable at all — gets the "not found on PATH" wording the doctor
|
|
22
|
+
* summary and PATH-shadow hint (added by the doctor layer) key off of;
|
|
23
|
+
* any other errno (e.g. `EACCES`) still reports gracefully but without
|
|
24
|
+
* implying a PATH problem.
|
|
25
|
+
*/
|
|
26
|
+
function spawnErrorOutcome(exe, err, latencyMs) {
|
|
27
|
+
if (err?.code === "ENOENT") {
|
|
28
|
+
return {
|
|
29
|
+
kind: "error",
|
|
30
|
+
latencyMs,
|
|
31
|
+
message: `${exe} not found on PATH (spawn ENOENT)`,
|
|
32
|
+
enoent: true,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
kind: "error",
|
|
37
|
+
latencyMs,
|
|
38
|
+
message: `spawn ${exe} failed: ${err?.message ?? "unknown spawn error"}`,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
18
41
|
async function runRealProbe(server, opts) {
|
|
19
42
|
if (server.enabled === false) {
|
|
20
43
|
return { name: server.name, outcome: { kind: "disabled" } };
|
|
@@ -49,6 +72,18 @@ async function runRealProbe(server, opts) {
|
|
|
49
72
|
let processExited = false;
|
|
50
73
|
let exitCode = null;
|
|
51
74
|
let exitSignal = null;
|
|
75
|
+
// A declared MCP whose binary cannot be resolved (spawn ENOENT, or any
|
|
76
|
+
// other spawn-time failure such as EACCES) emits an 'error' event on
|
|
77
|
+
// `child` ASYNCHRONOUSLY, not a synchronous throw from `spawn()` above.
|
|
78
|
+
// Node's EventEmitter treats an 'error' event with zero listeners as an
|
|
79
|
+
// unhandled exception that crashes the whole process — this is exactly
|
|
80
|
+
// the "spawn grounding-mcp ENOENT ... emitted 'error' event on
|
|
81
|
+
// ChildProcess" dogfood crash (harness/7f8fb4bc). Listening here turns
|
|
82
|
+
// it into a normal, per-server doctor outcome instead.
|
|
83
|
+
let spawnErr = null;
|
|
84
|
+
child.on("error", (err) => {
|
|
85
|
+
spawnErr = err;
|
|
86
|
+
});
|
|
52
87
|
child.stderr.on("data", (chunk) => {
|
|
53
88
|
stderrBuf += chunk.toString("utf8");
|
|
54
89
|
});
|
|
@@ -105,6 +140,14 @@ async function runRealProbe(server, opts) {
|
|
|
105
140
|
child.once("exit", () => resolve("exit"));
|
|
106
141
|
});
|
|
107
142
|
}
|
|
143
|
+
function waitForSpawnError() {
|
|
144
|
+
return new Promise((resolve) => {
|
|
145
|
+
if (spawnErr)
|
|
146
|
+
resolve("spawn-error");
|
|
147
|
+
else
|
|
148
|
+
child.once("error", () => resolve("spawn-error"));
|
|
149
|
+
});
|
|
150
|
+
}
|
|
108
151
|
const timers = new Set();
|
|
109
152
|
function timeoutPromise() {
|
|
110
153
|
return new Promise((resolve) => {
|
|
@@ -130,7 +173,14 @@ async function runRealProbe(server, opts) {
|
|
|
130
173
|
}),
|
|
131
174
|
waitForExit(),
|
|
132
175
|
timeoutPromise(),
|
|
176
|
+
waitForSpawnError(),
|
|
133
177
|
]);
|
|
178
|
+
if (initResult === "spawn-error") {
|
|
179
|
+
return {
|
|
180
|
+
name: server.name,
|
|
181
|
+
outcome: spawnErrorOutcome(exe, spawnErr, Date.now() - start),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
134
184
|
if (initResult === "exit") {
|
|
135
185
|
const latencyMs = Date.now() - start;
|
|
136
186
|
// Clean exit (code 0) before responding looks like a config issue
|
|
@@ -179,7 +229,14 @@ async function runRealProbe(server, opts) {
|
|
|
179
229
|
}),
|
|
180
230
|
waitForExit(),
|
|
181
231
|
timeoutPromise(),
|
|
232
|
+
waitForSpawnError(),
|
|
182
233
|
]);
|
|
234
|
+
if (verbResult === "spawn-error") {
|
|
235
|
+
return {
|
|
236
|
+
name: server.name,
|
|
237
|
+
outcome: spawnErrorOutcome(exe, spawnErr, Date.now() - start),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
183
240
|
if (verbResult === "exit") {
|
|
184
241
|
const latencyMs = Date.now() - start;
|
|
185
242
|
if (exitCode === 0) {
|
package/dist/probes/mcp.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/probes/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAuC,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/probes/mcp.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAuC,MAAM,oBAAoB,CAAC;AAEhF,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAmDxC,SAAS,cAAc,CAAC,CAAS;IAC/B,IAAI,CAAC,KAAK,GAAG;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC;IAC7C,IAAI,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACzE,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,aAAa,CAAC,MAAiB;IACtC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,OAAO,CAAC;QACxC,CAAC,CAAC,MAAM,CAAC,OAAO;QAChB,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,GAAG,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC/C,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,iBAAiB,CACxB,GAAW,EACX,GAAiC,EACjC,SAAiB;IAEjB,IAAI,GAAG,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC3B,OAAO;YACL,IAAI,EAAE,OAAO;YACb,SAAS;YACT,OAAO,EAAE,GAAG,GAAG,mCAAmC;YAClD,MAAM,EAAE,IAAI;SACb,CAAC;IACJ,CAAC;IACD,OAAO;QACL,IAAI,EAAE,OAAO;QACb,SAAS;QACT,OAAO,EAAE,SAAS,GAAG,YAAY,GAAG,EAAE,OAAO,IAAI,qBAAqB,EAAE;KACzE,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,YAAY,CACzB,MAAiB,EACjB,IAAyB;IAEzB,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC7B,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,CAAC;IAC9D,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,CAAC;IAClE,CAAC;IACD,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC;IACnD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,IAAI,KAAqC,CAAC;IAC1C,IAAI,CAAC;QACH,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YACvB,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC,EAAE;YAC9C,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE;gBACP,IAAI,EAAE,OAAO;gBACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;gBAC7B,OAAO,EAAE,iBAAkB,GAAa,CAAC,OAAO,EAAE;aACnD;SACF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;IACnD,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,UAAU,GAA0B,IAAI,CAAC;IAC7C,uEAAuE;IACvE,qEAAqE;IACrE,wEAAwE;IACxE,wEAAwE;IACxE,uEAAuE;IACvE,+DAA+D;IAC/D,uEAAuE;IACvE,uDAAuD;IACvD,IAAI,QAAQ,GAAiC,IAAI,CAAC;IAClD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;QACxB,QAAQ,GAAG,GAA4B,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QACxC,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;QAC3B,iFAAiF;IACnF,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;QAChC,aAAa,GAAG,IAAI,CAAC;QACrB,QAAQ,GAAG,IAAI,CAAC;QAChB,UAAU,GAAG,MAAM,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;QACxC,SAAS,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACpC,IAAI,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,OAAO,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC3C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YACpC,IAAI,IAAI,EAAE,CAAC;gBACT,IAAI,CAAC;oBACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAoB,CAAC;oBAChD,IAAI,OAAO,GAAG,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;wBAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;wBACpC,IAAI,OAAO,EAAE,CAAC;4BACZ,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;4BACvB,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;wBACvB,CAAC;oBACH,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,2BAA2B;gBAC7B,CAAC;YACH,CAAC;YACD,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,SAAS,IAAI,CAAC,EAAU,EAAE,MAAc,EAAE,MAA+B;QACvE,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;YACtC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YACjC,IAAI,CAAC;gBACH,IAAI,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;oBACrE,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC;gBACnF,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,yEAAyE;YAC3E,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,WAAW;QAClB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,aAAa;gBAAE,OAAO,CAAC,MAAM,CAAC,CAAC;;gBAC9B,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,SAAS,iBAAiB;QACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,IAAI,QAAQ;gBAAE,OAAO,CAAC,aAAa,CAAC,CAAC;;gBAChC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,SAAS,cAAc;QACrB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,MAAM,CAAC,GAAG,UAAU,CAAC,GAAG,EAAE;gBACxB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBACjB,OAAO,CAAC,SAAS,CAAC,CAAC;YACrB,CAAC,EAAE,SAAS,CAAC,CAAC;YACd,CAAC,CAAC,KAAK,EAAE,CAAC;YACV,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;IACL,CAAC;IACD,SAAS,WAAW;QAClB,KAAK,MAAM,CAAC,IAAI,MAAM;YAAE,YAAY,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,CAAC,KAAK,EAAE,CAAC;IACjB,CAAC;IAED,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YACpC,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE;gBACpB,eAAe,EAAE,YAAY;gBAC7B,YAAY,EAAE,EAAE;gBAChB,UAAU,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE;aACzD,CAAC;YACF,WAAW,EAAE;YACb,cAAc,EAAE;YAChB,iBAAiB,EAAE;SACpB,CAAC,CAAC;QACH,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;YACjC,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;aAC9D,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,kEAAkE;YAClE,+DAA+D;YAC/D,4DAA4D;YAC5D,oBAAoB;YACpB,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO;oBACL,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,SAAS,EAAE,KAAK,EAAE,YAAY,EAAE;iBACjE,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC;YAC5E,MAAM,MAAM,GACV,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,UAAU,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;YAC1F,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,SAAS;oBACT,OAAO,EAAE,WAAW,MAAM,KAAK,OAAO,EAAE;iBACzC;aACF,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;oBAC7B,OAAO,EAAE,8BAA8B,SAAS,IAAI;iBACrD;aACF,CAAC;QACJ,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,aAAa,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACrE,KAAK,CAAC,KAAK,CAAC,KAAK,CACf,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,2BAA2B,EAAE,CAAC,IAAI,CAC/E,CAAC;YACJ,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,2BAA2B;QAC7B,CAAC;QAED,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YACpC,IAAI,CAAC,CAAC,EAAE,YAAY,EAAE;gBACpB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;gBACxB,SAAS,EAAE,EAAE;aACd,CAAC;YACF,WAAW,EAAE;YACb,cAAc,EAAE;YAChB,iBAAiB,EAAE;SACpB,CAAC,CAAC;QACH,IAAI,UAAU,KAAK,aAAa,EAAE,CAAC;YACjC,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE,iBAAiB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;aAC9D,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACrC,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;gBACnB,OAAO;oBACL,IAAI,EAAE,MAAM,CAAC,IAAI;oBACjB,OAAO,EAAE;wBACP,IAAI,EAAE,aAAa;wBACnB,SAAS;wBACT,KAAK,EAAE,MAAM;wBACb,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI;qBACzB;iBACF,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,IAAI,EAAE,IAAI,aAAa,CAAC;YAC5E,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,SAAS;oBACT,OAAO,EAAE,yBAAyB,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE;iBACnE;aACF,CAAC;QACJ,CAAC;QACD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;oBAC7B,OAAO,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,oBAAoB,SAAS,IAAI;iBAChE;aACF,CAAC;QACJ,CAAC;QAED,MAAM,QAAQ,GAAG,UAAgE,CAAC;QAClF,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,OAAO;gBACL,IAAI,EAAE,MAAM,CAAC,IAAI;gBACjB,OAAO,EAAE;oBACP,IAAI,EAAE,OAAO;oBACb,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK;oBAC7B,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,IAAI,mBAAmB;iBACvD;aACF,CAAC;QACJ,CAAC;QACD,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE;SAC5D,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,WAAW,EAAE,CAAC;QACd,IAAI,CAAC,aAAa;YAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,MAAM,OAAO,YAAY;IACM;IAA7B,YAA6B,OAA4B,EAAE;QAA9B,SAAI,GAAJ,IAAI,CAA0B;IAAG,CAAC;IAC/D,IAAI,CAAC,MAAiB;QACpB,OAAO,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAoB,EACpB,KAAe;IAEf,OAAO,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACxD,CAAC"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/** Versioned algorithm tag, so a future re-key scheme can reject mismatches explicitly rather than guess. */
|
|
2
|
+
export declare const SIGNING_ALG = "hmac-sha256-v1";
|
|
3
|
+
/** Basename of the signing-key file, a sibling of `.approvals/` under `generatedDir`. */
|
|
4
|
+
export declare const SIGNING_KEY_BASENAME = ".approval-signing.key";
|
|
5
|
+
/** Filesystem path of the signing key for a given `generatedDir`. */
|
|
6
|
+
export declare function signingKeyPathFor(generatedDir: string): string;
|
|
7
|
+
export interface SigningKeyHandle {
|
|
8
|
+
key: Buffer;
|
|
9
|
+
filePath: string;
|
|
10
|
+
/** True when this call generated a fresh key (first use / post-rotation). */
|
|
11
|
+
created: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Read the signing key, generating one (0600, `crypto.randomBytes(32)`) on
|
|
15
|
+
* first use. Race-safe-ish: a concurrent create loses the `wx` (exclusive
|
|
16
|
+
* create) race and falls back to reading whatever the winner wrote, rather
|
|
17
|
+
* than throwing or clobbering it. A key file shorter than `KEY_BYTES`
|
|
18
|
+
* (truncated / corrupted) is treated as absent and regenerated, since a
|
|
19
|
+
* short key would only weaken every future signature.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getOrCreateSigningKey(generatedDir: string): SigningKeyHandle;
|
|
22
|
+
/**
|
|
23
|
+
* Force a fresh key, overwriting any existing one. Every marker signed
|
|
24
|
+
* under the old key stops verifying immediately (see module doc). Not
|
|
25
|
+
* currently wired to a CLI verb (documented as "delete the file" for v1);
|
|
26
|
+
* exported so a future `harness approve rotate-key`-style verb, or a test,
|
|
27
|
+
* can call it directly without re-deriving the path convention.
|
|
28
|
+
*/
|
|
29
|
+
export declare function rotateSigningKey(generatedDir: string): SigningKeyHandle;
|
|
30
|
+
/** sha256 hex digest of a string, used to bind a marker to a persisted report's content. */
|
|
31
|
+
export declare function sha256Hex(content: string): string;
|
|
32
|
+
export interface SignedMarkerFields {
|
|
33
|
+
approvedAt: string;
|
|
34
|
+
approvedBy: string;
|
|
35
|
+
/** sha256 hex of the persisted-report content this approval is bound to; null when no report exists to bind (e.g. branch-protection, or a ledger-only approval). */
|
|
36
|
+
reportContentHash: string | null;
|
|
37
|
+
alg: string;
|
|
38
|
+
signature: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Sign a marker's fields for `markerId` (the marker's lookup key — a raw
|
|
42
|
+
* sessionId for the understanding-gate session marker, `task-<id>` for a
|
|
43
|
+
* task-scoped marker, `branch-protection-<sessionId>` for the
|
|
44
|
+
* branch-protection twin). Binding `markerId` into the signed payload is
|
|
45
|
+
* what stops a validly-signed marker from being copied/renamed onto a
|
|
46
|
+
* DIFFERENT id and still verifying — verification always recomputes the
|
|
47
|
+
* HMAC using the id the caller is checking, not one read back out of the
|
|
48
|
+
* marker body.
|
|
49
|
+
*/
|
|
50
|
+
export declare function signMarker(generatedDir: string, markerId: string, marker: {
|
|
51
|
+
approvedAt: string;
|
|
52
|
+
approvedBy: string;
|
|
53
|
+
reportContentHash?: string | null;
|
|
54
|
+
}): SignedMarkerFields;
|
|
55
|
+
export type SignatureVerification = {
|
|
56
|
+
ok: true;
|
|
57
|
+
} | {
|
|
58
|
+
ok: false;
|
|
59
|
+
reason: string;
|
|
60
|
+
/**
|
|
61
|
+
* `"key-unavailable"` when verification could not even run because
|
|
62
|
+
* obtaining the signing key itself failed (permission error, disk
|
|
63
|
+
* issue) — a fail-closed I/O problem, not evidence of tampering.
|
|
64
|
+
* Callers (`checkApprovalMarker`) use this to classify the outcome
|
|
65
|
+
* distinctly from an actual forged/invalid signature (review LOW 1,
|
|
66
|
+
* harness/f9485cc7): a broken key file must not read as an active
|
|
67
|
+
* forgery attempt in audit output. Omitted for every other failure
|
|
68
|
+
* (missing field, wrong alg, bad signature).
|
|
69
|
+
*/
|
|
70
|
+
kind?: "key-unavailable";
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Verify a parsed marker JSON body against `markerId`. Never throws: any
|
|
74
|
+
* failure to obtain the signing key (permission error, disk issue) is
|
|
75
|
+
* treated as a verification failure, not an exception — the gate-side
|
|
76
|
+
* caller must fail closed (not approved) rather than crash the hook
|
|
77
|
+
* process, which the surrounding PreToolUse blocker would otherwise turn
|
|
78
|
+
* into an uncontrolled failure mode. That specific failure mode is tagged
|
|
79
|
+
* `kind: "key-unavailable"` so the caller can tell "we couldn't even check"
|
|
80
|
+
* apart from "we checked and it's invalid".
|
|
81
|
+
*/
|
|
82
|
+
export declare function verifyMarkerSignature(generatedDir: string, markerId: string, payload: Record<string, unknown>): SignatureVerification;
|