@alpacakit/agents 0.1.0-beta.35 → 0.1.0-beta.36
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/README.md +25 -0
- package/dist/agents/claude.d.ts +9 -2
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +16 -5
- package/dist/agents/codex.d.ts +1 -1
- package/dist/agents/codex.js +1 -1
- package/dist/core/error.d.ts +25 -0
- package/dist/core/error.d.ts.map +1 -1
- package/dist/core/error.js +15 -0
- package/dist/core/node-controlled-exec.d.ts.map +1 -1
- package/dist/core/node-controlled-exec.js +31 -8
- package/dist/core/node-ports.d.ts.map +1 -1
- package/dist/core/node-ports.js +20 -0
- package/dist/core/ports.d.ts +5 -0
- package/dist/core/ports.d.ts.map +1 -1
- package/dist/fleet/policy.d.ts.map +1 -1
- package/dist/fleet/policy.js +3 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/controlled-process/host-controller.d.ts +3 -2
- package/dist/internal/controlled-process/host-controller.d.ts.map +1 -1
- package/dist/internal/controlled-process/host-controller.js +63 -23
- package/dist/internal/controlled-process/input-transport.d.ts +17 -0
- package/dist/internal/controlled-process/input-transport.d.ts.map +1 -0
- package/dist/internal/controlled-process/input-transport.js +188 -0
- package/dist/internal/controlled-process/lifecycle.d.ts +1 -1
- package/dist/internal/controlled-process/lifecycle.d.ts.map +1 -1
- package/dist/internal/controlled-process/lifecycle.js +1 -0
- package/dist/internal/controlled-process/schema.d.ts +2 -2
- package/dist/internal/controlled-process/schema.js +1 -1
- package/dist/internal/controlled-process/shim-runtime.d.ts.map +1 -1
- package/dist/internal/controlled-process/shim-runtime.js +43 -4
- package/dist/internal/controlled-process/supervisor-coordinator.d.ts.map +1 -1
- package/dist/internal/controlled-process/supervisor-coordinator.js +4 -2
- package/dist/internal/controlled-process/supervisor-model.d.ts +24 -2
- package/dist/internal/controlled-process/supervisor-model.d.ts.map +1 -1
- package/dist/internal/controlled-process/supervisor-runtime.d.ts.map +1 -1
- package/dist/internal/controlled-process/supervisor-runtime.js +116 -15
- package/dist/internal/controlled-process/supervisor-schema.d.ts +66 -3
- package/dist/internal/controlled-process/supervisor-schema.d.ts.map +1 -1
- package/dist/internal/controlled-process/supervisor-schema.js +31 -3
- package/dist/internal/controlled-process/supervisor-values.d.ts +5 -0
- package/dist/internal/controlled-process/supervisor-values.d.ts.map +1 -1
- package/dist/internal/controlled-process/supervisor-values.js +5 -0
- package/dist/internal/controlled-process/values.d.ts +4 -4
- package/dist/internal/controlled-process/values.js +4 -4
- package/dist/structured/run-structured.d.ts.map +1 -1
- package/dist/structured/run-structured.js +2 -5
- package/package.json +2 -2
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
// biome-ignore-all lint/nursery/noExcessiveLinesPerFile: Section 5.103 confines the correction to this existing host lifecycle owner.
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { CHILD_PROCESS_ABSENCE_PROOF, CHILD_PROCESS_TERMINATION_EVIDENCE_KIND, CHILD_PROCESS_TERMINATION_KIND, CHILD_PROCESS_UNKILLABLE_REASON, } from "../../core/controlled-process.js";
|
|
4
|
+
import { CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY } from "../../core/error.js";
|
|
4
5
|
import { readFramedRecords, readRequiredFrame, writeFramedRecord, } from "./framed-transport.js";
|
|
6
|
+
import { ControlledProcessLaunchError, workerInputManifest, writeWorkerInput, } from "./input-transport.js";
|
|
5
7
|
import { transitionControlledProcess } from "./lifecycle.js";
|
|
6
8
|
import { controlledProcessRecordMatchesToken, signControlledProcessRecord, verifySignedControlledProcessRecord, } from "./record.js";
|
|
7
9
|
import { assertSecureRuntimeDirectory, resolveSupervisorSocketNamespace, SignedRecordStore, supervisorSocketPath, } from "./record-store.js";
|
|
@@ -14,6 +16,7 @@ import { decodeProcessStartToken } from "./token.js";
|
|
|
14
16
|
import { CONTROLLED_PROCESS_STATE } from "./values.js";
|
|
15
17
|
const HOST_BOOTSTRAP_FD = 3;
|
|
16
18
|
const HOST_IDENTITY_FD = 4;
|
|
19
|
+
const HOST_INPUT_FD = 5;
|
|
17
20
|
const OUTPUT_QUEUE_HIGH_WATER_BYTES = 64 * 1024;
|
|
18
21
|
const OUTPUT_QUEUE_LOW_WATER_BYTES = 32 * 1024;
|
|
19
22
|
const PRE_READY_CLEANUP_AUTHORITY = {
|
|
@@ -29,8 +32,9 @@ const TERMINATION_OBSERVATION_SOURCE = {
|
|
|
29
32
|
supervisorClose: "supervisor_close",
|
|
30
33
|
offlineFallback: "offline_fallback",
|
|
31
34
|
};
|
|
32
|
-
class SupervisorReportedLaunchFailure extends
|
|
35
|
+
class SupervisorReportedLaunchFailure extends ControlledProcessLaunchError {
|
|
33
36
|
}
|
|
37
|
+
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: Launch, readiness, and pre-ready cleanup share one custody scope.
|
|
34
38
|
export async function launchControlledProcess(input) {
|
|
35
39
|
const platform = createSupervisorPlatformAdapter();
|
|
36
40
|
const runtimeDirectory = await assertSecureRuntimeDirectory(input.runtimeDirectory, platform.uid);
|
|
@@ -39,6 +43,7 @@ export async function launchControlledProcess(input) {
|
|
|
39
43
|
void supervisorClose.promise.catch(() => undefined);
|
|
40
44
|
const control = supervisor.stdio[HOST_BOOTSTRAP_FD];
|
|
41
45
|
const identityStream = supervisor.stdio[HOST_IDENTITY_FD];
|
|
46
|
+
const inputStream = supervisor.stdio.at(HOST_INPUT_FD);
|
|
42
47
|
const identityFrames = readFramedRecords(identityStream)[Symbol.asyncIterator]();
|
|
43
48
|
let output = null;
|
|
44
49
|
let identity = null;
|
|
@@ -50,29 +55,52 @@ export async function launchControlledProcess(input) {
|
|
|
50
55
|
kind: SUPERVISOR_BOOTSTRAP_FRAME_KIND.bootstrap,
|
|
51
56
|
runtimeDirectory,
|
|
52
57
|
shimEntryPath: input.shimEntryPath,
|
|
53
|
-
worker:
|
|
58
|
+
worker: {
|
|
59
|
+
command: input.worker.command,
|
|
60
|
+
args: input.worker.args,
|
|
61
|
+
cwd: input.worker.cwd,
|
|
62
|
+
env: input.worker.env,
|
|
63
|
+
},
|
|
64
|
+
input: workerInputManifest(input.worker.stdin),
|
|
54
65
|
timing: input.timing,
|
|
55
66
|
});
|
|
67
|
+
const inputTransfer = writeWorkerInput(inputStream, input.worker.stdin);
|
|
68
|
+
void inputTransfer.catch(() => undefined);
|
|
56
69
|
identity = await readSupervisorIdentity(identityFrames);
|
|
57
|
-
let
|
|
58
|
-
const readinessObservation = observeSupervisorReadiness(identityFrames, () =>
|
|
70
|
+
let hostAckWriteStarted = false;
|
|
71
|
+
const readinessObservation = observeSupervisorReadiness(identityFrames, () => hostAckWriteStarted);
|
|
59
72
|
await input.lifecycle.beforeExec(identity);
|
|
60
73
|
descriptorCommitted = true;
|
|
74
|
+
hostAckWriteStarted = true;
|
|
61
75
|
await writeFramedRecord(control, {
|
|
62
76
|
kind: SUPERVISOR_BOOTSTRAP_FRAME_KIND.hostAck,
|
|
63
77
|
});
|
|
64
|
-
hostAckComplete = true;
|
|
65
78
|
control.end();
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
const [inputSettlement, readinessSettlement] = await Promise.allSettled([
|
|
80
|
+
inputTransfer,
|
|
81
|
+
concludeSupervisorReadiness({
|
|
82
|
+
observation: readinessObservation,
|
|
83
|
+
identity,
|
|
84
|
+
runtimeDirectory,
|
|
85
|
+
supervisorClose,
|
|
86
|
+
timing: input.timing,
|
|
87
|
+
}),
|
|
88
|
+
]);
|
|
89
|
+
if (readinessSettlement.status === "rejected") {
|
|
90
|
+
throw readinessSettlement.reason;
|
|
91
|
+
}
|
|
92
|
+
const readiness = readinessSettlement.value;
|
|
73
93
|
if (readiness.kind === "failure") {
|
|
74
94
|
cleanupAuthority = readiness.cleanupAuthority;
|
|
75
|
-
|
|
95
|
+
if (readiness.error instanceof SupervisorReportedLaunchFailure) {
|
|
96
|
+
throw readiness.error;
|
|
97
|
+
}
|
|
98
|
+
throw inputSettlement.status === "rejected"
|
|
99
|
+
? inputSettlement.reason
|
|
100
|
+
: readiness.error;
|
|
101
|
+
}
|
|
102
|
+
if (inputSettlement.status === "rejected") {
|
|
103
|
+
throw inputSettlement.reason;
|
|
76
104
|
}
|
|
77
105
|
const readyIdentity = identity;
|
|
78
106
|
let terminationRequested = false;
|
|
@@ -101,12 +129,14 @@ export async function launchControlledProcess(input) {
|
|
|
101
129
|
};
|
|
102
130
|
}
|
|
103
131
|
catch (primaryFailure) {
|
|
104
|
-
if (primaryFailure instanceof SupervisorReportedLaunchFailure
|
|
132
|
+
if (primaryFailure instanceof SupervisorReportedLaunchFailure &&
|
|
133
|
+
identity === null) {
|
|
105
134
|
cleanupAuthority = PRE_READY_CLEANUP_AUTHORITY.reportedFailure;
|
|
106
135
|
}
|
|
107
136
|
try {
|
|
108
137
|
await cleanupPreReadySupervisor({
|
|
109
138
|
control,
|
|
139
|
+
inputStream,
|
|
110
140
|
identity,
|
|
111
141
|
descriptorCommitted,
|
|
112
142
|
runtimeDirectory,
|
|
@@ -118,26 +148,32 @@ export async function launchControlledProcess(input) {
|
|
|
118
148
|
});
|
|
119
149
|
}
|
|
120
150
|
catch (cleanupFailure) {
|
|
151
|
+
if (primaryFailure instanceof ControlledProcessLaunchError) {
|
|
152
|
+
throw new ControlledProcessLaunchError({
|
|
153
|
+
...primaryFailure.failure,
|
|
154
|
+
retryability: CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY.permanent,
|
|
155
|
+
}, new AggregateError([primaryFailure, cleanupFailure], `Controlled process launch and pre-ready cleanup both failed (${cleanupAuthority}).`));
|
|
156
|
+
}
|
|
121
157
|
throw new AggregateError([primaryFailure, cleanupFailure], "Controlled process launch and pre-ready cleanup both failed.");
|
|
122
158
|
}
|
|
123
159
|
throw primaryFailure;
|
|
124
160
|
}
|
|
125
161
|
}
|
|
126
|
-
function observeSupervisorReadiness(frames,
|
|
162
|
+
function observeSupervisorReadiness(frames, hostAckWriteStarted) {
|
|
127
163
|
return frames.next().then((result) => ({
|
|
128
164
|
kind: "result",
|
|
129
165
|
result,
|
|
130
|
-
|
|
166
|
+
observedBeforeHostAckWrite: !hostAckWriteStarted(),
|
|
131
167
|
}), (error) => ({
|
|
132
168
|
kind: "failure",
|
|
133
169
|
error,
|
|
134
|
-
|
|
170
|
+
observedBeforeHostAckWrite: !hostAckWriteStarted(),
|
|
135
171
|
}));
|
|
136
172
|
}
|
|
137
173
|
async function concludeSupervisorReadiness(input) {
|
|
138
174
|
const observation = await input.observation;
|
|
139
|
-
if (observation.
|
|
140
|
-
return readinessFailure(new Error("Supervisor readiness arrived before host ACK
|
|
175
|
+
if (observation.observedBeforeHostAckWrite) {
|
|
176
|
+
return readinessFailure(new Error("Supervisor readiness arrived before the host ACK write began."), PRE_READY_CLEANUP_AUTHORITY.unprobed);
|
|
141
177
|
}
|
|
142
178
|
if (observation.kind === "failure") {
|
|
143
179
|
return readinessFailure(observation.error, PRE_READY_CLEANUP_AUTHORITY.unprobed);
|
|
@@ -145,7 +181,7 @@ async function concludeSupervisorReadiness(input) {
|
|
|
145
181
|
if (!observation.result.done) {
|
|
146
182
|
const failure = supervisorLaunchFailedFrameSchema.safeParse(observation.result.value);
|
|
147
183
|
if (failure.success) {
|
|
148
|
-
return readinessFailure(new
|
|
184
|
+
return readinessFailure(new SupervisorReportedLaunchFailure(failure.data.failure), PRE_READY_CLEANUP_AUTHORITY.live);
|
|
149
185
|
}
|
|
150
186
|
return readinessFailure(new Error("Supervisor readiness frame is invalid."), PRE_READY_CLEANUP_AUTHORITY.unprobed);
|
|
151
187
|
}
|
|
@@ -209,6 +245,9 @@ async function cleanupPreReadySupervisor(input) {
|
|
|
209
245
|
if (!input.control.destroyed && !input.control.writableEnded) {
|
|
210
246
|
input.control.end();
|
|
211
247
|
}
|
|
248
|
+
if (!input.inputStream.destroyed && !input.inputStream.writableEnded) {
|
|
249
|
+
input.inputStream.destroy();
|
|
250
|
+
}
|
|
212
251
|
if (input.output !== null) {
|
|
213
252
|
await input.output.queue[Symbol.asyncIterator]().return?.();
|
|
214
253
|
}
|
|
@@ -377,7 +416,8 @@ async function raceSupervisorRequestWithClose(input) {
|
|
|
377
416
|
return result;
|
|
378
417
|
}
|
|
379
418
|
function preReadyCleanupDeadline(timing) {
|
|
380
|
-
const budget = Math.min(Number.MAX_SAFE_INTEGER, timing.termGraceMs + timing.killProbeTimeoutMs + timing.probeIntervalMs)
|
|
419
|
+
const budget = Math.min(Number.MAX_SAFE_INTEGER, (timing.termGraceMs + timing.killProbeTimeoutMs + timing.probeIntervalMs) *
|
|
420
|
+
2);
|
|
381
421
|
return Math.min(Number.MAX_SAFE_INTEGER, Date.now() + budget);
|
|
382
422
|
}
|
|
383
423
|
function beforeDeadline(operation, deadline, message) {
|
|
@@ -716,7 +756,7 @@ function spawnSupervisor(entryPath) {
|
|
|
716
756
|
const child = spawn(process.execPath, [entryPath], {
|
|
717
757
|
detached: true,
|
|
718
758
|
env: {},
|
|
719
|
-
stdio: ["ignore", "pipe", "pipe", "pipe", "pipe"],
|
|
759
|
+
stdio: ["ignore", "pipe", "pipe", "pipe", "pipe", "pipe"],
|
|
720
760
|
});
|
|
721
761
|
containSupervisorStdioErrors(child);
|
|
722
762
|
child.once("error", () => { });
|
|
@@ -777,7 +817,7 @@ async function readSupervisorIdentity(frames) {
|
|
|
777
817
|
}
|
|
778
818
|
const failure = supervisorLaunchFailedFrameSchema.safeParse(frame);
|
|
779
819
|
if (failure.success) {
|
|
780
|
-
throw new SupervisorReportedLaunchFailure(failure.data.
|
|
820
|
+
throw new SupervisorReportedLaunchFailure(failure.data.failure);
|
|
781
821
|
}
|
|
782
822
|
throw new Error("Supervisor identity frame is invalid.");
|
|
783
823
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Readable, Writable } from "node:stream";
|
|
2
|
+
import type { ControlledProcessLaunchFailure, WorkerInputManifest } from "./supervisor-model.js";
|
|
3
|
+
export declare class ControlledProcessLaunchError extends Error {
|
|
4
|
+
readonly failure: ControlledProcessLaunchFailure;
|
|
5
|
+
constructor(failure: ControlledProcessLaunchFailure, cause?: unknown);
|
|
6
|
+
}
|
|
7
|
+
export declare function workerInputManifest(input: string | null): WorkerInputManifest;
|
|
8
|
+
export declare function writeWorkerInput(writable: Writable, input: string | null): Promise<void>;
|
|
9
|
+
export declare function transferWorkerInput(input: {
|
|
10
|
+
readonly readable: Readable;
|
|
11
|
+
readonly writable: Writable;
|
|
12
|
+
readonly manifest: WorkerInputManifest;
|
|
13
|
+
readonly stage: ControlledProcessLaunchFailure["stage"];
|
|
14
|
+
readonly timeoutMs: number;
|
|
15
|
+
}): Promise<void>;
|
|
16
|
+
export declare function launchFailure(code: ControlledProcessLaunchFailure["code"], stage: ControlledProcessLaunchFailure["stage"], expectedBytes: number | null, actualBytes: number | null, message: string, cause?: unknown): ControlledProcessLaunchError;
|
|
17
|
+
//# sourceMappingURL=input-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"input-transport.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/input-transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAKtD,OAAO,KAAK,EACV,8BAA8B,EAC9B,mBAAmB,EACpB,MAAM,uBAAuB,CAAC;AAK/B,qBAAa,4BAA6B,SAAQ,KAAK;IACrD,QAAQ,CAAC,OAAO,EAAE,8BAA8B,CAAC;gBAErC,OAAO,EAAE,8BAA8B,EAAE,KAAK,CAAC,EAAE,OAAO;CAKrE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,mBAAmB,CAU7E;AAED,wBAAsB,gBAAgB,CACpC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GACnB,OAAO,CAAC,IAAI,CAAC,CAiBf;AAED,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,8BAA8B,CAAC,OAAO,CAAC,CAAC;IACxD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,GAAG,OAAO,CAAC,IAAI,CAAC,CAuGhB;AAED,wBAAgB,aAAa,CAC3B,IAAI,EAAE,8BAA8B,CAAC,MAAM,CAAC,EAC5C,KAAK,EAAE,8BAA8B,CAAC,OAAO,CAAC,EAC9C,aAAa,EAAE,MAAM,GAAG,IAAI,EAC5B,WAAW,EAAE,MAAM,GAAG,IAAI,EAC1B,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,GACd,4BAA4B,CAW9B"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE, CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY, } from "../../core/error.js";
|
|
3
|
+
import { WORKER_INPUT_KIND } from "./supervisor-values.js";
|
|
4
|
+
const SHA256_HEX_PATTERN = /^[a-f0-9]{64}$/u;
|
|
5
|
+
export class ControlledProcessLaunchError extends Error {
|
|
6
|
+
failure;
|
|
7
|
+
constructor(failure, cause) {
|
|
8
|
+
super(failure.message, cause === undefined ? undefined : { cause });
|
|
9
|
+
this.name = "ControlledProcessLaunchError";
|
|
10
|
+
this.failure = failure;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function workerInputManifest(input) {
|
|
14
|
+
if (input === null) {
|
|
15
|
+
return { kind: WORKER_INPUT_KIND.none };
|
|
16
|
+
}
|
|
17
|
+
const bytes = Buffer.from(input, "utf8");
|
|
18
|
+
return {
|
|
19
|
+
kind: WORKER_INPUT_KIND.stream,
|
|
20
|
+
byteLength: bytes.byteLength,
|
|
21
|
+
sha256: createHash("sha256").update(bytes).digest("hex"),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export async function writeWorkerInput(writable, input) {
|
|
25
|
+
const bytes = input === null ? Buffer.alloc(0) : Buffer.from(input, "utf8");
|
|
26
|
+
try {
|
|
27
|
+
if (bytes.byteLength > 0) {
|
|
28
|
+
await writeChunk(writable, bytes);
|
|
29
|
+
}
|
|
30
|
+
await endWritable(writable);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
throw launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputWriteFailed, "host_to_supervisor", bytes.byteLength, null, "Worker input data channel failed while writing.", error);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function transferWorkerInput(input) {
|
|
37
|
+
const expectedBytes = input.manifest.kind === WORKER_INPUT_KIND.none
|
|
38
|
+
? 0
|
|
39
|
+
: input.manifest.byteLength;
|
|
40
|
+
const expectedDigest = input.manifest.kind === WORKER_INPUT_KIND.none
|
|
41
|
+
? null
|
|
42
|
+
: input.manifest.sha256;
|
|
43
|
+
let actualBytes = 0;
|
|
44
|
+
const digest = createHash("sha256");
|
|
45
|
+
let timeout = null;
|
|
46
|
+
let timeoutFailure = null;
|
|
47
|
+
const transfer = transferWorkerInputBytes();
|
|
48
|
+
void transfer.catch(() => undefined);
|
|
49
|
+
try {
|
|
50
|
+
await Promise.race([
|
|
51
|
+
transfer,
|
|
52
|
+
new Promise((_resolve, reject) => {
|
|
53
|
+
timeout = setTimeout(() => {
|
|
54
|
+
timeoutFailure = launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputTransferTimeout, input.stage, expectedBytes, actualBytes, "Worker input transfer exceeded its launch deadline.");
|
|
55
|
+
input.readable.destroy();
|
|
56
|
+
input.writable.destroy();
|
|
57
|
+
reject(timeoutFailure);
|
|
58
|
+
}, input.timeoutMs);
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (timeoutFailure !== null) {
|
|
64
|
+
throw timeoutFailure;
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
if (timeout !== null) {
|
|
70
|
+
clearTimeout(timeout);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async function transferWorkerInputBytes() {
|
|
74
|
+
try {
|
|
75
|
+
for await (const rawChunk of input.readable) {
|
|
76
|
+
const chunk = Buffer.isBuffer(rawChunk)
|
|
77
|
+
? rawChunk
|
|
78
|
+
: Buffer.from(String(rawChunk), "utf8");
|
|
79
|
+
actualBytes += chunk.byteLength;
|
|
80
|
+
if (actualBytes > expectedBytes) {
|
|
81
|
+
throw launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputOverrun, input.stage, expectedBytes, actualBytes, "Worker input contained bytes beyond its declared length.");
|
|
82
|
+
}
|
|
83
|
+
digest.update(chunk);
|
|
84
|
+
if (chunk.byteLength > 0) {
|
|
85
|
+
await writeChunk(input.writable, chunk);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
if (actualBytes !== expectedBytes) {
|
|
89
|
+
throw launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputEarlyEof, input.stage, expectedBytes, actualBytes, "Worker input ended before its declared length.");
|
|
90
|
+
}
|
|
91
|
+
if (expectedDigest !== null &&
|
|
92
|
+
(!SHA256_HEX_PATTERN.test(expectedDigest) ||
|
|
93
|
+
digest.digest("hex") !== expectedDigest)) {
|
|
94
|
+
throw launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputDigestMismatch, input.stage, expectedBytes, actualBytes, "Worker input did not match its declared SHA-256 digest.");
|
|
95
|
+
}
|
|
96
|
+
await endWritable(input.writable);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
input.writable.destroy();
|
|
100
|
+
if (error instanceof ControlledProcessLaunchError) {
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
throw launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputWriteFailed, input.stage, expectedBytes, actualBytes, "Worker input data channel failed during transfer.", error);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function launchFailure(code, stage, expectedBytes, actualBytes, message, cause) {
|
|
108
|
+
const retryability = code === CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputWriteFailed ||
|
|
109
|
+
code === CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputTransferTimeout ||
|
|
110
|
+
code === CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.workerExited
|
|
111
|
+
? CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY.transient
|
|
112
|
+
: CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY.permanent;
|
|
113
|
+
return new ControlledProcessLaunchError({ code, retryability, stage, expectedBytes, actualBytes, message }, cause);
|
|
114
|
+
}
|
|
115
|
+
function writeChunk(writable, chunk) {
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
let settled = false;
|
|
118
|
+
const cleanup = () => {
|
|
119
|
+
writable.off("error", failed);
|
|
120
|
+
writable.off("close", closed);
|
|
121
|
+
};
|
|
122
|
+
const settle = (error) => {
|
|
123
|
+
if (settled)
|
|
124
|
+
return;
|
|
125
|
+
settled = true;
|
|
126
|
+
cleanup();
|
|
127
|
+
if (error === null) {
|
|
128
|
+
resolve();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
reject(error);
|
|
132
|
+
};
|
|
133
|
+
const failed = (error) => settle(error);
|
|
134
|
+
const closed = () => settle(new Error("Worker input writable closed during write."));
|
|
135
|
+
writable.once("error", failed);
|
|
136
|
+
writable.once("close", closed);
|
|
137
|
+
if (writable.destroyed || writable.closed || writable.writableEnded) {
|
|
138
|
+
closed();
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
writable.write(chunk, (error) => settle(error ?? null));
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
settle(error instanceof Error
|
|
146
|
+
? error
|
|
147
|
+
: new Error("Worker input writable write failed."));
|
|
148
|
+
}
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
function endWritable(writable) {
|
|
152
|
+
if (writable.destroyed || writable.closed) {
|
|
153
|
+
return Promise.reject(new Error("Worker input writable closed before end settlement."));
|
|
154
|
+
}
|
|
155
|
+
if (writable.writableFinished) {
|
|
156
|
+
return Promise.resolve();
|
|
157
|
+
}
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
let settled = false;
|
|
160
|
+
const cleanup = () => {
|
|
161
|
+
writable.off("error", failed);
|
|
162
|
+
writable.off("close", closed);
|
|
163
|
+
};
|
|
164
|
+
const settle = (error) => {
|
|
165
|
+
if (settled)
|
|
166
|
+
return;
|
|
167
|
+
settled = true;
|
|
168
|
+
cleanup();
|
|
169
|
+
if (error === null) {
|
|
170
|
+
resolve();
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
reject(error);
|
|
174
|
+
};
|
|
175
|
+
const failed = (error) => settle(error);
|
|
176
|
+
const closed = () => settle(new Error("Worker input writable closed before end settlement."));
|
|
177
|
+
writable.once("error", failed);
|
|
178
|
+
writable.once("close", closed);
|
|
179
|
+
try {
|
|
180
|
+
writable.end((error) => settle(error ?? null));
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
settle(error instanceof Error
|
|
184
|
+
? error
|
|
185
|
+
: new Error("Worker input writable end failed."));
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ControlledProcessState } from "./model.js";
|
|
2
|
-
export declare const CONTROLLED_PROCESS_TRANSITIONS: readonly [readonly ["reserved", "acked"], readonly ["reserved", "absent"], readonly ["acked", "running"], readonly ["acked", "absent"], readonly ["running", "term"], readonly ["running", "absent"], readonly ["term", "kill_sent"], readonly ["term", "absent"], readonly ["kill_sent", "absent"], readonly ["absent", "released"]];
|
|
2
|
+
export declare const CONTROLLED_PROCESS_TRANSITIONS: readonly [readonly ["reserved", "acked"], readonly ["reserved", "absent"], readonly ["acked", "running"], readonly ["acked", "term"], readonly ["acked", "absent"], readonly ["running", "term"], readonly ["running", "absent"], readonly ["term", "kill_sent"], readonly ["term", "absent"], readonly ["kill_sent", "absent"], readonly ["absent", "released"]];
|
|
3
3
|
export declare function isControlledProcessTransitionAllowed(from: ControlledProcessState, to: ControlledProcessState): boolean;
|
|
4
4
|
export declare function transitionControlledProcess(from: ControlledProcessState, to: ControlledProcessState): ControlledProcessState;
|
|
5
5
|
//# sourceMappingURL=lifecycle.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAQzD,eAAO,MAAM,8BAA8B,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAQzD,eAAO,MAAM,8BAA8B,mWAYgB,CAAC;AAE5D,wBAAgB,oCAAoC,CAClD,IAAI,EAAE,sBAAsB,EAC5B,EAAE,EAAE,sBAAsB,GACzB,OAAO,CAIT;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,sBAAsB,EAC5B,EAAE,EAAE,sBAAsB,GACzB,sBAAsB,CAKxB"}
|
|
@@ -3,6 +3,7 @@ export const CONTROLLED_PROCESS_TRANSITIONS = [
|
|
|
3
3
|
[CONTROLLED_PROCESS_STATE.reserved, CONTROLLED_PROCESS_STATE.acked],
|
|
4
4
|
[CONTROLLED_PROCESS_STATE.reserved, CONTROLLED_PROCESS_STATE.absent],
|
|
5
5
|
[CONTROLLED_PROCESS_STATE.acked, CONTROLLED_PROCESS_STATE.running],
|
|
6
|
+
[CONTROLLED_PROCESS_STATE.acked, CONTROLLED_PROCESS_STATE.term],
|
|
6
7
|
[CONTROLLED_PROCESS_STATE.acked, CONTROLLED_PROCESS_STATE.absent],
|
|
7
8
|
[CONTROLLED_PROCESS_STATE.running, CONTROLLED_PROCESS_STATE.term],
|
|
8
9
|
[CONTROLLED_PROCESS_STATE.running, CONTROLLED_PROCESS_STATE.absent],
|
|
@@ -30,7 +30,7 @@ export declare const controlledProcessIdentitySchema: z.ZodReadonly<z.ZodObject<
|
|
|
30
30
|
processStartToken: z.ZodPipe<z.ZodString, z.ZodTransform<ProcessStartToken, string>>;
|
|
31
31
|
}, z.core.$strip>>;
|
|
32
32
|
export declare const controlledProcessRecordSchema: z.ZodReadonly<z.ZodObject<{
|
|
33
|
-
protocolVersion: z.ZodLiteral<"alpacakit-supervisor-
|
|
33
|
+
protocolVersion: z.ZodLiteral<"alpacakit-supervisor-v2">;
|
|
34
34
|
platform: z.ZodEnum<{
|
|
35
35
|
darwin: "darwin";
|
|
36
36
|
linux: "linux";
|
|
@@ -60,7 +60,7 @@ export declare const controlledProcessRecordSchema: z.ZodReadonly<z.ZodObject<{
|
|
|
60
60
|
}, z.core.$strip>>;
|
|
61
61
|
export declare const signedControlledProcessRecordSchema: z.ZodReadonly<z.ZodObject<{
|
|
62
62
|
record: z.ZodReadonly<z.ZodObject<{
|
|
63
|
-
protocolVersion: z.ZodLiteral<"alpacakit-supervisor-
|
|
63
|
+
protocolVersion: z.ZodLiteral<"alpacakit-supervisor-v2">;
|
|
64
64
|
platform: z.ZodEnum<{
|
|
65
65
|
darwin: "darwin";
|
|
66
66
|
linux: "linux";
|
|
@@ -5,7 +5,7 @@ import { SUPERVISOR_CONVERGENCE_REASON } from "./supervisor-values.js";
|
|
|
5
5
|
import { CONTROLLED_PROCESS_PLATFORM, CONTROLLED_PROCESS_PROTOCOL_VERSION, CONTROLLED_PROCESS_STATE, SUPERVISOR_AUTHENTICATION_FRAME_KIND, } from "./values.js";
|
|
6
6
|
const LOWER_HEX_16_BYTE_PATTERN = /^[0-9a-f]{32}$/u;
|
|
7
7
|
const LOWER_HEX_32_BYTE_PATTERN = /^[0-9a-f]{64}$/u;
|
|
8
|
-
const PROCESS_START_TOKEN_PATTERN = /^alpacakit-supervisor-
|
|
8
|
+
const PROCESS_START_TOKEN_PATTERN = /^alpacakit-supervisor-v2:(?:darwin|linux):[0-9a-f]{32}:[0-9a-f]{32}:[0-9a-f]{32}:[0-9a-f]{32}:[0-9a-f]{64}$/u;
|
|
9
9
|
const lowerHex16ByteSchema = z.string().regex(LOWER_HEX_16_BYTE_PATTERN);
|
|
10
10
|
export const lowerHex32ByteSchema = z.string().regex(LOWER_HEX_32_BYTE_PATTERN);
|
|
11
11
|
export const controlledProcessPlatformSchema = stringValueMapSchema(CONTROLLED_PROCESS_PLATFORM);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shim-runtime.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/shim-runtime.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shim-runtime.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/shim-runtime.ts"],"names":[],"mappings":"AAuBA,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC,CAiF9D"}
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
|
+
import { CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE } from "../../core/error.js";
|
|
2
3
|
import { readableFd, writableFd } from "./child-fd.js";
|
|
3
4
|
import { readFramedRecords, readRequiredFrame, writeFramedRecord, } from "./framed-transport.js";
|
|
5
|
+
import { ControlledProcessLaunchError, launchFailure, transferWorkerInput, } from "./input-transport.js";
|
|
4
6
|
import { shimAckSchema, shimBootstrapSchema } from "./supervisor-schema.js";
|
|
5
7
|
import { SHIM_FRAME_KIND } from "./supervisor-values.js";
|
|
6
8
|
const SHIM_CONTROL_FD = 3;
|
|
7
9
|
const SHIM_STATUS_FD = 4;
|
|
10
|
+
const SHIM_INPUT_FD = 5;
|
|
8
11
|
const SPAWN_FAILURE_EXIT_CODE = 127;
|
|
9
12
|
const KEEPALIVE_INTERVAL_MS = 60_000;
|
|
10
13
|
export async function runControlledProcessShim() {
|
|
11
14
|
const control = readableFd(SHIM_CONTROL_FD);
|
|
12
15
|
const status = writableFd(SHIM_STATUS_FD);
|
|
16
|
+
const input = readableFd(SHIM_INPUT_FD);
|
|
13
17
|
const frames = readFramedRecords(control)[Symbol.asyncIterator]();
|
|
14
18
|
const bootstrap = shimBootstrapSchema.parse(await readRequiredFrame(frames));
|
|
15
19
|
try {
|
|
@@ -18,7 +22,12 @@ export async function runControlledProcessShim() {
|
|
|
18
22
|
catch {
|
|
19
23
|
return;
|
|
20
24
|
}
|
|
21
|
-
|
|
25
|
+
let exitFailedLaunchOnSigterm = false;
|
|
26
|
+
process.on("SIGTERM", () => {
|
|
27
|
+
if (exitFailedLaunchOnSigterm) {
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
22
31
|
const worker = spawn(bootstrap.worker.command, [...bootstrap.worker.args], {
|
|
23
32
|
cwd: bootstrap.worker.cwd,
|
|
24
33
|
env: bootstrap.worker.env,
|
|
@@ -28,10 +37,34 @@ export async function runControlledProcessShim() {
|
|
|
28
37
|
});
|
|
29
38
|
const workerExit = waitForWorkerExit(worker);
|
|
30
39
|
worker.stdin.on("error", () => { });
|
|
31
|
-
|
|
32
|
-
|
|
40
|
+
const delivery = transferWorkerInput({
|
|
41
|
+
readable: input,
|
|
42
|
+
writable: worker.stdin,
|
|
43
|
+
manifest: bootstrap.input,
|
|
44
|
+
stage: "shim_to_worker",
|
|
45
|
+
timeoutMs: bootstrap.inputTimeoutMs,
|
|
46
|
+
});
|
|
47
|
+
void delivery.catch(() => undefined);
|
|
48
|
+
const launch = await Promise.race([
|
|
49
|
+
delivery.then(() => ({ kind: "delivered" }), (error) => ({ kind: "failed", error })),
|
|
50
|
+
workerExit.then((exit) => ({ kind: "exited", exit })),
|
|
51
|
+
]);
|
|
52
|
+
if (launch.kind === "exited") {
|
|
53
|
+
const failure = launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.workerExited, "shim_to_worker", bootstrap.input.kind === "stream" ? bootstrap.input.byteLength : 0, null, `Worker exited during input delivery with code ${launch.exit.code ?? "null"}.`);
|
|
54
|
+
exitFailedLaunchOnSigterm = true;
|
|
55
|
+
await reportLaunchFailure(status, failure);
|
|
56
|
+
setInterval(() => { }, KEEPALIVE_INTERVAL_MS);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (launch.kind === "failed") {
|
|
60
|
+
const { error } = launch;
|
|
61
|
+
exitFailedLaunchOnSigterm = true;
|
|
62
|
+
await reportLaunchFailure(status, error instanceof ControlledProcessLaunchError
|
|
63
|
+
? error
|
|
64
|
+
: launchFailure(CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE.inputWriteFailed, "shim_to_worker", bootstrap.input.kind === "stream" ? bootstrap.input.byteLength : 0, null, "Worker stdin delivery failed.", error));
|
|
65
|
+
setInterval(() => { }, KEEPALIVE_INTERVAL_MS);
|
|
66
|
+
return;
|
|
33
67
|
}
|
|
34
|
-
worker.stdin.end();
|
|
35
68
|
await writeFramedRecord(status, { kind: SHIM_FRAME_KIND.running });
|
|
36
69
|
const exit = await workerExit;
|
|
37
70
|
await writeFramedRecord(status, {
|
|
@@ -41,6 +74,12 @@ export async function runControlledProcessShim() {
|
|
|
41
74
|
});
|
|
42
75
|
setInterval(() => { }, KEEPALIVE_INTERVAL_MS);
|
|
43
76
|
}
|
|
77
|
+
async function reportLaunchFailure(status, error) {
|
|
78
|
+
await writeFramedRecord(status, {
|
|
79
|
+
kind: SHIM_FRAME_KIND.launchFailed,
|
|
80
|
+
failure: error.failure,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
44
83
|
function waitForWorkerExit(worker) {
|
|
45
84
|
return new Promise((resolve) => {
|
|
46
85
|
let spawnFailed = false;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supervisor-coordinator.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/supervisor-coordinator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,6BAA6B,EAE9B,MAAM,kCAAkC,CAAC;AAO1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EACV,uBAAuB,EAEvB,6BAA6B,EAC7B,oBAAoB,EACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EACV,2BAA2B,EAC3B,uBAAuB,EACxB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AAU1E,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,MAAM,EAAE,6BAA6B,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAA0B;IACxC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAuB;IAClD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA4B;IACrD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0B;IACjD,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,wBAAwB,CAAS;IACzC,OAAO,CAAC,oBAAoB,CAAS;IACrC,OAAO,CAAC,4BAA4B,CAC7B;IACP,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,mBAAmB,CAAS;gBAExB,KAAK,EAAE;QACjB,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;QACzC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;QAC1C,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC;QACvC,QAAQ,CAAC,QAAQ,EAAE,yBAAyB,CAAC;QAC7C,QAAQ,CAAC,MAAM,EAAE,uBAAuB,CAAC;QACzC,QAAQ,CAAC,GAAG,EAAE,MAAM,MAAM,CAAC;QAC3B,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC;QAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,IAAI,CAAC;KACjC;IAWD,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAa;IACtC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAa;IACxC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAe;IAEnC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAI/B,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAexB,gBAAgB,CAAC,UAAU,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"supervisor-coordinator.d.ts","sourceRoot":"","sources":["../../../src/internal/controlled-process/supervisor-coordinator.ts"],"names":[],"mappings":"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"}
|
|
@@ -65,7 +65,8 @@ export class SupervisorCoordinator {
|
|
|
65
65
|
return;
|
|
66
66
|
}
|
|
67
67
|
this.workerExit = workerExit;
|
|
68
|
-
if (this.record.state === CONTROLLED_PROCESS_STATE.running
|
|
68
|
+
if ((this.record.state === CONTROLLED_PROCESS_STATE.running ||
|
|
69
|
+
this.record.state === CONTROLLED_PROCESS_STATE.acked) &&
|
|
69
70
|
!this.groupAbsentObserved &&
|
|
70
71
|
this.unkillableReason === null) {
|
|
71
72
|
await this.beginTermination();
|
|
@@ -108,7 +109,8 @@ export class SupervisorCoordinator {
|
|
|
108
109
|
return this.enqueueResult(async () => {
|
|
109
110
|
this.terminationRequested = true;
|
|
110
111
|
this.terminationConvergenceReason ??= convergenceReason;
|
|
111
|
-
if (this.record.state === CONTROLLED_PROCESS_STATE.running
|
|
112
|
+
if ((this.record.state === CONTROLLED_PROCESS_STATE.running ||
|
|
113
|
+
this.record.state === CONTROLLED_PROCESS_STATE.acked) &&
|
|
112
114
|
!this.groupAbsentObserved &&
|
|
113
115
|
this.unkillableReason === null) {
|
|
114
116
|
await this.beginTermination();
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ValueOf } from "@alpacakit/core";
|
|
2
2
|
import type { ChildProcessIdentity, ChildProcessTerminationResult } from "../../core/controlled-process.js";
|
|
3
|
+
import type { CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE, CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY } from "../../core/error.js";
|
|
3
4
|
import type { ExecExit } from "../../core/ports.js";
|
|
4
5
|
import type { OUTPUT_CHANNEL_KIND, PROCESS_PROBE_KIND, PROCESS_SIGNAL_RESULT_KIND, SHIM_FRAME_KIND, SUPERVISOR_BOOTSTRAP_FRAME_KIND, SUPERVISOR_CONVERGENCE_REASON, SUPERVISOR_REQUEST_KIND, SUPERVISOR_RESPONSE_KIND } from "./supervisor-values.js";
|
|
5
6
|
export type SupervisorWorkerOptions = {
|
|
@@ -9,7 +10,21 @@ export type SupervisorWorkerOptions = {
|
|
|
9
10
|
readonly env: {
|
|
10
11
|
readonly [key: string]: string;
|
|
11
12
|
};
|
|
12
|
-
|
|
13
|
+
};
|
|
14
|
+
export type WorkerInputManifest = {
|
|
15
|
+
readonly kind: "none";
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: "stream";
|
|
18
|
+
readonly byteLength: number;
|
|
19
|
+
readonly sha256: string;
|
|
20
|
+
};
|
|
21
|
+
export type ControlledProcessLaunchFailure = {
|
|
22
|
+
readonly code: ValueOf<typeof CONTROLLED_PROCESS_LAUNCH_FAILURE_CODE>;
|
|
23
|
+
readonly retryability: ValueOf<typeof CONTROLLED_PROCESS_LAUNCH_FAILURE_RETRYABILITY>;
|
|
24
|
+
readonly stage: "host_to_supervisor" | "supervisor_to_shim" | "shim_to_worker" | "control_protocol";
|
|
25
|
+
readonly expectedBytes: number | null;
|
|
26
|
+
readonly actualBytes: number | null;
|
|
27
|
+
readonly message: string;
|
|
13
28
|
};
|
|
14
29
|
export type SupervisorTimingOptions = {
|
|
15
30
|
readonly termGraceMs: number;
|
|
@@ -23,6 +38,7 @@ export type SupervisorBootstrap = {
|
|
|
23
38
|
readonly runtimeDirectory: string;
|
|
24
39
|
readonly shimEntryPath: string;
|
|
25
40
|
readonly worker: SupervisorWorkerOptions;
|
|
41
|
+
readonly input: WorkerInputManifest;
|
|
26
42
|
readonly timing: SupervisorTimingOptions;
|
|
27
43
|
};
|
|
28
44
|
export type SupervisorHostAck = {
|
|
@@ -34,11 +50,13 @@ export type SupervisorIdentityFrame = {
|
|
|
34
50
|
};
|
|
35
51
|
export type SupervisorLaunchFailedFrame = {
|
|
36
52
|
readonly kind: typeof SUPERVISOR_BOOTSTRAP_FRAME_KIND.launchFailed;
|
|
37
|
-
readonly
|
|
53
|
+
readonly failure: ControlledProcessLaunchFailure;
|
|
38
54
|
};
|
|
39
55
|
export type ShimBootstrap = {
|
|
40
56
|
readonly kind: typeof SHIM_FRAME_KIND.bootstrap;
|
|
41
57
|
readonly worker: SupervisorWorkerOptions;
|
|
58
|
+
readonly input: WorkerInputManifest;
|
|
59
|
+
readonly inputTimeoutMs: number;
|
|
42
60
|
};
|
|
43
61
|
export type ShimAck = {
|
|
44
62
|
readonly kind: typeof SHIM_FRAME_KIND.ack;
|
|
@@ -46,6 +64,10 @@ export type ShimAck = {
|
|
|
46
64
|
export type ShimRunning = {
|
|
47
65
|
readonly kind: typeof SHIM_FRAME_KIND.running;
|
|
48
66
|
};
|
|
67
|
+
export type ShimLaunchFailed = {
|
|
68
|
+
readonly kind: typeof SHIM_FRAME_KIND.launchFailed;
|
|
69
|
+
readonly failure: ControlledProcessLaunchFailure;
|
|
70
|
+
};
|
|
49
71
|
export type WorkerExit = {
|
|
50
72
|
readonly kind: typeof SHIM_FRAME_KIND.workerExit;
|
|
51
73
|
readonly code: number | null;
|