@nimbus-sh/worker 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -0
- package/dist/_shared/crypto.d.ts +15 -0
- package/dist/_shared/crypto.d.ts.map +1 -0
- package/dist/_shared/crypto.js +122 -0
- package/dist/facets/manager.d.ts +24 -0
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +279 -70
- package/dist/facets/process.d.ts +11 -2
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +187 -21
- package/dist/facets/vite-dev-server.d.ts +2 -0
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +39 -12
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/loaders/child-process/spawn-facet.d.ts +1 -3
- package/dist/loaders/child-process/spawn-facet.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-facet.js +1 -12
- package/dist/loaders/child-process/spawn-pool.d.ts +1 -1
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +1 -6
- package/dist/loaders/loader-pool.d.ts +7 -0
- package/dist/loaders/loader-pool.d.ts.map +1 -1
- package/dist/loaders/loader-pool.js +3 -1
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +47 -27
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +2 -1
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +41 -13
- package/dist/npm/resolver.d.ts.map +1 -1
- package/dist/npm/resolver.js +70 -35
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +45 -21
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +29 -8
- package/dist/runtime/barrel-synthesizer.d.ts +7 -0
- package/dist/runtime/barrel-synthesizer.d.ts.map +1 -1
- package/dist/runtime/barrel-synthesizer.js +11 -0
- package/dist/runtime/clang-runner.d.ts +2 -2
- package/dist/runtime/clang-runner.d.ts.map +1 -1
- package/dist/runtime/clang-runner.js +64 -47
- package/dist/runtime/esbuild-service.d.ts +12 -5
- package/dist/runtime/esbuild-service.d.ts.map +1 -1
- package/dist/runtime/esbuild-service.js +28 -14
- package/dist/runtime/node-runner.d.ts +2 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-shims.d.ts +4 -2
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +404 -55
- package/dist/runtime/os-contracts.d.ts +128 -0
- package/dist/runtime/os-contracts.d.ts.map +1 -0
- package/dist/runtime/os-contracts.js +1 -0
- package/dist/runtime/package-manager.d.ts +31 -0
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +162 -15
- package/dist/runtime/port-registry.d.ts +5 -0
- package/dist/runtime/port-registry.d.ts.map +1 -1
- package/dist/runtime/port-registry.js +22 -1
- package/dist/runtime/pyodide-runtime-assets.d.ts +19 -0
- package/dist/runtime/pyodide-runtime-assets.d.ts.map +1 -0
- package/dist/runtime/pyodide-runtime-assets.js +44 -0
- package/dist/runtime/python-repl.d.ts +3 -0
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +18 -32
- package/dist/runtime/python-runner.d.ts +8 -9
- package/dist/runtime/python-runner.d.ts.map +1 -1
- package/dist/runtime/python-runner.js +958 -118
- package/dist/runtime/python-socket-shim.d.ts +11 -0
- package/dist/runtime/python-socket-shim.d.ts.map +1 -0
- package/dist/runtime/python-socket-shim.js +526 -0
- package/dist/runtime/require-resolver.d.ts.map +1 -1
- package/dist/runtime/require-resolver.js +2 -11
- package/dist/runtime/ruby-gems.d.ts +25 -0
- package/dist/runtime/ruby-gems.d.ts.map +1 -0
- package/dist/runtime/ruby-gems.js +582 -0
- package/dist/runtime/ruby-runner.d.ts +2 -4
- package/dist/runtime/ruby-runner.d.ts.map +1 -1
- package/dist/runtime/ruby-runner.js +335 -34
- package/dist/runtime/runtime-catalog.d.ts +16 -4
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +21 -10
- package/dist/runtime/runtime-registry.d.ts +3 -0
- package/dist/runtime/runtime-registry.d.ts.map +1 -1
- package/dist/runtime/runtime-registry.js +4 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts +43 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.d.ts.map +1 -0
- package/dist/runtime/sqlite-runtime-fs-bridge.js +224 -0
- package/dist/runtime/static-server.d.ts +3 -0
- package/dist/runtime/static-server.d.ts.map +1 -0
- package/dist/runtime/static-server.js +121 -0
- package/dist/runtime/vfs-snapshot.d.ts +57 -0
- package/dist/runtime/vfs-snapshot.d.ts.map +1 -0
- package/dist/runtime/vfs-snapshot.js +141 -0
- package/dist/runtime/virtual-socket-kernel.d.ts +16 -0
- package/dist/runtime/virtual-socket-kernel.d.ts.map +1 -0
- package/dist/runtime/virtual-socket-kernel.js +360 -0
- package/dist/runtime/wasi-instance.d.ts +6 -3
- package/dist/runtime/wasi-instance.d.ts.map +1 -1
- package/dist/runtime/wasm-runner.d.ts +1 -25
- package/dist/runtime/wasm-runner.d.ts.map +1 -1
- package/dist/runtime/wasm-runner.js +1 -160
- package/dist/session/agent.d.ts +43 -0
- package/dist/session/agent.d.ts.map +1 -0
- package/dist/session/agent.js +1252 -0
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +64 -19
- package/dist/session/nimbus-session.d.ts +14 -2
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +104 -30
- package/dist/session/programmatic.d.ts +10 -0
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +121 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +19 -1
- package/dist/session/rpc.d.ts +11 -0
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +56 -43
- package/dist/session/supervisor-rpc.d.ts +15 -0
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +32 -0
- package/dist/session/ws.d.ts +4 -3
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +11 -1
- package/dist/shell/unix-commands.d.ts.map +1 -1
- package/dist/shell/unix-commands.js +52 -51
- package/dist/vfs/path.d.ts +8 -4
- package/dist/vfs/path.d.ts.map +1 -1
- package/dist/vfs/path.js +16 -4
- package/dist/vfs/sqlite-vfs.d.ts +2 -0
- package/dist/vfs/sqlite-vfs.d.ts.map +1 -1
- package/dist/vfs/sqlite-vfs.js +11 -0
- package/dist/vfs/symlink-registry.d.ts +7 -1
- package/dist/vfs/symlink-registry.d.ts.map +1 -1
- package/dist/vfs/symlink-registry.js +16 -11
- package/package.json +4 -1
- package/public/index.html +240 -0
- package/public/s/index.html +1316 -128
- package/runtime-contracts/pyodide-workerd-adapter.json +18 -0
- package/scripts/bundle-runtime.mjs +99 -24
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* catalog/v1.json ← top-level catalog
|
|
12
12
|
* manifests/<name>-<version>.json ← per-version manifest
|
|
13
|
-
* blobs/<name>-<version>/<file>
|
|
13
|
+
* blobs/<name>-<version>/<sha256>/<file> ← content-addressed blobs
|
|
14
14
|
*
|
|
15
15
|
* Catalog schema (RuntimeCatalog):
|
|
16
16
|
* { version: 1, runtimes: { <name>: { default, versions: { <ver>: { manifest, size_bytes, license } } } } }
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* Manifest schema (RuntimeManifest):
|
|
19
19
|
* { name, version, license, wasi_namespace, memfs_companion,
|
|
20
20
|
* files: [{ path, content, sha256, size, mode? }],
|
|
21
|
-
* entrypoints: [{ binName, runner, args[], kind? }]
|
|
21
|
+
* entrypoints: [{ binName, runner, args[], kind? }],
|
|
22
|
+
* runtime_artifacts?: [{ path, kind, id, source_sha256?, sha256 }] }
|
|
22
23
|
*
|
|
23
24
|
* R2 and Cache API failures throw; the shell verb formats the diagnostic for
|
|
24
25
|
* the user.
|
|
@@ -70,6 +71,13 @@ export interface ManifestEntrypoint {
|
|
|
70
71
|
/** Optional secondary classification (e.g. "linker" for wasm-ld). */
|
|
71
72
|
kind?: string;
|
|
72
73
|
}
|
|
74
|
+
export interface RuntimeArtifactMetadata {
|
|
75
|
+
path: string;
|
|
76
|
+
kind: string;
|
|
77
|
+
id: string;
|
|
78
|
+
source_sha256?: string;
|
|
79
|
+
sha256: string;
|
|
80
|
+
}
|
|
73
81
|
export interface RuntimeManifest {
|
|
74
82
|
name: string;
|
|
75
83
|
version: string;
|
|
@@ -82,6 +90,7 @@ export interface RuntimeManifest {
|
|
|
82
90
|
memfs_companion: string | null;
|
|
83
91
|
files: ManifestFile[];
|
|
84
92
|
entrypoints: ManifestEntrypoint[];
|
|
93
|
+
runtime_artifacts?: RuntimeArtifactMetadata[];
|
|
85
94
|
}
|
|
86
95
|
/** Fetch the top-level catalog. Throws if neither L2 nor R2 has it. */
|
|
87
96
|
export declare function fetchCatalog(env: RuntimeCatalogEnv): Promise<RuntimeCatalog>;
|
|
@@ -89,8 +98,11 @@ export declare function fetchCatalog(env: RuntimeCatalogEnv): Promise<RuntimeCat
|
|
|
89
98
|
export declare function fetchManifest(env: RuntimeCatalogEnv, manifestKey: string): Promise<RuntimeManifest>;
|
|
90
99
|
/**
|
|
91
100
|
* Fetch a content-addressed blob by R2 key. Bytes are eternally
|
|
92
|
-
* cacheable
|
|
93
|
-
*
|
|
101
|
+
* cacheable when the manifest key includes the content digest. Older
|
|
102
|
+
* manifests used version-only keys, so L2 can contain stale bytes after
|
|
103
|
+
* a corrected runtime sync. A cached sha mismatch is therefore treated
|
|
104
|
+
* as a stale cache entry and refetched from R2; an R2 mismatch remains
|
|
105
|
+
* a hard integrity failure.
|
|
94
106
|
*/
|
|
95
107
|
export declare function fetchBlob(env: RuntimeCatalogEnv, blobKey: string, expectedSha256?: string): Promise<Uint8Array>;
|
|
96
108
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"runtime-catalog.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEH,2CAA2C;AAC3C,KAAK,YAAY,GAAG;IAClB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QACxB,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;QACpC,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;KACzB,GAAG,IAAI,CAAC,CAAC;CACX,GAAG,IAAI,GAAG,SAAS,CAAC;AAErB,8CAA8C;AAC9C,MAAM,WAAW,iBAAiB;IAChC,oBAAoB,CAAC,EAAE,YAAY,CAAC;CACrC;AAID,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,IAAI,EAAE,MAAM,CAAC;IACb,mCAAmC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB;4CACwC;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,yDAAyD;IACzD,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,qEAAqE;IACrE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;oEACgE;IAChE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;iEAC6D;IAC7D,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,iBAAiB,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC/C;AAaD,uEAAuE;AACvE,wBAAsB,YAAY,CAAC,GAAG,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,CAoBlF;AAED,kDAAkD;AAClD,wBAAsB,aAAa,CACjC,GAAG,EAAE,iBAAiB,EACtB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CAoB1B;AAED;;;;;;;GAOG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,GACtB,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* catalog/v1.json ← top-level catalog
|
|
12
12
|
* manifests/<name>-<version>.json ← per-version manifest
|
|
13
|
-
* blobs/<name>-<version>/<file>
|
|
13
|
+
* blobs/<name>-<version>/<sha256>/<file> ← content-addressed blobs
|
|
14
14
|
*
|
|
15
15
|
* Catalog schema (RuntimeCatalog):
|
|
16
16
|
* { version: 1, runtimes: { <name>: { default, versions: { <ver>: { manifest, size_bytes, license } } } } }
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
* Manifest schema (RuntimeManifest):
|
|
19
19
|
* { name, version, license, wasi_namespace, memfs_companion,
|
|
20
20
|
* files: [{ path, content, sha256, size, mode? }],
|
|
21
|
-
* entrypoints: [{ binName, runner, args[], kind? }]
|
|
21
|
+
* entrypoints: [{ binName, runner, args[], kind? }],
|
|
22
|
+
* runtime_artifacts?: [{ path, kind, id, source_sha256?, sha256 }] }
|
|
22
23
|
*
|
|
23
24
|
* R2 and Cache API failures throw; the shell verb formats the diagnostic for
|
|
24
25
|
* the user.
|
|
@@ -77,16 +78,19 @@ export async function fetchManifest(env, manifestKey) {
|
|
|
77
78
|
}
|
|
78
79
|
/**
|
|
79
80
|
* Fetch a content-addressed blob by R2 key. Bytes are eternally
|
|
80
|
-
* cacheable
|
|
81
|
-
*
|
|
81
|
+
* cacheable when the manifest key includes the content digest. Older
|
|
82
|
+
* manifests used version-only keys, so L2 can contain stale bytes after
|
|
83
|
+
* a corrected runtime sync. A cached sha mismatch is therefore treated
|
|
84
|
+
* as a stale cache entry and refetched from R2; an R2 mismatch remains
|
|
85
|
+
* a hard integrity failure.
|
|
82
86
|
*/
|
|
83
87
|
export async function fetchBlob(env, blobKey, expectedSha256) {
|
|
84
88
|
// L2 hot path.
|
|
85
89
|
const cached = await l2GetBytes(blobL2Key(blobKey));
|
|
86
90
|
if (cached) {
|
|
87
|
-
if (expectedSha256)
|
|
88
|
-
|
|
89
|
-
|
|
91
|
+
if (!expectedSha256 || await bytesMatchSha256(cached, expectedSha256)) {
|
|
92
|
+
return cached;
|
|
93
|
+
}
|
|
90
94
|
}
|
|
91
95
|
// R2 path.
|
|
92
96
|
const r2 = env.NIMBUS_RUNTIME_CACHE;
|
|
@@ -101,18 +105,25 @@ export async function fetchBlob(env, blobKey, expectedSha256) {
|
|
|
101
105
|
const bytes = new Uint8Array(ab);
|
|
102
106
|
if (expectedSha256)
|
|
103
107
|
await assertSha256(bytes, expectedSha256, blobKey);
|
|
104
|
-
// Eternal-immutable write-back
|
|
108
|
+
// Eternal-immutable write-back. Integrity has already been verified
|
|
109
|
+
// against the manifest before writing to L2.
|
|
105
110
|
await l2PutBytes(blobL2Key(blobKey), bytes);
|
|
106
111
|
return bytes;
|
|
107
112
|
}
|
|
108
113
|
// ── sha256 verifier ──────────────────────────────────────────────────
|
|
109
114
|
async function assertSha256(bytes, expected, label) {
|
|
110
|
-
const
|
|
111
|
-
const hex = bytesToHex(new Uint8Array(digest));
|
|
115
|
+
const hex = await sha256Hex(bytes);
|
|
112
116
|
if (hex !== expected.toLowerCase()) {
|
|
113
117
|
throw new Error(`sha256 mismatch for ${label}: expected ${expected} got ${hex}`);
|
|
114
118
|
}
|
|
115
119
|
}
|
|
120
|
+
async function bytesMatchSha256(bytes, expected) {
|
|
121
|
+
return await sha256Hex(bytes) === expected.toLowerCase();
|
|
122
|
+
}
|
|
123
|
+
async function sha256Hex(bytes) {
|
|
124
|
+
const digest = await crypto.subtle.digest('SHA-256', bytes);
|
|
125
|
+
return bytesToHex(new Uint8Array(digest));
|
|
126
|
+
}
|
|
116
127
|
function bytesToHex(b) {
|
|
117
128
|
let s = '';
|
|
118
129
|
for (let i = 0; i < b.length; i++)
|
|
@@ -63,6 +63,9 @@ export interface RuntimeRunOpts {
|
|
|
63
63
|
* runtimes ignore them safely. */
|
|
64
64
|
skipSpawn?: boolean;
|
|
65
65
|
callerPid?: number;
|
|
66
|
+
/** Capture stdout/stderr in the result instead of streaming to the
|
|
67
|
+
* terminal supervisor. Used by child_process pipe semantics. */
|
|
68
|
+
captureOutput?: boolean;
|
|
66
69
|
}
|
|
67
70
|
export interface RuntimeSpec {
|
|
68
71
|
/** Shell-command name: 'node' / 'bun' / 'wasm-runner' / 'python'. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACxC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;uCACmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"runtime-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-registry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,CAAC;IACxC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB;uCACmC;IACnC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;qEACiE;IACjE,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb;oEACgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,GAAG,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC3F;;;;;OAKG;IACH,aAAa,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAAC;IACzD;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,QAAQ,EAAE,aAAa,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;IACrF;;;;;;;OAOG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CAC3C;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE;IACJ,GAAG,EAAE,SAAS,CAAC;IACf,QAAQ,EAAE,YAAY,CAAC;IACvB;yDACqD;IACrD,UAAU,IAAI,cAAc,CAAC;IAC7B,QAAQ,EAAE,aAAa,CAAC;CACzB,GACA,CAAC,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CA6R/B"}
|
|
@@ -49,6 +49,7 @@ export function buildRuntimeHandler(spec, ctx0) {
|
|
|
49
49
|
return async function runtimeHandler(ctx) {
|
|
50
50
|
const args = ctx.args || [];
|
|
51
51
|
const name = spec.name;
|
|
52
|
+
const captureOutput = !!ctx.__nimbusCaptureOutput;
|
|
52
53
|
// ── Subcommand dispatch ──
|
|
53
54
|
//
|
|
54
55
|
// BEFORE flag-span computation: subcommands like `bun install`
|
|
@@ -103,6 +104,7 @@ export function buildRuntimeHandler(spec, ctx0) {
|
|
|
103
104
|
filename: '<eval>',
|
|
104
105
|
dirname: ctx.cwd || '/home/user',
|
|
105
106
|
command: `${name} -e ...`,
|
|
107
|
+
...(captureOutput ? { captureOutput: true } : {}),
|
|
106
108
|
});
|
|
107
109
|
if (result.stdout)
|
|
108
110
|
ctx.stdout.write(result.stdout);
|
|
@@ -160,6 +162,7 @@ export function buildRuntimeHandler(spec, ctx0) {
|
|
|
160
162
|
filename,
|
|
161
163
|
dirname,
|
|
162
164
|
command: `${name} ${args.slice(0, scriptIdx + 1).join(' ')}`,
|
|
165
|
+
...(captureOutput ? { captureOutput: true } : {}),
|
|
163
166
|
});
|
|
164
167
|
if (result.stdout)
|
|
165
168
|
ctx.stdout.write(result.stdout);
|
|
@@ -308,6 +311,7 @@ export function buildRuntimeHandler(spec, ctx0) {
|
|
|
308
311
|
dirname,
|
|
309
312
|
command: binSpawn?.command || `${name} ${args.slice(0, scriptIdx + 1).join(' ')}`,
|
|
310
313
|
...(binSpawn ? { skipSpawn: true, callerPid: binSpawn.callerPid } : {}),
|
|
314
|
+
...(captureOutput ? { captureOutput: true } : {}),
|
|
311
315
|
});
|
|
312
316
|
if (result.stdout)
|
|
313
317
|
ctx.stdout.write(result.stdout);
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SqliteVFS } from '../vfs/sqlite-vfs.js';
|
|
2
|
+
import type { RuntimeFileHandle, RuntimeFsBridge, RuntimeOpenFlags, RuntimeVfsDirEntry, RuntimeVfsStat } from './os-contracts.js';
|
|
3
|
+
export declare class SqliteRuntimeFsBridge implements RuntimeFsBridge {
|
|
4
|
+
private readonly vfs;
|
|
5
|
+
private nextHandleId;
|
|
6
|
+
private handles;
|
|
7
|
+
private symlinks;
|
|
8
|
+
constructor(vfs: SqliteVFS);
|
|
9
|
+
stat(path: string, options?: {
|
|
10
|
+
followSymlinks?: boolean;
|
|
11
|
+
}): Promise<RuntimeVfsStat | null>;
|
|
12
|
+
readFile(path: string, options?: {
|
|
13
|
+
followSymlinks?: boolean;
|
|
14
|
+
}): Promise<Uint8Array | null>;
|
|
15
|
+
writeFile(path: string, bytes: string | Uint8Array, options?: {
|
|
16
|
+
createParents?: boolean;
|
|
17
|
+
expectedRevision?: number;
|
|
18
|
+
}): Promise<void>;
|
|
19
|
+
open(path: string, flags: RuntimeOpenFlags): Promise<RuntimeFileHandle>;
|
|
20
|
+
read(handleId: number, offset: number | null, length: number): Promise<Uint8Array>;
|
|
21
|
+
write(handleId: number, offset: number | null, bytes: Uint8Array): Promise<number>;
|
|
22
|
+
close(handleId: number): Promise<void>;
|
|
23
|
+
readdir(path: string, options?: {
|
|
24
|
+
followSymlinks?: boolean;
|
|
25
|
+
}): Promise<RuntimeVfsDirEntry[]>;
|
|
26
|
+
mkdir(path: string, options?: {
|
|
27
|
+
recursive?: boolean;
|
|
28
|
+
mode?: number;
|
|
29
|
+
}): Promise<void>;
|
|
30
|
+
unlink(path: string): Promise<void>;
|
|
31
|
+
rmdir(path: string): Promise<void>;
|
|
32
|
+
rename(from: string, to: string): Promise<void>;
|
|
33
|
+
readlink(path: string): Promise<string | null>;
|
|
34
|
+
symlink(target: string, path: string): Promise<void>;
|
|
35
|
+
fsync(): Promise<void>;
|
|
36
|
+
revision(): Promise<number>;
|
|
37
|
+
subscribe(path: string, listener: Parameters<NonNullable<RuntimeFsBridge['subscribe']>>[1]): () => void;
|
|
38
|
+
private resolveDataPath;
|
|
39
|
+
private ensureParent;
|
|
40
|
+
private assertExpectedRevision;
|
|
41
|
+
private getHandle;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=sqlite-runtime-fs-bridge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sqlite-runtime-fs-bridge.d.ts","sourceRoot":"","sources":["../../src/runtime/sqlite-runtime-fs-bridge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EACV,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,cAAc,EACf,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,qBAAsB,YAAW,eAAe;IAK/C,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJhC,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,OAAO,CAAwC;IACvD,OAAO,CAAC,QAAQ,CAAkB;gBAEL,GAAG,EAAE,SAAS;IAIrC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;IA6B9F,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAU9F,SAAS,CACb,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,GAAG,UAAU,EAC1B,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;KAAO,GACnE,OAAO,CAAC,IAAI,CAAC;IAOV,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA4BvE,IAAI,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAWlF,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBlF,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtC,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAehG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxF,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnC,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa/C,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAI9C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAIjC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,WAAW,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIvG,OAAO,CAAC,eAAe;IAMvB,OAAO,CAAC,YAAY;IAKpB,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,SAAS;CAKlB"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { normalizeVfsPath, parentVfsPath } from '../vfs/path.js';
|
|
2
|
+
import { getSymlinkRegistry } from '../vfs/symlink-registry.js';
|
|
3
|
+
export class SqliteRuntimeFsBridge {
|
|
4
|
+
vfs;
|
|
5
|
+
nextHandleId = 1;
|
|
6
|
+
handles = new Map();
|
|
7
|
+
symlinks;
|
|
8
|
+
constructor(vfs) {
|
|
9
|
+
this.vfs = vfs;
|
|
10
|
+
this.symlinks = getSymlinkRegistry(vfs);
|
|
11
|
+
}
|
|
12
|
+
async stat(path, options = {}) {
|
|
13
|
+
const p = this.resolveDataPath(path, options.followSymlinks !== false);
|
|
14
|
+
if (!p)
|
|
15
|
+
return null;
|
|
16
|
+
if (this.symlinks.isSymlink(p) && options.followSymlinks === false) {
|
|
17
|
+
const target = this.symlinks.readlink(p) || '';
|
|
18
|
+
return {
|
|
19
|
+
type: 'symlink',
|
|
20
|
+
size: new TextEncoder().encode(target).byteLength,
|
|
21
|
+
ctime: Date.now(),
|
|
22
|
+
mtime: Date.now(),
|
|
23
|
+
mode: 0o777,
|
|
24
|
+
revision: this.vfs.revision(),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const st = this.vfs.stat(p);
|
|
29
|
+
return {
|
|
30
|
+
type: st.type === 'directory' ? 'directory' : 'file',
|
|
31
|
+
size: st.size,
|
|
32
|
+
ctime: st.ctime,
|
|
33
|
+
mtime: st.mtime,
|
|
34
|
+
mode: st.mode,
|
|
35
|
+
revision: this.vfs.revision(),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async readFile(path, options = {}) {
|
|
43
|
+
const p = this.resolveDataPath(path, options.followSymlinks !== false);
|
|
44
|
+
if (!p)
|
|
45
|
+
return null;
|
|
46
|
+
try {
|
|
47
|
+
return this.vfs.readFile(p);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async writeFile(path, bytes, options = {}) {
|
|
54
|
+
this.assertExpectedRevision(options.expectedRevision);
|
|
55
|
+
const p = this.resolveDataPath(path, true) || normalizeVfsPath(path);
|
|
56
|
+
if (options.createParents !== false)
|
|
57
|
+
this.ensureParent(p);
|
|
58
|
+
this.vfs.writeFile(p, bytes);
|
|
59
|
+
}
|
|
60
|
+
async open(path, flags) {
|
|
61
|
+
const normalizedFlags = normalizeOpenFlags(flags);
|
|
62
|
+
const p = this.resolveDataPath(path, normalizedFlags.followSymlinks) || normalizeVfsPath(path);
|
|
63
|
+
this.assertExpectedRevision(normalizedFlags.expectedRevision);
|
|
64
|
+
const exists = this.vfs.exists(p);
|
|
65
|
+
if (!exists && !normalizedFlags.create)
|
|
66
|
+
throw fsError('ENOENT', 'open', path);
|
|
67
|
+
if (exists && this.vfs.isDirectory(p))
|
|
68
|
+
throw fsError('EISDIR', 'open', path);
|
|
69
|
+
if (!exists) {
|
|
70
|
+
this.ensureParent(p);
|
|
71
|
+
this.vfs.writeFile(p, new Uint8Array(0));
|
|
72
|
+
}
|
|
73
|
+
else if (normalizedFlags.truncate) {
|
|
74
|
+
this.vfs.writeFile(p, new Uint8Array(0));
|
|
75
|
+
}
|
|
76
|
+
const stat = this.vfs.stat(p);
|
|
77
|
+
const handle = {
|
|
78
|
+
id: this.nextHandleId++,
|
|
79
|
+
path: p,
|
|
80
|
+
flags: normalizedFlags,
|
|
81
|
+
position: normalizedFlags.append ? stat.size : 0,
|
|
82
|
+
baseRevision: this.vfs.revision(),
|
|
83
|
+
closed: false,
|
|
84
|
+
};
|
|
85
|
+
this.handles.set(handle.id, handle);
|
|
86
|
+
return { ...handle };
|
|
87
|
+
}
|
|
88
|
+
async read(handleId, offset, length) {
|
|
89
|
+
const handle = this.getHandle(handleId);
|
|
90
|
+
if (!handle.flags.read)
|
|
91
|
+
throw fsError('EBADF', 'read', handle.path);
|
|
92
|
+
const data = this.vfs.readFile(handle.path);
|
|
93
|
+
const start = offset == null ? handle.position : Math.max(0, offset);
|
|
94
|
+
const end = Math.min(data.byteLength, start + Math.max(0, length));
|
|
95
|
+
const out = data.slice(start, end);
|
|
96
|
+
if (offset == null)
|
|
97
|
+
handle.position = end;
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
async write(handleId, offset, bytes) {
|
|
101
|
+
const handle = this.getHandle(handleId);
|
|
102
|
+
if (!handle.flags.write)
|
|
103
|
+
throw fsError('EBADF', 'write', handle.path);
|
|
104
|
+
if (handle.baseRevision < this.vfs.revision()) {
|
|
105
|
+
throw fsError('ESTALE', 'write', handle.path);
|
|
106
|
+
}
|
|
107
|
+
const prior = this.vfs.exists(handle.path) ? this.vfs.readFile(handle.path) : new Uint8Array(0);
|
|
108
|
+
const start = handle.flags.append ? prior.byteLength : offset == null ? handle.position : Math.max(0, offset);
|
|
109
|
+
const size = Math.max(prior.byteLength, start + bytes.byteLength);
|
|
110
|
+
const next = new Uint8Array(size);
|
|
111
|
+
next.set(prior.subarray(0, Math.min(prior.byteLength, size)), 0);
|
|
112
|
+
next.set(bytes, start);
|
|
113
|
+
this.vfs.writeFile(handle.path, next);
|
|
114
|
+
const end = start + bytes.byteLength;
|
|
115
|
+
if (offset == null || handle.flags.append)
|
|
116
|
+
handle.position = end;
|
|
117
|
+
handle.baseRevision = this.vfs.revision();
|
|
118
|
+
return bytes.byteLength;
|
|
119
|
+
}
|
|
120
|
+
async close(handleId) {
|
|
121
|
+
const handle = this.getHandle(handleId);
|
|
122
|
+
handle.closed = true;
|
|
123
|
+
this.handles.delete(handleId);
|
|
124
|
+
}
|
|
125
|
+
async readdir(path, options = {}) {
|
|
126
|
+
const p = this.resolveDataPath(path, options.followSymlinks !== false);
|
|
127
|
+
if (!p)
|
|
128
|
+
return [];
|
|
129
|
+
const entries = this.vfs.readdir(p).map((entry) => ({
|
|
130
|
+
name: entry.name,
|
|
131
|
+
type: entry.type === 'directory' ? 'directory' : 'file',
|
|
132
|
+
}));
|
|
133
|
+
const prefix = p ? `${p}/` : '';
|
|
134
|
+
for (const link of this.symlinks.list()) {
|
|
135
|
+
if (parentVfsPath(link.link) !== p)
|
|
136
|
+
continue;
|
|
137
|
+
entries.push({ name: link.link.slice(prefix.length), type: 'symlink' });
|
|
138
|
+
}
|
|
139
|
+
return entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
140
|
+
}
|
|
141
|
+
async mkdir(path, options = {}) {
|
|
142
|
+
this.vfs.mkdir(normalizeVfsPath(path), { recursive: !!options.recursive });
|
|
143
|
+
}
|
|
144
|
+
async unlink(path) {
|
|
145
|
+
const p = normalizeVfsPath(path);
|
|
146
|
+
if (this.symlinks.delete(p))
|
|
147
|
+
return;
|
|
148
|
+
this.vfs.unlink(p);
|
|
149
|
+
}
|
|
150
|
+
async rmdir(path) {
|
|
151
|
+
this.vfs.rmdir(normalizeVfsPath(path));
|
|
152
|
+
}
|
|
153
|
+
async rename(from, to) {
|
|
154
|
+
const oldPath = normalizeVfsPath(from);
|
|
155
|
+
const newPath = normalizeVfsPath(to);
|
|
156
|
+
const linkTarget = this.symlinks.readlink(oldPath);
|
|
157
|
+
if (linkTarget !== null) {
|
|
158
|
+
this.symlinks.delete(oldPath);
|
|
159
|
+
this.symlinks.set(newPath, linkTarget);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
this.ensureParent(newPath);
|
|
163
|
+
this.vfs.rename(oldPath, newPath);
|
|
164
|
+
}
|
|
165
|
+
async readlink(path) {
|
|
166
|
+
return this.symlinks.readlink(normalizeVfsPath(path));
|
|
167
|
+
}
|
|
168
|
+
async symlink(target, path) {
|
|
169
|
+
const p = normalizeVfsPath(path);
|
|
170
|
+
this.ensureParent(p);
|
|
171
|
+
this.symlinks.set(p, target);
|
|
172
|
+
}
|
|
173
|
+
async fsync() {
|
|
174
|
+
this.vfs.flushAll();
|
|
175
|
+
}
|
|
176
|
+
async revision() {
|
|
177
|
+
return this.vfs.revision();
|
|
178
|
+
}
|
|
179
|
+
subscribe(path, listener) {
|
|
180
|
+
return this.vfs.events.onPath(normalizeVfsPath(path), listener);
|
|
181
|
+
}
|
|
182
|
+
resolveDataPath(path, followSymlinks) {
|
|
183
|
+
const p = normalizeVfsPath(path);
|
|
184
|
+
if (!followSymlinks)
|
|
185
|
+
return p;
|
|
186
|
+
return this.symlinks.resolveChain(p);
|
|
187
|
+
}
|
|
188
|
+
ensureParent(path) {
|
|
189
|
+
const parent = parentVfsPath(path);
|
|
190
|
+
if (parent && !this.vfs.exists(parent))
|
|
191
|
+
this.vfs.mkdir(parent, { recursive: true });
|
|
192
|
+
}
|
|
193
|
+
assertExpectedRevision(expectedRevision) {
|
|
194
|
+
if (expectedRevision === undefined)
|
|
195
|
+
return;
|
|
196
|
+
if (expectedRevision !== this.vfs.revision()) {
|
|
197
|
+
throw fsError('ESTALE', 'write', `revision ${expectedRevision}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
getHandle(handleId) {
|
|
201
|
+
const handle = this.handles.get(handleId);
|
|
202
|
+
if (!handle || handle.closed)
|
|
203
|
+
throw fsError('EBADF', 'fd', String(handleId));
|
|
204
|
+
return handle;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
function normalizeOpenFlags(flags) {
|
|
208
|
+
return {
|
|
209
|
+
read: !!flags.read || !flags.write,
|
|
210
|
+
write: !!flags.write,
|
|
211
|
+
append: !!flags.append,
|
|
212
|
+
create: !!flags.create,
|
|
213
|
+
truncate: !!flags.truncate,
|
|
214
|
+
followSymlinks: flags.followSymlinks !== false,
|
|
215
|
+
expectedRevision: flags.expectedRevision,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function fsError(code, syscall, path) {
|
|
219
|
+
const err = new Error(`${code}: ${syscall} '${path}'`);
|
|
220
|
+
err.code = code;
|
|
221
|
+
err.syscall = syscall;
|
|
222
|
+
err.path = path;
|
|
223
|
+
return err;
|
|
224
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-server.d.ts","sourceRoot":"","sources":["../../src/runtime/static-server.ts"],"names":[],"mappings":"AAIA,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAK7E;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA6G/D"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
function jsString(value) {
|
|
2
|
+
return JSON.stringify(value);
|
|
3
|
+
}
|
|
4
|
+
export function parsePort(value, fallback) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return fallback;
|
|
7
|
+
const n = Number(value);
|
|
8
|
+
if (!Number.isInteger(n) || n <= 0 || n >= 65536)
|
|
9
|
+
return fallback;
|
|
10
|
+
return n;
|
|
11
|
+
}
|
|
12
|
+
export function createLiveStaticServerCode(root) {
|
|
13
|
+
const normalizedRoot = root.replace(/^\/+/, '').replace(/\/+$/, '') || 'home/user';
|
|
14
|
+
return `
|
|
15
|
+
import { WorkerEntrypoint } from "cloudflare:workers";
|
|
16
|
+
|
|
17
|
+
const ROOT = ${jsString(normalizedRoot)};
|
|
18
|
+
const TEXT_TYPES = {
|
|
19
|
+
".html": "text/html; charset=utf-8",
|
|
20
|
+
".htm": "text/html; charset=utf-8",
|
|
21
|
+
".css": "text/css; charset=utf-8",
|
|
22
|
+
".js": "text/javascript; charset=utf-8",
|
|
23
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
24
|
+
".json": "application/json; charset=utf-8",
|
|
25
|
+
".txt": "text/plain; charset=utf-8",
|
|
26
|
+
".md": "text/markdown; charset=utf-8",
|
|
27
|
+
".svg": "image/svg+xml; charset=utf-8",
|
|
28
|
+
".wasm": "application/wasm",
|
|
29
|
+
".png": "image/png",
|
|
30
|
+
".jpg": "image/jpeg",
|
|
31
|
+
".jpeg": "image/jpeg",
|
|
32
|
+
".gif": "image/gif",
|
|
33
|
+
".webp": "image/webp",
|
|
34
|
+
".ico": "image/x-icon",
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function cleanPath(pathname) {
|
|
38
|
+
const parts = [];
|
|
39
|
+
for (const raw of pathname.split("/")) {
|
|
40
|
+
if (!raw || raw === ".") continue;
|
|
41
|
+
if (raw === "..") { parts.pop(); continue; }
|
|
42
|
+
parts.push(raw);
|
|
43
|
+
}
|
|
44
|
+
return parts.join("/");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function joinRoot(rel) {
|
|
48
|
+
return rel ? ROOT + "/" + rel : ROOT;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function contentType(path) {
|
|
52
|
+
const idx = path.lastIndexOf(".");
|
|
53
|
+
const ext = idx >= 0 ? path.slice(idx).toLowerCase() : "";
|
|
54
|
+
return TEXT_TYPES[ext] || "application/octet-stream";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function htmlEscape(s) {
|
|
58
|
+
return String(s).replace(/[&<>"]/g, (ch) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[ch]));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function resolveFile(supervisor, rel) {
|
|
62
|
+
const base = joinRoot(rel);
|
|
63
|
+
const stat = await supervisor.stat(base);
|
|
64
|
+
const isDirectory = stat && (stat.type === "directory" || stat.isDir || stat.isDirectory);
|
|
65
|
+
if (stat && !isDirectory) return { path: base, stat };
|
|
66
|
+
if (isDirectory) {
|
|
67
|
+
for (const index of ["index.html", "index.htm"]) {
|
|
68
|
+
const candidate = base.replace(/\\/+$/, "") + "/" + index;
|
|
69
|
+
const s = await supervisor.stat(candidate);
|
|
70
|
+
if (s && !(s.type === "directory" || s.isDir || s.isDirectory)) return { path: candidate, stat: s };
|
|
71
|
+
}
|
|
72
|
+
return { directory: base };
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function renderDirectory(supervisor, path, rel, url) {
|
|
78
|
+
const entries = await supervisor.readdir(path);
|
|
79
|
+
const rows = entries
|
|
80
|
+
.sort((a, b) => (a.type === b.type ? a.name.localeCompare(b.name) : a.type === "directory" ? -1 : 1))
|
|
81
|
+
.map((entry) => {
|
|
82
|
+
const suffix = entry.type === "directory" ? "/" : "";
|
|
83
|
+
const href = new URL(url);
|
|
84
|
+
href.pathname = href.pathname.replace(/\\/?$/, "/") + encodeURIComponent(entry.name) + suffix;
|
|
85
|
+
return '<li><a href="' + href.pathname + href.search + '">' + htmlEscape(entry.name + suffix) + '</a></li>';
|
|
86
|
+
})
|
|
87
|
+
.join("");
|
|
88
|
+
return new Response(
|
|
89
|
+
'<!doctype html><meta charset="utf-8"><title>Index of /' + htmlEscape(rel) + '</title>' +
|
|
90
|
+
'<h1>Index of /' + htmlEscape(rel) + '</h1><ul>' + rows + '</ul>',
|
|
91
|
+
{ headers: { "Content-Type": "text/html; charset=utf-8" } }
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export default class NimbusStaticServer extends WorkerEntrypoint {
|
|
96
|
+
async fetch(request) {
|
|
97
|
+
return this.handleHttpRequest(request);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async handleHttpRequest(request) {
|
|
101
|
+
const supervisor = this.env && this.env.SUPERVISOR;
|
|
102
|
+
if (!supervisor) {
|
|
103
|
+
return new Response("Nimbus static server: SUPERVISOR binding missing", { status: 500 });
|
|
104
|
+
}
|
|
105
|
+
const url = new URL(request.url);
|
|
106
|
+
const rel = cleanPath(decodeURIComponent(url.pathname));
|
|
107
|
+
const resolved = await resolveFile(supervisor, rel);
|
|
108
|
+
if (!resolved) return new Response("Not found", { status: 404 });
|
|
109
|
+
if (resolved.directory) return renderDirectory(supervisor, resolved.directory, rel, url);
|
|
110
|
+
const bytes = await supervisor.readFileBytes(resolved.path);
|
|
111
|
+
if (!bytes) return new Response("Not found", { status: 404 });
|
|
112
|
+
return new Response(bytes, {
|
|
113
|
+
headers: {
|
|
114
|
+
"Content-Type": contentType(resolved.path),
|
|
115
|
+
"Cache-Control": "no-store",
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import type { WasiFsDiff, WasiFsSnapshot } from './wasi-instance.js';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal VFS shape runtime bridges need. Kept separate from SqliteVFS's
|
|
4
|
+
* concrete type so runtime helpers do not pull supervisor modules into facet
|
|
5
|
+
* preambles or create import cycles.
|
|
6
|
+
*/
|
|
7
|
+
export interface VfsLike {
|
|
8
|
+
exists(path: string): boolean;
|
|
9
|
+
isDirectory(path: string): boolean;
|
|
10
|
+
readFile(path: string): Uint8Array;
|
|
11
|
+
writeFile(path: string, content: Uint8Array | string): void;
|
|
12
|
+
readdir(path: string): {
|
|
13
|
+
name: string;
|
|
14
|
+
type: string;
|
|
15
|
+
}[];
|
|
16
|
+
mkdir(path: string, opts?: {
|
|
17
|
+
recursive?: boolean;
|
|
18
|
+
}): void;
|
|
19
|
+
unlink(path: string): void;
|
|
20
|
+
rmdir(path: string): void;
|
|
21
|
+
}
|
|
22
|
+
export interface VfsSnapshotCaps {
|
|
23
|
+
maxBytes?: number;
|
|
24
|
+
maxFiles?: number;
|
|
25
|
+
skipSubdirs?: Iterable<string>;
|
|
26
|
+
extraRoots?: Iterable<string>;
|
|
27
|
+
}
|
|
28
|
+
export interface VfsSnapshotResult {
|
|
29
|
+
snapshot: WasiFsSnapshot;
|
|
30
|
+
bytes: number;
|
|
31
|
+
files: number;
|
|
32
|
+
}
|
|
33
|
+
export declare function bytesToB64(bytes: Uint8Array): string;
|
|
34
|
+
export declare function b64ToBytes(b64: string): Uint8Array;
|
|
35
|
+
/**
|
|
36
|
+
* Snapshot a VFS subtree into a JSON-serializable WASI-shaped filesystem.
|
|
37
|
+
*
|
|
38
|
+
* The snapshot is intentionally bounded. Runtimes that need incremental or
|
|
39
|
+
* lazy file IO should add a streaming bridge; a single request payload is the
|
|
40
|
+
* right primitive only for normal source trees and small app state.
|
|
41
|
+
*/
|
|
42
|
+
export declare function snapshotVfs(vfs: VfsLike, vfsRoot: string, caps?: VfsSnapshotCaps): VfsSnapshotResult | {
|
|
43
|
+
error: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Apply a runtime-produced filesystem diff back into the supervisor VFS.
|
|
47
|
+
* Operations are independent so one bad path never loses the rest of a run.
|
|
48
|
+
*/
|
|
49
|
+
export declare function flushVfsDiff(vfs: VfsLike, diff: WasiFsDiff): {
|
|
50
|
+
written: number;
|
|
51
|
+
deleted: number;
|
|
52
|
+
mkdirs: number;
|
|
53
|
+
rmdirs: number;
|
|
54
|
+
timesTouched: number;
|
|
55
|
+
symlinks: number;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=vfs-snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vfs-snapshot.d.ts","sourceRoot":"","sources":["../../src/runtime/vfs-snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAErE;;;;GAIG;AACH,MAAM,WAAW,OAAO;IACtB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IACnC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5D,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxD,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;IAC1D,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/B,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,cAAc,CAAC;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAIpD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAKlD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,GAAE,eAAoB,GACzB,iBAAiB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAmEvC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,GAAG,EAAE,OAAO,EACZ,IAAI,EAAE,UAAU,GACf;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CA4C9G"}
|