@secure-exec/core 0.1.1-rc.3 → 0.2.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +2868 -494
- 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 +78 -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 +122 -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 +1393 -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 +312 -0
- package/dist/kernel/socket-table.js +1188 -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 +349 -22
- package/dist/shared/bridge-contract.js +62 -5
- package/dist/shared/console-formatter.js +8 -4
- package/dist/shared/global-exposure.js +364 -19
- package/dist/shared/in-memory-fs.d.ts +33 -11
- package/dist/shared/in-memory-fs.js +439 -130
- 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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// isolate-runtime/src/common/global-exposure.ts
|
|
3
|
+
// ../core/isolate-runtime/src/common/global-exposure.ts
|
|
4
4
|
function defineRuntimeGlobalBinding(name, value, mutable) {
|
|
5
5
|
Object.defineProperty(globalThis, name, {
|
|
6
6
|
value,
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
return createRuntimeGlobalExposer(true);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// isolate-runtime/src/inject/set-commonjs-file-globals.ts
|
|
24
|
+
// ../core/isolate-runtime/src/inject/set-commonjs-file-globals.ts
|
|
25
25
|
var __runtimeExposeMutableGlobal = getRuntimeExposeMutableGlobal();
|
|
26
26
|
var __commonJsFileConfig = globalThis.__runtimeCommonJsFileConfig ?? {};
|
|
27
27
|
var __filePath = typeof __commonJsFileConfig.filePath === "string" ? __commonJsFileConfig.filePath : "/<entry>.js";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// isolate-runtime/src/inject/set-stdin-data.ts
|
|
3
|
+
// ../core/isolate-runtime/src/inject/set-stdin-data.ts
|
|
4
4
|
if (typeof globalThis._stdinData !== "undefined") {
|
|
5
5
|
globalThis._stdinData = globalThis.__runtimeStdinData;
|
|
6
6
|
globalThis._stdinPosition = 0;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// isolate-runtime/src/common/global-access.ts
|
|
3
|
+
// ../core/isolate-runtime/src/common/global-access.ts
|
|
4
4
|
function isObjectLike(value) {
|
|
5
5
|
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
// isolate-runtime/src/common/global-exposure.ts
|
|
8
|
+
// ../core/isolate-runtime/src/common/global-exposure.ts
|
|
9
9
|
function defineRuntimeGlobalBinding(name, value, mutable) {
|
|
10
10
|
Object.defineProperty(globalThis, name, {
|
|
11
11
|
value,
|
|
@@ -26,30 +26,58 @@
|
|
|
26
26
|
return createRuntimeGlobalExposer(false);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
// isolate-runtime/src/inject/setup-dynamic-import.ts
|
|
29
|
+
// ../core/isolate-runtime/src/inject/setup-dynamic-import.ts
|
|
30
30
|
var __runtimeExposeCustomGlobal = getRuntimeExposeCustomGlobal();
|
|
31
31
|
var __dynamicImportConfig = globalThis.__runtimeDynamicImportConfig ?? {};
|
|
32
32
|
var __fallbackReferrer = typeof __dynamicImportConfig.referrerPath === "string" && __dynamicImportConfig.referrerPath.length > 0 ? __dynamicImportConfig.referrerPath : "/";
|
|
33
|
-
var
|
|
33
|
+
var __dynamicImportCache = /* @__PURE__ */ new Map();
|
|
34
|
+
var __pathToFileURL = typeof globalThis.require === "function" ? globalThis.require("node:url").pathToFileURL ?? null : null;
|
|
35
|
+
var __resolveDynamicImportPath = function(request, referrer) {
|
|
36
|
+
if (!request.startsWith("./") && !request.startsWith("../") && !request.startsWith("/")) {
|
|
37
|
+
return request;
|
|
38
|
+
}
|
|
39
|
+
const baseDir = referrer.endsWith("/") ? referrer : referrer.slice(0, referrer.lastIndexOf("/")) || "/";
|
|
40
|
+
const segments = baseDir.split("/").filter(Boolean);
|
|
41
|
+
for (const part of request.split("/")) {
|
|
42
|
+
if (part === "." || part.length === 0) continue;
|
|
43
|
+
if (part === "..") {
|
|
44
|
+
segments.pop();
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
segments.push(part);
|
|
48
|
+
}
|
|
49
|
+
return `/${segments.join("/")}`;
|
|
50
|
+
};
|
|
51
|
+
var __dynamicImportHandler = function(specifier, fromPath) {
|
|
34
52
|
const request = String(specifier);
|
|
35
53
|
const referrer = typeof fromPath === "string" && fromPath.length > 0 ? fromPath : __fallbackReferrer;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (namespace !== null) {
|
|
43
|
-
return namespace;
|
|
54
|
+
let resolved = null;
|
|
55
|
+
if (typeof globalThis._resolveModuleSync !== "undefined") {
|
|
56
|
+
resolved = globalThis._resolveModuleSync.applySync(
|
|
57
|
+
void 0,
|
|
58
|
+
[request, referrer, "import"]
|
|
59
|
+
);
|
|
44
60
|
}
|
|
45
|
-
|
|
46
|
-
|
|
61
|
+
const resolvedPath = typeof resolved === "string" && resolved.length > 0 ? resolved : __resolveDynamicImportPath(request, referrer);
|
|
62
|
+
const cacheKey = typeof resolved === "string" && resolved.length > 0 ? resolved : `${referrer}\0${request}`;
|
|
63
|
+
const cached = __dynamicImportCache.get(cacheKey);
|
|
64
|
+
if (cached) return Promise.resolve(cached);
|
|
65
|
+
if (typeof globalThis._requireFrom !== "function") {
|
|
66
|
+
throw new Error("Cannot load module: " + resolvedPath);
|
|
47
67
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
68
|
+
let mod;
|
|
69
|
+
try {
|
|
70
|
+
mod = globalThis._requireFrom(resolved ?? request, referrer);
|
|
71
|
+
} catch (error) {
|
|
72
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
73
|
+
if (error && typeof error === "object" && "code" in error && error.code === "MODULE_NOT_FOUND") {
|
|
74
|
+
throw new Error("Cannot load module: " + resolvedPath);
|
|
75
|
+
}
|
|
76
|
+
if (message.startsWith("Cannot find module ")) {
|
|
77
|
+
throw new Error("Cannot load module: " + resolvedPath);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
51
80
|
}
|
|
52
|
-
const mod = runtimeRequire(request);
|
|
53
81
|
const namespaceFallback = { default: mod };
|
|
54
82
|
if (isObjectLike(mod)) {
|
|
55
83
|
for (const key of Object.keys(mod)) {
|
|
@@ -58,7 +86,38 @@
|
|
|
58
86
|
}
|
|
59
87
|
}
|
|
60
88
|
}
|
|
61
|
-
|
|
89
|
+
__dynamicImportCache.set(cacheKey, namespaceFallback);
|
|
90
|
+
return Promise.resolve(namespaceFallback);
|
|
91
|
+
};
|
|
92
|
+
var __importMetaResolveHandler = function(specifier, fromPath) {
|
|
93
|
+
const request = String(specifier);
|
|
94
|
+
const referrer = typeof fromPath === "string" && fromPath.length > 0 ? fromPath : __fallbackReferrer;
|
|
95
|
+
let resolved = null;
|
|
96
|
+
if (typeof globalThis._resolveModuleSync !== "undefined") {
|
|
97
|
+
resolved = globalThis._resolveModuleSync.applySync(
|
|
98
|
+
void 0,
|
|
99
|
+
[request, referrer, "import"]
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
if (resolved === null || resolved === void 0) {
|
|
103
|
+
resolved = globalThis._resolveModule.applySyncPromise(
|
|
104
|
+
void 0,
|
|
105
|
+
[request, referrer, "import"]
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
if (resolved === null) {
|
|
109
|
+
const err = new Error("Cannot find module '" + request + "'");
|
|
110
|
+
err.code = "MODULE_NOT_FOUND";
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
if (resolved.startsWith("node:")) {
|
|
114
|
+
return resolved;
|
|
115
|
+
}
|
|
116
|
+
if (__pathToFileURL && resolved.startsWith("/")) {
|
|
117
|
+
return __pathToFileURL(resolved).href;
|
|
118
|
+
}
|
|
119
|
+
return resolved;
|
|
62
120
|
};
|
|
63
121
|
__runtimeExposeCustomGlobal("__dynamicImport", __dynamicImportHandler);
|
|
122
|
+
__runtimeExposeCustomGlobal("__importMetaResolve", __importMetaResolveHandler);
|
|
64
123
|
})();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(() => {
|
|
3
|
-
// isolate-runtime/src/common/global-exposure.ts
|
|
3
|
+
// ../core/isolate-runtime/src/common/global-exposure.ts
|
|
4
4
|
function defineRuntimeGlobalBinding(name, value, mutable) {
|
|
5
5
|
Object.defineProperty(globalThis, name, {
|
|
6
6
|
value,
|
|
@@ -21,28 +21,67 @@
|
|
|
21
21
|
return createRuntimeGlobalExposer(false);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
// isolate-runtime/src/inject/setup-fs-facade.ts
|
|
24
|
+
// ../core/isolate-runtime/src/inject/setup-fs-facade.ts
|
|
25
25
|
var __runtimeExposeCustomGlobal = getRuntimeExposeCustomGlobal();
|
|
26
|
-
var __fsFacade = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
26
|
+
var __fsFacade = {};
|
|
27
|
+
Object.defineProperties(__fsFacade, {
|
|
28
|
+
readFile: { get() {
|
|
29
|
+
return globalThis._fsReadFile;
|
|
30
|
+
}, enumerable: true },
|
|
31
|
+
writeFile: { get() {
|
|
32
|
+
return globalThis._fsWriteFile;
|
|
33
|
+
}, enumerable: true },
|
|
34
|
+
readFileBinary: { get() {
|
|
35
|
+
return globalThis._fsReadFileBinary;
|
|
36
|
+
}, enumerable: true },
|
|
37
|
+
writeFileBinary: { get() {
|
|
38
|
+
return globalThis._fsWriteFileBinary;
|
|
39
|
+
}, enumerable: true },
|
|
40
|
+
readDir: { get() {
|
|
41
|
+
return globalThis._fsReadDir;
|
|
42
|
+
}, enumerable: true },
|
|
43
|
+
mkdir: { get() {
|
|
44
|
+
return globalThis._fsMkdir;
|
|
45
|
+
}, enumerable: true },
|
|
46
|
+
rmdir: { get() {
|
|
47
|
+
return globalThis._fsRmdir;
|
|
48
|
+
}, enumerable: true },
|
|
49
|
+
exists: { get() {
|
|
50
|
+
return globalThis._fsExists;
|
|
51
|
+
}, enumerable: true },
|
|
52
|
+
stat: { get() {
|
|
53
|
+
return globalThis._fsStat;
|
|
54
|
+
}, enumerable: true },
|
|
55
|
+
unlink: { get() {
|
|
56
|
+
return globalThis._fsUnlink;
|
|
57
|
+
}, enumerable: true },
|
|
58
|
+
rename: { get() {
|
|
59
|
+
return globalThis._fsRename;
|
|
60
|
+
}, enumerable: true },
|
|
61
|
+
chmod: { get() {
|
|
62
|
+
return globalThis._fsChmod;
|
|
63
|
+
}, enumerable: true },
|
|
64
|
+
chown: { get() {
|
|
65
|
+
return globalThis._fsChown;
|
|
66
|
+
}, enumerable: true },
|
|
67
|
+
link: { get() {
|
|
68
|
+
return globalThis._fsLink;
|
|
69
|
+
}, enumerable: true },
|
|
70
|
+
symlink: { get() {
|
|
71
|
+
return globalThis._fsSymlink;
|
|
72
|
+
}, enumerable: true },
|
|
73
|
+
readlink: { get() {
|
|
74
|
+
return globalThis._fsReadlink;
|
|
75
|
+
}, enumerable: true },
|
|
76
|
+
lstat: { get() {
|
|
77
|
+
return globalThis._fsLstat;
|
|
78
|
+
}, enumerable: true },
|
|
79
|
+
truncate: { get() {
|
|
80
|
+
return globalThis._fsTruncate;
|
|
81
|
+
}, enumerable: true },
|
|
82
|
+
utimes: { get() {
|
|
83
|
+
return globalThis._fsUtimes;
|
|
84
|
+
}, enumerable: true }
|
|
85
|
+
});
|
|
47
86
|
__runtimeExposeCustomGlobal("_fs", __fsFacade);
|
|
48
87
|
})();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command registry.
|
|
3
|
+
*
|
|
4
|
+
* Maps command names to runtime drivers. When a process calls
|
|
5
|
+
* spawn("grep", ...), the registry resolves "grep" to the WasmVM driver.
|
|
6
|
+
* Also populates /bin in the VFS so shell PATH lookup succeeds.
|
|
7
|
+
*/
|
|
8
|
+
import type { RuntimeDriver } from "./types.js";
|
|
9
|
+
import type { VirtualFileSystem } from "./vfs.js";
|
|
10
|
+
export declare class CommandRegistry {
|
|
11
|
+
/** command name → RuntimeDriver */
|
|
12
|
+
private commands;
|
|
13
|
+
/** Warning log for command overrides. */
|
|
14
|
+
private warnings;
|
|
15
|
+
/**
|
|
16
|
+
* Register all commands from a driver.
|
|
17
|
+
* Last-mounted driver wins on conflicts (allows override with warning).
|
|
18
|
+
*/
|
|
19
|
+
register(driver: RuntimeDriver): void;
|
|
20
|
+
/** Get recorded warnings (for testing). */
|
|
21
|
+
getWarnings(): readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Register a single command to a driver.
|
|
24
|
+
* Used for on-demand dynamic registration (e.g. after tryResolve).
|
|
25
|
+
*/
|
|
26
|
+
registerCommand(command: string, driver: RuntimeDriver): void;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve a command name to a driver. Returns null if unknown.
|
|
29
|
+
* Supports path-based lookup: '/bin/ls' resolves to the driver for 'ls'.
|
|
30
|
+
*/
|
|
31
|
+
resolve(command: string): RuntimeDriver | null;
|
|
32
|
+
/** List all registered commands. Returns command → driver name. */
|
|
33
|
+
list(): Map<string, string>;
|
|
34
|
+
/**
|
|
35
|
+
* Create a single /bin stub entry for a command.
|
|
36
|
+
* Used for on-demand registration after tryResolve discovers a new command.
|
|
37
|
+
*/
|
|
38
|
+
populateBinEntry(vfs: VirtualFileSystem, command: string): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Populate /bin in the VFS with stub entries for all registered commands.
|
|
41
|
+
* This enables brush-shell's PATH lookup to find commands.
|
|
42
|
+
*/
|
|
43
|
+
populateBin(vfs: VirtualFileSystem): Promise<void>;
|
|
44
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command registry.
|
|
3
|
+
*
|
|
4
|
+
* Maps command names to runtime drivers. When a process calls
|
|
5
|
+
* spawn("grep", ...), the registry resolves "grep" to the WasmVM driver.
|
|
6
|
+
* Also populates /bin in the VFS so shell PATH lookup succeeds.
|
|
7
|
+
*/
|
|
8
|
+
export class CommandRegistry {
|
|
9
|
+
/** command name → RuntimeDriver */
|
|
10
|
+
commands = new Map();
|
|
11
|
+
/** Warning log for command overrides. */
|
|
12
|
+
warnings = [];
|
|
13
|
+
/**
|
|
14
|
+
* Register all commands from a driver.
|
|
15
|
+
* Last-mounted driver wins on conflicts (allows override with warning).
|
|
16
|
+
*/
|
|
17
|
+
register(driver) {
|
|
18
|
+
for (const cmd of driver.commands) {
|
|
19
|
+
const existing = this.commands.get(cmd);
|
|
20
|
+
if (existing) {
|
|
21
|
+
const msg = `command "${cmd}" overridden: ${existing.name} → ${driver.name}`;
|
|
22
|
+
this.warnings.push(msg);
|
|
23
|
+
console.warn(`[CommandRegistry] ${msg}`);
|
|
24
|
+
}
|
|
25
|
+
this.commands.set(cmd, driver);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Get recorded warnings (for testing). */
|
|
29
|
+
getWarnings() {
|
|
30
|
+
return this.warnings;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Register a single command to a driver.
|
|
34
|
+
* Used for on-demand dynamic registration (e.g. after tryResolve).
|
|
35
|
+
*/
|
|
36
|
+
registerCommand(command, driver) {
|
|
37
|
+
const existing = this.commands.get(command);
|
|
38
|
+
if (existing) {
|
|
39
|
+
const msg = `command "${command}" overridden: ${existing.name} → ${driver.name}`;
|
|
40
|
+
this.warnings.push(msg);
|
|
41
|
+
console.warn(`[CommandRegistry] ${msg}`);
|
|
42
|
+
}
|
|
43
|
+
this.commands.set(command, driver);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a command name to a driver. Returns null if unknown.
|
|
47
|
+
* Supports path-based lookup: '/bin/ls' resolves to the driver for 'ls'.
|
|
48
|
+
*/
|
|
49
|
+
resolve(command) {
|
|
50
|
+
// Direct name lookup
|
|
51
|
+
const direct = this.commands.get(command);
|
|
52
|
+
if (direct)
|
|
53
|
+
return direct;
|
|
54
|
+
// Path-based: extract basename and retry
|
|
55
|
+
if (command.includes("/")) {
|
|
56
|
+
const basename = command.split("/").pop();
|
|
57
|
+
if (basename)
|
|
58
|
+
return this.commands.get(basename) ?? null;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
/** List all registered commands. Returns command → driver name. */
|
|
63
|
+
list() {
|
|
64
|
+
const result = new Map();
|
|
65
|
+
for (const [cmd, driver] of this.commands) {
|
|
66
|
+
result.set(cmd, driver.name);
|
|
67
|
+
}
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a single /bin stub entry for a command.
|
|
72
|
+
* Used for on-demand registration after tryResolve discovers a new command.
|
|
73
|
+
*/
|
|
74
|
+
async populateBinEntry(vfs, command) {
|
|
75
|
+
if (!(await vfs.exists("/bin"))) {
|
|
76
|
+
await vfs.mkdir("/bin", { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
const path = `/bin/${command}`;
|
|
79
|
+
if (!(await vfs.exists(path))) {
|
|
80
|
+
const stub = new TextEncoder().encode("#!/bin/sh\n# kernel command stub\n");
|
|
81
|
+
await vfs.writeFile(path, stub);
|
|
82
|
+
try {
|
|
83
|
+
await vfs.chmod(path, 0o755);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// chmod may not be supported by all VFS backends
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Populate /bin in the VFS with stub entries for all registered commands.
|
|
92
|
+
* This enables brush-shell's PATH lookup to find commands.
|
|
93
|
+
*/
|
|
94
|
+
async populateBin(vfs) {
|
|
95
|
+
// Ensure /bin exists
|
|
96
|
+
if (!(await vfs.exists("/bin"))) {
|
|
97
|
+
await vfs.mkdir("/bin", { recursive: true });
|
|
98
|
+
}
|
|
99
|
+
// Create a stub file for each command
|
|
100
|
+
const stub = new TextEncoder().encode("#!/bin/sh\n# kernel command stub\n");
|
|
101
|
+
for (const cmd of this.commands.keys()) {
|
|
102
|
+
const path = `/bin/${cmd}`;
|
|
103
|
+
if (!(await vfs.exists(path))) {
|
|
104
|
+
await vfs.writeFile(path, stub);
|
|
105
|
+
try {
|
|
106
|
+
await vfs.chmod(path, 0o755);
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// chmod may not be supported by all VFS backends
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device layer.
|
|
3
|
+
*
|
|
4
|
+
* Intercepts device node paths (/dev/*) before they reach the VFS backend.
|
|
5
|
+
* Wraps a VirtualFileSystem and handles device-specific read/write semantics.
|
|
6
|
+
*/
|
|
7
|
+
import type { VirtualFileSystem } from "./vfs.js";
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a VFS with device node interception.
|
|
10
|
+
* Device paths are handled directly; all other paths pass through.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createDeviceLayer(vfs: VirtualFileSystem): VirtualFileSystem;
|