@secure-exec/core 0.1.1-rc.3 → 0.2.0-rc.1
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/esm-compiler.d.ts +5 -1
- package/dist/esm-compiler.js +5 -1
- package/dist/fs-helpers.d.ts +1 -1
- package/dist/generated/isolate-runtime.d.ts +15 -15
- package/dist/generated/isolate-runtime.js +15 -15
- package/dist/index.d.ts +24 -5
- package/dist/index.js +23 -3
- package/dist/isolate-runtime/apply-custom-global-policy.js +3 -3
- package/dist/isolate-runtime/apply-timing-mitigation-freeze.js +2 -2
- package/dist/isolate-runtime/apply-timing-mitigation-off.js +2 -2
- package/dist/isolate-runtime/bridge-attach.js +2 -2
- package/dist/isolate-runtime/bridge-initial-globals.js +145 -6
- package/dist/isolate-runtime/eval-script-result.js +1 -1
- package/dist/isolate-runtime/global-exposure-helpers.js +2 -2
- package/dist/isolate-runtime/init-commonjs-module-globals.js +2 -2
- package/dist/isolate-runtime/override-process-cwd.js +1 -1
- package/dist/isolate-runtime/override-process-env.js +1 -1
- package/dist/isolate-runtime/require-setup.js +1600 -338
- package/dist/isolate-runtime/set-commonjs-file-globals.js +2 -2
- package/dist/isolate-runtime/set-stdin-data.js +1 -1
- package/dist/isolate-runtime/setup-dynamic-import.js +47 -19
- package/dist/isolate-runtime/setup-fs-facade.js +62 -23
- package/dist/kernel/command-registry.d.ts +44 -0
- package/dist/kernel/command-registry.js +114 -0
- package/dist/kernel/device-layer.d.ts +12 -0
- package/dist/kernel/device-layer.js +262 -0
- package/dist/kernel/dns-cache.d.ts +29 -0
- package/dist/kernel/dns-cache.js +52 -0
- package/dist/kernel/fd-table.d.ts +84 -0
- package/dist/kernel/fd-table.js +278 -0
- package/dist/kernel/file-lock.d.ts +34 -0
- package/dist/kernel/file-lock.js +123 -0
- package/dist/kernel/host-adapter.d.ts +50 -0
- package/dist/kernel/host-adapter.js +8 -0
- package/dist/kernel/index.d.ts +36 -0
- package/dist/kernel/index.js +34 -0
- package/dist/kernel/inode-table.d.ts +43 -0
- package/dist/kernel/inode-table.js +85 -0
- package/dist/kernel/kernel.d.ts +9 -0
- package/dist/kernel/kernel.js +1396 -0
- package/dist/kernel/permissions.d.ts +27 -0
- package/dist/kernel/permissions.js +118 -0
- package/dist/kernel/pipe-manager.d.ts +64 -0
- package/dist/kernel/pipe-manager.js +267 -0
- package/dist/kernel/proc-layer.d.ts +11 -0
- package/dist/kernel/proc-layer.js +501 -0
- package/dist/kernel/process-table.d.ts +124 -0
- package/dist/kernel/process-table.js +631 -0
- package/dist/kernel/pty.d.ts +108 -0
- package/dist/kernel/pty.js +541 -0
- package/dist/kernel/socket-table.d.ts +305 -0
- package/dist/kernel/socket-table.js +1124 -0
- package/dist/kernel/timer-table.d.ts +54 -0
- package/dist/kernel/timer-table.js +108 -0
- package/dist/kernel/types.d.ts +500 -0
- package/dist/kernel/types.js +89 -0
- package/dist/kernel/user.d.ts +29 -0
- package/dist/kernel/user.js +35 -0
- package/dist/kernel/vfs.d.ts +54 -0
- package/dist/kernel/vfs.js +8 -0
- package/dist/kernel/wait.d.ts +45 -0
- package/dist/kernel/wait.js +112 -0
- package/dist/kernel/wstatus.d.ts +21 -0
- package/dist/kernel/wstatus.js +33 -0
- package/dist/module-resolver.d.ts +4 -0
- package/dist/module-resolver.js +4 -0
- package/dist/package-bundler.d.ts +6 -1
- package/dist/runtime-driver.d.ts +3 -1
- package/dist/shared/bridge-contract.d.ts +329 -20
- package/dist/shared/bridge-contract.js +60 -5
- package/dist/shared/console-formatter.js +8 -4
- package/dist/shared/global-exposure.js +269 -19
- package/dist/shared/in-memory-fs.d.ts +30 -11
- package/dist/shared/in-memory-fs.js +383 -109
- package/dist/shared/permissions.d.ts +4 -6
- package/dist/shared/permissions.js +19 -39
- package/dist/types.d.ts +8 -159
- package/dist/types.js +5 -0
- package/package.json +12 -22
- package/dist/bridge/active-handles.d.ts +0 -22
- package/dist/bridge/active-handles.js +0 -55
- package/dist/bridge/child-process.d.ts +0 -99
- package/dist/bridge/child-process.js +0 -670
- package/dist/bridge/fs.d.ts +0 -281
- package/dist/bridge/fs.js +0 -2235
- package/dist/bridge/index.d.ts +0 -10
- package/dist/bridge/index.js +0 -41
- package/dist/bridge/module.d.ts +0 -75
- package/dist/bridge/module.js +0 -308
- package/dist/bridge/network.d.ts +0 -350
- package/dist/bridge/network.js +0 -2050
- package/dist/bridge/os.d.ts +0 -13
- package/dist/bridge/os.js +0 -256
- package/dist/bridge/polyfills.d.ts +0 -2
- package/dist/bridge/polyfills.js +0 -11
- package/dist/bridge/process.d.ts +0 -89
- package/dist/bridge/process.js +0 -1015
- package/dist/bridge.js +0 -12496
- package/dist/python-runtime.d.ts +0 -16
- package/dist/python-runtime.js +0 -45
- package/dist/runtime.d.ts +0 -31
- package/dist/runtime.js +0 -69
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel timer table with per-process ownership and budget enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Tracks active timers (setTimeout/setInterval) per-process. Actual
|
|
5
|
+
* scheduling is delegated to the host via callbacks — the kernel only
|
|
6
|
+
* manages ownership, limits, and cleanup.
|
|
7
|
+
*/
|
|
8
|
+
export interface KernelTimer {
|
|
9
|
+
readonly id: number;
|
|
10
|
+
readonly pid: number;
|
|
11
|
+
readonly delayMs: number;
|
|
12
|
+
readonly repeat: boolean;
|
|
13
|
+
/** Host-side handle returned by the scheduling function (for cancellation). */
|
|
14
|
+
hostHandle: ReturnType<typeof setTimeout> | number | undefined;
|
|
15
|
+
/** User callback to invoke when the timer fires. */
|
|
16
|
+
callback: () => void;
|
|
17
|
+
/** True once the timer has been cleared. */
|
|
18
|
+
cleared: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface TimerTableOptions {
|
|
21
|
+
/** Default per-process timer limit. 0 = unlimited. */
|
|
22
|
+
defaultMaxTimers?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare class TimerTable {
|
|
25
|
+
private timers;
|
|
26
|
+
private nextTimerId;
|
|
27
|
+
private defaultMaxTimers;
|
|
28
|
+
/** Per-process limit overrides. */
|
|
29
|
+
private processLimits;
|
|
30
|
+
constructor(options?: TimerTableOptions);
|
|
31
|
+
/**
|
|
32
|
+
* Create a timer owned by `pid`.
|
|
33
|
+
* Returns the kernel timer ID. The caller must schedule the actual
|
|
34
|
+
* timeout on the host and set `timer.hostHandle`.
|
|
35
|
+
*/
|
|
36
|
+
createTimer(pid: number, delayMs: number, repeat: boolean, callback: () => void): number;
|
|
37
|
+
/** Get a timer by ID. Returns null if not found. */
|
|
38
|
+
get(timerId: number): KernelTimer | null;
|
|
39
|
+
/** Clear (cancel) a timer. The caller should also cancel the host-side handle. */
|
|
40
|
+
clearTimer(timerId: number, pid?: number): void;
|
|
41
|
+
/** Set per-process timer limit. */
|
|
42
|
+
setLimit(pid: number, maxTimers: number): void;
|
|
43
|
+
/** Get the active timer count for a process. */
|
|
44
|
+
countForProcess(pid: number): number;
|
|
45
|
+
/** Get all active timers for a process. */
|
|
46
|
+
getActiveTimers(pid: number): KernelTimer[];
|
|
47
|
+
/** Clear all timers owned by a process. Called on process exit. */
|
|
48
|
+
clearAllForProcess(pid: number): void;
|
|
49
|
+
/** Dispose all timers. Called on kernel shutdown. */
|
|
50
|
+
disposeAll(): void;
|
|
51
|
+
/** Number of active timers across all processes. */
|
|
52
|
+
get size(): number;
|
|
53
|
+
private getLimit;
|
|
54
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel timer table with per-process ownership and budget enforcement.
|
|
3
|
+
*
|
|
4
|
+
* Tracks active timers (setTimeout/setInterval) per-process. Actual
|
|
5
|
+
* scheduling is delegated to the host via callbacks — the kernel only
|
|
6
|
+
* manages ownership, limits, and cleanup.
|
|
7
|
+
*/
|
|
8
|
+
import { KernelError } from "./types.js";
|
|
9
|
+
export class TimerTable {
|
|
10
|
+
timers = new Map();
|
|
11
|
+
nextTimerId = 1;
|
|
12
|
+
defaultMaxTimers;
|
|
13
|
+
/** Per-process limit overrides. */
|
|
14
|
+
processLimits = new Map();
|
|
15
|
+
constructor(options) {
|
|
16
|
+
this.defaultMaxTimers = options?.defaultMaxTimers ?? 0;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Create a timer owned by `pid`.
|
|
20
|
+
* Returns the kernel timer ID. The caller must schedule the actual
|
|
21
|
+
* timeout on the host and set `timer.hostHandle`.
|
|
22
|
+
*/
|
|
23
|
+
createTimer(pid, delayMs, repeat, callback) {
|
|
24
|
+
// Enforce per-process limit
|
|
25
|
+
const limit = this.getLimit(pid);
|
|
26
|
+
if (limit > 0) {
|
|
27
|
+
const count = this.countForProcess(pid);
|
|
28
|
+
if (count >= limit) {
|
|
29
|
+
throw new KernelError("EAGAIN", "timer limit exceeded");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const id = this.nextTimerId++;
|
|
33
|
+
const timer = {
|
|
34
|
+
id,
|
|
35
|
+
pid,
|
|
36
|
+
delayMs,
|
|
37
|
+
repeat,
|
|
38
|
+
hostHandle: undefined,
|
|
39
|
+
callback,
|
|
40
|
+
cleared: false,
|
|
41
|
+
};
|
|
42
|
+
this.timers.set(id, timer);
|
|
43
|
+
return id;
|
|
44
|
+
}
|
|
45
|
+
/** Get a timer by ID. Returns null if not found. */
|
|
46
|
+
get(timerId) {
|
|
47
|
+
return this.timers.get(timerId) ?? null;
|
|
48
|
+
}
|
|
49
|
+
/** Clear (cancel) a timer. The caller should also cancel the host-side handle. */
|
|
50
|
+
clearTimer(timerId, pid) {
|
|
51
|
+
const timer = this.timers.get(timerId);
|
|
52
|
+
if (!timer)
|
|
53
|
+
return; // Clearing a non-existent timer is a no-op (matches POSIX)
|
|
54
|
+
// Cross-process isolation: if pid is provided, only the owning process can clear
|
|
55
|
+
if (pid !== undefined && timer.pid !== pid) {
|
|
56
|
+
throw new KernelError("EACCES", `timer ${timerId} not owned by pid ${pid}`);
|
|
57
|
+
}
|
|
58
|
+
timer.cleared = true;
|
|
59
|
+
this.timers.delete(timerId);
|
|
60
|
+
}
|
|
61
|
+
/** Set per-process timer limit. */
|
|
62
|
+
setLimit(pid, maxTimers) {
|
|
63
|
+
this.processLimits.set(pid, maxTimers);
|
|
64
|
+
}
|
|
65
|
+
/** Get the active timer count for a process. */
|
|
66
|
+
countForProcess(pid) {
|
|
67
|
+
let count = 0;
|
|
68
|
+
for (const timer of this.timers.values()) {
|
|
69
|
+
if (timer.pid === pid)
|
|
70
|
+
count++;
|
|
71
|
+
}
|
|
72
|
+
return count;
|
|
73
|
+
}
|
|
74
|
+
/** Get all active timers for a process. */
|
|
75
|
+
getActiveTimers(pid) {
|
|
76
|
+
const result = [];
|
|
77
|
+
for (const timer of this.timers.values()) {
|
|
78
|
+
if (timer.pid === pid)
|
|
79
|
+
result.push(timer);
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/** Clear all timers owned by a process. Called on process exit. */
|
|
84
|
+
clearAllForProcess(pid) {
|
|
85
|
+
for (const [id, timer] of this.timers) {
|
|
86
|
+
if (timer.pid === pid) {
|
|
87
|
+
timer.cleared = true;
|
|
88
|
+
this.timers.delete(id);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
this.processLimits.delete(pid);
|
|
92
|
+
}
|
|
93
|
+
/** Dispose all timers. Called on kernel shutdown. */
|
|
94
|
+
disposeAll() {
|
|
95
|
+
for (const timer of this.timers.values()) {
|
|
96
|
+
timer.cleared = true;
|
|
97
|
+
}
|
|
98
|
+
this.timers.clear();
|
|
99
|
+
this.processLimits.clear();
|
|
100
|
+
}
|
|
101
|
+
/** Number of active timers across all processes. */
|
|
102
|
+
get size() {
|
|
103
|
+
return this.timers.size;
|
|
104
|
+
}
|
|
105
|
+
getLimit(pid) {
|
|
106
|
+
return this.processLimits.get(pid) ?? this.defaultMaxTimers;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Kernel type definitions.
|
|
3
|
+
*
|
|
4
|
+
* The kernel is the shared OS layer. All runtimes make "syscalls" to the
|
|
5
|
+
* kernel for filesystem, process, pipe, and FD operations.
|
|
6
|
+
*/
|
|
7
|
+
import type { WaitQueue } from "./wait.js";
|
|
8
|
+
export type { VirtualFileSystem, VirtualDirEntry, VirtualStat, } from "./vfs.js";
|
|
9
|
+
export interface KernelOptions {
|
|
10
|
+
filesystem: import("./vfs.js").VirtualFileSystem;
|
|
11
|
+
permissions?: Permissions;
|
|
12
|
+
env?: Record<string, string>;
|
|
13
|
+
cwd?: string;
|
|
14
|
+
/** Maximum number of concurrent processes. Spawn beyond this limit throws EAGAIN. */
|
|
15
|
+
maxProcesses?: number;
|
|
16
|
+
/** Host network adapter for external socket routing (TCP, UDP, DNS). */
|
|
17
|
+
hostNetworkAdapter?: import("./host-adapter.js").HostNetworkAdapter;
|
|
18
|
+
}
|
|
19
|
+
export interface Kernel {
|
|
20
|
+
/** Mount a runtime driver. Calls driver.init() and registers its commands. */
|
|
21
|
+
mount(driver: RuntimeDriver): Promise<void>;
|
|
22
|
+
/** Dispose the kernel and all mounted drivers. */
|
|
23
|
+
dispose(): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Execute a command string through the shell.
|
|
26
|
+
* Equivalent to: spawn('sh', ['-c', command])
|
|
27
|
+
* Throws if no shell is mounted (e.g. no WasmVM runtime).
|
|
28
|
+
*/
|
|
29
|
+
exec(command: string, options?: ExecOptions): Promise<ExecResult>;
|
|
30
|
+
/**
|
|
31
|
+
* Spawn a process directly (no shell interpretation).
|
|
32
|
+
* The kernel resolves the command via the command registry and delegates
|
|
33
|
+
* to the appropriate runtime driver.
|
|
34
|
+
*/
|
|
35
|
+
spawn(command: string, args: string[], options?: SpawnOptions): ManagedProcess;
|
|
36
|
+
/**
|
|
37
|
+
* Flush pending /bin stub entries created by on-demand command discovery.
|
|
38
|
+
* Ensures VFS is consistent before shell PATH lookups.
|
|
39
|
+
*/
|
|
40
|
+
flushPendingBinEntries(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Open an interactive shell on a PTY.
|
|
43
|
+
* Wires PTY + process groups + termios for terminal use.
|
|
44
|
+
*/
|
|
45
|
+
openShell(options?: OpenShellOptions): ShellHandle;
|
|
46
|
+
/**
|
|
47
|
+
* Wire openShell() to process.stdin/stdout for an interactive terminal session.
|
|
48
|
+
* Sets raw mode, forwards input/output, handles resize, restores terminal on exit.
|
|
49
|
+
* Returns the shell exit code.
|
|
50
|
+
*/
|
|
51
|
+
connectTerminal(options?: ConnectTerminalOptions): Promise<number>;
|
|
52
|
+
readFile(path: string): Promise<Uint8Array>;
|
|
53
|
+
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
54
|
+
mkdir(path: string): Promise<void>;
|
|
55
|
+
readdir(path: string): Promise<string[]>;
|
|
56
|
+
stat(path: string): Promise<import("./vfs.js").VirtualStat>;
|
|
57
|
+
exists(path: string): Promise<boolean>;
|
|
58
|
+
readonly socketTable: import("./socket-table.js").SocketTable;
|
|
59
|
+
readonly timerTable: import("./timer-table.js").TimerTable;
|
|
60
|
+
readonly inodeTable: import("./inode-table.js").InodeTable;
|
|
61
|
+
readonly commands: ReadonlyMap<string, string>;
|
|
62
|
+
readonly processes: ReadonlyMap<number, ProcessInfo>;
|
|
63
|
+
/** Number of pending zombie cleanup timers (test observability). */
|
|
64
|
+
readonly zombieTimerCount: number;
|
|
65
|
+
}
|
|
66
|
+
export interface ExecOptions {
|
|
67
|
+
env?: Record<string, string>;
|
|
68
|
+
cwd?: string;
|
|
69
|
+
stdin?: string | Uint8Array;
|
|
70
|
+
timeout?: number;
|
|
71
|
+
onStdout?: (data: Uint8Array) => void;
|
|
72
|
+
onStderr?: (data: Uint8Array) => void;
|
|
73
|
+
}
|
|
74
|
+
export interface ExecResult {
|
|
75
|
+
exitCode: number;
|
|
76
|
+
stdout: string;
|
|
77
|
+
stderr: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SpawnOptions extends ExecOptions {
|
|
80
|
+
stdio?: "pipe" | "inherit";
|
|
81
|
+
/** FD in caller's table to wire as child's stdin (pipe read end). */
|
|
82
|
+
stdinFd?: number;
|
|
83
|
+
/** FD in caller's table to wire as child's stdout (pipe write end). */
|
|
84
|
+
stdoutFd?: number;
|
|
85
|
+
/** FD in caller's table to wire as child's stderr (pipe write end). */
|
|
86
|
+
stderrFd?: number;
|
|
87
|
+
}
|
|
88
|
+
export interface ManagedProcess {
|
|
89
|
+
pid: number;
|
|
90
|
+
writeStdin(data: Uint8Array | string): void;
|
|
91
|
+
closeStdin(): void;
|
|
92
|
+
kill(signal?: number): void;
|
|
93
|
+
wait(): Promise<number>;
|
|
94
|
+
readonly exitCode: number | null;
|
|
95
|
+
}
|
|
96
|
+
export interface OpenShellOptions {
|
|
97
|
+
/** Shell command to run (default: "sh"). */
|
|
98
|
+
command?: string;
|
|
99
|
+
/** Arguments to pass to the shell command. */
|
|
100
|
+
args?: string[];
|
|
101
|
+
/** Environment variables for the shell process. */
|
|
102
|
+
env?: Record<string, string>;
|
|
103
|
+
/** Working directory for the shell process. */
|
|
104
|
+
cwd?: string;
|
|
105
|
+
/** Initial terminal columns. */
|
|
106
|
+
cols?: number;
|
|
107
|
+
/** Initial terminal rows. */
|
|
108
|
+
rows?: number;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Handle returned by kernel.openShell().
|
|
112
|
+
* Provides write/onData/resize/kill/wait for interactive shell use.
|
|
113
|
+
*/
|
|
114
|
+
export interface ShellHandle {
|
|
115
|
+
/** PID of the shell process. */
|
|
116
|
+
pid: number;
|
|
117
|
+
/** Write data to the shell (goes through PTY line discipline). */
|
|
118
|
+
write(data: Uint8Array | string): void;
|
|
119
|
+
/** Callback for data produced by the shell (program output). */
|
|
120
|
+
onData: ((data: Uint8Array) => void) | null;
|
|
121
|
+
/** Notify terminal resize — delivers SIGWINCH to foreground process group. */
|
|
122
|
+
resize(cols: number, rows: number): void;
|
|
123
|
+
/** Kill the shell process. */
|
|
124
|
+
kill(signal?: number): void;
|
|
125
|
+
/** Wait for the shell to exit. Returns exit code. */
|
|
126
|
+
wait(): Promise<number>;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Options for connectTerminal().
|
|
130
|
+
* Extends OpenShellOptions with an optional output handler override.
|
|
131
|
+
*/
|
|
132
|
+
export interface ConnectTerminalOptions extends OpenShellOptions {
|
|
133
|
+
/** Custom output handler. Defaults to writing to process.stdout. */
|
|
134
|
+
onData?: (data: Uint8Array) => void;
|
|
135
|
+
}
|
|
136
|
+
export interface RuntimeDriver {
|
|
137
|
+
/** Driver name (e.g. 'wasmvm', 'node', 'python') */
|
|
138
|
+
name: string;
|
|
139
|
+
/** Commands this driver handles */
|
|
140
|
+
commands: string[];
|
|
141
|
+
/**
|
|
142
|
+
* Called when the driver is mounted to the kernel.
|
|
143
|
+
* Use this to initialize resources (compile WASM, load Pyodide, etc.)
|
|
144
|
+
*/
|
|
145
|
+
init(kernel: KernelInterface): Promise<void>;
|
|
146
|
+
/**
|
|
147
|
+
* Spawn a process for the given command.
|
|
148
|
+
* The kernel has already resolved the command to this driver.
|
|
149
|
+
*/
|
|
150
|
+
spawn(command: string, args: string[], ctx: ProcessContext): DriverProcess;
|
|
151
|
+
/**
|
|
152
|
+
* On-demand command discovery. Called by the kernel when a command is not
|
|
153
|
+
* found in the registry. Returns true if this driver can handle the command
|
|
154
|
+
* (e.g. found a matching WASM binary on disk). The kernel then registers
|
|
155
|
+
* the command and retries the spawn.
|
|
156
|
+
*/
|
|
157
|
+
tryResolve?(command: string): boolean;
|
|
158
|
+
/** Cleanup resources */
|
|
159
|
+
dispose(): Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
export interface ProcessContext {
|
|
162
|
+
pid: number;
|
|
163
|
+
ppid: number;
|
|
164
|
+
env: Record<string, string>;
|
|
165
|
+
cwd: string;
|
|
166
|
+
fds: {
|
|
167
|
+
stdin: number;
|
|
168
|
+
stdout: number;
|
|
169
|
+
stderr: number;
|
|
170
|
+
};
|
|
171
|
+
/** Whether stdin/stdout/stderr are connected to a PTY slave. */
|
|
172
|
+
stdinIsTTY?: boolean;
|
|
173
|
+
stdoutIsTTY?: boolean;
|
|
174
|
+
stderrIsTTY?: boolean;
|
|
175
|
+
/** Kernel-provided callback for stdout data emitted during spawn. */
|
|
176
|
+
onStdout?: (data: Uint8Array) => void;
|
|
177
|
+
/** Kernel-provided callback for stderr data emitted during spawn. */
|
|
178
|
+
onStderr?: (data: Uint8Array) => void;
|
|
179
|
+
}
|
|
180
|
+
export interface DriverProcess {
|
|
181
|
+
/** Called by kernel when data is written to this process's stdin FD */
|
|
182
|
+
writeStdin(data: Uint8Array): void;
|
|
183
|
+
closeStdin(): void;
|
|
184
|
+
/** Called by kernel to terminate the process */
|
|
185
|
+
kill(signal: number): void;
|
|
186
|
+
/** Resolves with exit code when process completes */
|
|
187
|
+
wait(): Promise<number>;
|
|
188
|
+
/** Callbacks for the driver to push data to the kernel */
|
|
189
|
+
onStdout: ((data: Uint8Array) => void) | null;
|
|
190
|
+
onStderr: ((data: Uint8Array) => void) | null;
|
|
191
|
+
onExit: ((code: number) => void) | null;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Interface the kernel exposes TO drivers.
|
|
195
|
+
* Drivers call these methods for kernel services.
|
|
196
|
+
*/
|
|
197
|
+
export interface KernelInterface {
|
|
198
|
+
vfs: import("./vfs.js").VirtualFileSystem;
|
|
199
|
+
fdOpen(pid: number, path: string, flags: number, mode?: number): number;
|
|
200
|
+
fdRead(pid: number, fd: number, length: number): Promise<Uint8Array>;
|
|
201
|
+
fdWrite(pid: number, fd: number, data: Uint8Array): number | Promise<number>;
|
|
202
|
+
fdClose(pid: number, fd: number): void;
|
|
203
|
+
fdSeek(pid: number, fd: number, offset: bigint, whence: number): Promise<bigint>;
|
|
204
|
+
fdPread(pid: number, fd: number, length: number, offset: bigint): Promise<Uint8Array>;
|
|
205
|
+
fdPwrite(pid: number, fd: number, data: Uint8Array, offset: bigint): Promise<number>;
|
|
206
|
+
fdDup(pid: number, fd: number): number;
|
|
207
|
+
fdDup2(pid: number, oldFd: number, newFd: number): void;
|
|
208
|
+
fdDupMin(pid: number, fd: number, minFd: number): number;
|
|
209
|
+
fdStat(pid: number, fd: number): FDStat;
|
|
210
|
+
/** Query poll state for a file descriptor (pipe, PTY, or regular file). */
|
|
211
|
+
fdPoll(pid: number, fd: number): {
|
|
212
|
+
readable: boolean;
|
|
213
|
+
writable: boolean;
|
|
214
|
+
hangup: boolean;
|
|
215
|
+
invalid: boolean;
|
|
216
|
+
};
|
|
217
|
+
fdSetCloexec(pid: number, fd: number, value: boolean): void;
|
|
218
|
+
fdGetCloexec(pid: number, fd: number): boolean;
|
|
219
|
+
fcntl(pid: number, fd: number, cmd: number, arg?: number): number;
|
|
220
|
+
/** Apply or remove an advisory lock on the file referenced by fd. */
|
|
221
|
+
flock(pid: number, fd: number, operation: number): Promise<void>;
|
|
222
|
+
spawn(command: string, args: string[], ctx: Partial<ProcessContext> & {
|
|
223
|
+
stdinFd?: number;
|
|
224
|
+
stdoutFd?: number;
|
|
225
|
+
stderrFd?: number;
|
|
226
|
+
}): ManagedProcess;
|
|
227
|
+
waitpid(pid: number, options?: number): Promise<{
|
|
228
|
+
pid: number;
|
|
229
|
+
status: number;
|
|
230
|
+
termSignal: number;
|
|
231
|
+
} | null>;
|
|
232
|
+
kill(pid: number, signal: number): void;
|
|
233
|
+
getpid(pid: number): number;
|
|
234
|
+
getppid(pid: number): number;
|
|
235
|
+
setpgid(pid: number, pgid: number): void;
|
|
236
|
+
getpgid(pid: number): number;
|
|
237
|
+
setsid(pid: number): number;
|
|
238
|
+
getsid(pid: number): number;
|
|
239
|
+
/** Create a pipe and install both ends in the given process's FD table. */
|
|
240
|
+
pipe(pid: number): {
|
|
241
|
+
readFd: number;
|
|
242
|
+
writeFd: number;
|
|
243
|
+
};
|
|
244
|
+
/** Allocate a PTY master/slave pair and install FDs in the process's table. */
|
|
245
|
+
openpty(pid: number): {
|
|
246
|
+
masterFd: number;
|
|
247
|
+
slaveFd: number;
|
|
248
|
+
path: string;
|
|
249
|
+
};
|
|
250
|
+
/** Check if an FD refers to a terminal (PTY slave). */
|
|
251
|
+
isatty(pid: number, fd: number): boolean;
|
|
252
|
+
/** Set line discipline configuration on the PTY associated with the given FD. */
|
|
253
|
+
ptySetDiscipline(pid: number, fd: number, config: {
|
|
254
|
+
canonical?: boolean;
|
|
255
|
+
echo?: boolean;
|
|
256
|
+
isig?: boolean;
|
|
257
|
+
}): void;
|
|
258
|
+
/** Set the foreground process group for signal delivery on the PTY. */
|
|
259
|
+
ptySetForegroundPgid(pid: number, fd: number, pgid: number): void;
|
|
260
|
+
/** Get terminal attributes for the PTY associated with the given FD. */
|
|
261
|
+
tcgetattr(pid: number, fd: number): Termios;
|
|
262
|
+
/** Set terminal attributes for the PTY associated with the given FD. */
|
|
263
|
+
tcsetattr(pid: number, fd: number, termios: Partial<Termios>): void;
|
|
264
|
+
/** Set the foreground process group for the terminal. */
|
|
265
|
+
tcsetpgrp(pid: number, fd: number, pgid: number): void;
|
|
266
|
+
/** Get the foreground process group for the terminal. */
|
|
267
|
+
tcgetpgrp(pid: number, fd: number): number;
|
|
268
|
+
/** List open FD numbers for a process (readDir /dev/fd). */
|
|
269
|
+
devFdReadDir(pid: number): string[];
|
|
270
|
+
/** Stat the underlying file for /dev/fd/N. */
|
|
271
|
+
devFdStat(pid: number, fd: number): Promise<import("./vfs.js").VirtualStat>;
|
|
272
|
+
getenv(pid: number): Record<string, string>;
|
|
273
|
+
setenv(pid: number, key: string, value: string): void;
|
|
274
|
+
unsetenv(pid: number, key: string): void;
|
|
275
|
+
getcwd(pid: number): string;
|
|
276
|
+
chdir(pid: number, path: string): Promise<void>;
|
|
277
|
+
/** Schedule SIGALRM delivery after `seconds`. Returns previous alarm remaining (0 if none). alarm(pid, 0) cancels. */
|
|
278
|
+
alarm(pid: number, seconds: number): number;
|
|
279
|
+
/** Get/set the process's umask. Returns the previous mask. If newMask is omitted, mask is unchanged. */
|
|
280
|
+
umask(pid: number, newMask?: number): number;
|
|
281
|
+
/** Create a directory, applying the process's umask to the given mode. */
|
|
282
|
+
mkdir(pid: number, path: string, mode?: number): Promise<void>;
|
|
283
|
+
readonly socketTable: import("./socket-table.js").SocketTable;
|
|
284
|
+
readonly timerTable: import("./timer-table.js").TimerTable;
|
|
285
|
+
readonly processTable: import("./process-table.js").ProcessTable;
|
|
286
|
+
}
|
|
287
|
+
export interface FDStat {
|
|
288
|
+
filetype: number;
|
|
289
|
+
flags: number;
|
|
290
|
+
rights: bigint;
|
|
291
|
+
}
|
|
292
|
+
export interface FileDescription {
|
|
293
|
+
id: number;
|
|
294
|
+
path: string;
|
|
295
|
+
/** Stable inode identity for FD I/O after the pathname is unlinked. */
|
|
296
|
+
inode?: number;
|
|
297
|
+
cursor: bigint;
|
|
298
|
+
flags: number;
|
|
299
|
+
refCount: number;
|
|
300
|
+
/** Mode to apply when the file is first created (set by O_CREAT with umask). */
|
|
301
|
+
creationMode?: number;
|
|
302
|
+
}
|
|
303
|
+
export interface FDEntry {
|
|
304
|
+
fd: number;
|
|
305
|
+
description: FileDescription;
|
|
306
|
+
rights: bigint;
|
|
307
|
+
filetype: number;
|
|
308
|
+
/** Close-on-exec flag (FD_CLOEXEC). Per-FD, not per-description. */
|
|
309
|
+
cloexec: boolean;
|
|
310
|
+
}
|
|
311
|
+
export declare const O_RDONLY = 0;
|
|
312
|
+
export declare const O_WRONLY = 1;
|
|
313
|
+
export declare const O_RDWR = 2;
|
|
314
|
+
export declare const O_CREAT = 64;
|
|
315
|
+
export declare const O_EXCL = 128;
|
|
316
|
+
export declare const O_TRUNC = 512;
|
|
317
|
+
export declare const O_APPEND = 1024;
|
|
318
|
+
export declare const O_NONBLOCK = 4;
|
|
319
|
+
export declare const O_CLOEXEC = 524288;
|
|
320
|
+
export declare const F_DUPFD = 0;
|
|
321
|
+
export declare const F_GETFD = 1;
|
|
322
|
+
export declare const F_SETFD = 2;
|
|
323
|
+
export declare const F_GETFL = 3;
|
|
324
|
+
export declare const F_DUPFD_CLOEXEC = 1030;
|
|
325
|
+
export declare const FD_CLOEXEC = 1;
|
|
326
|
+
export declare const SEEK_SET = 0;
|
|
327
|
+
export declare const SEEK_CUR = 1;
|
|
328
|
+
export declare const SEEK_END = 2;
|
|
329
|
+
export declare const FILETYPE_UNKNOWN = 0;
|
|
330
|
+
export declare const FILETYPE_CHARACTER_DEVICE = 2;
|
|
331
|
+
export declare const FILETYPE_DIRECTORY = 3;
|
|
332
|
+
export declare const FILETYPE_REGULAR_FILE = 4;
|
|
333
|
+
export declare const FILETYPE_SYMBOLIC_LINK = 7;
|
|
334
|
+
export declare const FILETYPE_PIPE = 6;
|
|
335
|
+
export interface ProcessEntry {
|
|
336
|
+
pid: number;
|
|
337
|
+
ppid: number;
|
|
338
|
+
/** Process group ID. Defaults to parent's pgid, or pid for session leaders. */
|
|
339
|
+
pgid: number;
|
|
340
|
+
/** Session ID. Defaults to parent's sid, or pid for session leaders. */
|
|
341
|
+
sid: number;
|
|
342
|
+
driver: string;
|
|
343
|
+
command: string;
|
|
344
|
+
args: string[];
|
|
345
|
+
status: "running" | "stopped" | "exited";
|
|
346
|
+
exitCode: number | null;
|
|
347
|
+
/** How the process terminated: 'normal' for exit(), 'signal' for kill(). */
|
|
348
|
+
exitReason: "normal" | "signal" | null;
|
|
349
|
+
/** Signal that killed the process (0 = normal exit). */
|
|
350
|
+
termSignal: number;
|
|
351
|
+
exitTime: number | null;
|
|
352
|
+
env: Record<string, string>;
|
|
353
|
+
cwd: string;
|
|
354
|
+
/** File mode creation mask (POSIX umask). Inherited from parent, default 0o022. */
|
|
355
|
+
umask: number;
|
|
356
|
+
/** Active handles tracked for this process (id → description). */
|
|
357
|
+
activeHandles: Map<string, string>;
|
|
358
|
+
/** Maximum number of active handles allowed for this process. 0 = unlimited. */
|
|
359
|
+
handleLimit: number;
|
|
360
|
+
/** Signal handling state: registered handlers, blocked signals, pending signals. */
|
|
361
|
+
signalState: ProcessSignalState;
|
|
362
|
+
driverProcess: DriverProcess;
|
|
363
|
+
}
|
|
364
|
+
export interface ProcessInfo {
|
|
365
|
+
pid: number;
|
|
366
|
+
ppid: number;
|
|
367
|
+
pgid: number;
|
|
368
|
+
sid: number;
|
|
369
|
+
driver: string;
|
|
370
|
+
command: string;
|
|
371
|
+
status: "running" | "stopped" | "exited";
|
|
372
|
+
exitCode: number | null;
|
|
373
|
+
}
|
|
374
|
+
/** POSIX error codes used by the kernel. */
|
|
375
|
+
export type KernelErrorCode = "EACCES" | "EADDRINUSE" | "EAGAIN" | "EBADF" | "ECONNREFUSED" | "EINPROGRESS" | "EINTR" | "EEXIST" | "EINVAL" | "EIO" | "EISDIR" | "EMFILE" | "EMSGSIZE" | "ENOENT" | "ENOSPC" | "ENOSYS" | "ENOTCONN" | "ENOTEMPTY" | "ENOTDIR" | "EPERM" | "EPIPE" | "ESPIPE" | "ESRCH" | "ETIMEDOUT";
|
|
376
|
+
/**
|
|
377
|
+
* Structured error for kernel operations.
|
|
378
|
+
* Carries a machine-readable `code` so callers can map to errno without
|
|
379
|
+
* string matching.
|
|
380
|
+
*/
|
|
381
|
+
export declare class KernelError extends Error {
|
|
382
|
+
readonly code: KernelErrorCode;
|
|
383
|
+
constructor(code: KernelErrorCode, message: string);
|
|
384
|
+
}
|
|
385
|
+
/** Terminal attributes — controls line discipline behavior on a PTY. */
|
|
386
|
+
export interface Termios {
|
|
387
|
+
/** Map CR (0x0d) to NL (0x0a) on input (POSIX ICRNL). */
|
|
388
|
+
icrnl: boolean;
|
|
389
|
+
/** Post-process output (master for ONLCR, etc.). */
|
|
390
|
+
opost: boolean;
|
|
391
|
+
/** Map NL to CR-NL on output (requires opost). */
|
|
392
|
+
onlcr: boolean;
|
|
393
|
+
/** Canonical mode: buffer input until newline, handle backspace. */
|
|
394
|
+
icanon: boolean;
|
|
395
|
+
/** Echo input bytes back through output (master reads them). */
|
|
396
|
+
echo: boolean;
|
|
397
|
+
/** Enable signal generation from control characters (^C, ^Z, ^\). */
|
|
398
|
+
isig: boolean;
|
|
399
|
+
/** Control characters. */
|
|
400
|
+
cc: TermiosCC;
|
|
401
|
+
}
|
|
402
|
+
export interface TermiosCC {
|
|
403
|
+
vintr: number;
|
|
404
|
+
vquit: number;
|
|
405
|
+
vsusp: number;
|
|
406
|
+
veof: number;
|
|
407
|
+
verase: number;
|
|
408
|
+
}
|
|
409
|
+
/** Returns the POSIX-standard default termios: canonical on, echo on, isig on, opost+onlcr on. */
|
|
410
|
+
export declare function defaultTermios(): Termios;
|
|
411
|
+
export declare const SIGHUP = 1;
|
|
412
|
+
export declare const SIGINT = 2;
|
|
413
|
+
export declare const SIGQUIT = 3;
|
|
414
|
+
export declare const SIGKILL = 9;
|
|
415
|
+
export declare const SIGPIPE = 13;
|
|
416
|
+
export declare const SIGALRM = 14;
|
|
417
|
+
export declare const SIGTERM = 15;
|
|
418
|
+
export declare const SIGCHLD = 17;
|
|
419
|
+
export declare const SIGCONT = 18;
|
|
420
|
+
export declare const SIGSTOP = 19;
|
|
421
|
+
export declare const SIGTSTP = 20;
|
|
422
|
+
export declare const SIGWINCH = 28;
|
|
423
|
+
export declare const SA_RESTART = 268435456;
|
|
424
|
+
export declare const SA_RESETHAND = 2147483648;
|
|
425
|
+
export declare const SA_NOCLDSTOP = 1;
|
|
426
|
+
export declare const SIG_BLOCK = 0;
|
|
427
|
+
export declare const SIG_UNBLOCK = 1;
|
|
428
|
+
export declare const SIG_SETMASK = 2;
|
|
429
|
+
export declare const WNOHANG = 1;
|
|
430
|
+
/** Signal disposition: default kernel action, ignore, or user-defined handler. */
|
|
431
|
+
export type SignalDisposition = "default" | "ignore" | ((signal: number) => void);
|
|
432
|
+
/** Per-signal handler registration (matches POSIX struct sigaction). */
|
|
433
|
+
export interface SignalHandler {
|
|
434
|
+
handler: SignalDisposition;
|
|
435
|
+
/** Signals to block during handler execution (sa_mask). */
|
|
436
|
+
mask: Set<number>;
|
|
437
|
+
/** Flags (SA_RESTART, SA_RESETHAND, SA_NOCLDSTOP, etc.). */
|
|
438
|
+
flags: number;
|
|
439
|
+
}
|
|
440
|
+
/** Per-process signal state. */
|
|
441
|
+
export interface ProcessSignalState {
|
|
442
|
+
/** Signal number → registered handler. */
|
|
443
|
+
handlers: Map<number, SignalHandler>;
|
|
444
|
+
/** Currently blocked signals (sigprocmask). */
|
|
445
|
+
blockedSignals: Set<number>;
|
|
446
|
+
/** Signals queued while blocked. Standard signals (1-31) coalesce to max 1. */
|
|
447
|
+
pendingSignals: Set<number>;
|
|
448
|
+
/** Waiters blocked on signal-aware syscalls for this process. */
|
|
449
|
+
signalWaiters: WaitQueue;
|
|
450
|
+
/** Monotonic counter for delivered signals. */
|
|
451
|
+
deliverySeq: number;
|
|
452
|
+
/** Most recently delivered signal number, or null if none. */
|
|
453
|
+
lastDeliveredSignal: number | null;
|
|
454
|
+
/** Flags from the most recently delivered handler registration. */
|
|
455
|
+
lastDeliveredFlags: number;
|
|
456
|
+
}
|
|
457
|
+
export interface Pipe {
|
|
458
|
+
id: number;
|
|
459
|
+
readFd: number;
|
|
460
|
+
writeFd: number;
|
|
461
|
+
readerPid: number;
|
|
462
|
+
writerPid: number;
|
|
463
|
+
buffer: Uint8Array[];
|
|
464
|
+
closed: {
|
|
465
|
+
read: boolean;
|
|
466
|
+
write: boolean;
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
export interface PermissionDecision {
|
|
470
|
+
allow: boolean;
|
|
471
|
+
reason?: string;
|
|
472
|
+
}
|
|
473
|
+
export type PermissionCheck<T> = (request: T) => PermissionDecision;
|
|
474
|
+
export interface FsAccessRequest {
|
|
475
|
+
op: "read" | "write" | "mkdir" | "createDir" | "readdir" | "stat" | "rm" | "rename" | "exists" | "symlink" | "readlink" | "link" | "chmod" | "chown" | "utimes" | "truncate";
|
|
476
|
+
path: string;
|
|
477
|
+
}
|
|
478
|
+
export interface NetworkAccessRequest {
|
|
479
|
+
op: "fetch" | "http" | "dns" | "listen" | "connect";
|
|
480
|
+
url?: string;
|
|
481
|
+
method?: string;
|
|
482
|
+
hostname?: string;
|
|
483
|
+
}
|
|
484
|
+
export interface ChildProcessAccessRequest {
|
|
485
|
+
command: string;
|
|
486
|
+
args: string[];
|
|
487
|
+
cwd?: string;
|
|
488
|
+
env?: Record<string, string>;
|
|
489
|
+
}
|
|
490
|
+
export interface EnvAccessRequest {
|
|
491
|
+
op: "read" | "write";
|
|
492
|
+
key: string;
|
|
493
|
+
value?: string;
|
|
494
|
+
}
|
|
495
|
+
export interface Permissions {
|
|
496
|
+
fs?: PermissionCheck<FsAccessRequest>;
|
|
497
|
+
network?: PermissionCheck<NetworkAccessRequest>;
|
|
498
|
+
childProcess?: PermissionCheck<ChildProcessAccessRequest>;
|
|
499
|
+
env?: PermissionCheck<EnvAccessRequest>;
|
|
500
|
+
}
|