martin-loop 0.1.4 → 1.3.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/CODE_OF_CONDUCT.md +32 -0
- package/README.md +172 -227
- package/demo/seeded-workspace/README.md +35 -0
- package/demo/seeded-workspace/TASKS.md +29 -0
- package/demo/seeded-workspace/martin.config.yaml +11 -0
- package/demo/seeded-workspace/package.json +8 -0
- package/demo/seeded-workspace/src/invoice-summary.js +11 -0
- package/demo/seeded-workspace/test/invoice-summary.test.js +20 -0
- package/dist/bin/martin-loop.js +0 -0
- package/dist/vendor/adapters/claude-cli.d.ts +19 -4
- package/dist/vendor/adapters/claude-cli.js +55 -24
- package/dist/vendor/adapters/cli-bridge.d.ts +1 -0
- package/dist/vendor/adapters/cli-bridge.js +154 -28
- package/dist/vendor/adapters/counter.d.ts +1 -0
- package/dist/vendor/adapters/counter.js +4 -0
- package/dist/vendor/adapters/git-baseline.d.ts +50 -0
- package/dist/vendor/adapters/git-baseline.js +233 -0
- package/dist/vendor/adapters/index.d.ts +1 -0
- package/dist/vendor/adapters/index.js +1 -0
- package/dist/vendor/adapters/openrouter-adapter.d.ts +15 -0
- package/dist/vendor/adapters/openrouter-adapter.js +302 -0
- package/dist/vendor/adapters/usage.d.ts +48 -0
- package/dist/vendor/adapters/usage.js +66 -0
- package/dist/vendor/adapters/verifier-only.d.ts +7 -0
- package/dist/vendor/adapters/verifier-only.js +57 -0
- package/dist/vendor/cli/bin/exit.d.ts +12 -0
- package/dist/vendor/cli/bin/exit.js +28 -0
- package/dist/vendor/cli/commands/analyze.d.ts +5 -0
- package/dist/vendor/cli/commands/analyze.js +58 -0
- package/dist/vendor/cli/commands/audit-log-verify.d.ts +34 -0
- package/dist/vendor/cli/commands/audit-log-verify.js +99 -0
- package/dist/vendor/cli/commands/audit.d.ts +8 -0
- package/dist/vendor/cli/commands/audit.js +199 -0
- package/dist/vendor/cli/commands/corpus.d.ts +5 -0
- package/dist/vendor/cli/commands/corpus.js +60 -0
- package/dist/vendor/cli/commands/doctor.d.ts +8 -0
- package/dist/vendor/cli/commands/doctor.js +219 -0
- package/dist/vendor/cli/commands/explain.d.ts +17 -0
- package/dist/vendor/cli/commands/explain.js +176 -0
- package/dist/vendor/cli/commands/export.d.ts +5 -0
- package/dist/vendor/cli/commands/export.js +60 -0
- package/dist/vendor/cli/commands/governance.d.ts +8 -0
- package/dist/vendor/cli/commands/governance.js +95 -0
- package/dist/vendor/cli/commands/improve.d.ts +18 -0
- package/dist/vendor/cli/commands/improve.js +396 -0
- package/dist/vendor/cli/commands/init.d.ts +8 -0
- package/dist/vendor/cli/commands/init.js +281 -0
- package/dist/vendor/cli/commands/migration.d.ts +8 -0
- package/dist/vendor/cli/commands/migration.js +67 -0
- package/dist/vendor/cli/commands/prior.d.ts +23 -0
- package/dist/vendor/cli/commands/prior.js +145 -0
- package/dist/vendor/cli/commands/resume.d.ts +21 -0
- package/dist/vendor/cli/commands/resume.js +73 -0
- package/dist/vendor/cli/commands/verify.d.ts +6 -0
- package/dist/vendor/cli/commands/verify.js +43 -0
- package/dist/vendor/cli/index.d.ts +6 -1
- package/dist/vendor/cli/index.js +124 -7
- package/dist/vendor/cli/research/public-corpus.d.ts +43 -0
- package/dist/vendor/cli/research/public-corpus.js +151 -0
- package/dist/vendor/cli/ui/error-card.d.ts +38 -0
- package/dist/vendor/cli/ui/error-card.js +103 -0
- package/dist/vendor/cli/ui/mission-brief.d.ts +41 -0
- package/dist/vendor/cli/ui/mission-brief.js +173 -0
- package/dist/vendor/cli/ui/summary-card.d.ts +34 -0
- package/dist/vendor/cli/ui/summary-card.js +102 -0
- package/dist/vendor/contracts/audit.d.ts +46 -0
- package/dist/vendor/contracts/audit.js +360 -0
- package/dist/vendor/contracts/index.d.ts +3 -1
- package/dist/vendor/contracts/post-phase15.d.ts +240 -0
- package/dist/vendor/contracts/post-phase15.js +166 -0
- package/dist/vendor/core/agent/mandates.d.ts +46 -0
- package/dist/vendor/core/agent/mandates.js +178 -0
- package/dist/vendor/core/agent/receipts.d.ts +38 -0
- package/dist/vendor/core/agent/receipts.js +131 -0
- package/dist/vendor/core/agent/signing.d.ts +17 -0
- package/dist/vendor/core/agent/signing.js +91 -0
- package/dist/vendor/core/attestation/sign.d.ts +25 -0
- package/dist/vendor/core/attestation/sign.js +216 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.d.ts +120 -0
- package/dist/vendor/core/autonomy/autonomous-promotion.js +346 -0
- package/dist/vendor/core/autonomy/envelope-v2.d.ts +29 -0
- package/dist/vendor/core/autonomy/envelope-v2.js +60 -0
- package/dist/vendor/core/autonomy/envelope.d.ts +17 -0
- package/dist/vendor/core/autonomy/envelope.js +27 -0
- package/dist/vendor/core/autonomy/escalation-ledger.d.ts +20 -0
- package/dist/vendor/core/autonomy/escalation-ledger.js +18 -0
- package/dist/vendor/core/autonomy/resume.d.ts +15 -0
- package/dist/vendor/core/autonomy/resume.js +23 -0
- package/dist/vendor/core/circuit/circuit-breaker.d.ts +60 -0
- package/dist/vendor/core/circuit/circuit-breaker.js +143 -0
- package/dist/vendor/core/compiler.d.ts +2 -0
- package/dist/vendor/core/compiler.js +10 -4
- package/dist/vendor/core/context-distillation.d.ts +3 -0
- package/dist/vendor/core/context-distillation.js +44 -0
- package/dist/vendor/core/context-flow/compile-context.d.ts +8 -0
- package/dist/vendor/core/context-flow/compile-context.js +111 -0
- package/dist/vendor/core/context-flow/entities.d.ts +2 -0
- package/dist/vendor/core/context-flow/entities.js +44 -0
- package/dist/vendor/core/context-flow/evaluate-policy.d.ts +2 -0
- package/dist/vendor/core/context-flow/evaluate-policy.js +42 -0
- package/dist/vendor/core/context-flow/index.d.ts +11 -0
- package/dist/vendor/core/context-flow/index.js +24 -0
- package/dist/vendor/core/context-flow/labels.d.ts +3 -0
- package/dist/vendor/core/context-flow/labels.js +17 -0
- package/dist/vendor/core/context-flow/normalizer.d.ts +9 -0
- package/dist/vendor/core/context-flow/normalizer.js +69 -0
- package/dist/vendor/core/context-flow/profiles.d.ts +33 -0
- package/dist/vendor/core/context-flow/profiles.js +36 -0
- package/dist/vendor/core/context-flow/redaction.d.ts +1 -0
- package/dist/vendor/core/context-flow/redaction.js +6 -0
- package/dist/vendor/core/context-flow/sensitivity.d.ts +2 -0
- package/dist/vendor/core/context-flow/sensitivity.js +27 -0
- package/dist/vendor/core/context-flow/sync-preview.d.ts +2 -0
- package/dist/vendor/core/context-flow/sync-preview.js +22 -0
- package/dist/vendor/core/context-flow/token-estimator.d.ts +3 -0
- package/dist/vendor/core/context-flow/token-estimator.js +13 -0
- package/dist/vendor/core/context-flow/types.d.ts +91 -0
- package/dist/vendor/core/context-flow/types.js +2 -0
- package/dist/vendor/core/context-integrity.d.ts +26 -0
- package/dist/vendor/core/context-integrity.js +56 -0
- package/dist/vendor/core/context-utility.d.ts +47 -0
- package/dist/vendor/core/context-utility.js +405 -0
- package/dist/vendor/core/cost/pipeline.d.ts +92 -0
- package/dist/vendor/core/cost/pipeline.js +141 -0
- package/dist/vendor/core/cost/tagged-cost.d.ts +27 -0
- package/dist/vendor/core/cost/tagged-cost.js +55 -0
- package/dist/vendor/core/cost-governor.d.ts +2 -0
- package/dist/vendor/core/cost-governor.js +50 -0
- package/dist/vendor/core/cve/cve-check.d.ts +80 -0
- package/dist/vendor/core/cve/cve-check.js +172 -0
- package/dist/vendor/core/digital-twin/index.d.ts +27 -0
- package/dist/vendor/core/digital-twin/index.js +90 -0
- package/dist/vendor/core/drift/drift-graph.d.ts +47 -0
- package/dist/vendor/core/drift/drift-graph.js +100 -0
- package/dist/vendor/core/drift/objective-lock.d.ts +69 -0
- package/dist/vendor/core/drift/objective-lock.js +88 -0
- package/dist/vendor/core/drift/scope.d.ts +46 -0
- package/dist/vendor/core/drift/scope.js +102 -0
- package/dist/vendor/core/drift/signature-lock.d.ts +48 -0
- package/dist/vendor/core/drift/signature-lock.js +202 -0
- package/dist/vendor/core/drift/stale-proof-gate.d.ts +21 -0
- package/dist/vendor/core/drift/stale-proof-gate.js +19 -0
- package/dist/vendor/core/eval/known-bad-world-runner.d.ts +24 -0
- package/dist/vendor/core/eval/known-bad-world-runner.js +256 -0
- package/dist/vendor/core/evidence/claim-audit.d.ts +18 -0
- package/dist/vendor/core/evidence/claim-audit.js +89 -0
- package/dist/vendor/core/exit-intelligence.d.ts +2 -0
- package/dist/vendor/core/exit-intelligence.js +58 -0
- package/dist/vendor/core/explain/formatter.d.ts +42 -0
- package/dist/vendor/core/explain/formatter.js +171 -0
- package/dist/vendor/core/explain/timeline.d.ts +29 -0
- package/dist/vendor/core/explain/timeline.js +213 -0
- package/dist/vendor/core/failure-taxonomy.d.ts +2 -0
- package/dist/vendor/core/failure-taxonomy.js +76 -0
- package/dist/vendor/core/gateway/index.d.ts +10 -0
- package/dist/vendor/core/gateway/index.js +12 -0
- package/dist/vendor/core/gateway/registry.d.ts +40 -0
- package/dist/vendor/core/gateway/registry.js +97 -0
- package/dist/vendor/core/gateway/transport.d.ts +31 -0
- package/dist/vendor/core/gateway/transport.js +82 -0
- package/dist/vendor/core/gateway/vault.d.ts +19 -0
- package/dist/vendor/core/gateway/vault.js +29 -0
- package/dist/vendor/core/graph/adapters.d.ts +43 -0
- package/dist/vendor/core/graph/adapters.js +91 -0
- package/dist/vendor/core/graph/hotspots.d.ts +22 -0
- package/dist/vendor/core/graph/hotspots.js +30 -0
- package/dist/vendor/core/graph/index.d.ts +1 -0
- package/dist/vendor/core/graph/index.js +2 -0
- package/dist/vendor/core/honey/honey-tokens.d.ts +32 -0
- package/dist/vendor/core/honey/honey-tokens.js +44 -0
- package/dist/vendor/core/index.d.ts +7 -4
- package/dist/vendor/core/index.js +222 -64
- package/dist/vendor/core/learning/bayesian-update.d.ts +31 -0
- package/dist/vendor/core/learning/bayesian-update.js +60 -0
- package/dist/vendor/core/learning/prior-sets.d.ts +42 -0
- package/dist/vendor/core/learning/prior-sets.js +111 -0
- package/dist/vendor/core/learning/promotion-gate.d.ts +17 -0
- package/dist/vendor/core/learning/promotion-gate.js +23 -0
- package/dist/vendor/core/leash/blast-radius.d.ts +42 -0
- package/dist/vendor/core/leash/blast-radius.js +156 -0
- package/dist/vendor/core/leash/policy-leash.d.ts +31 -0
- package/dist/vendor/core/leash/policy-leash.js +117 -0
- package/dist/vendor/core/memo/memo.d.ts +63 -0
- package/dist/vendor/core/memo/memo.js +97 -0
- package/dist/vendor/core/memory/learning-pipeline.d.ts +154 -0
- package/dist/vendor/core/memory/learning-pipeline.js +391 -0
- package/dist/vendor/core/memory/palace.d.ts +84 -0
- package/dist/vendor/core/memory/palace.js +379 -0
- package/dist/vendor/core/merge/ast-merge.d.ts +22 -0
- package/dist/vendor/core/merge/ast-merge.js +350 -0
- package/dist/vendor/core/merge/text-merge.d.ts +12 -0
- package/dist/vendor/core/merge/text-merge.js +182 -0
- package/dist/vendor/core/otel/tracer.d.ts +45 -0
- package/dist/vendor/core/otel/tracer.js +116 -0
- package/dist/vendor/core/parallel/parallel-attempts.d.ts +28 -0
- package/dist/vendor/core/parallel/parallel-attempts.js +41 -0
- package/dist/vendor/core/parallel/scorer.d.ts +24 -0
- package/dist/vendor/core/parallel/scorer.js +65 -0
- package/dist/vendor/core/pattern-detection.d.ts +64 -0
- package/dist/vendor/core/pattern-detection.js +108 -0
- package/dist/vendor/core/persistence/checkpoint.d.ts +44 -0
- package/dist/vendor/core/persistence/checkpoint.js +156 -0
- package/dist/vendor/core/persistence/cleanup.d.ts +22 -0
- package/dist/vendor/core/persistence/cleanup.js +131 -0
- package/dist/vendor/core/persistence/index.d.ts +2 -0
- package/dist/vendor/core/persistence/index.js +1 -0
- package/dist/vendor/core/persistence/runs-reader.d.ts +52 -0
- package/dist/vendor/core/persistence/runs-reader.js +84 -0
- package/dist/vendor/core/persistence/store.d.ts +6 -1
- package/dist/vendor/core/persistence/store.js +5 -0
- package/dist/vendor/core/policy/file-touch-quota.d.ts +60 -0
- package/dist/vendor/core/policy/file-touch-quota.js +105 -0
- package/dist/vendor/core/policy/policy-loader.d.ts +30 -0
- package/dist/vendor/core/policy/policy-loader.js +170 -0
- package/dist/vendor/core/policy/policy-schema.d.ts +55 -0
- package/dist/vendor/core/policy/policy-schema.js +78 -0
- package/dist/vendor/core/policy.d.ts +6 -0
- package/dist/vendor/core/probe/probe.d.ts +49 -0
- package/dist/vendor/core/probe/probe.js +115 -0
- package/dist/vendor/core/proof/patch-proof.d.ts +58 -0
- package/dist/vendor/core/proof/patch-proof.js +84 -0
- package/dist/vendor/core/proof/semantic-probe.d.ts +25 -0
- package/dist/vendor/core/proof/semantic-probe.js +82 -0
- package/dist/vendor/core/recovery/failure-mode-runner.d.ts +29 -0
- package/dist/vendor/core/recovery/failure-mode-runner.js +39 -0
- package/dist/vendor/core/red-blue/red-phase.d.ts +64 -0
- package/dist/vendor/core/red-blue/red-phase.js +141 -0
- package/dist/vendor/core/red-blue/risk-tiers.d.ts +22 -0
- package/dist/vendor/core/red-blue/risk-tiers.js +33 -0
- package/dist/vendor/core/replay/replay.d.ts +85 -0
- package/dist/vendor/core/replay/replay.js +109 -0
- package/dist/vendor/core/router/engine.d.ts +54 -0
- package/dist/vendor/core/router/engine.js +131 -0
- package/dist/vendor/core/router/index.d.ts +1 -0
- package/dist/vendor/core/router/index.js +2 -0
- package/dist/vendor/core/router/trust-calibration.d.ts +57 -0
- package/dist/vendor/core/router/trust-calibration.js +127 -0
- package/dist/vendor/core/run-martin.d.ts +2 -0
- package/dist/vendor/core/run-martin.js +287 -0
- package/dist/vendor/core/security/cve-scanner.d.ts +62 -0
- package/dist/vendor/core/security/cve-scanner.js +178 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.d.ts +29 -0
- package/dist/vendor/core/sentinel/efficiency-sentinel.js +30 -0
- package/dist/vendor/core/sentinel/progress-guard.d.ts +35 -0
- package/dist/vendor/core/sentinel/progress-guard.js +46 -0
- package/dist/vendor/core/siem/siem-emitter.d.ts +49 -0
- package/dist/vendor/core/siem/siem-emitter.js +157 -0
- package/dist/vendor/core/strategy/attempt-brief.d.ts +22 -0
- package/dist/vendor/core/strategy/attempt-brief.js +89 -0
- package/dist/vendor/core/summarize/diff-summary.d.ts +35 -0
- package/dist/vendor/core/summarize/diff-summary.js +204 -0
- package/dist/vendor/core/surface-signals.d.ts +21 -0
- package/dist/vendor/core/surface-signals.js +139 -0
- package/dist/vendor/core/truth/truth-wall.d.ts +51 -0
- package/dist/vendor/core/truth/truth-wall.js +69 -0
- package/dist/vendor/core/truth-spine.d.ts +26 -0
- package/dist/vendor/core/truth-spine.js +62 -0
- package/dist/vendor/core/types.d.ts +115 -0
- package/dist/vendor/core/types.js +2 -0
- package/dist/vendor/core/verification/tiered-verify.d.ts +17 -0
- package/dist/vendor/core/verification/tiered-verify.js +29 -0
- package/dist/vendor/core/verifier-pyramid.d.ts +32 -0
- package/dist/vendor/core/verifier-pyramid.js +111 -0
- package/dist/vendor/core/workflow-artifacts.d.ts +99 -0
- package/dist/vendor/core/workflow-artifacts.js +668 -0
- package/dist/vendor/core/wrap/supervised-run.d.ts +96 -0
- package/dist/vendor/core/wrap/supervised-run.js +178 -0
- package/docs/assets/cli-animated.svg +139 -0
- package/docs/assets/cli-static.svg +34 -0
- package/docs/assets/github-hero-v2.svg +23 -0
- package/docs/assets/martin-raplph.png.jpg +0 -0
- package/docs/assets/martinloop-logo.png +0 -0
- package/docs/assets/nvidia-inception-program-light.png +0 -0
- package/docs/assets/nvidia-inception-program.png +0 -0
- package/docs/assets/phase3c-sidesidebyside-demo.html +228 -0
- package/docs/assets/side-by-side.svg +134 -0
- package/docs/oss/CLAUDE-CODE-WALKTHROUGH.md +142 -0
- package/docs/oss/EXAMPLES.md +9 -1
- package/docs/oss/OSS-BOUNDARY-REPORT.json +109 -113
- package/docs/oss/OSS-BOUNDARY-REPORT.md +48 -48
- package/docs/oss/QUICKSTART.md +39 -4
- package/docs/oss/RALPH-LOOP-SAFETY.md +113 -0
- package/docs/oss/README.md +7 -4
- package/docs/oss/RELEASE-SURFACE-REPORT.json +46 -45
- package/docs/oss/RELEASE-SURFACE-REPORT.md +36 -35
- package/package.json +129 -49
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { MartinAdapter } from "../core/index.js";
|
|
2
|
+
export interface VerifierOnlyAdapterOptions {
|
|
3
|
+
workingDirectory?: string;
|
|
4
|
+
verifyTimeoutMs?: number;
|
|
5
|
+
label?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function createVerifierOnlyAdapter(options?: VerifierOnlyAdapterOptions): MartinAdapter;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { readGitExecutionArtifacts, runVerification } from "./cli-bridge.js";
|
|
2
|
+
import { createAdapterCapabilities, normalizeUsage } from "./runtime-support.js";
|
|
3
|
+
export function createVerifierOnlyAdapter(options = {}) {
|
|
4
|
+
const workingDirectory = options.workingDirectory ?? process.cwd();
|
|
5
|
+
const verifyTimeoutMs = options.verifyTimeoutMs ?? 60_000;
|
|
6
|
+
return {
|
|
7
|
+
adapterId: "direct:verifier:verify-only",
|
|
8
|
+
kind: "direct-provider",
|
|
9
|
+
label: options.label ?? "Verifier-only adapter",
|
|
10
|
+
metadata: {
|
|
11
|
+
providerId: "verifier",
|
|
12
|
+
model: "verify-only",
|
|
13
|
+
transport: "cli",
|
|
14
|
+
capabilities: createAdapterCapabilities({
|
|
15
|
+
usageSettlement: true,
|
|
16
|
+
diffArtifacts: true
|
|
17
|
+
})
|
|
18
|
+
},
|
|
19
|
+
async execute(request) {
|
|
20
|
+
const verification = await runVerification(request.context.verificationPlan, workingDirectory, verifyTimeoutMs, request.context.verificationStack);
|
|
21
|
+
const execution = await readGitExecutionArtifacts(workingDirectory, 5_000);
|
|
22
|
+
const changedFiles = execution.changedFiles ?? [];
|
|
23
|
+
if (verification.passed) {
|
|
24
|
+
return {
|
|
25
|
+
status: "completed",
|
|
26
|
+
summary: changedFiles.length > 0
|
|
27
|
+
? `Verifier-only run completed but modified files: ${changedFiles.join(", ")}`
|
|
28
|
+
: "Verifier-only run completed without file edits.",
|
|
29
|
+
usage: normalizeUsage({
|
|
30
|
+
actualUsd: 0,
|
|
31
|
+
tokensIn: 0,
|
|
32
|
+
tokensOut: 0,
|
|
33
|
+
provenance: "actual"
|
|
34
|
+
}),
|
|
35
|
+
verification,
|
|
36
|
+
execution
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
status: "failed",
|
|
41
|
+
summary: "Verifier-only run failed.",
|
|
42
|
+
usage: normalizeUsage({
|
|
43
|
+
actualUsd: 0,
|
|
44
|
+
tokensIn: 0,
|
|
45
|
+
tokensOut: 0,
|
|
46
|
+
provenance: "actual"
|
|
47
|
+
}),
|
|
48
|
+
verification,
|
|
49
|
+
execution,
|
|
50
|
+
failure: {
|
|
51
|
+
message: verification.summary
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=verifier-only.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Writable } from "node:stream";
|
|
2
|
+
export interface CliProcessResult {
|
|
3
|
+
exitCode: number;
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
}
|
|
7
|
+
export interface CliExitRuntime {
|
|
8
|
+
stdout: Writable;
|
|
9
|
+
stderr: Writable;
|
|
10
|
+
exit: (code: number) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare function writeCliResultAndExit(result: CliProcessResult, runtime?: CliExitRuntime): Promise<void>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export async function writeCliResultAndExit(result, runtime = {
|
|
2
|
+
stdout: process.stdout,
|
|
3
|
+
stderr: process.stderr,
|
|
4
|
+
exit: (code) => {
|
|
5
|
+
process.exit(code);
|
|
6
|
+
}
|
|
7
|
+
}) {
|
|
8
|
+
await Promise.all([
|
|
9
|
+
writeStream(runtime.stdout, result.stdout),
|
|
10
|
+
writeStream(runtime.stderr, result.stderr)
|
|
11
|
+
]);
|
|
12
|
+
runtime.exit(result.exitCode);
|
|
13
|
+
}
|
|
14
|
+
function writeStream(stream, value) {
|
|
15
|
+
if (!value) {
|
|
16
|
+
return Promise.resolve();
|
|
17
|
+
}
|
|
18
|
+
return new Promise((resolve, reject) => {
|
|
19
|
+
stream.write(`${value}\n`, (error) => {
|
|
20
|
+
if (error) {
|
|
21
|
+
reject(error);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
resolve();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=exit.js.map
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { analyzeRuns, buildReport, computeStats, createRlmClient, readRuns, renderMarkdown, writeReport } from "@martin/trace-intelligence";
|
|
3
|
+
export async function handleAnalyzeCommand(args) {
|
|
4
|
+
const maxRuns = parseIntFlag(args, "--runs") ?? 20;
|
|
5
|
+
const since = parseFlag(args, "--since");
|
|
6
|
+
const output = parseFlag(args, "--output");
|
|
7
|
+
const runsRoot = parseFlag(args, "--runs-root");
|
|
8
|
+
const noRlm = args.includes("--no-rlm");
|
|
9
|
+
const jsonOut = args.includes("--json");
|
|
10
|
+
try {
|
|
11
|
+
const runs = await readRuns({ maxRuns, since, runsRoot });
|
|
12
|
+
if (runs.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
exitCode: 0,
|
|
15
|
+
stdout: "",
|
|
16
|
+
stderr: "No runs found. Run martin-loop at least once to generate trace data."
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const rlmClient = noRlm ? undefined : createRlmClient();
|
|
20
|
+
const analysis = await analyzeRuns(runs, noRlm ? { deterministicOnly: true } : { client: rlmClient });
|
|
21
|
+
const stats = computeStats(runs);
|
|
22
|
+
const report = buildReport(runs, analysis, stats);
|
|
23
|
+
if (jsonOut) {
|
|
24
|
+
return {
|
|
25
|
+
exitCode: 0,
|
|
26
|
+
stdout: JSON.stringify(report, null, 2),
|
|
27
|
+
stderr: ""
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const jsonPath = output ? join(output) : "trace-report.json";
|
|
31
|
+
const mdPath = jsonPath.replace(/\.json$/, ".md");
|
|
32
|
+
const { jsonPath: writtenJson, mdPath: writtenMd } = await writeReport(report, {
|
|
33
|
+
jsonOutput: jsonPath,
|
|
34
|
+
mdOutput: mdPath
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
exitCode: 0,
|
|
38
|
+
stdout: renderMarkdown(report),
|
|
39
|
+
stderr: `\nReports written:\n ${writtenJson}\n ${writtenMd}\n`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44
|
+
return { exitCode: 1, stdout: "", stderr: `Error: ${message}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function parseFlag(args, flag) {
|
|
48
|
+
const idx = args.indexOf(flag);
|
|
49
|
+
return idx >= 0 ? args[idx + 1] : undefined;
|
|
50
|
+
}
|
|
51
|
+
function parseIntFlag(args, flag) {
|
|
52
|
+
const val = parseFlag(args, flag);
|
|
53
|
+
if (val === undefined)
|
|
54
|
+
return undefined;
|
|
55
|
+
const n = parseInt(val, 10);
|
|
56
|
+
return isNaN(n) ? undefined : n;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=analyze.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-log-verify.ts
|
|
3
|
+
*
|
|
4
|
+
* Implements `martin audit log-verify [--from ISO] [--to ISO]`.
|
|
5
|
+
*
|
|
6
|
+
* Walks every run directory in the Martin run store, verifies the Ed25519
|
|
7
|
+
* signature on each loop-record.json, and reports gaps, tamper evidence,
|
|
8
|
+
* and an overall PASS/FAIL verdict.
|
|
9
|
+
*
|
|
10
|
+
* Ed25519 verification is performed via verifyLoopRecordAttestation() from
|
|
11
|
+
* @martin/core — the same signing infrastructure used during run settlement.
|
|
12
|
+
*/
|
|
13
|
+
export interface AuditLogVerifyOptions {
|
|
14
|
+
/** ISO 8601 date string — only verify runs created on or after this date */
|
|
15
|
+
from?: string;
|
|
16
|
+
/** ISO 8601 date string — only verify runs created on or before this date */
|
|
17
|
+
to?: string;
|
|
18
|
+
/** Override the default runs root directory */
|
|
19
|
+
runsRoot?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface AuditLogVerifyResult {
|
|
22
|
+
runsRoot: string;
|
|
23
|
+
rangeFrom: string;
|
|
24
|
+
rangeTo: string;
|
|
25
|
+
eventsChecked: number;
|
|
26
|
+
signaturesFailed: number;
|
|
27
|
+
gaps: string[];
|
|
28
|
+
failures: Array<{
|
|
29
|
+
runId: string;
|
|
30
|
+
reason: string;
|
|
31
|
+
}>;
|
|
32
|
+
verdict: "PASS" | "FAIL";
|
|
33
|
+
}
|
|
34
|
+
export declare function runAuditLogVerify(options?: AuditLogVerifyOptions): Promise<AuditLogVerifyResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* audit-log-verify.ts
|
|
3
|
+
*
|
|
4
|
+
* Implements `martin audit log-verify [--from ISO] [--to ISO]`.
|
|
5
|
+
*
|
|
6
|
+
* Walks every run directory in the Martin run store, verifies the Ed25519
|
|
7
|
+
* signature on each loop-record.json, and reports gaps, tamper evidence,
|
|
8
|
+
* and an overall PASS/FAIL verdict.
|
|
9
|
+
*
|
|
10
|
+
* Ed25519 verification is performed via verifyLoopRecordAttestation() from
|
|
11
|
+
* @martin/core — the same signing infrastructure used during run settlement.
|
|
12
|
+
*/
|
|
13
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
14
|
+
import { join } from "node:path";
|
|
15
|
+
import { resolveRunsRoot, verifyLoopRecordAttestation } from "../../core/index.js";
|
|
16
|
+
export async function runAuditLogVerify(options = {}) {
|
|
17
|
+
const runsRoot = options.runsRoot ?? resolveRunsRoot(process.env);
|
|
18
|
+
const fromMs = options.from ? new Date(options.from).getTime() : 0;
|
|
19
|
+
const toMs = options.to ? new Date(options.to).getTime() : Date.now();
|
|
20
|
+
const rangeFrom = new Date(fromMs).toISOString();
|
|
21
|
+
const rangeTo = new Date(toMs).toISOString();
|
|
22
|
+
// Enumerate run directories — each is a directory named by loopId
|
|
23
|
+
let entries;
|
|
24
|
+
try {
|
|
25
|
+
entries = await readdir(runsRoot);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
// Run store doesn't exist yet — zero runs is a valid "PASS" state
|
|
29
|
+
return {
|
|
30
|
+
runsRoot,
|
|
31
|
+
rangeFrom,
|
|
32
|
+
rangeTo,
|
|
33
|
+
eventsChecked: 0,
|
|
34
|
+
signaturesFailed: 0,
|
|
35
|
+
gaps: [],
|
|
36
|
+
failures: [],
|
|
37
|
+
verdict: "PASS",
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
const runDirs = [];
|
|
41
|
+
for (const entry of entries) {
|
|
42
|
+
const full = join(runsRoot, entry);
|
|
43
|
+
try {
|
|
44
|
+
const info = await stat(full);
|
|
45
|
+
if (info.isDirectory()) {
|
|
46
|
+
runDirs.push(full);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// skip unreadable entries
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// Filter by date range using loop-record.json createdAt field
|
|
54
|
+
const inRangeDirs = [];
|
|
55
|
+
const gaps = [];
|
|
56
|
+
for (const runDir of runDirs) {
|
|
57
|
+
const recordPath = join(runDir, "loop-record.json");
|
|
58
|
+
try {
|
|
59
|
+
const raw = await readFile(recordPath, "utf8");
|
|
60
|
+
const record = JSON.parse(raw);
|
|
61
|
+
const createdMs = record.createdAt ? new Date(record.createdAt).getTime() : NaN;
|
|
62
|
+
if (isNaN(createdMs)) {
|
|
63
|
+
gaps.push(`${runDir}: missing or unparseable createdAt`);
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
if (createdMs >= fromMs && createdMs <= toMs) {
|
|
67
|
+
inRangeDirs.push(runDir);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
gaps.push(`${runDir}: loop-record.json unreadable — possible gap in audit trail`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Verify each in-range run
|
|
75
|
+
const failures = [];
|
|
76
|
+
for (const runDir of inRangeDirs) {
|
|
77
|
+
const result = await verifyLoopRecordAttestation(runDir);
|
|
78
|
+
if (!result.ok) {
|
|
79
|
+
failures.push({
|
|
80
|
+
runId: runDir.split(/[\\/]/).pop() ?? runDir,
|
|
81
|
+
reason: result.reason ?? "signature verification failed",
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const eventsChecked = inRangeDirs.length;
|
|
86
|
+
const signaturesFailed = failures.length;
|
|
87
|
+
const verdict = signaturesFailed === 0 && gaps.length === 0 ? "PASS" : "FAIL";
|
|
88
|
+
return {
|
|
89
|
+
runsRoot,
|
|
90
|
+
rangeFrom,
|
|
91
|
+
rangeTo,
|
|
92
|
+
eventsChecked,
|
|
93
|
+
signaturesFailed,
|
|
94
|
+
gaps,
|
|
95
|
+
failures,
|
|
96
|
+
verdict,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=audit-log-verify.js.map
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { generateAuditPackFromRunDirectory, verifyAuditPack } from "@martin/contracts/audit";
|
|
4
|
+
import { resolveRunsRoot } from "../../core/index.js";
|
|
5
|
+
export async function handleAuditCommand(args, options = {}) {
|
|
6
|
+
const subcommand = args[0];
|
|
7
|
+
try {
|
|
8
|
+
switch (subcommand) {
|
|
9
|
+
case "generate": {
|
|
10
|
+
const runId = parseFlag(args, "--run-id");
|
|
11
|
+
const output = parseFlag(args, "--output") ?? join(options.cwd ?? process.cwd(), "audit-pack");
|
|
12
|
+
if (!runId) {
|
|
13
|
+
return { stdout: "", stderr: "Missing --run-id", exitCode: 1 };
|
|
14
|
+
}
|
|
15
|
+
const result = await generateAuditPackFromRunDirectory({
|
|
16
|
+
runId,
|
|
17
|
+
runDir: join(resolveRunsRoot(process.env), runId),
|
|
18
|
+
outputDir: output,
|
|
19
|
+
generatedBy: "martin"
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
stdout: JSON.stringify({
|
|
23
|
+
command: "audit.generate",
|
|
24
|
+
runId,
|
|
25
|
+
packPath: result.packPath,
|
|
26
|
+
manifest: result.manifest
|
|
27
|
+
}, null, 2),
|
|
28
|
+
stderr: "",
|
|
29
|
+
exitCode: 0
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
case "verify": {
|
|
33
|
+
const pack = parseFlag(args, "--pack");
|
|
34
|
+
if (!pack) {
|
|
35
|
+
return { stdout: "", stderr: "Missing --pack", exitCode: 1 };
|
|
36
|
+
}
|
|
37
|
+
const result = await verifyAuditPack(pack);
|
|
38
|
+
return formatVerificationResult(result);
|
|
39
|
+
}
|
|
40
|
+
case "show": {
|
|
41
|
+
const runId = parseFlag(args, "--run-id");
|
|
42
|
+
if (!runId) {
|
|
43
|
+
return { stdout: "", stderr: "Missing --run-id", exitCode: 1 };
|
|
44
|
+
}
|
|
45
|
+
const runsRoot = parseFlag(args, "--runs-root") ?? resolveRunsRoot(process.env);
|
|
46
|
+
const summary = await summarizeRunAudit(join(runsRoot, runId), runId);
|
|
47
|
+
return {
|
|
48
|
+
stdout: JSON.stringify({
|
|
49
|
+
command: "audit.show",
|
|
50
|
+
...summary
|
|
51
|
+
}, null, 2),
|
|
52
|
+
stderr: "",
|
|
53
|
+
exitCode: 0
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
case "log-verify": {
|
|
57
|
+
const { runAuditLogVerify } = await import("./audit-log-verify.js");
|
|
58
|
+
const from = parseFlag(args, "--from");
|
|
59
|
+
const to = parseFlag(args, "--to");
|
|
60
|
+
const runsRoot = parseFlag(args, "--runs-root");
|
|
61
|
+
const result = await runAuditLogVerify({ from, to, runsRoot });
|
|
62
|
+
const output = JSON.stringify({
|
|
63
|
+
command: "audit.log-verify",
|
|
64
|
+
...result,
|
|
65
|
+
}, null, 2);
|
|
66
|
+
if (result.verdict === "PASS") {
|
|
67
|
+
return { stdout: output, stderr: "", exitCode: 0 };
|
|
68
|
+
}
|
|
69
|
+
return { stdout: "", stderr: output, exitCode: 1 };
|
|
70
|
+
}
|
|
71
|
+
default:
|
|
72
|
+
return {
|
|
73
|
+
stdout: "",
|
|
74
|
+
stderr: [
|
|
75
|
+
"Usage: martin audit <subcommand> [options]",
|
|
76
|
+
"",
|
|
77
|
+
"Subcommands:",
|
|
78
|
+
" generate --run-id <id> --output <dir> Generate audit pack",
|
|
79
|
+
" verify --pack <dir> Verify audit pack manifest",
|
|
80
|
+
" show --run-id <id> [--runs-root <dir>] Summarize persisted run audit evidence",
|
|
81
|
+
" log-verify [--from ISO] [--to ISO] Verify Ed25519 signatures across run log"
|
|
82
|
+
].join("\n"),
|
|
83
|
+
exitCode: 1
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
89
|
+
return { stdout: "", stderr: message, exitCode: 1 };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function summarizeRunAudit(runDir, runId) {
|
|
93
|
+
const [contract, state, loop, ledger, attemptDirs] = await Promise.all([
|
|
94
|
+
readJsonIfExists(join(runDir, "contract.json")),
|
|
95
|
+
readJsonIfExists(join(runDir, "state.json")),
|
|
96
|
+
readJsonIfExists(join(runDir, "loop-record.json")),
|
|
97
|
+
readLedgerIfExists(join(runDir, "ledger.jsonl")),
|
|
98
|
+
readAttemptDirs(join(runDir, "artifacts"))
|
|
99
|
+
]);
|
|
100
|
+
const eventKinds = ledger.reduce((counts, event) => {
|
|
101
|
+
const kind = typeof event.kind === "string" ? event.kind : "unknown";
|
|
102
|
+
counts[kind] = (counts[kind] ?? 0) + 1;
|
|
103
|
+
return counts;
|
|
104
|
+
}, {});
|
|
105
|
+
const loopRecord = isRecord(loop) ? loop : undefined;
|
|
106
|
+
const contractRecord = isRecord(contract) ? contract : undefined;
|
|
107
|
+
const stateRecord = isRecord(state) ? state : undefined;
|
|
108
|
+
const task = isRecord(loopRecord?.task)
|
|
109
|
+
? loopRecord.task
|
|
110
|
+
: isRecord(contractRecord?.task)
|
|
111
|
+
? contractRecord.task
|
|
112
|
+
: undefined;
|
|
113
|
+
return {
|
|
114
|
+
runId,
|
|
115
|
+
runDir,
|
|
116
|
+
status: asString(loopRecord?.status) ?? "unknown",
|
|
117
|
+
lifecycleState: asString(loopRecord?.lifecycleState) ?? asString(stateRecord?.phase) ?? null,
|
|
118
|
+
taskTitle: asString(task?.title) ?? null,
|
|
119
|
+
attempts: Array.isArray(loopRecord?.attempts) ? loopRecord.attempts.length : attemptDirs.length,
|
|
120
|
+
ledgerEvents: ledger.length,
|
|
121
|
+
eventKinds,
|
|
122
|
+
cost: isRecord(loopRecord?.cost) ? loopRecord.cost : null,
|
|
123
|
+
artifacts: {
|
|
124
|
+
hasContract: contract !== null,
|
|
125
|
+
hasState: state !== null,
|
|
126
|
+
hasLoopRecord: loop !== null,
|
|
127
|
+
attemptDirs
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function readJsonIfExists(path) {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(await readFile(path, "utf8"));
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function readLedgerIfExists(path) {
|
|
140
|
+
try {
|
|
141
|
+
const contents = await readFile(path, "utf8");
|
|
142
|
+
return contents
|
|
143
|
+
.trim()
|
|
144
|
+
.split(/\r?\n/u)
|
|
145
|
+
.filter(Boolean)
|
|
146
|
+
.map((line) => JSON.parse(line));
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
async function readAttemptDirs(path) {
|
|
153
|
+
try {
|
|
154
|
+
const entries = await readdir(path, { withFileTypes: true });
|
|
155
|
+
return entries
|
|
156
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith("attempt-"))
|
|
157
|
+
.map((entry) => entry.name)
|
|
158
|
+
.sort();
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function isRecord(value) {
|
|
165
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
166
|
+
}
|
|
167
|
+
function asString(value) {
|
|
168
|
+
return typeof value === "string" ? value : undefined;
|
|
169
|
+
}
|
|
170
|
+
function formatVerificationResult(result) {
|
|
171
|
+
if (result.ok) {
|
|
172
|
+
return {
|
|
173
|
+
stdout: JSON.stringify({
|
|
174
|
+
command: "audit.verify",
|
|
175
|
+
status: "passed",
|
|
176
|
+
manifest: result.manifest,
|
|
177
|
+
checkedFiles: result.checkedFiles.length
|
|
178
|
+
}, null, 2),
|
|
179
|
+
stderr: "",
|
|
180
|
+
exitCode: 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
stdout: "",
|
|
185
|
+
stderr: JSON.stringify({
|
|
186
|
+
command: "audit.verify",
|
|
187
|
+
status: "failed",
|
|
188
|
+
failures: result.failures
|
|
189
|
+
}, null, 2),
|
|
190
|
+
exitCode: 1
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
function parseFlag(args, flag) {
|
|
194
|
+
const idx = args.indexOf(flag);
|
|
195
|
+
if (idx === -1 || idx >= args.length - 1)
|
|
196
|
+
return undefined;
|
|
197
|
+
return args[idx + 1];
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { computeCorpusStats, loadCorpus, resolveEntry, resolveCorpusPath, saveCorpus } from "@martin/trace-intelligence";
|
|
2
|
+
export async function handleCorpusCommand(args) {
|
|
3
|
+
const subcommand = args[0];
|
|
4
|
+
const corpusPath = resolveCorpusPath();
|
|
5
|
+
try {
|
|
6
|
+
const corpus = await loadCorpus(corpusPath);
|
|
7
|
+
switch (subcommand) {
|
|
8
|
+
case "show":
|
|
9
|
+
case undefined: {
|
|
10
|
+
const stats = computeCorpusStats(corpus);
|
|
11
|
+
const lines = [
|
|
12
|
+
`Corpus: ${corpusPath}`,
|
|
13
|
+
`Entries: ${stats.totalEntries} (${stats.resolvedEntries} resolved, ${stats.activeEntries} open)`,
|
|
14
|
+
""
|
|
15
|
+
];
|
|
16
|
+
if (corpus.entries.length === 0) {
|
|
17
|
+
lines.push("No patterns in corpus yet. Run martin analyze to populate.");
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
for (const entry of corpus.entries) {
|
|
21
|
+
const resolved = entry.resolved ? "[resolved]" : "[open] ";
|
|
22
|
+
lines.push(`${resolved} ${entry.fingerprint.slice(0, 12)} ${entry.kind} (${entry.severity}) — seen ${entry.occurrenceCount}x, ${entry.totalAffectedRuns} runs`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { exitCode: 0, stdout: lines.join("\n"), stderr: "" };
|
|
26
|
+
}
|
|
27
|
+
case "clear": {
|
|
28
|
+
const cleared = { ...corpus, entries: [], updatedAt: new Date().toISOString() };
|
|
29
|
+
await saveCorpus(cleared, corpusPath);
|
|
30
|
+
return { exitCode: 0, stdout: `Corpus cleared (${corpus.entries.length} entries removed).`, stderr: "" };
|
|
31
|
+
}
|
|
32
|
+
case "resolve": {
|
|
33
|
+
const fingerprint = args[1];
|
|
34
|
+
if (!fingerprint) {
|
|
35
|
+
return { exitCode: 1, stdout: "", stderr: "Usage: martin corpus resolve <fingerprint>" };
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const updated = resolveEntry(corpus, fingerprint);
|
|
39
|
+
await saveCorpus(updated, corpusPath);
|
|
40
|
+
return { exitCode: 0, stdout: `Entry ${fingerprint} marked as resolved.`, stderr: "" };
|
|
41
|
+
}
|
|
42
|
+
catch (resolveErr) {
|
|
43
|
+
const msg = resolveErr instanceof Error ? resolveErr.message : String(resolveErr);
|
|
44
|
+
return { exitCode: 1, stdout: "", stderr: msg };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
default:
|
|
48
|
+
return {
|
|
49
|
+
exitCode: 1,
|
|
50
|
+
stdout: "",
|
|
51
|
+
stderr: `Unknown corpus subcommand: ${subcommand}\nUsage: martin corpus [show|clear|resolve <fingerprint>]`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
57
|
+
return { exitCode: 1, stdout: "", stderr: `Error: ${message}` };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=corpus.js.map
|