assertledger 1.0.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/CONTRIBUTING.md +31 -0
- package/LICENSE +21 -0
- package/README.fr.md +236 -0
- package/README.md +224 -0
- package/SECURITY.md +51 -0
- package/benchmarks/agentic-profile/README.md +15 -0
- package/benchmarks/agentic-profile/public/README.md +5 -0
- package/benchmarks/self-hosted-core/README.md +113 -0
- package/benchmarks/self-hosted-core/adapter.mjs +293 -0
- package/benchmarks/self-hosted-core/builder.ts +193 -0
- package/benchmarks/self-hosted-core/campaign.ts +233 -0
- package/benchmarks/self-hosted-core/existing-tests-builder.ts +217 -0
- package/benchmarks/self-hosted-core/existing-tests.ts +146 -0
- package/benchmarks/self-hosted-core/liveness.test.mjs +8 -0
- package/conformance/v1/bundle.json +104 -0
- package/conformance/v1/expected/canonical-order-a.json +4 -0
- package/conformance/v1/expected/canonical-order-b.json +4 -0
- package/conformance/v1/expected/create-benchmark-v1-measured.json +575 -0
- package/conformance/v1/expected/create-profile-v1-qualified.json +280 -0
- package/conformance/v1/expected/decide-collection-failure-non-kill.json +192 -0
- package/conformance/v1/expected/decide-compile-failure-non-kill.json +192 -0
- package/conformance/v1/expected/decide-infra-error-non-kill.json +192 -0
- package/conformance/v1/expected/decide-no-test-discovered-non-kill.json +192 -0
- package/conformance/v1/expected/decide-process-crash-non-kill.json +192 -0
- package/conformance/v1/expected/decide-timeout-non-kill.json +192 -0
- package/conformance/v1/expected/decide-verified.json +192 -0
- package/conformance/v1/expected/replay-benchmark-v1-resealed-summary-forgery.json +12 -0
- package/conformance/v1/expected/replay-evidence-raw-tamper.json +6 -0
- package/conformance/v1/expected/replay-evidence-resealed-semantic-forgery.json +6 -0
- package/conformance/v1/inputs/canonical-order-a.json +8 -0
- package/conformance/v1/inputs/canonical-order-b.json +8 -0
- package/conformance/v1/inputs/create-benchmark-v1-measured.json +459 -0
- package/conformance/v1/inputs/create-profile-v1-qualified.json +228 -0
- package/conformance/v1/inputs/decide-collection-failure-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-compile-failure-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-infra-error-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-no-test-discovered-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-process-crash-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-timeout-non-kill.json +143 -0
- package/conformance/v1/inputs/decide-verified.json +143 -0
- package/conformance/v1/inputs/replay-benchmark-v1-resealed-summary-forgery.json +575 -0
- package/conformance/v1/inputs/replay-evidence-raw-tamper.json +201 -0
- package/conformance/v1/inputs/replay-evidence-resealed-semantic-forgery.json +192 -0
- package/conformance/v1/schemas/expected-digests.json +175 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +951 -0
- package/dist/cli.js.map +1 -0
- package/dist/contracts/diagnostics.d.ts +18 -0
- package/dist/contracts/diagnostics.d.ts.map +1 -0
- package/dist/contracts/diagnostics.js +13 -0
- package/dist/contracts/diagnostics.js.map +1 -0
- package/dist/contracts/index.d.ts +3908 -0
- package/dist/contracts/index.d.ts.map +1 -0
- package/dist/contracts/index.js +2569 -0
- package/dist/contracts/index.js.map +1 -0
- package/dist/contracts/runtime-doctor.d.ts +107 -0
- package/dist/contracts/runtime-doctor.d.ts.map +1 -0
- package/dist/contracts/runtime-doctor.js +91 -0
- package/dist/contracts/runtime-doctor.js.map +1 -0
- package/dist/core/index.d.ts +200 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +2587 -0
- package/dist/core/index.js.map +1 -0
- package/dist/diagnostics.d.ts +7 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +252 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/engine/adapters/node-test-profile.d.ts +14 -0
- package/dist/engine/adapters/node-test-profile.d.ts.map +1 -0
- package/dist/engine/adapters/node-test-profile.js +14 -0
- package/dist/engine/adapters/node-test-profile.js.map +1 -0
- package/dist/engine/adapters/node-test-runtime.d.ts +39 -0
- package/dist/engine/adapters/node-test-runtime.d.ts.map +1 -0
- package/dist/engine/adapters/node-test-runtime.js +173 -0
- package/dist/engine/adapters/node-test-runtime.js.map +1 -0
- package/dist/engine/adapters/runtime-facts.d.ts +26 -0
- package/dist/engine/adapters/runtime-facts.d.ts.map +1 -0
- package/dist/engine/adapters/runtime-facts.js +73 -0
- package/dist/engine/adapters/runtime-facts.js.map +1 -0
- package/dist/engine/connection.d.ts +22 -0
- package/dist/engine/connection.d.ts.map +1 -0
- package/dist/engine/connection.js +343 -0
- package/dist/engine/connection.js.map +1 -0
- package/dist/engine/git-regression.d.ts +25 -0
- package/dist/engine/git-regression.d.ts.map +1 -0
- package/dist/engine/git-regression.js +803 -0
- package/dist/engine/git-regression.js.map +1 -0
- package/dist/engine/index.d.ts +55 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +2782 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/node-test-reporter.d.ts +2 -0
- package/dist/engine/node-test-reporter.d.ts.map +1 -0
- package/dist/engine/node-test-reporter.js +70 -0
- package/dist/engine/node-test-reporter.js.map +1 -0
- package/dist/engine/runtime-doctor.d.ts +16 -0
- package/dist/engine/runtime-doctor.d.ts.map +1 -0
- package/dist/engine/runtime-doctor.js +100 -0
- package/dist/engine/runtime-doctor.js.map +1 -0
- package/dist/evaluation/agentic-corpus.d.ts +161 -0
- package/dist/evaluation/agentic-corpus.d.ts.map +1 -0
- package/dist/evaluation/agentic-corpus.js +710 -0
- package/dist/evaluation/agentic-corpus.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +13 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +391 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/stdio.d.ts +3 -0
- package/dist/mcp/stdio.d.ts.map +1 -0
- package/dist/mcp/stdio.js +13 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/sdk/index.d.ts +52 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +224 -0
- package/dist/sdk/index.js.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/docs/adapter-protocol.md +196 -0
- package/docs/agentic-benchmark.md +118 -0
- package/docs/agentic-corpus-experiment-h3.md +89 -0
- package/docs/agentic-corpus-plan.md +105 -0
- package/docs/agentic-corpus-provenance.md +59 -0
- package/docs/agentic-test-profile-pilot.md +57 -0
- package/docs/agentic-test-profile-v2.md +116 -0
- package/docs/agentic-test-profile.md +274 -0
- package/docs/architecture.md +157 -0
- package/docs/ci.md +37 -0
- package/docs/client-connections.md +61 -0
- package/docs/conformance-v1.md +72 -0
- package/docs/decisions/0001-typescript-runtime.md +24 -0
- package/docs/developer-experience.md +55 -0
- package/docs/diagnostics.md +35 -0
- package/docs/distribution.md +40 -0
- package/docs/git-regression.md +39 -0
- package/docs/migration-repository-validation-order.md +35 -0
- package/docs/migration-testforge-to-assertledger.md +64 -0
- package/docs/project-intent.md +173 -0
- package/docs/proof-model.md +116 -0
- package/docs/reference.md +336 -0
- package/docs/release-1.0.md +63 -0
- package/docs/repository-audit.md +52 -0
- package/docs/repository-init.md +60 -0
- package/docs/research-basis.md +27 -0
- package/docs/roadmap.md +74 -0
- package/docs/runtime-doctor.md +65 -0
- package/docs/testexplora-calibration.md +71 -0
- package/examples/agentic-benchmark/benchmark-request.mjs +19 -0
- package/examples/agentic-benchmark/structured-phase-adapter-fixture.mjs +35 -0
- package/examples/agentic-profile/profile-benchmark.mjs +34 -0
- package/examples/agentic-profile/profile-manifest.mjs +28 -0
- package/examples/git-history/README.md +44 -0
- package/examples/git-history/create-demo.mjs +128 -0
- package/examples/git-history/escape-string-regexp/LICENSE +9 -0
- package/examples/git-history/escape-string-regexp/before.cjs.txt +11 -0
- package/examples/git-history/escape-string-regexp/fixed.cjs.txt +13 -0
- package/examples/git-history/escape-string-regexp/provenance.json +28 -0
- package/examples/node-test/repository/package.json +5 -0
- package/examples/node-test/repository/src/is-even.js +3 -0
- package/examples/node-test/repository/tests/base.test.js +6 -0
- package/examples/node-test/request.json +93 -0
- package/integrations/skill/SKILL.md +51 -0
- package/package.json +88 -0
- package/schemas/agentic-benchmark-acquisition-replay-result.v1.json +70 -0
- package/schemas/agentic-benchmark-acquisition-request.v1.json +564 -0
- package/schemas/agentic-benchmark-acquisition-result.v1.json +1409 -0
- package/schemas/agentic-benchmark-artifact.v1.json +1251 -0
- package/schemas/agentic-benchmark-replay-result.v1.json +84 -0
- package/schemas/agentic-benchmark-request.v1.json +1034 -0
- package/schemas/agentic-corpus-allocation-commitment-replay-result.v1.json +58 -0
- package/schemas/agentic-corpus-allocation-commitment.v1.json +141 -0
- package/schemas/agentic-corpus-allocation-replay-result.v1.json +34 -0
- package/schemas/agentic-corpus-allocation-request.v1.json +65 -0
- package/schemas/agentic-corpus-allocation-reveal.v1.json +66 -0
- package/schemas/agentic-corpus-allocation.v1.json +167 -0
- package/schemas/agentic-corpus-experiment-artifact.v1.json +329 -0
- package/schemas/agentic-corpus-experiment-plan-replay-result.v1.json +50 -0
- package/schemas/agentic-corpus-experiment-plan.v1.json +424 -0
- package/schemas/agentic-corpus-experiment-replay-request.v1.json +336 -0
- package/schemas/agentic-corpus-experiment-replay-result.v1.json +106 -0
- package/schemas/agentic-corpus-experiment-request.v1.json +204 -0
- package/schemas/agentic-corpus-provenance.v1.json +143 -0
- package/schemas/agentic-corpus-trust-policy.v1.json +133 -0
- package/schemas/agentic-profile-replay-result.v1.json +56 -0
- package/schemas/agentic-profile-replay-result.v2.json +63 -0
- package/schemas/agentic-profile-report.v1.json +961 -0
- package/schemas/agentic-profile-report.v2.json +1674 -0
- package/schemas/agentic-profile-request.v1.json +671 -0
- package/schemas/agentic-profile-request.v2.json +1338 -0
- package/schemas/evidence-manifest.v1.json +636 -0
- package/schemas/replay-result.v1.json +49 -0
- package/schemas/repository-analysis.v1.json +119 -0
- package/schemas/repository-audit.v1.json +811 -0
- package/schemas/repository-init-config.v1.json +183 -0
- package/schemas/repository-init-lock.v1.json +162 -0
- package/schemas/repository-init-result.v1.json +212 -0
- package/schemas/verification-request.v1.json +389 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { mkdtemp, open, realpath, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { normalizeRuntimeFacts, RUNTIME_FACTS_VERSION } from "./runtime-facts.js";
|
|
6
|
+
const PREFLIGHT_ERROR = "NODE_TEST_PROFILE_PREFLIGHT_FAILED";
|
|
7
|
+
const PREFLIGHT_REPORT_LIMIT_BYTES = 64 * 1024;
|
|
8
|
+
function validCount(value) {
|
|
9
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
10
|
+
}
|
|
11
|
+
function parseReporterDocument(value) {
|
|
12
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
13
|
+
return undefined;
|
|
14
|
+
const record = value;
|
|
15
|
+
const keys = [
|
|
16
|
+
"candidateFailureCount",
|
|
17
|
+
"candidateFailuresAllAssertions",
|
|
18
|
+
"candidateSyntaxFailureCount",
|
|
19
|
+
"candidateTestsDiscovered",
|
|
20
|
+
"nonCandidateFailureCount",
|
|
21
|
+
"protocolVersion",
|
|
22
|
+
"testsDiscovered",
|
|
23
|
+
];
|
|
24
|
+
if (Object.keys(record).sort().join("\0") !== keys.join("\0"))
|
|
25
|
+
return undefined;
|
|
26
|
+
if (record.protocolVersion !== "1.0.0" ||
|
|
27
|
+
!validCount(record.testsDiscovered) ||
|
|
28
|
+
!validCount(record.candidateTestsDiscovered) ||
|
|
29
|
+
record.candidateTestsDiscovered > record.testsDiscovered ||
|
|
30
|
+
!validCount(record.candidateFailureCount) ||
|
|
31
|
+
!validCount(record.nonCandidateFailureCount) ||
|
|
32
|
+
!validCount(record.candidateSyntaxFailureCount) ||
|
|
33
|
+
record.candidateSyntaxFailureCount > record.candidateFailureCount ||
|
|
34
|
+
typeof record.candidateFailuresAllAssertions !== "boolean") {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return record;
|
|
38
|
+
}
|
|
39
|
+
async function readBoundedReporterDocument(reportPath) {
|
|
40
|
+
const handle = await open(reportPath, "r");
|
|
41
|
+
try {
|
|
42
|
+
const buffer = Buffer.alloc(PREFLIGHT_REPORT_LIMIT_BYTES + 1);
|
|
43
|
+
let bytesRead = 0;
|
|
44
|
+
while (bytesRead < buffer.byteLength) {
|
|
45
|
+
const chunk = await handle.read(buffer, bytesRead, buffer.byteLength - bytesRead, null);
|
|
46
|
+
if (chunk.bytesRead === 0)
|
|
47
|
+
break;
|
|
48
|
+
bytesRead += chunk.bytesRead;
|
|
49
|
+
}
|
|
50
|
+
if (bytesRead > PREFLIGHT_REPORT_LIMIT_BYTES)
|
|
51
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
52
|
+
return JSON.parse(buffer.subarray(0, bytesRead).toString("utf8"));
|
|
53
|
+
}
|
|
54
|
+
finally {
|
|
55
|
+
await handle.close();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async function executeProbe(input, root, reporterPath, controlPath, name, body) {
|
|
59
|
+
const candidatePath = path.join(root, `${name}.test.mjs`);
|
|
60
|
+
const reportPath = path.join(root, `${name}.json`);
|
|
61
|
+
await writeFile(candidatePath, body, { flag: "wx" });
|
|
62
|
+
const processResult = await input.processRunner({
|
|
63
|
+
executable: input.executable,
|
|
64
|
+
args: [
|
|
65
|
+
"--test",
|
|
66
|
+
`--test-reporter=${pathToFileURL(reporterPath).href}`,
|
|
67
|
+
`--test-reporter-destination=${reportPath}`,
|
|
68
|
+
"--",
|
|
69
|
+
controlPath,
|
|
70
|
+
candidatePath,
|
|
71
|
+
],
|
|
72
|
+
cwd: root,
|
|
73
|
+
environment: {
|
|
74
|
+
...input.environment,
|
|
75
|
+
TESTFORGE_NODE_CANDIDATE_FILES: JSON.stringify([candidatePath]),
|
|
76
|
+
},
|
|
77
|
+
timeoutMs: Math.min(input.timeoutMs, 5_000),
|
|
78
|
+
maximumOutputBytes: Math.min(input.maximumOutputBytes, PREFLIGHT_REPORT_LIMIT_BYTES),
|
|
79
|
+
});
|
|
80
|
+
if (processResult.outcome !== "PROCESS_CRASH" ||
|
|
81
|
+
processResult.exitCode === null ||
|
|
82
|
+
processResult.exitCode === 0) {
|
|
83
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
84
|
+
}
|
|
85
|
+
const document = parseReporterDocument(await readBoundedReporterDocument(reportPath));
|
|
86
|
+
if (document === undefined)
|
|
87
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
88
|
+
const expectedAssertionFacts = name === "assertion";
|
|
89
|
+
if (document.testsDiscovered !== 2 ||
|
|
90
|
+
document.candidateTestsDiscovered !== 1 ||
|
|
91
|
+
document.candidateFailureCount !== 1 ||
|
|
92
|
+
document.nonCandidateFailureCount !== 0 ||
|
|
93
|
+
document.candidateSyntaxFailureCount !== 0 ||
|
|
94
|
+
document.candidateFailuresAllAssertions !== expectedAssertionFacts) {
|
|
95
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
96
|
+
}
|
|
97
|
+
const normalized = normalizeRuntimeFacts({
|
|
98
|
+
factsVersion: RUNTIME_FACTS_VERSION,
|
|
99
|
+
reportValid: true,
|
|
100
|
+
hasCandidate: true,
|
|
101
|
+
processExitedZero: false,
|
|
102
|
+
candidateTestsDiscovered: document.candidateTestsDiscovered,
|
|
103
|
+
nonCandidateFailureCount: document.nonCandidateFailureCount,
|
|
104
|
+
candidateCollectionFailureCount: 0,
|
|
105
|
+
candidateCompileFailureCount: document.candidateSyntaxFailureCount,
|
|
106
|
+
candidateFailureCount: document.candidateFailureCount,
|
|
107
|
+
candidateFailuresAllAssertions: document.candidateFailuresAllAssertions,
|
|
108
|
+
});
|
|
109
|
+
if (document.candidateTestsDiscovered !== 1 ||
|
|
110
|
+
(normalized.outcome !== "ASSERTION_FAILURE" && normalized.outcome !== "PROCESS_CRASH")) {
|
|
111
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
outcome: normalized.outcome,
|
|
115
|
+
attributed: normalized.attributed,
|
|
116
|
+
candidateTestsDiscovered: 1,
|
|
117
|
+
testsDiscovered: 2,
|
|
118
|
+
candidateFailureCount: 1,
|
|
119
|
+
nonCandidateFailureCount: 0,
|
|
120
|
+
candidateSyntaxFailureCount: 0,
|
|
121
|
+
candidateFailuresAllAssertions: document.candidateFailuresAllAssertions,
|
|
122
|
+
processExitedNonZero: true,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
export async function runNodeTestRuntimePreflight(input) {
|
|
126
|
+
let root;
|
|
127
|
+
let result;
|
|
128
|
+
let failure;
|
|
129
|
+
try {
|
|
130
|
+
root = await realpath(await mkdtemp(path.join(os.tmpdir(), "assertledger-node-test-preflight-")));
|
|
131
|
+
const reporterPath = path.join(root, "reporter.mjs");
|
|
132
|
+
const controlPath = path.join(root, "control.test.mjs");
|
|
133
|
+
await writeFile(reporterPath, input.reporterSource, { flag: "wx" });
|
|
134
|
+
await writeFile(controlPath, ['import test from "node:test";', 'test("control", () => {});', ""].join("\n"), { flag: "wx" });
|
|
135
|
+
const assertionProbe = await executeProbe(input, root, reporterPath, controlPath, "assertion", [
|
|
136
|
+
'import test from "node:test";',
|
|
137
|
+
'import assert from "node:assert/strict";',
|
|
138
|
+
'test("assertion", () => assert.equal(false, true));',
|
|
139
|
+
"",
|
|
140
|
+
].join("\n"));
|
|
141
|
+
const genericThrowProbe = await executeProbe(input, root, reporterPath, controlPath, "generic-throw", [
|
|
142
|
+
'import test from "node:test";',
|
|
143
|
+
'import assert from "node:assert/strict";',
|
|
144
|
+
'test("generic throw", () => {',
|
|
145
|
+
' throw new Error("preflight", { cause: new assert.AssertionError({ message: "nested" }) });',
|
|
146
|
+
"});",
|
|
147
|
+
"",
|
|
148
|
+
].join("\n"));
|
|
149
|
+
if (assertionProbe.outcome !== "ASSERTION_FAILURE" ||
|
|
150
|
+
assertionProbe.attributed !== true ||
|
|
151
|
+
genericThrowProbe.outcome !== "PROCESS_CRASH" ||
|
|
152
|
+
genericThrowProbe.attributed !== false) {
|
|
153
|
+
throw new Error(PREFLIGHT_ERROR);
|
|
154
|
+
}
|
|
155
|
+
result = { protocolVersion: "1.0.0", assertionProbe, genericThrowProbe };
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
failure = error;
|
|
159
|
+
}
|
|
160
|
+
if (root !== undefined) {
|
|
161
|
+
try {
|
|
162
|
+
await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
|
|
163
|
+
}
|
|
164
|
+
catch (error) {
|
|
165
|
+
failure ??= error;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
if (failure !== undefined || result === undefined) {
|
|
169
|
+
throw new Error(PREFLIGHT_ERROR, { cause: failure });
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=node-test-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-test-runtime.js","sourceRoot":"","sources":["../../../src/engine/adapters/node-test-runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const RUNTIME_FACTS_VERSION: "1.0.0";
|
|
2
|
+
export type RuntimeOutcome = "PASS" | "ASSERTION_FAILURE" | "COLLECTION_FAILURE" | "COMPILE_FAILURE" | "PROCESS_CRASH" | "INFRA_ERROR" | "NO_TEST_DISCOVERED";
|
|
3
|
+
export interface RuntimeFacts {
|
|
4
|
+
factsVersion: typeof RUNTIME_FACTS_VERSION;
|
|
5
|
+
/** False when the report is missing, malformed, or the process reported infrastructure failure. */
|
|
6
|
+
reportValid: boolean;
|
|
7
|
+
hasCandidate: boolean;
|
|
8
|
+
processExitedZero: boolean;
|
|
9
|
+
candidateTestsDiscovered: number;
|
|
10
|
+
nonCandidateFailureCount: number;
|
|
11
|
+
candidateCollectionFailureCount: number;
|
|
12
|
+
candidateCompileFailureCount: number;
|
|
13
|
+
candidateFailureCount: number;
|
|
14
|
+
candidateFailuresAllAssertions: boolean;
|
|
15
|
+
}
|
|
16
|
+
export interface NormalizedRuntimeOutcome {
|
|
17
|
+
outcome: RuntimeOutcome;
|
|
18
|
+
attributed: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Fail-closed priority: infrastructure/invalid report, collection, compilation,
|
|
22
|
+
* crash/non-assertion/non-candidate, pure candidate assertion, no test discovered, pass.
|
|
23
|
+
* TIMEOUT is engine-authoritative and is never produced here.
|
|
24
|
+
*/
|
|
25
|
+
export declare function normalizeRuntimeFacts(facts: RuntimeFacts): NormalizedRuntimeOutcome;
|
|
26
|
+
//# sourceMappingURL=runtime-facts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-facts.d.ts","sourceRoot":"","sources":["../../../src/engine/adapters/runtime-facts.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,qBAAqB,EAAG,OAAgB,CAAC;AAEtD,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,mBAAmB,GACnB,oBAAoB,GACpB,iBAAiB,GACjB,eAAe,GACf,aAAa,GACb,oBAAoB,CAAC;AAEzB,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,OAAO,qBAAqB,CAAC;IAC3C,mGAAmG;IACnG,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;IACtB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,wBAAwB,EAAE,MAAM,CAAC;IACjC,wBAAwB,EAAE,MAAM,CAAC;IACjC,+BAA+B,EAAE,MAAM,CAAC;IACxC,4BAA4B,EAAE,MAAM,CAAC;IACrC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,8BAA8B,EAAE,OAAO,CAAC;CACzC;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,cAAc,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,GAAG,wBAAwB,CAoEnF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Pure runtime-fact normalizer shared by every in-process official profile (node:test today;
|
|
2
|
+
// vitest/jest/bun/pytest are prepared for but not wired in this slice). No filesystem, process,
|
|
3
|
+
// network, clock, or candidate-selection logic belongs here.
|
|
4
|
+
export const RUNTIME_FACTS_VERSION = "1.0.0";
|
|
5
|
+
function nonNegativeSafeInteger(value) {
|
|
6
|
+
return Number.isSafeInteger(value) && value >= 0;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Fail-closed priority: infrastructure/invalid report, collection, compilation,
|
|
10
|
+
* crash/non-assertion/non-candidate, pure candidate assertion, no test discovered, pass.
|
|
11
|
+
* TIMEOUT is engine-authoritative and is never produced here.
|
|
12
|
+
*/
|
|
13
|
+
export function normalizeRuntimeFacts(facts) {
|
|
14
|
+
if (facts.factsVersion !== RUNTIME_FACTS_VERSION) {
|
|
15
|
+
throw new TypeError(`Unsupported runtime facts version: ${facts.factsVersion}`);
|
|
16
|
+
}
|
|
17
|
+
if (!facts.reportValid)
|
|
18
|
+
return { outcome: "INFRA_ERROR", attributed: false };
|
|
19
|
+
const counts = [
|
|
20
|
+
facts.candidateTestsDiscovered,
|
|
21
|
+
facts.nonCandidateFailureCount,
|
|
22
|
+
facts.candidateCollectionFailureCount,
|
|
23
|
+
facts.candidateCompileFailureCount,
|
|
24
|
+
facts.candidateFailureCount,
|
|
25
|
+
];
|
|
26
|
+
if (!counts.every(nonNegativeSafeInteger)) {
|
|
27
|
+
return { outcome: "INFRA_ERROR", attributed: false };
|
|
28
|
+
}
|
|
29
|
+
const candidateFailureFacts = facts.candidateCollectionFailureCount +
|
|
30
|
+
facts.candidateCompileFailureCount +
|
|
31
|
+
facts.candidateFailureCount;
|
|
32
|
+
if ((!facts.hasCandidate && (facts.candidateTestsDiscovered > 0 || candidateFailureFacts > 0)) ||
|
|
33
|
+
(facts.processExitedZero && (candidateFailureFacts > 0 || facts.nonCandidateFailureCount > 0))) {
|
|
34
|
+
return { outcome: "INFRA_ERROR", attributed: false };
|
|
35
|
+
}
|
|
36
|
+
if (!facts.hasCandidate) {
|
|
37
|
+
const controlClean = facts.processExitedZero && facts.nonCandidateFailureCount === 0;
|
|
38
|
+
return { outcome: controlClean ? "PASS" : "PROCESS_CRASH", attributed: false };
|
|
39
|
+
}
|
|
40
|
+
if (facts.candidateTestsDiscovered === 0) {
|
|
41
|
+
if (facts.candidateCollectionFailureCount > 0) {
|
|
42
|
+
return { outcome: "COLLECTION_FAILURE", attributed: false };
|
|
43
|
+
}
|
|
44
|
+
if (facts.candidateCompileFailureCount > 0) {
|
|
45
|
+
return { outcome: "COMPILE_FAILURE", attributed: false };
|
|
46
|
+
}
|
|
47
|
+
const crashLike = facts.candidateFailureCount > 0 ||
|
|
48
|
+
facts.nonCandidateFailureCount > 0 ||
|
|
49
|
+
!facts.processExitedZero;
|
|
50
|
+
return { outcome: crashLike ? "PROCESS_CRASH" : "NO_TEST_DISCOVERED", attributed: false };
|
|
51
|
+
}
|
|
52
|
+
if (facts.candidateCollectionFailureCount > 0) {
|
|
53
|
+
return { outcome: "COLLECTION_FAILURE", attributed: false };
|
|
54
|
+
}
|
|
55
|
+
if (facts.candidateCompileFailureCount > 0) {
|
|
56
|
+
return { outcome: "COMPILE_FAILURE", attributed: false };
|
|
57
|
+
}
|
|
58
|
+
if (facts.nonCandidateFailureCount > 0) {
|
|
59
|
+
return { outcome: "PROCESS_CRASH", attributed: false };
|
|
60
|
+
}
|
|
61
|
+
if (facts.candidateFailureCount > 0) {
|
|
62
|
+
const assertionOnly = facts.candidateFailuresAllAssertions;
|
|
63
|
+
return {
|
|
64
|
+
outcome: assertionOnly ? "ASSERTION_FAILURE" : "PROCESS_CRASH",
|
|
65
|
+
attributed: assertionOnly,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
outcome: facts.processExitedZero ? "PASS" : "PROCESS_CRASH",
|
|
70
|
+
attributed: facts.processExitedZero,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=runtime-facts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-facts.js","sourceRoot":"","sources":["../../../src/engine/adapters/runtime-facts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type CodexProjectConfigStatus = "EMITTED" | "CREATED" | "UNCHANGED" | "CONFLICT";
|
|
2
|
+
export interface CodexProjectConfigResult {
|
|
3
|
+
status: CodexProjectConfigStatus;
|
|
4
|
+
path: string;
|
|
5
|
+
content: string;
|
|
6
|
+
}
|
|
7
|
+
export type ConnectionClient = "codex" | "claude-code" | "mcp";
|
|
8
|
+
export type ClientConnectionStatus = "EMITTED" | "CREATED" | "UNCHANGED" | "REMOVED" | "ABSENT" | "CONFLICT";
|
|
9
|
+
export interface ClientConnectionArtifact {
|
|
10
|
+
kind: "configuration" | "skill" | "descriptor";
|
|
11
|
+
path: string | null;
|
|
12
|
+
content: string;
|
|
13
|
+
}
|
|
14
|
+
export interface ClientConnectionResult {
|
|
15
|
+
client: ConnectionClient;
|
|
16
|
+
status: ClientConnectionStatus;
|
|
17
|
+
artifacts: ClientConnectionArtifact[];
|
|
18
|
+
}
|
|
19
|
+
export declare function createCodexProjectConfig(requestedRoot: string, requestedCliEntry: string, write: boolean): Promise<CodexProjectConfigResult>;
|
|
20
|
+
export declare function connectClient(requestedRoot: string, requestedCliEntry: string, client: ConnectionClient, write: boolean): Promise<ClientConnectionResult>;
|
|
21
|
+
export declare function disconnectClient(requestedRoot: string, requestedCliEntry: string, client: Exclude<ConnectionClient, "mcp">, write: boolean): Promise<ClientConnectionResult>;
|
|
22
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/engine/connection.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,wBAAwB,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAExF,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,wBAAwB,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,aAAa,GAAG,KAAK,CAAC;AAC/D,MAAM,MAAM,sBAAsB,GAC9B,SAAS,GACT,SAAS,GACT,WAAW,GACX,SAAS,GACT,QAAQ,GACR,UAAU,CAAC;AAEf,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,eAAe,GAAG,OAAO,GAAG,YAAY,CAAC;IAC/C,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACvC;AAsDD,wBAAsB,wBAAwB,CAC5C,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,wBAAwB,CAAC,CA4BnC;AA2MD,wBAAsB,aAAa,CACjC,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,gBAAgB,EACxB,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,sBAAsB,CAAC,CAwCjC;AAED,wBAAsB,gBAAgB,CACpC,aAAa,EAAE,MAAM,EACrB,iBAAiB,EAAE,MAAM,EACzB,MAAM,EAAE,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,EACxC,KAAK,EAAE,OAAO,GACb,OAAO,CAAC,sBAAsB,CAAC,CAkCjC"}
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { lstat, mkdir, readFile, realpath, stat, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
const CONFIG_DIRECTORY = ".codex";
|
|
4
|
+
const CONFIG_FILE = "config.toml";
|
|
5
|
+
function tomlString(value) {
|
|
6
|
+
return JSON.stringify(value);
|
|
7
|
+
}
|
|
8
|
+
function codexConfig(nodeExecutable, cliEntry, root) {
|
|
9
|
+
const argumentsToml = [cliEntry, "mcp", "--root", root].map(tomlString).join(", ");
|
|
10
|
+
return [
|
|
11
|
+
"[mcp_servers.assertledger]",
|
|
12
|
+
`command = ${tomlString(nodeExecutable)}`,
|
|
13
|
+
`args = [${argumentsToml}]`,
|
|
14
|
+
`cwd = ${tomlString(root)}`,
|
|
15
|
+
"",
|
|
16
|
+
].join("\n");
|
|
17
|
+
}
|
|
18
|
+
async function existingConfigStatus(configPath, expectedContent) {
|
|
19
|
+
try {
|
|
20
|
+
const metadata = await lstat(configPath);
|
|
21
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
22
|
+
throw new Error("CONNECT_CONFIG_PATH_UNSAFE");
|
|
23
|
+
}
|
|
24
|
+
return (await readFile(configPath)).equals(Buffer.from(expectedContent, "utf8"))
|
|
25
|
+
? "UNCHANGED"
|
|
26
|
+
: "CONFLICT";
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (error.code === "ENOENT")
|
|
30
|
+
return "ABSENT";
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
async function ensureSafeConfigDirectory(root, directory) {
|
|
35
|
+
try {
|
|
36
|
+
const metadata = await lstat(directory);
|
|
37
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
|
|
38
|
+
throw new Error("CONNECT_CONFIG_PATH_UNSAFE");
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
if (error.code !== "ENOENT")
|
|
43
|
+
throw error;
|
|
44
|
+
await mkdir(directory);
|
|
45
|
+
}
|
|
46
|
+
const resolvedDirectory = await realpath(directory);
|
|
47
|
+
if (path.dirname(resolvedDirectory) !== root ||
|
|
48
|
+
path.basename(resolvedDirectory) !== CONFIG_DIRECTORY) {
|
|
49
|
+
throw new Error("CONNECT_CONFIG_PATH_UNSAFE");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export async function createCodexProjectConfig(requestedRoot, requestedCliEntry, write) {
|
|
53
|
+
const root = await realpath(requestedRoot);
|
|
54
|
+
if (!(await stat(root)).isDirectory())
|
|
55
|
+
throw new Error("REPOSITORY_ROOT_NOT_DIRECTORY");
|
|
56
|
+
const cliEntry = await realpath(requestedCliEntry);
|
|
57
|
+
if (!(await stat(cliEntry)).isFile() ||
|
|
58
|
+
path.basename(cliEntry) !== "cli.js" ||
|
|
59
|
+
path.basename(path.dirname(cliEntry)) !== "dist") {
|
|
60
|
+
throw new Error("CONNECT_BUILD_REQUIRED");
|
|
61
|
+
}
|
|
62
|
+
const content = codexConfig(process.execPath, cliEntry, root);
|
|
63
|
+
const configDirectory = path.join(root, CONFIG_DIRECTORY);
|
|
64
|
+
const configPath = path.join(configDirectory, CONFIG_FILE);
|
|
65
|
+
if (!write)
|
|
66
|
+
return { status: "EMITTED", path: configPath, content };
|
|
67
|
+
await ensureSafeConfigDirectory(root, configDirectory);
|
|
68
|
+
const existing = await existingConfigStatus(configPath, content);
|
|
69
|
+
if (existing !== "ABSENT")
|
|
70
|
+
return { status: existing, path: configPath, content };
|
|
71
|
+
try {
|
|
72
|
+
await writeFile(configPath, content, { encoding: "utf8", flag: "wx" });
|
|
73
|
+
return { status: "CREATED", path: configPath, content };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error.code !== "EEXIST")
|
|
77
|
+
throw error;
|
|
78
|
+
const raced = await existingConfigStatus(configPath, content);
|
|
79
|
+
return { status: raced === "UNCHANGED" ? "UNCHANGED" : "CONFLICT", path: configPath, content };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function mcpDescriptor(nodeExecutable, cliEntry, root) {
|
|
83
|
+
return `${JSON.stringify({
|
|
84
|
+
transport: "stdio",
|
|
85
|
+
command: nodeExecutable,
|
|
86
|
+
args: [cliEntry, "mcp", "--root", root],
|
|
87
|
+
cwd: root,
|
|
88
|
+
}, null, 2)}\n`;
|
|
89
|
+
}
|
|
90
|
+
function claudeCodeConfig(nodeExecutable, cliEntry, root) {
|
|
91
|
+
return `${JSON.stringify({
|
|
92
|
+
mcpServers: {
|
|
93
|
+
assertledger: {
|
|
94
|
+
type: "stdio",
|
|
95
|
+
command: nodeExecutable,
|
|
96
|
+
args: [cliEntry, "mcp", "--root", root],
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
}, null, 2)}\n`;
|
|
100
|
+
}
|
|
101
|
+
async function resolveBuiltEntry(requestedCliEntry) {
|
|
102
|
+
const cliEntry = await realpath(requestedCliEntry);
|
|
103
|
+
const metadata = await stat(cliEntry);
|
|
104
|
+
if (!metadata.isFile() ||
|
|
105
|
+
path.basename(cliEntry) !== "cli.js" ||
|
|
106
|
+
path.basename(path.dirname(cliEntry)) !== "dist") {
|
|
107
|
+
throw new Error("CONNECT_BUILD_REQUIRED");
|
|
108
|
+
}
|
|
109
|
+
return cliEntry;
|
|
110
|
+
}
|
|
111
|
+
async function resolveRepositoryRoot(requestedRoot) {
|
|
112
|
+
const root = await realpath(requestedRoot);
|
|
113
|
+
if (!(await stat(root)).isDirectory())
|
|
114
|
+
throw new Error("REPOSITORY_ROOT_NOT_DIRECTORY");
|
|
115
|
+
return root;
|
|
116
|
+
}
|
|
117
|
+
async function packagedSkill(cliEntry) {
|
|
118
|
+
const skillPath = path.join(path.dirname(path.dirname(cliEntry)), "integrations", "skill", "SKILL.md");
|
|
119
|
+
let metadata;
|
|
120
|
+
try {
|
|
121
|
+
metadata = await lstat(skillPath);
|
|
122
|
+
}
|
|
123
|
+
catch (error) {
|
|
124
|
+
if (error.code === "ENOENT") {
|
|
125
|
+
throw new Error("CONNECT_SKILL_REQUIRED");
|
|
126
|
+
}
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
if (!metadata.isFile() || metadata.isSymbolicLink()) {
|
|
130
|
+
throw new Error("CONNECT_SKILL_PATH_UNSAFE");
|
|
131
|
+
}
|
|
132
|
+
return readFile(skillPath, "utf8");
|
|
133
|
+
}
|
|
134
|
+
function clientArtifacts(client, root, cliEntry, skill) {
|
|
135
|
+
if (client === "mcp") {
|
|
136
|
+
return [
|
|
137
|
+
{
|
|
138
|
+
kind: "descriptor",
|
|
139
|
+
path: null,
|
|
140
|
+
content: mcpDescriptor(process.execPath, cliEntry, root),
|
|
141
|
+
},
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
if (skill === undefined)
|
|
145
|
+
throw new Error("CONNECT_SKILL_REQUIRED");
|
|
146
|
+
if (client === "codex") {
|
|
147
|
+
return [
|
|
148
|
+
{
|
|
149
|
+
kind: "configuration",
|
|
150
|
+
path: path.join(root, ".codex", "config.toml"),
|
|
151
|
+
content: codexConfig(process.execPath, cliEntry, root),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
kind: "skill",
|
|
155
|
+
path: path.join(root, ".agents", "skills", "assertledger", "SKILL.md"),
|
|
156
|
+
content: skill,
|
|
157
|
+
},
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
return [
|
|
161
|
+
{
|
|
162
|
+
kind: "configuration",
|
|
163
|
+
path: path.join(root, ".mcp.json"),
|
|
164
|
+
content: claudeCodeConfig(process.execPath, cliEntry, root),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
kind: "skill",
|
|
168
|
+
path: path.join(root, ".claude", "skills", "assertledger", "SKILL.md"),
|
|
169
|
+
content: skill,
|
|
170
|
+
},
|
|
171
|
+
];
|
|
172
|
+
}
|
|
173
|
+
function assertConnectionClient(client) {
|
|
174
|
+
if (client !== "codex" && client !== "claude-code" && client !== "mcp") {
|
|
175
|
+
throw new Error("CONNECT_CLIENT_UNSUPPORTED");
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function assertManagedConnectionClient(client) {
|
|
179
|
+
if (client !== "codex" && client !== "claude-code") {
|
|
180
|
+
throw new Error("DISCONNECT_CLIENT_UNSUPPORTED");
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function validateParentPath(root, targetPath, errorCode) {
|
|
184
|
+
const relativeParent = path.relative(root, path.dirname(targetPath));
|
|
185
|
+
if (relativeParent.startsWith("..") || path.isAbsolute(relativeParent))
|
|
186
|
+
throw new Error(errorCode);
|
|
187
|
+
let current = root;
|
|
188
|
+
for (const component of relativeParent.split(path.sep).filter(Boolean)) {
|
|
189
|
+
current = path.join(current, component);
|
|
190
|
+
try {
|
|
191
|
+
const metadata = await lstat(current);
|
|
192
|
+
if (!metadata.isDirectory() || metadata.isSymbolicLink())
|
|
193
|
+
throw new Error(errorCode);
|
|
194
|
+
if ((await realpath(current)) !== current)
|
|
195
|
+
throw new Error(errorCode);
|
|
196
|
+
}
|
|
197
|
+
catch (error) {
|
|
198
|
+
if (error.code === "ENOENT")
|
|
199
|
+
return;
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function inspectArtifact(artifact, errorCode) {
|
|
205
|
+
if (artifact.path === null)
|
|
206
|
+
return "ABSENT";
|
|
207
|
+
try {
|
|
208
|
+
const metadata = await lstat(artifact.path);
|
|
209
|
+
if (!metadata.isFile() || metadata.isSymbolicLink())
|
|
210
|
+
throw new Error(errorCode);
|
|
211
|
+
return (await readFile(artifact.path)).equals(Buffer.from(artifact.content, "utf8"))
|
|
212
|
+
? "UNCHANGED"
|
|
213
|
+
: "CONFLICT";
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
if (error.code === "ENOENT")
|
|
217
|
+
return "ABSENT";
|
|
218
|
+
throw error;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async function createSafeParents(root, targetPath) {
|
|
222
|
+
const relativeParent = path.relative(root, path.dirname(targetPath));
|
|
223
|
+
let current = root;
|
|
224
|
+
for (const component of relativeParent.split(path.sep).filter(Boolean)) {
|
|
225
|
+
current = path.join(current, component);
|
|
226
|
+
try {
|
|
227
|
+
await mkdir(current);
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
if (error.code !== "EEXIST")
|
|
231
|
+
throw error;
|
|
232
|
+
}
|
|
233
|
+
const metadata = await lstat(current);
|
|
234
|
+
if (!metadata.isDirectory() ||
|
|
235
|
+
metadata.isSymbolicLink() ||
|
|
236
|
+
(await realpath(current)) !== current) {
|
|
237
|
+
throw new Error("CONNECT_CONFIG_PATH_UNSAFE");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async function rollbackCreated(artifacts) {
|
|
242
|
+
for (const artifact of [...artifacts].reverse()) {
|
|
243
|
+
if (artifact.path === null)
|
|
244
|
+
continue;
|
|
245
|
+
try {
|
|
246
|
+
if ((await inspectArtifact(artifact, "CONNECT_CONFIG_PATH_UNSAFE")) === "UNCHANGED") {
|
|
247
|
+
await unlink(artifact.path);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
// Preserve the original failure; rollback is best-effort under the trusted local stability model.
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
export async function connectClient(requestedRoot, requestedCliEntry, client, write) {
|
|
256
|
+
assertConnectionClient(client);
|
|
257
|
+
const root = await resolveRepositoryRoot(requestedRoot);
|
|
258
|
+
const cliEntry = await resolveBuiltEntry(requestedCliEntry);
|
|
259
|
+
if (client === "mcp") {
|
|
260
|
+
return {
|
|
261
|
+
client,
|
|
262
|
+
status: "EMITTED",
|
|
263
|
+
artifacts: clientArtifacts(client, root, cliEntry, undefined),
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
const artifacts = clientArtifacts(client, root, cliEntry, await packagedSkill(cliEntry));
|
|
267
|
+
const states = [];
|
|
268
|
+
for (const artifact of artifacts) {
|
|
269
|
+
if (artifact.path === null)
|
|
270
|
+
continue;
|
|
271
|
+
await validateParentPath(root, artifact.path, "CONNECT_CONFIG_PATH_UNSAFE");
|
|
272
|
+
states.push(await inspectArtifact(artifact, "CONNECT_CONFIG_PATH_UNSAFE"));
|
|
273
|
+
}
|
|
274
|
+
if (states.includes("CONFLICT"))
|
|
275
|
+
return { client, status: "CONFLICT", artifacts };
|
|
276
|
+
if (!write)
|
|
277
|
+
return { client, status: "EMITTED", artifacts };
|
|
278
|
+
if (states.every((state) => state === "UNCHANGED")) {
|
|
279
|
+
return { client, status: "UNCHANGED", artifacts };
|
|
280
|
+
}
|
|
281
|
+
const created = [];
|
|
282
|
+
try {
|
|
283
|
+
for (const [index, artifact] of artifacts.entries()) {
|
|
284
|
+
if (artifact.path === null || states[index] === "UNCHANGED")
|
|
285
|
+
continue;
|
|
286
|
+
await createSafeParents(root, artifact.path);
|
|
287
|
+
}
|
|
288
|
+
for (const [index, artifact] of artifacts.entries()) {
|
|
289
|
+
if (artifact.path === null || states[index] === "UNCHANGED")
|
|
290
|
+
continue;
|
|
291
|
+
await writeFile(artifact.path, artifact.content, { encoding: "utf8", flag: "wx" });
|
|
292
|
+
created.push(artifact);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
await rollbackCreated(created);
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
return { client, status: "CREATED", artifacts };
|
|
300
|
+
}
|
|
301
|
+
export async function disconnectClient(requestedRoot, requestedCliEntry, client, write) {
|
|
302
|
+
assertManagedConnectionClient(client);
|
|
303
|
+
const root = await resolveRepositoryRoot(requestedRoot);
|
|
304
|
+
const cliEntry = await resolveBuiltEntry(requestedCliEntry);
|
|
305
|
+
const artifacts = clientArtifacts(client, root, cliEntry, await packagedSkill(cliEntry));
|
|
306
|
+
const states = [];
|
|
307
|
+
for (const artifact of artifacts) {
|
|
308
|
+
if (artifact.path === null)
|
|
309
|
+
continue;
|
|
310
|
+
await validateParentPath(root, artifact.path, "DISCONNECT_PATH_UNSAFE");
|
|
311
|
+
states.push(await inspectArtifact(artifact, "DISCONNECT_PATH_UNSAFE"));
|
|
312
|
+
}
|
|
313
|
+
if (states.includes("CONFLICT"))
|
|
314
|
+
return { client, status: "CONFLICT", artifacts };
|
|
315
|
+
if (!write)
|
|
316
|
+
return { client, status: "EMITTED", artifacts };
|
|
317
|
+
if (states.every((state) => state === "ABSENT"))
|
|
318
|
+
return { client, status: "ABSENT", artifacts };
|
|
319
|
+
const removed = [];
|
|
320
|
+
try {
|
|
321
|
+
for (const [index, artifact] of artifacts.entries()) {
|
|
322
|
+
if (artifact.path === null || states[index] === "ABSENT")
|
|
323
|
+
continue;
|
|
324
|
+
await unlink(artifact.path);
|
|
325
|
+
removed.push(artifact);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
catch (error) {
|
|
329
|
+
for (const artifact of [...removed].reverse()) {
|
|
330
|
+
if (artifact.path === null)
|
|
331
|
+
continue;
|
|
332
|
+
try {
|
|
333
|
+
await writeFile(artifact.path, artifact.content, { encoding: "utf8", flag: "wx" });
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// Preserve the original failure; rollback is best-effort under the trusted local stability model.
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
throw error;
|
|
340
|
+
}
|
|
341
|
+
return { client, status: "REMOVED", artifacts };
|
|
342
|
+
}
|
|
343
|
+
//# sourceMappingURL=connection.js.map
|