@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
|
@@ -8,17 +8,15 @@
|
|
|
8
8
|
import { staticStdinReader } from '@nimbus-sh/core/shell/stdin-adapter.js';
|
|
9
9
|
import { DurableObject as CloudflareDurableObject } from 'cloudflare:workers';
|
|
10
10
|
import { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import { FacetProcessManager } from '../facets/process.js';
|
|
11
|
+
import { composeFacetManager } from '../facets/compose.js';
|
|
12
|
+
import { FacetProcessManager, textBytes } from '../facets/process.js';
|
|
14
13
|
import { ChildProcessSpawnPool } from '../loaders/child-process/spawn-pool.js';
|
|
15
14
|
import { SessionProcessSupervisor } from '@nimbus-sh/core/runtime/session-process-supervisor.js';
|
|
16
15
|
import { PID_GEN_STRIDE } from '@nimbus-sh/core/runtime/process-table.js';
|
|
17
16
|
import { CRED_KERNEL, CRED_SESSION_USER, } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
18
17
|
import { PortRegistry } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
18
|
+
import { EsbuildBundlePool } from '../facets/esbuild-bundle-pool.js';
|
|
19
19
|
import { EsbuildService } from '@nimbus-sh/core/runtime/esbuild-service.js';
|
|
20
|
-
import { ESBUILD_TRANSFORM_WORKER_ID, esbuildTransformWorkerCode, } from '../facets/esbuild-transform.js';
|
|
21
|
-
import { fetchEsbuildWasmBytes } from '../runtime/esbuild-wasm-bytes.js';
|
|
22
20
|
import { registerAllocObserver } from '@nimbus-sh/platform/heavy-alloc-coord.js';
|
|
23
21
|
// S10: oom-discriminator helpers (recordFailure, getFailures,
|
|
24
22
|
// getLastRpcFrame, getLastFacetId, snapshotForStorage, rehydrateFromStorage)
|
|
@@ -35,7 +33,7 @@ import { registerAllocObserver } from '@nimbus-sh/platform/heavy-alloc-coord.js'
|
|
|
35
33
|
import { adoptCtxExports } from '@nimbus-sh/fabric/composition.js';
|
|
36
34
|
import { NIMBUS_VERSION, DEFAULT_MOUNT_POINTS, CF_COMPAT_DATE } from '@nimbus-sh/core/constants.js';
|
|
37
35
|
import { seedBaseFilesystem } from '@nimbus-sh/core/workspace';
|
|
38
|
-
import { seedProject } from '@nimbus-sh/core/vfs/seed-project.js';
|
|
36
|
+
import { seedProject, SEED_PROJECT_NAME } from '@nimbus-sh/core/vfs/seed-project.js';
|
|
39
37
|
import { BASE_PATH_HEADER } from '../_shared/session-router.js';
|
|
40
38
|
import { ATTACH_BOOTSTRAP_JTI_KEY_PREFIX, SESSION_DESTROYED_KEY } from './keys.js';
|
|
41
39
|
import { appendScrollback } from './state-store.js';
|
|
@@ -56,13 +54,13 @@ import { initSession as _w11InitSession } from './init.js';
|
|
|
56
54
|
import { wsMessage as _wsDoMessage, wsClose as _wsDoClose, wsError as _wsDoError, safePersistRing as _wsDoSafePersistRing, } from './ws.js';
|
|
57
55
|
// S8: Supervisor RPC + W8 cp* + legacy VFS impls extracted.
|
|
58
56
|
import * as _rpc from './rpc.js';
|
|
59
|
-
import {
|
|
60
|
-
import { processHostFor } from '../loaders/process-host.js';
|
|
57
|
+
import { buildSessionSupervisorOps } from './supervisor-op.js';
|
|
61
58
|
// The supervisor terminates a facet's outbound sockets so inbound frames
|
|
62
59
|
// arrive as supervisor replies (VFS coherence witness 3).
|
|
63
60
|
import { WebSocketRelay } from './ws-relay.js';
|
|
64
61
|
// S9: HTTP fetch routing extracted (combined S9a + S9b).
|
|
65
62
|
import * as _routes from './routes.js';
|
|
63
|
+
import * as _portCapability from './port-capability.js';
|
|
66
64
|
// Programmatic SDK RPC surface.
|
|
67
65
|
import * as _programmatic from './programmatic.js';
|
|
68
66
|
// S10: heap probe + W5 OOM-ring persistence extracted.
|
|
@@ -170,7 +168,7 @@ Cloud-native development environment on Cloudflare Workers.
|
|
|
170
168
|
| --- | --- |
|
|
171
169
|
| Run JavaScript | \`node hello.js\` |
|
|
172
170
|
| Install npm packages | \`npm install <pkg>\` |
|
|
173
|
-
| Start the starter app | \`cd
|
|
171
|
+
| Start the starter app | \`cd ${SEED_PROJECT_NAME} && npm install && npm run dev\` |
|
|
174
172
|
| Run a Cloudflare Worker locally | \`nimbus-wrangler dev\` |
|
|
175
173
|
|
|
176
174
|
## AI Models
|
|
@@ -245,12 +243,15 @@ The editor opens this file in Markdown preview mode by default. Use
|
|
|
245
243
|
export class NimbusSession extends CloudflareDurableObject {
|
|
246
244
|
// this.ctx and this.env are provided by the DurableObject base class
|
|
247
245
|
sqliteFs = null;
|
|
248
|
-
runtimeFsBridges = null;
|
|
249
246
|
kernel = null;
|
|
250
247
|
shell = null;
|
|
251
248
|
shellProcessPid = null;
|
|
252
249
|
terminal = null;
|
|
253
|
-
|
|
250
|
+
/** The composed manager is the one field; `.manager` is derived. */
|
|
251
|
+
facetManagerComposed = null;
|
|
252
|
+
get facetManager() {
|
|
253
|
+
return this.facetManagerComposed?.manager ?? null;
|
|
254
|
+
}
|
|
254
255
|
/** W8: child_process broker. Lazy — only constructed when first cp* RPC arrives. */
|
|
255
256
|
facetProcessManager = null;
|
|
256
257
|
/**
|
|
@@ -260,6 +261,12 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
260
261
|
*/
|
|
261
262
|
webSocketRelay = null;
|
|
262
263
|
esbuildService = null;
|
|
264
|
+
/**
|
|
265
|
+
* The session's single esbuild facet pool, shared by the npm installer's
|
|
266
|
+
* pre-bundler and the dev server's on-demand /@modules/ path. Lazy; see
|
|
267
|
+
* ensureBundlePool. Disposed with the installer and dev server.
|
|
268
|
+
*/
|
|
269
|
+
bundlePool = null;
|
|
263
270
|
viteDevServer = null;
|
|
264
271
|
/**
|
|
265
272
|
* runtime primitive support (P5): PID + port the default-Cirrus vite shim is
|
|
@@ -393,6 +400,13 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
393
400
|
sessionBasePath = '';
|
|
394
401
|
/** Have we attempted to hydrate sessionBasePath from storage yet? */
|
|
395
402
|
sessionBasePathHydrated = false;
|
|
403
|
+
/**
|
|
404
|
+
* The origin this session was last reached at (`https://host`), remembered
|
|
405
|
+
* beside the base path so the session can spell a path-form URL for one
|
|
406
|
+
* of its own applications — `nimbus expose` printing one, `apps.list`
|
|
407
|
+
* carrying one — on a deployment with no preview-host suffix.
|
|
408
|
+
*/
|
|
409
|
+
sessionOrigin = '';
|
|
396
410
|
/**
|
|
397
411
|
* Has the "wrangler is aliased to nimbus-wrangler" banner been shown
|
|
398
412
|
* this session? Reset on WebSocket close/reopen so a reconnecting user
|
|
@@ -545,8 +559,8 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
545
559
|
* it arrives as a new invocation, so the chunk runs against a fresh CPU
|
|
546
560
|
* budget rather than the one the launch has already been spending.
|
|
547
561
|
*/
|
|
548
|
-
_scheduleLaunchTurn() {
|
|
549
|
-
return timers(this, this.ctx).schedule('resident-launch', Date.now());
|
|
562
|
+
_scheduleLaunchTurn(notBefore = 0) {
|
|
563
|
+
return timers(this, this.ctx).schedule('resident-launch', Math.max(Date.now(), notBefore));
|
|
550
564
|
}
|
|
551
565
|
/**
|
|
552
566
|
* Convenience: the full URL prefix for the Vite dev server inside this
|
|
@@ -572,6 +586,9 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
572
586
|
const saved = await this.ctx.storage.get('session-base-path');
|
|
573
587
|
if (typeof saved === 'string')
|
|
574
588
|
this.sessionBasePath = saved;
|
|
589
|
+
const origin = await this.ctx.storage.get('session-origin');
|
|
590
|
+
if (typeof origin === 'string')
|
|
591
|
+
this.sessionOrigin = origin;
|
|
575
592
|
}
|
|
576
593
|
catch { /* storage unavailable — stay empty */ }
|
|
577
594
|
this.sessionBasePathHydrated = true;
|
|
@@ -584,6 +601,23 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
584
601
|
}
|
|
585
602
|
catch { }
|
|
586
603
|
}
|
|
604
|
+
// The router forwards the request under its own origin; a preview host
|
|
605
|
+
// is not this session's control-plane origin, so only a request that
|
|
606
|
+
// carried the base path (a /s/<sid>/… door) teaches it.
|
|
607
|
+
if (fromHeader) {
|
|
608
|
+
let origin = '';
|
|
609
|
+
try {
|
|
610
|
+
origin = new URL(request.url).origin;
|
|
611
|
+
}
|
|
612
|
+
catch { /* not an absolute URL — nothing to learn */ }
|
|
613
|
+
if (origin && origin !== this.sessionOrigin) {
|
|
614
|
+
this.sessionOrigin = origin;
|
|
615
|
+
try {
|
|
616
|
+
await this.ctx.storage.put('session-origin', origin);
|
|
617
|
+
}
|
|
618
|
+
catch { }
|
|
619
|
+
}
|
|
620
|
+
}
|
|
587
621
|
}
|
|
588
622
|
/**
|
|
589
623
|
* Consume a single-use attach bootstrap token id (`jti`) — set-if-absent.
|
|
@@ -605,27 +639,48 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
605
639
|
// dispatches by name from the stub) keeps working. Method bodies are
|
|
606
640
|
// 1-line delegators that pass `this as any` (per plan §IX rec 1 +
|
|
607
641
|
// DEFECT-D1: ctx is protected and not on a public interface).
|
|
642
|
+
/**
|
|
643
|
+
* The one supervisor-op handler this session's bindings, loopback stubs and
|
|
644
|
+
* `_rpc*` delegates all dispatch through — native filesystem ops against
|
|
645
|
+
* the shared bridge store, session overrides for the accounting-carrying
|
|
646
|
+
* reads and the output stream, and the canonical route table for the rest.
|
|
647
|
+
* Lazy: sqliteFs exists only after ensureSqliteFs().
|
|
648
|
+
*/
|
|
649
|
+
_supervisorOps = null;
|
|
650
|
+
supervisorOps() {
|
|
651
|
+
if (!this._supervisorOps)
|
|
652
|
+
this._supervisorOps = buildSessionSupervisorOps(this);
|
|
653
|
+
return this._supervisorOps;
|
|
654
|
+
}
|
|
655
|
+
/** The pid-keyed filesystem bridge behind the supervisor ops. */
|
|
656
|
+
supervisorBridge(pid) {
|
|
657
|
+
return this.supervisorOps().bridge(pid);
|
|
658
|
+
}
|
|
659
|
+
/** Drop a dead pid's supervisor bridge — its credential stops being valid. */
|
|
660
|
+
supervisorForgetBridge(pid) {
|
|
661
|
+
this._supervisorOps?.forget(pid);
|
|
662
|
+
}
|
|
608
663
|
// Supervisor RPC (file/log/HMR/batch)
|
|
609
664
|
supervisorOp(envelope) {
|
|
610
|
-
return
|
|
665
|
+
return this.supervisorOps().dispatch(envelope);
|
|
611
666
|
}
|
|
612
|
-
async _rpcReadFile(path, pid) { return _rpc._rpcReadFile(this, path, pid); }
|
|
613
|
-
async _rpcReadFileBytes(path, pid) { return _rpc._rpcReadFileBytes(this, path, pid); }
|
|
667
|
+
async _rpcReadFile(path, pid, cred) { return _rpc._rpcReadFile(this, path, pid, cred); }
|
|
668
|
+
async _rpcReadFileBytes(path, pid, cred) { return _rpc._rpcReadFileBytes(this, path, pid, cred); }
|
|
614
669
|
async _rpcInnerDoFetch(req) { return _rpc._rpcInnerDoFetch(this, req); }
|
|
615
|
-
async _rpcWriteFile(path, content, pid) { return _rpc._rpcWriteFile(this, path, content, pid); }
|
|
670
|
+
async _rpcWriteFile(path, content, pid, cred) { return _rpc._rpcWriteFile(this, path, content, pid, cred); }
|
|
616
671
|
async _rpcWriteProtectedRootFile(rootPath, path, content) {
|
|
617
672
|
return _rpc._rpcWriteProtectedRootFile(this, rootPath, path, content);
|
|
618
673
|
}
|
|
619
|
-
async _rpcStat(path, pid) { return _rpc._rpcStat(this, path, pid); }
|
|
620
|
-
async _rpcLstat(path, pid) { return _rpc._rpcLstat(this, path, pid); }
|
|
674
|
+
async _rpcStat(path, pid, cred) { return _rpc._rpcStat(this, path, pid, cred); }
|
|
675
|
+
async _rpcLstat(path, pid, cred) { return _rpc._rpcLstat(this, path, pid, cred); }
|
|
621
676
|
async _rpcHasLegacySymlinkUnder(path, pid) {
|
|
622
677
|
return _rpc._rpcHasLegacySymlinkUnder(this, path, pid);
|
|
623
678
|
}
|
|
624
679
|
async _rpcUtimes(path, atimeMs, mtimeMs, pid) {
|
|
625
680
|
return _rpc._rpcUtimes(this, path, atimeMs, mtimeMs, pid);
|
|
626
681
|
}
|
|
627
|
-
async _rpcChmod(path, mode, pid) {
|
|
628
|
-
return _rpc._rpcChmod(this, path, mode, pid);
|
|
682
|
+
async _rpcChmod(path, mode, pid, cred) {
|
|
683
|
+
return _rpc._rpcChmod(this, path, mode, pid, cred);
|
|
629
684
|
}
|
|
630
685
|
async _rpcAccess(path, mode, pid) {
|
|
631
686
|
return _rpc._rpcAccess(this, path, mode, pid);
|
|
@@ -636,11 +691,11 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
636
691
|
async _rpcSetUmask(mask, pid) {
|
|
637
692
|
return _rpc._rpcSetUmask(this, mask, pid);
|
|
638
693
|
}
|
|
639
|
-
async _rpcReaddir(path, pid) { return _rpc._rpcReaddir(this, path, pid); }
|
|
640
|
-
async _rpcExists(path, pid) { return _rpc._rpcExists(this, path, pid); }
|
|
641
|
-
async _rpcMkdir(path, pid) { return _rpc._rpcMkdir(this, path, pid); }
|
|
694
|
+
async _rpcReaddir(path, pid, cred) { return _rpc._rpcReaddir(this, path, pid, cred); }
|
|
695
|
+
async _rpcExists(path, pid, cred) { return _rpc._rpcExists(this, path, pid, cred); }
|
|
696
|
+
async _rpcMkdir(path, pid, cred) { return _rpc._rpcMkdir(this, path, pid, cred); }
|
|
642
697
|
async _rpcRmdir(path, pid) { return _rpc._rpcRmdir(this, path, pid); }
|
|
643
|
-
async _rpcRename(from, to, pid) { return _rpc._rpcRename(this, from, to, pid); }
|
|
698
|
+
async _rpcRename(from, to, pid, cred) { return _rpc._rpcRename(this, from, to, pid, cred); }
|
|
644
699
|
async _rpcReadlink(path, pid) { return _rpc._rpcReadlink(this, path, pid); }
|
|
645
700
|
async _rpcSymlink(target, path, pid) { return _rpc._rpcSymlink(this, target, path, pid); }
|
|
646
701
|
async _rpcFsRevision(path, pid) { return _rpc._rpcFsRevision(this, path, pid); }
|
|
@@ -670,8 +725,8 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
670
725
|
return _rpc._rpcFsWrite(this, handleId, offset, bytes, pid);
|
|
671
726
|
}
|
|
672
727
|
async _rpcFsClose(handleId, pid) { return _rpc._rpcFsClose(this, handleId, pid); }
|
|
673
|
-
async _rpcFsReadRange(path, offset, length, pid) {
|
|
674
|
-
return _rpc._rpcFsReadRange(this, path, offset, length, pid);
|
|
728
|
+
async _rpcFsReadRange(path, offset, length, pid, cred) {
|
|
729
|
+
return _rpc._rpcFsReadRange(this, path, offset, length, pid, cred);
|
|
675
730
|
}
|
|
676
731
|
async _rpcFsReadRangeUncached(path, offset, length, pid) {
|
|
677
732
|
return _rpc._rpcFsReadRangeUncached(this, path, offset, length, pid);
|
|
@@ -774,6 +829,14 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
774
829
|
async _rpcExposePort(port, options) {
|
|
775
830
|
return _programmatic.rpcExposePort(this, port, options);
|
|
776
831
|
}
|
|
832
|
+
// The identity-centric application surface: one implementation each in
|
|
833
|
+
// programmatic.ts, addressed by port, pid, name or owner.
|
|
834
|
+
async _rpcExposeApp(target, options) {
|
|
835
|
+
return _programmatic.rpcExposeApp(this, target, options);
|
|
836
|
+
}
|
|
837
|
+
async _rpcListApps() { return _programmatic.rpcListApps(this); }
|
|
838
|
+
async _rpcRotateLink(target) { return _programmatic.rpcRotateLink(this, target); }
|
|
839
|
+
async _rpcRemoveApp(target) { return _programmatic.rpcRemoveApp(this, target); }
|
|
777
840
|
async _rpcEnsureDurableApp(input) {
|
|
778
841
|
return _programmatic.rpcEnsureDurableApp(this, input);
|
|
779
842
|
}
|
|
@@ -783,7 +846,15 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
783
846
|
async _rpcUnexposePort(port) { return _programmatic.rpcUnexposePort(this, port); }
|
|
784
847
|
/** Capability-authenticated port route, for an embedder holding the token. */
|
|
785
848
|
async _rpcRouteCapabilityPort(port, capability, request, innerPath) {
|
|
786
|
-
return
|
|
849
|
+
return _portCapability.routeCapabilityPort(this, port, capability, request, innerPath);
|
|
850
|
+
}
|
|
851
|
+
/** The port route's dev-server restore, delegated — lives in routes.ts where the other session routes do. */
|
|
852
|
+
restorePersistedDevServer(onlyPort) {
|
|
853
|
+
return _routes.restorePersistedDevServer(this, onlyPort);
|
|
854
|
+
}
|
|
855
|
+
/** The port route's in-DO HMR accept, delegated — same reason. */
|
|
856
|
+
acceptCirrusHmrWs(request) {
|
|
857
|
+
return _routes.acceptCirrusHmrWs(this, request);
|
|
787
858
|
}
|
|
788
859
|
/**
|
|
789
860
|
* The port route's recovery seam: a durable application journalled but
|
|
@@ -791,10 +862,20 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
791
862
|
* manager decides 'started' | 'absent' | 'failed'; the route maps them.
|
|
792
863
|
*/
|
|
793
864
|
async ensureDurableAppOnPort(port) {
|
|
865
|
+
// Stood up the same way the alarm pump stands itself up: a port request
|
|
866
|
+
// is the first thing to reach a replacement instance as often as an
|
|
867
|
+
// alarm is, and the launch it re-drives needs a filesystem to be
|
|
868
|
+
// re-driven onto — measured live (staging 2026-09-14): without it the
|
|
869
|
+
// re-drive failed with "a resident process needs a session filesystem".
|
|
870
|
+
this.ensureSqliteFs();
|
|
794
871
|
this.ensureFacetManager();
|
|
795
872
|
return this.facetManager.ensureDurableAppOnPort(port);
|
|
796
873
|
}
|
|
797
|
-
async _rpcDeleteFile(path, options) { return _programmatic.rpcDeleteFile(this, path, options); }
|
|
874
|
+
async _rpcDeleteFile(path, options, cred) { return _programmatic.rpcDeleteFile(this, path, options, cred); }
|
|
875
|
+
/** Colocated embedders only (DO stub); not on the remote dispatcher. See `rpcSpawnWorker`. */
|
|
876
|
+
async _rpcSpawnWorker(workerCode, command, cwd, opts) {
|
|
877
|
+
return _programmatic.rpcSpawnWorker(this, workerCode, command, cwd, opts);
|
|
878
|
+
}
|
|
798
879
|
async _rpcDestroy(options) { return _programmatic.rpcDestroy(this, options); }
|
|
799
880
|
// Legacy VFS (direct method calls)
|
|
800
881
|
vfsReadFile(path) { return _rpc.vfsReadFile(this, path); }
|
|
@@ -883,6 +964,7 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
883
964
|
console.warn('[nimbus/W5] ring rehydrate failed:', e?.message);
|
|
884
965
|
});
|
|
885
966
|
}
|
|
967
|
+
return this.sqliteFs;
|
|
886
968
|
}
|
|
887
969
|
// ── W5 Lever 5: ring buffer persistence on DO storage ─────────────────
|
|
888
970
|
// Storage key W5_RING_STORAGE_KEY lives in ./nimbus-session-keys.ts (S5).
|
|
@@ -906,72 +988,72 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
906
988
|
_w5PersistRing() {
|
|
907
989
|
return _diag.persistRing(this, this.ctx);
|
|
908
990
|
}
|
|
991
|
+
/** The session's esbuild facet pool provider; constructing it does no work. */
|
|
992
|
+
ensureBundlePool() {
|
|
993
|
+
if (!this.bundlePool)
|
|
994
|
+
this.bundlePool = new EsbuildBundlePool(this.env, this.ctx);
|
|
995
|
+
return this.bundlePool;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* The session's FacetManager, composed through the one factory every host
|
|
999
|
+
* uses (`facets/compose.ts`). What is wired here is only what is the
|
|
1000
|
+
* session's: the terminal a spawn is announced on, the scrollback a notice
|
|
1001
|
+
* survives in, the alarm that grants a launch its next turn, and the exit
|
|
1002
|
+
* report that keeps the process table honest. The isolated esbuild
|
|
1003
|
+
* transform and the durable image-store fallback are the factory's.
|
|
1004
|
+
*/
|
|
909
1005
|
ensureFacetManager() {
|
|
910
|
-
if (!this.
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
this.processes.openInput(pid);
|
|
1006
|
+
if (!this.facetManagerComposed) {
|
|
1007
|
+
// The manager is composed over the filesystem, so the filesystem comes
|
|
1008
|
+
// first. Cheap and idempotent; every caller already stood it up or is
|
|
1009
|
+
// about to.
|
|
1010
|
+
this.ensureSqliteFs();
|
|
1011
|
+
this.facetManagerComposed = composeFacetManager({
|
|
1012
|
+
ctx: this.ctx,
|
|
1013
|
+
env: this.env,
|
|
1014
|
+
processes: this.processes,
|
|
1015
|
+
portRegistry: this.portRegistry,
|
|
1016
|
+
vfs: this.sqliteFs,
|
|
1017
|
+
...(this.esbuildService ? { esbuild: this.esbuildService } : {}),
|
|
1018
|
+
hooks: {
|
|
1019
|
+
onExternalExit: (pid, code, reason) => this._reportExternalExit(pid, code, reason),
|
|
1020
|
+
requestLaunchTurn: (notBefore) => { void this._scheduleLaunchTurn(notBefore); },
|
|
1021
|
+
notify: (line) => this._notifySession(line),
|
|
1022
|
+
onSpawn: (pid, command, longRunning) => {
|
|
1023
|
+
const attachedTty = this.processes.get(pid)?.attachedTty === true;
|
|
1024
|
+
if (longRunning) {
|
|
1025
|
+
try {
|
|
1026
|
+
this.processes.openInput(pid);
|
|
1027
|
+
}
|
|
1028
|
+
catch { }
|
|
934
1029
|
}
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
});
|
|
949
|
-
},
|
|
950
|
-
// The fallback resolver a self-owned durable spawn re-drives
|
|
951
|
-
// through: read the image blobs the spawn persisted under
|
|
952
|
-
// `.nimbus/images/<sha256>` and restore the launch's env and
|
|
953
|
-
// modules from them. An embedder-owned launch answers its own
|
|
954
|
-
// bookkeeping through resolveWorkerLaunch instead — this hook is
|
|
955
|
-
// only the fallback for applications nobody else is keeping, and
|
|
956
|
-
// because it cannot re-mint a live globalOutbound binding the
|
|
957
|
-
// spawn path refuses such a launch under it.
|
|
958
|
-
resolveWorkerLaunchFallback: async (recipe) => {
|
|
959
|
-
this.ensureSqliteFs();
|
|
960
|
-
return resolveDurableWorkerImage(this.sqliteFs, recipe);
|
|
1030
|
+
// Only surface long-running / user-visible spawns to keep
|
|
1031
|
+
// the terminal uncluttered. Short `node <file>` evals also
|
|
1032
|
+
// get a line because users want the pid for `logs`/`kill`.
|
|
1033
|
+
if (!this.terminal)
|
|
1034
|
+
return;
|
|
1035
|
+
const label = longRunning ? 'started (long-running)' : 'started';
|
|
1036
|
+
this.terminal.write(`\x1b[2m[facet ${label}: pid=${pid} cmd="${command}"]\x1b[0m\r\n`);
|
|
1037
|
+
// Structured event so the tabs UI can auto-open a log tab
|
|
1038
|
+
// for long-running processes (vite, wrangler dev, etc.).
|
|
1039
|
+
notifyTerminalEvent(this.terminal, {
|
|
1040
|
+
type: 'spawn', pid, command, longRunning, attachedTty,
|
|
1041
|
+
});
|
|
1042
|
+
},
|
|
961
1043
|
},
|
|
962
1044
|
});
|
|
963
1045
|
}
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
}
|
|
1046
|
+
const composed = this.facetManagerComposed;
|
|
1047
|
+
// W3.5 Fix B: share the session's lazy esbuildService with the
|
|
1048
|
+
// FacetManager so the bundle's ESM→CJS pre-pass doesn't pay
|
|
1049
|
+
// wasm-init twice. The session may construct it after the manager
|
|
1050
|
+
// exists, so the share is re-offered on every call; FacetManager
|
|
1051
|
+
// otherwise lazy-creates its own on first exec — same wasm bytes,
|
|
1052
|
+
// same ~10ms init cost, just paid once per surface.
|
|
1053
|
+
if (this.esbuildService) {
|
|
1054
|
+
composed.manager.setEsbuildService(this.esbuildService);
|
|
974
1055
|
}
|
|
1056
|
+
return composed;
|
|
975
1057
|
}
|
|
976
1058
|
/**
|
|
977
1059
|
* The supervisor-owned WebSocket relay. Lazy, because most sessions never
|
|
@@ -1013,29 +1095,29 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1013
1095
|
try {
|
|
1014
1096
|
const parsed = CpFacetDirectPayloadSchema.safeParse(JSON.parse(codeJson));
|
|
1015
1097
|
if (!parsed.success) {
|
|
1016
|
-
hooks.onStderr('child_process: facet dispatch requires a broker-assigned process pid\n');
|
|
1098
|
+
hooks.onStderr(textBytes('child_process: facet dispatch requires a broker-assigned process pid\n'));
|
|
1017
1099
|
return 1;
|
|
1018
1100
|
}
|
|
1019
1101
|
payload = parsed.data;
|
|
1020
1102
|
}
|
|
1021
1103
|
catch {
|
|
1022
|
-
hooks.onStderr('child_process: invalid facet dispatch payload\n');
|
|
1104
|
+
hooks.onStderr(textBytes('child_process: invalid facet dispatch payload\n'));
|
|
1023
1105
|
return 1;
|
|
1024
1106
|
}
|
|
1025
1107
|
const registry = this._cpRegistry;
|
|
1026
1108
|
if (!registry) {
|
|
1027
|
-
hooks.onStderr('child_process: command registry unavailable\n');
|
|
1109
|
+
hooks.onStderr(textBytes('child_process: command registry unavailable\n'));
|
|
1028
1110
|
return 127;
|
|
1029
1111
|
}
|
|
1030
1112
|
const commandName = normalizeCpCommandName(payload.command);
|
|
1031
1113
|
const cmd = await registry.resolve(commandName);
|
|
1032
1114
|
if (!cmd) {
|
|
1033
|
-
hooks.onStderr(`${payload.command}: command not found\n`);
|
|
1115
|
+
hooks.onStderr(textBytes(`${payload.command}: command not found\n`));
|
|
1034
1116
|
return 127;
|
|
1035
1117
|
}
|
|
1036
1118
|
// Synthesize a CommandContext for the internal shell substrate.
|
|
1037
|
-
const stdoutStream = { write: (d) => hooks.onStdout(String(d)) };
|
|
1038
|
-
const stderrStream = { write: (d) => hooks.onStderr(String(d)) };
|
|
1119
|
+
const stdoutStream = { write: (d) => hooks.onStdout(textBytes(String(d))) };
|
|
1120
|
+
const stderrStream = { write: (d) => hooks.onStderr(textBytes(String(d))) };
|
|
1039
1121
|
const ac = new AbortController();
|
|
1040
1122
|
const cred = this.processes.cred(payload.processPid);
|
|
1041
1123
|
const ctx = {
|
|
@@ -1071,7 +1153,7 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1071
1153
|
return typeof code === 'number' ? code : 0;
|
|
1072
1154
|
}
|
|
1073
1155
|
catch (e) {
|
|
1074
|
-
hooks.onStderr(`${payload.command}: ${e?.message || String(e)}\n`);
|
|
1156
|
+
hooks.onStderr(textBytes(`${payload.command}: ${e?.message || String(e)}\n`));
|
|
1075
1157
|
return 1;
|
|
1076
1158
|
}
|
|
1077
1159
|
},
|
|
@@ -1089,10 +1171,10 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1089
1171
|
// construction near line 2058 — registry passed as ctor arg there).
|
|
1090
1172
|
const cmdRegistryAdapter = {
|
|
1091
1173
|
// Consult the live shell registry FIRST so dynamically-registered
|
|
1092
|
-
// commands (registerUnixCommands /
|
|
1093
|
-
//
|
|
1094
|
-
//
|
|
1095
|
-
//
|
|
1174
|
+
// commands (registerUnixCommands / git / npm / wrangler etc.) are seen
|
|
1175
|
+
// even if they're not in the static _CP_PURE_BUILTIN allow-list. Falls
|
|
1176
|
+
// back to the static facet-direct table for known facet-only commands.
|
|
1177
|
+
// Returns null
|
|
1096
1178
|
// (→ exit 127) for everything unknown.
|
|
1097
1179
|
resolve: (name) => {
|
|
1098
1180
|
const commandName = normalizeCpCommandName(name);
|
|
@@ -1108,13 +1190,13 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1108
1190
|
runPureBuiltin: async (pid, name, args, env, cwd, stdin, hooks) => {
|
|
1109
1191
|
const registry = this._cpRegistry;
|
|
1110
1192
|
if (!registry) {
|
|
1111
|
-
hooks.onStderr('cp: registry unavailable\n');
|
|
1193
|
+
hooks.onStderr(textBytes('cp: registry unavailable\n'));
|
|
1112
1194
|
return 127;
|
|
1113
1195
|
}
|
|
1114
1196
|
const commandName = normalizeCpCommandName(name);
|
|
1115
1197
|
const cmd = await registry.resolve(commandName);
|
|
1116
1198
|
if (!cmd) {
|
|
1117
|
-
hooks.onStderr(`${name}: command not found\n`);
|
|
1199
|
+
hooks.onStderr(textBytes(`${name}: command not found\n`));
|
|
1118
1200
|
return 127;
|
|
1119
1201
|
}
|
|
1120
1202
|
const cred = this.processes.cred(pid);
|
|
@@ -1124,8 +1206,8 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1124
1206
|
cred,
|
|
1125
1207
|
args, env, cwd,
|
|
1126
1208
|
vfs: this.sqliteFs.as(cred),
|
|
1127
|
-
stdout: { write: (d) => hooks.onStdout(String(d)) },
|
|
1128
|
-
stderr: { write: (d) => hooks.onStderr(String(d)) },
|
|
1209
|
+
stdout: { write: (d) => hooks.onStdout(textBytes(String(d))) },
|
|
1210
|
+
stderr: { write: (d) => hooks.onStderr(textBytes(String(d))) },
|
|
1129
1211
|
signal: ac.signal,
|
|
1130
1212
|
stdin: staticStdinReader(stdin),
|
|
1131
1213
|
setUmask: (mask) => { this.processes.setUmask(pid, mask); },
|
|
@@ -1148,7 +1230,7 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1148
1230
|
return typeof code === 'number' ? code : 0;
|
|
1149
1231
|
}
|
|
1150
1232
|
catch (e) {
|
|
1151
|
-
hooks.onStderr(`${name}: ${e?.message || String(e)}\n`);
|
|
1233
|
+
hooks.onStderr(textBytes(`${name}: ${e?.message || String(e)}\n`));
|
|
1152
1234
|
return 1;
|
|
1153
1235
|
}
|
|
1154
1236
|
},
|
|
@@ -1173,7 +1255,7 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1173
1255
|
shellExecutor: {
|
|
1174
1256
|
execute: async (pid, commandLine, env, cwd, stdin, hooks) => {
|
|
1175
1257
|
if (!this.shell) {
|
|
1176
|
-
hooks.onStderr('sh: shell unavailable\n');
|
|
1258
|
+
hooks.onStderr(textBytes('sh: shell unavailable\n'));
|
|
1177
1259
|
return 127;
|
|
1178
1260
|
}
|
|
1179
1261
|
const cred = this.processes.cred(pid);
|
|
@@ -1194,8 +1276,8 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1194
1276
|
const result = await this.shell.execute(String(commandLine), {
|
|
1195
1277
|
cwd: cwd || '/home/user',
|
|
1196
1278
|
env: { ...this.shell.env, ...(env || {}) },
|
|
1197
|
-
onStdout:
|
|
1198
|
-
onStderr:
|
|
1279
|
+
onStdout: hooks.onStdout,
|
|
1280
|
+
onStderr: hooks.onStderr,
|
|
1199
1281
|
stdin,
|
|
1200
1282
|
isolateShellState: true,
|
|
1201
1283
|
commandContext: { pid, cred, setUmask },
|
|
@@ -1356,11 +1438,13 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1356
1438
|
}
|
|
1357
1439
|
this.npmInstaller = new NpmInstaller(this.sqliteFs, this.ctx.storage.sql, {
|
|
1358
1440
|
esbuild: this.esbuildService,
|
|
1441
|
+
bundlePool: this.ensureBundlePool(),
|
|
1359
1442
|
ctx: this.ctx,
|
|
1360
1443
|
env: this.env,
|
|
1361
1444
|
onProgress,
|
|
1362
1445
|
fetchFn,
|
|
1363
1446
|
});
|
|
1447
|
+
return this.npmInstaller;
|
|
1364
1448
|
}
|
|
1365
1449
|
/**
|
|
1366
1450
|
* Read an environment flag with default-on semantics. Mirrors the
|
|
@@ -1385,7 +1469,7 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1385
1469
|
// command registrations + boot wiring). The class retains `initSession`
|
|
1386
1470
|
// as a delegator per plan §IX.4 R1. Visibility relaxed (was `private`)
|
|
1387
1471
|
// so the SessionInternal interface declares it.
|
|
1388
|
-
initSession(ws) {
|
|
1472
|
+
initSession(ws, options) {
|
|
1389
1473
|
// A destroyed session id being legitimately re-initialized (shell WS
|
|
1390
1474
|
// attach, or SDK ready via ensureProgrammaticReady which routes here)
|
|
1391
1475
|
// lifts the tombstone so the recreated session's log-janitor can arm
|
|
@@ -1394,8 +1478,14 @@ export class NimbusSession extends CloudflareDurableObject {
|
|
|
1394
1478
|
// Cast pattern (per plan §IX recommendation 1, used here only because
|
|
1395
1479
|
// initSession reads this.ctx + this.env extensively; siblings that need
|
|
1396
1480
|
// ctx/env take them as separate explicit args per DEFECT-D1).
|
|
1397
|
-
return _w11InitSession(this, ws);
|
|
1481
|
+
return _w11InitSession(this, ws, options);
|
|
1398
1482
|
}
|
|
1483
|
+
/**
|
|
1484
|
+
* The rebuild in flight for a shell socket that woke this instance from
|
|
1485
|
+
* hibernation, so every frame that arrives while it runs awaits the one
|
|
1486
|
+
* build instead of starting its own. See session/ws.ts bindShellSocket.
|
|
1487
|
+
*/
|
|
1488
|
+
_wakeRebuild = null;
|
|
1399
1489
|
// ── Filesystem seeding ────────────────────────────────────────────────
|
|
1400
1490
|
ensureGlobalPrefixDirs(prefix) {
|
|
1401
1491
|
const fs = this.sqliteFs.as(CRED_SESSION_USER);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The worker's half of `npm install`: core's `npm` command parses the
|
|
3
|
+
* invocation, owns the pre-checks and the summary text, and hands the
|
|
4
|
+
* install itself to this port. What the port owns is everything the
|
|
5
|
+
* command must not know: the NpmInstaller, the global prefix's directory
|
|
6
|
+
* shape, and the materialisation of bin shims into <prefix>/bin.
|
|
7
|
+
*
|
|
8
|
+
* `projectDir` is always the shell's cwd; `globalPrefix` (absolute VFS
|
|
9
|
+
* path, e.g. `/usr/local`) is present only for `npm install -g` and says
|
|
10
|
+
* where `<prefix>/lib/node_modules` and `<prefix>/bin` land. `pid` is the
|
|
11
|
+
* running command's pid — it authorizes the batch-facet writes.
|
|
12
|
+
*/
|
|
13
|
+
import type { NpmInstallPort } from '@nimbus-sh/core/substrate/lifo/commands/system/npm.js';
|
|
14
|
+
import type { SessionInternal } from './internal.js';
|
|
15
|
+
export declare function createNpmInstallPort(self: SessionInternal): NpmInstallPort;
|
|
16
|
+
//# sourceMappingURL=npm-install-port.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"npm-install-port.d.ts","sourceRoot":"","sources":["../../src/session/npm-install-port.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uDAAuD,CAAC;AAI5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAErD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,eAAe,GAAG,cAAc,CAyC1E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
2
|
+
import { materializeNpmBinShims } from '../npm/bin-links.js';
|
|
3
|
+
export function createNpmInstallPort(self) {
|
|
4
|
+
return {
|
|
5
|
+
async install(spec) {
|
|
6
|
+
const globalPrefix = spec.global ? spec.globalPrefix : undefined;
|
|
7
|
+
const globalBinDir = globalPrefix ? `${globalPrefix}/bin` : undefined;
|
|
8
|
+
const sqliteFs = self.ensureSqliteFs();
|
|
9
|
+
const installer = await self.ensureNpmInstaller();
|
|
10
|
+
if (globalPrefix)
|
|
11
|
+
self.ensureGlobalPrefixDirs(globalPrefix);
|
|
12
|
+
const installCwd = globalPrefix ? `${globalPrefix}/lib` : spec.projectDir;
|
|
13
|
+
const result = await installer.install(installCwd, {
|
|
14
|
+
packages: spec.packages.length > 0 ? [...spec.packages] : undefined,
|
|
15
|
+
production: spec.production,
|
|
16
|
+
pid: spec.pid,
|
|
17
|
+
npmLog: spec.npmLog ?? undefined,
|
|
18
|
+
onProgress: spec.onProgress,
|
|
19
|
+
});
|
|
20
|
+
let linkedBins = 0;
|
|
21
|
+
if (globalPrefix && globalBinDir) {
|
|
22
|
+
// Materialise on-PATH bin shims even for partial installs — the
|
|
23
|
+
// bin linker already skips entries whose target never landed, so
|
|
24
|
+
// a partial tree safely exposes exactly the bins that installed.
|
|
25
|
+
const vfs = sqliteFs.as(CRED_KERNEL);
|
|
26
|
+
linkedBins = materializeNpmBinShims(vfs, `${installCwd}/node_modules`, globalBinDir);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
installed: result.installed,
|
|
30
|
+
failed: result.failed,
|
|
31
|
+
totalFiles: result.totalFiles,
|
|
32
|
+
fromCacheHits: result.cachedHits,
|
|
33
|
+
linkedBins,
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|