@dsh-enhanced/assistant-isolation 0.1.67 → 0.1.68
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 +39 -68
- package/lib/cli.d.ts +2 -4
- package/lib/cli.d.ts.map +1 -1
- package/lib/cli.js +51 -77
- package/lib/cli.js.map +1 -1
- package/lib/config.d.ts +4 -7
- package/lib/config.d.ts.map +1 -1
- package/lib/config.js +26 -40
- package/lib/config.js.map +1 -1
- package/lib/index.d.ts +3 -3
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +2 -2
- package/lib/index.js.map +1 -1
- package/lib/ledger.d.ts +2 -13
- package/lib/ledger.d.ts.map +1 -1
- package/lib/ledger.js +7 -85
- package/lib/ledger.js.map +1 -1
- package/lib/probe.d.ts +0 -3
- package/lib/probe.d.ts.map +1 -1
- package/lib/probe.js +1 -41
- package/lib/probe.js.map +1 -1
- package/lib/service.d.ts +1 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +10 -90
- package/lib/service.js.map +1 -1
- package/lib/types.d.ts +4 -31
- package/lib/types.d.ts.map +1 -1
- package/lib/verifier-runner.d.ts +8 -16
- package/lib/verifier-runner.d.ts.map +1 -1
- package/lib/verifier-runner.js +9 -207
- package/lib/verifier-runner.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/package.json +8 -5
- package/lib/cleanup-receipt.d.ts +0 -28
- package/lib/cleanup-receipt.d.ts.map +0 -1
- package/lib/cleanup-receipt.js +0 -137
- package/lib/cleanup-receipt.js.map +0 -1
- package/lib/daemon-binding.d.ts +0 -13
- package/lib/daemon-binding.d.ts.map +0 -1
- package/lib/daemon-binding.js +0 -120
- package/lib/daemon-binding.js.map +0 -1
- package/lib/docker-socket.d.ts +0 -10
- package/lib/docker-socket.d.ts.map +0 -1
- package/lib/docker-socket.js +0 -43
- package/lib/docker-socket.js.map +0 -1
- package/lib/reconcile.d.ts +0 -5
- package/lib/reconcile.d.ts.map +0 -1
- package/lib/reconcile.js +0 -31
- package/lib/reconcile.js.map +0 -1
- package/lib/runner.d.ts +0 -12
- package/lib/runner.d.ts.map +0 -1
- package/lib/runner.js +0 -227
- package/lib/runner.js.map +0 -1
- package/lib/runtime-witness.d.ts +0 -32
- package/lib/runtime-witness.d.ts.map +0 -1
- package/lib/runtime-witness.js +0 -225
- package/lib/runtime-witness.js.map +0 -1
- package/lib/state-socket-binding.d.ts +0 -4
- package/lib/state-socket-binding.d.ts.map +0 -1
- package/lib/state-socket-binding.js +0 -64
- package/lib/state-socket-binding.js.map +0 -1
- package/runtime/supervisor.mjs +0 -300
package/runtime/supervisor.mjs
DELETED
|
@@ -1,300 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process'
|
|
2
|
-
import { chmod, mkdtemp, rm } from 'node:fs/promises'
|
|
3
|
-
import { lstatSync } from 'node:fs'
|
|
4
|
-
import { tmpdir } from 'node:os'
|
|
5
|
-
import { join } from 'node:path'
|
|
6
|
-
|
|
7
|
-
const OPERATION_TIMEOUT_MS = 10_000
|
|
8
|
-
const NAME = /^dsh-isolation-[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/
|
|
9
|
-
const IMAGE = /^sha256:[0-9a-f]{64}$/
|
|
10
|
-
let config
|
|
11
|
-
let workerOwned = false
|
|
12
|
-
let keeperOwned = false
|
|
13
|
-
let volumeOwned = false
|
|
14
|
-
let started = false
|
|
15
|
-
let finalizing = false
|
|
16
|
-
let complete = false
|
|
17
|
-
let deadlineTimer
|
|
18
|
-
let stdout = ''
|
|
19
|
-
let stderr = ''
|
|
20
|
-
let dockerDirectory
|
|
21
|
-
let provisioning = Promise.resolve()
|
|
22
|
-
let cancelReason
|
|
23
|
-
let creationAmbiguous = false
|
|
24
|
-
let mutationAmbiguous = false
|
|
25
|
-
let requestsSettled = true
|
|
26
|
-
const activeCommands = new Set()
|
|
27
|
-
const workerName = () => config.containerName
|
|
28
|
-
const keeperName = () => `${workerName()}-keeper`
|
|
29
|
-
const volumeName = () => `${workerName()}-workspace`
|
|
30
|
-
|
|
31
|
-
function environment() {
|
|
32
|
-
return { PATH: process.env.PATH || '/usr/bin:/bin', LANG: 'C', LC_ALL: 'C' }
|
|
33
|
-
}
|
|
34
|
-
function send(message) {
|
|
35
|
-
if (process.connected) { try { process.send(message) } catch {} }
|
|
36
|
-
}
|
|
37
|
-
function decode(buffer) {
|
|
38
|
-
try { return new TextDecoder('utf-8', { fatal: true }).decode(buffer) }
|
|
39
|
-
catch { return undefined }
|
|
40
|
-
}
|
|
41
|
-
function succeeded(result) {
|
|
42
|
-
return result.code === 0 && !result.timeout && !result.overflow
|
|
43
|
-
}
|
|
44
|
-
function socketUnchanged() {
|
|
45
|
-
try {
|
|
46
|
-
const info = lstatSync(config.socket.path)
|
|
47
|
-
return info.isSocket() && info.dev === config.socket.dev && info.ino === config.socket.ino
|
|
48
|
-
} catch { return false }
|
|
49
|
-
}
|
|
50
|
-
function docker(args, { outputLimit = 65_536, timeoutMs = OPERATION_TIMEOUT_MS } = {}) {
|
|
51
|
-
return new Promise(resolve => {
|
|
52
|
-
let child
|
|
53
|
-
let out = Buffer.alloc(0)
|
|
54
|
-
let err = Buffer.alloc(0)
|
|
55
|
-
let overflow = false
|
|
56
|
-
let timeout = false
|
|
57
|
-
let settled = false
|
|
58
|
-
let timer
|
|
59
|
-
const finish = result => {
|
|
60
|
-
if (settled) return
|
|
61
|
-
settled = true
|
|
62
|
-
if (timer !== undefined) clearTimeout(timer)
|
|
63
|
-
const mutating = ['create', 'start', 'cp', 'exec'].includes(args[0]) || (args[0] === 'volume' && args[1] === 'create')
|
|
64
|
-
if (mutating && (timeout || overflow || result.code === null)) mutationAmbiguous = true
|
|
65
|
-
if (mutating && (timeout || overflow || result.code !== 0 || result.signal || result.error)) requestsSettled = false
|
|
66
|
-
const decoded = decode(out)
|
|
67
|
-
resolve({ ...result, stdout: decoded ?? '', stderr: decode(err) ?? '', stdoutBytes: out.length,
|
|
68
|
-
stdoutUtf8: decoded !== undefined, overflow, timeout })
|
|
69
|
-
}
|
|
70
|
-
if (!socketUnchanged()) { mutationAmbiguous = true; finish({ code: null, error: 'docker-socket-changed' }); return }
|
|
71
|
-
try {
|
|
72
|
-
child = spawn(config.dockerPath, ['--config', dockerDirectory, '-H', `unix://${config.socket.path}`, ...args], {
|
|
73
|
-
shell: false, env: environment(), stdio: ['ignore', 'pipe', 'pipe'],
|
|
74
|
-
})
|
|
75
|
-
} catch (error) { finish({ code: null, error: String(error) }); return }
|
|
76
|
-
activeCommands.add(child)
|
|
77
|
-
child.once('close', () => activeCommands.delete(child))
|
|
78
|
-
const collect = (target, chunk) => {
|
|
79
|
-
const kept = chunk.subarray(0, Math.max(0, outputLimit - out.length - err.length))
|
|
80
|
-
if (target === 'out') out = Buffer.concat([out, kept])
|
|
81
|
-
else err = Buffer.concat([err, kept])
|
|
82
|
-
if (kept.length !== chunk.length && !overflow) { overflow = true; child.kill('SIGTERM') }
|
|
83
|
-
}
|
|
84
|
-
child.stdout.on('data', chunk => collect('out', chunk))
|
|
85
|
-
child.stderr.on('data', chunk => collect('err', chunk))
|
|
86
|
-
child.once('error', error => finish({ code: null, error: error.message }))
|
|
87
|
-
// close, not exit: collect the final pipe bytes before accepting an artifact.
|
|
88
|
-
child.once('close', (code, signal) => finish({ code, signal }))
|
|
89
|
-
timer = setTimeout(() => { timeout = true; child.kill('SIGKILL') }, timeoutMs)
|
|
90
|
-
timer.unref()
|
|
91
|
-
})
|
|
92
|
-
}
|
|
93
|
-
async function absent(type, name) {
|
|
94
|
-
const result = await docker(['inspect', '--type', type, name])
|
|
95
|
-
return !result.timeout && !result.overflow && result.code !== 0
|
|
96
|
-
&& /no such (object|container|volume)/i.test(result.stderr)
|
|
97
|
-
}
|
|
98
|
-
async function erase(type, name) {
|
|
99
|
-
for (let attempt = 0; attempt < 3; attempt++) {
|
|
100
|
-
const result = await docker(type === 'volume' ? ['volume', 'rm', name] : ['rm', '-f', name])
|
|
101
|
-
if (succeeded(result) || await absent(type, name)) return true
|
|
102
|
-
if (attempt < 2) await new Promise(resolve => setTimeout(resolve, 100))
|
|
103
|
-
}
|
|
104
|
-
return false
|
|
105
|
-
}
|
|
106
|
-
async function removeWorker() {
|
|
107
|
-
if (!workerOwned) return true
|
|
108
|
-
await docker(['kill', workerName()])
|
|
109
|
-
return erase('container', workerName())
|
|
110
|
-
}
|
|
111
|
-
async function removeStorage() {
|
|
112
|
-
const keeperRemoved = !keeperOwned || await erase('container', keeperName())
|
|
113
|
-
const volumeRemoved = !volumeOwned || await erase('volume', volumeName())
|
|
114
|
-
return keeperRemoved && volumeRemoved
|
|
115
|
-
}
|
|
116
|
-
function artifactPath(path) {
|
|
117
|
-
return typeof path === 'string' && path.length > 0 && path.length <= 4096 && !path.startsWith('/')
|
|
118
|
-
&& !/[\p{Cc}\\]/u.test(path) && path.split('/').every(part => part && part !== '.' && part !== '..')
|
|
119
|
-
}
|
|
120
|
-
function exportInterruption() {
|
|
121
|
-
if (cancelReason !== undefined) return cancelReason
|
|
122
|
-
return Date.now() >= config.deadline ? 'deadline-expired' : undefined
|
|
123
|
-
}
|
|
124
|
-
function exportTimeout() {
|
|
125
|
-
return Math.max(1, Math.min(OPERATION_TIMEOUT_MS, config.deadline - Date.now()))
|
|
126
|
-
}
|
|
127
|
-
async function stat(path, regular) {
|
|
128
|
-
if (exportInterruption()) return undefined
|
|
129
|
-
const result = await docker(['exec', keeperName(), '/bin/busybox', 'stat', '-c', '%f:%h:%s', '--', path], { timeoutMs: exportTimeout() })
|
|
130
|
-
if (!succeeded(result) || exportInterruption() || !/^[0-9a-fA-F]+:\d+:\d+\n?$/.test(result.stdout)) return undefined
|
|
131
|
-
const fields = result.stdout.trim().split(':')
|
|
132
|
-
const mode = Number.parseInt(fields[0], 16)
|
|
133
|
-
const links = Number(fields[1])
|
|
134
|
-
const size = Number(fields[2])
|
|
135
|
-
if (![mode, links, size].every(Number.isSafeInteger)) return undefined
|
|
136
|
-
// BusyBox stat without -L examines the link itself. Each parent is examined
|
|
137
|
-
// separately after worker removal, so no worker can race path resolution.
|
|
138
|
-
if (regular ? (mode & 0o170000) !== 0o100000 || links !== 1 : (mode & 0o170000) !== 0o40000) return undefined
|
|
139
|
-
return { size }
|
|
140
|
-
}
|
|
141
|
-
async function exportArtifacts() {
|
|
142
|
-
const files = []
|
|
143
|
-
let total = 0
|
|
144
|
-
for (const path of config.artifacts) {
|
|
145
|
-
if (exportInterruption() || !artifactPath(path)) return undefined
|
|
146
|
-
let parent = '/workspace'
|
|
147
|
-
if (!await stat(parent, false)) return undefined
|
|
148
|
-
const parts = path.split('/')
|
|
149
|
-
for (const part of parts.slice(0, -1)) {
|
|
150
|
-
parent += `/${part}`
|
|
151
|
-
if (!await stat(parent, false)) return undefined
|
|
152
|
-
}
|
|
153
|
-
const target = `/workspace/${path}`
|
|
154
|
-
const entry = await stat(target, true)
|
|
155
|
-
if (!entry || entry.size > config.limits.maxArtifactBytes - total || exportInterruption()) return undefined
|
|
156
|
-
const result = await docker(['exec', keeperName(), '/bin/busybox', 'cat', '--', target], {
|
|
157
|
-
outputLimit: entry.size, timeoutMs: exportTimeout(),
|
|
158
|
-
})
|
|
159
|
-
if (!succeeded(result) || !result.stdoutUtf8 || result.stdoutBytes !== entry.size || exportInterruption()) return undefined
|
|
160
|
-
total += entry.size
|
|
161
|
-
files.push({ path, content: result.stdout })
|
|
162
|
-
}
|
|
163
|
-
return files
|
|
164
|
-
}
|
|
165
|
-
async function finish(status, reason, exitCode) {
|
|
166
|
-
if (finalizing) return
|
|
167
|
-
finalizing = true
|
|
168
|
-
if (deadlineTimer !== undefined) clearTimeout(deadlineTimer)
|
|
169
|
-
// Cleanup cannot overtake a create/copy request still owned by this process.
|
|
170
|
-
await provisioning
|
|
171
|
-
const workerRemoved = await removeWorker()
|
|
172
|
-
let artifacts = []
|
|
173
|
-
if (status === 'succeeded' && workerRemoved && keeperOwned) {
|
|
174
|
-
const exported = await exportArtifacts()
|
|
175
|
-
const interrupted = exportInterruption()
|
|
176
|
-
if (interrupted !== undefined) {
|
|
177
|
-
status = interrupted === 'deadline-expired' ? 'timed-out' : 'cancelled'
|
|
178
|
-
reason = interrupted
|
|
179
|
-
} else if (exported === undefined) {
|
|
180
|
-
status = 'failed'
|
|
181
|
-
reason = 'artifact-export-rejected'
|
|
182
|
-
} else artifacts = exported
|
|
183
|
-
}
|
|
184
|
-
const storageRemoved = await removeStorage()
|
|
185
|
-
// A final receipt must not outrun an attached start or other CLI still alive.
|
|
186
|
-
// No new create can follow provisioning; begin() stops when finalizing is set.
|
|
187
|
-
const outstanding = [...activeCommands]
|
|
188
|
-
await Promise.all(outstanding.map(child => new Promise(resolve => child.once('close', resolve))))
|
|
189
|
-
// A timed-out CLI can leave an in-flight daemon create request. A momentary
|
|
190
|
-
// absence is not a release receipt for such a request; retain its reservation.
|
|
191
|
-
const quiescent = workerRemoved && storageRemoved && !mutationAmbiguous
|
|
192
|
-
if (creationAmbiguous) reason = 'docker-creation-unconfirmed'
|
|
193
|
-
else if (mutationAmbiguous) reason = 'docker-mutation-unconfirmed'
|
|
194
|
-
if (!quiescent || status !== 'succeeded') artifacts = []
|
|
195
|
-
if (dockerDirectory !== undefined) await rm(dockerDirectory, { recursive: true, force: true }).catch(() => undefined)
|
|
196
|
-
complete = true
|
|
197
|
-
send({ type: 'result', settlementProtocol: 'all-cli-closed/v1', requestsSettled: requestsSettled && !mutationAmbiguous, result: { status: quiescent ? status : 'unknown', quiescent,
|
|
198
|
-
...(exitCode === undefined ? {} : { exitCode }), stdout, stderr, artifacts,
|
|
199
|
-
...(reason === undefined ? {} : { reason }) } })
|
|
200
|
-
if (process.connected) process.disconnect()
|
|
201
|
-
}
|
|
202
|
-
async function begin() {
|
|
203
|
-
const attached = await docker(['start', '--attach', workerName()], {
|
|
204
|
-
outputLimit: config.limits.maxOutputBytes, timeoutMs: Math.max(1, config.deadline - Date.now()),
|
|
205
|
-
})
|
|
206
|
-
stdout = attached.stdout
|
|
207
|
-
stderr = attached.stderr
|
|
208
|
-
if (finalizing) return
|
|
209
|
-
if (attached.overflow) { await finish('failed', 'output-limit-exceeded'); return }
|
|
210
|
-
if (attached.timeout) { await finish('unknown', 'docker-start-timeout'); return }
|
|
211
|
-
const inspected = await docker(['inspect', '--type', 'container', '--format', '{{json .State}}', workerName()])
|
|
212
|
-
let state
|
|
213
|
-
try { state = JSON.parse(inspected.stdout) } catch {}
|
|
214
|
-
if (!succeeded(inspected) || typeof state?.Running !== 'boolean' || state.Running || !Number.isSafeInteger(state.ExitCode)) {
|
|
215
|
-
await finish('unknown', 'container-state-unconfirmed')
|
|
216
|
-
return
|
|
217
|
-
}
|
|
218
|
-
await finish(state.ExitCode === 0 ? 'succeeded' : 'failed', state.ExitCode === 0 ? undefined : 'container-exited-nonzero', state.ExitCode)
|
|
219
|
-
}
|
|
220
|
-
function valid(input) {
|
|
221
|
-
return input && NAME.test(input.containerName) && IMAGE.test(input.image)
|
|
222
|
-
&& typeof input.dockerPath === 'string' && input.dockerPath.startsWith('/')
|
|
223
|
-
&& input.socket && typeof input.socket.path === 'string' && input.socket.path.startsWith('/')
|
|
224
|
-
&& Number.isSafeInteger(input.socket.dev) && Number.isSafeInteger(input.socket.ino)
|
|
225
|
-
&& typeof input.workspacePath === 'string' && input.workspacePath.startsWith('/') && !/[\p{Cc},]/u.test(input.workspacePath)
|
|
226
|
-
&& typeof input.command === 'string' && Number.isSafeInteger(input.deadline)
|
|
227
|
-
&& input.limits && Object.values(input.limits).every(value => typeof value === 'number' && Number.isFinite(value) && value > 0)
|
|
228
|
-
&& Array.isArray(input.artifacts) && input.artifacts.length <= input.limits.maxFiles && input.artifacts.every(artifactPath)
|
|
229
|
-
}
|
|
230
|
-
function sandbox(keeper = false) {
|
|
231
|
-
const memory = keeper ? '32m' : `${Math.floor(config.limits.memoryMiB)}m`
|
|
232
|
-
const flags = ['--network', 'none', '--read-only', '--log-driver', 'none', '--cap-drop', 'ALL',
|
|
233
|
-
'--security-opt', 'no-new-privileges', '--user', `${process.getuid()}:${process.getgid()}`,
|
|
234
|
-
'--pids-limit', keeper ? '16' : String(Math.floor(config.limits.pidsLimit)), '--memory', memory, '--memory-swap', memory,
|
|
235
|
-
'--shm-size', '1m', '--cpus', String(config.limits.cpus)]
|
|
236
|
-
if (!keeper) {
|
|
237
|
-
const tmpMiB = Math.max(1, Math.min(64, Math.floor(config.limits.memoryMiB / 4)))
|
|
238
|
-
flags.push('--tmpfs', `/tmp:rw,nosuid,nodev,size=${tmpMiB}m,nr_inodes=${Math.floor(config.limits.workspaceInodes)}`)
|
|
239
|
-
}
|
|
240
|
-
return flags
|
|
241
|
-
}
|
|
242
|
-
function creationResult(result) {
|
|
243
|
-
if (result.timeout || result.overflow || result.code === null) creationAmbiguous = true
|
|
244
|
-
return !finalizing && succeeded(result)
|
|
245
|
-
}
|
|
246
|
-
async function provision() {
|
|
247
|
-
const inspected = await docker(['image', 'inspect', '--format', '{{json .Config.Volumes}}', config.image])
|
|
248
|
-
if (finalizing || !succeeded(inspected) || !['null', '{}'].includes(inspected.stdout.trim())) return false
|
|
249
|
-
// Mark each unique resource owned before sending its create request, including
|
|
250
|
-
// the timeout / lost-ack window. No resource name comes from the model.
|
|
251
|
-
volumeOwned = true
|
|
252
|
-
const options = `o=size=${Math.floor(config.limits.workspaceMiB)}m,nr_inodes=${Math.floor(config.limits.workspaceInodes)},uid=${process.getuid()},gid=${process.getgid()},mode=0700,nosuid,nodev`
|
|
253
|
-
if (!creationResult(await docker(['volume', 'create', '--driver', 'local', '--opt', 'type=tmpfs', '--opt', 'device=tmpfs', '--opt', options, volumeName()]))) return false
|
|
254
|
-
keeperOwned = true
|
|
255
|
-
if (!creationResult(await docker(['create', '--pull', 'never', '--name', keeperName(), ...sandbox(true),
|
|
256
|
-
'--mount', `type=volume,src=${volumeName()},dst=/workspace,volume-nocopy`, '--entrypoint', '/bin/busybox', config.image, 'sleep', '3600']))) return false
|
|
257
|
-
if (!succeeded(await docker(['start', keeperName()])) || finalizing) return false
|
|
258
|
-
if (!succeeded(await docker(['cp', '-a', `${config.workspacePath}/.`, `${keeperName()}:/workspace`])) || finalizing) return false
|
|
259
|
-
workerOwned = true
|
|
260
|
-
return creationResult(await docker(['create', '--pull', 'never', '--name', workerName(), ...sandbox(),
|
|
261
|
-
'--mount', `type=volume,src=${volumeName()},dst=/workspace,volume-nocopy`, '--workdir', '/workspace',
|
|
262
|
-
'--entrypoint', '/bin/sh', config.image, '-c', config.command]))
|
|
263
|
-
}
|
|
264
|
-
async function configure(input) {
|
|
265
|
-
if (config !== undefined || !valid(input)) { send({ type: 'error', reason: 'invalid-supervisor-config' }); return }
|
|
266
|
-
if (!['linux', 'darwin'].includes(process.platform) || typeof process.getuid !== 'function' || typeof process.getgid !== 'function' || process.getuid() === 0) {
|
|
267
|
-
send({ type: 'error', reason: 'non-root-unix-host-required' })
|
|
268
|
-
return
|
|
269
|
-
}
|
|
270
|
-
config = input
|
|
271
|
-
if (!socketUnchanged()) { send({ type: 'error', reason: 'docker-socket-changed' }); return }
|
|
272
|
-
try {
|
|
273
|
-
dockerDirectory = await mkdtemp(join(tmpdir(), 'dsh-isolation-docker-'))
|
|
274
|
-
await chmod(dockerDirectory, 0o700)
|
|
275
|
-
} catch { await finish('failed', 'docker-config-directory-unavailable'); return }
|
|
276
|
-
if (finalizing) { await rm(dockerDirectory, { recursive: true, force: true }).catch(() => undefined); return }
|
|
277
|
-
if (config.deadline <= Date.now()) { await finish('timed-out', 'deadline-expired-before-create'); return }
|
|
278
|
-
deadlineTimer = setTimeout(() => { void finish('timed-out', 'deadline-expired') }, config.deadline - Date.now())
|
|
279
|
-
deadlineTimer.unref()
|
|
280
|
-
let markSettled
|
|
281
|
-
provisioning = new Promise(resolve => { markSettled = resolve })
|
|
282
|
-
let prepared = false
|
|
283
|
-
try { prepared = await provision() } finally { markSettled() }
|
|
284
|
-
if (finalizing) return
|
|
285
|
-
if (!prepared) { await finish('failed', 'docker-create-failed'); return }
|
|
286
|
-
if (!process.connected) { await finish('unknown', 'parent-disconnected-before-authorization'); return }
|
|
287
|
-
send({ type: 'ready' })
|
|
288
|
-
}
|
|
289
|
-
process.on('message', message => {
|
|
290
|
-
if (!message || typeof message !== 'object') return
|
|
291
|
-
if (message.type === 'configure') void configure(message.config)
|
|
292
|
-
else if (message.type === 'start' && config !== undefined && !finalizing && !started) { started = true; void begin() }
|
|
293
|
-
else if (message.type === 'cancel' && config !== undefined) {
|
|
294
|
-
cancelReason = typeof message.reason === 'string' ? message.reason : 'cancelled'
|
|
295
|
-
void finish(cancelReason === 'deadline-expired' ? 'timed-out' : 'cancelled', cancelReason)
|
|
296
|
-
}
|
|
297
|
-
})
|
|
298
|
-
process.on('disconnect', () => {
|
|
299
|
-
if (config !== undefined && !complete) { cancelReason = 'parent-ipc-disconnected'; void finish('unknown', cancelReason) }
|
|
300
|
-
})
|