@nimbus-sh/worker 0.2.3 → 0.3.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/dist/_shared/session-router.d.ts +8 -0
- package/dist/_shared/session-router.d.ts.map +1 -1
- package/dist/_shared/session-router.js +8 -0
- package/dist/facets/cirrus-real.js +1 -1
- package/dist/facets/manager.d.ts +40 -126
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +97 -313
- package/dist/facets/opencode-staging.d.ts +1 -1
- package/dist/facets/opencode-staging.d.ts.map +1 -1
- package/dist/facets/opencode-staging.js +4 -0
- package/dist/facets/vite-dev-server.d.ts +2 -2
- package/dist/facets/vite-dev-server.js +4 -4
- package/dist/git/commands.d.ts +8 -0
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +41 -9
- package/dist/git/network-facet.d.ts +1 -1
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +35 -12
- package/dist/git-bundle.generated.d.ts +2 -2
- package/dist/git-bundle.generated.d.ts.map +1 -1
- package/dist/git-bundle.generated.js +3 -3
- package/dist/index.js +1 -1
- package/dist/loaders/child-process/spawn-facet.js +1 -1
- package/dist/loaders/child-process/spawn-pool.d.ts.map +1 -1
- package/dist/loaders/child-process/spawn-pool.js +2 -2
- package/dist/loaders/generated-workers.d.ts +1 -1
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +2 -2
- package/dist/loaders/npm-resolve-preamble.d.ts +3 -3
- package/dist/loaders/npm-resolve-preamble.js +3 -3
- package/dist/loaders/pre-bundle-preamble.d.ts +5 -5
- package/dist/loaders/pre-bundle-preamble.js +6 -6
- package/dist/loaders/process-host.d.ts +6 -107
- package/dist/loaders/process-host.d.ts.map +1 -1
- package/dist/loaders/process-host.js +6 -405
- package/dist/npm/install-batch-facet.js +1 -1
- package/dist/npm/installer.d.ts +4 -4
- package/dist/npm/installer.js +18 -18
- package/dist/npm/pre-bundle-facet.d.ts +2 -2
- package/dist/npm/pre-bundle-facet.js +5 -5
- package/dist/npm/resolve-facet.d.ts +1 -1
- package/dist/npm/resolve-facet.js +1 -1
- package/dist/npm/resolve-one-facet.d.ts +5 -5
- package/dist/npm/resolve-one-facet.js +5 -5
- package/dist/router/index.js +1 -1
- package/dist/router/remote-api.js +8 -0
- package/dist/runtime/bun-repl.d.ts +1 -1
- package/dist/runtime/bun-repl.js +3 -3
- package/dist/runtime/facet-loader-host.d.ts +5 -8
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +7 -11
- package/dist/runtime/node-repl.js +2 -2
- package/dist/runtime/python-repl.js +3 -3
- package/dist/runtime/ruby-repl.js +2 -2
- package/dist/runtime/ruby-resident.js +1 -1
- package/dist/session/hibernation.d.ts +18 -51
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +49 -186
- package/dist/session/init-phases.d.ts +2 -2
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +1 -1
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +6 -3
- package/dist/session/keys.d.ts +14 -37
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +17 -37
- package/dist/session/nimbus-session.d.ts +10 -4
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +20 -11
- package/dist/session/port-capability.d.ts +43 -0
- package/dist/session/port-capability.d.ts.map +1 -0
- package/dist/session/port-capability.js +49 -0
- package/dist/session/programmatic.d.ts +41 -5
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +147 -15
- package/dist/session/routes.d.ts +2 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +91 -16
- package/dist/session/rpc.d.ts +35 -8
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +73 -14
- package/dist/session/start-real-vite.d.ts.map +1 -1
- package/dist/session/start-real-vite.js +2 -0
- package/dist/session/state-store.d.ts +3 -2
- package/dist/session/state-store.d.ts.map +1 -1
- package/dist/session/state-store.js +3 -2
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +5 -0
- package/dist/session/ws.d.ts +2 -2
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +2 -2
- package/dist/wrangler/nimbus-wrangler.js +1 -1
- package/package.json +3 -2
- package/dist/facets/inner-do-registry.d.ts +0 -41
- package/dist/facets/inner-do-registry.d.ts.map +0 -1
- package/dist/facets/inner-do-registry.js +0 -51
- package/dist/facets/launch-pacer.d.ts +0 -111
- package/dist/facets/launch-pacer.d.ts.map +0 -1
- package/dist/facets/launch-pacer.js +0 -130
- package/dist/loaders/fanout-pool.d.ts +0 -207
- package/dist/loaders/fanout-pool.d.ts.map +0 -1
- package/dist/loaders/fanout-pool.js +0 -363
- package/dist/loaders/loader-pool.d.ts +0 -294
- package/dist/loaders/loader-pool.d.ts.map +0 -1
- package/dist/loaders/loader-pool.js +0 -642
- package/dist/loaders/process-fabric.d.ts +0 -509
- package/dist/loaders/process-fabric.d.ts.map +0 -1
- package/dist/loaders/process-fabric.js +0 -360
- package/dist/loaders/vendor/errors.d.ts +0 -24
- package/dist/loaders/vendor/errors.d.ts.map +0 -1
- package/dist/loaders/vendor/errors.js +0 -46
- package/dist/loaders/vendor/serialize.d.ts +0 -3
- package/dist/loaders/vendor/serialize.d.ts.map +0 -1
- package/dist/loaders/vendor/serialize.js +0 -25
- package/dist/loaders/vendor/types.d.ts +0 -69
- package/dist/loaders/vendor/types.d.ts.map +0 -1
- package/dist/loaders/vendor/types.js +0 -4
- package/dist/loaders/workerd-facet-host.d.ts +0 -141
- package/dist/loaders/workerd-facet-host.d.ts.map +0 -1
- package/dist/loaders/workerd-facet-host.js +0 -284
- package/dist/session/bindings.d.ts +0 -209
- package/dist/session/bindings.d.ts.map +0 -1
- package/dist/session/bindings.js +0 -680
- package/dist/session/ctx-exports.d.ts +0 -16
- package/dist/session/ctx-exports.d.ts.map +0 -1
- package/dist/session/ctx-exports.js +0 -22
- package/dist/session/ws-hibernation-config.d.ts +0 -64
- package/dist/session/ws-hibernation-config.d.ts.map +0 -1
- package/dist/session/ws-hibernation-config.js +0 -89
package/dist/facets/manager.js
CHANGED
|
@@ -22,23 +22,25 @@ import { FACET_RESIDENT_STORE_SOURCE } from '../vfs/facet-resident-store.js';
|
|
|
22
22
|
import { VFS_CURSOR_SEED_SOURCE, serializeFacetVfsCursor, } from '@nimbus-sh/core/_shared/facet-vfs-cursor.js';
|
|
23
23
|
import { VFS_WRITE_LEDGER_SOURCE } from '@nimbus-sh/core/_shared/vfs-write-ledger.js';
|
|
24
24
|
import { vfsPathExtension } from '@nimbus-sh/core/vfs/path.js';
|
|
25
|
+
import { clearPortCapability } from '../session/port-capability.js';
|
|
25
26
|
import { prefetchForRequire } from '@nimbus-sh/core/runtime/require-resolver.js';
|
|
26
27
|
import { hasTopLevelModuleSyntax } from '@nimbus-sh/core/runtime/javascript-ast.js';
|
|
27
28
|
import { bindImportMetaResolve, importMetaDefines } from '@nimbus-sh/core/runtime/import-meta-transform.js';
|
|
28
29
|
import { recordFailure, getLastRpcFrame, getLastFacetId } from '@nimbus-sh/core/observability/oom-discriminator.js';
|
|
29
30
|
import { classifyError } from '@nimbus-sh/core/observability/oom-classify.js';
|
|
30
|
-
import { LaunchPacer, launchChunkMaxBytes } from '
|
|
31
|
+
import { LaunchPacer, LaunchTurnPump, launchChunkMaxBytes } from '@nimbus-sh/fabric/launch-pacer.js';
|
|
32
|
+
import { ResidentLaunchJournal, } from '@nimbus-sh/fabric/launch-journal.js';
|
|
31
33
|
import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
32
34
|
import { isExecDiagEnabled, recordExecTelemetry } from './exec-telemetry.js';
|
|
33
35
|
import { disposeRpcResource, disposeRpcResources } from '@nimbus-sh/core/_shared/rpc-dispose.js';
|
|
34
36
|
import { sqliteWasmModuleEntry } from './opencode-staging.js';
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
37
|
+
import { FacetImageStore, } from '@nimbus-sh/fabric/facet-image-store.js';
|
|
38
|
+
import { ProcessFabric, } from '@nimbus-sh/fabric/process-fabric.js';
|
|
39
|
+
import { createLoadedWorkerEntrypoint, getNimbusCtxExports, } from '@nimbus-sh/fabric/workerd-facet-host.js';
|
|
37
40
|
import { SQLITE_WASM_MODULE_NAME, } from '../runtime/opencode-facet-runner.js';
|
|
38
41
|
import { parsePortFromArgv, resolveLongRunningPort } from '@nimbus-sh/core/runtime/long-running-handle.js';
|
|
39
42
|
import { DEFAULT_FACET_BUNDLE_PROFILE, } from '@nimbus-sh/core/runtime/bundle-profile.js';
|
|
40
|
-
import { BUNDLE_BUILD_DEADLINE_MS, CF_COMPAT_DATE,
|
|
41
|
-
import { RESIDENT_LAUNCH_KEY_PREFIX } from '../session/keys.js';
|
|
43
|
+
import { BUNDLE_BUILD_DEADLINE_MS, CF_COMPAT_DATE, FACET_TIMEOUT_MS, VFS_BUNDLE_MAX_FILES, VFS_BUNDLE_MAX_BYTES, CWD_SNAPSHOT_MAX_FILE_BYTES, BUNDLE_MAX_ENCODED_BYTES, MAX_RPC_SAFE_PAYLOAD_BYTES, PREFETCH_CACHE_MAX_BYTES, } from '@nimbus-sh/core/constants.js';
|
|
42
44
|
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
43
45
|
import { acquireSupervisorAllocation } from '@nimbus-sh/core/observability/heavy-alloc-coord.js';
|
|
44
46
|
import { prefetchBundleStart, prefetchBundleEnd, setPrefetchCacheBytes, } from '@nimbus-sh/core/observability/diag-counters.js';
|
|
@@ -3147,19 +3149,11 @@ async function _buildPrefetchBundle(vfs, scriptPath, cwd, entryCode, esbuild, bu
|
|
|
3147
3149
|
};
|
|
3148
3150
|
}
|
|
3149
3151
|
const ROUTEABLE_PORT_ATTACH_TIMEOUT_MS = 1_000;
|
|
3150
|
-
/**
|
|
3151
|
-
*
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
* back to copy-on-write — which rewrites every chunk of the file, per slice,
|
|
3156
|
-
* making a sliced write quadratic in its size. A whole number of chunks is
|
|
3157
|
-
* the other half of that: a slice that ends mid-chunk makes the next one read
|
|
3158
|
-
* the partial chunk back to complete it.
|
|
3159
|
-
*/
|
|
3160
|
-
const FACET_IMAGE_WRITE_SLICE_BYTES = Math.floor(MAX_TX_BLOB_BYTES / CHUNK_SIZE) * CHUNK_SIZE;
|
|
3161
|
-
/** A launch is re-driven once. A reset that recurs is not the transient one. */
|
|
3162
|
-
const RESIDENT_LAUNCH_MAX_ATTEMPT = 1;
|
|
3152
|
+
/** 'running' is the measured common case: the platform's reset strikes
|
|
3153
|
+
* seconds after a launch settles, while the resident runs. */
|
|
3154
|
+
function residentLaunchDoing(record) {
|
|
3155
|
+
return record.phase === 'running' ? 'running' : 'starting';
|
|
3156
|
+
}
|
|
3163
3157
|
export class FacetManager {
|
|
3164
3158
|
ctx;
|
|
3165
3159
|
env;
|
|
@@ -3183,29 +3177,25 @@ export class FacetManager {
|
|
|
3183
3177
|
/** NIMBUS_DEBUG=1: placement diagnostics into the process log store. */
|
|
3184
3178
|
debugEnabled = false;
|
|
3185
3179
|
processRpcResources = new Map();
|
|
3186
|
-
/** pid → the boot images its facet loads from; the image sweep's root set. */
|
|
3187
|
-
residentImages = new Map();
|
|
3188
3180
|
/**
|
|
3189
|
-
*
|
|
3190
|
-
*
|
|
3191
|
-
*
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3195
|
-
*
|
|
3196
|
-
*
|
|
3181
|
+
* The content-addressed boot-image store (fabric's facet-image-store.ts),
|
|
3182
|
+
* writing through this session's kernel-credentialed VFS and rooted off the
|
|
3183
|
+
* live process table.
|
|
3184
|
+
*/
|
|
3185
|
+
imageStore = new FacetImageStore(() => this._imageBlobs(), (pid) => this.processes.get(pid)?.state === 'running');
|
|
3186
|
+
/**
|
|
3187
|
+
* The resident-launch journal (fabric's launch-journal.ts): the durable
|
|
3188
|
+
* record of every resident this session owes the user, and its recovery
|
|
3189
|
+
* after an instance reset. This manager supplies what a launch IS — the
|
|
3190
|
+
* inputs `_spawnResident` re-drives from — and how its loss is reported.
|
|
3197
3191
|
*/
|
|
3198
|
-
|
|
3199
|
-
/** Whether this instance has already read the journal a reset leaves behind. */
|
|
3200
|
-
launchesRecovered = false;
|
|
3192
|
+
launchJournal;
|
|
3201
3193
|
/**
|
|
3202
|
-
*
|
|
3203
|
-
*
|
|
3204
|
-
*
|
|
3205
|
-
* correct: rows from a previous instance are recovery's to consume, never
|
|
3206
|
-
* this hook's.
|
|
3194
|
+
* The granting side of the launch pacer (fabric's launch-pacer.ts). The
|
|
3195
|
+
* session's alarm re-enters the object through `pumpResidentLaunches`;
|
|
3196
|
+
* journal recovery rides the first pump.
|
|
3207
3197
|
*/
|
|
3208
|
-
|
|
3198
|
+
launchPump;
|
|
3209
3199
|
timedOutProcessIds = new Set();
|
|
3210
3200
|
// attach-pid → serve-pid: the resident serve facet a bare-`opencode` dual
|
|
3211
3201
|
// spawn created as an OS-child of the attach TUI. When the attach process
|
|
@@ -3276,42 +3266,62 @@ export class FacetManager {
|
|
|
3276
3266
|
? Reflect.get(env, 'NIMBUS_DEBUG')
|
|
3277
3267
|
: undefined;
|
|
3278
3268
|
this.debugEnabled = debugVar === '1' || debugVar === 'true';
|
|
3269
|
+
this.launchJournal = new ResidentLaunchJournal(ctx.storage, {
|
|
3270
|
+
generationBase: () => this.processes.pidBase,
|
|
3271
|
+
waitUntil: (promise) => this.ctx.waitUntil(promise),
|
|
3272
|
+
redrive: (record, attempt) => this._spawnResident(record.code, record.opts, attempt),
|
|
3273
|
+
onRedrive: (record) => this.hooks.notify?.('\x1b[2m[nimbus: the session restarted while '
|
|
3274
|
+
+ `"${record.command}" was ${residentLaunchDoing(record)} — restarting it]\x1b[0m\r\n`),
|
|
3275
|
+
onAbandoned: (record) => this.hooks.notify?.('\x1b[2m[nimbus: the session restarted again while '
|
|
3276
|
+
+ `"${record.command}" was ${residentLaunchDoing(record)} — leaving it stopped]\x1b[0m\r\n`),
|
|
3277
|
+
onRedriveFailed: (record, e) => this.hooks.notify?.(`\x1b[2m[nimbus: "${record.command}" could not be restarted: `
|
|
3278
|
+
+ `${errorMessage(e)}]\x1b[0m\r\n`),
|
|
3279
|
+
});
|
|
3280
|
+
this.launchPump = new LaunchTurnPump({
|
|
3281
|
+
requestTurn: hooks.requestLaunchTurn?.bind(hooks),
|
|
3282
|
+
recover: () => this.launchJournal.recoverInterrupted(),
|
|
3283
|
+
});
|
|
3279
3284
|
// The journal row of a resident lives for the PROCESS's lifetime, so its
|
|
3280
3285
|
// release belongs on the one seam every end-of-life passes through —
|
|
3281
3286
|
// exit, kill, self-reported exit and timeout abort all mark the table.
|
|
3282
3287
|
// Rooted on waitUntil: the hook fires synchronously inside whatever turn
|
|
3283
3288
|
// ended the process, and the delete must not be a floating promise there.
|
|
3284
3289
|
this.processes.setOnTerminal((pid) => {
|
|
3285
|
-
this.ctx.waitUntil(this.
|
|
3290
|
+
this.ctx.waitUntil(this.launchJournal.release(pid));
|
|
3286
3291
|
});
|
|
3287
3292
|
}
|
|
3288
3293
|
setVfs(vfs) { this.vfs = vfs; }
|
|
3289
3294
|
/**
|
|
3290
|
-
* The
|
|
3291
|
-
*
|
|
3292
|
-
*
|
|
3293
|
-
*
|
|
3294
|
-
* built from: the root listing gained an entry the moment an image landed,
|
|
3295
|
-
* so the next spawn of an identical program generated different text and
|
|
3296
|
-
* addressed a different image. Existing before the first walk makes it
|
|
3297
|
-
* stable.
|
|
3298
|
-
*
|
|
3299
|
-
* Sited on the exec path and not in setVfs, because setVfs runs while the
|
|
3300
|
-
* Durable Object is coming up — including on every wake — and a synchronous
|
|
3301
|
-
* filesystem write there costs the session its startup. Measured: a
|
|
3302
|
-
* throwaway built that way stopped accepting terminal connections at all,
|
|
3303
|
-
* while the same build without it served them.
|
|
3295
|
+
* The env/ctx pair every loader-backed runtime builds its facet pools
|
|
3296
|
+
* from. A pool is constructed from exactly these two, so the manager
|
|
3297
|
+
* exposes them as one narrow accessor rather than every runtime reaching
|
|
3298
|
+
* into its private fields.
|
|
3304
3299
|
*/
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3300
|
+
loaderHost() {
|
|
3301
|
+
return { env: this.env, ctx: this.ctx };
|
|
3302
|
+
}
|
|
3303
|
+
/**
|
|
3304
|
+
* The image store's disk: this session's VFS, as the kernel — the store is
|
|
3305
|
+
* written by the kernel and read by processes through supervisor bindings
|
|
3306
|
+
* that enforce their own credential. Mode 0644 at creation, as POSIX has
|
|
3307
|
+
* it, is what makes the read succeed for any process by construction; the
|
|
3308
|
+
* store itself decides nothing about modes.
|
|
3309
|
+
*/
|
|
3310
|
+
_imageBlobs() {
|
|
3311
|
+
const vfs = this.vfs;
|
|
3312
|
+
if (!vfs) {
|
|
3313
|
+
throw new Error('Nimbus: a resident process needs a session filesystem to materialize its boot image');
|
|
3311
3314
|
}
|
|
3312
|
-
|
|
3315
|
+
const fs = vfs.as(CRED_KERNEL);
|
|
3316
|
+
return {
|
|
3317
|
+
mkdirp: (dir) => fs.mkdir(dir, { recursive: true, mode: 0o755 }),
|
|
3318
|
+
sizeOf: (path) => (fs.exists(path) ? fs.lstat(path).size : null),
|
|
3319
|
+
writeFile: (path, bytes) => fs.writeFile(path, bytes, { mode: 0o644 }),
|
|
3320
|
+
writeRange: (path, offset, bytes) => fs.writeRange(path, offset, bytes),
|
|
3321
|
+
list: (dir) => fs.readdir(dir).map((entry) => entry.name),
|
|
3322
|
+
unlink: (path) => fs.unlink(path),
|
|
3323
|
+
};
|
|
3313
3324
|
}
|
|
3314
|
-
imageStoreDirReady = false;
|
|
3315
3325
|
/**
|
|
3316
3326
|
* W3.5 Fix B: hand the FacetManager a pre-warmed EsbuildService for
|
|
3317
3327
|
* the ESM→CJS bundle pre-pass. NimbusSession already lazy-creates one
|
|
@@ -3619,7 +3629,7 @@ export class FacetManager {
|
|
|
3619
3629
|
const diagOn = isExecDiagEnabled();
|
|
3620
3630
|
const __bundleStart = diagOn ? Date.now() : 0;
|
|
3621
3631
|
if (this.vfs)
|
|
3622
|
-
this.
|
|
3632
|
+
this.imageStore.ensureDir();
|
|
3623
3633
|
const processVfs = this.vfs?.as(entry.cred);
|
|
3624
3634
|
const credKey = `${entry.cred.uid}:${entry.cred.gid}:${entry.cred.groups.join(',')}`;
|
|
3625
3635
|
const vfsState = processVfs
|
|
@@ -3929,7 +3939,7 @@ export class FacetManager {
|
|
|
3929
3939
|
// home dirs (~/.local/share/opencode, …) via fs.promises.mkdir; those and
|
|
3930
3940
|
// other writes flush live through the SUPERVISOR RPC bridge.
|
|
3931
3941
|
if (this.vfs)
|
|
3932
|
-
this.
|
|
3942
|
+
this.imageStore.ensureDir();
|
|
3933
3943
|
const processVfs = this.vfs?.as(entry.cred);
|
|
3934
3944
|
const vfsState = processVfs
|
|
3935
3945
|
? await buildPrefetchBundle(processVfs, undefined, opts.cwd, '', this.esbuild || undefined)
|
|
@@ -4138,6 +4148,7 @@ export class FacetManager {
|
|
|
4138
4148
|
.finally(() => {
|
|
4139
4149
|
this.releaseProcessRpcResources(pid);
|
|
4140
4150
|
}));
|
|
4151
|
+
await clearPortCapability({ ctx: this.ctx, portRegistry: this.portRegistry }, port);
|
|
4141
4152
|
this.portRegistry.register(port, pid);
|
|
4142
4153
|
return { pid, exitCode: 0, stdout: '', stderr: '', vfsWrites: {} };
|
|
4143
4154
|
}
|
|
@@ -4174,7 +4185,7 @@ export class FacetManager {
|
|
|
4174
4185
|
* The reader the fabric completes a boot spec's by-path members with.
|
|
4175
4186
|
*
|
|
4176
4187
|
* Reads as CRED_KERNEL because that is who WROTE them: the generated images
|
|
4177
|
-
* are kernel-owned (`
|
|
4188
|
+
* are kernel-owned (`_imageBlobs`) and the runtime wasm images
|
|
4178
4189
|
* are installed by the kernel. Uncached because these are the session's
|
|
4179
4190
|
* largest files — a ruby interpreter image is 34.3 MiB — and pinning one in
|
|
4180
4191
|
* the VFS content LRU for the life of the session is what once crashed the
|
|
@@ -4188,82 +4199,11 @@ export class FacetManager {
|
|
|
4188
4199
|
const fs = vfs.as(CRED_KERNEL);
|
|
4189
4200
|
return { readFile: (path) => fs.readFileUncached(path) };
|
|
4190
4201
|
}
|
|
4191
|
-
/**
|
|
4192
|
-
* Materialize generated module sources in the content-addressed image store
|
|
4193
|
-
* and return the `vfsTextModules` map naming them.
|
|
4194
|
-
*
|
|
4195
|
-
* A resident process's module map is sized by the user's disk, so it does
|
|
4196
|
-
* not ride inside the boot spec — see ResidentCodeSpec.vfsTextModules.
|
|
4197
|
-
* Writing it here, once, is what lets the session stop holding it: after
|
|
4198
|
-
* this returns, the only thing it keeps is a path.
|
|
4199
|
-
*
|
|
4200
|
-
* The store is written by the kernel and read by the process, so nothing
|
|
4201
|
-
* here depends on which credential spawned what. Digest collisions are the
|
|
4202
|
-
* hash's problem; everything else is idempotent — an image already present
|
|
4203
|
-
* at its own digest is already the bytes we were about to write.
|
|
4204
|
-
*/
|
|
4205
|
-
async _materializeFacetImages(pid, modules, pacer) {
|
|
4206
|
-
const vfs = this.vfs;
|
|
4207
|
-
if (!vfs) {
|
|
4208
|
-
throw new Error('Nimbus: a resident process needs a session filesystem to materialize its boot image');
|
|
4209
|
-
}
|
|
4210
|
-
const fs = vfs.as(CRED_KERNEL);
|
|
4211
|
-
const images = {};
|
|
4212
|
-
const sources = new Map();
|
|
4213
|
-
for (const [moduleName, source] of Object.entries(modules)) {
|
|
4214
|
-
const path = facetImagePath(await facetImageDigest(source));
|
|
4215
|
-
images[moduleName] = path;
|
|
4216
|
-
sources.set(path, source);
|
|
4217
|
-
await pacer.spend(source.length);
|
|
4218
|
-
}
|
|
4219
|
-
// Register the WHOLE root set here, in one synchronous step, before any
|
|
4220
|
-
// byte of it exists on disk. That ordering is the entire protocol between
|
|
4221
|
-
// a launch and the sweep: an image is rooted from before it is written, so
|
|
4222
|
-
// a sweep can never observe a file this launch has written but not yet
|
|
4223
|
-
// claimed. The old loop achieved it by not awaiting at all, which read as
|
|
4224
|
-
// "the writes must not be interrupted" — they may be. What must not be
|
|
4225
|
-
// interrupted is the gap between writing and rooting, and rooting first
|
|
4226
|
-
// closes it for every write that follows, however many turns they span.
|
|
4227
|
-
this.residentImages.set(pid, [...sources.keys()]);
|
|
4228
|
-
fs.mkdir(FACET_IMAGE_DIR, { recursive: true, mode: 0o755 });
|
|
4229
|
-
for (const [path, source] of sources) {
|
|
4230
|
-
const stored = path.replace(/^\/+/, '');
|
|
4231
|
-
const bytes = new TextEncoder().encode(source);
|
|
4232
|
-
// An image at its full size is a COMPLETE one: a write only ever grows
|
|
4233
|
-
// the file from offset zero, so a write cut short by a reset leaves a
|
|
4234
|
-
// strictly shorter file and fails this test. Size is enough of a check
|
|
4235
|
-
// because the reader verifies the digest before the loader sees it.
|
|
4236
|
-
if (fs.exists(stored) && fs.lstat(stored).size === bytes.byteLength) {
|
|
4237
|
-
await pacer.spend(bytes.byteLength);
|
|
4238
|
-
continue;
|
|
4239
|
-
}
|
|
4240
|
-
// Sliced because the platform resets the object over what ONE TURN has
|
|
4241
|
-
// outstanding, not over what it eventually writes — pi's 22.9 MB map
|
|
4242
|
-
// went in as a single write and took the session down with it ~25% of
|
|
4243
|
-
// the time. Spending between slices is what puts the rest of the image
|
|
4244
|
-
// on later turns; the slice bound is what keeps any one of them small.
|
|
4245
|
-
let offset = 0;
|
|
4246
|
-
do {
|
|
4247
|
-
const slice = bytes.subarray(offset, offset + FACET_IMAGE_WRITE_SLICE_BYTES);
|
|
4248
|
-
// The first slice REPLACES the file, so an interrupted write's remains
|
|
4249
|
-
// are truncated to a known length rather than left as a tail past this
|
|
4250
|
-
// content — and the mode is chosen here, at creation, as POSIX has it.
|
|
4251
|
-
if (offset === 0)
|
|
4252
|
-
fs.writeFile(stored, slice, { mode: 0o644 });
|
|
4253
|
-
else
|
|
4254
|
-
fs.writeRange(stored, offset, slice);
|
|
4255
|
-
offset += slice.byteLength;
|
|
4256
|
-
await pacer.spend(slice.byteLength);
|
|
4257
|
-
} while (offset < bytes.byteLength);
|
|
4258
|
-
}
|
|
4259
|
-
this._sweepFacetImages(fs);
|
|
4260
|
-
return images;
|
|
4261
|
-
}
|
|
4262
4202
|
/**
|
|
4263
4203
|
* Fail a paced launch whose process went away while it was suspended.
|
|
4264
4204
|
*
|
|
4265
|
-
* Between turns anything may happen to the process — a kill, a reap,
|
|
4266
|
-
*
|
|
4205
|
+
* Between turns anything may happen to the process — a kill, a reap, an
|
|
4206
|
+
* image sweep that has already unrooted its images. Continuing would
|
|
4267
4207
|
* spend further turns building a facet for a pid nothing will ever attach
|
|
4268
4208
|
* to, and would write image files the next sweep immediately collects.
|
|
4269
4209
|
*/
|
|
@@ -4272,44 +4212,6 @@ export class FacetManager {
|
|
|
4272
4212
|
return;
|
|
4273
4213
|
throw new Error(`Nimbus: resident launch for pid ${pid} was cancelled while it was suspended`);
|
|
4274
4214
|
}
|
|
4275
|
-
/**
|
|
4276
|
-
* Drop every image no running process boots from.
|
|
4277
|
-
*
|
|
4278
|
-
* Content addressing means a changed program writes a NEW image rather than
|
|
4279
|
-
* replacing one, so a watch loop — or simply a session that runs a few
|
|
4280
|
-
* different programs — would otherwise leave one bundle-sized file behind
|
|
4281
|
-
* per distinct version. The root set is the process table, which is exact:
|
|
4282
|
-
* an image is live for precisely as long as the process that boots from it.
|
|
4283
|
-
* Nothing is left for a TTL or an eviction heuristic to guess at, and after
|
|
4284
|
-
* a DO reset the table is empty so every orphan goes.
|
|
4285
|
-
*/
|
|
4286
|
-
_sweepFacetImages(fs) {
|
|
4287
|
-
const live = new Set();
|
|
4288
|
-
for (const [pid, paths] of this.residentImages) {
|
|
4289
|
-
if (this.processes.get(pid)?.state === 'running') {
|
|
4290
|
-
for (const path of paths)
|
|
4291
|
-
live.add(path);
|
|
4292
|
-
}
|
|
4293
|
-
else {
|
|
4294
|
-
this.residentImages.delete(pid);
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
let entries;
|
|
4298
|
-
try {
|
|
4299
|
-
entries = fs.readdir(FACET_IMAGE_DIR);
|
|
4300
|
-
}
|
|
4301
|
-
catch {
|
|
4302
|
-
return;
|
|
4303
|
-
}
|
|
4304
|
-
for (const entry of entries) {
|
|
4305
|
-
if (live.has(`/${FACET_IMAGE_DIR}/${entry.name}`))
|
|
4306
|
-
continue;
|
|
4307
|
-
try {
|
|
4308
|
-
fs.unlink(`${FACET_IMAGE_DIR}/${entry.name}`);
|
|
4309
|
-
}
|
|
4310
|
-
catch { /* already gone */ }
|
|
4311
|
-
}
|
|
4312
|
-
}
|
|
4313
4215
|
/**
|
|
4314
4216
|
* The one way this manager boots a resident process. Every resident process
|
|
4315
4217
|
* is a facet of this session; there is nothing to place and nothing here
|
|
@@ -4340,125 +4242,16 @@ export class FacetManager {
|
|
|
4340
4242
|
this.vfs?.activateAppendWriter(pid, writerId);
|
|
4341
4243
|
}
|
|
4342
4244
|
/**
|
|
4343
|
-
*
|
|
4344
|
-
*
|
|
4345
|
-
*
|
|
4346
|
-
* is genuinely a new invocation — the session's alarm. Without such a host
|
|
4347
|
-
* there is no fresh turn to be had, and the launch continues on this one
|
|
4348
|
-
* rather than hanging: that is exactly the single-turn launch this path has
|
|
4349
|
-
* always performed, so a harness or a runtime without alarms loses the
|
|
4350
|
-
* responsiveness but keeps the behaviour.
|
|
4351
|
-
*/
|
|
4352
|
-
launchScheduler = {
|
|
4353
|
-
nextTurn: (chunkEnded) => new Promise((resume) => {
|
|
4354
|
-
this.launchWaiters.push({ resume, chunkEnded });
|
|
4355
|
-
if (this.hooks.requestLaunchTurn) {
|
|
4356
|
-
this.hooks.requestLaunchTurn();
|
|
4357
|
-
return;
|
|
4358
|
-
}
|
|
4359
|
-
setTimeout(() => { void this.pumpResidentLaunches(); }, 0);
|
|
4360
|
-
}),
|
|
4361
|
-
};
|
|
4362
|
-
/**
|
|
4363
|
-
* Run one chunk of every launch waiting for a turn.
|
|
4364
|
-
*
|
|
4365
|
-
* Awaits the chunk each resumed launch then performs, so the invocation that
|
|
4366
|
-
* granted the turn is the invocation that pays for the work — rather than
|
|
4367
|
-
* releasing it into a handler's microtask drain, where nothing owns it and
|
|
4368
|
-
* the runtime may tear the context down mid-chunk.
|
|
4369
|
-
*/
|
|
4370
|
-
async pumpResidentLaunches() {
|
|
4371
|
-
await this._recoverInterruptedLaunches();
|
|
4372
|
-
const waiting = this.launchWaiters;
|
|
4373
|
-
if (waiting.length === 0)
|
|
4374
|
-
return;
|
|
4375
|
-
this.launchWaiters = [];
|
|
4376
|
-
for (const waiter of waiting)
|
|
4377
|
-
waiter.resume();
|
|
4378
|
-
await Promise.all(waiting.map((waiter) => waiter.chunkEnded));
|
|
4379
|
-
}
|
|
4380
|
-
/**
|
|
4381
|
-
* Re-drive the launches a previous instance was building when it was reset.
|
|
4382
|
-
*
|
|
4383
|
-
* The platform resets a session Durable Object over what one turn has
|
|
4384
|
-
* outstanding in storage ("Internal error in Durable Object storage caused
|
|
4385
|
-
* object to be reset"), and a launch is the largest writer this session has.
|
|
4386
|
-
* Everything the launch held was in memory, so the process it was building
|
|
4387
|
-
* and the terminal watching it both went with the instance, and until now
|
|
4388
|
-
* the user was told nothing at all.
|
|
4389
|
-
*
|
|
4390
|
-
* Sited on the pump because the pump is what an alarm calls, and a launch
|
|
4391
|
-
* that was suspended has an alarm armed for it — a reset during a chunk
|
|
4392
|
-
* fails that alarm, and the platform re-delivers it to the instance that
|
|
4393
|
-
* replaces this one. So the first turn after a reset is already this one.
|
|
4394
|
-
*
|
|
4395
|
-
* Runs once per instance: the journal only changes when a launch of THIS
|
|
4396
|
-
* instance starts or settles, and those are rows this instance wrote.
|
|
4397
|
-
*/
|
|
4398
|
-
async _recoverInterruptedLaunches() {
|
|
4399
|
-
if (this.launchesRecovered)
|
|
4400
|
-
return;
|
|
4401
|
-
this.launchesRecovered = true;
|
|
4402
|
-
const journal = await this.ctx.storage.list({
|
|
4403
|
-
prefix: RESIDENT_LAUNCH_KEY_PREFIX,
|
|
4404
|
-
});
|
|
4405
|
-
for (const [key, record] of journal) {
|
|
4406
|
-
// A pid at or below this instance's base was allocated by a PREVIOUS one
|
|
4407
|
-
// (process-table.ts, PID_GEN_STRIDE), so its launch never finished; above
|
|
4408
|
-
// the base is this instance's own, still running. Same predicate as
|
|
4409
|
-
// `session/rpc.ts` uses to attribute a prior generation's pid.
|
|
4410
|
-
if (!(record.pid > 0 && record.pid <= this.processes.pidBase))
|
|
4411
|
-
continue;
|
|
4412
|
-
await this.ctx.storage.delete(key);
|
|
4413
|
-
// 'running' is the measured common case: the platform's reset strikes
|
|
4414
|
-
// seconds after a launch settles, while the resident runs.
|
|
4415
|
-
const doing = record.phase === 'running' ? 'running' : 'starting';
|
|
4416
|
-
if (record.attempt >= RESIDENT_LAUNCH_MAX_ATTEMPT) {
|
|
4417
|
-
this.hooks.notify?.('\x1b[2m[nimbus: the session restarted again while '
|
|
4418
|
-
+ `"${record.command}" was ${doing} — leaving it stopped]\x1b[0m\r\n`);
|
|
4419
|
-
continue;
|
|
4420
|
-
}
|
|
4421
|
-
this.hooks.notify?.('\x1b[2m[nimbus: the session restarted while '
|
|
4422
|
-
+ `"${record.command}" was ${doing} — restarting it]\x1b[0m\r\n`);
|
|
4423
|
-
// Not awaited: this call is running inside the alarm that granted the
|
|
4424
|
-
// turn, and the launch it starts asks for turns of its own through that
|
|
4425
|
-
// same alarm — awaiting it here would be waiting on an alarm that cannot
|
|
4426
|
-
// be scheduled until this one returns.
|
|
4427
|
-
this.ctx.waitUntil(this._spawnResident(record.code, record.opts, record.attempt + 1)
|
|
4428
|
-
.catch((e) => {
|
|
4429
|
-
this.hooks.notify?.(`\x1b[2m[nimbus: "${record.command}" could not be restarted: `
|
|
4430
|
-
+ `${errorMessage(e)}]\x1b[0m\r\n`);
|
|
4431
|
-
}));
|
|
4432
|
-
}
|
|
4433
|
-
}
|
|
4434
|
-
/**
|
|
4435
|
-
* Record a launch as in flight, so an instance that replaces this one knows
|
|
4436
|
-
* it never finished. Best-effort: a launch that cannot be journalled still
|
|
4437
|
-
* runs, and a reset then costs exactly what it cost before the journal.
|
|
4438
|
-
*
|
|
4439
|
-
* Synced, not merely put: `await put()` resolves before durability, and the
|
|
4440
|
-
* reset this journal exists for destroys every write its turn still had
|
|
4441
|
-
* outstanding — measured live, a launch killed in its first chunks left NO
|
|
4442
|
-
* row for the replacement instance to find, which is how the recovery this
|
|
4443
|
-
* feeds sat inert while its own test stayed green. `sync()` is the storage
|
|
4444
|
-
* layer's durability barrier: the row is on disk before the launch performs
|
|
4445
|
-
* its first byte of real work. What remains is a reset between the put and
|
|
4446
|
-
* the sync's completion — and a launch that dies there has not started, so
|
|
4447
|
-
* losing its row costs a retype, not a recovery.
|
|
4245
|
+
* Grant every suspended launch a chunk of this turn — the session's alarm
|
|
4246
|
+
* calls this, and journal recovery rides the first pump. See fabric's
|
|
4247
|
+
* `LaunchTurnPump.pump` for the ownership argument.
|
|
4448
4248
|
*/
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
this.journalledPids.add(record.pid);
|
|
4452
|
-
await this.ctx.storage.put(`${RESIDENT_LAUNCH_KEY_PREFIX}${record.pid}`, record);
|
|
4453
|
-
await this.ctx.storage.sync();
|
|
4454
|
-
}
|
|
4455
|
-
catch (e) {
|
|
4456
|
-
console.warn('[nimbus] resident launch journal write failed:', errorMessage(e));
|
|
4457
|
-
}
|
|
4249
|
+
pumpResidentLaunches() {
|
|
4250
|
+
return this.launchPump.pump();
|
|
4458
4251
|
}
|
|
4459
4252
|
/** Whether any launch is suspended waiting for a turn. */
|
|
4460
4253
|
get hasPendingLaunchTurns() {
|
|
4461
|
-
return this.
|
|
4254
|
+
return this.launchPump.hasPending;
|
|
4462
4255
|
}
|
|
4463
4256
|
/** Allocate a free loopback port for a resident server facet (from 4096 up). */
|
|
4464
4257
|
_allocateLoopbackPort() {
|
|
@@ -4695,7 +4488,7 @@ export class FacetManager {
|
|
|
4695
4488
|
*/
|
|
4696
4489
|
async _runResidentLaunch(entry, code, command, opts, attempt) {
|
|
4697
4490
|
const cwd = opts.cwd || '/home/user';
|
|
4698
|
-
const pacer = new LaunchPacer(this.
|
|
4491
|
+
const pacer = new LaunchPacer(this.launchPump, launchChunkMaxBytes(this.env), () => this._assertLaunchStillOwned(entry.pid));
|
|
4699
4492
|
// Journalled before the first byte of work. A launch that FAILS deletes
|
|
4700
4493
|
// its row on the way out — its process has already been exited and the
|
|
4701
4494
|
// user notified, so there is nothing left to owe. A launch that SETTLES
|
|
@@ -4713,12 +4506,12 @@ export class FacetManager {
|
|
|
4713
4506
|
attempt,
|
|
4714
4507
|
phase: 'starting',
|
|
4715
4508
|
};
|
|
4716
|
-
await this.
|
|
4509
|
+
await this.launchJournal.journal(record);
|
|
4717
4510
|
try {
|
|
4718
4511
|
await this._residentLaunchBody(entry, code, command, cwd, opts, pacer);
|
|
4719
4512
|
}
|
|
4720
4513
|
catch (e) {
|
|
4721
|
-
await this.
|
|
4514
|
+
await this.launchJournal.release(entry.pid);
|
|
4722
4515
|
throw e;
|
|
4723
4516
|
}
|
|
4724
4517
|
finally {
|
|
@@ -4728,24 +4521,8 @@ export class FacetManager {
|
|
|
4728
4521
|
// its running life with a fresh re-drive budget. If the process already
|
|
4729
4522
|
// ended inside the launch body's own settlement, the terminal hook has
|
|
4730
4523
|
// released the row — do not write it back.
|
|
4731
|
-
if (this.
|
|
4732
|
-
await this.
|
|
4733
|
-
}
|
|
4734
|
-
}
|
|
4735
|
-
/**
|
|
4736
|
-
* The journal row's one release: the process is over, nothing is owed.
|
|
4737
|
-
* Synced so an instance reset moments later cannot roll the delete back and
|
|
4738
|
-
* resurrect a process the user watched end.
|
|
4739
|
-
*/
|
|
4740
|
-
async _releaseResidentJournal(pid) {
|
|
4741
|
-
if (!this.journalledPids.delete(pid))
|
|
4742
|
-
return;
|
|
4743
|
-
try {
|
|
4744
|
-
await this.ctx.storage.delete(`${RESIDENT_LAUNCH_KEY_PREFIX}${pid}`);
|
|
4745
|
-
await this.ctx.storage.sync();
|
|
4746
|
-
}
|
|
4747
|
-
catch (e) {
|
|
4748
|
-
console.warn('[nimbus] resident launch journal delete failed:', errorMessage(e));
|
|
4524
|
+
if (this.launchJournal.has(entry.pid)) {
|
|
4525
|
+
await this.launchJournal.journal({ ...record, attempt: 0, phase: 'running' });
|
|
4749
4526
|
}
|
|
4750
4527
|
}
|
|
4751
4528
|
async _residentLaunchBody(entry, code, command, cwd, opts, pacer) {
|
|
@@ -4758,7 +4535,7 @@ export class FacetManager {
|
|
|
4758
4535
|
}
|
|
4759
4536
|
}
|
|
4760
4537
|
if (this.vfs)
|
|
4761
|
-
this.
|
|
4538
|
+
this.imageStore.ensureDir();
|
|
4762
4539
|
const diagOn = isExecDiagEnabled();
|
|
4763
4540
|
const __bundleStart = diagOn ? Date.now() : 0;
|
|
4764
4541
|
const processVfs = this.vfs?.as(entry.cred);
|
|
@@ -4803,7 +4580,7 @@ export class FacetManager {
|
|
|
4803
4580
|
let handle;
|
|
4804
4581
|
let resourcesTracked = false;
|
|
4805
4582
|
try {
|
|
4806
|
-
const vfsTextModules = await this.
|
|
4583
|
+
const vfsTextModules = await this.imageStore.materialize(entry.pid, {
|
|
4807
4584
|
'worker.js': generatedWorker.code,
|
|
4808
4585
|
...generatedWorker.modules,
|
|
4809
4586
|
}, pacer);
|
|
@@ -4893,6 +4670,9 @@ export class FacetManager {
|
|
|
4893
4670
|
await handle.booted();
|
|
4894
4671
|
}
|
|
4895
4672
|
if (opts.port && opts.port > 0 && opts.port < 65536) {
|
|
4673
|
+
// A new process on a port retires the previous occupant's preview
|
|
4674
|
+
// capability, so a URL handed out for that one cannot reach this one.
|
|
4675
|
+
await clearPortCapability({ ctx: this.ctx, portRegistry: this.portRegistry }, opts.port);
|
|
4896
4676
|
this.portRegistry.register(opts.port, entry.pid);
|
|
4897
4677
|
}
|
|
4898
4678
|
}
|
|
@@ -4960,6 +4740,9 @@ export class FacetManager {
|
|
|
4960
4740
|
resourcesTracked = true;
|
|
4961
4741
|
this.portRegistry.bindFacetStub(entry.pid, handle.routeTarget);
|
|
4962
4742
|
if (opts.port && opts.port > 0 && opts.port < 65536) {
|
|
4743
|
+
// A new process on a port retires the previous occupant's preview
|
|
4744
|
+
// capability, so a URL handed out for that one cannot reach this one.
|
|
4745
|
+
await clearPortCapability({ ctx: this.ctx, portRegistry: this.portRegistry }, opts.port);
|
|
4963
4746
|
this.portRegistry.register(opts.port, entry.pid);
|
|
4964
4747
|
}
|
|
4965
4748
|
return { pid: entry.pid, boot: await handle.booted() };
|
|
@@ -4980,8 +4763,9 @@ export class FacetManager {
|
|
|
4980
4763
|
throw e;
|
|
4981
4764
|
}
|
|
4982
4765
|
}
|
|
4983
|
-
registerPort(pid, port) {
|
|
4766
|
+
async registerPort(pid, port) {
|
|
4984
4767
|
if (port > 0 && port < 65536) {
|
|
4768
|
+
await clearPortCapability({ ctx: this.ctx, portRegistry: this.portRegistry }, port);
|
|
4985
4769
|
this.portRegistry.register(port, pid);
|
|
4986
4770
|
}
|
|
4987
4771
|
}
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* place; the L2 asset cache makes refetches cheap).
|
|
24
24
|
*/
|
|
25
25
|
import { z } from 'zod/v4';
|
|
26
|
-
import type { WorkerCode } from '
|
|
26
|
+
import type { WorkerCode } from '@nimbus-sh/fabric/vendor/types.js';
|
|
27
27
|
export interface OpencodeAssetsEnv {
|
|
28
28
|
ASSETS: {
|
|
29
29
|
fetch(req: Request): Promise<Response>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opencode-staging.d.ts","sourceRoot":"","sources":["../../src/facets/opencode-staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAwB3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"opencode-staging.d.ts","sourceRoot":"","sources":["../../src/facets/opencode-staging.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAwB3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAIpE,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QAAE,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;CACpD;AAED;;;;GAIG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;;iBAwBlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAoCxE,iFAAiF;AACjF,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,EAC/B,UAAU,EAAE,OAAO,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC,CAMhD;AAuFD;;;;GAIG;AACH,wBAAsB,2BAA2B,CAC/C,GAAG,EAAE,OAAO,CAAC,iBAAiB,CAAC,EAC/B,SAAS,EAAE,OAAO,GACjB,OAAO,CAAC,UAAU,CAAC,CAyDrB"}
|
|
@@ -30,6 +30,7 @@ import { fetchOpenTUIWasmBytes } from '../runtime/opentui-wasm-bytes.js';
|
|
|
30
30
|
import { OPENTUI_WASM_MODULE_NAME } from '../runtime/opentui-facet-backend.js';
|
|
31
31
|
import { OPENCODE_CHUNKS_PACK, OPENCODE_TREE_SITTER_WASMS, OPENCODE_TUI_WORKERS, OPENCODE_YOGA_WASM, } from '../opencode-artifact.generated.js';
|
|
32
32
|
import { generateOpencodeRunnerCode, opencodeBuiltinBridgeModules, OPENCODE_BUNDLE_MODULE_NAME, SQLITE_WASM_MODULE_NAME, YOGA_WASM_MODULE_NAME, } from '../runtime/opencode-facet-runner.js';
|
|
33
|
+
import { setStagedBootAssembler } from '@nimbus-sh/fabric/process-fabric.js';
|
|
33
34
|
import { CF_COMPAT_DATE } from '@nimbus-sh/core/constants.js';
|
|
34
35
|
/**
|
|
35
36
|
* Everything a facet spawn needs beyond the artifact sources themselves.
|
|
@@ -145,6 +146,9 @@ function chunkModuleEntries(env) {
|
|
|
145
146
|
}
|
|
146
147
|
return chunkEntriesInflight;
|
|
147
148
|
}
|
|
149
|
+
// A 'staged' boot spec reaching the fabric assembles through this. Module
|
|
150
|
+
// scope so every isolate that can host a staged facet has it before first use.
|
|
151
|
+
setStagedBootAssembler((env, stage) => assembleOpencodeFacetConfig(env, stage));
|
|
148
152
|
/**
|
|
149
153
|
* Assemble the full Worker Loader config for an opencode facet from a stage
|
|
150
154
|
* spec. Returns the config WITHOUT the SUPERVISOR env binding — the caller
|
|
@@ -50,7 +50,7 @@ export interface ViteDevServerOptions {
|
|
|
50
50
|
/**
|
|
51
51
|
* Worker bindings env. Required for the on-demand-bundle facet path
|
|
52
52
|
* (LOADER + ctx-exports). When provided, /preview/@modules/<spec>
|
|
53
|
-
* misses bundle in a
|
|
53
|
+
* misses bundle in a LoaderPool isolate instead of the
|
|
54
54
|
* supervisor's EsbuildService — same architecture as the
|
|
55
55
|
* pre-bundle path. Without this option, the supervisor falls back
|
|
56
56
|
* to in-process esbuild (legacy behaviour).
|
|
@@ -180,7 +180,7 @@ export declare class ViteDevServer {
|
|
|
180
180
|
private logSink;
|
|
181
181
|
constructor(opts: ViteDevServerOptions);
|
|
182
182
|
/**
|
|
183
|
-
* Lazily construct the
|
|
183
|
+
* Lazily construct the LoaderPool used for on-demand bundling
|
|
184
184
|
* of /preview/@modules/<spec> requests that miss both the in-memory
|
|
185
185
|
* and pkg_esm_bundles caches. Mirrors the pre-bundle pool's
|
|
186
186
|
* configuration: 1 worker, internal pLimit not needed (one bundle
|