@jigging/jig 0.0.0 → 0.1.0-alpha.2

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.
@@ -0,0 +1,623 @@
1
+ // @bun
2
+ // src/internal/linux-rootless-supervisor.ts
3
+ import { spawn } from "child_process";
4
+ import { randomBytes } from "crypto";
5
+ import { closeSync, readFileSync, statSync, writeSync } from "fs";
6
+ import {
7
+ link,
8
+ mkdir,
9
+ open,
10
+ readFile,
11
+ rmdir,
12
+ stat,
13
+ unlink,
14
+ writeFile
15
+ } from "fs/promises";
16
+ import { connect } from "net";
17
+ import { posix } from "path";
18
+ var POLICY = Object.freeze(["--no-env-file", "--no-install", "--config=/dev/null"]);
19
+ var SANDBOX_BUN = "/jig-runtime/bun";
20
+ var SANDBOX_LIBRARY_PATH = "/jig-runtime/lib";
21
+ var MAX_CONTROL_BYTES = 64 * 1024;
22
+ var MODULE_DESTINATION = "/jig/linux-rootless-supervisor.ts";
23
+ var DIGEST = /^sha256:[0-9a-f]{64}$/;
24
+ var OWNER_TOKEN = /^[0-9a-f]{64}$/;
25
+ var ENVIRONMENT_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/;
26
+ async function supervisorMain(controlPath, startupTimeoutText) {
27
+ requireFixedBunPosture("supervisor");
28
+ if (!absolute(controlPath))
29
+ throw new Error("invalid rootless supervisor control path");
30
+ const startupTimeoutMs = Number(startupTimeoutText);
31
+ if (!/^[1-9][0-9]*$/.test(startupTimeoutText) || !positiveInteger(startupTimeoutMs) || startupTimeoutMs > 2147483647) {
32
+ throw new Error("invalid rootless supervisor startup timeout");
33
+ }
34
+ const startupDeadlineUnixMs = Date.now() + startupTimeoutMs;
35
+ const control = connect(controlPath);
36
+ control.on("error", () => {});
37
+ try {
38
+ await beforeStartupDeadline(connected(control), startupDeadlineUnixMs, control);
39
+ await superviseConnected(control, startupDeadlineUnixMs);
40
+ } finally {
41
+ control.destroy();
42
+ }
43
+ }
44
+ async function superviseConnected(control, startupDeadlineUnixMs) {
45
+ const iterator = readJsonLines(control)[Symbol.asyncIterator]();
46
+ const first = await beforeStartupDeadline(iterator.next(), startupDeadlineUnixMs, control);
47
+ if (first.done)
48
+ return;
49
+ const configuration = requireStart(first.value);
50
+ const { limits, runCgroup } = configuration;
51
+ const claim = await claimActive(configuration);
52
+ if (claim === "cancelled")
53
+ return;
54
+ let cgroupCreated = false;
55
+ let child;
56
+ let childExit;
57
+ let stopReason;
58
+ let admitted = false;
59
+ let resolveAdmission;
60
+ let rejectAdmission;
61
+ const admission = new Promise((resolve, reject) => {
62
+ resolveAdmission = resolve;
63
+ rejectAdmission = reject;
64
+ });
65
+ const killRun = async () => {
66
+ if (!cgroupCreated)
67
+ return;
68
+ await writeFile(`${runCgroup}/cgroup.kill`, `1
69
+ `).catch(ignoreMissing);
70
+ };
71
+ const stop = (reason) => {
72
+ if (stopReason === undefined || stopReason === "payload_exit")
73
+ stopReason = reason;
74
+ if (!admitted)
75
+ rejectAdmission(new Error(`rootless launch stopped before admission: ${reason}`));
76
+ child?.kill("SIGKILL");
77
+ killRun().catch(() => {});
78
+ };
79
+ const controlTask = (async () => {
80
+ for (;; ) {
81
+ const next = await iterator.next();
82
+ if (next.done)
83
+ return stop("coordinator_lost");
84
+ const type = recordType(next.value);
85
+ if (type === "admit" && !admitted) {
86
+ admitted = true;
87
+ resolveAdmission();
88
+ } else if (type === "cancel") {
89
+ stop("cancelled");
90
+ } else {
91
+ stop("cancelled");
92
+ }
93
+ }
94
+ })();
95
+ controlTask.catch(() => stop("coordinator_lost"));
96
+ const hardDeadlineUnixMs = limits.deadlineUnixMs + limits.cancellationGraceMs;
97
+ const deadline = setTimeout(() => stop("deadline"), Math.max(0, hardDeadlineUnixMs - Date.now()));
98
+ let exitCode = null;
99
+ let exitSignal = null;
100
+ let evidence = emptyEvidence();
101
+ let terminal;
102
+ try {
103
+ await mkdir(runCgroup, { mode: 493 });
104
+ cgroupCreated = true;
105
+ await writeFile(`${runCgroup}/memory.max`, `${limits.memoryBytes}
106
+ `);
107
+ if (await exists(`${runCgroup}/memory.swap.max`))
108
+ await writeFile(`${runCgroup}/memory.swap.max`, `0
109
+ `);
110
+ await writeFile(`${runCgroup}/pids.max`, `${limits.pids}
111
+ `);
112
+ await writeFile(`${runCgroup}/cpu.max`, `${limits.cpuQuotaMicros} ${limits.cpuPeriodMicros}
113
+ `);
114
+ if (stopReason !== undefined)
115
+ throw new Error(`rootless launch stopped during setup: ${stopReason}`);
116
+ safeSend(control, { type: "prepared", runCgroup, supervisorPid: process.pid });
117
+ await admission;
118
+ if (stopReason !== undefined)
119
+ throw new Error(`rootless launch stopped before spawn: ${stopReason}`);
120
+ const launched = spawn(configuration.bunPath, [
121
+ ...POLICY,
122
+ configuration.supervisorPath,
123
+ "--enter",
124
+ runCgroup,
125
+ configuration.ownerStateDirectory,
126
+ configuration.ownerToken,
127
+ configuration.ownerStateAllocationDigest,
128
+ configuration.bubblewrapPath,
129
+ ...bubblewrapArguments(configuration)
130
+ ], {
131
+ cwd: "/",
132
+ env: {
133
+ LD_LIBRARY_PATH: configuration.bunHostLibraryPath
134
+ },
135
+ stdio: ["inherit", "inherit", "inherit", "pipe"]
136
+ });
137
+ childExit = childClose(launched);
138
+ child = launched;
139
+ const ready = await readReady(launched);
140
+ safeSend(control, {
141
+ type: "ready",
142
+ runCgroup,
143
+ payloadPid: ready,
144
+ supervisorPid: process.pid
145
+ });
146
+ const exit = await childExit;
147
+ exitCode = exit.code;
148
+ exitSignal = exit.signal;
149
+ stopReason ??= "payload_exit";
150
+ } catch (error) {
151
+ stopReason ??= "setup_failed";
152
+ if (stopReason === "setup_failed")
153
+ process.stderr.write(`${errorText(error)}
154
+ `);
155
+ } finally {
156
+ clearTimeout(deadline);
157
+ let cleanupError;
158
+ try {
159
+ child?.kill("SIGKILL");
160
+ await killRun();
161
+ if (childExit !== undefined) {
162
+ await withinTimeout(childExit.catch(() => {
163
+ return;
164
+ }), limits.cleanupTimeoutMs, "rootless entry child did not settle");
165
+ }
166
+ if (cgroupCreated) {
167
+ await waitUntilEmpty(runCgroup, limits.cleanupTimeoutMs);
168
+ evidence = await readEvidence(runCgroup);
169
+ await removeEmptyCgroup(runCgroup, limits.cleanupTimeoutMs);
170
+ cgroupCreated = false;
171
+ }
172
+ } catch (error) {
173
+ cleanupError = errorText(error);
174
+ }
175
+ terminal = Object.freeze({
176
+ type: "terminal",
177
+ ownerDigest: configuration.ownerDigest,
178
+ runCgroup,
179
+ supervisorPid: process.pid,
180
+ exitCode,
181
+ signal: exitSignal,
182
+ stopReason: stopReason ?? "setup_failed",
183
+ fenced: cleanupError === undefined && !cgroupCreated,
184
+ ...cleanupError === undefined ? {} : { cleanupError },
185
+ evidence
186
+ });
187
+ if (terminal.fenced)
188
+ await persistFinal(configuration, terminal);
189
+ safeSend(control, terminal);
190
+ control.end();
191
+ }
192
+ }
193
+ async function enterMain(arguments_) {
194
+ requireFixedBunPosture("entry trampoline");
195
+ if (arguments_.length < 7)
196
+ throw new Error("invalid rootless entry trampoline arguments");
197
+ const [
198
+ runCgroup,
199
+ ownerStateDirectory,
200
+ ownerToken,
201
+ ownerStateAllocationDigest,
202
+ bubblewrap,
203
+ ...bubblewrapArguments_
204
+ ] = arguments_;
205
+ if (!absolute(runCgroup) || !absolute(ownerStateDirectory) || !absolute(bubblewrap)) {
206
+ throw new Error("invalid rootless entry trampoline path");
207
+ }
208
+ await writeFile(`${runCgroup}/cgroup.procs`, `0
209
+ `);
210
+ const current = readFileSync("/proc/self/cgroup", "utf8").split(`
211
+ `).find((line) => line.startsWith("0::"))?.slice(3);
212
+ if (`/sys/fs/cgroup${current ?? ""}` !== runCgroup) {
213
+ throw new Error("rootless entry trampoline did not enter the Run cgroup");
214
+ }
215
+ await requireActiveClaim(ownerStateDirectory, ownerToken, ownerStateAllocationDigest);
216
+ writeSync(3, `${process.pid}
217
+ `);
218
+ closeSync(3);
219
+ const child = spawn(bubblewrap, bubblewrapArguments_, { cwd: "/", env: {}, stdio: "inherit" });
220
+ const exit = await childClose(child);
221
+ if (exit.signal !== null)
222
+ process.kill(process.pid, exit.signal);
223
+ process.exitCode = exit.code ?? 1;
224
+ }
225
+ async function innerMain(command) {
226
+ if (command.length === 0 || !absolute(command[0]))
227
+ throw new Error("invalid rootless inner command");
228
+ const nullDevice = statSync("/dev/null");
229
+ const entropyDevice = statSync("/dev/urandom");
230
+ if (!nullDevice.isCharacterDevice() || !entropyDevice.isCharacterDevice() || (nullDevice.mode & 511) !== 438 || (entropyDevice.mode & 511) !== 438) {
231
+ throw new Error("rootless private devices do not satisfy the required projection");
232
+ }
233
+ const child = spawn(command[0], command.slice(1), { cwd: "/work", env: process.env, stdio: "inherit" });
234
+ const exit = await childClose(child);
235
+ if (exit.signal !== null)
236
+ process.kill(process.pid, exit.signal);
237
+ process.exitCode = exit.code ?? 1;
238
+ }
239
+ function bubblewrapArguments(configuration) {
240
+ const result = [
241
+ "--unshare-all",
242
+ ...configuration.network === "inherited" ? ["--share-net"] : [],
243
+ "--unshare-user",
244
+ "--disable-userns",
245
+ "--assert-userns-disabled",
246
+ "--as-pid-1",
247
+ "--die-with-parent",
248
+ "--new-session",
249
+ "--clearenv",
250
+ "--proc",
251
+ "/proc",
252
+ "--remount-ro",
253
+ "/proc",
254
+ "--dev",
255
+ "/dev",
256
+ "--tmpfs",
257
+ "/tmp",
258
+ "--tmpfs",
259
+ "/run",
260
+ "--dir",
261
+ "/work",
262
+ "--dir",
263
+ "/etc",
264
+ "--dir",
265
+ "/jig",
266
+ "--dir",
267
+ "/jig-runtime",
268
+ "--dir",
269
+ "/jig-runtime/lib",
270
+ "--dir",
271
+ "/lib64",
272
+ "--chdir",
273
+ "/work"
274
+ ];
275
+ for (const mount of configuration.readOnlyMounts)
276
+ result.push("--ro-bind", mount.source, mount.destination);
277
+ result.push("--ro-bind", configuration.supervisorPath, MODULE_DESTINATION);
278
+ for (const [name, value] of Object.entries(configuration.environment).sort(([a], [b]) => a.localeCompare(b))) {
279
+ result.push("--setenv", name, value);
280
+ }
281
+ result.push("--setenv", "LD_LIBRARY_PATH", SANDBOX_LIBRARY_PATH, "--uid", String(configuration.payloadUid), "--gid", String(configuration.payloadGid), "--cap-drop", "ALL", "--", SANDBOX_BUN, ...POLICY, MODULE_DESTINATION, "--inner", "--", ...configuration.command);
282
+ return result;
283
+ }
284
+ async function claimActive(configuration) {
285
+ await requireOwnerRecord(configuration);
286
+ const path = `${configuration.ownerStateDirectory}/claim.json`;
287
+ try {
288
+ const file = await open(path, "wx", 384);
289
+ try {
290
+ await file.writeFile(`${JSON.stringify({
291
+ allocationDigest: configuration.ownerStateAllocationDigest,
292
+ kind: "private-linux-owner-claim/1",
293
+ state: "active",
294
+ token: configuration.ownerToken
295
+ })}
296
+ `, "utf8");
297
+ await file.sync();
298
+ } finally {
299
+ await file.close();
300
+ }
301
+ await syncDirectory(configuration.ownerStateDirectory);
302
+ return "created";
303
+ } catch (error) {
304
+ if (!hasCode(error, "EEXIST"))
305
+ throw error;
306
+ }
307
+ const claim = JSON.parse(await readFile(path, "utf8"));
308
+ if (claim.allocationDigest !== configuration.ownerStateAllocationDigest || claim.kind !== "private-linux-owner-claim/1" || claim.token !== configuration.ownerToken || claim.state !== "active" && claim.state !== "cancelled") {
309
+ throw new Error("rootless owner claim is invalid");
310
+ }
311
+ if (claim.state === "active") {
312
+ throw new Error("rootless owner claim is already active");
313
+ }
314
+ return "cancelled";
315
+ }
316
+ async function requireActiveClaim(directory, token, allocationDigest) {
317
+ const claim = JSON.parse(await readFile(`${directory}/claim.json`, "utf8"));
318
+ if (claim.kind !== "private-linux-owner-claim/1" || claim.state !== "active" || claim.token !== token || claim.allocationDigest !== allocationDigest) {
319
+ throw new Error("rootless owner was cancelled before entry");
320
+ }
321
+ }
322
+ async function requireOwnerRecord(configuration) {
323
+ const value = JSON.parse(await readFile(`${configuration.ownerStateDirectory}/owner.json`, "utf8"));
324
+ if (value.kind !== "private-linux-owner-state/1" || value.allocationDigest !== configuration.ownerStateAllocationDigest || value.token !== configuration.ownerToken || value.ownerDigest !== configuration.ownerDigest || value.runCgroup !== configuration.runCgroup || value.mechanismDigest !== configuration.mechanismDigest || value.sealedPlanDigest !== configuration.sealedPlanDigest) {
325
+ throw new Error("rootless owner-state record is invalid");
326
+ }
327
+ }
328
+ async function persistFinal(configuration, terminal) {
329
+ const directory = configuration.ownerStateDirectory;
330
+ const target = `${directory}/final.json`;
331
+ const temporary = `${directory}/.final-${randomBytes(8).toString("hex")}`;
332
+ const file = await open(temporary, "wx", 384);
333
+ try {
334
+ await file.writeFile(`${JSON.stringify(terminal)}
335
+ `, "utf8");
336
+ await file.sync();
337
+ } finally {
338
+ await file.close();
339
+ }
340
+ try {
341
+ await link(temporary, target);
342
+ await syncDirectory(directory);
343
+ } catch (error) {
344
+ if (!hasCode(error, "EEXIST"))
345
+ throw error;
346
+ const existing = await readFile(target, "utf8");
347
+ if (existing !== `${JSON.stringify(terminal)}
348
+ `) {
349
+ throw new Error("rootless cleanup owners produced conflicting fence receipts");
350
+ }
351
+ } finally {
352
+ await unlink(temporary).catch(() => {
353
+ return;
354
+ });
355
+ }
356
+ }
357
+ function requireStart(value) {
358
+ if (recordType(value) !== "start" || value === null || typeof value !== "object" || !("configuration" in value))
359
+ throw new Error("invalid rootless supervisor start message");
360
+ const configuration = value.configuration;
361
+ const keys = [
362
+ "bubblewrapPath",
363
+ "bunHostLibraryPath",
364
+ "bunPath",
365
+ "command",
366
+ "delegatedCgroup",
367
+ "environment",
368
+ "limits",
369
+ "mechanismDigest",
370
+ "network",
371
+ "ownerDigest",
372
+ "ownerStateAllocationDigest",
373
+ "ownerStateDirectory",
374
+ "ownerToken",
375
+ "payloadGid",
376
+ "payloadUid",
377
+ "readOnlyMounts",
378
+ "runCgroup",
379
+ "sealedPlanDigest",
380
+ "supervisorPath"
381
+ ];
382
+ if (configuration === null || typeof configuration !== "object" || Array.isArray(configuration) || Object.keys(configuration).sort().join("\x00") !== keys.sort().join("\x00") || !absolute(configuration.delegatedCgroup) || !absolute(configuration.bunHostLibraryPath) || !absolute(configuration.runCgroup) || posix.dirname(configuration.runCgroup) !== configuration.delegatedCgroup || !/^jig-run-[a-z0-9][a-z0-9-]{0,47}-[0-9a-f]{24}$/.test(posix.basename(configuration.runCgroup)) || !configuration.runCgroup.startsWith("/sys/fs/cgroup/") || !absolute(configuration.ownerStateDirectory) || !absolute(configuration.bunPath) || !absolute(configuration.bubblewrapPath) || !absolute(configuration.supervisorPath) || typeof configuration.mechanismDigest !== "string" || !DIGEST.test(configuration.mechanismDigest) || typeof configuration.ownerDigest !== "string" || !DIGEST.test(configuration.ownerDigest) || typeof configuration.sealedPlanDigest !== "string" || !DIGEST.test(configuration.sealedPlanDigest) || typeof configuration.ownerStateAllocationDigest !== "string" || !DIGEST.test(configuration.ownerStateAllocationDigest) || typeof configuration.ownerToken !== "string" || !OWNER_TOKEN.test(configuration.ownerToken) || configuration.network !== "isolated" && configuration.network !== "inherited" || !positiveInteger(configuration.payloadUid) || !positiveInteger(configuration.payloadGid) || !validLimits(configuration.limits) || !validMounts(configuration.readOnlyMounts) || !validEnvironment(configuration.environment) || !Array.isArray(configuration.command) || configuration.command.length === 0 || !absolute(configuration.command[0]) || configuration.command.some((part) => typeof part !== "string" || part.includes("\x00"))) {
383
+ throw new Error("invalid rootless supervisor configuration");
384
+ }
385
+ return configuration;
386
+ }
387
+ function validLimits(value) {
388
+ if (value === null || typeof value !== "object" || Array.isArray(value))
389
+ return false;
390
+ const limits = value;
391
+ const keys = [
392
+ "cancellationGraceMs",
393
+ "cleanupTimeoutMs",
394
+ "cpuPeriodMicros",
395
+ "cpuQuotaMicros",
396
+ "deadlineUnixMs",
397
+ "memoryBytes",
398
+ "pids"
399
+ ];
400
+ return Object.keys(limits).sort().join("\x00") === keys.sort().join("\x00") && positiveInteger(limits.memoryBytes) && positiveInteger(limits.pids) && positiveInteger(limits.cpuQuotaMicros) && positiveInteger(limits.cpuPeriodMicros) && nonnegativeInteger(limits.deadlineUnixMs) && positiveInteger(limits.cancellationGraceMs) && positiveInteger(limits.cleanupTimeoutMs) && Number(limits.deadlineUnixMs) + Number(limits.cancellationGraceMs) <= Number.MAX_SAFE_INTEGER && Number(limits.deadlineUnixMs) + Number(limits.cancellationGraceMs) - Date.now() <= 2147483647;
401
+ }
402
+ function validMounts(value) {
403
+ return Array.isArray(value) && value.every((mount) => {
404
+ if (mount === null || typeof mount !== "object" || Array.isArray(mount))
405
+ return false;
406
+ const record = mount;
407
+ return Object.keys(record).sort().join("\x00") === "destination\x00source" && typeof record.source === "string" && absolute(record.source) && typeof record.destination === "string" && absolute(record.destination);
408
+ });
409
+ }
410
+ function validEnvironment(value) {
411
+ if (value === null || typeof value !== "object" || Array.isArray(value))
412
+ return false;
413
+ return Object.entries(value).every(([name, content]) => ENVIRONMENT_NAME.test(name) && name !== "BUN_BE_BUN" && name !== "LD_LIBRARY_PATH" && typeof content === "string" && !content.includes("\x00"));
414
+ }
415
+ function positiveInteger(value) {
416
+ return Number.isSafeInteger(value) && Number(value) > 0;
417
+ }
418
+ function nonnegativeInteger(value) {
419
+ return Number.isSafeInteger(value) && Number(value) >= 0;
420
+ }
421
+ function recordType(value) {
422
+ return value !== null && typeof value === "object" && !Array.isArray(value) && "type" in value && typeof value.type === "string" ? value.type : undefined;
423
+ }
424
+ async function* readJsonLines(stream) {
425
+ let buffer = "";
426
+ for await (const chunk of stream) {
427
+ buffer += String(chunk);
428
+ if (Buffer.byteLength(buffer) > MAX_CONTROL_BYTES)
429
+ throw new Error("rootless control stream overflow");
430
+ let newline = buffer.indexOf(`
431
+ `);
432
+ while (newline !== -1) {
433
+ const line = buffer.slice(0, newline);
434
+ buffer = buffer.slice(newline + 1);
435
+ if (line !== "")
436
+ yield JSON.parse(line);
437
+ newline = buffer.indexOf(`
438
+ `);
439
+ }
440
+ }
441
+ if (buffer !== "")
442
+ throw new Error("rootless control stream ended mid-message");
443
+ }
444
+ function safeSend(stream, value) {
445
+ try {
446
+ stream.write(`${JSON.stringify(value)}
447
+ `);
448
+ } catch {}
449
+ }
450
+ async function readReady(child) {
451
+ const stream = child.stdio[3];
452
+ if (stream === null || stream === undefined)
453
+ throw new Error("rootless entry readiness pipe is unavailable");
454
+ let text = "";
455
+ for await (const chunk of stream) {
456
+ text += String(chunk);
457
+ if (text.length > 32)
458
+ throw new Error("invalid rootless entry readiness receipt");
459
+ }
460
+ const pid = Number(text.trim());
461
+ if (!Number.isSafeInteger(pid) || pid <= 0)
462
+ throw new Error("invalid rootless entry readiness receipt");
463
+ return pid;
464
+ }
465
+ async function waitUntilEmpty(cgroup, timeoutMs) {
466
+ const deadline = Date.now() + timeoutMs;
467
+ while (Date.now() <= deadline) {
468
+ if ((await readCounters(`${cgroup}/cgroup.events`)).populated === 0)
469
+ return;
470
+ await new Promise((resolve) => setTimeout(resolve, 10));
471
+ }
472
+ throw new Error("rootless Run cgroup did not become empty");
473
+ }
474
+ async function removeEmptyCgroup(cgroup, timeoutMs) {
475
+ const deadline = Date.now() + timeoutMs;
476
+ for (;; ) {
477
+ try {
478
+ await rmdir(cgroup);
479
+ return;
480
+ } catch (error) {
481
+ if (!hasCode(error, "EBUSY") || Date.now() > deadline)
482
+ throw error;
483
+ await new Promise((resolve) => setTimeout(resolve, 10));
484
+ }
485
+ }
486
+ }
487
+ async function readEvidence(cgroup) {
488
+ return Object.freeze({
489
+ cpuStat: Object.freeze(await readCounters(`${cgroup}/cpu.stat`)),
490
+ memoryEvents: Object.freeze(await readCounters(`${cgroup}/memory.events`)),
491
+ pidsEvents: Object.freeze(await readCounters(`${cgroup}/pids.events`))
492
+ });
493
+ }
494
+ async function readCounters(path) {
495
+ const result = Object.create(null);
496
+ for (const line of (await readFile(path, "utf8")).trim().split(`
497
+ `)) {
498
+ if (line === "")
499
+ continue;
500
+ const [name, raw] = line.trim().split(/\s+/, 2);
501
+ const value = Number(raw);
502
+ if (name === undefined || !Number.isSafeInteger(value) || value < 0)
503
+ throw new Error(`invalid counter in ${path}`);
504
+ result[name] = value;
505
+ }
506
+ return result;
507
+ }
508
+ function emptyEvidence() {
509
+ return Object.freeze({ cpuStat: Object.freeze({}), memoryEvents: Object.freeze({}), pidsEvents: Object.freeze({}) });
510
+ }
511
+ async function exists(path) {
512
+ try {
513
+ await stat(path);
514
+ return true;
515
+ } catch (error) {
516
+ if (hasCode(error, "ENOENT"))
517
+ return false;
518
+ throw error;
519
+ }
520
+ }
521
+ function ignoreMissing(error) {
522
+ if (!hasCode(error, "ENOENT"))
523
+ throw error;
524
+ }
525
+ function hasCode(error, code) {
526
+ return error !== null && typeof error === "object" && error.code === code;
527
+ }
528
+ function absolute(path) {
529
+ return typeof path === "string" && path.startsWith("/") && !path.includes("\x00") && posix.normalize(path) === path;
530
+ }
531
+ function requireFixedBunPosture(label) {
532
+ const environmentKeys = Object.keys(process.env).sort();
533
+ if (process.cwd() !== "/" || environmentKeys.join("\x00") !== "LD_LIBRARY_PATH" || !absolute(process.env.LD_LIBRARY_PATH ?? "") || process.execArgv.length !== POLICY.length || process.execArgv.some((value, index) => value !== POLICY[index])) {
534
+ throw new Error(`rootless ${label} has an invalid startup posture`);
535
+ }
536
+ }
537
+ function childClose(child) {
538
+ return new Promise((resolve, reject) => {
539
+ let failure;
540
+ child.on("error", (error) => {
541
+ failure ??= error;
542
+ });
543
+ child.once("close", (code, signal) => {
544
+ if (failure === undefined)
545
+ resolve({ code, signal });
546
+ else
547
+ reject(failure);
548
+ });
549
+ });
550
+ }
551
+ function withinTimeout(promise, timeoutMs, message) {
552
+ return new Promise((resolve, reject) => {
553
+ const timer = setTimeout(() => reject(new Error(message)), timeoutMs);
554
+ promise.then((value) => {
555
+ clearTimeout(timer);
556
+ resolve(value);
557
+ }, (error) => {
558
+ clearTimeout(timer);
559
+ reject(error);
560
+ });
561
+ });
562
+ }
563
+ async function syncDirectory(path) {
564
+ const handle = await open(path, "r");
565
+ try {
566
+ await handle.sync();
567
+ } finally {
568
+ await handle.close();
569
+ }
570
+ }
571
+ function connected(socket) {
572
+ return new Promise((resolve, reject) => {
573
+ socket.once("connect", resolve);
574
+ socket.once("error", reject);
575
+ });
576
+ }
577
+ function beforeStartupDeadline(promise, deadlineUnixMs, control) {
578
+ return new Promise((resolve, reject) => {
579
+ const remaining = deadlineUnixMs - Date.now();
580
+ if (remaining <= 0) {
581
+ control.destroy();
582
+ reject(new Error("rootless supervisor start timed out"));
583
+ return;
584
+ }
585
+ const timer = setTimeout(() => {
586
+ control.destroy();
587
+ reject(new Error("rootless supervisor start timed out"));
588
+ }, remaining);
589
+ promise.then((value) => {
590
+ clearTimeout(timer);
591
+ resolve(value);
592
+ }, (error) => {
593
+ clearTimeout(timer);
594
+ reject(error);
595
+ });
596
+ });
597
+ }
598
+ function errorText(error) {
599
+ return error instanceof Error ? error.message : String(error);
600
+ }
601
+ async function main() {
602
+ const [mode, ...arguments_] = process.argv.slice(2);
603
+ if (mode === "--supervisor") {
604
+ if (arguments_.length !== 2)
605
+ throw new Error("invalid rootless supervisor arguments");
606
+ return await supervisorMain(arguments_[0], arguments_[1]);
607
+ }
608
+ if (mode === "--enter")
609
+ return await enterMain(arguments_);
610
+ if (mode === "--inner") {
611
+ if (arguments_[0] !== "--")
612
+ throw new Error("invalid rootless inner separator");
613
+ return await innerMain(arguments_.slice(1));
614
+ }
615
+ throw new Error("invalid rootless supervisor mode");
616
+ }
617
+ if (import.meta.main) {
618
+ main().catch((error) => {
619
+ process.stderr.write(`jig rootless supervisor failed: ${errorText(error)}
620
+ `);
621
+ process.exitCode = 70;
622
+ });
623
+ }