@alpacakit/agents 0.1.0-beta.19 → 0.1.0-beta.21
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 +60 -0
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +3 -0
- package/dist/agents/codex.d.ts.map +1 -1
- package/dist/agents/codex.js +4 -0
- package/dist/agents/openai-compatible/config.d.ts.map +1 -1
- package/dist/agents/openai-compatible/config.js +5 -0
- package/dist/agents/openai-compatible/fetch-engine.d.ts.map +1 -1
- package/dist/agents/openai-compatible/fetch-engine.js +6 -0
- package/dist/agents/openai-compatible/pi/module.d.ts.map +1 -1
- package/dist/agents/openai-compatible/pi/module.js +1 -0
- package/dist/agents/openai-compatible/pi/sandbox.js +2 -0
- package/dist/agents/openai-compatible/response.js +1 -0
- package/dist/auth/profile-store.d.ts.map +1 -1
- package/dist/auth/profile-store.js +1 -0
- package/dist/auth/secrets.d.ts.map +1 -1
- package/dist/auth/secrets.js +2 -0
- package/dist/codex/app-server.js +6 -0
- package/dist/core/controlled-process.d.ts +98 -0
- package/dist/core/controlled-process.d.ts.map +1 -0
- package/dist/core/controlled-process.js +29 -0
- package/dist/core/error.d.ts +25 -0
- package/dist/core/error.d.ts.map +1 -1
- package/dist/core/error.js +32 -0
- package/dist/core/node-controlled-exec.d.ts +3 -0
- package/dist/core/node-controlled-exec.d.ts.map +1 -0
- package/dist/core/node-controlled-exec.js +32 -0
- package/dist/core/node-ports.d.ts +1 -0
- package/dist/core/node-ports.d.ts.map +1 -1
- package/dist/core/node-ports.js +9 -1
- package/dist/core/ports.d.ts +5 -0
- package/dist/core/ports.d.ts.map +1 -1
- package/dist/fleet/fleet.d.ts.map +1 -1
- package/dist/fleet/fleet.js +5 -2
- package/dist/fleet/store.d.ts.map +1 -1
- package/dist/fleet/store.js +3 -0
- package/dist/host/github-dir.d.ts.map +1 -1
- package/dist/host/github-dir.js +5 -0
- package/dist/host/mcp-list.d.ts.map +1 -1
- package/dist/host/mcp-list.js +1 -0
- package/dist/host/run-command.d.ts.map +1 -1
- package/dist/host/run-command.js +2 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/internal/controlled-process/authentication.d.ts +13 -0
- package/dist/internal/controlled-process/authentication.d.ts.map +1 -0
- package/dist/internal/controlled-process/authentication.js +112 -0
- package/dist/internal/controlled-process/child-fd.d.ts +5 -0
- package/dist/internal/controlled-process/child-fd.d.ts.map +1 -0
- package/dist/internal/controlled-process/child-fd.js +47 -0
- package/dist/internal/controlled-process/framed-transport.d.ts +6 -0
- package/dist/internal/controlled-process/framed-transport.d.ts.map +1 -0
- package/dist/internal/controlled-process/framed-transport.js +117 -0
- package/dist/internal/controlled-process/host-controller.d.ts +23 -0
- package/dist/internal/controlled-process/host-controller.d.ts.map +1 -0
- package/dist/internal/controlled-process/host-controller.js +908 -0
- package/dist/internal/controlled-process/lifecycle.d.ts +5 -0
- package/dist/internal/controlled-process/lifecycle.d.ts.map +1 -0
- package/dist/internal/controlled-process/lifecycle.js +22 -0
- package/dist/internal/controlled-process/model.d.ts +99 -0
- package/dist/internal/controlled-process/model.d.ts.map +1 -0
- package/dist/internal/controlled-process/model.js +1 -0
- package/dist/internal/controlled-process/record-store.d.ts +17 -0
- package/dist/internal/controlled-process/record-store.d.ts.map +1 -0
- package/dist/internal/controlled-process/record-store.js +170 -0
- package/dist/internal/controlled-process/record.d.ts +12 -0
- package/dist/internal/controlled-process/record.d.ts.map +1 -0
- package/dist/internal/controlled-process/record.js +87 -0
- package/dist/internal/controlled-process/schema.d.ts +133 -0
- package/dist/internal/controlled-process/schema.d.ts.map +1 -0
- package/dist/internal/controlled-process/schema.js +99 -0
- package/dist/internal/controlled-process/shim-entry.d.ts +2 -0
- package/dist/internal/controlled-process/shim-entry.d.ts.map +1 -0
- package/dist/internal/controlled-process/shim-entry.js +2 -0
- package/dist/internal/controlled-process/shim-runtime.d.ts +2 -0
- package/dist/internal/controlled-process/shim-runtime.d.ts.map +1 -0
- package/dist/internal/controlled-process/shim-runtime.js +57 -0
- package/dist/internal/controlled-process/supervisor-coordinator.d.ts +67 -0
- package/dist/internal/controlled-process/supervisor-coordinator.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-coordinator.js +325 -0
- package/dist/internal/controlled-process/supervisor-entry.d.ts +2 -0
- package/dist/internal/controlled-process/supervisor-entry.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-entry.js +2 -0
- package/dist/internal/controlled-process/supervisor-model.d.ts +92 -0
- package/dist/internal/controlled-process/supervisor-model.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-model.js +1 -0
- package/dist/internal/controlled-process/supervisor-platform.d.ts +16 -0
- package/dist/internal/controlled-process/supervisor-platform.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-platform.js +104 -0
- package/dist/internal/controlled-process/supervisor-runtime.d.ts +11 -0
- package/dist/internal/controlled-process/supervisor-runtime.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-runtime.js +773 -0
- package/dist/internal/controlled-process/supervisor-schema.d.ts +100 -0
- package/dist/internal/controlled-process/supervisor-schema.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-schema.js +73 -0
- package/dist/internal/controlled-process/supervisor-transport.d.ts +46 -0
- package/dist/internal/controlled-process/supervisor-transport.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-transport.js +368 -0
- package/dist/internal/controlled-process/supervisor-values.d.ts +39 -0
- package/dist/internal/controlled-process/supervisor-values.d.ts.map +1 -0
- package/dist/internal/controlled-process/supervisor-values.js +38 -0
- package/dist/internal/controlled-process/token.d.ts +4 -0
- package/dist/internal/controlled-process/token.d.ts.map +1 -0
- package/dist/internal/controlled-process/token.js +38 -0
- package/dist/internal/controlled-process/values.d.ts +39 -0
- package/dist/internal/controlled-process/values.d.ts.map +1 -0
- package/dist/internal/controlled-process/values.js +38 -0
- package/dist/kernel/cli-agent.d.ts.map +1 -1
- package/dist/kernel/cli-agent.js +330 -34
- package/dist/kernel/cli-login.d.ts.map +1 -1
- package/dist/kernel/cli-login.js +5 -0
- package/dist/kernel/env-template.js +1 -0
- package/dist/kernel/facets.d.ts.map +1 -1
- package/dist/kernel/facets.js +1 -0
- package/dist/kernel/keyed.d.ts.map +1 -1
- package/dist/kernel/keyed.js +1 -0
- package/dist/kernel/login.js +2 -0
- package/dist/kernel/options.d.ts +14 -0
- package/dist/kernel/options.d.ts.map +1 -1
- package/dist/kernel/options.js +3 -0
- package/dist/kernel/process-lifecycle-failure.d.ts +5 -0
- package/dist/kernel/process-lifecycle-failure.d.ts.map +1 -0
- package/dist/kernel/process-lifecycle-failure.js +5 -0
- package/dist/kernel/run.d.ts.map +1 -1
- package/dist/kernel/run.js +11 -0
- package/dist/kernel/runtime.d.ts.map +1 -1
- package/dist/kernel/runtime.js +2 -0
- package/dist/keyring/resolver.d.ts.map +1 -1
- package/dist/keyring/resolver.js +2 -0
- package/dist/structured/run-structured.js +1 -0
- package/dist/testing/fake-controlled-exec.d.ts +21 -0
- package/dist/testing/fake-controlled-exec.d.ts.map +1 -0
- package/dist/testing/fake-controlled-exec.js +172 -0
- package/dist/testing/fake-exec.d.ts.map +1 -1
- package/dist/testing/fake-exec.js +64 -54
- package/dist/testing/index.d.ts +1 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
// biome-ignore-all lint/nursery/noExcessiveLinesPerFile: Section 5.103 confines the supervisor correction to this existing runtime owner.
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { randomBytes } from "node:crypto";
|
|
4
|
+
import { CHILD_PROCESS_TERMINATION_KIND } from "../../core/controlled-process.js";
|
|
5
|
+
import { readString } from "../../core/json.js";
|
|
6
|
+
import { closeWritableFd, readableFd, writableFd } from "./child-fd.js";
|
|
7
|
+
import { readFramedRecords, readRequiredFrame, writeFramedRecord, } from "./framed-transport.js";
|
|
8
|
+
import { assertSecureRuntimeDirectory, resolveSupervisorSocketNamespace, SignedRecordStore, supervisorSocketPath, } from "./record-store.js";
|
|
9
|
+
import { controlledProcessRecordIdSchema, shimProcessIdSchema, supervisorCapabilitySchema, supervisorInstanceIdSchema, supervisorSocketLocatorSchema, } from "./schema.js";
|
|
10
|
+
import { SupervisorCoordinator } from "./supervisor-coordinator.js";
|
|
11
|
+
import { createSupervisorPlatformAdapter } from "./supervisor-platform.js";
|
|
12
|
+
import { shimRunningSchema, supervisorBootstrapSchema, supervisorHostAckSchema, workerExitSchema, } from "./supervisor-schema.js";
|
|
13
|
+
import { SupervisorSocketServer } from "./supervisor-transport.js";
|
|
14
|
+
import { PROCESS_PROBE_KIND, SHIM_FRAME_KIND, SUPERVISOR_BOOTSTRAP_FRAME_KIND, SUPERVISOR_REQUEST_KIND, SUPERVISOR_RESPONSE_KIND, } from "./supervisor-values.js";
|
|
15
|
+
import { encodeProcessStartToken } from "./token.js";
|
|
16
|
+
import { CONTROLLED_PROCESS_PROTOCOL_VERSION, CONTROLLED_PROCESS_STATE, } from "./values.js";
|
|
17
|
+
const HOST_BOOTSTRAP_FD = 3;
|
|
18
|
+
const HOST_IDENTITY_FD = 4;
|
|
19
|
+
const SHIM_CONTROL_FD = 3;
|
|
20
|
+
const SHIM_STATUS_FD = 4;
|
|
21
|
+
const RANDOM_ID_BYTES = 16;
|
|
22
|
+
const CAPABILITY_BYTES = 32;
|
|
23
|
+
const ATTACHED_STDOUT = process.stdout;
|
|
24
|
+
const ATTACHED_STDERR = process.stderr;
|
|
25
|
+
let attachedOutputSettlement = null;
|
|
26
|
+
const SUPERVISOR_RUNTIME_DEPENDENCIES = {
|
|
27
|
+
writeShimAck: async (control) => writeFramedRecord(control, { kind: SHIM_FRAME_KIND.ack }),
|
|
28
|
+
persistInitialRecord: async (coordinator) => coordinator.persistInitial(),
|
|
29
|
+
finalizeSupervisorSocket: async (server) => server.finalize(),
|
|
30
|
+
};
|
|
31
|
+
export async function runControlledProcessSupervisor(dependencies = SUPERVISOR_RUNTIME_DEPENDENCIES) {
|
|
32
|
+
const hostInput = readableFd(HOST_BOOTSTRAP_FD);
|
|
33
|
+
const hostOutput = {
|
|
34
|
+
stream: writableFd(HOST_IDENTITY_FD),
|
|
35
|
+
closePromise: null,
|
|
36
|
+
};
|
|
37
|
+
try {
|
|
38
|
+
const frames = readFramedRecords(hostInput)[Symbol.asyncIterator]();
|
|
39
|
+
const bootstrap = supervisorBootstrapSchema.parse(await readRequiredFrame(frames));
|
|
40
|
+
await runSupervisorSession({
|
|
41
|
+
bootstrap,
|
|
42
|
+
frames,
|
|
43
|
+
hostInput,
|
|
44
|
+
hostOutput,
|
|
45
|
+
dependencies,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (hostOutput.closePromise === null) {
|
|
50
|
+
await writeFramedRecord(hostOutput.stream, {
|
|
51
|
+
kind: SUPERVISOR_BOOTSTRAP_FRAME_KIND.launchFailed,
|
|
52
|
+
reason: error instanceof Error ? error.message : "Supervisor launch failed.",
|
|
53
|
+
}).catch(() => undefined);
|
|
54
|
+
await closeHostOutput(hostOutput).catch(() => undefined);
|
|
55
|
+
}
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// biome-ignore lint/complexity/noExcessiveLinesPerFunction: The lifecycle sequence is kept contiguous because section 5.103 forbids a new runtime owner.
|
|
60
|
+
async function runSupervisorSession(input) {
|
|
61
|
+
const platform = createSupervisorPlatformAdapter();
|
|
62
|
+
const runtimeDirectory = await assertSecureRuntimeDirectory(input.bootstrap.runtimeDirectory, platform.uid);
|
|
63
|
+
const socketDirectory = await resolveSupervisorSocketNamespace(platform.platform, platform.uid);
|
|
64
|
+
const tokenFields = await createTokenFields(platform);
|
|
65
|
+
const socketPath = supervisorSocketPath({
|
|
66
|
+
socketDirectory,
|
|
67
|
+
socketLocator: tokenFields.socketLocator,
|
|
68
|
+
platform: platform.platform,
|
|
69
|
+
});
|
|
70
|
+
const store = new SignedRecordStore(runtimeDirectory, tokenFields.recordId, platform.uid);
|
|
71
|
+
let coordinator = null;
|
|
72
|
+
let shimPid = null;
|
|
73
|
+
let shimControl = null;
|
|
74
|
+
let shimStatus = null;
|
|
75
|
+
let shimClosed = null;
|
|
76
|
+
let lifecycleMonitor = null;
|
|
77
|
+
let lifecycleMonitorInFlight = null;
|
|
78
|
+
let coordinatorTickEnabled = false;
|
|
79
|
+
let coordinatorAckMayHaveCommitted = false;
|
|
80
|
+
let initialPersistCommitted = false;
|
|
81
|
+
let initialPersistReceipt = null;
|
|
82
|
+
let identityDelivered = false;
|
|
83
|
+
let ready = false;
|
|
84
|
+
let rollbackAttempted = false;
|
|
85
|
+
let ownershipLossObserved = false;
|
|
86
|
+
let ownershipLossPromise = null;
|
|
87
|
+
const localCoordinatorOperations = [];
|
|
88
|
+
let releaseSupervisor = () => { };
|
|
89
|
+
const released = new Promise((resolve) => {
|
|
90
|
+
releaseSupervisor = resolve;
|
|
91
|
+
});
|
|
92
|
+
const trackLocalCoordinatorOperation = (operation) => {
|
|
93
|
+
localCoordinatorOperations.push(operation);
|
|
94
|
+
void operation.catch(() => undefined);
|
|
95
|
+
return operation;
|
|
96
|
+
};
|
|
97
|
+
const settleLocalCoordinatorOperations = async () => {
|
|
98
|
+
await Promise.all(localCoordinatorOperations);
|
|
99
|
+
};
|
|
100
|
+
let requestHandler = async (_request) => {
|
|
101
|
+
throw new Error("Supervisor coordinator is unavailable.");
|
|
102
|
+
};
|
|
103
|
+
const server = new SupervisorSocketServer(socketPath, platform.uid, tokenFields.recordId, tokenFields.capability, async (request) => requestHandler(request));
|
|
104
|
+
const stopLifecycleMonitor = () => {
|
|
105
|
+
if (lifecycleMonitor !== null) {
|
|
106
|
+
clearInterval(lifecycleMonitor);
|
|
107
|
+
lifecycleMonitor = null;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
const settleLifecycleMonitor = async () => {
|
|
111
|
+
if (lifecycleMonitorInFlight !== null) {
|
|
112
|
+
await lifecycleMonitorInFlight;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
const startOwnershipLossConvergence = () => {
|
|
116
|
+
ownershipLossObserved = true;
|
|
117
|
+
ownershipLossPromise ??= containOwnershipLossFailure((async () => {
|
|
118
|
+
const deadline = Date.now() + supervisorTerminationBudget(input.bootstrap.timing);
|
|
119
|
+
stopLifecycleMonitor();
|
|
120
|
+
input.hostInput.destroy();
|
|
121
|
+
await beforeOwnershipLossDeadline(Promise.all([
|
|
122
|
+
settleOwnershipLossHostOutput(input.hostOutput, identityDelivered
|
|
123
|
+
? "Supervisor socket ownership was lost."
|
|
124
|
+
: "Supervisor socket ownership was lost before process launch."),
|
|
125
|
+
server.settleAcceptedSessions(),
|
|
126
|
+
]).then(() => undefined), deadline, "Supervisor ownership-loss host output or session drain timed out.");
|
|
127
|
+
const persistReceipt = initialPersistReceipt;
|
|
128
|
+
const persistCommitted = persistReceipt === null
|
|
129
|
+
? false
|
|
130
|
+
: await beforeOwnershipLossDeadline(persistReceipt, deadline, "Supervisor initial persistence receipt did not settle.");
|
|
131
|
+
if (!persistCommitted) {
|
|
132
|
+
return convergeNoRecordAfterOwnershipLoss({
|
|
133
|
+
server,
|
|
134
|
+
store,
|
|
135
|
+
platform,
|
|
136
|
+
shimPid,
|
|
137
|
+
shimControl,
|
|
138
|
+
shimStatus,
|
|
139
|
+
shimClosed,
|
|
140
|
+
hostInput: input.hostInput,
|
|
141
|
+
hostOutput: input.hostOutput,
|
|
142
|
+
timing: input.bootstrap.timing,
|
|
143
|
+
deadline,
|
|
144
|
+
settleLocalCoordinatorOperations,
|
|
145
|
+
settleLifecycleMonitor,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
return convergeAfterOwnershipLoss({
|
|
149
|
+
server,
|
|
150
|
+
coordinator: requireCapturedCoordinator(coordinator),
|
|
151
|
+
coordinatorAckMayHaveCommitted: () => coordinatorAckMayHaveCommitted,
|
|
152
|
+
shimControl: requireCapturedStream(shimControl, "shim control"),
|
|
153
|
+
shimStatus: requireCapturedStream(shimStatus, "shim status"),
|
|
154
|
+
shimClosed: requireCapturedPromise(shimClosed, "shim close"),
|
|
155
|
+
hostInput: input.hostInput,
|
|
156
|
+
hostOutput: input.hostOutput,
|
|
157
|
+
timing: input.bootstrap.timing,
|
|
158
|
+
deadline,
|
|
159
|
+
stopLifecycleMonitor,
|
|
160
|
+
settleLifecycleMonitor,
|
|
161
|
+
settleLocalCoordinatorOperations,
|
|
162
|
+
});
|
|
163
|
+
})());
|
|
164
|
+
return ownershipLossPromise;
|
|
165
|
+
};
|
|
166
|
+
const observeOwnership = async (rejectOperationOnLoss) => {
|
|
167
|
+
const ownership = await server.observeOwnership();
|
|
168
|
+
if (ownership.kind !== "ownership_lost") {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
void startOwnershipLossConvergence();
|
|
172
|
+
if (rejectOperationOnLoss) {
|
|
173
|
+
throw new Error("Supervisor socket ownership was lost.");
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
const runLifecycleMonitor = () => {
|
|
177
|
+
if (lifecycleMonitorInFlight !== null || ownershipLossObserved) {
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
const operation = (async () => {
|
|
181
|
+
const ownership = await server.observeOwnership();
|
|
182
|
+
if (ownership.kind === "ownership_lost") {
|
|
183
|
+
void startOwnershipLossConvergence();
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
if (coordinatorTickEnabled && coordinator !== null) {
|
|
187
|
+
await coordinator.tick();
|
|
188
|
+
}
|
|
189
|
+
})();
|
|
190
|
+
lifecycleMonitorInFlight = operation;
|
|
191
|
+
void operation
|
|
192
|
+
.catch(() => undefined)
|
|
193
|
+
.finally(() => {
|
|
194
|
+
if (lifecycleMonitorInFlight === operation) {
|
|
195
|
+
lifecycleMonitorInFlight = null;
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
};
|
|
199
|
+
makeAttachedOutputNonFatal(ATTACHED_STDOUT);
|
|
200
|
+
makeAttachedOutputNonFatal(ATTACHED_STDERR);
|
|
201
|
+
await server.listen();
|
|
202
|
+
const initialOwnership = await server.observeOwnership();
|
|
203
|
+
if (initialOwnership.kind === "ownership_lost") {
|
|
204
|
+
await startOwnershipLossConvergence();
|
|
205
|
+
}
|
|
206
|
+
lifecycleMonitor = setInterval(runLifecycleMonitor, input.bootstrap.timing.probeIntervalMs);
|
|
207
|
+
try {
|
|
208
|
+
const shim = spawnShim(input.bootstrap);
|
|
209
|
+
shimClosed = waitForShimClose(shim);
|
|
210
|
+
shimControl = shim.stdio[SHIM_CONTROL_FD];
|
|
211
|
+
shimStatus = shim.stdio[SHIM_STATUS_FD];
|
|
212
|
+
const pid = requireShimPid(shim);
|
|
213
|
+
shimPid = pid;
|
|
214
|
+
const record = {
|
|
215
|
+
protocolVersion: CONTROLLED_PROCESS_PROTOCOL_VERSION,
|
|
216
|
+
platform: tokenFields.platform,
|
|
217
|
+
bootId: tokenFields.bootId,
|
|
218
|
+
supervisorInstanceId: tokenFields.supervisorInstanceId,
|
|
219
|
+
recordId: tokenFields.recordId,
|
|
220
|
+
socketLocator: tokenFields.socketLocator,
|
|
221
|
+
pid,
|
|
222
|
+
state: CONTROLLED_PROCESS_STATE.reserved,
|
|
223
|
+
absenceProof: null,
|
|
224
|
+
};
|
|
225
|
+
coordinator = new SupervisorCoordinator({
|
|
226
|
+
record,
|
|
227
|
+
capability: tokenFields.capability,
|
|
228
|
+
store,
|
|
229
|
+
platform,
|
|
230
|
+
timing: input.bootstrap.timing,
|
|
231
|
+
now: Date.now,
|
|
232
|
+
onAbsent: endAttachedOutput,
|
|
233
|
+
onReleased: releaseSupervisor,
|
|
234
|
+
});
|
|
235
|
+
const activeCoordinator = coordinator;
|
|
236
|
+
const runCoordinatorOperation = async (operation) => {
|
|
237
|
+
await observeOwnership(true);
|
|
238
|
+
const result = await operation(activeCoordinator);
|
|
239
|
+
await observeOwnership(true);
|
|
240
|
+
return result;
|
|
241
|
+
};
|
|
242
|
+
const runLocalCoordinatorOperation = async (operation) => {
|
|
243
|
+
if (ownershipLossObserved && !initialPersistCommitted) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
await observeOwnership(false);
|
|
247
|
+
if (ownershipLossObserved && !initialPersistCommitted) {
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
await operation(activeCoordinator);
|
|
251
|
+
await observeOwnership(false);
|
|
252
|
+
};
|
|
253
|
+
requestHandler = async (request) => {
|
|
254
|
+
await observeOwnership(true);
|
|
255
|
+
const response = await handleRequest(activeCoordinator, request.kind);
|
|
256
|
+
await observeOwnership(false);
|
|
257
|
+
return response;
|
|
258
|
+
};
|
|
259
|
+
trackLocalCoordinatorOperation(attachOutput(shim, runLocalCoordinatorOperation));
|
|
260
|
+
trackLocalCoordinatorOperation(pumpShimStatus(shimStatus, runLocalCoordinatorOperation).catch(() => runLocalCoordinatorOperation(async (current) => current.recordProtocolFailure())));
|
|
261
|
+
shim.once("exit", () => {
|
|
262
|
+
trackLocalCoordinatorOperation(runLocalCoordinatorOperation(async (current) => current.recordShimExit()));
|
|
263
|
+
});
|
|
264
|
+
shim.once("close", () => {
|
|
265
|
+
trackLocalCoordinatorOperation(runLocalCoordinatorOperation(async (current) => current.recordShimClosed()));
|
|
266
|
+
});
|
|
267
|
+
await writeFramedRecord(shimControl, {
|
|
268
|
+
kind: SHIM_FRAME_KIND.bootstrap,
|
|
269
|
+
worker: input.bootstrap.worker,
|
|
270
|
+
});
|
|
271
|
+
await observeOwnership(true);
|
|
272
|
+
const persistOperation = (input.dependencies.persistInitialRecord ??
|
|
273
|
+
SUPERVISOR_RUNTIME_DEPENDENCIES.persistInitialRecord)(activeCoordinator);
|
|
274
|
+
initialPersistReceipt = persistOperation.then(() => {
|
|
275
|
+
initialPersistCommitted = true;
|
|
276
|
+
return true;
|
|
277
|
+
}, () => false);
|
|
278
|
+
await persistOperation;
|
|
279
|
+
await observeOwnership(true);
|
|
280
|
+
await writeFramedRecord(input.hostOutput.stream, {
|
|
281
|
+
kind: SUPERVISOR_BOOTSTRAP_FRAME_KIND.identity,
|
|
282
|
+
identity: {
|
|
283
|
+
pid,
|
|
284
|
+
processStartToken: encodeProcessStartToken(tokenFields),
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
identityDelivered = true;
|
|
288
|
+
const acknowledged = await readHostAck(input.frames);
|
|
289
|
+
if (!acknowledged) {
|
|
290
|
+
rollbackAttempted = true;
|
|
291
|
+
await rollbackPreReadySupervisor({
|
|
292
|
+
server,
|
|
293
|
+
finalizeSupervisorSocket: () => (input.dependencies.finalizeSupervisorSocket ??
|
|
294
|
+
SUPERVISOR_RUNTIME_DEPENDENCIES.finalizeSupervisorSocket)(server),
|
|
295
|
+
coordinator: activeCoordinator,
|
|
296
|
+
coordinatorAckMayHaveCommitted,
|
|
297
|
+
shimControl,
|
|
298
|
+
shimStatus,
|
|
299
|
+
shimClosed,
|
|
300
|
+
probeIntervalMs: input.bootstrap.timing.probeIntervalMs,
|
|
301
|
+
timeoutMs: supervisorTerminationBudget(input.bootstrap.timing),
|
|
302
|
+
onOwnershipLoss: startOwnershipLossConvergence,
|
|
303
|
+
stopLifecycleMonitor,
|
|
304
|
+
settleLifecycleMonitor,
|
|
305
|
+
});
|
|
306
|
+
stopLifecycleMonitor();
|
|
307
|
+
await settleLifecycleMonitor();
|
|
308
|
+
await closeHostOutput(input.hostOutput);
|
|
309
|
+
input.hostInput.destroy();
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
await runCoordinatorOperation(async (current) => current.acknowledge());
|
|
313
|
+
coordinatorAckMayHaveCommitted = true;
|
|
314
|
+
await input.dependencies.writeShimAck(shimControl);
|
|
315
|
+
await observeOwnership(false);
|
|
316
|
+
coordinatorTickEnabled = true;
|
|
317
|
+
ready = true;
|
|
318
|
+
await closeHostOutput(input.hostOutput);
|
|
319
|
+
input.hostInput.destroy();
|
|
320
|
+
await released;
|
|
321
|
+
await shimClosed;
|
|
322
|
+
await settleLocalCoordinatorOperations();
|
|
323
|
+
stopLifecycleMonitor();
|
|
324
|
+
await settleLifecycleMonitor();
|
|
325
|
+
const finalization = await (input.dependencies.finalizeSupervisorSocket ??
|
|
326
|
+
SUPERVISOR_RUNTIME_DEPENDENCIES.finalizeSupervisorSocket)(server);
|
|
327
|
+
if (finalization.kind === "ownership_lost") {
|
|
328
|
+
await startOwnershipLossConvergence();
|
|
329
|
+
}
|
|
330
|
+
shimControl.destroy();
|
|
331
|
+
shimStatus.destroy();
|
|
332
|
+
}
|
|
333
|
+
catch (primaryFailure) {
|
|
334
|
+
await observeOwnership(false).catch(() => undefined);
|
|
335
|
+
if (ownershipLossPromise !== null) {
|
|
336
|
+
await ownershipLossPromise;
|
|
337
|
+
}
|
|
338
|
+
if (ready || rollbackAttempted) {
|
|
339
|
+
stopLifecycleMonitor();
|
|
340
|
+
await settleLifecycleMonitor().catch(() => undefined);
|
|
341
|
+
throw primaryFailure;
|
|
342
|
+
}
|
|
343
|
+
try {
|
|
344
|
+
rollbackAttempted = true;
|
|
345
|
+
if (coordinator !== null &&
|
|
346
|
+
shimControl !== null &&
|
|
347
|
+
shimStatus !== null &&
|
|
348
|
+
shimClosed !== null) {
|
|
349
|
+
await waitForSecureRuntimeDirectory({
|
|
350
|
+
runtimeDirectory,
|
|
351
|
+
uid: platform.uid,
|
|
352
|
+
timing: input.bootstrap.timing,
|
|
353
|
+
});
|
|
354
|
+
await rollbackPreReadySupervisor({
|
|
355
|
+
server,
|
|
356
|
+
finalizeSupervisorSocket: () => (input.dependencies.finalizeSupervisorSocket ??
|
|
357
|
+
SUPERVISOR_RUNTIME_DEPENDENCIES.finalizeSupervisorSocket)(server),
|
|
358
|
+
coordinator,
|
|
359
|
+
coordinatorAckMayHaveCommitted,
|
|
360
|
+
shimControl,
|
|
361
|
+
shimStatus,
|
|
362
|
+
shimClosed,
|
|
363
|
+
probeIntervalMs: input.bootstrap.timing.probeIntervalMs,
|
|
364
|
+
timeoutMs: supervisorTerminationBudget(input.bootstrap.timing),
|
|
365
|
+
onOwnershipLoss: startOwnershipLossConvergence,
|
|
366
|
+
stopLifecycleMonitor,
|
|
367
|
+
settleLifecycleMonitor,
|
|
368
|
+
});
|
|
369
|
+
if (!identityDelivered) {
|
|
370
|
+
await store.removeUnpublished();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
const finalization = await (input.dependencies.finalizeSupervisorSocket ??
|
|
375
|
+
SUPERVISOR_RUNTIME_DEPENDENCIES.finalizeSupervisorSocket)(server);
|
|
376
|
+
if (finalization.kind === "ownership_lost") {
|
|
377
|
+
await startOwnershipLossConvergence();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
stopLifecycleMonitor();
|
|
381
|
+
await settleLifecycleMonitor();
|
|
382
|
+
}
|
|
383
|
+
catch (cleanupFailure) {
|
|
384
|
+
throw new AggregateError([primaryFailure, cleanupFailure], "Controlled process supervisor launch and rollback both failed.");
|
|
385
|
+
}
|
|
386
|
+
finally {
|
|
387
|
+
input.hostInput.destroy();
|
|
388
|
+
}
|
|
389
|
+
throw primaryFailure;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async function convergeNoRecordAfterOwnershipLoss(input) {
|
|
393
|
+
if (input.shimControl !== null &&
|
|
394
|
+
!input.shimControl.destroyed &&
|
|
395
|
+
!input.shimControl.writableEnded) {
|
|
396
|
+
input.shimControl.end();
|
|
397
|
+
}
|
|
398
|
+
if (input.shimClosed !== null) {
|
|
399
|
+
await beforeOwnershipLossDeadline(input.shimClosed, input.deadline, "Supervisor ownership-loss shim close did not settle.");
|
|
400
|
+
}
|
|
401
|
+
if (input.shimPid !== null) {
|
|
402
|
+
await waitForRuntimeGroupAbsence({
|
|
403
|
+
platform: input.platform,
|
|
404
|
+
pid: input.shimPid,
|
|
405
|
+
probeIntervalMs: input.timing.probeIntervalMs,
|
|
406
|
+
deadline: input.deadline,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
await beforeOwnershipLossDeadline(input.settleLocalCoordinatorOperations(), input.deadline, "Supervisor ownership-loss local operations did not settle.");
|
|
410
|
+
endAttachedOutput();
|
|
411
|
+
await beforeOwnershipLossDeadline(Promise.all([
|
|
412
|
+
settleAttachedOutput(),
|
|
413
|
+
input.store.removeUnpublished(),
|
|
414
|
+
input.settleLifecycleMonitor(),
|
|
415
|
+
]).then(() => undefined), input.deadline, "Supervisor ownership-loss unpublished cleanup did not settle.");
|
|
416
|
+
input.shimControl?.destroy();
|
|
417
|
+
input.shimStatus?.destroy();
|
|
418
|
+
return selfSigkill();
|
|
419
|
+
}
|
|
420
|
+
async function waitForSecureRuntimeDirectory(input) {
|
|
421
|
+
const deadline = Date.now() +
|
|
422
|
+
input.timing.termGraceMs +
|
|
423
|
+
input.timing.killProbeTimeoutMs +
|
|
424
|
+
input.timing.probeIntervalMs;
|
|
425
|
+
for (;;) {
|
|
426
|
+
try {
|
|
427
|
+
await assertSecureRuntimeDirectory(input.runtimeDirectory, input.uid);
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
catch (error) {
|
|
431
|
+
if (Date.now() >= deadline) {
|
|
432
|
+
throw error;
|
|
433
|
+
}
|
|
434
|
+
await new Promise((resolve) => setTimeout(resolve, input.timing.probeIntervalMs));
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
async function rollbackPreReadySupervisor(input) {
|
|
439
|
+
if (!input.shimControl.destroyed && !input.shimControl.writableEnded) {
|
|
440
|
+
input.shimControl.end();
|
|
441
|
+
}
|
|
442
|
+
let result;
|
|
443
|
+
if (input.coordinatorAckMayHaveCommitted) {
|
|
444
|
+
result = await input.coordinator.terminate();
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
await input.coordinator.abortBeforeAck();
|
|
448
|
+
result = await input.coordinator.probe();
|
|
449
|
+
}
|
|
450
|
+
result = await waitForPreReadyConvergence(input.coordinator, result, input.probeIntervalMs, input.timeoutMs);
|
|
451
|
+
if (result.kind !== CHILD_PROCESS_TERMINATION_KIND.absent) {
|
|
452
|
+
throw new Error(`Pre-ready supervisor rollback did not prove absence: ${result.kind}.`);
|
|
453
|
+
}
|
|
454
|
+
await input.shimClosed;
|
|
455
|
+
input.stopLifecycleMonitor();
|
|
456
|
+
await input.settleLifecycleMonitor();
|
|
457
|
+
input.shimControl.destroy();
|
|
458
|
+
input.shimStatus.destroy();
|
|
459
|
+
const finalization = await input.finalizeSupervisorSocket();
|
|
460
|
+
if (finalization.kind === "ownership_lost") {
|
|
461
|
+
await input.onOwnershipLoss();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
async function convergeAfterOwnershipLoss(input) {
|
|
465
|
+
input.stopLifecycleMonitor();
|
|
466
|
+
if (!input.coordinatorAckMayHaveCommitted() &&
|
|
467
|
+
!input.shimControl.destroyed &&
|
|
468
|
+
!input.shimControl.writableEnded) {
|
|
469
|
+
input.shimControl.end();
|
|
470
|
+
}
|
|
471
|
+
let result;
|
|
472
|
+
if (input.coordinatorAckMayHaveCommitted()) {
|
|
473
|
+
result = await beforeOwnershipLossDeadline(input.coordinator.terminate(), input.deadline, "Supervisor ownership-loss termination did not settle.");
|
|
474
|
+
}
|
|
475
|
+
else {
|
|
476
|
+
await beforeOwnershipLossDeadline(input.coordinator.abortBeforeAck(), input.deadline, "Supervisor ownership-loss rollback did not settle.");
|
|
477
|
+
result = await beforeOwnershipLossDeadline(input.coordinator.probe(), input.deadline, "Supervisor ownership-loss rollback probe did not settle.");
|
|
478
|
+
}
|
|
479
|
+
while (result.kind === CHILD_PROCESS_TERMINATION_KIND.pending) {
|
|
480
|
+
await delayBeforeOwnershipLossDeadline(Math.min(result.retryAfterMs, input.timing.probeIntervalMs), input.deadline, "Supervisor ownership-loss convergence timed out.");
|
|
481
|
+
result = await beforeOwnershipLossDeadline(input.coordinator.probe(), input.deadline, "Supervisor ownership-loss probe did not settle.");
|
|
482
|
+
}
|
|
483
|
+
if (result.kind !== CHILD_PROCESS_TERMINATION_KIND.absent) {
|
|
484
|
+
throw new Error(`Supervisor ownership-loss convergence did not prove absence: ${result.kind}.`);
|
|
485
|
+
}
|
|
486
|
+
await beforeOwnershipLossDeadline(input.shimClosed, input.deadline, "Supervisor ownership-loss shim close did not settle.");
|
|
487
|
+
await beforeOwnershipLossDeadline(input.settleLocalCoordinatorOperations(), input.deadline, "Supervisor ownership-loss local operations did not settle.");
|
|
488
|
+
endAttachedOutput();
|
|
489
|
+
input.shimControl.destroy();
|
|
490
|
+
input.shimStatus.destroy();
|
|
491
|
+
await beforeOwnershipLossDeadline(settleAttachedOutput(), input.deadline, "Supervisor ownership-loss attached output did not settle.");
|
|
492
|
+
await beforeOwnershipLossDeadline(input.settleLifecycleMonitor(), input.deadline, "Supervisor ownership-loss lifecycle monitor did not settle.");
|
|
493
|
+
return selfSigkill();
|
|
494
|
+
}
|
|
495
|
+
function settleOwnershipLossHostOutput(hostOutput, reason) {
|
|
496
|
+
return hostOutput.closePromise === null
|
|
497
|
+
? writeLaunchFailure(hostOutput, reason)
|
|
498
|
+
: hostOutput.closePromise;
|
|
499
|
+
}
|
|
500
|
+
async function waitForRuntimeGroupAbsence(input) {
|
|
501
|
+
for (;;) {
|
|
502
|
+
const probe = input.platform.probeGroup(input.pid);
|
|
503
|
+
if (probe.kind === PROCESS_PROBE_KIND.absent) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (probe.kind !== PROCESS_PROBE_KIND.alive) {
|
|
507
|
+
throw new Error(`Supervisor ownership-loss group probe failed: ${probe.kind}.`);
|
|
508
|
+
}
|
|
509
|
+
await delayBeforeOwnershipLossDeadline(input.probeIntervalMs, input.deadline, "Supervisor ownership-loss process group remained live.");
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function containOwnershipLossFailure(convergence) {
|
|
513
|
+
return convergence.catch(() => new Promise(() => { }));
|
|
514
|
+
}
|
|
515
|
+
function beforeOwnershipLossDeadline(operation, deadline, message) {
|
|
516
|
+
return new Promise((resolve, reject) => {
|
|
517
|
+
const remaining = deadline - Date.now();
|
|
518
|
+
if (remaining <= 0) {
|
|
519
|
+
reject(new Error(message));
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
const timeout = setTimeout(() => reject(new Error(message)), remaining);
|
|
523
|
+
operation.then((result) => {
|
|
524
|
+
clearTimeout(timeout);
|
|
525
|
+
resolve(result);
|
|
526
|
+
}, (error) => {
|
|
527
|
+
clearTimeout(timeout);
|
|
528
|
+
reject(error);
|
|
529
|
+
});
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
function delayBeforeOwnershipLossDeadline(delayMs, deadline, message) {
|
|
533
|
+
const remaining = deadline - Date.now();
|
|
534
|
+
if (remaining <= 0) {
|
|
535
|
+
return Promise.reject(new Error(message));
|
|
536
|
+
}
|
|
537
|
+
return new Promise((resolve) => setTimeout(resolve, Math.min(delayMs, remaining)));
|
|
538
|
+
}
|
|
539
|
+
async function waitForPreReadyConvergence(coordinator, initial, probeIntervalMs, timeoutMs) {
|
|
540
|
+
let result = initial;
|
|
541
|
+
const deadline = Date.now() + timeoutMs;
|
|
542
|
+
for (;;) {
|
|
543
|
+
if (result.kind === CHILD_PROCESS_TERMINATION_KIND.absent) {
|
|
544
|
+
return result;
|
|
545
|
+
}
|
|
546
|
+
if (Date.now() >= deadline) {
|
|
547
|
+
return result;
|
|
548
|
+
}
|
|
549
|
+
await new Promise((resolve) => setTimeout(resolve, probeIntervalMs));
|
|
550
|
+
result = await coordinator.probe();
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function supervisorTerminationBudget(timing) {
|
|
554
|
+
return (timing.termGraceMs + timing.killProbeTimeoutMs + timing.probeIntervalMs);
|
|
555
|
+
}
|
|
556
|
+
async function createTokenFields(platform) {
|
|
557
|
+
return {
|
|
558
|
+
protocolVersion: CONTROLLED_PROCESS_PROTOCOL_VERSION,
|
|
559
|
+
platform: platform.platform,
|
|
560
|
+
bootId: await platform.bootId(),
|
|
561
|
+
supervisorInstanceId: supervisorInstanceIdSchema.parse(randomHex(RANDOM_ID_BYTES)),
|
|
562
|
+
recordId: controlledProcessRecordIdSchema.parse(randomHex(RANDOM_ID_BYTES)),
|
|
563
|
+
socketLocator: supervisorSocketLocatorSchema.parse(randomHex(RANDOM_ID_BYTES)),
|
|
564
|
+
capability: supervisorCapabilitySchema.parse(randomHex(CAPABILITY_BYTES)),
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
function spawnShim(bootstrap) {
|
|
568
|
+
const shim = spawn(process.execPath, [bootstrap.shimEntryPath], {
|
|
569
|
+
detached: true,
|
|
570
|
+
env: {},
|
|
571
|
+
stdio: ["ignore", "pipe", "pipe", "pipe", "pipe"],
|
|
572
|
+
});
|
|
573
|
+
shim.once("error", () => { });
|
|
574
|
+
return shim;
|
|
575
|
+
}
|
|
576
|
+
function waitForShimClose(shim) {
|
|
577
|
+
return new Promise((resolve) => {
|
|
578
|
+
shim.once("close", () => resolve());
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
function requireShimPid(shim) {
|
|
582
|
+
if (shim.pid === undefined) {
|
|
583
|
+
throw new Error("Detached shim did not expose a pid.");
|
|
584
|
+
}
|
|
585
|
+
return shimProcessIdSchema.parse(shim.pid);
|
|
586
|
+
}
|
|
587
|
+
async function readHostAck(frames) {
|
|
588
|
+
try {
|
|
589
|
+
supervisorHostAckSchema.parse(await readRequiredFrame(frames));
|
|
590
|
+
return true;
|
|
591
|
+
}
|
|
592
|
+
catch {
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
function attachOutput(shim, runCoordinatorOperation) {
|
|
597
|
+
const stdout = requireStream(shim.stdout, "stdout");
|
|
598
|
+
const stderr = requireStream(shim.stderr, "stderr");
|
|
599
|
+
return Promise.all([
|
|
600
|
+
pumpOutput(stdout, ATTACHED_STDOUT),
|
|
601
|
+
pumpOutput(stderr, ATTACHED_STDERR),
|
|
602
|
+
]).then(async () => runCoordinatorOperation(async (coordinator) => coordinator.recordOutputClosed()), async () => runCoordinatorOperation(async (coordinator) => coordinator.recordProtocolFailure()));
|
|
603
|
+
}
|
|
604
|
+
async function pumpShimStatus(status, runCoordinatorOperation) {
|
|
605
|
+
for await (const frame of readFramedRecords(status)) {
|
|
606
|
+
const kind = readString(frame, "kind");
|
|
607
|
+
if (kind === SHIM_FRAME_KIND.running) {
|
|
608
|
+
shimRunningSchema.parse(frame);
|
|
609
|
+
await runCoordinatorOperation(async (coordinator) => coordinator.running());
|
|
610
|
+
}
|
|
611
|
+
else if (kind === SHIM_FRAME_KIND.workerExit) {
|
|
612
|
+
const workerExit = workerExitSchema.parse(frame);
|
|
613
|
+
await runCoordinatorOperation(async (coordinator) => coordinator.recordWorkerExit({
|
|
614
|
+
code: workerExit.code,
|
|
615
|
+
signal: workerExit.signal,
|
|
616
|
+
}));
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
throw new Error("Shim status frame is invalid.");
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
async function handleRequest(coordinator, kind) {
|
|
624
|
+
if (kind === SUPERVISOR_REQUEST_KIND.probe) {
|
|
625
|
+
const result = await coordinator.probe();
|
|
626
|
+
return {
|
|
627
|
+
kind: SUPERVISOR_RESPONSE_KIND.termination,
|
|
628
|
+
result,
|
|
629
|
+
workerExit: coordinator.workerExitEvidence(),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
if (kind === SUPERVISOR_REQUEST_KIND.terminate) {
|
|
633
|
+
const result = await coordinator.terminate();
|
|
634
|
+
return {
|
|
635
|
+
kind: SUPERVISOR_RESPONSE_KIND.termination,
|
|
636
|
+
result,
|
|
637
|
+
workerExit: coordinator.workerExitEvidence(),
|
|
638
|
+
};
|
|
639
|
+
}
|
|
640
|
+
if (kind === SUPERVISOR_REQUEST_KIND.release) {
|
|
641
|
+
return (await coordinator.release())
|
|
642
|
+
? { kind: SUPERVISOR_RESPONSE_KIND.released }
|
|
643
|
+
: { kind: SUPERVISOR_RESPONSE_KIND.releaseBeforeAbsent };
|
|
644
|
+
}
|
|
645
|
+
return unreachableRequest(kind);
|
|
646
|
+
}
|
|
647
|
+
function unreachableRequest(kind) {
|
|
648
|
+
throw new Error(`Unreachable supervisor request: ${String(kind)}`);
|
|
649
|
+
}
|
|
650
|
+
function requireStream(stream, name) {
|
|
651
|
+
if (stream === null) {
|
|
652
|
+
throw new Error(`Shim ${name} stream is unavailable.`);
|
|
653
|
+
}
|
|
654
|
+
return stream;
|
|
655
|
+
}
|
|
656
|
+
async function pumpOutput(readable, writable) {
|
|
657
|
+
let sinkLive = isWritableSinkLive(writable);
|
|
658
|
+
for await (const chunk of readable) {
|
|
659
|
+
if (sinkLive) {
|
|
660
|
+
try {
|
|
661
|
+
if (!writable.write(chunk)) {
|
|
662
|
+
sinkLive = await waitForOutputDrain(writable);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
sinkLive = false;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
function waitForOutputDrain(writable) {
|
|
672
|
+
return new Promise((resolve) => {
|
|
673
|
+
let settled = false;
|
|
674
|
+
const cleanup = () => {
|
|
675
|
+
writable.off("drain", drained);
|
|
676
|
+
writable.off("error", dead);
|
|
677
|
+
writable.off("close", dead);
|
|
678
|
+
};
|
|
679
|
+
const settle = (live) => {
|
|
680
|
+
if (!settled) {
|
|
681
|
+
settled = true;
|
|
682
|
+
cleanup();
|
|
683
|
+
resolve(live);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
const drained = () => settle(true);
|
|
687
|
+
const dead = () => settle(false);
|
|
688
|
+
writable.once("drain", drained);
|
|
689
|
+
writable.once("error", dead);
|
|
690
|
+
writable.once("close", dead);
|
|
691
|
+
if (!isWritableSinkLive(writable)) {
|
|
692
|
+
dead();
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
function isWritableSinkLive(writable) {
|
|
697
|
+
return !writable.destroyed && !writable.closed && !writable.writableEnded;
|
|
698
|
+
}
|
|
699
|
+
function makeAttachedOutputNonFatal(writable) {
|
|
700
|
+
writable.on("error", () => { });
|
|
701
|
+
}
|
|
702
|
+
function endAttachedOutput() {
|
|
703
|
+
if (attachedOutputSettlement !== null) {
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
attachedOutputSettlement = Promise.all([
|
|
707
|
+
waitForWritableFinish(ATTACHED_STDOUT),
|
|
708
|
+
waitForWritableFinish(ATTACHED_STDERR),
|
|
709
|
+
]).then(() => undefined);
|
|
710
|
+
ATTACHED_STDOUT.end();
|
|
711
|
+
ATTACHED_STDERR.end();
|
|
712
|
+
}
|
|
713
|
+
async function settleAttachedOutput() {
|
|
714
|
+
endAttachedOutput();
|
|
715
|
+
await attachedOutputSettlement;
|
|
716
|
+
}
|
|
717
|
+
function waitForWritableFinish(writable) {
|
|
718
|
+
if (writable.writableFinished) {
|
|
719
|
+
return Promise.resolve();
|
|
720
|
+
}
|
|
721
|
+
return new Promise((resolve, reject) => {
|
|
722
|
+
const cleanup = () => {
|
|
723
|
+
writable.off("finish", resolveFinish);
|
|
724
|
+
writable.off("error", rejectError);
|
|
725
|
+
};
|
|
726
|
+
const resolveFinish = () => {
|
|
727
|
+
cleanup();
|
|
728
|
+
resolve();
|
|
729
|
+
};
|
|
730
|
+
const rejectError = (error) => {
|
|
731
|
+
cleanup();
|
|
732
|
+
reject(error);
|
|
733
|
+
};
|
|
734
|
+
writable.once("finish", resolveFinish);
|
|
735
|
+
writable.once("error", rejectError);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function requireCapturedStream(stream, name) {
|
|
739
|
+
if (stream === null) {
|
|
740
|
+
throw new Error(`Supervisor ${name} was not captured.`);
|
|
741
|
+
}
|
|
742
|
+
return stream;
|
|
743
|
+
}
|
|
744
|
+
function requireCapturedPromise(promise, name) {
|
|
745
|
+
if (promise === null) {
|
|
746
|
+
throw new Error(`Supervisor ${name} was not captured.`);
|
|
747
|
+
}
|
|
748
|
+
return promise;
|
|
749
|
+
}
|
|
750
|
+
function requireCapturedCoordinator(coordinator) {
|
|
751
|
+
if (coordinator === null) {
|
|
752
|
+
throw new Error("Supervisor coordinator was not captured.");
|
|
753
|
+
}
|
|
754
|
+
return coordinator;
|
|
755
|
+
}
|
|
756
|
+
async function writeLaunchFailure(hostOutput, reason) {
|
|
757
|
+
await writeFramedRecord(hostOutput.stream, {
|
|
758
|
+
kind: SUPERVISOR_BOOTSTRAP_FRAME_KIND.launchFailed,
|
|
759
|
+
reason,
|
|
760
|
+
});
|
|
761
|
+
await closeHostOutput(hostOutput);
|
|
762
|
+
}
|
|
763
|
+
function closeHostOutput(hostOutput) {
|
|
764
|
+
hostOutput.closePromise ??= closeWritableFd(hostOutput.stream, HOST_IDENTITY_FD);
|
|
765
|
+
return hostOutput.closePromise;
|
|
766
|
+
}
|
|
767
|
+
async function selfSigkill() {
|
|
768
|
+
process.kill(process.pid, "SIGKILL");
|
|
769
|
+
return new Promise(() => { });
|
|
770
|
+
}
|
|
771
|
+
function randomHex(bytes) {
|
|
772
|
+
return randomBytes(bytes).toString("hex");
|
|
773
|
+
}
|