@nimbus-sh/worker 0.5.0 → 0.7.0
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 +2 -2
- package/dist/_shared/preview-host.d.ts +26 -8
- package/dist/_shared/preview-host.d.ts.map +1 -1
- package/dist/_shared/preview-host.js +51 -11
- package/dist/cirrus-npm-cjs.generated.d.ts +1 -1
- package/dist/cirrus-npm-cjs.generated.js +1 -1
- package/dist/facets/cirrus-real.d.ts.map +1 -1
- package/dist/facets/cirrus-real.js +6 -5
- package/dist/facets/compose.d.ts +148 -0
- package/dist/facets/compose.d.ts.map +1 -0
- package/dist/facets/compose.js +146 -0
- package/dist/facets/durable-images.d.ts +18 -5
- package/dist/facets/durable-images.d.ts.map +1 -1
- package/dist/facets/durable-images.js +36 -7
- package/dist/facets/durable-slots.d.ts +1 -1
- package/dist/facets/durable-slots.d.ts.map +1 -1
- package/dist/facets/durable-slots.js +2 -1
- package/dist/facets/esbuild-bundle-pool.d.ts +49 -0
- package/dist/facets/esbuild-bundle-pool.d.ts.map +1 -0
- package/dist/facets/esbuild-bundle-pool.js +122 -0
- package/dist/facets/exec-telemetry.d.ts +8 -0
- package/dist/facets/exec-telemetry.d.ts.map +1 -1
- package/dist/facets/manager.d.ts +337 -123
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +1100 -449
- package/dist/facets/process.d.ts +20 -16
- package/dist/facets/process.d.ts.map +1 -1
- package/dist/facets/process.js +59 -28
- package/dist/facets/real-vite-fs-shim.js +2 -2
- package/dist/facets/resident-identity.d.ts +23 -0
- package/dist/facets/resident-identity.d.ts.map +1 -0
- package/dist/facets/resident-identity.js +31 -0
- package/dist/facets/vite-dev-server.d.ts +25 -47
- package/dist/facets/vite-dev-server.d.ts.map +1 -1
- package/dist/facets/vite-dev-server.js +185 -219
- package/dist/facets/wasm-image-digest.d.ts +25 -0
- package/dist/facets/wasm-image-digest.d.ts.map +1 -0
- package/dist/facets/wasm-image-digest.js +25 -0
- package/dist/facets/wasm-swap-registry.d.ts +51 -92
- package/dist/facets/wasm-swap-registry.d.ts.map +1 -1
- package/dist/facets/wasm-swap-registry.js +68 -227
- package/dist/git/commands.d.ts +0 -1
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +0 -3
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- package/dist/loaders/child-process/spawn-pool.d.ts +3 -2
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +3 -2
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -3
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -6
- package/dist/loaders/npm-resolve-preamble.d.ts.map +1 -1
- package/dist/loaders/npm-resolve-preamble.js +34 -108
- package/dist/node-shims-artifact.generated.js +3 -3
- package/dist/npm/install-batch-facet.d.ts +3 -2
- package/dist/npm/install-batch-facet.d.ts.map +1 -1
- package/dist/npm/install-batch-facet.js +45 -15
- package/dist/npm/installer.d.ts +22 -17
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +209 -193
- package/dist/npm/pre-bundle-facet.d.ts +1 -1
- package/dist/npm/pre-bundle-facet.d.ts.map +1 -1
- package/dist/npm/pre-bundle-facet.js +1 -1
- package/dist/npm/resolve-facet.d.ts +6 -0
- package/dist/npm/resolve-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.d.ts +21 -20
- package/dist/npm/resolve-one-facet.d.ts.map +1 -1
- package/dist/npm/resolve-one-facet.js +85 -84
- package/dist/npm/semver.d.ts +59 -0
- package/dist/npm/semver.d.ts.map +1 -0
- package/dist/npm/semver.js +226 -0
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +47 -10
- package/dist/router/public-directory.d.ts +5 -1
- package/dist/router/public-directory.d.ts.map +1 -1
- package/dist/router/public-directory.js +9 -2
- package/dist/router/remote-api.d.ts +18 -0
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +64 -5
- package/dist/runtime/cpython-resident.d.ts.map +1 -1
- package/dist/runtime/cpython-resident.js +3 -1
- package/dist/runtime/html-entrypoint.d.ts +7 -0
- package/dist/runtime/html-entrypoint.d.ts.map +1 -1
- package/dist/runtime/html-entrypoint.js +18 -2
- package/dist/runtime/node-runner.d.ts +3 -0
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +7 -0
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +305 -29
- package/dist/runtime/opencode-facet-runner.js +2 -2
- package/dist/runtime/package-manager.d.ts +44 -1
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +168 -3
- package/dist/runtime/ruby-resident.d.ts.map +1 -1
- package/dist/runtime/ruby-resident.js +2 -0
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/agent.js +28 -1
- package/dist/session/helpers.d.ts +38 -0
- package/dist/session/helpers.d.ts.map +1 -1
- package/dist/session/helpers.js +76 -0
- package/dist/session/init-phases.d.ts +1 -1
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +2 -12
- package/dist/session/init.d.ts +15 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +91 -603
- package/dist/session/keys.d.ts +4 -0
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +4 -0
- package/dist/session/nimbus-session.d.ts +90 -23
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +200 -110
- package/dist/session/npm-install-port.d.ts +16 -0
- package/dist/session/npm-install-port.d.ts.map +1 -0
- package/dist/session/npm-install-port.js +37 -0
- package/dist/session/port-capability.d.ts +90 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +219 -9
- package/dist/session/programmatic.d.ts +125 -5
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +439 -89
- package/dist/session/routes.d.ts +39 -17
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +67 -131
- package/dist/session/rpc.d.ts +39 -16
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +137 -130
- package/dist/session/serving-port.d.ts +27 -0
- package/dist/session/serving-port.d.ts.map +1 -0
- package/dist/session/serving-port.js +27 -0
- package/dist/session/start-real-vite.d.ts +12 -0
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +4 -4
- package/dist/session/supervisor-op.d.ts +41 -4
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +101 -73
- package/dist/session/supervisor-rpc.d.ts +7 -7
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +6 -3
- package/dist/session/vite-command.d.ts +18 -0
- package/dist/session/vite-command.d.ts.map +1 -0
- package/dist/session/vite-command.js +609 -0
- package/dist/session/ws.d.ts +61 -0
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +122 -12
- package/dist/shell/npm-bin-entrypoints.d.ts +0 -35
- package/dist/shell/npm-bin-entrypoints.d.ts.map +1 -1
- package/dist/shell/npm-bin-entrypoints.js +12 -85
- package/package.json +24 -4
- package/public/_assets/runtime/{node-shims-9e1380d9ee430fcc.js → node-shims-8f8c64edd2a1f0f5.js} +305 -29
- package/public/index.html +1 -1
- package/scripts/bundle-facet-workers.mjs +1 -1
- package/scripts/bundle-npm-cjs.mjs +2 -2
- package/dist/facets/on-demand-bundle-gate.d.ts +0 -65
- package/dist/facets/on-demand-bundle-gate.d.ts.map +0 -1
- package/dist/facets/on-demand-bundle-gate.js +0 -125
- package/dist/npm/install-args.d.ts +0 -21
- package/dist/npm/install-args.d.ts.map +0 -1
- package/dist/npm/install-args.js +0 -70
- package/dist/npm/npm-log.d.ts +0 -31
- package/dist/npm/npm-log.d.ts.map +0 -1
- package/dist/npm/npm-log.js +0 -51
package/dist/facets/manager.d.ts
CHANGED
|
@@ -19,11 +19,13 @@ import type { ProcessEntry } from '@nimbus-sh/core/runtime/process-table.js';
|
|
|
19
19
|
import { SessionProcessSupervisor } from '@nimbus-sh/core/runtime/session-process-supervisor.js';
|
|
20
20
|
import type { CredentialedVfs, SqliteVFS, VfsStat } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
21
21
|
import type { PortRegistry } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
22
|
+
import { type PortVisibility } from '../session/port-capability.js';
|
|
22
23
|
import { TurnBudget } from '@nimbus-sh/fabric/turn-budget.js';
|
|
23
24
|
import { EsbuildService, type TransformResult } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
24
25
|
import { type ProcessHostFactory, type ResidentCodeSpec } from '@nimbus-sh/fabric/process-fabric.js';
|
|
25
26
|
import { type OpencodeRunnerOptions } from '../runtime/opencode-facet-runner.js';
|
|
26
27
|
import { type FacetBundleProfile } from '@nimbus-sh/core/runtime/bundle-profile.js';
|
|
28
|
+
import { type WasmImageRecord } from './wasm-image-digest.js';
|
|
27
29
|
type EsbuildTransformOptions = NonNullable<Parameters<EsbuildService['transform']>[1]>;
|
|
28
30
|
type LargeEsmTransform = (code: string, options: EsbuildTransformOptions) => Promise<TransformResult>;
|
|
29
31
|
/** Result returned from a facet execution */
|
|
@@ -74,34 +76,6 @@ export interface StagedArtifactExecResult extends FacetExecResult {
|
|
|
74
76
|
/** For the resident server path: the loopback port the facet is bound to. */
|
|
75
77
|
port?: number;
|
|
76
78
|
}
|
|
77
|
-
/**
|
|
78
|
-
* Reserve held back from a one-shot facet's lifetime so a program that runs
|
|
79
|
-
* out of time is still alive to say so.
|
|
80
|
-
*
|
|
81
|
-
* A one-shot exec is ALREADY bounded: `_execWithTimeout` kills it at
|
|
82
|
-
* FACET_TIMEOUT_MS with exit 124 and "[process killed: timeout after 30s]".
|
|
83
|
-
* The entry drain must therefore not be a second, tighter, independent
|
|
84
|
-
* timeout. Measured against a deployed Worker, floating async work of 5s /
|
|
85
|
-
* 15s / 25s completes and 40s is killed by that outer bound at exactly 30s —
|
|
86
|
-
* so the fixed 8s budget this used to carry was abandoning programs 22
|
|
87
|
-
* seconds before anything actually required it.
|
|
88
|
-
*
|
|
89
|
-
* The drain therefore runs to the outer bound MINUS this reserve, which is
|
|
90
|
-
* what buys the facet time to flush and report the honest "still in flight"
|
|
91
|
-
* reason instead of the supervisor's generic kill. The reserve has to cover
|
|
92
|
-
* the longest tail a facet can have after the drain: settling pending RPC,
|
|
93
|
-
* writing back __vfsWrites (bounded by MAX_RPC_SAFE_PAYLOAD_BYTES; a 20 MiB
|
|
94
|
-
* write-back measures ~1.5s), draining children, then reportExit.
|
|
95
|
-
*/
|
|
96
|
-
export declare const ONE_SHOT_EXIT_RESERVE_MS = 3000;
|
|
97
|
-
/**
|
|
98
|
-
* Budget used when the supervisor did not stamp an absolute deadline on the
|
|
99
|
-
* payload. The deadline is the real bound — see `entryDeadlineAt` — because
|
|
100
|
-
* it is measured from the supervisor's own timer rather than restarted when
|
|
101
|
-
* the drain begins, so a slow module init cannot push the drain past the kill
|
|
102
|
-
* and lose the honest message.
|
|
103
|
-
*/
|
|
104
|
-
export declare const ONE_SHOT_ENTRY_DEADLINE_MS: number;
|
|
105
79
|
/**
|
|
106
80
|
* How long a RESIDENT facet settles its startup before answering its boot
|
|
107
81
|
* call. It keeps running afterwards, so this is not a lifetime decision: the
|
|
@@ -117,8 +91,11 @@ export declare const RESIDENT_BOOT_SETTLE_MS = 1000;
|
|
|
117
91
|
* servers, requests in flight. A promise is not a handle: a program whose
|
|
118
92
|
* last act leaves `new Promise(() => {})` unsettled prints its output and
|
|
119
93
|
* exits 0. Counting unsettled promises as work was a real divergence from
|
|
120
|
-
* that — such a program
|
|
121
|
-
* reported as having not finished.
|
|
94
|
+
* that — such a program's drain used to end early and it was
|
|
95
|
+
* reported as having not finished. A user-invoked program runs this loop
|
|
96
|
+
* with NO deadline — Node itself has no wall-clock kill; the only endings
|
|
97
|
+
* are the program's exit and a signal. Callers that still pass a finite
|
|
98
|
+
* deadline (the resident boot settle) arm the expiry timer.
|
|
122
99
|
*
|
|
123
100
|
* Three kinds of handle, each owned by the shim that creates them:
|
|
124
101
|
*
|
|
@@ -131,19 +108,12 @@ export declare const RESIDENT_BOOT_SETTLE_MS = 1000;
|
|
|
131
108
|
* - listening SERVERS (`__portRegistry`), open until the program closes
|
|
132
109
|
* them.
|
|
133
110
|
*
|
|
134
|
-
* The bound is a REAL wall-clock deadline, armed as a timer rather than
|
|
135
|
-
* compared against `Date.now()`: a `setTimeout(0)` turn in workerd costs
|
|
136
|
-
* ~5µs, so the pass budget this loop used to carry (50k) expired after
|
|
137
|
-
* ~150ms and silently overrode every longer deadline the callers declared —
|
|
138
|
-
* anything slower than that, including an ordinary network fetch, was
|
|
139
|
-
* abandoned mid-flight and reported as a clean exit.
|
|
140
|
-
*
|
|
141
111
|
* The loop subscribes to the exit promise ONCE — a per-pass
|
|
142
112
|
* `exitPromise.then()` allocates a promise every iteration — and yields
|
|
143
113
|
* through the raw setTimeout so its own ticks don't inflate the timer count
|
|
144
114
|
* it watches.
|
|
145
115
|
*/
|
|
146
|
-
export declare const ENTRYPOINT_EVENT_LOOP = "\nfunction __nimbusHandleCount(__name) {\n const __value = globalThis[__name];\n return typeof __value === \"number\" ? __value : 0;\n}\n\n// Work an entrypoint's STARTUP has to settle before it can be called booted.\nfunction __nimbusPendingStartupWork() {\n return __nimbusHandleCount(\"__nimbusPendingTimers\") + __nimbusHandleCount(\"__nimbusPendingOps\");\n}\n\n// The above, plus the handles a program holds open on purpose. A bound port\n// keeps a Node process alive, and it keeps a one-shot facet alive too.\nfunction __nimbusLiveHandles() {\n const __servers = globalThis.__portRegistry;\n const __bound = __servers && typeof __servers.size === \"number\" ? __servers.size : 0;\n return __nimbusPendingStartupWork() + __bound;\n}\n\nasync function __nimbusRunEventLoop(__countHandles, __exitPromise, __deadlineMs, __minPasses) {\n let __exited = false;\n if (__exitPromise && typeof __exitPromise.then === \"function\") {\n __exitPromise.then(() => { __exited = true; }, () => { __exited = true; });\n }\n const __rawSetTimeout = (typeof globalThis.__nimbusRawSetTimeout === \"function\")\n ? globalThis.__nimbusRawSetTimeout\n : globalThis.setTimeout;\n const __rawClearTimeout = (typeof globalThis.__nimbusRawClearTimeout === \"function\")\n ? globalThis.__nimbusRawClearTimeout\n : globalThis.clearTimeout;\n let __expired = false;\n const __deadline = __rawSetTimeout(() => { __expired = true; }, __deadlineMs)
|
|
116
|
+
export declare const ENTRYPOINT_EVENT_LOOP = "\nfunction __nimbusHandleCount(__name) {\n const __value = globalThis[__name];\n return typeof __value === \"number\" ? __value : 0;\n}\n\n// Work an entrypoint's STARTUP has to settle before it can be called booted.\nfunction __nimbusPendingStartupWork() {\n return __nimbusHandleCount(\"__nimbusPendingTimers\") + __nimbusHandleCount(\"__nimbusPendingOps\");\n}\n\n// The above, plus the handles a program holds open on purpose. A bound port\n// keeps a Node process alive, and it keeps a one-shot facet alive too.\nfunction __nimbusLiveHandles() {\n const __servers = globalThis.__portRegistry;\n const __bound = __servers && typeof __servers.size === \"number\" ? __servers.size : 0;\n return __nimbusPendingStartupWork() + __bound;\n}\n\nasync function __nimbusRunEventLoop(__countHandles, __exitPromise, __deadlineMs, __minPasses) {\n let __exited = false;\n if (__exitPromise && typeof __exitPromise.then === \"function\") {\n __exitPromise.then(() => { __exited = true; }, () => { __exited = true; });\n }\n const __rawSetTimeout = (typeof globalThis.__nimbusRawSetTimeout === \"function\")\n ? globalThis.__nimbusRawSetTimeout\n : globalThis.setTimeout;\n const __rawClearTimeout = (typeof globalThis.__nimbusRawClearTimeout === \"function\")\n ? globalThis.__nimbusRawClearTimeout\n : globalThis.clearTimeout;\n let __expired = false;\n let __pass = 0;\n // A user-invoked program runs until it exits or is killed \u2014 there is no\n // wall-clock deadline, so no expiry timer is armed at all. (Callers that\n // still pass a finite deadline get the timer for compatibility.)\n const __deadline = Number.isFinite(__deadlineMs)\n ? __rawSetTimeout(() => { __expired = true; }, __deadlineMs)\n : null;\n while (!__exited && !__expired && (__pass < __minPasses || __countHandles() > 0)) {\n // The warm-up passes give a settling microtask chain its turns and cost\n // ~5\u00B5s each; past them the loop is waiting on wall-clock work, where\n // spinning at 0ms would burn the isolate's CPU indefinitely.\n await new Promise((resolve) => __rawSetTimeout(resolve, __pass < __minPasses ? 0 : 1));\n __pass++;\n }\n if (__deadline !== null) { try { __rawClearTimeout(__deadline); } catch {} }\n // `pending` is what the caller reports when it gives up: a one-shot program\n // still holding a handle did NOT finish, and exiting 0 would claim it did.\n return { passes: __pass, pending: __exited ? 0 : __countHandles() };\n}\n\n// An ESM entry's own evaluation promise (top-level await) is the one promise\n// that IS a handle \u2014 the module has not finished loading until it settles.\n// Answers true when process.exit won the race instead.\nasync function __nimbusAwaitEntryEvaluation(__entryResult) {\n if (!__entryResult || typeof __entryResult.then !== \"function\") return false;\n const __exit = {};\n const __raced = await Promise.race([\n __entryResult.then(() => null),\n __nimbusProcessExitPromise.then(() => __exit, () => __exit),\n ]);\n return __raced === __exit;\n}\n\n// A one-shot facet's lifetime IS the loop: it runs the program until Node\n// would exit, or until the lifetime budget runs out.\nasync function __nimbusRunEntrypointToExit(__entryResult, __deadlineMs) {\n if (await __nimbusAwaitEntryEvaluation(__entryResult)) return { passes: 0, pending: 0 };\n return await __nimbusRunEventLoop(__nimbusLiveHandles, __nimbusProcessExitPromise, __deadlineMs, 4);\n}\n\n// A resident facet keeps running after the call that boots it returns, so it\n// settles startup and nothing more. The handles it holds open deliberately \u2014\n// its listening port \u2014 are the point of it, not a reason to make the shell's\n// prompt wait.\nasync function __nimbusSettleEntrypointStartup(__entryResult, __deadlineMs) {\n if (await __nimbusAwaitEntryEvaluation(__entryResult)) return { passes: 0, pending: 0 };\n return await __nimbusRunEventLoop(\n __nimbusPendingStartupWork, __nimbusProcessExitPromise, __deadlineMs, 4,\n );\n}\n";
|
|
147
117
|
/**
|
|
148
118
|
* A generated facet's module map: its main module plus whatever side modules
|
|
149
119
|
* the VFS bundle had to be partitioned across.
|
|
@@ -155,14 +125,32 @@ interface GeneratedNodeFacetCode {
|
|
|
155
125
|
/**
|
|
156
126
|
* Generate one-shot runtime code with a plain fetch handler.
|
|
157
127
|
*/
|
|
158
|
-
export declare function generateEntrypointCode(userCode: string, vfsState: FacetVfsState, usesSqlite: boolean, shims: string): Promise<GeneratedNodeFacetCode>;
|
|
128
|
+
export declare function generateEntrypointCode(userCode: string, vfsState: FacetVfsState, usesSqlite: boolean, shims: string, wasmImports?: readonly FacetWasmImport[]): Promise<GeneratedNodeFacetCode>;
|
|
129
|
+
/** One wasm image the generated main module imports from the module map. */
|
|
130
|
+
export interface FacetWasmImport {
|
|
131
|
+
/** The module-map name the boot spec carries the image under. */
|
|
132
|
+
moduleName: string;
|
|
133
|
+
/** The absolute VFS path the program reads the same bytes from. */
|
|
134
|
+
vfsPath: string;
|
|
135
|
+
/**
|
|
136
|
+
* A content key for the same bytes, for an image the program never reads
|
|
137
|
+
* from the filesystem — one inlined in a package's own source as base64.
|
|
138
|
+
* The seam recognises the bytes instead of the path.
|
|
139
|
+
*/
|
|
140
|
+
digest?: string;
|
|
141
|
+
}
|
|
142
|
+
/** The module-map name a precompiled wasm image travels under. */
|
|
143
|
+
export declare function facetWasmModuleName(index: number): string;
|
|
159
144
|
/**
|
|
160
|
-
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* serving HTTP requests from the shimmed http.Server registry.
|
|
145
|
+
* The wasm imports one launch stages: the images its options name, then
|
|
146
|
+
* every image the closure walk recorded (FacetVfsState.wasmImages) that the
|
|
147
|
+
* options did not already name by path. One member per path; the closure's
|
|
148
|
+
* record supplies the digest an option without one lacks.
|
|
165
149
|
*/
|
|
150
|
+
export declare function facetWasmImports(named: readonly {
|
|
151
|
+
vfsPath: string;
|
|
152
|
+
digest: string | undefined;
|
|
153
|
+
}[], closure: readonly WasmImageRecord[]): FacetWasmImport[];
|
|
166
154
|
export declare function generateLongRunningNodeCode(userCode: string, vfsState: FacetVfsState, opts: {
|
|
167
155
|
argv?: string[];
|
|
168
156
|
env?: Record<string, string>;
|
|
@@ -172,6 +160,8 @@ export declare function generateLongRunningNodeCode(userCode: string, vfsState:
|
|
|
172
160
|
stdin?: string;
|
|
173
161
|
attachedTty?: boolean;
|
|
174
162
|
cred: ProcessEntry['cred'];
|
|
163
|
+
/** Wasm images the generated main module imports and parks in the seam. */
|
|
164
|
+
wasmImports?: readonly FacetWasmImport[];
|
|
175
165
|
}, usesSqlite: boolean, shims: string, pacer?: TurnBudget): Promise<GeneratedNodeFacetCode>;
|
|
176
166
|
/**
|
|
177
167
|
* Result of preparing facet VFS state.
|
|
@@ -237,6 +227,11 @@ interface FacetVfsState {
|
|
|
237
227
|
serializedMetadata?: string;
|
|
238
228
|
/** Move the bundle out of the main module when combined state exceeds its ceiling. */
|
|
239
229
|
bundleSideModulesRequired?: boolean;
|
|
230
|
+
/**
|
|
231
|
+
* The wasm images the closure holds, by path and content digest — see
|
|
232
|
+
* `collectClosureWasmImages`. A launch stages each as a wasm map entry.
|
|
233
|
+
*/
|
|
234
|
+
wasmImages?: readonly WasmImageRecord[];
|
|
240
235
|
/**
|
|
241
236
|
* Memoized `bundleUsesNodeSqlite(entryCode, bundle)`. Answered while the raw
|
|
242
237
|
* cells are still in hand so `releaseSerializedSources` can drop them — it is
|
|
@@ -262,38 +257,37 @@ interface FacetVfsState {
|
|
|
262
257
|
* `bundleSource`, `serializedManifest` and `serializedMetadata` are total
|
|
263
258
|
* encodings of `bundle`, `manifest` and `metadata` — no caller can distinguish
|
|
264
259
|
* a state carrying both from one carrying only the serialized halves, because
|
|
265
|
-
*
|
|
260
|
+
* both facet generators read the serialized halves and nothing else does.
|
|
266
261
|
* Holding both doubles the cost of a cached entry for its whole lifetime, and
|
|
267
262
|
* that lifetime spans execs.
|
|
268
263
|
*
|
|
269
|
-
*
|
|
270
|
-
*
|
|
271
|
-
*
|
|
272
|
-
*
|
|
273
|
-
* (`
|
|
274
|
-
* goes through here.
|
|
264
|
+
* Applied by `_buildProcessBundle` to every state it builds, whether or not
|
|
265
|
+
* the entry turns out small enough to retain: the launch being served reads
|
|
266
|
+
* the serialized forms too. `_stageOpencodeFacet` builds its own uncached
|
|
267
|
+
* state and genuinely re-reads the raw cells
|
|
268
|
+
* (`assertStagedBundleFitsRpcPayload`); it does not go through here.
|
|
275
269
|
*/
|
|
276
270
|
export declare function releaseSerializedSources(vfsState: FacetVfsState): void;
|
|
277
271
|
/**
|
|
278
|
-
* Drop
|
|
279
|
-
*
|
|
280
|
-
* The one-shot path releases its map at LOADER.load; this is the same policy
|
|
281
|
-
* for the path `spawnNode` takes, which is the path every attached-TTY npm bin
|
|
282
|
-
* takes — how a real agentic CLI starts. The generated source is a total
|
|
283
|
-
* encoding of the cells, the manifest and the metadata, and the only thing the
|
|
284
|
-
* rest of a launch reads off the state is `cursor`. Everything else is a second
|
|
285
|
-
* copy of the largest thing this DO builds — 22.9 MB for pi — held for exactly
|
|
286
|
-
* as long as the facet takes to boot on it.
|
|
272
|
+
* Drop the serialized forms once a module map has been generated from them.
|
|
287
273
|
*
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
*
|
|
274
|
+
* The generated source is a total encoding of all three: every byte of the
|
|
275
|
+
* bundle expression, the manifest and the metadata is inside it. Holding them
|
|
276
|
+
* afterwards keeps a second copy of the largest thing this DO builds alive for
|
|
277
|
+
* as long as the facet runs — for pi, 22.7 MB across the ~20 s window in which
|
|
278
|
+
* the isolate was being reset. For the one-shot path that window is the run;
|
|
279
|
+
* for a resident launch — the path every attached-TTY npm bin takes, how a
|
|
280
|
+
* real agentic CLI starts — it is the boot, and holding the copy across it
|
|
281
|
+
* reset the session isolate with exceededMemory, which tears the terminal
|
|
282
|
+
* WebSocket down with no exit frame: the dead screen reading "[process
|
|
283
|
+
* terminal closed]".
|
|
291
284
|
*
|
|
292
|
-
*
|
|
293
|
-
*
|
|
294
|
-
*
|
|
285
|
+
* Only for a state the prefetch cache refused. A retained entry's serialized
|
|
286
|
+
* forms ARE the entry, and a later launch is served from them. An emptied
|
|
287
|
+
* state could still generate a map — one with no program in it — so this
|
|
288
|
+
* marks the state instead of trusting callers to stop.
|
|
295
289
|
*/
|
|
296
|
-
export declare function
|
|
290
|
+
export declare function releaseGeneratedSources(vfsState: FacetVfsState): void;
|
|
297
291
|
interface FacetVfsBundleSource {
|
|
298
292
|
expression: string;
|
|
299
293
|
imports: string;
|
|
@@ -350,13 +344,35 @@ export declare function assertStagedBundleFitsRpcPayload(serialized: string, bun
|
|
|
350
344
|
* computed-path requires). Bounded to package.json + 1 main-entry file
|
|
351
345
|
* per package — sub-agent §Q3 quantified the worst-case cumulative
|
|
352
346
|
* budget impact (~322 KiB for fastify, ~1.7 MiB for ts-jest).
|
|
347
|
+
*
|
|
348
|
+
* `requiredPaths` is the static require closure. A package the closure
|
|
349
|
+
* already reached — but reached only through a SUBPATH — has its main entry
|
|
350
|
+
* skipped; see `mainIsSpeculative`.
|
|
353
351
|
*/
|
|
354
352
|
export declare function greedyAddMainEntries(vfs: CredentialedVfs, cwd: string, bundle: Record<string, string | Uint8Array>, budgetState: {
|
|
355
353
|
totalBytes: number;
|
|
356
354
|
fileCount: number;
|
|
357
|
-
}): {
|
|
355
|
+
}, requiredPaths?: ReadonlySet<string>): {
|
|
358
356
|
added: number;
|
|
359
357
|
};
|
|
358
|
+
/**
|
|
359
|
+
* The packages a computed `require(name)` inside the program can plausibly
|
|
360
|
+
* name: the project root's own runtime `dependencies`, plus every package
|
|
361
|
+
* ONE `dependencies` hop from a package that already owns a file in the
|
|
362
|
+
* static closure. Never devDependencies, never a second hop.
|
|
363
|
+
*
|
|
364
|
+
* Unbounded, the greedy oversample read every installed package's main:
|
|
365
|
+
* for `node -e "import('got')"` in got's repo — a one-file static closure —
|
|
366
|
+
* that was 1,526 files / 10.9 MB from 706 packages, which every later pass
|
|
367
|
+
* re-scanned and esbuild-wasm transformed, and the exec path's 20 s bundle
|
|
368
|
+
* deadline fired on a program that reads none of it. A bound that followed
|
|
369
|
+
* dependency edges from the project's devDependencies reached all 772 of
|
|
370
|
+
* them (measured), because a library repo's dev toolchain reaches the whole
|
|
371
|
+
* tree. Computed requires almost always target a declared runtime
|
|
372
|
+
* dependency of the package doing the requiring, so the bound is one hop
|
|
373
|
+
* over `dependencies` only. Directories, sorted for a stable bundle.
|
|
374
|
+
*/
|
|
375
|
+
export declare function speculativePackageDirs(vfs: CredentialedVfs, cwdStripped: string, bundle: Record<string, string | Uint8Array>): string[];
|
|
360
376
|
/**
|
|
361
377
|
* X.5-Z3: scan every JS source already in `bundle` for static
|
|
362
378
|
* `fs.readFileSync(path.resolve(__dirname, "<rel>"))` shapes and pull
|
|
@@ -488,7 +504,21 @@ export declare function addBinTargetSiblings(vfs: CredentialedVfs, scriptPath: s
|
|
|
488
504
|
fileCount: number;
|
|
489
505
|
}, bundleProfile: FacetBundleProfile): {
|
|
490
506
|
added: number;
|
|
507
|
+
wasmPaths: string[];
|
|
491
508
|
};
|
|
509
|
+
/**
|
|
510
|
+
* The wasm images a program's closure holds, by path and content digest.
|
|
511
|
+
*
|
|
512
|
+
* Two sources, one record: a `.wasm` cell the walk already staged (digested
|
|
513
|
+
* from the cell, no second read), and a `.wasm` file the bin-package pass
|
|
514
|
+
* saw but did not stage because it is over the bundle's per-file cap —
|
|
515
|
+
* esbuild-wasm's 11.9 MiB image is the motivating one. A launch stages each
|
|
516
|
+
* as a module-map member the loader compiles, registered under both keys,
|
|
517
|
+
* so the program's own `new WebAssembly.Module(bytes)` is answered from the
|
|
518
|
+
* map whether it read the bytes by path or carried them inline. A file that
|
|
519
|
+
* cannot be read is left out; the seam's refusal names the module later.
|
|
520
|
+
*/
|
|
521
|
+
export declare function collectClosureWasmImages(vfs: Pick<CredentialedVfs, 'readFile'>, bundle: Record<string, string | Uint8Array | FacetVfsDenial>, unstagedPaths: readonly string[]): WasmImageRecord[];
|
|
492
522
|
/**
|
|
493
523
|
* Stage the paths an earlier run of the same entry read synchronously and did
|
|
494
524
|
* not have.
|
|
@@ -576,7 +606,7 @@ export declare const BUNDLE_PRECOMPILE_LOOP: string;
|
|
|
576
606
|
* behaviour for code paths that don't have esbuild handy).
|
|
577
607
|
*
|
|
578
608
|
*/
|
|
579
|
-
export declare function buildPrefetchBundle(vfs: CredentialedVfs, scriptPath: string | undefined, cwd: string, entryCode: string, esbuild?: EsbuildService, bundleProfile?: FacetBundleProfile, observedReads?: ReadonlySet<string>, pacer?: TurnBudget, isolatedTransform?: LargeEsmTransform): Promise<FacetVfsState>;
|
|
609
|
+
export declare function buildPrefetchBundle(vfs: CredentialedVfs, scriptPath: string | undefined, cwd: string, entryCode: string, esbuild?: EsbuildService, bundleProfile?: FacetBundleProfile, observedReads?: ReadonlySet<string>, pacer?: TurnBudget, isolatedTransform?: LargeEsmTransform, maxBundleBytes?: number): Promise<FacetVfsState>;
|
|
580
610
|
/**
|
|
581
611
|
* Optional hooks wired in by NimbusSession. Kept as callbacks so
|
|
582
612
|
* FacetManager stays unaware of the session / log-store types.
|
|
@@ -598,7 +628,7 @@ export interface FacetManagerHooks {
|
|
|
598
628
|
* genuinely re-enters the object: a fresh turn is both a released thread and
|
|
599
629
|
* a fresh CPU budget, and a launch needs each for a different reason.
|
|
600
630
|
*/
|
|
601
|
-
requestLaunchTurn?: () => void;
|
|
631
|
+
requestLaunchTurn?: (notBefore?: number) => void;
|
|
602
632
|
/**
|
|
603
633
|
* Put a line in front of the user, whether or not a terminal is attached.
|
|
604
634
|
*
|
|
@@ -628,6 +658,12 @@ export interface FacetManagerHooks {
|
|
|
628
658
|
resolveWorkerLaunchFallback?: (recipe: WorkerRecipe) => Promise<ResolvedWorkerLaunch | null>;
|
|
629
659
|
}
|
|
630
660
|
export interface LongRunningWorkerSpawnOptions {
|
|
661
|
+
/** Interpreter residents share Node's atomic derived-owner claim. */
|
|
662
|
+
resident?: {
|
|
663
|
+
runtime: 'ruby' | 'python';
|
|
664
|
+
argv: string[];
|
|
665
|
+
};
|
|
666
|
+
restart?: ResidentRestartPolicy;
|
|
631
667
|
port?: number;
|
|
632
668
|
/** Inline modules: source text, or small wasm carried by value. */
|
|
633
669
|
modules?: Record<string, string | {
|
|
@@ -639,6 +675,20 @@ export interface LongRunningWorkerSpawnOptions {
|
|
|
639
675
|
* 34.3 MiB, past what any single RPC value may carry.
|
|
640
676
|
*/
|
|
641
677
|
vfsWasmModules?: Record<string, string>;
|
|
678
|
+
/**
|
|
679
|
+
* Module name → VFS path of a content-addressed module SOURCE, read as
|
|
680
|
+
* UTF-8 when the facet loads — the same by-path posture as
|
|
681
|
+
* `vfsWasmModules`, through the same kernel image reader, for module text
|
|
682
|
+
* an embedder keeps in its own content store rather than carrying by value.
|
|
683
|
+
* Each path must name its own digest (`…/<sha256>.js`, see fabric's
|
|
684
|
+
* `facetImagePath`); the loader verifies it on read.
|
|
685
|
+
*/
|
|
686
|
+
vfsTextModules?: Record<string, string>;
|
|
687
|
+
/**
|
|
688
|
+
* The module the isolate boots from; `workerCode` is placed under this name
|
|
689
|
+
* in the module map. Default `'worker.js'`.
|
|
690
|
+
*/
|
|
691
|
+
mainModule?: string;
|
|
642
692
|
compatibilityFlags?: string[];
|
|
643
693
|
compatibilityDate?: string;
|
|
644
694
|
env?: ResidentCodeSpec['env'];
|
|
@@ -658,6 +708,29 @@ export interface LongRunningWorkerSpawnOptions {
|
|
|
658
708
|
};
|
|
659
709
|
};
|
|
660
710
|
}
|
|
711
|
+
/** The main module name a worker launch boots from unless told otherwise. */
|
|
712
|
+
export declare const DEFAULT_WORKER_MAIN_MODULE = "worker.js";
|
|
713
|
+
/**
|
|
714
|
+
* The narrow handle `spawnWorker` returns beside the pid: the process's own
|
|
715
|
+
* inbound surface, bound to the resident handle's route target, for an
|
|
716
|
+
* embedder that invokes the worker directly rather than through a registered
|
|
717
|
+
* port. It carries NO release — `kill(pid)` remains the one lifecycle owner,
|
|
718
|
+
* and this handle is dead once that pid is; nothing here can end, restart or
|
|
719
|
+
* detach the process.
|
|
720
|
+
*/
|
|
721
|
+
export interface WorkerFacet {
|
|
722
|
+
/** Inbound HTTP, on the facet's `handleHttpRequest`. */
|
|
723
|
+
fetch(request: Request): Promise<Response>;
|
|
724
|
+
/** Inbound WebSocket upgrade, on the facet's `handleWebSocketRequest`. */
|
|
725
|
+
connect(request: Request): Promise<Response>;
|
|
726
|
+
}
|
|
727
|
+
/** What `spawnWorker` answers with. */
|
|
728
|
+
export interface SpawnedWorker {
|
|
729
|
+
pid: number;
|
|
730
|
+
/** The runner's startProcess payload. */
|
|
731
|
+
boot: unknown;
|
|
732
|
+
facet: WorkerFacet;
|
|
733
|
+
}
|
|
661
734
|
/** What `spawnNode` needs to build and boot one resident Node process. */
|
|
662
735
|
export interface ResidentSpawnOptions {
|
|
663
736
|
argv?: string[];
|
|
@@ -675,6 +748,19 @@ export interface ResidentSpawnOptions {
|
|
|
675
748
|
/** The launch inputs a re-drive rebuilds a worker from: content digests and
|
|
676
749
|
* transport, never env or credentials — the embedder resolves those. */
|
|
677
750
|
export interface WorkerRecipe {
|
|
751
|
+
/**
|
|
752
|
+
* Set only for an interpreter resident the SESSION launched itself — a
|
|
753
|
+
* `python`/`ruby` socket server whose image is the runtime the session
|
|
754
|
+
* installed. It is the runtime and argv of that interpreter, never a
|
|
755
|
+
* boolean, and it is what routes the recipe's re-drive to the session's own
|
|
756
|
+
* image-store fallback (`resolveWorkerLaunchFallback`) rather than the
|
|
757
|
+
* embedder's `resolveWorkerLaunch`: the session owns that image and its
|
|
758
|
+
* bookkeeping, and no embedder was asked about the launch. A worker recipe
|
|
759
|
+
* WITHOUT it — every embedder-driven `spawnWorker` — re-drives through the
|
|
760
|
+
* embedder's resolver, with the fallback consulted only when no embedder
|
|
761
|
+
* hook is composed.
|
|
762
|
+
*/
|
|
763
|
+
resident?: LongRunningWorkerSpawnOptions['resident'];
|
|
678
764
|
kind: 'worker';
|
|
679
765
|
/** The durable application this process belongs to, keyed by the embedder. */
|
|
680
766
|
owner: string;
|
|
@@ -687,17 +773,53 @@ export interface WorkerRecipe {
|
|
|
687
773
|
cwd: string;
|
|
688
774
|
compatibilityDate: string;
|
|
689
775
|
compatibilityFlags: string[];
|
|
690
|
-
startArgs
|
|
776
|
+
startArgs?: unknown;
|
|
777
|
+
/**
|
|
778
|
+
* The main module the launch booted from, when it was not the default —
|
|
779
|
+
* so a re-drive whose resolver answers modules alone still boots the same
|
|
780
|
+
* one. Rows written before the field existed booted `worker.js`.
|
|
781
|
+
*/
|
|
782
|
+
mainModule?: string;
|
|
783
|
+
}
|
|
784
|
+
export type ResidentRestartPolicy = 'never' | 'on-failure';
|
|
785
|
+
/** The env var a launch reads its restart policy from — set by startProcess({ restart }) and `nimbus start --restart`. */
|
|
786
|
+
export declare const RESTART_POLICY_ENV = "NIMBUS_RESTART";
|
|
787
|
+
/** One application as `apps.list` reports it — every stamped identity, live or not. */
|
|
788
|
+
export interface ResidentAppSummary {
|
|
789
|
+
owner: string;
|
|
790
|
+
name: string | null;
|
|
791
|
+
port: number | null;
|
|
792
|
+
pid: number | null;
|
|
793
|
+
status: 'running' | 'starting' | 'stopped' | 'failed';
|
|
794
|
+
visibility: PortVisibility;
|
|
795
|
+
capability: string | null;
|
|
796
|
+
restart: ResidentRestartPolicy;
|
|
797
|
+
/** Set with status 'failed': what went wrong, in the user's terms. */
|
|
798
|
+
diagnostic: string | null;
|
|
799
|
+
}
|
|
800
|
+
/** What a pid's journal row says about who it is. */
|
|
801
|
+
export interface ResidentIdentity {
|
|
802
|
+
owner: string | undefined;
|
|
803
|
+
ephemeral: boolean;
|
|
804
|
+
port: number | undefined;
|
|
691
805
|
}
|
|
692
806
|
/** What the embedder supplies for a re-driven worker launch. */
|
|
693
807
|
export interface ResolvedWorkerLaunch {
|
|
808
|
+
startArgs?: unknown;
|
|
694
809
|
/** Null is the absent answer — the same JSON the image blob carries. */
|
|
695
810
|
env: ResidentCodeSpec['env'] | null;
|
|
696
811
|
globalOutbound: ResidentCodeSpec['globalOutbound'];
|
|
697
|
-
/** Module name → source text, including the
|
|
812
|
+
/** Module name → source text, including the main module under its name. */
|
|
698
813
|
modules: Record<string, string>;
|
|
699
814
|
/** Module name → VFS path of a wasm image, restored with the launch. */
|
|
700
815
|
vfsWasmModules?: Record<string, string>;
|
|
816
|
+
/** Module name → VFS path of a content-addressed module source, restored with the launch. */
|
|
817
|
+
vfsTextModules?: Record<string, string>;
|
|
818
|
+
/**
|
|
819
|
+
* The name in `modules` the launch boots from. Absent, the recipe's own
|
|
820
|
+
* `mainModule` decides, then `'worker.js'`.
|
|
821
|
+
*/
|
|
822
|
+
mainModule?: string;
|
|
701
823
|
}
|
|
702
824
|
export declare class FacetManager {
|
|
703
825
|
private ctx;
|
|
@@ -741,7 +863,6 @@ export declare class FacetManager {
|
|
|
741
863
|
* journal recovery rides the first pump.
|
|
742
864
|
*/
|
|
743
865
|
private readonly launchPump;
|
|
744
|
-
private timedOutProcessIds;
|
|
745
866
|
private _pairedServeFacet;
|
|
746
867
|
/**
|
|
747
868
|
* W3.5 Fix B: lazily-created EsbuildService for the ESM→CJS pre-pass
|
|
@@ -783,6 +904,15 @@ export declare class FacetManager {
|
|
|
783
904
|
private residencyProfiles;
|
|
784
905
|
/** In-flight request-driven durable-app ensures, single-flight per port. */
|
|
785
906
|
private ensureInflight;
|
|
907
|
+
/** Per-pid chain of journal-row amendments; see `_amendRow`. */
|
|
908
|
+
private rowAmendments;
|
|
909
|
+
/**
|
|
910
|
+
* pid → the derived owner it duplicates: the second live instance of an
|
|
911
|
+
* identity. Not journalled (nothing re-drives it), so this is the only
|
|
912
|
+
* record of why `expose(pid)` refuses it.
|
|
913
|
+
*/
|
|
914
|
+
private ephemeralPids;
|
|
915
|
+
private residentClaims;
|
|
786
916
|
private static readonly RESIDENCY_PROFILE_MAX_ENTRIES;
|
|
787
917
|
/**
|
|
788
918
|
* A program that reads a directory of data files misses once per file, so
|
|
@@ -792,6 +922,23 @@ export declare class FacetManager {
|
|
|
792
922
|
*/
|
|
793
923
|
private static readonly RESIDENCY_PROFILE_MAX_PATHS;
|
|
794
924
|
constructor(ctx: DurableObjectState, env: unknown, processes: SessionProcessSupervisor, portRegistry: PortRegistry, host: ProcessHostFactory, hooks?: FacetManagerHooks);
|
|
925
|
+
/**
|
|
926
|
+
* The process is over. Every end-of-life passes through here: a clean
|
|
927
|
+
* exit, a kill, a timeout, a crash. Only one of them owes anything more
|
|
928
|
+
* than the journal row's release — a crash under 'on-failure' is re-driven
|
|
929
|
+
* from the row, after a backoff, while the row is still in storage so a
|
|
930
|
+
* reset inside the backoff window recovers it like any other resident.
|
|
931
|
+
*/
|
|
932
|
+
private _onResidentTerminal;
|
|
933
|
+
/** Claim identity AND write its recovery row in one serializable storage transaction. */
|
|
934
|
+
private _claimResident;
|
|
935
|
+
private _releaseResidentClaim;
|
|
936
|
+
/**
|
|
937
|
+
* Amend one journal row in place — port stamp, owner adoption, settle —
|
|
938
|
+
* serialized per pid so two amendments in flight on the same row cannot
|
|
939
|
+
* interleave their read and write and lose one another's fields.
|
|
940
|
+
*/
|
|
941
|
+
private _amendRow;
|
|
795
942
|
setVfs(vfs: SqliteVFS): void;
|
|
796
943
|
/**
|
|
797
944
|
* The env/ctx pair every loader-backed runtime builds its facet pools
|
|
@@ -823,39 +970,56 @@ export declare class FacetManager {
|
|
|
823
970
|
*/
|
|
824
971
|
setEsbuildService(esbuild: EsbuildService): void;
|
|
825
972
|
/**
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
*
|
|
829
|
-
* pass + source construction. See `prefetchBundleCache` for the
|
|
830
|
-
* correctness argument behind the conservative global-revision watermark.
|
|
831
|
-
*
|
|
832
|
-
* The bundle source and manifest are computed once on the miss path and
|
|
833
|
-
* stored so subsequent hits skip rebuilding them too.
|
|
973
|
+
* The pacer every launch is built under: the session's alarm-driven turn
|
|
974
|
+
* pump, the deployment's chunk bound, and the one check a suspended launch
|
|
975
|
+
* makes when it resumes — that the process it is building for still exists.
|
|
834
976
|
*/
|
|
977
|
+
private _launchPacer;
|
|
835
978
|
/**
|
|
836
|
-
*
|
|
979
|
+
* Assemble the filesystem bundle a process boots on, across as many
|
|
980
|
+
* Durable Object turns as it takes.
|
|
981
|
+
*
|
|
982
|
+
* The one builder for every Node process this manager starts. A one-shot
|
|
983
|
+
* exec and a resident launch used to own two copies of this: exec's was
|
|
984
|
+
* memoized behind the prefetch cache and raced a wall-clock deadline in a
|
|
985
|
+
* single turn; the resident's was paged with a TurnBudget and never cached.
|
|
986
|
+
* Two paths, one job — and a tree large enough to page on one path hit the
|
|
987
|
+
* deadline on the other, failing every `node -e` in it with "assembling the
|
|
988
|
+
* filesystem bundle … exceeded". What the two callers genuinely differ in is
|
|
989
|
+
* the entry, the working directory and the process they build for; that is
|
|
990
|
+
* all they supply. Everything else — the revision-keyed cache and its stale
|
|
991
|
+
* eviction, the residency profile a previous miss learned, the reachable-set
|
|
992
|
+
* walk and its enrichment passes, the ESM→CJS transform, the manifest and
|
|
993
|
+
* metadata, the module-map serialization with its side-module split, the
|
|
994
|
+
* `node:sqlite` answer, and the release of the raw cells once they are
|
|
995
|
+
* serialized — happens here, once, and yields the turn whenever a chunk's
|
|
996
|
+
* worth of it has been done.
|
|
837
997
|
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
* never returns, with no exit record for the process.
|
|
998
|
+
* There is no deadline. A launch that spans turns costs turns, not a held
|
|
999
|
+
* thread, so a large tree is not a defect to be reported at N seconds; the
|
|
1000
|
+
* pacer's `stillWanted` check is what ends a build nothing will use — a
|
|
1001
|
+
* process killed while its build was suspended throws from the next resume,
|
|
1002
|
+
* and the caller reports that as it reports any other launch failure.
|
|
844
1003
|
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
1004
|
+
* The returned state carries its serialized forms (`bundleSource`,
|
|
1005
|
+
* `serializedManifest`, `serializedMetadata`) and has already released the
|
|
1006
|
+
* raw ones: `generateEntrypointCode` and `generateLongRunningNodeCode` read
|
|
1007
|
+
* the serialized forms and nothing else. A state the cache retained belongs
|
|
1008
|
+
* to the cache — a caller must not release its serialized forms either
|
|
1009
|
+
* (`cacheRetained` says which); one the cache refused belongs to the caller
|
|
1010
|
+
* alone, and `releaseGeneratedSources` drops it once a map is generated.
|
|
1011
|
+
*
|
|
1012
|
+
* A session without a filesystem gets an empty state: there is nothing to
|
|
1013
|
+
* stage and nothing to yield for.
|
|
1014
|
+
*/
|
|
1015
|
+
/**
|
|
1016
|
+
* The closure's wasm images as module-map members for a one-shot facet,
|
|
1017
|
+
* read by path under the process's own credential. An image that cannot
|
|
1018
|
+
* be read is left out; the program's compile then meets the seam's own
|
|
1019
|
+
* refusal, which names the module.
|
|
1020
|
+
*/
|
|
1021
|
+
private _wasmModulesByValue;
|
|
1022
|
+
private _buildProcessBundle;
|
|
859
1023
|
/**
|
|
860
1024
|
* Admit an entry and evict, oldest first, until the LRU is inside BOTH its
|
|
861
1025
|
* entry count and its byte bound.
|
|
@@ -932,6 +1096,8 @@ export declare class FacetManager {
|
|
|
932
1096
|
/** Return stdout/stderr in the result while keeping supervisor RPC
|
|
933
1097
|
* available for VFS and child_process operations. */
|
|
934
1098
|
captureOutput?: boolean;
|
|
1099
|
+
/** Shell abort (Ctrl+C): aborting this aborts the in-flight run. */
|
|
1100
|
+
signal?: AbortSignal;
|
|
935
1101
|
}): Promise<FacetExecResult>;
|
|
936
1102
|
/**
|
|
937
1103
|
* W5 Lever 5: push a DiagFailure into the OOM ring for every facet
|
|
@@ -1074,10 +1240,16 @@ export declare class FacetManager {
|
|
|
1074
1240
|
private _warmOpencodeServer;
|
|
1075
1241
|
/** Recent stderr/stdout tail for a pid, for fail-loud diagnostics. */
|
|
1076
1242
|
private _processLogTail;
|
|
1243
|
+
/**
|
|
1244
|
+
* A launch that fails before its process is running reports the same way
|
|
1245
|
+
* regardless of which phase failed: the pid is exited, the terminal event
|
|
1246
|
+
* recorded, and the session notified. Callers do their phase-specific
|
|
1247
|
+
* cleanup (ports, tracked RPC resources) first and pass a reason that names
|
|
1248
|
+
* the phase.
|
|
1249
|
+
*/
|
|
1250
|
+
private _failLaunch;
|
|
1077
1251
|
/** Flush files written by the script back to the supervisor's VFS. */
|
|
1078
1252
|
private _flushVfsWrites;
|
|
1079
|
-
/** Execution timeout. */
|
|
1080
|
-
private _execWithTimeout;
|
|
1081
1253
|
/**
|
|
1082
1254
|
* Re-drive a journalled launch after an instance reset. What the journal
|
|
1083
1255
|
* row carries is the recipe and nothing else: env and credentials are never
|
|
@@ -1118,10 +1290,21 @@ export declare class FacetManager {
|
|
|
1118
1290
|
private _runResidentLaunch;
|
|
1119
1291
|
private _residentLaunchBody;
|
|
1120
1292
|
/**
|
|
1121
|
-
*
|
|
1293
|
+
* The exit code of a launched process that has already ended, or null
|
|
1294
|
+
* while it runs. What a caller that started a resident reads to tell a
|
|
1295
|
+
* server that is up from a program that finished during its boot.
|
|
1296
|
+
*/
|
|
1297
|
+
processExitCode(pid: number): number | null;
|
|
1298
|
+
/**
|
|
1299
|
+
* Spawn a long-running dynamic Worker, boot it, and return its boot payload
|
|
1300
|
+
* beside the pid and the process's own inbound facet.
|
|
1122
1301
|
*
|
|
1123
1302
|
* The shared primitive for any runtime that serves over
|
|
1124
|
-
* handleHttpRequest(Request) — the python and ruby socket servers today
|
|
1303
|
+
* handleHttpRequest(Request) — the python and ruby socket servers today —
|
|
1304
|
+
* and for an embedder's own Worker-class program: `workerCode` boots as
|
|
1305
|
+
* `opts.mainModule` (default `worker.js`), `opts.modules` ride inline,
|
|
1306
|
+
* `opts.vfsTextModules` and `opts.vfsWasmModules` are read by path when the
|
|
1307
|
+
* facet loads.
|
|
1125
1308
|
*
|
|
1126
1309
|
* The interpreter image it carries is the memory that should not sit in the
|
|
1127
1310
|
* session's own isolate — ruby's interpreter+stdlib alone is 34.3 MiB — and
|
|
@@ -1129,30 +1312,61 @@ export declare class FacetManager {
|
|
|
1129
1312
|
* no readiness coupling back into the session: the runner answers
|
|
1130
1313
|
* startProcess with its boot payload and the caller waits on that one
|
|
1131
1314
|
* promise, so nothing polls the port to decide the process is up.
|
|
1315
|
+
*
|
|
1316
|
+
* The returned `facet` is bound to the resident handle's route target — the
|
|
1317
|
+
* same target a registered port routes to — so a port-less process can be
|
|
1318
|
+
* invoked directly. It has no release: `kill(pid)` is the one lifecycle
|
|
1319
|
+
* owner, and the facet is dead once the pid is.
|
|
1132
1320
|
*/
|
|
1133
|
-
spawnWorker(workerCode: string, command: string, cwd: string, opts?: LongRunningWorkerSpawnOptions): Promise<
|
|
1134
|
-
pid: number;
|
|
1135
|
-
boot: unknown;
|
|
1136
|
-
}>;
|
|
1321
|
+
spawnWorker(workerCode: string, command: string, cwd: string, opts?: LongRunningWorkerSpawnOptions): Promise<SpawnedWorker>;
|
|
1137
1322
|
/** `attempt` is the journal's re-drive budget, as `_spawnResident` carries it. */
|
|
1138
1323
|
private _spawnWorker;
|
|
1139
1324
|
/**
|
|
1140
|
-
* A resident process announcing it bound `port`.
|
|
1141
|
-
*
|
|
1142
|
-
*
|
|
1143
|
-
*
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
-
*
|
|
1325
|
+
* A resident process announcing it bound `port`.
|
|
1326
|
+
*
|
|
1327
|
+
* The stamp is unconditional: the pid's journal row gets `{ port }`
|
|
1328
|
+
* whether or not anything reserved it, which is what lets
|
|
1329
|
+
* `ensureDurableAppOnPort` re-drive ANY resident a reset killed — the
|
|
1330
|
+
* scoped URLs need no capability, so this alone makes every server's
|
|
1331
|
+
* preview survive a reset on demand.
|
|
1332
|
+
*
|
|
1333
|
+
* The capability is bound to identity, not to the port. The stored
|
|
1334
|
+
* capability is re-adopted only when the row's owner IS the reservation's
|
|
1335
|
+
* owner; any other occupant — an unrelated server, a pid outside the
|
|
1336
|
+
* resident lifecycle, the ephemeral second instance of an identity —
|
|
1337
|
+
* retires it and mints fresh, so a shared link 404s rather than reaching
|
|
1338
|
+
* a program it was never handed out for. An EXPLICIT reservation (an
|
|
1339
|
+
* embedder's `ensureDurableApp`) is the one exception, and it is the
|
|
1340
|
+
* landed contract: the embedder declared the port, so the row adopts the
|
|
1341
|
+
* reservation's owner and the capability with it.
|
|
1147
1342
|
*
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
* owner. An accidental port reuse inside one session therefore cannot
|
|
1152
|
-
* inherit the public capability; same-session processes are one trust
|
|
1153
|
-
* domain, so this is hygiene, not a security boundary.
|
|
1343
|
+
* Under an injected `$PORT`, a resident that binds a different port is
|
|
1344
|
+
* registered anyway — the server must not break — but the row records
|
|
1345
|
+
* the mismatch, so `apps.list` reports it as failed and the user is told.
|
|
1154
1346
|
*/
|
|
1155
1347
|
private _registerResidentPort;
|
|
1348
|
+
/**
|
|
1349
|
+
* Who a pid is. One resolver, in precedence order: the ephemeral-duplicate
|
|
1350
|
+
* mark, the journal row (the owner a launch this manager made was stamped
|
|
1351
|
+
* with — derived from the launch's own cwd+argv, or adopted from an
|
|
1352
|
+
* explicit reservation), and finally the process table. The table knows
|
|
1353
|
+
* cwd and argv for every pid, so a serving process nothing journalled — the
|
|
1354
|
+
* in-process Vite dev server, real-vite, a staged artifact, any wrapper pid
|
|
1355
|
+
* a builtin adopted — has the same derived identity shape as a resident
|
|
1356
|
+
* and answers to the same app verbs. It just cannot be re-driven after a
|
|
1357
|
+
* reset: only a journal row carries a recipe. Null only for a pid that is
|
|
1358
|
+
* neither journalled nor running.
|
|
1359
|
+
*/
|
|
1360
|
+
residentIdentity(pid: number): Promise<ResidentIdentity | null>;
|
|
1361
|
+
/**
|
|
1362
|
+
* Every stamped identity — the reservations, and the journal rows that
|
|
1363
|
+
* carry an owner — folded one row per owner. A live pid in THIS instance
|
|
1364
|
+
* makes it running (or starting, until its launch settles and its port
|
|
1365
|
+
* is registered); a mismatch diagnostic makes it failed; everything else
|
|
1366
|
+
* is stopped, which for a row a reset left behind means re-drivable on
|
|
1367
|
+
* request.
|
|
1368
|
+
*/
|
|
1369
|
+
listResidentApps(): Promise<ResidentAppSummary[]>;
|
|
1156
1370
|
registerPort(pid: number, port: number): Promise<void>;
|
|
1157
1371
|
waitForRouteablePorts(pid: number, timeoutMs?: number): Promise<number[]>;
|
|
1158
1372
|
finishProcess(pid: number, exitCode: number, reason?: string): void;
|