@kontextmind/kxm 0.6.0 → 0.7.4
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 +1 -1
- package/.kxm/agents/coordinator.yaml +9 -0
- package/.kxm/agents/critic-arch.yaml +13 -0
- package/.kxm/agents/critic-cli.yaml +13 -0
- package/.kxm/agents/implementer.yaml +13 -0
- package/.kxm/gates.yaml +8 -0
- package/.kxm/producers.yaml +22 -0
- package/.kxm/project.yaml +15 -0
- package/.kxm/roles/writer.yaml +7 -0
- package/.kxm/workflows/default.yaml +47 -0
- package/CHANGELOG.md +39 -7
- package/README.md +1 -0
- package/docs/README.md +4 -0
- package/docs/agent-skills.md +121 -0
- package/docs/architecture.md +1 -1
- package/docs/assignment-runner.md +21 -8
- package/docs/configuration.md +11 -2
- package/docs/getting-started.md +21 -0
- package/docs/kxm-handbook.md +3 -3
- package/docs/operations.md +24 -0
- package/docs/operator-pi-packages.md +63 -0
- package/docs/skills/repo-work-delivery.md +107 -0
- package/docs/skills.md +2 -0
- package/docs/test-matrix.md +4 -3
- package/docs/troubleshooting.md +41 -1
- package/docs/vnext/validation.md +9 -0
- package/docs/webhook-workflows.md +2 -2
- package/examples/README.md +1 -1
- package/package.json +16 -17
- package/plugins/kxm/.claude-plugin/plugin.json +1 -1
- package/plugins/kxm/README.md +1 -1
- package/plugins/kxm/dist/cli.js +8955 -3934
- package/plugins/kxm/dist/core.js +271 -34
- package/plugins/kxm/dist/extension.js +7759 -86
- package/plugins/kxm/dist/mcp-server.js +75 -21
- package/plugins/kxm/dist/runtime.js +5637 -1125
- package/plugins/kxm/dist/server.js +3125 -2260
- package/plugins/kxm/dist/vnext-runtime-supervisor.js +5808 -565
- package/plugins/kxm/package.json +1 -1
- package/plugins/kxm/skills/SUITE.md +5 -0
- package/plugins/kxm/skills/hints.json +73 -0
- package/plugins/kxm/skills/kxm/SKILL.md +30 -83
- package/plugins/kxm/skills/kxm-context-memory/SKILL.md +69 -0
- package/plugins/kxm/skills/kxm-definitions/SKILL.md +65 -0
- package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +34 -0
- package/plugins/kxm/skills/kxm-harvest/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-hub-ops/SKILL.md +43 -0
- package/plugins/kxm/skills/kxm-insights/SKILL.md +48 -0
- package/plugins/kxm/skills/kxm-mind/SKILL.md +59 -0
- package/plugins/kxm/skills/kxm-peer/SKILL.md +110 -0
- package/plugins/kxm/skills/kxm-project-setup/SKILL.md +42 -0
- package/plugins/kxm/skills/kxm-projects/SKILL.md +43 -0
- package/plugins/kxm/skills/kxm-protocol/SKILL.md +66 -0
- package/plugins/kxm/skills/kxm-query/SKILL.md +45 -0
- package/plugins/kxm/skills/kxm-routing-improve/SKILL.md +30 -0
- package/plugins/kxm/skills/kxm-runs/SKILL.md +29 -0
- package/plugins/kxm/skills/kxm-setup/SKILL.md +55 -0
- package/plugins/kxm/skills/kxm-skill-lifecycle/SKILL.md +31 -0
- package/plugins/kxm/skills/kxm-tasks/SKILL.md +33 -0
- package/plugins/kxm/skills/kxm-triage/SKILL.md +47 -0
- package/plugins/kxm/skills/kxm-work/SKILL.md +44 -0
- package/plugins/kxm/skills/kxm-workflow/SKILL.md +45 -0
- package/plugins/kxm/src/autocomplete.ts +9 -3
- package/plugins/kxm/src/cli.ts +1637 -73
- package/plugins/kxm/src/commands.ts +150 -8
- package/plugins/kxm/src/completion-install.ts +223 -0
- package/plugins/kxm/src/config.ts +7 -4
- package/plugins/kxm/src/context-packet.ts +172 -0
- package/plugins/kxm/src/database.ts +1 -1
- package/plugins/kxm/src/extension.ts +36 -1
- package/plugins/kxm/src/external-effects.ts +357 -8
- package/plugins/kxm/src/hub-env.ts +193 -0
- package/plugins/kxm/src/hub.ts +2 -4
- package/plugins/kxm/src/improve.ts +72 -0
- package/plugins/kxm/src/init-guide-setup.ts +547 -0
- package/plugins/kxm/src/local-snapshot.ts +1 -1
- package/plugins/kxm/src/mcp-server.ts +1 -1
- package/plugins/kxm/src/model-inventory.ts +127 -0
- package/plugins/kxm/src/policy-draft.d.mts +55 -0
- package/plugins/kxm/src/policy-draft.mjs +565 -0
- package/plugins/kxm/src/price-calc.ts +17 -18
- package/plugins/kxm/src/prices.ts +32 -16
- package/plugins/kxm/src/producers.ts +71 -0
- package/plugins/kxm/src/protocol.ts +111 -0
- package/plugins/kxm/src/restricted-yaml.d.mts +31 -0
- package/plugins/kxm/src/restricted-yaml.mjs +145 -0
- package/plugins/kxm/src/role.ts +710 -0
- package/plugins/kxm/src/routing.ts +99 -1
- package/plugins/kxm/src/safety-integrity.ts +76 -0
- package/plugins/kxm/src/session-work.ts +9 -2
- package/plugins/kxm/src/sqlite.ts +76 -0
- package/plugins/kxm/src/store.ts +1 -1
- package/plugins/kxm/src/studio-layout.ts +660 -17
- package/plugins/kxm/src/suggest.ts +7 -13
- package/plugins/kxm/src/telemetry.ts +82 -0
- package/plugins/kxm/src/tui.ts +140 -0
- package/plugins/kxm/src/vnext-bindings.ts +1 -1
- package/plugins/kxm/src/vnext-config.ts +53 -111
- package/plugins/kxm/src/vnext-engine-command.ts +2 -0
- package/plugins/kxm/src/vnext-engine.ts +214 -62
- package/plugins/kxm/src/vnext-harness.ts +267 -83
- package/plugins/kxm/src/vnext-oneshot-evidence.ts +85 -0
- package/plugins/kxm/src/vnext-oneshot-process.ts +166 -0
- package/plugins/kxm/src/vnext-oneshot-producer.ts +179 -224
- package/plugins/kxm/src/vnext-runtime-store.ts +36 -2
- package/plugins/kxm/src/vnext-runtime-supervisor.ts +120 -5
- package/plugins/kxm/src/vnext-runtime.ts +14 -0
- package/plugins/kxm/src/workflow-manager.ts +392 -0
- package/plugins/kxm/src/workflow-tui.ts +255 -0
- package/plugins/kxm/src/workflow.ts +144 -0
- package/schemas/policy-draft/README.md +17 -0
- package/schemas/policy-draft/model.v2.schema.json +140 -0
- package/schemas/policy-draft/role.v2.schema.json +91 -0
- package/schemas/vnext/role.schema.json +76 -0
- package/schemas/vnext/run-event.schema.json +1 -0
- package/scripts/assignment-run.d.mts +1 -1
- package/scripts/assignment-run.mjs +44 -35
- package/scripts/check-generated.mjs +33 -9
- package/scripts/emit-codex-artifacts.mjs +255 -11
- package/scripts/harness-run.d.mts +12 -4
- package/scripts/harness-run.mjs +65 -17
- package/scripts/kxm-bump-version.mjs +146 -0
- package/scripts/kxm-hub.mjs +150 -2
- package/scripts/kxm-publish-npm.mjs +3 -1
- package/scripts/kxm-release-github.mjs +3 -1
- package/scripts/kxm.mjs +0 -0
- package/scripts/native-critic.d.mts +5 -0
- package/scripts/native-critic.mjs +60 -0
- package/.kxm/config/README.md +0 -5
- package/.kxm/config/agents.json +0 -43
- package/.kxm/config/env.example +0 -56
- package/.kxm/config/update.example.yaml +0 -9
- package/.kxm/config/workflows/fix.json +0 -160
- package/.kxm/config/workflows/jira-development.json +0 -116
- package/.kxm/config/workflows/provenance-quorum.json +0 -150
- package/.kxm/config/workflows/v04-dogfood.json +0 -72
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { lstat, mkdir, open, rename } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { VnextOneShotProcessResult } from "./vnext-oneshot-process.ts";
|
|
5
|
+
|
|
6
|
+
const TEXT_LIMIT = 4 * 1024 * 1024;
|
|
7
|
+
const RECORD_LIMIT = 16 * 1024 * 1024;
|
|
8
|
+
const digest = (value: string): string => `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
9
|
+
|
|
10
|
+
/** Private diagnostic evidence, never a peer reply or acceptance authority.
|
|
11
|
+
* Retained until operator archival; no automatic deletion of failed attempts.
|
|
12
|
+
* Each record is bounded; inability to reserve/write evidence fails closed.
|
|
13
|
+
*/
|
|
14
|
+
export async function beginOneShotEvidence(root: string, intent: {
|
|
15
|
+
runId: string; stepId: string; attemptId: string; assignmentId: string;
|
|
16
|
+
harness: string; provider: string; model: string; cwd: string;
|
|
17
|
+
command: string; args: readonly string[]; input?: string | undefined;
|
|
18
|
+
}, sensitive: readonly string[]): Promise<{
|
|
19
|
+
id: string;
|
|
20
|
+
finish(result: VnextOneShotProcessResult, observation: unknown): Promise<string>;
|
|
21
|
+
}> {
|
|
22
|
+
await mkdir(root, { recursive: true, mode: 0o700 });
|
|
23
|
+
const stat = await lstat(root);
|
|
24
|
+
if (!stat.isDirectory() || stat.isSymbolicLink()
|
|
25
|
+
|| (process.platform !== "win32" && ((stat.mode & 0o077) !== 0 || stat.uid !== process.getuid?.()))) {
|
|
26
|
+
throw new Error("oneshot_evidence_root_not_private");
|
|
27
|
+
}
|
|
28
|
+
const id = `evd_${randomUUID().replaceAll("-", "")}`;
|
|
29
|
+
const dir = join(root, id);
|
|
30
|
+
await mkdir(dir, { mode: 0o700 });
|
|
31
|
+
const secrets = [...new Set(sensitive.filter((value) => value.length >= 8))].sort((a, b) => b.length - a.length);
|
|
32
|
+
const redact = (value: string): string => {
|
|
33
|
+
let text = value;
|
|
34
|
+
for (const secret of secrets) text = text.replaceAll(secret, "[REDACTED]");
|
|
35
|
+
return text.replace(/(\b(?:authorization|api[_-]?key|access[_-]?token|refresh[_-]?token|password|secret)\b["']?\s*[:=]\s*["']?)(?:Bearer\s+)?[^\s"',;}]+/gi, "$1[REDACTED]");
|
|
36
|
+
};
|
|
37
|
+
const bounded = (value: string): { text: string; truncated: boolean; sha256: string } => {
|
|
38
|
+
const bytes = Buffer.from(redact(value));
|
|
39
|
+
return { text: bytes.subarray(0, TEXT_LIMIT).toString("utf8"), truncated: bytes.length > TEXT_LIMIT, sha256: digest(value) };
|
|
40
|
+
};
|
|
41
|
+
const write = async (name: string, value: unknown): Promise<string> => {
|
|
42
|
+
const current = await lstat(dir);
|
|
43
|
+
if (!current.isDirectory() || current.isSymbolicLink()
|
|
44
|
+
|| (process.platform !== "win32" && ((current.mode & 0o077) !== 0 || current.uid !== process.getuid?.()))) {
|
|
45
|
+
throw new Error("oneshot_evidence_directory_changed");
|
|
46
|
+
}
|
|
47
|
+
const bytes = JSON.stringify(value);
|
|
48
|
+
if (Buffer.byteLength(bytes) > RECORD_LIMIT) throw new Error("oneshot_evidence_record_limit");
|
|
49
|
+
const target = join(dir, name);
|
|
50
|
+
const partial = `${target}.partial`;
|
|
51
|
+
const file = await open(partial, "wx", 0o600);
|
|
52
|
+
try { await file.writeFile(bytes); await file.sync(); }
|
|
53
|
+
finally { await file.close(); }
|
|
54
|
+
await rename(partial, target);
|
|
55
|
+
if (process.platform !== "win32") {
|
|
56
|
+
const directory = await open(dir, "r");
|
|
57
|
+
try { await directory.sync(); } finally { await directory.close(); }
|
|
58
|
+
}
|
|
59
|
+
return digest(bytes);
|
|
60
|
+
};
|
|
61
|
+
await write("intent.json", {
|
|
62
|
+
schema: "kxm.oneshot-evidence.v1", id, recordedAt: new Date().toISOString(),
|
|
63
|
+
runId: intent.runId, stepId: intent.stepId, attemptId: intent.attemptId, assignmentId: intent.assignmentId,
|
|
64
|
+
harness: intent.harness, provider: intent.provider, requestedModel: intent.model,
|
|
65
|
+
cwd: intent.cwd, command: intent.command,
|
|
66
|
+
argv: bounded(JSON.stringify(intent.args)), stdin: intent.input === undefined ? null : bounded(intent.input),
|
|
67
|
+
acceptance: false,
|
|
68
|
+
});
|
|
69
|
+
let finished = false;
|
|
70
|
+
return {
|
|
71
|
+
id,
|
|
72
|
+
async finish(result, observation) {
|
|
73
|
+
if (finished) throw new Error("oneshot_evidence_already_finished");
|
|
74
|
+
finished = true;
|
|
75
|
+
return write("result.json", {
|
|
76
|
+
schema: "kxm.oneshot-evidence.v1", id, recordedAt: new Date().toISOString(),
|
|
77
|
+
stdout: bounded(result.stdout), stderr: bounded(result.stderr),
|
|
78
|
+
code: result.code, signal: result.signal ?? null, started: result.started ?? null,
|
|
79
|
+
observedChildExit: result.observedChildExit ?? null, terminationRequested: result.terminationRequested ?? null,
|
|
80
|
+
error: result.error ? bounded(result.error.message) : null,
|
|
81
|
+
observation: bounded(JSON.stringify(observation)), acceptance: false,
|
|
82
|
+
});
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export interface VnextOneShotProcessResult {
|
|
4
|
+
stdout: string;
|
|
5
|
+
stderr: string;
|
|
6
|
+
code: number | null;
|
|
7
|
+
signal?: NodeJS.Signals | null | undefined;
|
|
8
|
+
observedChildExit?: boolean | undefined;
|
|
9
|
+
started?: boolean | undefined;
|
|
10
|
+
/** Stop was requested; direct-child reaping cannot attest escaped descendants. */
|
|
11
|
+
terminationRequested?: boolean | undefined;
|
|
12
|
+
error?: Error | undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface VnextOneShotSpawnOptions {
|
|
16
|
+
cwd?: string | undefined;
|
|
17
|
+
env?: NodeJS.ProcessEnv | undefined;
|
|
18
|
+
input?: string | undefined;
|
|
19
|
+
timeoutMs?: number | undefined;
|
|
20
|
+
signal?: AbortSignal | undefined;
|
|
21
|
+
}
|
|
22
|
+
export type VnextOneShotSpawn = (command: string, args: readonly string[], options: VnextOneShotSpawnOptions) => Promise<VnextOneShotProcessResult>;
|
|
23
|
+
|
|
24
|
+
const OUTPUT_LIMIT = 8 * 1024 * 1024;
|
|
25
|
+
const KILL_GRACE_MS = 250;
|
|
26
|
+
const DRAIN_GRACE_MS = 500;
|
|
27
|
+
const REAP_GRACE_MS = 1000;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* POSIX process-group tree kill with fallback to direct child kill.
|
|
31
|
+
* Ensures orphaned subshells, test workers, and background daemons are pruned on cancellation/timeout.
|
|
32
|
+
*/
|
|
33
|
+
export function killProcessTree(child: ReturnType<typeof spawn>, signal: NodeJS.Signals = "SIGKILL"): void {
|
|
34
|
+
if (process.platform !== "win32" && child.pid) {
|
|
35
|
+
try {
|
|
36
|
+
process.kill(-child.pid, signal);
|
|
37
|
+
return;
|
|
38
|
+
} catch {
|
|
39
|
+
// Fall through to direct child kill if process group is unavailable
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
child.kill(signal);
|
|
44
|
+
} catch {
|
|
45
|
+
// Direct kill error if child already exited
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Bound both execution and pipe drain. Observing exit is not proof of descendant death. */
|
|
50
|
+
export function defaultSpawn(command: string, args: readonly string[], options: VnextOneShotSpawnOptions): Promise<VnextOneShotProcessResult> {
|
|
51
|
+
if (options.signal?.aborted) {
|
|
52
|
+
return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
|
|
53
|
+
}
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
const stdout: Buffer[] = [];
|
|
56
|
+
const stderr: Buffer[] = [];
|
|
57
|
+
let outputBytes = 0;
|
|
58
|
+
let outputChunks = 0;
|
|
59
|
+
let code: number | null = null;
|
|
60
|
+
let signal: NodeJS.Signals | null = null;
|
|
61
|
+
let observedChildExit = false;
|
|
62
|
+
let finished = false;
|
|
63
|
+
let stopping = false;
|
|
64
|
+
let closed = false;
|
|
65
|
+
let killSent = false;
|
|
66
|
+
let error: Error | undefined;
|
|
67
|
+
let wallTimer: ReturnType<typeof setTimeout> | undefined;
|
|
68
|
+
let killTimer: ReturnType<typeof setTimeout> | undefined;
|
|
69
|
+
let drainTimer: ReturnType<typeof setTimeout> | undefined;
|
|
70
|
+
let reapTimer: ReturnType<typeof setTimeout> | undefined;
|
|
71
|
+
let child: ReturnType<typeof spawn>;
|
|
72
|
+
const finish = (): void => {
|
|
73
|
+
if (finished) return;
|
|
74
|
+
finished = true;
|
|
75
|
+
for (const timer of [wallTimer, killTimer, drainTimer, reapTimer]) clearTimeout(timer);
|
|
76
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
77
|
+
child?.stdin?.destroy();
|
|
78
|
+
child?.stdout?.destroy();
|
|
79
|
+
child?.stderr?.destroy();
|
|
80
|
+
child?.unref();
|
|
81
|
+
const started = Boolean(child?.pid);
|
|
82
|
+
if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
83
|
+
resolve({ stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8"), code, signal, started, observedChildExit, terminationRequested: stopping, ...(error ? { error } : {}) });
|
|
84
|
+
};
|
|
85
|
+
const kill = (requested: NodeJS.Signals): void => {
|
|
86
|
+
killProcessTree(child, requested);
|
|
87
|
+
};
|
|
88
|
+
const stop = (reason: string): void => {
|
|
89
|
+
if (finished) return;
|
|
90
|
+
error ??= new Error(reason);
|
|
91
|
+
if (stopping) return;
|
|
92
|
+
stopping = true;
|
|
93
|
+
clearTimeout(wallTimer);
|
|
94
|
+
kill("SIGTERM");
|
|
95
|
+
killTimer = setTimeout(() => {
|
|
96
|
+
kill("SIGKILL");
|
|
97
|
+
killSent = true;
|
|
98
|
+
if (closed) { finish(); return; }
|
|
99
|
+
reapTimer = setTimeout(() => {
|
|
100
|
+
if (!observedChildExit) error ??= new Error("process_exit_unobserved");
|
|
101
|
+
finish();
|
|
102
|
+
}, REAP_GRACE_MS);
|
|
103
|
+
}, KILL_GRACE_MS);
|
|
104
|
+
};
|
|
105
|
+
const onAbort = (): void => stop("process_aborted");
|
|
106
|
+
try {
|
|
107
|
+
child = spawn(command, [...args], {
|
|
108
|
+
cwd: options.cwd,
|
|
109
|
+
env: options.env ?? process.env,
|
|
110
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
111
|
+
shell: false,
|
|
112
|
+
windowsHide: true,
|
|
113
|
+
detached: process.platform !== "win32",
|
|
114
|
+
});
|
|
115
|
+
} catch (cause) {
|
|
116
|
+
error = cause instanceof Error ? cause : new Error("process_spawn_failed");
|
|
117
|
+
finish();
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const collect = (target: Buffer[], chunk: Buffer | string): void => {
|
|
121
|
+
if (finished) return;
|
|
122
|
+
const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
123
|
+
const remaining = Math.max(0, OUTPUT_LIMIT - outputBytes);
|
|
124
|
+
if (remaining && outputChunks < 16384) {
|
|
125
|
+
const kept = bytes.subarray(0, remaining);
|
|
126
|
+
target.push(kept);
|
|
127
|
+
outputBytes += kept.length;
|
|
128
|
+
outputChunks++;
|
|
129
|
+
}
|
|
130
|
+
if (bytes.length > remaining || outputChunks >= 16384) stop("process_output_limit");
|
|
131
|
+
};
|
|
132
|
+
child.stdout!.on("data", (chunk: Buffer) => collect(stdout, chunk));
|
|
133
|
+
child.stderr!.on("data", (chunk: Buffer) => collect(stderr, chunk));
|
|
134
|
+
child.stdin!.on("error", () => stop("process_stdin_error"));
|
|
135
|
+
child.stdout!.on("error", () => stop("process_stdio_error"));
|
|
136
|
+
child.stderr!.on("error", () => stop("process_stdio_error"));
|
|
137
|
+
child.on("error", (cause) => {
|
|
138
|
+
error ??= cause;
|
|
139
|
+
if (!child.pid) finish();
|
|
140
|
+
else stop("process_error");
|
|
141
|
+
});
|
|
142
|
+
child.on("exit", (exitCode, exitSignal) => {
|
|
143
|
+
if (finished) return;
|
|
144
|
+
observedChildExit = true;
|
|
145
|
+
code = exitCode;
|
|
146
|
+
signal = exitSignal;
|
|
147
|
+
clearTimeout(wallTimer);
|
|
148
|
+
drainTimer = setTimeout(() => stop("process_stdio_unclosed"), DRAIN_GRACE_MS);
|
|
149
|
+
});
|
|
150
|
+
child.on("close", (exitCode, exitSignal) => {
|
|
151
|
+
if (finished) return;
|
|
152
|
+
closed = true;
|
|
153
|
+
code = exitCode;
|
|
154
|
+
signal = exitSignal;
|
|
155
|
+
// The leader can exit and close its pipes while a descendant ignores TERM.
|
|
156
|
+
// Never cancel the pending group escalation just because close arrived.
|
|
157
|
+
if (!stopping || killSent) finish();
|
|
158
|
+
});
|
|
159
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
160
|
+
if (options.signal?.aborted) onAbort();
|
|
161
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
162
|
+
if (!stopping) wallTimer = setTimeout(() => stop("process_timeout"), timeoutMs > 0 && Number.isFinite(timeoutMs) ? timeoutMs : 120_000);
|
|
163
|
+
try { child.stdin!.end(options.input); }
|
|
164
|
+
catch { stop("process_stdin_error"); }
|
|
165
|
+
});
|
|
166
|
+
}
|