intentdna 1.9.0 → 1.9.3
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +227 -87
- package/dist/cli/commands/run-lifecycle.d.ts +61 -11
- package/dist/cli/commands/run-lifecycle.js +184 -50
- package/dist/cli/commands/run-observer.d.ts +15 -0
- package/dist/cli/commands/run-observer.js +210 -0
- package/dist/cli/commands/run.d.ts +16 -20
- package/dist/cli/commands/run.js +461 -659
- package/dist/cli/commands/sync.js +16 -12
- package/dist/cli/index.js +14 -5
- package/dist/compiler/compile.d.ts +1 -0
- package/dist/compiler/compile.js +1 -1
- package/dist/compiler/controller.d.ts +16 -0
- package/dist/compiler/controller.js +693 -0
- package/dist/compiler/input-resolver.d.ts +2 -0
- package/dist/compiler/input-resolver.js +22 -6
- package/dist/hooks/cli.d.ts +45 -0
- package/dist/hooks/cli.js +263 -31
- package/dist/hooks/host-event.d.ts +57 -0
- package/dist/hooks/host-event.js +187 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/protocol.d.ts +7 -0
- package/dist/hooks/schema.d.ts +1 -0
- package/dist/hooks/schema.js +89 -1
- package/dist/hooks/state.d.ts +23 -1
- package/dist/hooks/state.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-run.d.ts +4 -0
- package/dist/mcp/tools-run.js +419 -0
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +13 -241
- package/dist/runtime/artifact-store.d.ts +146 -0
- package/dist/runtime/artifact-store.js +1436 -0
- package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
- package/dist/runtime/canonical-attempt-outcome.js +195 -0
- package/dist/runtime/canonical-json.d.ts +10 -0
- package/dist/runtime/canonical-json.js +105 -0
- package/dist/runtime/canonical-run-application.d.ts +93 -0
- package/dist/runtime/canonical-run-application.js +229 -0
- package/dist/runtime/canonical-run-service.d.ts +454 -0
- package/dist/runtime/canonical-run-service.js +4760 -0
- package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
- package/dist/runtime/canonical-runtime-composition.js +34 -0
- package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
- package/dist/runtime/canonical-runtime-projection.js +4 -0
- package/dist/runtime/canonical-target-compiler.d.ts +46 -0
- package/dist/runtime/canonical-target-compiler.js +514 -0
- package/dist/runtime/claude-agent-identity.d.ts +2 -0
- package/dist/runtime/claude-agent-identity.js +21 -0
- package/dist/runtime/executable-run-plan.d.ts +328 -0
- package/dist/runtime/executable-run-plan.js +1485 -0
- package/dist/runtime/execution-authority.d.ts +83 -0
- package/dist/runtime/execution-authority.js +98 -0
- package/dist/runtime/handoff-resolver.js +4 -1
- package/dist/runtime/harness-pull-adapter.d.ts +146 -0
- package/dist/runtime/harness-pull-adapter.js +315 -0
- package/dist/runtime/index.d.ts +39 -14
- package/dist/runtime/index.js +29 -7
- package/dist/runtime/local-execution-authority.d.ts +194 -0
- package/dist/runtime/local-execution-authority.js +2897 -0
- package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
- package/dist/runtime/local-execution-reconciliation.js +107 -0
- package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
- package/dist/runtime/local-execution-supervisor-script.js +793 -0
- package/dist/runtime/local-provider-sandbox.d.ts +42 -0
- package/dist/runtime/local-provider-sandbox.js +154 -0
- package/dist/runtime/local-verifier-execution.d.ts +24 -0
- package/dist/runtime/local-verifier-execution.js +290 -0
- package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
- package/dist/runtime/local-windows-execution-supervisor-script.js +788 -0
- package/dist/runtime/plan-store.d.ts +30 -0
- package/dist/runtime/plan-store.js +231 -0
- package/dist/runtime/process-tree.d.ts +9 -0
- package/dist/runtime/process-tree.js +97 -24
- package/dist/runtime/providers/codex.js +2 -2
- package/dist/runtime/push-driver.d.ts +184 -0
- package/dist/runtime/push-driver.js +1002 -0
- package/dist/runtime/result-store.d.ts +34 -3
- package/dist/runtime/result-store.js +1073 -2
- package/dist/runtime/run-binding.d.ts +68 -0
- package/dist/runtime/run-binding.js +278 -0
- package/dist/runtime/run-contracts.d.ts +575 -0
- package/dist/runtime/run-contracts.js +58 -7
- package/dist/runtime/run-controller.d.ts +1 -6
- package/dist/runtime/run-controller.js +0 -41
- package/dist/runtime/run-store.d.ts +85 -7
- package/dist/runtime/run-store.js +2528 -186
- package/dist/runtime/skill-adapter.d.ts +18 -7
- package/dist/runtime/skill-adapter.js +100 -742
- package/dist/runtime/structured-output-validator.d.ts +44 -0
- package/dist/runtime/structured-output-validator.js +171 -0
- package/dist/runtime/verifier-command-binding.d.ts +22 -0
- package/dist/runtime/verifier-command-binding.js +162 -0
- package/dist/runtime/verifier.d.ts +46 -6
- package/dist/runtime/verifier.js +355 -53
- package/dist/runtime/windows-job-keeper.d.ts +47 -0
- package/dist/runtime/windows-job-keeper.js +231 -0
- package/dist/runtime/worker-executor.d.ts +17 -1
- package/dist/runtime/worker-executor.js +26 -5
- package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
- package/dist/runtime/workflow-plan-adapter.js +502 -6
- package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
- package/dist/runtime/workflow-runtime-manifest.js +5 -0
- package/dist/runtime/workspace-isolation.d.ts +29 -2
- package/dist/runtime/workspace-isolation.js +488 -11
- package/dist/runtime/workspace-observation.d.ts +44 -0
- package/dist/runtime/workspace-observation.js +216 -0
- package/dist/schema/controller-registry.d.ts +0 -7
- package/dist/schema/controller-registry.js +0 -8
- package/dist/schema/types.d.ts +84 -10
- package/dist/schema/types.js +2 -0
- package/dist/schema/validate.js +151 -1
- package/dist/schema/validators/controllers.d.ts +1 -1
- package/dist/schema/validators/controllers.js +126 -41
- package/dist/schema/workflow-authoring-contract.js +2 -0
- package/dist/schema/yaml-parser.js +1 -1
- package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
- package/dist/templates/flutter-rewrite.dna.yaml +87 -60
- package/dist/templates/safe-refactoring.dna.yaml +1 -0
- package/native/windows-job-keeper/README.md +80 -0
- package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/keeper.c +979 -0
- package/native/windows-job-keeper/manifest.json +33 -0
- package/package.json +6 -2
- package/scripts/build-windows-job-keeper.mjs +172 -0
- package/scripts/verify-windows-job-keeper.mjs +184 -0
package/dist/runtime/verifier.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
1
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
import { readFile, realpath, stat } from "node:fs/promises";
|
|
3
4
|
import { dirname, isAbsolute, relative, resolve } from "node:path";
|
|
4
5
|
import { EvaluatorResultError, parseEvaluatorResult } from "./evaluator.js";
|
|
@@ -7,6 +8,191 @@ export const MAX_VERIFIER_EVIDENCE_BYTES = 4096;
|
|
|
7
8
|
export const VERIFIER_DIAGNOSTIC_PREFIX = "INTENTDNA_DIAGNOSTIC:";
|
|
8
9
|
export const MAX_VERIFIER_DIAGNOSTIC_BYTES = 2048;
|
|
9
10
|
const VERIFIER_DIAGNOSTIC_CODE = /^INTENTDNA_DIAGNOSTIC:[A-Za-z0-9][A-Za-z0-9_.:-]{0,255}$/;
|
|
11
|
+
function assertVerifierRecord(value, label) {
|
|
12
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
13
|
+
throw new TypeError(`${label} must be an object`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function assertVerifierExactKeys(value, required, optional = [], label) {
|
|
17
|
+
const allowed = new Set([...required, ...optional]);
|
|
18
|
+
const keys = Object.keys(value);
|
|
19
|
+
if (required.some((key) => !Object.hasOwn(value, key)) || keys.some((key) => !allowed.has(key))) {
|
|
20
|
+
throw new TypeError(`${label} has invalid fields`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function assertVerifierTimestamp(value, label) {
|
|
24
|
+
if (typeof value !== "string"
|
|
25
|
+
|| !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/u.test(value)) {
|
|
26
|
+
throw new TypeError(`${label} must be a canonical UTC timestamp`);
|
|
27
|
+
}
|
|
28
|
+
const parsed = Date.parse(value);
|
|
29
|
+
if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) {
|
|
30
|
+
throw new TypeError(`${label} must be a valid timestamp`);
|
|
31
|
+
}
|
|
32
|
+
return parsed;
|
|
33
|
+
}
|
|
34
|
+
function assertVerifierDigest(value, label) {
|
|
35
|
+
if (typeof value !== "string" || !/^sha256:[a-f0-9]{64}$/u.test(value)) {
|
|
36
|
+
throw new TypeError(`${label} must be a lowercase sha256 digest`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function assertVerifierSignal(value, label) {
|
|
40
|
+
if (typeof value !== "string" || !/^SIG[A-Z0-9]+$/u.test(value)) {
|
|
41
|
+
throw new TypeError(`${label} must be a signal name`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function assertVerifierCommandProcessOutcome(value) {
|
|
45
|
+
assertVerifierRecord(value, "verifier execution outcome");
|
|
46
|
+
assertVerifierExactKeys(value, ["kind", "exitCode", "signal", "timedOut", "launchError"], [], "verifier execution outcome");
|
|
47
|
+
if (value.kind === "exited") {
|
|
48
|
+
if (!Number.isSafeInteger(value.exitCode)
|
|
49
|
+
|| value.exitCode < 0
|
|
50
|
+
|| value.signal !== null
|
|
51
|
+
|| value.timedOut !== false
|
|
52
|
+
|| value.launchError !== null) {
|
|
53
|
+
throw new TypeError("verifier exited outcome has an invalid tuple");
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (value.kind === "signaled") {
|
|
58
|
+
if (value.exitCode !== null || value.timedOut !== false || value.launchError !== null) {
|
|
59
|
+
throw new TypeError("verifier signaled outcome has an invalid tuple");
|
|
60
|
+
}
|
|
61
|
+
assertVerifierSignal(value.signal, "verifier signaled outcome signal");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (value.kind === "timed_out") {
|
|
65
|
+
if ((value.exitCode !== null
|
|
66
|
+
&& (!Number.isSafeInteger(value.exitCode) || value.exitCode <= 0))
|
|
67
|
+
|| value.timedOut !== true
|
|
68
|
+
|| value.launchError !== null) {
|
|
69
|
+
throw new TypeError("verifier timed_out outcome has an invalid tuple");
|
|
70
|
+
}
|
|
71
|
+
if (value.signal !== null)
|
|
72
|
+
assertVerifierSignal(value.signal, "verifier timed_out outcome signal");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (value.kind === "launch_error") {
|
|
76
|
+
if (value.exitCode !== null || value.signal !== null || value.timedOut !== false) {
|
|
77
|
+
throw new TypeError("verifier launch_error outcome has an invalid tuple");
|
|
78
|
+
}
|
|
79
|
+
assertVerifierRecord(value.launchError, "verifier launch error");
|
|
80
|
+
assertVerifierExactKeys(value.launchError, ["code", "messageDigest"], [], "verifier launch error");
|
|
81
|
+
if (typeof value.launchError.code !== "string"
|
|
82
|
+
|| !/^[A-Z][A-Z0-9_]*$/u.test(value.launchError.code)) {
|
|
83
|
+
throw new TypeError("verifier launch error code is invalid");
|
|
84
|
+
}
|
|
85
|
+
assertVerifierDigest(value.launchError.messageDigest, "verifier launch error message digest");
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
throw new TypeError("verifier execution outcome kind is invalid");
|
|
89
|
+
}
|
|
90
|
+
export function assertVerifierCommandExecutionResult(value) {
|
|
91
|
+
assertVerifierRecord(value, "verifier command execution result");
|
|
92
|
+
assertVerifierExactKeys(value, [
|
|
93
|
+
"outcome",
|
|
94
|
+
"processTreeDrained",
|
|
95
|
+
"startedAt",
|
|
96
|
+
"completedAt",
|
|
97
|
+
"stdoutDigest",
|
|
98
|
+
"stdoutBytes",
|
|
99
|
+
"stderrDigest",
|
|
100
|
+
"stderrBytes",
|
|
101
|
+
], ["evidence"], "verifier command execution result");
|
|
102
|
+
assertVerifierCommandProcessOutcome(value.outcome);
|
|
103
|
+
if (typeof value.processTreeDrained !== "boolean") {
|
|
104
|
+
throw new TypeError("verifier command execution processTreeDrained must be a boolean");
|
|
105
|
+
}
|
|
106
|
+
if (value.evidence !== undefined
|
|
107
|
+
&& (typeof value.evidence !== "string"
|
|
108
|
+
|| value.evidence.length === 0
|
|
109
|
+
|| value.evidence.length > MAX_VERIFIER_EVIDENCE_BYTES)) {
|
|
110
|
+
throw new TypeError("verifier command execution evidence is invalid");
|
|
111
|
+
}
|
|
112
|
+
const startedAt = assertVerifierTimestamp(value.startedAt, "verifier command execution startedAt");
|
|
113
|
+
const completedAt = assertVerifierTimestamp(value.completedAt, "verifier command execution completedAt");
|
|
114
|
+
if (completedAt < startedAt) {
|
|
115
|
+
throw new TypeError("verifier command execution completedAt precedes startedAt");
|
|
116
|
+
}
|
|
117
|
+
assertVerifierDigest(value.stdoutDigest, "verifier command execution stdoutDigest");
|
|
118
|
+
assertVerifierDigest(value.stderrDigest, "verifier command execution stderrDigest");
|
|
119
|
+
if (!Number.isSafeInteger(value.stdoutBytes) || value.stdoutBytes < 0
|
|
120
|
+
|| !Number.isSafeInteger(value.stderrBytes) || value.stderrBytes < 0) {
|
|
121
|
+
throw new TypeError("verifier command execution byte counts must be non-negative safe integers");
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function verifierCommandExecutionPassed(execution) {
|
|
125
|
+
return execution.processTreeDrained
|
|
126
|
+
&& execution.outcome.kind === "exited"
|
|
127
|
+
&& execution.outcome.exitCode === 0;
|
|
128
|
+
}
|
|
129
|
+
export function verifierCommandExecutionTimedOut(execution) {
|
|
130
|
+
return execution.outcome.timedOut;
|
|
131
|
+
}
|
|
132
|
+
export function verifierCommandExecutionExitCode(execution) {
|
|
133
|
+
return execution.outcome.exitCode;
|
|
134
|
+
}
|
|
135
|
+
export function verifierCommandExecutionSignal(execution) {
|
|
136
|
+
return execution.outcome.signal;
|
|
137
|
+
}
|
|
138
|
+
const VERIFIER_PROCESS_DRAIN_GRACE_MS = 1_000;
|
|
139
|
+
function terminateVerifierProcessGroup(child, signal) {
|
|
140
|
+
if (child.pid === undefined)
|
|
141
|
+
return;
|
|
142
|
+
if (process.platform === "win32") {
|
|
143
|
+
const force = signal === "SIGKILL" ? ["/F"] : [];
|
|
144
|
+
const result = spawnSync("taskkill", ["/PID", String(child.pid), "/T", ...force], { windowsHide: true, stdio: "ignore" });
|
|
145
|
+
if (!result.error && result.status === 0)
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
if (process.platform === "win32")
|
|
150
|
+
child.kill(signal);
|
|
151
|
+
else
|
|
152
|
+
process.kill(-child.pid, signal);
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
if (error.code !== "ESRCH")
|
|
156
|
+
throw error;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function verifierProcessGroupExists(child) {
|
|
160
|
+
if (child.pid === undefined)
|
|
161
|
+
return false;
|
|
162
|
+
if (process.platform === "win32")
|
|
163
|
+
return child.exitCode === null && child.signalCode === null;
|
|
164
|
+
try {
|
|
165
|
+
process.kill(-child.pid, 0);
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
const code = error.code;
|
|
170
|
+
if (code === "ESRCH")
|
|
171
|
+
return false;
|
|
172
|
+
if (code === "EPERM")
|
|
173
|
+
return true;
|
|
174
|
+
throw error;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function drainVerifierProcessGroup(child) {
|
|
178
|
+
if (!verifierProcessGroupExists(child))
|
|
179
|
+
return;
|
|
180
|
+
terminateVerifierProcessGroup(child, "SIGTERM");
|
|
181
|
+
const deadline = Date.now() + VERIFIER_PROCESS_DRAIN_GRACE_MS;
|
|
182
|
+
while (verifierProcessGroupExists(child) && Date.now() < deadline) {
|
|
183
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
|
|
184
|
+
}
|
|
185
|
+
if (!verifierProcessGroupExists(child))
|
|
186
|
+
return;
|
|
187
|
+
terminateVerifierProcessGroup(child, "SIGKILL");
|
|
188
|
+
const forcedDeadline = Date.now() + VERIFIER_PROCESS_DRAIN_GRACE_MS;
|
|
189
|
+
while (verifierProcessGroupExists(child) && Date.now() < forcedDeadline) {
|
|
190
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 10));
|
|
191
|
+
}
|
|
192
|
+
if (verifierProcessGroupExists(child)) {
|
|
193
|
+
throw new Error("verifier process group did not drain after SIGKILL");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
10
196
|
const SAFE_COMMAND_VARIABLE_RE = /^[A-Za-z0-9_-]+$/;
|
|
11
197
|
const SAFE_PATH_VARIABLE_RE = /^[-A-Za-z0-9_./]+$/;
|
|
12
198
|
function isSafeRelativePathVariable(value) {
|
|
@@ -214,60 +400,167 @@ function appendFailureDiagnostic(message, evidence) {
|
|
|
214
400
|
const diagnostics = extractVerifierDiagnostics(evidence ?? "").join("\n");
|
|
215
401
|
return diagnostics ? `${message}\n${diagnostics}` : message;
|
|
216
402
|
}
|
|
217
|
-
|
|
403
|
+
function verifierLaunchError(error, fallbackCode) {
|
|
404
|
+
const rawCode = error && typeof error === "object" && "code" in error
|
|
405
|
+
? String(error.code ?? "")
|
|
406
|
+
: "";
|
|
407
|
+
const code = /^[A-Z][A-Z0-9_]*$/u.test(rawCode) ? rawCode : fallbackCode;
|
|
408
|
+
const message = error instanceof Error
|
|
409
|
+
? `${error.name}:${error.message}`
|
|
410
|
+
: String(error);
|
|
411
|
+
return {
|
|
412
|
+
code,
|
|
413
|
+
messageDigest: `sha256:${createHash("sha256").update(message).digest("hex")}`,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
function verifierProcessOutcome(code, signal, timedOut) {
|
|
417
|
+
if (timedOut) {
|
|
418
|
+
return {
|
|
419
|
+
kind: "timed_out",
|
|
420
|
+
exitCode: code === 0 ? null : code,
|
|
421
|
+
signal,
|
|
422
|
+
timedOut: true,
|
|
423
|
+
launchError: null,
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
if (code !== null && signal === null) {
|
|
427
|
+
return {
|
|
428
|
+
kind: "exited",
|
|
429
|
+
exitCode: code,
|
|
430
|
+
signal: null,
|
|
431
|
+
timedOut: false,
|
|
432
|
+
launchError: null,
|
|
433
|
+
};
|
|
434
|
+
}
|
|
435
|
+
if (code === null && signal !== null) {
|
|
436
|
+
return {
|
|
437
|
+
kind: "signaled",
|
|
438
|
+
exitCode: null,
|
|
439
|
+
signal,
|
|
440
|
+
timedOut: false,
|
|
441
|
+
launchError: null,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
kind: "launch_error",
|
|
446
|
+
exitCode: null,
|
|
447
|
+
signal: null,
|
|
448
|
+
timedOut: false,
|
|
449
|
+
launchError: verifierLaunchError(new Error("process closed without exactly one of exit code or signal"), "INVALID_PROCESS_OUTCOME"),
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
export async function execVerifierCommand(projectDir, command, timeoutMs = DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS, env = {}, now = () => new Date()) {
|
|
218
453
|
return new Promise((resolvePromise) => {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
454
|
+
const startedAt = now().toISOString();
|
|
455
|
+
let child;
|
|
456
|
+
try {
|
|
457
|
+
child = spawn("bash", ["--noprofile", "--norc", "-c", command], {
|
|
458
|
+
cwd: projectDir,
|
|
459
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
460
|
+
env: { ...env },
|
|
461
|
+
detached: process.platform !== "win32",
|
|
462
|
+
windowsHide: true,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
catch (error) {
|
|
466
|
+
const emptyDigest = `sha256:${createHash("sha256").digest("hex")}`;
|
|
467
|
+
resolvePromise({
|
|
468
|
+
outcome: {
|
|
469
|
+
kind: "launch_error",
|
|
470
|
+
exitCode: null,
|
|
471
|
+
signal: null,
|
|
472
|
+
timedOut: false,
|
|
473
|
+
launchError: verifierLaunchError(error, "SPAWN_ERROR"),
|
|
474
|
+
},
|
|
475
|
+
processTreeDrained: true,
|
|
476
|
+
startedAt,
|
|
477
|
+
completedAt: now().toISOString(),
|
|
478
|
+
stdoutDigest: emptyDigest,
|
|
479
|
+
stdoutBytes: 0,
|
|
480
|
+
stderrDigest: emptyDigest,
|
|
481
|
+
stderrBytes: 0,
|
|
482
|
+
});
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
224
485
|
let stdoutPendingLine = "";
|
|
225
486
|
let stderrPendingLine = "";
|
|
226
487
|
let stdoutBytes = 0;
|
|
227
488
|
let stderrBytes = 0;
|
|
489
|
+
const stdoutDigest = createHash("sha256");
|
|
490
|
+
const stderrDigest = createHash("sha256");
|
|
228
491
|
let settled = false;
|
|
492
|
+
let timedOut = false;
|
|
493
|
+
let drainPromise = null;
|
|
494
|
+
let timeoutId = null;
|
|
229
495
|
const diagnostics = [];
|
|
230
|
-
const
|
|
496
|
+
const startDrain = () => {
|
|
497
|
+
drainPromise ??= drainVerifierProcessGroup(child).then(() => true, () => false);
|
|
498
|
+
return drainPromise;
|
|
499
|
+
};
|
|
500
|
+
const settle = (outcome, processTreeDrained) => {
|
|
231
501
|
if (settled)
|
|
232
502
|
return;
|
|
233
503
|
settled = true;
|
|
234
|
-
|
|
235
|
-
|
|
504
|
+
if (timeoutId !== null)
|
|
505
|
+
clearTimeout(timeoutId);
|
|
506
|
+
resolvePromise({
|
|
507
|
+
outcome,
|
|
508
|
+
processTreeDrained,
|
|
509
|
+
evidence: summarizeCommandEvidenceFromStats(stdoutBytes, stderrBytes, [
|
|
510
|
+
...diagnostics,
|
|
511
|
+
...extractVerifierDiagnostics(`${stdoutPendingLine}\n${stderrPendingLine}`),
|
|
512
|
+
]),
|
|
513
|
+
startedAt,
|
|
514
|
+
completedAt: now().toISOString(),
|
|
515
|
+
stdoutDigest: `sha256:${stdoutDigest.digest("hex")}`,
|
|
516
|
+
stdoutBytes,
|
|
517
|
+
stderrDigest: `sha256:${stderrDigest.digest("hex")}`,
|
|
518
|
+
stderrBytes,
|
|
519
|
+
});
|
|
236
520
|
};
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
521
|
+
timeoutId = setTimeout(() => {
|
|
522
|
+
timedOut = true;
|
|
523
|
+
void startDrain().then((drained) => {
|
|
524
|
+
if (!drained)
|
|
525
|
+
settle(verifierProcessOutcome(null, null, true), false);
|
|
526
|
+
});
|
|
242
527
|
}, timeoutMs);
|
|
243
|
-
child.stdout
|
|
244
|
-
const
|
|
245
|
-
|
|
528
|
+
child.stdout?.on("data", (chunk) => {
|
|
529
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
530
|
+
const text = bytes.toString("utf8");
|
|
531
|
+
stdoutBytes += bytes.length;
|
|
532
|
+
stdoutDigest.update(bytes);
|
|
246
533
|
stdoutPendingLine = collectVerifierDiagnostics(stdoutPendingLine, text, diagnostics);
|
|
247
534
|
});
|
|
248
|
-
child.stderr
|
|
249
|
-
const
|
|
250
|
-
|
|
535
|
+
child.stderr?.on("data", (chunk) => {
|
|
536
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
537
|
+
const text = bytes.toString("utf8");
|
|
538
|
+
stderrBytes += bytes.length;
|
|
539
|
+
stderrDigest.update(bytes);
|
|
251
540
|
stderrPendingLine = collectVerifierDiagnostics(stderrPendingLine, text, diagnostics);
|
|
252
541
|
});
|
|
253
|
-
child.on("error", () =>
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
})
|
|
542
|
+
child.on("error", (error) => {
|
|
543
|
+
void startDrain().then((drained) => {
|
|
544
|
+
if (timedOut) {
|
|
545
|
+
settle(verifierProcessOutcome(null, null, true), drained);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
settle({
|
|
549
|
+
kind: "launch_error",
|
|
550
|
+
exitCode: null,
|
|
551
|
+
signal: null,
|
|
552
|
+
timedOut: false,
|
|
553
|
+
launchError: verifierLaunchError(error, "SPAWN_ERROR"),
|
|
554
|
+
}, drained);
|
|
555
|
+
});
|
|
556
|
+
});
|
|
557
|
+
child.on("exit", () => {
|
|
558
|
+
void startDrain();
|
|
559
|
+
});
|
|
560
|
+
child.on("close", async (code, signal) => {
|
|
561
|
+
const drained = await startDrain();
|
|
562
|
+
settle(verifierProcessOutcome(code, signal, timedOut), drained);
|
|
563
|
+
});
|
|
271
564
|
});
|
|
272
565
|
}
|
|
273
566
|
async function execVerifierStdout(projectDir, command, args) {
|
|
@@ -522,15 +815,18 @@ export async function runCompletionVerifier(completion, ir, context) {
|
|
|
522
815
|
return { passed: false, target: commandResolution.target, evidence: commandResolution.evidence, exit_code: commandResolution.exit_code, message: commandResolution.message };
|
|
523
816
|
const command = commandResolution.command;
|
|
524
817
|
const commandResult = await execVerifierCommand(context.projectDir, command, context.commandTimeoutMs, verifierCommandEnv(context.variables));
|
|
818
|
+
const passed = verifierCommandExecutionPassed(commandResult);
|
|
819
|
+
const timedOut = verifierCommandExecutionTimedOut(commandResult);
|
|
820
|
+
const exitCode = verifierCommandExecutionExitCode(commandResult);
|
|
525
821
|
return {
|
|
526
|
-
passed
|
|
822
|
+
passed,
|
|
527
823
|
target: command,
|
|
528
824
|
evidence: commandResult.evidence,
|
|
529
|
-
exit_code:
|
|
530
|
-
timedOut
|
|
531
|
-
message:
|
|
825
|
+
exit_code: exitCode ?? (timedOut ? 124 : 1),
|
|
826
|
+
timedOut,
|
|
827
|
+
message: passed
|
|
532
828
|
? undefined
|
|
533
|
-
:
|
|
829
|
+
: timedOut
|
|
534
830
|
? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
|
|
535
831
|
: appendFailureDiagnostic("Verifier command failed", commandResult.evidence),
|
|
536
832
|
};
|
|
@@ -548,15 +844,18 @@ export async function runCheckpointVerifier(checkpoint, ir, context) {
|
|
|
548
844
|
return { passed: false, target: commandResolution.target, evidence: commandResolution.evidence, exit_code: commandResolution.exit_code, message: commandResolution.message };
|
|
549
845
|
const command = commandResolution.command;
|
|
550
846
|
const commandResult = await execVerifierCommand(context.projectDir, command, context.commandTimeoutMs, verifierCommandEnv(context.variables));
|
|
847
|
+
const passed = verifierCommandExecutionPassed(commandResult);
|
|
848
|
+
const timedOut = verifierCommandExecutionTimedOut(commandResult);
|
|
849
|
+
const exitCode = verifierCommandExecutionExitCode(commandResult);
|
|
551
850
|
return {
|
|
552
|
-
passed
|
|
851
|
+
passed,
|
|
553
852
|
target: command,
|
|
554
853
|
evidence: commandResult.evidence,
|
|
555
|
-
exit_code:
|
|
556
|
-
timedOut
|
|
557
|
-
message:
|
|
854
|
+
exit_code: exitCode ?? (timedOut ? 124 : 1),
|
|
855
|
+
timedOut,
|
|
856
|
+
message: passed
|
|
558
857
|
? checkpoint.message
|
|
559
|
-
:
|
|
858
|
+
: timedOut
|
|
560
859
|
? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
|
|
561
860
|
: appendFailureDiagnostic("Verifier command failed", commandResult.evidence),
|
|
562
861
|
};
|
|
@@ -598,14 +897,17 @@ export async function runCheckpointVerifier(checkpoint, ir, context) {
|
|
|
598
897
|
return { passed: false, target: checkpoint.assert, evidence: "policy_denied", exit_code: 126, message: verifierAssertPolicyMessage(checkpoint.assert) };
|
|
599
898
|
}
|
|
600
899
|
const commandResult = await execVerifierCommand(context.projectDir, assertCommands[checkpoint.assert], context.commandTimeoutMs);
|
|
900
|
+
const passed = verifierCommandExecutionPassed(commandResult);
|
|
901
|
+
const timedOut = verifierCommandExecutionTimedOut(commandResult);
|
|
902
|
+
const exitCode = verifierCommandExecutionExitCode(commandResult);
|
|
601
903
|
return {
|
|
602
|
-
passed
|
|
904
|
+
passed,
|
|
603
905
|
target: checkpoint.assert,
|
|
604
906
|
evidence: commandResult.evidence,
|
|
605
|
-
exit_code:
|
|
606
|
-
message:
|
|
907
|
+
exit_code: exitCode ?? (timedOut ? 124 : 1),
|
|
908
|
+
message: passed
|
|
607
909
|
? checkpoint.message
|
|
608
|
-
:
|
|
910
|
+
: timedOut
|
|
609
911
|
? `Verifier command timed out after ${context.commandTimeoutMs ?? DEFAULT_VERIFIER_COMMAND_TIMEOUT_MS}ms`
|
|
610
912
|
: checkpoint.message
|
|
611
913
|
? appendFailureDiagnostic(checkpoint.message, commandResult.evidence)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { CanonicalResultDigest } from "./run-contracts.js";
|
|
2
|
+
export declare const WINDOWS_JOB_KEEPER_PROTOCOL_VERSION: "IDNA_WINDOWS_JOB_KEEPER_V1";
|
|
3
|
+
export declare const WINDOWS_JOB_KEEPER_MANIFEST_SCHEMA: "intentdna.windows_job_keeper_manifest.v1";
|
|
4
|
+
export type WindowsJobKeeperArchitecture = "x86_64" | "aarch64";
|
|
5
|
+
interface WindowsJobKeeperManifestFile {
|
|
6
|
+
readonly path: string;
|
|
7
|
+
readonly sha256: CanonicalResultDigest;
|
|
8
|
+
}
|
|
9
|
+
interface WindowsJobKeeperManifestBinary extends WindowsJobKeeperManifestFile {
|
|
10
|
+
readonly pe_machine: "0x8664" | "0xaa64";
|
|
11
|
+
}
|
|
12
|
+
export interface WindowsJobKeeperManifest {
|
|
13
|
+
readonly schema_version: typeof WINDOWS_JOB_KEEPER_MANIFEST_SCHEMA;
|
|
14
|
+
readonly protocol_version: typeof WINDOWS_JOB_KEEPER_PROTOCOL_VERSION;
|
|
15
|
+
readonly source: WindowsJobKeeperManifestFile;
|
|
16
|
+
readonly toolchain: {
|
|
17
|
+
readonly name: "zig";
|
|
18
|
+
readonly version: "0.16.0";
|
|
19
|
+
readonly build_script: WindowsJobKeeperManifestFile & {
|
|
20
|
+
readonly path: "../../scripts/build-windows-job-keeper.mjs";
|
|
21
|
+
};
|
|
22
|
+
readonly source_date_epoch: 0;
|
|
23
|
+
readonly targets: Readonly<Record<WindowsJobKeeperArchitecture, "x86_64-windows-gnu" | "aarch64-windows-gnu">>;
|
|
24
|
+
};
|
|
25
|
+
readonly binaries: Readonly<Record<WindowsJobKeeperArchitecture, WindowsJobKeeperManifestBinary>>;
|
|
26
|
+
}
|
|
27
|
+
export interface WindowsJobKeeperArtifact {
|
|
28
|
+
readonly architecture: WindowsJobKeeperArchitecture;
|
|
29
|
+
readonly path: string;
|
|
30
|
+
readonly digest: CanonicalResultDigest;
|
|
31
|
+
readonly source_digest: CanonicalResultDigest;
|
|
32
|
+
readonly protocol_version: typeof WINDOWS_JOB_KEEPER_PROTOCOL_VERSION;
|
|
33
|
+
readonly manifest_path: string | null;
|
|
34
|
+
}
|
|
35
|
+
export interface ResolveWindowsJobKeeperOptions {
|
|
36
|
+
readonly platform: NodeJS.Platform;
|
|
37
|
+
readonly architecture?: NodeJS.Architecture;
|
|
38
|
+
readonly helper_path?: string;
|
|
39
|
+
readonly expected_digest?: CanonicalResultDigest;
|
|
40
|
+
}
|
|
41
|
+
export declare class WindowsJobKeeperArtifactError extends Error {
|
|
42
|
+
constructor(message: string, options?: ErrorOptions);
|
|
43
|
+
}
|
|
44
|
+
export declare function parseWindowsJobKeeperManifest(value: unknown): WindowsJobKeeperManifest;
|
|
45
|
+
export declare function readPeMachine(path: string): number;
|
|
46
|
+
export declare function resolveWindowsJobKeeperArtifact(options: ResolveWindowsJobKeeperOptions): WindowsJobKeeperArtifact | null;
|
|
47
|
+
export {};
|