sandboxedjs 0.1.36 → 0.1.37
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/dist/agent.cjs +584 -0
- package/dist/agent.cjs.map +1 -0
- package/dist/agent.d.cts +208 -0
- package/dist/agent.d.ts +208 -0
- package/dist/agent.js +577 -0
- package/dist/agent.js.map +1 -0
- package/dist/container-BsPKqY9R.d.cts +1789 -0
- package/dist/container-BsPKqY9R.d.ts +1789 -0
- package/dist/index.d.cts +3 -1789
- package/dist/index.d.ts +3 -1789
- package/package.json +6 -1
|
@@ -0,0 +1,1789 @@
|
|
|
1
|
+
/** The handle a pod's process manager returns. */
|
|
2
|
+
interface ChildHandle {
|
|
3
|
+
pid: number;
|
|
4
|
+
state: "starting" | "running" | "exited";
|
|
5
|
+
exitCode: number | undefined;
|
|
6
|
+
on(event: "stdout" | "stderr" | "exit" | "rawmode", listener: (...args: any[]) => void): unknown;
|
|
7
|
+
exec(): void;
|
|
8
|
+
sendStdin(data: string): void;
|
|
9
|
+
/**
|
|
10
|
+
* Close the child's input.
|
|
11
|
+
*
|
|
12
|
+
* Without this a child that reads stdin to EOF — every filter, and every
|
|
13
|
+
* tool a library pipes into, `xsel` and `base64` alike — waits forever for
|
|
14
|
+
* an end that never comes, and the parent waits on its exit.
|
|
15
|
+
*/
|
|
16
|
+
endStdin?(): void;
|
|
17
|
+
kill(signal?: string): void;
|
|
18
|
+
}
|
|
19
|
+
interface ChildSpawnConfig {
|
|
20
|
+
command: string;
|
|
21
|
+
args?: string[];
|
|
22
|
+
cwd?: string;
|
|
23
|
+
env?: Record<string, string>;
|
|
24
|
+
parentPid?: number;
|
|
25
|
+
/**
|
|
26
|
+
* The child was given the parent's streams (`stdio: "inherit"`).
|
|
27
|
+
*
|
|
28
|
+
* Its input is then the parent's terminal rather than a pipe that will end,
|
|
29
|
+
* which is the difference between a program that waits for what the user
|
|
30
|
+
* types and one that reads to end-of-input and stops.
|
|
31
|
+
*/
|
|
32
|
+
inheritStdio?: boolean;
|
|
33
|
+
}
|
|
34
|
+
type SpawnChild = (config: ChildSpawnConfig) => ChildHandle;
|
|
35
|
+
/**
|
|
36
|
+
* Run a child to completion without returning to the event loop.
|
|
37
|
+
*
|
|
38
|
+
* Supplied only by a pod that can actually block — one whose guest runs on its
|
|
39
|
+
* own thread. Where it is absent the synchronous entry points keep reporting
|
|
40
|
+
* that they are unavailable, which is the honest answer for an in-realm pod.
|
|
41
|
+
*/
|
|
42
|
+
type SyncSpawn = (request: {
|
|
43
|
+
command: string;
|
|
44
|
+
args: string[];
|
|
45
|
+
cwd: string;
|
|
46
|
+
env?: Record<string, string>;
|
|
47
|
+
input?: string;
|
|
48
|
+
inheritStdio?: boolean;
|
|
49
|
+
}) => {
|
|
50
|
+
status: number | null;
|
|
51
|
+
stdout: string;
|
|
52
|
+
stderr: string;
|
|
53
|
+
signal: string | null;
|
|
54
|
+
error?: {
|
|
55
|
+
code?: string;
|
|
56
|
+
message: string;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
declare function createChildProcessModule(spawnChild: SpawnChild, defaultCwd: () => string, syncSpawn?: SyncSpawn, defaultEnv?: () => Record<string, string>): Record<string, unknown>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Clean-room contracts between SandboxedJS and its JavaScript runtime.
|
|
63
|
+
*
|
|
64
|
+
* These deliberately describe only behavior SandboxedJS consumes. Runtime
|
|
65
|
+
* implementations may use Web Workers in browsers or worker_threads on Node.
|
|
66
|
+
*/
|
|
67
|
+
|
|
68
|
+
interface VolumeStats {
|
|
69
|
+
totalBytes: number;
|
|
70
|
+
fileCount: number;
|
|
71
|
+
/** New runtime spelling. */
|
|
72
|
+
directoryCount?: number;
|
|
73
|
+
/** Compatibility spelling used by existing volume implementations. */
|
|
74
|
+
dirCount?: number;
|
|
75
|
+
}
|
|
76
|
+
interface VolumeStat {
|
|
77
|
+
mode: number;
|
|
78
|
+
size: number;
|
|
79
|
+
uid: number;
|
|
80
|
+
gid: number;
|
|
81
|
+
ino: number;
|
|
82
|
+
nlink: number;
|
|
83
|
+
atimeMs: number;
|
|
84
|
+
mtimeMs: number;
|
|
85
|
+
ctimeMs: number;
|
|
86
|
+
birthtimeMs: number;
|
|
87
|
+
isFile(): boolean;
|
|
88
|
+
isDirectory(): boolean;
|
|
89
|
+
isSymbolicLink(): boolean;
|
|
90
|
+
}
|
|
91
|
+
interface RuntimeVolume {
|
|
92
|
+
readFileSync(path: string): Uint8Array;
|
|
93
|
+
writeFileSync(path: string, data: string | Uint8Array): void;
|
|
94
|
+
appendFileSync(path: string, data: string | Uint8Array): void;
|
|
95
|
+
readdirSync(path: string): string[];
|
|
96
|
+
lstatSync(path: string): VolumeStat;
|
|
97
|
+
readlinkSync(path: string): string;
|
|
98
|
+
mkdirSync(path: string, options?: {
|
|
99
|
+
mode?: number;
|
|
100
|
+
}): void;
|
|
101
|
+
rmdirSync(path: string): void;
|
|
102
|
+
unlinkSync(path: string): void;
|
|
103
|
+
renameSync(from: string, to: string): void;
|
|
104
|
+
symlinkSync(target: string, path: string): void;
|
|
105
|
+
linkSync(existing: string, path: string): void;
|
|
106
|
+
truncateSync(path: string, length?: number): void;
|
|
107
|
+
chmodSync(path: string, mode: number): void;
|
|
108
|
+
lchmodSync(path: string, mode: number): void;
|
|
109
|
+
chownSync(path: string, uid: number, gid: number): void;
|
|
110
|
+
lchownSync(path: string, uid: number, gid: number): void;
|
|
111
|
+
utimesSync(path: string, atime: Date, mtime: Date): void;
|
|
112
|
+
getStats(): VolumeStats;
|
|
113
|
+
}
|
|
114
|
+
interface RuntimeProcessResult {
|
|
115
|
+
exitCode: number;
|
|
116
|
+
stdout: string;
|
|
117
|
+
stderr: string;
|
|
118
|
+
}
|
|
119
|
+
interface RuntimeProcess {
|
|
120
|
+
readonly completion: Promise<RuntimeProcessResult>;
|
|
121
|
+
/**
|
|
122
|
+
* `output` and `error` carry stdout and stderr; `exit` the code. `rawmode`
|
|
123
|
+
* reports the program turning terminal raw mode on or off, which a terminal
|
|
124
|
+
* needs so that it stops echoing input the program is drawing itself.
|
|
125
|
+
*/
|
|
126
|
+
on(event: "output" | "error" | "exit" | "rawmode", listener: (...args: any[]) => void): this;
|
|
127
|
+
write(data: string): void;
|
|
128
|
+
kill(signal?: string): void;
|
|
129
|
+
}
|
|
130
|
+
interface RuntimeHttpResponse {
|
|
131
|
+
statusCode?: number;
|
|
132
|
+
statusMessage?: string;
|
|
133
|
+
headers?: Record<string, string>;
|
|
134
|
+
body?: string | Uint8Array | ArrayBuffer;
|
|
135
|
+
}
|
|
136
|
+
interface RuntimePackageInstaller {
|
|
137
|
+
install(name: string, version?: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
138
|
+
installFromManifest(path: string, options?: Record<string, unknown>): Promise<unknown>;
|
|
139
|
+
/** Create a view that installs into another project root. */
|
|
140
|
+
forCwd?(cwd: string): RuntimePackageInstaller;
|
|
141
|
+
}
|
|
142
|
+
/** The process-manager protocol a container's kernel bridge substitutes for. */
|
|
143
|
+
interface RuntimeProcessManager {
|
|
144
|
+
spawn(config: ChildSpawnConfig): ChildHandle;
|
|
145
|
+
}
|
|
146
|
+
interface RuntimePod {
|
|
147
|
+
readonly volume: RuntimeVolume;
|
|
148
|
+
readonly packages: RuntimePackageInstaller;
|
|
149
|
+
readonly instanceId: string;
|
|
150
|
+
readonly processManager: RuntimeProcessManager;
|
|
151
|
+
readonly proxy: {
|
|
152
|
+
activePorts(instanceId?: string): number[];
|
|
153
|
+
};
|
|
154
|
+
spawn(command: string, args?: string[], options?: Record<string, unknown>): Promise<RuntimeProcess>;
|
|
155
|
+
request(port: number, init?: Record<string, unknown>): Promise<RuntimeHttpResponse>;
|
|
156
|
+
snapshot(options?: Record<string, unknown>): unknown;
|
|
157
|
+
restore(snapshot: unknown, options?: Record<string, unknown>): Promise<void>;
|
|
158
|
+
teardown(): void;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
type FileKind = "file" | "directory" | "symlink" | "chardev" | "blockdev" | "fifo" | "socket";
|
|
162
|
+
/** Render as `drwxr-xr-x`, honouring setuid/setgid/sticky. */
|
|
163
|
+
declare function formatMode(mode: number): string;
|
|
164
|
+
/** Zero-padded octal permissions, as `stat -c %a`/`%04a` would show. */
|
|
165
|
+
declare function octalMode(mode: number, width?: number): string;
|
|
166
|
+
/**
|
|
167
|
+
* Apply a `chmod` spec to an existing mode. Accepts octal (`755`, `0644`) and
|
|
168
|
+
* the symbolic grammar (`u+rwx,go-w`, `a=r`, `+X`, `u+s`, `o+t`).
|
|
169
|
+
*
|
|
170
|
+
* @param isDir whether the target is a directory — needed for the `X` flag.
|
|
171
|
+
*/
|
|
172
|
+
declare function applyChmod(spec: string, current: number, isDir: boolean, umask?: number): number;
|
|
173
|
+
/** Parse the `umask` builtin's argument. */
|
|
174
|
+
declare function parseUmask(spec: string): number;
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* The `Stats` object handed back by `Vfs.stat`. Shaped like `fs.Stats` so it
|
|
178
|
+
* feels familiar, but with a real `st_mode` that carries the file-type bits
|
|
179
|
+
* (which the underlying volume stores separately).
|
|
180
|
+
*/
|
|
181
|
+
|
|
182
|
+
interface StatInit {
|
|
183
|
+
mode: number;
|
|
184
|
+
size: number;
|
|
185
|
+
uid: number;
|
|
186
|
+
gid: number;
|
|
187
|
+
ino: number;
|
|
188
|
+
nlink: number;
|
|
189
|
+
atimeMs: number;
|
|
190
|
+
mtimeMs: number;
|
|
191
|
+
ctimeMs: number;
|
|
192
|
+
birthtimeMs?: number;
|
|
193
|
+
dev?: number;
|
|
194
|
+
rdev?: number;
|
|
195
|
+
blksize?: number;
|
|
196
|
+
}
|
|
197
|
+
declare class Stats {
|
|
198
|
+
readonly mode: number;
|
|
199
|
+
readonly size: number;
|
|
200
|
+
readonly uid: number;
|
|
201
|
+
readonly gid: number;
|
|
202
|
+
readonly ino: number;
|
|
203
|
+
readonly nlink: number;
|
|
204
|
+
readonly dev: number;
|
|
205
|
+
readonly rdev: number;
|
|
206
|
+
readonly blksize: number;
|
|
207
|
+
readonly atimeMs: number;
|
|
208
|
+
readonly mtimeMs: number;
|
|
209
|
+
readonly ctimeMs: number;
|
|
210
|
+
readonly birthtimeMs: number;
|
|
211
|
+
constructor(init: StatInit);
|
|
212
|
+
get blocks(): number;
|
|
213
|
+
get atime(): Date;
|
|
214
|
+
get mtime(): Date;
|
|
215
|
+
get ctime(): Date;
|
|
216
|
+
get birthtime(): Date;
|
|
217
|
+
get kind(): FileKind;
|
|
218
|
+
isFile(): boolean;
|
|
219
|
+
isDirectory(): boolean;
|
|
220
|
+
isSymbolicLink(): boolean;
|
|
221
|
+
isCharacterDevice(): boolean;
|
|
222
|
+
isBlockDevice(): boolean;
|
|
223
|
+
isFIFO(): boolean;
|
|
224
|
+
isSocket(): boolean;
|
|
225
|
+
/** Permission bits only, with the type bits masked off. */
|
|
226
|
+
get perms(): number;
|
|
227
|
+
}
|
|
228
|
+
interface DirEntry {
|
|
229
|
+
name: string;
|
|
230
|
+
kind: FileKind;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The container's virtual filesystem.
|
|
235
|
+
*
|
|
236
|
+
* Real file content lives in the RuntimePod's `MemoryVolume`, deliberately the
|
|
237
|
+
* *same* volume the Node.js worker processes see — so a file written by `echo`
|
|
238
|
+
* is readable by `require('fs')` inside a spawned script, and vice versa.
|
|
239
|
+
*
|
|
240
|
+
* On top of that volume this layer adds the parts a Linux userland expects and
|
|
241
|
+
* the raw volume does not have: file-type bits in `st_mode`, permission and
|
|
242
|
+
* ownership checks, an `O_*` open/fd table, and pluggable *virtual providers*
|
|
243
|
+
* that synthesise `/proc`, `/sys` and `/dev` on demand.
|
|
244
|
+
*/
|
|
245
|
+
|
|
246
|
+
/** The identity a filesystem operation runs as. */
|
|
247
|
+
interface Cred {
|
|
248
|
+
uid: number;
|
|
249
|
+
gid: number;
|
|
250
|
+
groups: number[];
|
|
251
|
+
umask: number;
|
|
252
|
+
}
|
|
253
|
+
declare const ROOT_CRED: Cred;
|
|
254
|
+
declare function makeCred(uid: number, gid: number, groups?: number[], umask?: number): Cred;
|
|
255
|
+
/** A file that does not live in the volume — `/proc/uptime`, `/dev/null`, … */
|
|
256
|
+
interface VirtualNode {
|
|
257
|
+
kind: FileKind;
|
|
258
|
+
/** Permission bits only; the type bits are added from `kind`. */
|
|
259
|
+
mode: number;
|
|
260
|
+
uid?: number;
|
|
261
|
+
gid?: number;
|
|
262
|
+
size?: number;
|
|
263
|
+
mtimeMs?: number;
|
|
264
|
+
/** Symlink target, when `kind === "symlink"`. */
|
|
265
|
+
target?: string;
|
|
266
|
+
read?(): Uint8Array | string;
|
|
267
|
+
write?(data: Uint8Array, append: boolean): void;
|
|
268
|
+
/** Directory listing, when `kind === "directory"`. */
|
|
269
|
+
list?(): string[];
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Supplies a subtree of synthetic files. `resolve` receives the path *relative*
|
|
273
|
+
* to `root` ("" means the mount point itself) and returns null for misses.
|
|
274
|
+
*/
|
|
275
|
+
interface VirtualProvider {
|
|
276
|
+
root: string;
|
|
277
|
+
resolve(rel: string): VirtualNode | null;
|
|
278
|
+
/**
|
|
279
|
+
* When true (the default) the provider owns its whole subtree and a miss is
|
|
280
|
+
* `ENOENT` — that is what `/proc` wants, so a dead pid does not resolve to a
|
|
281
|
+
* stale on-disk file. `/dev` and `/sys` set this to false so that synthetic
|
|
282
|
+
* nodes overlay a real directory users can still write into.
|
|
283
|
+
*/
|
|
284
|
+
exclusive?: boolean;
|
|
285
|
+
}
|
|
286
|
+
interface WriteOptions {
|
|
287
|
+
mode?: number;
|
|
288
|
+
append?: boolean;
|
|
289
|
+
cred?: Cred;
|
|
290
|
+
/** Skip the permission check — used by kernel-internal writes. */
|
|
291
|
+
privileged?: boolean;
|
|
292
|
+
}
|
|
293
|
+
interface ResolveOptions {
|
|
294
|
+
cred?: Cred;
|
|
295
|
+
/** Follow a symlink in the final position. Off for `lstat`, `rm`, `chmod -h`. */
|
|
296
|
+
followFinal?: boolean;
|
|
297
|
+
}
|
|
298
|
+
declare class Vfs {
|
|
299
|
+
readonly volume: RuntimeVolume;
|
|
300
|
+
private readonly providers;
|
|
301
|
+
private nextVirtualIno;
|
|
302
|
+
private readonly virtualInos;
|
|
303
|
+
constructor(volume: RuntimeVolume);
|
|
304
|
+
addProvider(provider: VirtualProvider): void;
|
|
305
|
+
removeProvider(root: string): void;
|
|
306
|
+
/** The mount points currently served synthetically. */
|
|
307
|
+
get virtualRoots(): string[];
|
|
308
|
+
private lookupVirtual;
|
|
309
|
+
/** True when a miss at `abs` must be ENOENT rather than a volume lookup. */
|
|
310
|
+
private isUnderProvider;
|
|
311
|
+
/** Synthetic children a non-exclusive provider contributes to a directory. */
|
|
312
|
+
private virtualChildren;
|
|
313
|
+
private virtualIno;
|
|
314
|
+
/** True when `cred` may perform `mode` (R_OK/W_OK/X_OK) on a stat result. */
|
|
315
|
+
permitted(st: Stats, mode: number, cred: Cred): boolean;
|
|
316
|
+
private require;
|
|
317
|
+
/**
|
|
318
|
+
* Walk `abs` component by component, following symlinks and checking search
|
|
319
|
+
* (`+x`) permission on every directory along the way, exactly like `namei`.
|
|
320
|
+
*
|
|
321
|
+
* Returns the fully resolved absolute path. Does *not* require the final
|
|
322
|
+
* component to exist — callers decide whether a miss is fatal.
|
|
323
|
+
*/
|
|
324
|
+
resolvePath(abs: string, opts?: ResolveOptions): string;
|
|
325
|
+
/** lstat that returns null instead of throwing, for internal probing. */
|
|
326
|
+
private tryLstat;
|
|
327
|
+
private readlinkRaw;
|
|
328
|
+
/** stat(2) — follows symlinks. */
|
|
329
|
+
stat(abs: string, opts?: {
|
|
330
|
+
cred?: Cred;
|
|
331
|
+
}): Stats;
|
|
332
|
+
/** lstat(2) — does not follow a symlink in the final position. */
|
|
333
|
+
lstat(abs: string): Stats;
|
|
334
|
+
private statFromVirtual;
|
|
335
|
+
exists(abs: string, cred?: Cred): boolean;
|
|
336
|
+
lexists(abs: string): boolean;
|
|
337
|
+
access(abs: string, mode?: number, cred?: Cred): void;
|
|
338
|
+
realpath(abs: string, cred?: Cred): string;
|
|
339
|
+
readFile(abs: string, cred?: Cred): Uint8Array;
|
|
340
|
+
readText(abs: string, cred?: Cred): string;
|
|
341
|
+
readdir(abs: string, cred?: Cred): string[];
|
|
342
|
+
/** True when the volume itself has a real directory at `abs`. */
|
|
343
|
+
private volumeHasDir;
|
|
344
|
+
readdirWithTypes(abs: string, cred?: Cred): DirEntry[];
|
|
345
|
+
readlink(abs: string, cred?: Cred): string;
|
|
346
|
+
writeFile(abs: string, data: Uint8Array | string, opts?: WriteOptions): void;
|
|
347
|
+
appendFile(abs: string, data: Uint8Array | string, opts?: WriteOptions): void;
|
|
348
|
+
truncate(abs: string, len?: number, cred?: Cred): void;
|
|
349
|
+
mkdir(abs: string, opts?: {
|
|
350
|
+
mode?: number;
|
|
351
|
+
recursive?: boolean;
|
|
352
|
+
cred?: Cred;
|
|
353
|
+
}): void;
|
|
354
|
+
private mkdirOne;
|
|
355
|
+
rmdir(abs: string, cred?: Cred): void;
|
|
356
|
+
unlink(abs: string, cred?: Cred): void;
|
|
357
|
+
/** Recursive delete, the engine behind `rm -r`. */
|
|
358
|
+
rmrf(abs: string, cred?: Cred): void;
|
|
359
|
+
private requireParentWrite;
|
|
360
|
+
rename(from: string, to: string, cred?: Cred): void;
|
|
361
|
+
copyFile(from: string, to: string, cred?: Cred): void;
|
|
362
|
+
symlink(target: string, linkPath: string, cred?: Cred): void;
|
|
363
|
+
link(existing: string, newPath: string, cred?: Cred): void;
|
|
364
|
+
chmod(abs: string, mode: number, cred?: Cred, follow?: boolean): void;
|
|
365
|
+
chown(abs: string, uid: number, gid: number, cred?: Cred, follow?: boolean): void;
|
|
366
|
+
utimes(abs: string, atimeMs: number, mtimeMs: number, cred?: Cred): void;
|
|
367
|
+
/** `touch` semantics: create when missing, otherwise bump the timestamps. */
|
|
368
|
+
touch(abs: string, cred?: Cred, timeMs?: number): void;
|
|
369
|
+
/** Depth-first walk yielding absolute paths. Symlinks are not followed. */
|
|
370
|
+
walk(abs: string, opts?: {
|
|
371
|
+
includeSelf?: boolean;
|
|
372
|
+
cred?: Cred;
|
|
373
|
+
maxDepth?: number;
|
|
374
|
+
}): Generator<string>;
|
|
375
|
+
/** Recursive copy used by `cp -r` and the container's `copyIn` helper. */
|
|
376
|
+
copyTree(from: string, to: string, cred?: Cred): void;
|
|
377
|
+
/** Free/used byte accounting for `df` and `du`. */
|
|
378
|
+
usage(abs?: string): {
|
|
379
|
+
files: number;
|
|
380
|
+
dirs: number;
|
|
381
|
+
bytes: number;
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Byte streams for stdin/stdout/stderr, pipelines and redirections.
|
|
387
|
+
*
|
|
388
|
+
* Everything here is promise-based rather than Node-stream based: commands are
|
|
389
|
+
* plain async functions, a pipeline is just a chain of `Pipe` objects, and a
|
|
390
|
+
* closed reader turns further writes into `EPIPE` the way a real pipe does.
|
|
391
|
+
*/
|
|
392
|
+
|
|
393
|
+
interface OutputStream {
|
|
394
|
+
write(data: Uint8Array | string): void;
|
|
395
|
+
end(): void;
|
|
396
|
+
/** True once the far end went away — producers should stop. */
|
|
397
|
+
readonly closed: boolean;
|
|
398
|
+
isTTY: boolean;
|
|
399
|
+
/** Terminal width, when this is a TTY. */
|
|
400
|
+
columns?: number;
|
|
401
|
+
rows?: number;
|
|
402
|
+
}
|
|
403
|
+
interface InputStream {
|
|
404
|
+
/** Resolves to null at EOF. `size` is a maximum, not a guarantee. */
|
|
405
|
+
read(size?: number): Promise<Uint8Array | null>;
|
|
406
|
+
readAll(): Promise<Uint8Array>;
|
|
407
|
+
/** One line without its terminator; null at EOF. */
|
|
408
|
+
readLine(): Promise<string | null>;
|
|
409
|
+
/** Bytes already buffered, for non-blocking peeks. */
|
|
410
|
+
readonly available: number;
|
|
411
|
+
close(): void;
|
|
412
|
+
isTTY: boolean;
|
|
413
|
+
/**
|
|
414
|
+
* True when the far end is a live caller who may never signal EOF — a
|
|
415
|
+
* terminal, or the `stdin` pipe of `Container.spawn`. Programs that would
|
|
416
|
+
* otherwise slurp stdin before starting must not block on these.
|
|
417
|
+
*/
|
|
418
|
+
readonly interactive?: boolean;
|
|
419
|
+
}
|
|
420
|
+
interface Stdio {
|
|
421
|
+
stdin: InputStream;
|
|
422
|
+
stdout: OutputStream;
|
|
423
|
+
stderr: OutputStream;
|
|
424
|
+
}
|
|
425
|
+
/** An in-memory pipe: writable on one end, readable on the other. */
|
|
426
|
+
declare class Pipe implements InputStream, OutputStream {
|
|
427
|
+
private chunks;
|
|
428
|
+
private buffered;
|
|
429
|
+
private writerClosed;
|
|
430
|
+
private readerClosed;
|
|
431
|
+
private wakers;
|
|
432
|
+
isTTY: boolean;
|
|
433
|
+
/** Set on pipes owned by an outside caller, who may never call `end()`. */
|
|
434
|
+
interactive: boolean;
|
|
435
|
+
/**
|
|
436
|
+
* Set while the running program has put the terminal in raw mode.
|
|
437
|
+
*
|
|
438
|
+
* A program in raw mode draws its own input — a prompt library redraws the
|
|
439
|
+
* whole line on every keystroke — so the terminal must stop echoing, or
|
|
440
|
+
* every character appears twice.
|
|
441
|
+
*/
|
|
442
|
+
private raw;
|
|
443
|
+
onRawMode?: (enabled: boolean) => void;
|
|
444
|
+
get rawMode(): boolean;
|
|
445
|
+
set rawMode(enabled: boolean);
|
|
446
|
+
columns: number | undefined;
|
|
447
|
+
rows: number | undefined;
|
|
448
|
+
get closed(): boolean;
|
|
449
|
+
get ended(): boolean;
|
|
450
|
+
get available(): number;
|
|
451
|
+
write(data: Uint8Array | string): void;
|
|
452
|
+
end(): void;
|
|
453
|
+
close(): void;
|
|
454
|
+
private wake;
|
|
455
|
+
private waitForData;
|
|
456
|
+
read(size?: number): Promise<Uint8Array | null>;
|
|
457
|
+
readAll(): Promise<Uint8Array>;
|
|
458
|
+
private lineRemainder;
|
|
459
|
+
readLine(): Promise<string | null>;
|
|
460
|
+
/** Seed the pipe with content then close it — handy for here-docs. */
|
|
461
|
+
static from(data: Uint8Array | string): Pipe;
|
|
462
|
+
static empty(): Pipe;
|
|
463
|
+
}
|
|
464
|
+
/** Discards everything; `/dev/null` as an output stream. */
|
|
465
|
+
declare class NullOutput implements OutputStream {
|
|
466
|
+
readonly closed = false;
|
|
467
|
+
isTTY: boolean;
|
|
468
|
+
write(): void;
|
|
469
|
+
end(): void;
|
|
470
|
+
}
|
|
471
|
+
/** Always at EOF; `/dev/null` as an input stream. */
|
|
472
|
+
declare class NullInput implements InputStream {
|
|
473
|
+
readonly available = 0;
|
|
474
|
+
isTTY: boolean;
|
|
475
|
+
read(): Promise<null>;
|
|
476
|
+
readAll(): Promise<Uint8Array>;
|
|
477
|
+
readLine(): Promise<null>;
|
|
478
|
+
close(): void;
|
|
479
|
+
}
|
|
480
|
+
/** Collects everything written, for `exec()` and command substitution. */
|
|
481
|
+
declare class BufferSink implements OutputStream {
|
|
482
|
+
private readonly onWrite?;
|
|
483
|
+
private chunks;
|
|
484
|
+
private total;
|
|
485
|
+
private _closed;
|
|
486
|
+
isTTY: boolean;
|
|
487
|
+
columns: number | undefined;
|
|
488
|
+
rows: number | undefined;
|
|
489
|
+
constructor(onWrite?: ((chunk: Uint8Array) => void) | undefined);
|
|
490
|
+
get closed(): boolean;
|
|
491
|
+
write(data: Uint8Array | string): void;
|
|
492
|
+
end(): void;
|
|
493
|
+
bytes(): Uint8Array;
|
|
494
|
+
text(): string;
|
|
495
|
+
get length(): number;
|
|
496
|
+
reset(): void;
|
|
497
|
+
}
|
|
498
|
+
/** Forwards each write to a callback — used to stream into a terminal. */
|
|
499
|
+
declare class CallbackSink implements OutputStream {
|
|
500
|
+
private readonly sink;
|
|
501
|
+
private _closed;
|
|
502
|
+
isTTY: boolean;
|
|
503
|
+
columns: number | undefined;
|
|
504
|
+
rows: number | undefined;
|
|
505
|
+
constructor(sink: (text: string) => void, opts?: {
|
|
506
|
+
isTTY?: boolean;
|
|
507
|
+
columns?: number;
|
|
508
|
+
rows?: number;
|
|
509
|
+
});
|
|
510
|
+
get closed(): boolean;
|
|
511
|
+
write(data: Uint8Array | string): void;
|
|
512
|
+
end(): void;
|
|
513
|
+
}
|
|
514
|
+
/** Fan-out, for `tee` and for `2>&1`-style duplication. */
|
|
515
|
+
declare class TeeOutput implements OutputStream {
|
|
516
|
+
private readonly targets;
|
|
517
|
+
constructor(targets: OutputStream[]);
|
|
518
|
+
get closed(): boolean;
|
|
519
|
+
isTTY: boolean;
|
|
520
|
+
write(data: Uint8Array | string): void;
|
|
521
|
+
end(): void;
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Writes into a VFS file. Buffers in memory and flushes on every write so a
|
|
525
|
+
* long-running redirect (`cmd > log &`) is observable while it runs.
|
|
526
|
+
*/
|
|
527
|
+
declare class FileOutput implements OutputStream {
|
|
528
|
+
private readonly vfs;
|
|
529
|
+
private readonly path;
|
|
530
|
+
private readonly opts;
|
|
531
|
+
private _closed;
|
|
532
|
+
isTTY: boolean;
|
|
533
|
+
constructor(vfs: Vfs, path: string, opts?: {
|
|
534
|
+
append?: boolean;
|
|
535
|
+
cred?: Cred;
|
|
536
|
+
mode?: number;
|
|
537
|
+
});
|
|
538
|
+
get closed(): boolean;
|
|
539
|
+
write(data: Uint8Array | string): void;
|
|
540
|
+
end(): void;
|
|
541
|
+
}
|
|
542
|
+
/** Reads a VFS file as an input stream, for `cmd < file`. */
|
|
543
|
+
declare class FileInput implements InputStream {
|
|
544
|
+
private pipe;
|
|
545
|
+
isTTY: boolean;
|
|
546
|
+
constructor(vfs: Vfs, path: string, cred?: Cred);
|
|
547
|
+
get available(): number;
|
|
548
|
+
read(size?: number): Promise<Uint8Array | null>;
|
|
549
|
+
readAll(): Promise<Uint8Array>;
|
|
550
|
+
readLine(): Promise<string | null>;
|
|
551
|
+
close(): void;
|
|
552
|
+
}
|
|
553
|
+
/** Convenience factory for a fully buffered stdio triple. */
|
|
554
|
+
declare function captureStdio(stdin?: Uint8Array | string): {
|
|
555
|
+
stdio: Stdio;
|
|
556
|
+
out: BufferSink;
|
|
557
|
+
err: BufferSink;
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* The process table.
|
|
562
|
+
*
|
|
563
|
+
* Processes here are cooperative JavaScript tasks, not OS processes, but they
|
|
564
|
+
* carry the state Linux tooling expects to see: pid/ppid/pgid/sid, a state
|
|
565
|
+
* letter, credentials, a cwd, an environment, and an exit status that encodes
|
|
566
|
+
* the killing signal. `ps`, `kill`, `jobs` and `/proc` all read from here.
|
|
567
|
+
*/
|
|
568
|
+
|
|
569
|
+
type ProcessState = "R" | "S" | "D" | "T" | "Z" | "X";
|
|
570
|
+
type Env = Record<string, string>;
|
|
571
|
+
interface ProcessOptions {
|
|
572
|
+
argv: string[];
|
|
573
|
+
cwd: string;
|
|
574
|
+
env: Env;
|
|
575
|
+
cred: Cred;
|
|
576
|
+
ppid?: number;
|
|
577
|
+
pgid?: number;
|
|
578
|
+
sid?: number;
|
|
579
|
+
stdio?: Partial<Stdio>;
|
|
580
|
+
tty?: string | null;
|
|
581
|
+
/** Marks the process as a shell builtin frame rather than a real command. */
|
|
582
|
+
kind?: ProcessKind;
|
|
583
|
+
}
|
|
584
|
+
type ProcessKind = "init" | "shell" | "builtin" | "command" | "node" | "python" | "script";
|
|
585
|
+
declare class Process {
|
|
586
|
+
readonly pid: number;
|
|
587
|
+
ppid: number;
|
|
588
|
+
pgid: number;
|
|
589
|
+
sid: number;
|
|
590
|
+
argv: string[];
|
|
591
|
+
cwd: string;
|
|
592
|
+
env: Env;
|
|
593
|
+
cred: Cred;
|
|
594
|
+
kind: ProcessKind;
|
|
595
|
+
tty: string | null;
|
|
596
|
+
state: ProcessState;
|
|
597
|
+
exitCode: number | null;
|
|
598
|
+
/** Signal that terminated the process, if any. */
|
|
599
|
+
termSignal: string | null;
|
|
600
|
+
readonly startTime: number;
|
|
601
|
+
cpuMs: number;
|
|
602
|
+
stdin: InputStream;
|
|
603
|
+
stdout: OutputStream;
|
|
604
|
+
stderr: OutputStream;
|
|
605
|
+
readonly children: Set<Process>;
|
|
606
|
+
private readonly aborter;
|
|
607
|
+
private readonly exitWaiters;
|
|
608
|
+
private readonly signalHandlers;
|
|
609
|
+
constructor(opts: ProcessOptions);
|
|
610
|
+
/** Basename of argv[0], the `comm` field in `ps`. */
|
|
611
|
+
get comm(): string;
|
|
612
|
+
get cmdline(): string;
|
|
613
|
+
get running(): boolean;
|
|
614
|
+
get signal(): AbortSignal;
|
|
615
|
+
/** Seconds since the process started, for `ps etime`. */
|
|
616
|
+
get elapsedMs(): number;
|
|
617
|
+
setStdio(stdio: Partial<Stdio>): void;
|
|
618
|
+
onSignal(sig: string, handler: "default" | "ignore" | ((sig: string) => void)): void;
|
|
619
|
+
/**
|
|
620
|
+
* Deliver a signal. Returns true when the process handled or died from it.
|
|
621
|
+
* Catchable signals run the installed handler; uncatchable ones always kill.
|
|
622
|
+
*/
|
|
623
|
+
deliver(sigSpec: string | number): boolean;
|
|
624
|
+
exit(code: number): void;
|
|
625
|
+
/** Remove from the table entirely — the parent has reaped us. */
|
|
626
|
+
reap(): void;
|
|
627
|
+
wait(): Promise<number>;
|
|
628
|
+
}
|
|
629
|
+
interface ProcessFilter {
|
|
630
|
+
pid?: number;
|
|
631
|
+
pgid?: number;
|
|
632
|
+
uid?: number;
|
|
633
|
+
comm?: string;
|
|
634
|
+
includeDead?: boolean;
|
|
635
|
+
}
|
|
636
|
+
declare class ProcessTable {
|
|
637
|
+
private readonly map;
|
|
638
|
+
create(opts: ProcessOptions): Process;
|
|
639
|
+
get(pid: number): Process | undefined;
|
|
640
|
+
has(pid: number): boolean;
|
|
641
|
+
remove(pid: number): void;
|
|
642
|
+
list(filter?: ProcessFilter): Process[];
|
|
643
|
+
get size(): number;
|
|
644
|
+
/** Send a signal to a pid, a process group (negative pid), or everything (-1). */
|
|
645
|
+
signal(target: number, sig: string | number): number;
|
|
646
|
+
/** Drop finished processes so the table does not grow without bound. */
|
|
647
|
+
gc(keepMs?: number): void;
|
|
648
|
+
clear(): void;
|
|
649
|
+
}
|
|
650
|
+
/** Reset the pid counter — used by tests and by `Container.reset()`. */
|
|
651
|
+
declare function resetPidCounter(start?: number): void;
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* The user and group database, backed by real `/etc/passwd`, `/etc/group` and
|
|
655
|
+
* `/etc/shadow` files so `cat /etc/passwd` and `id` agree with each other and
|
|
656
|
+
* `useradd` is just a file edit.
|
|
657
|
+
*/
|
|
658
|
+
|
|
659
|
+
interface PasswdEntry {
|
|
660
|
+
name: string;
|
|
661
|
+
passwd: string;
|
|
662
|
+
uid: number;
|
|
663
|
+
gid: number;
|
|
664
|
+
gecos: string;
|
|
665
|
+
home: string;
|
|
666
|
+
shell: string;
|
|
667
|
+
}
|
|
668
|
+
interface GroupEntry {
|
|
669
|
+
name: string;
|
|
670
|
+
passwd: string;
|
|
671
|
+
gid: number;
|
|
672
|
+
members: string[];
|
|
673
|
+
}
|
|
674
|
+
declare class UserDatabase {
|
|
675
|
+
private readonly vfs;
|
|
676
|
+
constructor(vfs: Vfs);
|
|
677
|
+
private readLines;
|
|
678
|
+
users(): PasswdEntry[];
|
|
679
|
+
groups(): GroupEntry[];
|
|
680
|
+
userByName(name: string): PasswdEntry | undefined;
|
|
681
|
+
userByUid(uid: number): PasswdEntry | undefined;
|
|
682
|
+
groupByName(name: string): GroupEntry | undefined;
|
|
683
|
+
groupByGid(gid: number): GroupEntry | undefined;
|
|
684
|
+
/** Accepts a name or a numeric id, the way `chown` arguments do. */
|
|
685
|
+
resolveUid(spec: string): number | undefined;
|
|
686
|
+
resolveGid(spec: string): number | undefined;
|
|
687
|
+
nameForUid(uid: number): string;
|
|
688
|
+
nameForGid(gid: number): string;
|
|
689
|
+
/** Every group id a user belongs to, primary first. */
|
|
690
|
+
groupsFor(name: string): number[];
|
|
691
|
+
credFor(nameOrUid: string | number, umask?: number): Cred;
|
|
692
|
+
nextFreeUid(min?: number, max?: number): number;
|
|
693
|
+
nextFreeGid(min?: number, max?: number): number;
|
|
694
|
+
addUser(entry: PasswdEntry, opts?: {
|
|
695
|
+
createHome?: boolean;
|
|
696
|
+
password?: string;
|
|
697
|
+
}): void;
|
|
698
|
+
addGroup(entry: GroupEntry): void;
|
|
699
|
+
removeUser(name: string): boolean;
|
|
700
|
+
removeGroup(name: string): boolean;
|
|
701
|
+
/** Add `user` to the supplementary members of `group`. */
|
|
702
|
+
addUserToGroup(user: string, group: string): boolean;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* The contract every in-container program implements.
|
|
707
|
+
*
|
|
708
|
+
* A "binary" is an async function over an `ExecContext`, returning an exit
|
|
709
|
+
* code. The shell, the coreutils and the language runtimes all speak this one
|
|
710
|
+
* interface, which is what lets `node`, `python3` and `grep` sit side by side
|
|
711
|
+
* in `$PATH` and be pipelined together.
|
|
712
|
+
*/
|
|
713
|
+
|
|
714
|
+
interface ExecContext {
|
|
715
|
+
/** argv[0] is the command name as invoked. */
|
|
716
|
+
readonly argv: string[];
|
|
717
|
+
readonly stdin: InputStream;
|
|
718
|
+
readonly stdout: OutputStream;
|
|
719
|
+
readonly stderr: OutputStream;
|
|
720
|
+
readonly env: Env;
|
|
721
|
+
readonly cwd: string;
|
|
722
|
+
readonly vfs: Vfs;
|
|
723
|
+
readonly kernel: Kernel;
|
|
724
|
+
readonly proc: Process;
|
|
725
|
+
readonly cred: Cred;
|
|
726
|
+
readonly signal: AbortSignal;
|
|
727
|
+
/** Command name, for error prefixes. */
|
|
728
|
+
readonly name: string;
|
|
729
|
+
/** argv without argv[0]. */
|
|
730
|
+
readonly args: string[];
|
|
731
|
+
/** Resolve a possibly relative path against the process cwd. */
|
|
732
|
+
path(p: string): string;
|
|
733
|
+
/** Write to stdout verbatim. */
|
|
734
|
+
write(text: string | Uint8Array): void;
|
|
735
|
+
/** Write a line to stdout. */
|
|
736
|
+
line(text?: string): void;
|
|
737
|
+
/** Write `name: message` to stderr, followed by a newline. */
|
|
738
|
+
warn(message: string): void;
|
|
739
|
+
/** Report an error and produce an exit code in one expression. */
|
|
740
|
+
fail(message: string, code?: number): number;
|
|
741
|
+
/** Turn a caught filesystem error into the message coreutils would print. */
|
|
742
|
+
reportError(e: unknown, subject?: string): number;
|
|
743
|
+
/** Change the calling process's directory (used by `cd`, `chroot`). */
|
|
744
|
+
chdir(dir: string): void;
|
|
745
|
+
}
|
|
746
|
+
interface Command {
|
|
747
|
+
readonly name: string;
|
|
748
|
+
/** One-line summary shown by `help` and `whatis`. */
|
|
749
|
+
readonly summary?: string;
|
|
750
|
+
/** Usage string shown on `--help` and on a `UsageError`. */
|
|
751
|
+
readonly usage?: string;
|
|
752
|
+
/** Longer text shown by `man`. */
|
|
753
|
+
readonly manual?: string;
|
|
754
|
+
/** Aliases registered into the same PATH entry, e.g. `egrep` → `grep`. */
|
|
755
|
+
readonly aliases?: string[];
|
|
756
|
+
/** Where the binary claims to live, for `which` and `type`. */
|
|
757
|
+
readonly path?: string;
|
|
758
|
+
run(ctx: ExecContext): Promise<number> | number;
|
|
759
|
+
}
|
|
760
|
+
declare function defineCommand(cmd: Command): Command;
|
|
761
|
+
declare class CommandRegistry {
|
|
762
|
+
private readonly commands;
|
|
763
|
+
register(cmd: Command): void;
|
|
764
|
+
registerAll(cmds: Command[]): void;
|
|
765
|
+
get(name: string): Command | undefined;
|
|
766
|
+
has(name: string): boolean;
|
|
767
|
+
names(): string[];
|
|
768
|
+
all(): Command[];
|
|
769
|
+
/** Default install location, used when populating `/bin` and `/usr/bin`. */
|
|
770
|
+
binPath(name: string): string;
|
|
771
|
+
}
|
|
772
|
+
interface ContextInit {
|
|
773
|
+
argv: string[];
|
|
774
|
+
proc: Process;
|
|
775
|
+
kernel: Kernel;
|
|
776
|
+
stdin?: InputStream;
|
|
777
|
+
stdout?: OutputStream;
|
|
778
|
+
stderr?: OutputStream;
|
|
779
|
+
env?: Env;
|
|
780
|
+
cwd?: string;
|
|
781
|
+
cred?: Cred;
|
|
782
|
+
}
|
|
783
|
+
declare function createContext(init: ContextInit): ExecContext;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* The container's network stack.
|
|
787
|
+
*
|
|
788
|
+
* There is no real socket layer: HTTP servers started inside the container are
|
|
789
|
+
* registered with the RuntimePod's request proxy, and this module is the routing and
|
|
790
|
+
* name-resolution layer on top — interfaces for `ip`/`ifconfig`, a hosts file
|
|
791
|
+
* resolver, a listening-port table for `ss`/`netstat`, and an outbound policy
|
|
792
|
+
* that decides whether `curl https://example.com` is allowed to touch the real
|
|
793
|
+
* network.
|
|
794
|
+
*/
|
|
795
|
+
|
|
796
|
+
interface NetInterface {
|
|
797
|
+
name: string;
|
|
798
|
+
mac: string;
|
|
799
|
+
ipv4: string;
|
|
800
|
+
netmask: string;
|
|
801
|
+
broadcast?: string;
|
|
802
|
+
ipv6?: string;
|
|
803
|
+
mtu: number;
|
|
804
|
+
up: boolean;
|
|
805
|
+
loopback: boolean;
|
|
806
|
+
rxBytes: number;
|
|
807
|
+
txBytes: number;
|
|
808
|
+
rxPackets: number;
|
|
809
|
+
txPackets: number;
|
|
810
|
+
}
|
|
811
|
+
interface ListeningPort {
|
|
812
|
+
port: number;
|
|
813
|
+
proto: "tcp" | "udp";
|
|
814
|
+
address: string;
|
|
815
|
+
pid: number;
|
|
816
|
+
program: string;
|
|
817
|
+
since: number;
|
|
818
|
+
}
|
|
819
|
+
interface NetworkOptions {
|
|
820
|
+
/**
|
|
821
|
+
* Allow outbound requests to the real internet. When false (the default),
|
|
822
|
+
* `curl`/`wget`/`fetch` only reach servers running inside the container.
|
|
823
|
+
*/
|
|
824
|
+
allowOutbound?: boolean;
|
|
825
|
+
/** Host allowlist applied when `allowOutbound` is on. `null` means any host. */
|
|
826
|
+
allowedHosts?: string[] | null;
|
|
827
|
+
/** Address handed to eth0. */
|
|
828
|
+
ipv4?: string;
|
|
829
|
+
gateway?: string;
|
|
830
|
+
}
|
|
831
|
+
declare class NetworkStack {
|
|
832
|
+
private readonly pod;
|
|
833
|
+
private readonly vfs;
|
|
834
|
+
private readonly ifaces;
|
|
835
|
+
private readonly listeners;
|
|
836
|
+
readonly options: Required<Pick<NetworkOptions, "allowOutbound">> & NetworkOptions;
|
|
837
|
+
constructor(pod: RuntimePod, vfs: Vfs, options?: NetworkOptions);
|
|
838
|
+
interfaces(): NetInterface[];
|
|
839
|
+
interface(name: string): NetInterface | undefined;
|
|
840
|
+
setInterfaceUp(name: string, up: boolean): boolean;
|
|
841
|
+
get gateway(): string;
|
|
842
|
+
/** Resolve through `/etc/hosts`; returns null when the name is not local. */
|
|
843
|
+
resolve(host: string): string | null;
|
|
844
|
+
isLocal(host: string): boolean;
|
|
845
|
+
registerListener(port: number, info: Omit<ListeningPort, "port" | "since">): void;
|
|
846
|
+
unregisterListener(port: number): void;
|
|
847
|
+
listening(): ListeningPort[];
|
|
848
|
+
/** Ports the pod's proxy has registered for this instance. */
|
|
849
|
+
private knownPodPorts;
|
|
850
|
+
/** True when something inside the container answers on `port`. */
|
|
851
|
+
isPortOpen(port: number, timeoutMs?: number): Promise<boolean>;
|
|
852
|
+
/** Wait for an in-container server to start answering on `port`. */
|
|
853
|
+
waitForPort(port: number, opts?: {
|
|
854
|
+
timeoutMs?: number;
|
|
855
|
+
intervalMs?: number;
|
|
856
|
+
}): Promise<boolean>;
|
|
857
|
+
outboundAllowed(url: string): boolean;
|
|
858
|
+
procNetDev(): string;
|
|
859
|
+
procNetRoute(): string;
|
|
860
|
+
countTx(bytes: number, iface?: string): void;
|
|
861
|
+
countRx(bytes: number, iface?: string): void;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* The kernel: the object that owns the filesystem, the process table, the user
|
|
866
|
+
* database and the executable namespace, and knows how to turn an `argv` into
|
|
867
|
+
* a running process.
|
|
868
|
+
*
|
|
869
|
+
* Executables in `$PATH` are real files. Built-in programs are installed as
|
|
870
|
+
* tiny stub files whose shebang points at the in-kernel implementation, so
|
|
871
|
+
* `ls -l /usr/bin/grep`, `which grep` and `file /usr/bin/grep` all behave, and
|
|
872
|
+
* a user-written script in `/usr/local/bin` is dispatched by exactly the same
|
|
873
|
+
* lookup path.
|
|
874
|
+
*/
|
|
875
|
+
|
|
876
|
+
interface KernelOptions {
|
|
877
|
+
pod: RuntimePod;
|
|
878
|
+
hostname?: string;
|
|
879
|
+
/** Login user for interactive sessions. Defaults to `root`. */
|
|
880
|
+
user?: string;
|
|
881
|
+
env?: Env;
|
|
882
|
+
/** Total "RAM" reported by `free`, `/proc/meminfo` and `top`. */
|
|
883
|
+
memoryBytes?: number;
|
|
884
|
+
/** Simulated CPU count for `nproc` and `/proc/cpuinfo`. */
|
|
885
|
+
cpus?: number;
|
|
886
|
+
now?: () => number;
|
|
887
|
+
}
|
|
888
|
+
interface RunOptions {
|
|
889
|
+
cwd?: string;
|
|
890
|
+
env?: Env;
|
|
891
|
+
cred?: Cred;
|
|
892
|
+
stdin?: InputStream | string | Uint8Array;
|
|
893
|
+
stdout?: OutputStream;
|
|
894
|
+
stderr?: OutputStream;
|
|
895
|
+
ppid?: number;
|
|
896
|
+
pgid?: number;
|
|
897
|
+
kind?: ProcessKind;
|
|
898
|
+
tty?: string | null;
|
|
899
|
+
/** Wall-clock budget; the process is sent SIGKILL when it expires. */
|
|
900
|
+
timeoutMs?: number;
|
|
901
|
+
}
|
|
902
|
+
interface RunResult {
|
|
903
|
+
exitCode: number;
|
|
904
|
+
stdout: string;
|
|
905
|
+
stderr: string;
|
|
906
|
+
pid: number;
|
|
907
|
+
signal: string | null;
|
|
908
|
+
timedOut: boolean;
|
|
909
|
+
}
|
|
910
|
+
type ExecutableKind = "builtin" | "script" | "unknown";
|
|
911
|
+
interface ResolvedExecutable {
|
|
912
|
+
kind: ExecutableKind;
|
|
913
|
+
/** Absolute path of the file that was found. */
|
|
914
|
+
path: string;
|
|
915
|
+
/** Present when `kind === "builtin"`. */
|
|
916
|
+
command?: Command;
|
|
917
|
+
/** Interpreter argv from a `#!` line, when `kind === "script"`. */
|
|
918
|
+
interpreter?: string[];
|
|
919
|
+
}
|
|
920
|
+
interface MountEntry {
|
|
921
|
+
device: string;
|
|
922
|
+
mountpoint: string;
|
|
923
|
+
fstype: string;
|
|
924
|
+
options: string;
|
|
925
|
+
totalBytes: number;
|
|
926
|
+
}
|
|
927
|
+
declare class Kernel {
|
|
928
|
+
readonly vfs: Vfs;
|
|
929
|
+
readonly procs: ProcessTable;
|
|
930
|
+
readonly commands: CommandRegistry;
|
|
931
|
+
readonly users: UserDatabase;
|
|
932
|
+
readonly pod: RuntimePod;
|
|
933
|
+
readonly bootTime: number;
|
|
934
|
+
readonly memoryBytes: number;
|
|
935
|
+
readonly cpus: number;
|
|
936
|
+
readonly now: () => number;
|
|
937
|
+
/** Populated by the network module once it is attached. */
|
|
938
|
+
net: NetworkStack;
|
|
939
|
+
/** init — pid 1, the ancestor of everything. */
|
|
940
|
+
readonly init: Process;
|
|
941
|
+
private readonly builtinByPath;
|
|
942
|
+
private readonly mounts;
|
|
943
|
+
private disposed;
|
|
944
|
+
private _current;
|
|
945
|
+
/**
|
|
946
|
+
* The process whose builtin is currently on the stack. `/proc/self` resolves
|
|
947
|
+
* through this. It is set around each dispatch, so a command that reads
|
|
948
|
+
* `/proc/self/...` synchronously always sees itself.
|
|
949
|
+
*/
|
|
950
|
+
get currentProcess(): Process | null;
|
|
951
|
+
constructor(opts: KernelOptions);
|
|
952
|
+
get hostname(): string;
|
|
953
|
+
set hostname(value: string);
|
|
954
|
+
get uptimeMs(): number;
|
|
955
|
+
addMount(entry: MountEntry): void;
|
|
956
|
+
removeMount(mountpoint: string): boolean;
|
|
957
|
+
mountTable(): MountEntry[];
|
|
958
|
+
/**
|
|
959
|
+
* Install a command as a real file in `$PATH`. The file holds a shebang that
|
|
960
|
+
* points at the in-kernel dispatcher, which is what `resolve` looks for.
|
|
961
|
+
*/
|
|
962
|
+
installCommand(cmd: Command, dir?: string): void;
|
|
963
|
+
installCommands(cmds: Command[], dir?: string): void;
|
|
964
|
+
/** Directories from `$PATH`, with a sane fallback. */
|
|
965
|
+
pathDirs(env: Env): string[];
|
|
966
|
+
/**
|
|
967
|
+
* Find `name` the way `execvp` does. Returns null when nothing matches.
|
|
968
|
+
*/
|
|
969
|
+
resolveExecutable(name: string, cwd: string, env: Env, cred?: Cred): ResolvedExecutable | null;
|
|
970
|
+
/** `which`-style lookup that only reports the path. */
|
|
971
|
+
which(name: string, cwd: string, env: Env, cred?: Cred): string | null;
|
|
972
|
+
/** Every executable name reachable through `$PATH`, for tab completion. */
|
|
973
|
+
executableNames(env: Env, cred?: Cred): string[];
|
|
974
|
+
private toInputStream;
|
|
975
|
+
/**
|
|
976
|
+
* Create a process for `argv` and start it. Returns the process immediately;
|
|
977
|
+
* `proc.wait()` resolves with the exit code.
|
|
978
|
+
*/
|
|
979
|
+
spawn(argv: string[], opts?: RunOptions): Process;
|
|
980
|
+
/** Run to completion and collect stdout/stderr. */
|
|
981
|
+
run(argv: string[], opts?: RunOptions): Promise<RunResult>;
|
|
982
|
+
/**
|
|
983
|
+
* Resolve `proc.argv` and execute it in-process. Interpreted scripts are
|
|
984
|
+
* dispatched by re-entering with the interpreter's argv, up to a small depth
|
|
985
|
+
* so a self-referential shebang cannot loop forever.
|
|
986
|
+
*/
|
|
987
|
+
private dispatch;
|
|
988
|
+
/** Convenience for internal callers that just want the text output. */
|
|
989
|
+
capture(argv: string[], opts?: RunOptions): Promise<string>;
|
|
990
|
+
dispose(): void;
|
|
991
|
+
get isDisposed(): boolean;
|
|
992
|
+
assertActive(): void;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/** Shell syntax tree. Words stay raw; `expand.ts` interprets them. */
|
|
996
|
+
type RedirectOp = ">" | ">>" | "<" | "<>" | ">|" | ">&" | "<&" | "&>" | "&>>" | "<<" | "<<<";
|
|
997
|
+
interface Redirect {
|
|
998
|
+
op: RedirectOp;
|
|
999
|
+
/** Source fd, e.g. `2` in `2>file`. Defaults per operator. */
|
|
1000
|
+
fd?: number;
|
|
1001
|
+
/** Target word (a filename, an fd number, or here-doc/string content). */
|
|
1002
|
+
target: string;
|
|
1003
|
+
/** Populated for `<<`. */
|
|
1004
|
+
heredoc?: {
|
|
1005
|
+
body: string;
|
|
1006
|
+
expand: boolean;
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
interface Assignment {
|
|
1010
|
+
name: string;
|
|
1011
|
+
/** Raw value word; undefined for `name=` with nothing after it. */
|
|
1012
|
+
value: string;
|
|
1013
|
+
/** `name+=value`. */
|
|
1014
|
+
append: boolean;
|
|
1015
|
+
/** `name=(a b c)` array literal. */
|
|
1016
|
+
arrayWords?: string[];
|
|
1017
|
+
}
|
|
1018
|
+
type Node = ListNode | PipelineNode | SimpleCommandNode | SubshellNode | GroupNode | IfNode | ForNode | ForArithNode | WhileNode | CaseNode | FunctionNode | ArithCommandNode | CondNode;
|
|
1019
|
+
type ListOperator = ";" | "&" | "&&" | "||";
|
|
1020
|
+
interface ListItem {
|
|
1021
|
+
node: Node;
|
|
1022
|
+
/** Operator that *follows* this item. */
|
|
1023
|
+
op: ListOperator;
|
|
1024
|
+
}
|
|
1025
|
+
interface ListNode {
|
|
1026
|
+
type: "list";
|
|
1027
|
+
items: ListItem[];
|
|
1028
|
+
}
|
|
1029
|
+
interface PipelineNode {
|
|
1030
|
+
type: "pipeline";
|
|
1031
|
+
commands: Node[];
|
|
1032
|
+
/** `! cmd` inverts the exit status. */
|
|
1033
|
+
negated: boolean;
|
|
1034
|
+
/** `cmd |& next` pipes stderr too. */
|
|
1035
|
+
stderrToo: boolean[];
|
|
1036
|
+
/** `time cmd` */
|
|
1037
|
+
timed?: boolean;
|
|
1038
|
+
}
|
|
1039
|
+
interface SimpleCommandNode {
|
|
1040
|
+
type: "command";
|
|
1041
|
+
assignments: Assignment[];
|
|
1042
|
+
words: string[];
|
|
1043
|
+
redirects: Redirect[];
|
|
1044
|
+
}
|
|
1045
|
+
interface SubshellNode {
|
|
1046
|
+
type: "subshell";
|
|
1047
|
+
body: Node;
|
|
1048
|
+
redirects: Redirect[];
|
|
1049
|
+
}
|
|
1050
|
+
interface GroupNode {
|
|
1051
|
+
type: "group";
|
|
1052
|
+
body: Node;
|
|
1053
|
+
redirects: Redirect[];
|
|
1054
|
+
}
|
|
1055
|
+
interface IfClause {
|
|
1056
|
+
condition: Node;
|
|
1057
|
+
body: Node;
|
|
1058
|
+
}
|
|
1059
|
+
interface IfNode {
|
|
1060
|
+
type: "if";
|
|
1061
|
+
clauses: IfClause[];
|
|
1062
|
+
elseBody?: Node;
|
|
1063
|
+
redirects: Redirect[];
|
|
1064
|
+
}
|
|
1065
|
+
interface ForNode {
|
|
1066
|
+
type: "for";
|
|
1067
|
+
name: string;
|
|
1068
|
+
/** Absent means `for x; do` which iterates `"$@"`. */
|
|
1069
|
+
words?: string[];
|
|
1070
|
+
body: Node;
|
|
1071
|
+
redirects: Redirect[];
|
|
1072
|
+
/** `select` shares the same shape. */
|
|
1073
|
+
select?: boolean;
|
|
1074
|
+
}
|
|
1075
|
+
interface ForArithNode {
|
|
1076
|
+
type: "for-arith";
|
|
1077
|
+
init: string;
|
|
1078
|
+
condition: string;
|
|
1079
|
+
step: string;
|
|
1080
|
+
body: Node;
|
|
1081
|
+
redirects: Redirect[];
|
|
1082
|
+
}
|
|
1083
|
+
interface WhileNode {
|
|
1084
|
+
type: "while";
|
|
1085
|
+
condition: Node;
|
|
1086
|
+
body: Node;
|
|
1087
|
+
until: boolean;
|
|
1088
|
+
redirects: Redirect[];
|
|
1089
|
+
}
|
|
1090
|
+
interface CaseItem {
|
|
1091
|
+
patterns: string[];
|
|
1092
|
+
body: Node | null;
|
|
1093
|
+
/** `;;` stops, `;&` falls through, `;;&` retests. */
|
|
1094
|
+
terminator: ";;" | ";&" | ";;&";
|
|
1095
|
+
}
|
|
1096
|
+
interface CaseNode {
|
|
1097
|
+
type: "case";
|
|
1098
|
+
word: string;
|
|
1099
|
+
items: CaseItem[];
|
|
1100
|
+
redirects: Redirect[];
|
|
1101
|
+
}
|
|
1102
|
+
interface FunctionNode {
|
|
1103
|
+
type: "function";
|
|
1104
|
+
name: string;
|
|
1105
|
+
body: Node;
|
|
1106
|
+
redirects: Redirect[];
|
|
1107
|
+
}
|
|
1108
|
+
interface ArithCommandNode {
|
|
1109
|
+
type: "arith";
|
|
1110
|
+
expression: string;
|
|
1111
|
+
redirects: Redirect[];
|
|
1112
|
+
}
|
|
1113
|
+
/** `[[ ... ]]` — parsed as a small expression tree of its own. */
|
|
1114
|
+
type CondExpr = {
|
|
1115
|
+
type: "unary";
|
|
1116
|
+
op: string;
|
|
1117
|
+
operand: string;
|
|
1118
|
+
} | {
|
|
1119
|
+
type: "binary";
|
|
1120
|
+
op: string;
|
|
1121
|
+
left: string;
|
|
1122
|
+
right: string;
|
|
1123
|
+
} | {
|
|
1124
|
+
type: "not";
|
|
1125
|
+
operand: CondExpr;
|
|
1126
|
+
} | {
|
|
1127
|
+
type: "and";
|
|
1128
|
+
left: CondExpr;
|
|
1129
|
+
right: CondExpr;
|
|
1130
|
+
} | {
|
|
1131
|
+
type: "or";
|
|
1132
|
+
left: CondExpr;
|
|
1133
|
+
right: CondExpr;
|
|
1134
|
+
} | {
|
|
1135
|
+
type: "word";
|
|
1136
|
+
value: string;
|
|
1137
|
+
};
|
|
1138
|
+
interface CondNode {
|
|
1139
|
+
type: "cond";
|
|
1140
|
+
expression: CondExpr;
|
|
1141
|
+
redirects: Redirect[];
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
/**
|
|
1145
|
+
* Shell variable table: scalars, indexed arrays, export/readonly attributes,
|
|
1146
|
+
* and the function-local scoping that `local` introduces.
|
|
1147
|
+
*/
|
|
1148
|
+
interface VarAttributes {
|
|
1149
|
+
exported?: boolean;
|
|
1150
|
+
readonly?: boolean;
|
|
1151
|
+
integer?: boolean;
|
|
1152
|
+
/** `declare -l` / `-u` case folding. */
|
|
1153
|
+
lower?: boolean;
|
|
1154
|
+
upper?: boolean;
|
|
1155
|
+
}
|
|
1156
|
+
interface VarEntry extends VarAttributes {
|
|
1157
|
+
value: string;
|
|
1158
|
+
array?: string[];
|
|
1159
|
+
assoc?: Map<string, string>;
|
|
1160
|
+
}
|
|
1161
|
+
declare class Variables {
|
|
1162
|
+
/** Innermost scope last; index 0 is the global scope. */
|
|
1163
|
+
private readonly scopes;
|
|
1164
|
+
constructor(initial?: Record<string, string>);
|
|
1165
|
+
pushScope(): void;
|
|
1166
|
+
popScope(): void;
|
|
1167
|
+
get depth(): number;
|
|
1168
|
+
private find;
|
|
1169
|
+
has(name: string): boolean;
|
|
1170
|
+
get(name: string): string | undefined;
|
|
1171
|
+
entry(name: string): VarEntry | undefined;
|
|
1172
|
+
getArray(name: string): string[] | undefined;
|
|
1173
|
+
isArray(name: string): boolean;
|
|
1174
|
+
set(name: string, value: string, attrs?: VarAttributes): void;
|
|
1175
|
+
/** Declare in the innermost scope, shadowing outer definitions (`local`). */
|
|
1176
|
+
setLocal(name: string, value: string, attrs?: VarAttributes): void;
|
|
1177
|
+
setArray(name: string, values: string[], attrs?: VarAttributes): void;
|
|
1178
|
+
setIndex(name: string, index: number, value: string): void;
|
|
1179
|
+
append(name: string, value: string): void;
|
|
1180
|
+
unset(name: string): boolean;
|
|
1181
|
+
export(name: string, exported?: boolean): void;
|
|
1182
|
+
markReadonly(name: string): void;
|
|
1183
|
+
names(): string[];
|
|
1184
|
+
/** The environment handed to a child process. */
|
|
1185
|
+
environment(): Record<string, string>;
|
|
1186
|
+
/** Every variable with its attributes, for `declare -p` / `set`. */
|
|
1187
|
+
all(): Array<{
|
|
1188
|
+
name: string;
|
|
1189
|
+
entry: VarEntry;
|
|
1190
|
+
}>;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
/**
|
|
1194
|
+
* Word expansion, in the order POSIX specifies:
|
|
1195
|
+
*
|
|
1196
|
+
* brace → tilde → parameter/command/arithmetic → field splitting →
|
|
1197
|
+
* pathname → quote removal
|
|
1198
|
+
*
|
|
1199
|
+
* The tricky part is that only text produced by *unquoted* expansions may be
|
|
1200
|
+
* field-split, and only unquoted text may be globbed. Each fragment therefore
|
|
1201
|
+
* carries `split` and `glob` flags through the pipeline, and quote removal is
|
|
1202
|
+
* the last thing that happens.
|
|
1203
|
+
*/
|
|
1204
|
+
|
|
1205
|
+
interface ExpandContext {
|
|
1206
|
+
vars: Variables;
|
|
1207
|
+
/** `$1`, `$2`, … */
|
|
1208
|
+
positional: string[];
|
|
1209
|
+
/** `$0` */
|
|
1210
|
+
scriptName: string;
|
|
1211
|
+
/** `$?` */
|
|
1212
|
+
lastStatus: number;
|
|
1213
|
+
/** `$$` */
|
|
1214
|
+
shellPid: number;
|
|
1215
|
+
/** `$!` */
|
|
1216
|
+
lastBackgroundPid: number;
|
|
1217
|
+
/** `$-` */
|
|
1218
|
+
optionFlags: string;
|
|
1219
|
+
cwd: string;
|
|
1220
|
+
vfs: Vfs;
|
|
1221
|
+
cred: Cred;
|
|
1222
|
+
/** Home directory lookup for `~user`. */
|
|
1223
|
+
homeFor(user: string): string | undefined;
|
|
1224
|
+
/** Runs a command substitution and returns its stdout. */
|
|
1225
|
+
runSubstitution(command: string): Promise<string>;
|
|
1226
|
+
/** Materialises `<(cmd)` / `>(cmd)` as a path. Optional. */
|
|
1227
|
+
processSubstitution?(command: string, direction: "in" | "out"): Promise<string>;
|
|
1228
|
+
/** `set -u` */
|
|
1229
|
+
nounset?: boolean;
|
|
1230
|
+
/** `set -f` */
|
|
1231
|
+
noglob?: boolean;
|
|
1232
|
+
/** Extended globbing (`shopt -s extglob`). */
|
|
1233
|
+
extglob?: boolean;
|
|
1234
|
+
/** Include dotfiles in globs (`shopt -s dotglob`). */
|
|
1235
|
+
dotglob?: boolean;
|
|
1236
|
+
/** Leave an unmatched glob as-is (bash default) or drop it (`nullglob`). */
|
|
1237
|
+
nullglob?: boolean;
|
|
1238
|
+
/** Error out on an unmatched glob (`failglob`). */
|
|
1239
|
+
failglob?: boolean;
|
|
1240
|
+
}
|
|
1241
|
+
/** Full expansion of a list of words, as used for command arguments. */
|
|
1242
|
+
declare function expandWords(words: string[], ctx: ExpandContext): Promise<string[]>;
|
|
1243
|
+
/** Expand one word into zero or more fields. */
|
|
1244
|
+
declare function expandWord(word: string, ctx: ExpandContext): Promise<string[]>;
|
|
1245
|
+
/** `a{b,c}d` → `abd acd`; `{1..5}` and `{a..e}` sequences too. */
|
|
1246
|
+
declare function braceExpand(word: string): string[];
|
|
1247
|
+
declare function shellQuote(value: string): string;
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* The shell interpreter.
|
|
1251
|
+
*
|
|
1252
|
+
* Walks the syntax tree, applies redirections, wires pipelines together, and
|
|
1253
|
+
* dispatches each simple command to a function, a builtin, or the kernel's
|
|
1254
|
+
* executable lookup — in that order, which is the order bash uses.
|
|
1255
|
+
*/
|
|
1256
|
+
|
|
1257
|
+
interface ShellIO {
|
|
1258
|
+
stdin: InputStream;
|
|
1259
|
+
stdout: OutputStream;
|
|
1260
|
+
stderr: OutputStream;
|
|
1261
|
+
}
|
|
1262
|
+
interface ShellOptions {
|
|
1263
|
+
/** `set -e` */
|
|
1264
|
+
errexit: boolean;
|
|
1265
|
+
/** `set -u` */
|
|
1266
|
+
nounset: boolean;
|
|
1267
|
+
/** `set -x` */
|
|
1268
|
+
xtrace: boolean;
|
|
1269
|
+
/** `set -v` */
|
|
1270
|
+
verbose: boolean;
|
|
1271
|
+
/** `set -f` */
|
|
1272
|
+
noglob: boolean;
|
|
1273
|
+
/** `set -o pipefail` */
|
|
1274
|
+
pipefail: boolean;
|
|
1275
|
+
/** `set -n` */
|
|
1276
|
+
noexec: boolean;
|
|
1277
|
+
/** `set -m` */
|
|
1278
|
+
monitor: boolean;
|
|
1279
|
+
/** `set -C` */
|
|
1280
|
+
noclobber: boolean;
|
|
1281
|
+
/** `set -a` */
|
|
1282
|
+
allexport: boolean;
|
|
1283
|
+
/** Interactive shells print prompts and keep history. */
|
|
1284
|
+
interactive: boolean;
|
|
1285
|
+
/** Login shells source `/etc/profile`. */
|
|
1286
|
+
login: boolean;
|
|
1287
|
+
}
|
|
1288
|
+
interface Job {
|
|
1289
|
+
id: number;
|
|
1290
|
+
pgid: number;
|
|
1291
|
+
command: string;
|
|
1292
|
+
state: "running" | "done" | "stopped";
|
|
1293
|
+
exitCode: number | null;
|
|
1294
|
+
promise: Promise<number>;
|
|
1295
|
+
pids: number[];
|
|
1296
|
+
}
|
|
1297
|
+
declare class ShellExit {
|
|
1298
|
+
readonly code: number;
|
|
1299
|
+
constructor(code: number);
|
|
1300
|
+
}
|
|
1301
|
+
interface ShellInit {
|
|
1302
|
+
kernel: Kernel;
|
|
1303
|
+
proc: Process;
|
|
1304
|
+
cwd?: string;
|
|
1305
|
+
env?: Record<string, string>;
|
|
1306
|
+
cred?: Cred;
|
|
1307
|
+
options?: Partial<ShellOptions>;
|
|
1308
|
+
positional?: string[];
|
|
1309
|
+
scriptName?: string;
|
|
1310
|
+
}
|
|
1311
|
+
declare class Shell {
|
|
1312
|
+
readonly kernel: Kernel;
|
|
1313
|
+
readonly proc: Process;
|
|
1314
|
+
readonly vars: Variables;
|
|
1315
|
+
readonly functions: Map<string, Node>;
|
|
1316
|
+
readonly aliases: Map<string, string>;
|
|
1317
|
+
readonly traps: Map<string, string>;
|
|
1318
|
+
readonly dirStack: string[];
|
|
1319
|
+
readonly jobs: Job[];
|
|
1320
|
+
readonly history: string[];
|
|
1321
|
+
options: ShellOptions;
|
|
1322
|
+
shopts: Set<string>;
|
|
1323
|
+
positional: string[];
|
|
1324
|
+
scriptName: string;
|
|
1325
|
+
lastStatus: number;
|
|
1326
|
+
lastBackgroundPid: number;
|
|
1327
|
+
cwd: string;
|
|
1328
|
+
cred: Cred;
|
|
1329
|
+
/** Pipeline exit statuses, exposed as `PIPESTATUS`. */
|
|
1330
|
+
pipeStatus: number[];
|
|
1331
|
+
private nextJobId;
|
|
1332
|
+
private functionDepth;
|
|
1333
|
+
private tempFileCounter;
|
|
1334
|
+
private exiting;
|
|
1335
|
+
constructor(init: ShellInit);
|
|
1336
|
+
/** Parse and run a script fragment. Returns the last exit status. */
|
|
1337
|
+
execute(source: string, io: ShellIO): Promise<number>;
|
|
1338
|
+
/** Signal the current terminal pipeline without terminating the interactive shell. */
|
|
1339
|
+
interruptForeground(signal: string, stdin: InputStream): void;
|
|
1340
|
+
get isExiting(): boolean;
|
|
1341
|
+
/** True when `source` is not yet a complete command (for REPL continuation). */
|
|
1342
|
+
static isIncomplete(source: string): boolean;
|
|
1343
|
+
expandContext(): ExpandContext;
|
|
1344
|
+
optionFlagString(): string;
|
|
1345
|
+
/** Run `command` in a subshell and return its stdout. */
|
|
1346
|
+
captureSubshell(command: string): Promise<string>;
|
|
1347
|
+
/** `<(cmd)` — run the command now and hand back a path holding its output. */
|
|
1348
|
+
private makeProcessSubstitution;
|
|
1349
|
+
/** A copy that shares nothing mutable with this shell. */
|
|
1350
|
+
fork(): Shell;
|
|
1351
|
+
private currentIO;
|
|
1352
|
+
run(node: Node, io: ShellIO): Promise<number>;
|
|
1353
|
+
private runList;
|
|
1354
|
+
/** Skip past short-circuited members of an `&&`/`||` chain. */
|
|
1355
|
+
private skipChain;
|
|
1356
|
+
private runPipeline;
|
|
1357
|
+
/**
|
|
1358
|
+
* Every stage of a pipeline runs in its own subshell in bash. Sharing the
|
|
1359
|
+
* variable table would let `echo x | read v` leak `v` into the parent.
|
|
1360
|
+
*/
|
|
1361
|
+
private forkForPipeline;
|
|
1362
|
+
private reportTime;
|
|
1363
|
+
private startBackgroundJob;
|
|
1364
|
+
private runSubshell;
|
|
1365
|
+
private runGroup;
|
|
1366
|
+
private runIf;
|
|
1367
|
+
/** Conditions are exempt from `set -e`. */
|
|
1368
|
+
private runCondition;
|
|
1369
|
+
private runFor;
|
|
1370
|
+
private runSelect;
|
|
1371
|
+
private runForArith;
|
|
1372
|
+
private runWhile;
|
|
1373
|
+
private runCase;
|
|
1374
|
+
private runFunctionDefinition;
|
|
1375
|
+
private runArithCommand;
|
|
1376
|
+
private runCond;
|
|
1377
|
+
private evalCond;
|
|
1378
|
+
private runSimpleCommand;
|
|
1379
|
+
private reportCommandError;
|
|
1380
|
+
/** One level of alias substitution on the command word. */
|
|
1381
|
+
private substituteAlias;
|
|
1382
|
+
private invoke;
|
|
1383
|
+
private callFunction;
|
|
1384
|
+
private runExternal;
|
|
1385
|
+
/** `VAR=x cmd` — set for the duration, then restore. */
|
|
1386
|
+
private applyTemporaryAssignments;
|
|
1387
|
+
applyAssignment(assignment: SimpleCommandNode["assignments"][number], exported: boolean): Promise<void>;
|
|
1388
|
+
applyRedirects(redirects: Redirect[], io: ShellIO): Promise<{
|
|
1389
|
+
io: ShellIO;
|
|
1390
|
+
cleanup: () => void;
|
|
1391
|
+
}>;
|
|
1392
|
+
private openInput;
|
|
1393
|
+
private openOutput;
|
|
1394
|
+
private expandHeredoc;
|
|
1395
|
+
setTrap(signal: string, action: string): void;
|
|
1396
|
+
runTrap(signal: string, io: ShellIO): Promise<void>;
|
|
1397
|
+
changeDirectory(target: string): void;
|
|
1398
|
+
throwBreak(levels: number): never;
|
|
1399
|
+
throwContinue(levels: number): never;
|
|
1400
|
+
throwReturn(code: number): never;
|
|
1401
|
+
throwExit(code: number): never;
|
|
1402
|
+
addJob(job: Job): void;
|
|
1403
|
+
reapJobs(): Job[];
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
/**
|
|
1407
|
+
* The CPython runtime: Pyodide, wired to the container the same way
|
|
1408
|
+
* the other runtimes are — filesystem, argv, environment and standard streams.
|
|
1409
|
+
*
|
|
1410
|
+
* This is real CPython, so `sqlite3`, `dataclasses`, `decimal`, `typing` and
|
|
1411
|
+
* the rest of the standard library are present, and `micropip` can install
|
|
1412
|
+
* pure-Python wheels.
|
|
1413
|
+
*
|
|
1414
|
+
* CPython keeps its own library inside the Emscripten filesystem, so:
|
|
1415
|
+
*
|
|
1416
|
+
* - the container is mounted subtree by subtree rather than over `/`, so
|
|
1417
|
+
* Pyodide's standard-library files survive;
|
|
1418
|
+
* - an interpreter costs roughly a second and a half to start, so one is kept
|
|
1419
|
+
* per container and each program is run in a fresh namespace instead.
|
|
1420
|
+
*/
|
|
1421
|
+
|
|
1422
|
+
interface CPythonOptions {
|
|
1423
|
+
/**
|
|
1424
|
+
* Where Pyodide's own assets live.
|
|
1425
|
+
*
|
|
1426
|
+
* Node resolves them from the installed package, so this is only needed in a
|
|
1427
|
+
* browser, where they are served or taken from a CDN.
|
|
1428
|
+
*/
|
|
1429
|
+
indexURL?: string;
|
|
1430
|
+
/**
|
|
1431
|
+
* URL of the Pyodide ES module, for hosts that cannot resolve the package by
|
|
1432
|
+
* name — a browser without a bundler, typically.
|
|
1433
|
+
*/
|
|
1434
|
+
moduleURL?: string;
|
|
1435
|
+
}
|
|
1436
|
+
declare function configureCPython(options?: CPythonOptions): void;
|
|
1437
|
+
/** True when this host can start CPython at all. */
|
|
1438
|
+
declare function isCPythonAvailable(): Promise<boolean>;
|
|
1439
|
+
|
|
1440
|
+
/** The existing Python CLI surface, backed exclusively by CPython/Pyodide. */
|
|
1441
|
+
|
|
1442
|
+
declare const PYTHON_VERSION = "3.13";
|
|
1443
|
+
interface PythonOptions {
|
|
1444
|
+
/** Where Pyodide's assets live; only a browser normally needs this. */
|
|
1445
|
+
indexURL?: string;
|
|
1446
|
+
/** URL of pyodide.mjs, for browsers without package resolution. */
|
|
1447
|
+
pyodideURL?: string;
|
|
1448
|
+
}
|
|
1449
|
+
declare function configurePython(options?: PythonOptions): void;
|
|
1450
|
+
declare const isPythonAvailable: typeof isCPythonAvailable;
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* A promise-based filesystem façade for host code, shaped like `fs/promises`
|
|
1454
|
+
* so it reads naturally from the outside.
|
|
1455
|
+
*/
|
|
1456
|
+
|
|
1457
|
+
/** Data accepted by the host-facing filesystem API.
|
|
1458
|
+
*
|
|
1459
|
+
* `Blob` includes browser `File` objects, which is what an `<input
|
|
1460
|
+
* type="file">` returns. The bytes are copied into the container; no host
|
|
1461
|
+
* filesystem path is ever resolved by the sandbox.
|
|
1462
|
+
*/
|
|
1463
|
+
type FileData = string | Uint8Array | ArrayBuffer | ArrayBufferView | Blob;
|
|
1464
|
+
declare class ContainerFs {
|
|
1465
|
+
private readonly kernel;
|
|
1466
|
+
constructor(kernel: Kernel);
|
|
1467
|
+
private get vfs();
|
|
1468
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
1469
|
+
readFile(path: string, encoding: "utf8" | "utf-8"): Promise<string>;
|
|
1470
|
+
writeFile(path: string, data: FileData, opts?: {
|
|
1471
|
+
mode?: number;
|
|
1472
|
+
}): Promise<void>;
|
|
1473
|
+
appendFile(path: string, data: FileData): Promise<void>;
|
|
1474
|
+
readdir(path: string): Promise<string[]>;
|
|
1475
|
+
readdir(path: string, opts: {
|
|
1476
|
+
withFileTypes: true;
|
|
1477
|
+
}): Promise<DirEntry[]>;
|
|
1478
|
+
mkdir(path: string, opts?: {
|
|
1479
|
+
recursive?: boolean;
|
|
1480
|
+
mode?: number;
|
|
1481
|
+
}): Promise<void>;
|
|
1482
|
+
rm(path: string, opts?: {
|
|
1483
|
+
recursive?: boolean;
|
|
1484
|
+
force?: boolean;
|
|
1485
|
+
}): Promise<void>;
|
|
1486
|
+
rename(from: string, to: string): Promise<void>;
|
|
1487
|
+
copyFile(from: string, to: string): Promise<void>;
|
|
1488
|
+
stat(path: string): Promise<Stats>;
|
|
1489
|
+
lstat(path: string): Promise<Stats>;
|
|
1490
|
+
exists(path: string): Promise<boolean>;
|
|
1491
|
+
symlink(target: string, link: string): Promise<void>;
|
|
1492
|
+
readlink(path: string): Promise<string>;
|
|
1493
|
+
realpath(path: string): Promise<string>;
|
|
1494
|
+
chmod(path: string, mode: number): Promise<void>;
|
|
1495
|
+
chown(path: string, uid: number, gid: number): Promise<void>;
|
|
1496
|
+
/** Every path beneath `root`, depth-first. */
|
|
1497
|
+
walk(root?: string): Promise<string[]>;
|
|
1498
|
+
/** Total bytes and file counts, as `df` reports them. */
|
|
1499
|
+
usage(root?: string): Promise<{
|
|
1500
|
+
files: number;
|
|
1501
|
+
dirs: number;
|
|
1502
|
+
bytes: number;
|
|
1503
|
+
}>;
|
|
1504
|
+
/** Read many files at once, keyed by path. */
|
|
1505
|
+
readAll(paths: string[]): Promise<Record<string, string>>;
|
|
1506
|
+
/** Write a whole map of files, creating parents. */
|
|
1507
|
+
writeAll(files: Record<string, FileData>, opts?: {
|
|
1508
|
+
cwd?: string;
|
|
1509
|
+
}): Promise<void>;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
/**
|
|
1513
|
+
* A stateful shell session — `cd`, variables, functions and history persist
|
|
1514
|
+
* across calls, the way a terminal does. `Container.exec` is deliberately
|
|
1515
|
+
* stateless; this is the other half.
|
|
1516
|
+
*/
|
|
1517
|
+
|
|
1518
|
+
interface SessionInit {
|
|
1519
|
+
cwd: string;
|
|
1520
|
+
env: Record<string, string>;
|
|
1521
|
+
cred: Cred;
|
|
1522
|
+
hooks?: {
|
|
1523
|
+
onStdout?: (chunk: string) => void;
|
|
1524
|
+
onStderr?: (chunk: string) => void;
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1527
|
+
interface SessionRunOptions {
|
|
1528
|
+
stdin?: string | Uint8Array | InputStream;
|
|
1529
|
+
onStdout?: (chunk: string) => void;
|
|
1530
|
+
onStderr?: (chunk: string) => void;
|
|
1531
|
+
timeoutMs?: number;
|
|
1532
|
+
/** Present the session as attached to a terminal. */
|
|
1533
|
+
tty?: boolean;
|
|
1534
|
+
columns?: number;
|
|
1535
|
+
rows?: number;
|
|
1536
|
+
}
|
|
1537
|
+
interface SessionResult {
|
|
1538
|
+
exitCode: number;
|
|
1539
|
+
stdout: string;
|
|
1540
|
+
stderr: string;
|
|
1541
|
+
output: string;
|
|
1542
|
+
}
|
|
1543
|
+
declare class Session {
|
|
1544
|
+
private readonly kernel;
|
|
1545
|
+
readonly shell: Shell;
|
|
1546
|
+
readonly proc: Process;
|
|
1547
|
+
private closed;
|
|
1548
|
+
constructor(kernel: Kernel, init: SessionInit);
|
|
1549
|
+
private readonly hooks;
|
|
1550
|
+
get cwd(): string;
|
|
1551
|
+
get env(): Record<string, string>;
|
|
1552
|
+
get history(): string[];
|
|
1553
|
+
/** Run a command line, keeping every side effect for the next call. */
|
|
1554
|
+
run(command: string, opts?: SessionRunOptions): Promise<SessionResult>;
|
|
1555
|
+
/** Stream a long-running command; resolves when it exits. */
|
|
1556
|
+
stream(command: string, handlers?: {
|
|
1557
|
+
onStdout?: (chunk: string) => void;
|
|
1558
|
+
onStderr?: (chunk: string) => void;
|
|
1559
|
+
}): Promise<number>;
|
|
1560
|
+
/** Feed the session an arbitrary output stream, for terminal integration. */
|
|
1561
|
+
pipeTo(command: string, stdout: OutputStream, stderr: OutputStream, stdin?: InputStream): Promise<number>;
|
|
1562
|
+
/** True when the last command asked the shell to exit. */
|
|
1563
|
+
get isExiting(): boolean;
|
|
1564
|
+
close(): void;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
/**
|
|
1568
|
+
* The public surface: a Linux-like container you can boot inside any Node
|
|
1569
|
+
* process, run commands in, and throw away.
|
|
1570
|
+
*
|
|
1571
|
+
* ```ts
|
|
1572
|
+
* const box = await createContainer({ files: { "/app/index.js": "console.log(1)" } });
|
|
1573
|
+
* await box.exec("node /app/index.js");
|
|
1574
|
+
* ```
|
|
1575
|
+
*/
|
|
1576
|
+
|
|
1577
|
+
interface ContainerOptions {
|
|
1578
|
+
/**
|
|
1579
|
+
* Files to seed the filesystem with, keyed by absolute (or `cwd`-relative)
|
|
1580
|
+
* path. Parent directories are created automatically — this is the fast way
|
|
1581
|
+
* to drop a whole project in and run it.
|
|
1582
|
+
*/
|
|
1583
|
+
files?: Record<string, string | Uint8Array>;
|
|
1584
|
+
/** Directory `files` keys are resolved against, and the default cwd. Default `/`. */
|
|
1585
|
+
cwd?: string;
|
|
1586
|
+
hostname?: string;
|
|
1587
|
+
/**
|
|
1588
|
+
* Login user. `"root"` (default) runs privileged; any other name is created
|
|
1589
|
+
* with uid 1000 and sudo rights. Pass `null` for a root-only image.
|
|
1590
|
+
*/
|
|
1591
|
+
user?: string | null;
|
|
1592
|
+
env?: Record<string, string>;
|
|
1593
|
+
/** RAM reported by `free`, `top` and `/proc/meminfo`. Default 2 GiB. */
|
|
1594
|
+
memory?: number;
|
|
1595
|
+
/** CPU count reported by `nproc` and `/proc/cpuinfo`. Default 4. */
|
|
1596
|
+
cpus?: number;
|
|
1597
|
+
network?: NetworkOptions;
|
|
1598
|
+
timezone?: string;
|
|
1599
|
+
/** Default wall-clock limit for `exec`. Omit for no limit. */
|
|
1600
|
+
timeoutMs?: number;
|
|
1601
|
+
/** Called for every byte any command writes to stdout, across the container. */
|
|
1602
|
+
onStdout?: (chunk: string) => void;
|
|
1603
|
+
onStderr?: (chunk: string) => void;
|
|
1604
|
+
/** Invoked when an in-container HTTP server starts listening. */
|
|
1605
|
+
onServerReady?: (port: number, url: string) => void;
|
|
1606
|
+
/**
|
|
1607
|
+
* Run on a JavaScript runtime you booted yourself.
|
|
1608
|
+
*
|
|
1609
|
+
* The container boots a {@link LocalRuntimePod} when this is omitted, which
|
|
1610
|
+
* is what almost every caller wants. Pass one to share a single runtime
|
|
1611
|
+
* across containers, to seed it differently, or to substitute an
|
|
1612
|
+
* implementation of your own — anything satisfying {@link RuntimePod} works.
|
|
1613
|
+
*
|
|
1614
|
+
* ```ts
|
|
1615
|
+
* const pod = await LocalRuntimePod.boot({ workdir: "/app" });
|
|
1616
|
+
* const box = await createContainer({ pod, cwd: "/app" });
|
|
1617
|
+
* ```
|
|
1618
|
+
*/
|
|
1619
|
+
pod?: RuntimePod;
|
|
1620
|
+
/**
|
|
1621
|
+
* Where guest programs run.
|
|
1622
|
+
*
|
|
1623
|
+
* `"auto"` (the default) tries the worker runtime and reports any fallback.
|
|
1624
|
+
* `"worker"` requires a working guest worker and shared-memory channel: boot
|
|
1625
|
+
* rejects with the cause if either is unavailable, rather than letting
|
|
1626
|
+
* synchronous child-process calls fail later. `"realm"` opts out explicitly.
|
|
1627
|
+
* Processes requiring host-native modules can still run in the host realm.
|
|
1628
|
+
*/
|
|
1629
|
+
isolation?: "auto" | "worker" | "realm";
|
|
1630
|
+
/** Receives the reason for an automatic fallback; defaults to console.warn. */
|
|
1631
|
+
onRuntimeFallback?: (error: Error) => void;
|
|
1632
|
+
/**
|
|
1633
|
+
* Where to load the guest worker bundle from.
|
|
1634
|
+
*
|
|
1635
|
+
* Defaults to the copy shipped beside the main bundle, which is what a
|
|
1636
|
+
* published package wants. Worth setting when a bundler has moved or
|
|
1637
|
+
* rewritten it — or when running from source, where the built file is the
|
|
1638
|
+
* only one a Worker can load.
|
|
1639
|
+
*/
|
|
1640
|
+
workerUrl?: string | URL;
|
|
1641
|
+
/** Python runtime settings; a browser host uses this to locate the wasm. */
|
|
1642
|
+
python?: PythonOptions;
|
|
1643
|
+
}
|
|
1644
|
+
interface ExecOptions {
|
|
1645
|
+
cwd?: string;
|
|
1646
|
+
env?: Record<string, string>;
|
|
1647
|
+
/** Run as this user instead of the container default. */
|
|
1648
|
+
user?: string;
|
|
1649
|
+
stdin?: string | Uint8Array;
|
|
1650
|
+
/** Stream output as it is produced, in addition to buffering it. */
|
|
1651
|
+
onStdout?: (chunk: string) => void;
|
|
1652
|
+
onStderr?: (chunk: string) => void;
|
|
1653
|
+
timeoutMs?: number;
|
|
1654
|
+
/** Report the command as running on a terminal (affects `ls` colour, `-t`). */
|
|
1655
|
+
tty?: boolean;
|
|
1656
|
+
columns?: number;
|
|
1657
|
+
rows?: number;
|
|
1658
|
+
}
|
|
1659
|
+
interface ExecResult {
|
|
1660
|
+
exitCode: number;
|
|
1661
|
+
stdout: string;
|
|
1662
|
+
stderr: string;
|
|
1663
|
+
/** stdout and stderr interleaved in write order. */
|
|
1664
|
+
output: string;
|
|
1665
|
+
timedOut: boolean;
|
|
1666
|
+
durationMs: number;
|
|
1667
|
+
}
|
|
1668
|
+
interface SpawnHandle {
|
|
1669
|
+
pid: number;
|
|
1670
|
+
stdin: Pipe;
|
|
1671
|
+
stdout: Pipe;
|
|
1672
|
+
stderr: Pipe;
|
|
1673
|
+
/** Resolves with the exit code. */
|
|
1674
|
+
wait(): Promise<number>;
|
|
1675
|
+
kill(signal?: string): void;
|
|
1676
|
+
readonly exitCode: number | null;
|
|
1677
|
+
}
|
|
1678
|
+
interface HttpResponse {
|
|
1679
|
+
status: number;
|
|
1680
|
+
statusText: string;
|
|
1681
|
+
headers: Record<string, string>;
|
|
1682
|
+
body: string;
|
|
1683
|
+
bytes: Uint8Array;
|
|
1684
|
+
json<T = unknown>(): T;
|
|
1685
|
+
}
|
|
1686
|
+
declare class Container {
|
|
1687
|
+
readonly kernel: Kernel;
|
|
1688
|
+
readonly pod: RuntimePod;
|
|
1689
|
+
readonly fs: ContainerFs;
|
|
1690
|
+
readonly net: NetworkStack;
|
|
1691
|
+
private readonly defaults;
|
|
1692
|
+
private readonly hooks;
|
|
1693
|
+
private disposed;
|
|
1694
|
+
private defaultSession;
|
|
1695
|
+
private readonly restoreNodeChildProcessBridge;
|
|
1696
|
+
private constructor();
|
|
1697
|
+
static create(opts?: ContainerOptions): Promise<Container>;
|
|
1698
|
+
/**
|
|
1699
|
+
* Drop a map of files into the container. Keys may be absolute or relative
|
|
1700
|
+
* to `opts.cwd`; parent directories are created as needed.
|
|
1701
|
+
*/
|
|
1702
|
+
mount(files: Record<string, string | Uint8Array>, opts?: {
|
|
1703
|
+
cwd?: string;
|
|
1704
|
+
mode?: number;
|
|
1705
|
+
}): this;
|
|
1706
|
+
private mountSync;
|
|
1707
|
+
/** Copy a directory tree from the host filesystem into the container. */
|
|
1708
|
+
copyIn(hostPath: string, containerPath: string): Promise<void>;
|
|
1709
|
+
/** Copy a file or directory out of the container onto the host. */
|
|
1710
|
+
copyOut(containerPath: string, hostPath: string): Promise<void>;
|
|
1711
|
+
/** Run a shell command line and collect its output. */
|
|
1712
|
+
exec(command: string, opts?: ExecOptions): Promise<ExecResult>;
|
|
1713
|
+
/** Run a program directly, without a shell parsing the arguments. */
|
|
1714
|
+
run(argv: string[], opts?: ExecOptions): Promise<ExecResult>;
|
|
1715
|
+
/**
|
|
1716
|
+
* Start a command and get streams back, for long-running processes such as a
|
|
1717
|
+
* dev server that you want to watch and later kill.
|
|
1718
|
+
*/
|
|
1719
|
+
spawn(command: string, opts?: ExecOptions): SpawnHandle;
|
|
1720
|
+
/**
|
|
1721
|
+
* A stateful shell session: `cd`, variables and functions persist between
|
|
1722
|
+
* calls, the way a terminal behaves.
|
|
1723
|
+
*/
|
|
1724
|
+
session(opts?: {
|
|
1725
|
+
cwd?: string;
|
|
1726
|
+
env?: Record<string, string>;
|
|
1727
|
+
user?: string;
|
|
1728
|
+
}): Session;
|
|
1729
|
+
/** The container-wide session used by `shell()` shorthand helpers. */
|
|
1730
|
+
get shell(): Session;
|
|
1731
|
+
private makeStdio;
|
|
1732
|
+
/** Send an HTTP request to a server running inside the container. */
|
|
1733
|
+
request(port: number, init?: {
|
|
1734
|
+
method?: string;
|
|
1735
|
+
path?: string;
|
|
1736
|
+
headers?: Record<string, string>;
|
|
1737
|
+
body?: string | Uint8Array;
|
|
1738
|
+
}): Promise<HttpResponse>;
|
|
1739
|
+
/**
|
|
1740
|
+
* Deliver a request whose body is bytes, without letting them become text.
|
|
1741
|
+
*
|
|
1742
|
+
* A RuntimePod's public `request()` may run the body through `toString("utf8")` on
|
|
1743
|
+
* its way in, so anything above `0x7f` is replaced: a five-byte payload
|
|
1744
|
+
* containing `0x89` and `0xff` arrives as nine. That silently destroys every
|
|
1745
|
+
* upload — an image or a video reaches the server the wrong size and no
|
|
1746
|
+
* longer decodes, with no error raised anywhere.
|
|
1747
|
+
*
|
|
1748
|
+
* Its own dispatcher one layer down does preserve bytes, so a binary body
|
|
1749
|
+
* goes straight there. This reaches past the published surface deliberately,
|
|
1750
|
+
* so it is written to fail soft: any shape it does not recognise returns
|
|
1751
|
+
* `null` and the caller falls back to the ordinary path, which is exactly
|
|
1752
|
+
* the behaviour that existed before. Text bodies never come through here.
|
|
1753
|
+
*/
|
|
1754
|
+
private dispatchBinary;
|
|
1755
|
+
/** Wait until something inside the container answers on `port`. */
|
|
1756
|
+
waitForPort(port: number, opts?: {
|
|
1757
|
+
timeoutMs?: number;
|
|
1758
|
+
intervalMs?: number;
|
|
1759
|
+
}): Promise<boolean>;
|
|
1760
|
+
/**
|
|
1761
|
+
* Bridge a container port onto a real host port, so a browser (or anything
|
|
1762
|
+
* else on your machine) can reach a dev server running inside the sandbox.
|
|
1763
|
+
*/
|
|
1764
|
+
expose(port: number, opts?: {
|
|
1765
|
+
hostPort?: number;
|
|
1766
|
+
hostname?: string;
|
|
1767
|
+
}): Promise<{
|
|
1768
|
+
url: string;
|
|
1769
|
+
port: number;
|
|
1770
|
+
close(): Promise<void>;
|
|
1771
|
+
}>;
|
|
1772
|
+
/** A serialisable snapshot of the whole filesystem. */
|
|
1773
|
+
snapshot(opts?: {
|
|
1774
|
+
shallow?: boolean;
|
|
1775
|
+
}): unknown;
|
|
1776
|
+
restore(snapshot: unknown): Promise<void>;
|
|
1777
|
+
get hostname(): string;
|
|
1778
|
+
get user(): string;
|
|
1779
|
+
get cwd(): string;
|
|
1780
|
+
get env(): Record<string, string>;
|
|
1781
|
+
private assertActive;
|
|
1782
|
+
/** Tear down every process and release the runtime pod. */
|
|
1783
|
+
dispose(): void;
|
|
1784
|
+
get isDisposed(): boolean;
|
|
1785
|
+
}
|
|
1786
|
+
/** Boot a container. The one function most callers need. */
|
|
1787
|
+
declare function createContainer(opts?: ContainerOptions): Promise<Container>;
|
|
1788
|
+
|
|
1789
|
+
export { type ProcessKind as $, type KernelOptions as A, BufferSink as B, Container as C, type DirEntry as D, type ExecContext as E, type FileData as F, type GroupEntry as G, type HttpResponse as H, type InputStream as I, type Job as J, Kernel as K, type ListeningPort as L, type MountEntry as M, type Node as N, type OutputStream as O, type NetInterface as P, type NetworkOptions as Q, type RuntimeVolume as R, Shell as S, NetworkStack as T, NullInput as U, Vfs as V, NullOutput as W, PYTHON_VERSION as X, type PasswdEntry as Y, Pipe as Z, Process as _, type ShellIO as a, type ProcessOptions as a0, type ProcessState as a1, ProcessTable as a2, type PythonOptions as a3, ROOT_CRED as a4, type ResolvedExecutable as a5, type RunOptions as a6, type RunResult as a7, type RuntimeProcessManager as a8, type RuntimeProcessResult as a9, isCPythonAvailable as aA, isPythonAvailable as aB, makeCred as aC, octalMode as aD, parseUmask as aE, resetPidCounter as aF, shellQuote as aG, type SessionInit as aa, type SessionResult as ab, type SessionRunOptions as ac, ShellExit as ad, type ShellInit as ae, type ShellOptions as af, type SpawnHandle as ag, Stats as ah, type Stdio as ai, TeeOutput as aj, UserDatabase as ak, Variables as al, type VirtualNode as am, type VirtualProvider as an, type WriteOptions as ao, applyChmod as ap, braceExpand as aq, captureStdio as ar, configureCPython as as, configurePython as at, createChildProcessModule as au, createContext as av, defineCommand as aw, expandWord as ax, expandWords as ay, formatMode as az, Session as b, type Cred as c, type Command as d, type VolumeStat as e, type VolumeStats as f, type RuntimeHttpResponse as g, type SpawnChild as h, type SyncSpawn as i, type RuntimePod as j, type RuntimePackageInstaller as k, type ChildSpawnConfig as l, type ChildHandle as m, type RuntimeProcess as n, createContainer as o, type CPythonOptions as p, CallbackSink as q, CommandRegistry as r, ContainerFs as s, type ContainerOptions as t, type ContextInit as u, type Env as v, type ExecOptions as w, type ExecResult as x, FileInput as y, FileOutput as z };
|