@nimbus-sh/worker 0.6.0 → 0.8.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/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 +9 -5
- package/dist/facets/durable-images.d.ts.map +1 -1
- package/dist/facets/durable-images.js +17 -7
- 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 +240 -109
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +677 -342
- 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/real-vite-hmr.d.ts +3 -1
- package/dist/facets/real-vite-hmr.d.ts.map +1 -1
- package/dist/facets/real-vite-hmr.js +18 -8
- 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/facets/ws-terminal.d.ts +9 -31
- package/dist/facets/ws-terminal.d.ts.map +1 -1
- package/dist/facets/ws-terminal.js +58 -51
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- package/dist/hosted/commands.d.ts +12 -0
- package/dist/hosted/commands.d.ts.map +1 -0
- package/dist/hosted/commands.js +1658 -0
- package/dist/hosted/runtime.d.ts +114 -0
- package/dist/hosted/runtime.d.ts.map +1 -0
- package/dist/hosted/runtime.js +288 -0
- package/dist/hosted/services.d.ts +40 -0
- package/dist/hosted/services.d.ts.map +1 -0
- package/dist/hosted/services.js +512 -0
- 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/remote-api.js +31 -1
- package/dist/runtime/bash-repl.d.ts +1 -1
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +38 -4
- package/dist/runtime/bun-runner.js +1 -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 +4 -1
- package/dist/runtime/node-runner.d.ts.map +1 -1
- package/dist/runtime/node-runner.js +9 -2
- 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 +16 -16
- package/dist/runtime/package-manager.d.ts +35 -122
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +61 -596
- package/dist/runtime/process-logs-api.d.ts +1 -1
- package/dist/runtime/process-logs-api.d.ts.map +1 -1
- package/dist/runtime/process-logs-api.js +1 -1
- package/dist/runtime/python-repl.d.ts +2 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +82 -59
- package/dist/runtime/repl-session.d.ts +9 -47
- package/dist/runtime/repl-session.d.ts.map +1 -1
- package/dist/runtime/repl-session.js +105 -105
- package/dist/runtime/runtime-catalog.d.ts +13 -0
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +88 -0
- package/dist/session/agent.d.ts +1 -21
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/ai.d.ts +5 -1
- package/dist/session/ai.d.ts.map +1 -1
- package/dist/session/ai.js +7 -0
- 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/hibernation.d.ts +2 -1
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +7 -4
- 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 -36
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +50 -2327
- package/dist/session/loopback.d.ts +2 -29
- package/dist/session/loopback.d.ts.map +1 -1
- package/dist/session/loopback.js +3 -27
- package/dist/session/nimbus-session.d.ts +77 -34
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +126 -514
- package/dist/session/npm-install-port.d.ts +18 -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 +44 -0
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +110 -0
- package/dist/session/programmatic.d.ts +36 -43
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +109 -115
- package/dist/session/routes.d.ts +30 -17
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +42 -131
- package/dist/session/rpc.d.ts +15 -15
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +98 -49
- 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 +4 -4
- package/dist/session/supervisor-op.d.ts.map +1 -1
- package/dist/session/supervisor-op.js +24 -10
- 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 +4 -0
- 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/dist/workspace-host.d.ts +6 -0
- package/dist/workspace-host.d.ts.map +1 -0
- package/dist/workspace-host.js +5 -0
- package/dist/workspace.d.ts +2 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/workspace.js +1 -0
- package/package.json +34 -9
- 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
|
@@ -5,23 +5,29 @@
|
|
|
5
5
|
* Durable Object exposes a typed, programmatic sandbox surface without
|
|
6
6
|
* duplicating the interactive terminal boot path.
|
|
7
7
|
*/
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* session/programmatic.ts - public sandbox RPC helpers.
|
|
10
|
+
*
|
|
11
|
+
* These helpers are called by NimbusSession one-line delegators so the
|
|
12
|
+
* Durable Object exposes a typed, programmatic sandbox surface without
|
|
13
|
+
* duplicating the interactive terminal boot path.
|
|
14
|
+
*/
|
|
15
|
+
import { ensureRuntimesProgrammatic, installRuntimeProgrammatic } from '../runtime/package-manager.js';
|
|
10
16
|
import { PID_GEN_STRIDE } from '@nimbus-sh/core/runtime/process-table.js';
|
|
11
17
|
import { notifyTerminalEvent } from '../runtime/process-logs-api.js';
|
|
12
18
|
import { SessionProcessSupervisor } from '@nimbus-sh/core/runtime/session-process-supervisor.js';
|
|
13
19
|
import { PortRegistry, createPortCapability } from '@nimbus-sh/core/runtime/port-registry.js';
|
|
14
|
-
import { CRED_KERNEL } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
15
|
-
import { endProcessInput, resizeProcess, signalProcess, writeProcessInput
|
|
20
|
+
import { CRED_KERNEL, requireVfsCred } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
21
|
+
import { endProcessInput, resizeProcess, signalProcess, writeProcessInput } from '@nimbus-sh/core/runtime/process-input-routing.js';
|
|
16
22
|
import { z } from 'zod/v4';
|
|
17
23
|
import { SESSION_DESTROYED_KEY, SHELL_STATE_KEY_PREFIX, VITE_CONFIG_KEY } from './keys.js';
|
|
18
|
-
import { clearPortCapability, isValidAppName, persistPortCapability,
|
|
19
|
-
import { PORT_CAPABILITY_KEY_PREFIX } from './keys.js';
|
|
24
|
+
import { clearPortCapability, isValidAppName, persistPortCapability, portRecordKey, readPortReservation, readPortReservationByName, readPortReservationByOwner, reservePort, restorePortCapability, rotatePortCapability } from './port-capability.js';
|
|
20
25
|
import { bindPublicPortCapability, unbindPublicPortCapability } from '../router/public-directory.js';
|
|
21
|
-
import { buildPreviewHost, buildPublicPreviewHost, isPreviewHostSafeSid, readPreviewHostSuffix
|
|
26
|
+
import { buildPreviewHost, buildPublicPreviewHost, isPreviewHostSafeSid, readPreviewHostSuffix } from '../_shared/preview-host.js';
|
|
22
27
|
import { RESTART_POLICY_ENV } from '../facets/manager.js';
|
|
23
28
|
import { GENERATION_KEY, assumeGeneration, generation } from '@nimbus-sh/fabric/generation.js';
|
|
24
29
|
import { HeadlessTerminal, Shell } from '@nimbus-sh/core/substrate/lifo/index.js';
|
|
30
|
+
import { textSink } from '@nimbus-sh/core/_shared/bytes.js';
|
|
25
31
|
const ShellIdSchema = z.string().min(1).max(160).regex(/^[A-Za-z0-9][A-Za-z0-9._:-]*$/);
|
|
26
32
|
const ShellStateSchema = z.object({
|
|
27
33
|
cwd: z.string().startsWith('/'),
|
|
@@ -113,35 +119,6 @@ const ProcessLogsOptionsSchema = z.object({
|
|
|
113
119
|
lines: z.number().int().nonnegative().optional(),
|
|
114
120
|
bytes: z.number().int().nonnegative().optional(),
|
|
115
121
|
}).strict();
|
|
116
|
-
function makeHeadlessWebSocket() {
|
|
117
|
-
const listeners = new Map();
|
|
118
|
-
const state = { readyState: 1 };
|
|
119
|
-
const ws = {
|
|
120
|
-
get readyState() { return state.readyState; },
|
|
121
|
-
send(_data) { },
|
|
122
|
-
close() {
|
|
123
|
-
state.readyState = 3;
|
|
124
|
-
for (const cb of listeners.get('close') ?? []) {
|
|
125
|
-
try {
|
|
126
|
-
cb();
|
|
127
|
-
}
|
|
128
|
-
catch { }
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
accept() { },
|
|
132
|
-
addEventListener(type, cb) {
|
|
133
|
-
const set = listeners.get(type) ?? new Set();
|
|
134
|
-
set.add(cb);
|
|
135
|
-
listeners.set(type, set);
|
|
136
|
-
},
|
|
137
|
-
removeEventListener(type, cb) {
|
|
138
|
-
listeners.get(type)?.delete(cb);
|
|
139
|
-
},
|
|
140
|
-
serializeAttachment(_value) { },
|
|
141
|
-
deserializeAttachment() { return { kind: 'programmatic' }; },
|
|
142
|
-
};
|
|
143
|
-
return ws;
|
|
144
|
-
}
|
|
145
122
|
function getHome(self) {
|
|
146
123
|
try {
|
|
147
124
|
const envHome = self.shell?.env?.HOME;
|
|
@@ -164,23 +141,14 @@ function runtimeDeps(self) {
|
|
|
164
141
|
if (!self._cpRegistry)
|
|
165
142
|
throw new Error('Nimbus shell registry did not initialize');
|
|
166
143
|
return {
|
|
167
|
-
|
|
168
|
-
vfs: self.sqliteFs,
|
|
144
|
+
runtimes: self.runtimeManager,
|
|
145
|
+
vfs: self.sqliteFs.as(CRED_KERNEL),
|
|
169
146
|
registry: self._cpRegistry,
|
|
170
147
|
getHome: () => getHome(self),
|
|
171
148
|
};
|
|
172
149
|
}
|
|
173
150
|
export async function ensureProgrammaticReady(self, options = {}) {
|
|
174
|
-
|
|
175
|
-
await self.initSession(makeHeadlessWebSocket());
|
|
176
|
-
// A programmatic boot owns no terminal socket, which is what
|
|
177
|
-
// 'drained' reports to the diagnostics and the recovery ring.
|
|
178
|
-
self._b4Phase = 'drained';
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
self.ensureSqliteFs();
|
|
182
|
-
self.ensureFacetManager();
|
|
183
|
-
}
|
|
151
|
+
await self.ensureRuntimeReady();
|
|
184
152
|
const preinstall = Array.from(new Set(options.preinstall ?? []))
|
|
185
153
|
.map((s) => String(s).trim())
|
|
186
154
|
.filter(Boolean);
|
|
@@ -218,14 +186,13 @@ function startShellJob(self, command, options, job, scoped) {
|
|
|
218
186
|
catch { /* already settled */ }
|
|
219
187
|
});
|
|
220
188
|
const emit = (stream, sink) => (data) => {
|
|
221
|
-
const text = String(data);
|
|
222
189
|
if (job.background) {
|
|
223
190
|
try {
|
|
224
|
-
self.processes.
|
|
191
|
+
self.processes.appendOutputBytes(pid, stream, data);
|
|
225
192
|
}
|
|
226
193
|
catch { /* ring gone */ }
|
|
227
194
|
}
|
|
228
|
-
sink?.(
|
|
195
|
+
sink?.(data);
|
|
229
196
|
};
|
|
230
197
|
const run = shell.execute(line, {
|
|
231
198
|
// A named shell already holds its own cwd and env; passing them again
|
|
@@ -261,7 +228,20 @@ function startShellJob(self, command, options, job, scoped) {
|
|
|
261
228
|
}
|
|
262
229
|
catch { } } };
|
|
263
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* The shell, process table and VFS all key on absolute POSIX paths. A
|
|
233
|
+
* relative `cwd` arriving here means a caller skipped its own boundary
|
|
234
|
+
* normalization (the SDK resolves it); handed to the shell it degrades to
|
|
235
|
+
* silently wrong cwd semantics and ENOENT writes. Name the field and fail.
|
|
236
|
+
*/
|
|
237
|
+
function assertAbsoluteExecCwd(options) {
|
|
238
|
+
const cwd = options.cwd;
|
|
239
|
+
if (cwd !== undefined && (typeof cwd !== 'string' || !cwd.startsWith('/'))) {
|
|
240
|
+
throw new Error(`cwd must be an absolute POSIX path starting with '/', got ${JSON.stringify(cwd)}`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
264
243
|
export async function rpcExec(self, command, options = {}) {
|
|
244
|
+
assertAbsoluteExecCwd(options);
|
|
265
245
|
await ensureProgrammaticReady(self, options);
|
|
266
246
|
return withShellState(self, options, false, (scoped) => execOnShell(self, command, options, scoped));
|
|
267
247
|
}
|
|
@@ -273,8 +253,8 @@ async function execOnShell(self, command, options, scoped) {
|
|
|
273
253
|
let timedOut = false;
|
|
274
254
|
const job = startShellJob(self, command, options, {
|
|
275
255
|
background: false,
|
|
276
|
-
onStdout: (d) => stdout.push(d),
|
|
277
|
-
onStderr: (d) => stderr.push(d),
|
|
256
|
+
onStdout: textSink((d) => stdout.push(d)),
|
|
257
|
+
onStderr: textSink((d) => stderr.push(d)),
|
|
278
258
|
}, scoped);
|
|
279
259
|
let result;
|
|
280
260
|
try {
|
|
@@ -353,6 +333,7 @@ function collectJobOutput(self, pid) {
|
|
|
353
333
|
* process surface (`listProcesses`, `processLogs`, `killProcess`).
|
|
354
334
|
*/
|
|
355
335
|
export async function rpcStartProcess(self, command, options = {}) {
|
|
336
|
+
assertAbsoluteExecCwd(options);
|
|
356
337
|
await ensureProgrammaticReady(self, options);
|
|
357
338
|
if (options.restart !== undefined && options.restart !== 'never' && options.restart !== 'on-failure') {
|
|
358
339
|
throw new Error(`startProcess: restart must be 'never' or 'on-failure', got ${String(options.restart)}`);
|
|
@@ -408,7 +389,15 @@ async function startOnShell(self, command, options, scoped) {
|
|
|
408
389
|
* the handoff, the facet is the live process, and it reports its own exit.
|
|
409
390
|
*/
|
|
410
391
|
function finishBackgroundJob(self, pid, command, exitCode) {
|
|
411
|
-
|
|
392
|
+
// The shell line returned, but the pid may still be serving: a resident
|
|
393
|
+
// facet the node runner handed off under this pid, or an in-process dev
|
|
394
|
+
// server a builtin (`vite`) adopted this pid for and registered a port
|
|
395
|
+
// under. Either way the process is alive — marking it exited would put a
|
|
396
|
+
// dead pid on a live port, and the app verbs identify a server by the pid
|
|
397
|
+
// serving its port.
|
|
398
|
+
const stillServing = self.facetManager?.hasResidentProcess(pid) === true
|
|
399
|
+
|| self.portRegistry.getAll().some((live) => live.pid === pid);
|
|
400
|
+
if (exitCode === 0 && stillServing)
|
|
412
401
|
return;
|
|
413
402
|
self.processes.exit(pid, exitCode);
|
|
414
403
|
if (!self.processes.getExit(pid))
|
|
@@ -447,8 +436,8 @@ export async function rpcEnsureRuntimes(self, specs, options = {}) {
|
|
|
447
436
|
export async function rpcListRuntimes(self) {
|
|
448
437
|
await ensureProgrammaticReady(self);
|
|
449
438
|
return {
|
|
450
|
-
installed:
|
|
451
|
-
available: await
|
|
439
|
+
installed: self.runtimeManager.list(),
|
|
440
|
+
available: await self.runtimeManager.available(),
|
|
452
441
|
};
|
|
453
442
|
}
|
|
454
443
|
export async function rpcListProcesses(self) {
|
|
@@ -528,12 +517,9 @@ export async function rpcProcessLogs(self, pid, options = {}) {
|
|
|
528
517
|
}
|
|
529
518
|
export async function rpcListPorts(self) {
|
|
530
519
|
await ensureProgrammaticReady(self);
|
|
531
|
-
|
|
532
|
-
//
|
|
533
|
-
|
|
534
|
-
// survive anything.
|
|
535
|
-
await Promise.all(entries.map((entry) => persistPortCapability(self, entry.port, entry.capability)));
|
|
536
|
-
return entries.map(serializePort);
|
|
520
|
+
// The composed manager's copy is the one implementation — persistence
|
|
521
|
+
// at tell-time included.
|
|
522
|
+
return self.ensureFacetManager().apps.listPorts();
|
|
537
523
|
}
|
|
538
524
|
/**
|
|
539
525
|
* Browser-facing URL for an application, built inside the session: the
|
|
@@ -612,8 +598,9 @@ export async function rpcExposeApp(self, target, options = {}) {
|
|
|
612
598
|
throw new Error(`${describeTarget(target)} has not bound a port yet — expose it once it is listening`);
|
|
613
599
|
}
|
|
614
600
|
if (resolved.owner === null) {
|
|
615
|
-
|
|
616
|
-
|
|
601
|
+
// Every running process has an identity, so this is only a port nothing
|
|
602
|
+
// serves and no reservation names.
|
|
603
|
+
throw new Error(`nothing is serving ${describeTarget(target)} — start the server, then expose it`);
|
|
617
604
|
}
|
|
618
605
|
const exposed = await applyExposure(self, resolved.port, resolved.owner, options);
|
|
619
606
|
const live = self.portRegistry.get(resolved.port);
|
|
@@ -791,7 +778,23 @@ export async function rpcRemoveApp(self, target) {
|
|
|
791
778
|
if (resolved.owner === null) {
|
|
792
779
|
throw new Error(`${describeTarget(target)} names no application — nothing to remove`);
|
|
793
780
|
}
|
|
794
|
-
|
|
781
|
+
const owner = resolved.owner;
|
|
782
|
+
// The manager's removal kills what the journal knows. A server nothing
|
|
783
|
+
// journalled — the dev servers above all — is found the way every verb
|
|
784
|
+
// finds it, by the identity of the pid serving a port, and ended through
|
|
785
|
+
// the session's own kill, which is the one that also stops an in-process
|
|
786
|
+
// dev server rather than only marking its pid dead.
|
|
787
|
+
const fm = self.facetManager;
|
|
788
|
+
let killed = 0;
|
|
789
|
+
for (const live of self.portRegistry.getAll()) {
|
|
790
|
+
const identity = await fm.residentIdentity(live.pid);
|
|
791
|
+
if (identity === null || identity.ephemeral || identity.owner !== owner)
|
|
792
|
+
continue;
|
|
793
|
+
if ((await rpcKillProcess(self, live.pid)).ok)
|
|
794
|
+
killed += 1;
|
|
795
|
+
}
|
|
796
|
+
const durable = await rpcRemoveDurableApp(self, owner);
|
|
797
|
+
return { ...durable, removed: durable.removed || killed > 0 };
|
|
795
798
|
}
|
|
796
799
|
function describeTarget(target) {
|
|
797
800
|
if (typeof target === 'number')
|
|
@@ -873,7 +876,7 @@ async function resolveAppTarget(self, target) {
|
|
|
873
876
|
if ('pid' in target) {
|
|
874
877
|
const asPid = await byPid(Number(target.pid));
|
|
875
878
|
if (asPid === null) {
|
|
876
|
-
throw new Error(`pid ${target.pid} has no launch record
|
|
879
|
+
throw new Error(`pid ${target.pid} is not running and has no launch record`);
|
|
877
880
|
}
|
|
878
881
|
return asPid;
|
|
879
882
|
}
|
|
@@ -898,32 +901,10 @@ async function resolveAppTarget(self, target) {
|
|
|
898
901
|
*/
|
|
899
902
|
export async function rpcEnsureDurableApp(self, input) {
|
|
900
903
|
await ensureProgrammaticReady(self);
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
const occupied = new Set(self.portRegistry.getAll().map((entry) => entry.port));
|
|
906
|
-
const port = await reservePort(self.ctx, {
|
|
907
|
-
owner,
|
|
908
|
-
preferredPort: input.preferredPort,
|
|
909
|
-
occupiedPorts: occupied,
|
|
910
|
-
// Minted here, not at boot: the URL is built from this capability, and
|
|
911
|
-
// re-drive re-adopts it out of the same row.
|
|
912
|
-
capability: createPortCapability(),
|
|
913
|
-
...(input.visibility !== undefined ? { visibility: input.visibility } : {}),
|
|
914
|
-
});
|
|
915
|
-
const record = await readPortReservation(self.ctx, port);
|
|
916
|
-
if (record?.visibility === 'public' && record.capability !== null) {
|
|
917
|
-
// The URL is built on this capability before the app has ever run:
|
|
918
|
-
// publish it to the directory now, loudly refusing a deployment whose
|
|
919
|
-
// binding is missing rather than minting an unroutable URL.
|
|
920
|
-
await bindPublicPortCapability(self, record.capability, port);
|
|
921
|
-
}
|
|
922
|
-
return {
|
|
923
|
-
port,
|
|
924
|
-
capability: record?.capability ?? null,
|
|
925
|
-
visibility: record?.visibility ?? 'scoped',
|
|
926
|
-
};
|
|
904
|
+
// The composed manager owns the durable-app verbs — one implementation,
|
|
905
|
+
// capability minted there so a URL handed out before boot is the one the
|
|
906
|
+
// eventual binding re-adopts.
|
|
907
|
+
return self.ensureFacetManager().apps.ensureDurableApp(input);
|
|
927
908
|
}
|
|
928
909
|
export async function rpcUnexposePort(self, port) {
|
|
929
910
|
await ensureProgrammaticReady(self);
|
|
@@ -946,26 +927,8 @@ export async function rpcUnexposePort(self, port) {
|
|
|
946
927
|
*/
|
|
947
928
|
export async function rpcRemoveDurableApp(self, owner) {
|
|
948
929
|
await ensureProgrammaticReady(self);
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
}
|
|
952
|
-
// Which durable address is being released — the reservation's row names it
|
|
953
|
-
// before the manager retires it.
|
|
954
|
-
const rows = await self.ctx.storage.list({ prefix: PORT_CAPABILITY_KEY_PREFIX });
|
|
955
|
-
let port = null;
|
|
956
|
-
for (const [key, record] of rows) {
|
|
957
|
-
const parsed = PortRecordSchema.safeParse(record);
|
|
958
|
-
if (!parsed.success || parsed.data.owner !== owner)
|
|
959
|
-
continue;
|
|
960
|
-
const n = Number(key.slice(PORT_CAPABILITY_KEY_PREFIX.length));
|
|
961
|
-
if (Number.isInteger(n) && n > 0) {
|
|
962
|
-
port = n;
|
|
963
|
-
break;
|
|
964
|
-
}
|
|
965
|
-
}
|
|
966
|
-
self.ensureFacetManager();
|
|
967
|
-
const removed = await self.facetManager.removeDurableApp(owner);
|
|
968
|
-
return { owner, removed, port };
|
|
930
|
+
// The composed manager owns the durable-app verbs — validation included.
|
|
931
|
+
return self.ensureFacetManager().apps.removeDurableApp(owner);
|
|
969
932
|
}
|
|
970
933
|
/**
|
|
971
934
|
* Route an embedder request that carries a port capability. The embedder has
|
|
@@ -983,10 +946,34 @@ export async function rpcRouteCapabilityPort(self, port, capability, request, pa
|
|
|
983
946
|
const routed = await self.portRegistry.routeCapabilityRequest(Number(port), String(capability), request, pathname);
|
|
984
947
|
return routed ?? new Response('Not found', { status: 404 });
|
|
985
948
|
}
|
|
986
|
-
|
|
949
|
+
/**
|
|
950
|
+
* `spawnWorker` for a colocated embedder holding the DO stub: boot the
|
|
951
|
+
* embedder's own Worker-class program — its main module, inline modules and
|
|
952
|
+
* content-addressed text/wasm modules — as one of this session's resident
|
|
953
|
+
* processes, and answer with the pid, the runner's boot payload and the
|
|
954
|
+
* process's facet (`fetch`/`connect`, bound to the resident handle; no
|
|
955
|
+
* release — `killProcess(pid)` ends it). Deliberately absent from the remote
|
|
956
|
+
* HTTP dispatcher: the facet is a live handle, and a remote token holder is
|
|
957
|
+
* not the embedder.
|
|
958
|
+
*/
|
|
959
|
+
export async function rpcSpawnWorker(self, workerCode, command, cwd, opts = {}) {
|
|
960
|
+
await ensureProgrammaticReady(self);
|
|
961
|
+
if (typeof workerCode !== 'string' || workerCode.length === 0) {
|
|
962
|
+
throw new Error('spawnWorker: workerCode must be a non-empty string');
|
|
963
|
+
}
|
|
964
|
+
self.ensureFacetManager();
|
|
965
|
+
return self.facetManager.spawnWorker(workerCode, String(command), String(cwd), opts);
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* `files.delete`. Absent a `cred` this acts as CRED_KERNEL — what it has
|
|
969
|
+
* always done, and the embedder's trusted surface: only a caller holding the
|
|
970
|
+
* DO binding reaches it. A `cred` binds the removal to that identity instead,
|
|
971
|
+
* the same view `SqliteVFS.as(cred)` gives in-process.
|
|
972
|
+
*/
|
|
973
|
+
export async function rpcDeleteFile(self, path, options = {}, cred) {
|
|
987
974
|
await ensureProgrammaticReady(self);
|
|
988
975
|
const p = String(path).replace(/^\/+/, '');
|
|
989
|
-
const vfs = self.sqliteFs.as(CRED_KERNEL);
|
|
976
|
+
const vfs = self.sqliteFs.as(cred === undefined ? CRED_KERNEL : requireVfsCred(cred, 'files.delete'));
|
|
990
977
|
if (!vfs.exists(p))
|
|
991
978
|
return;
|
|
992
979
|
if (vfs.isDirectory(p)) {
|
|
@@ -1212,10 +1199,17 @@ function resetInMemorySessionState(self) {
|
|
|
1212
1199
|
self.shell = null;
|
|
1213
1200
|
self.shellProcessPid = null;
|
|
1214
1201
|
self.terminal = null;
|
|
1215
|
-
self.
|
|
1202
|
+
self.facetManagerComposed = null;
|
|
1216
1203
|
self.facetProcessManager = null;
|
|
1217
1204
|
self.esbuildService = null;
|
|
1218
1205
|
self.viteDevServer = null;
|
|
1206
|
+
// The installer's pre-bundler and the dev server's on-demand path share
|
|
1207
|
+
// this pool; it goes when both of them go.
|
|
1208
|
+
try {
|
|
1209
|
+
self.bundlePool?.dispose();
|
|
1210
|
+
}
|
|
1211
|
+
catch { }
|
|
1212
|
+
self.bundlePool = null;
|
|
1219
1213
|
self.cirrusReal = null;
|
|
1220
1214
|
self._cirrusHmrWsClients = null;
|
|
1221
1215
|
self.nimbusWrangler = null;
|
package/dist/session/routes.d.ts
CHANGED
|
@@ -25,24 +25,22 @@
|
|
|
25
25
|
*/
|
|
26
26
|
type RoutesHost = any;
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
28
|
+
* Restore the dev server a previous isolate left behind.
|
|
29
|
+
*
|
|
30
|
+
* Hibernation takes the ViteDevServer and the whole port registry with it;
|
|
31
|
+
* only the `vite-config` blob survives in DO storage. Restoring it is the
|
|
32
|
+
* first thing every route that can reach that server does, so a woken session
|
|
33
|
+
* serves on all of them rather than on whichever one happened to carry the
|
|
34
|
+
* restore.
|
|
35
|
+
*
|
|
36
|
+
* `onlyPort` scopes the restore to a config that would listen there: a
|
|
37
|
+
* request for a port nothing ever persisted stays an honest 502, and a port
|
|
38
|
+
* something else already holds is left alone.
|
|
39
|
+
*
|
|
40
|
+
* Idempotent, and silent on failure — a session with no dev server to restore
|
|
41
|
+
* is the normal case, not an error.
|
|
42
42
|
*/
|
|
43
|
-
export declare function
|
|
44
|
-
/** Route a capability-authenticated embedder request to a guest HTTP server. */
|
|
45
|
-
export declare function routeCapabilityPort(self: RoutesHost, port: number, capability: string, request: Request, innerPath: string): Promise<Response>;
|
|
43
|
+
export declare function restorePersistedDevServer(self: RoutesHost, onlyPort?: number): Promise<void>;
|
|
46
44
|
/**
|
|
47
45
|
* The name-addressed door: `/app/<name>/…` — what the scoped `<name>--<sid>`
|
|
48
46
|
* host is forwarded as, and reachable in path form as `/s/<sid>/app/<name>/`.
|
|
@@ -52,6 +50,21 @@ export declare function routeCapabilityPort(self: RoutesHost, port: number, capa
|
|
|
52
50
|
* nothing holds is a 404, never a guess.
|
|
53
51
|
*/
|
|
54
52
|
export declare function routeToSessionApp(self: RoutesHost, name: string, request: Request, innerPath: string, capability?: string): Promise<Response>;
|
|
53
|
+
/**
|
|
54
|
+
* Accept a Vite HMR WebSocket for the running cirrus-real facet and wire it
|
|
55
|
+
* into the facet's HMR bridge.
|
|
56
|
+
*
|
|
57
|
+
* Handled in the DO, never through the port-registry proxy: that proxy moves a
|
|
58
|
+
* Request/Response over RPC and drops the `webSocket` handshake, so a HMR
|
|
59
|
+
* upgrade routed through it hangs. This is the ONE place a preview HMR socket
|
|
60
|
+
* is accepted — shared by the `/preview/` path and the `<port>--<sid>` host's
|
|
61
|
+
* `/port/N` route, so HMR works the same on both.
|
|
62
|
+
*
|
|
63
|
+
* ctx.acceptWebSocket (hibernatable) is required because HMR messages arrive
|
|
64
|
+
* from a DIFFERENT request context (the facet's long-poll RPC), and workerd
|
|
65
|
+
* forbids cross-request I/O on a `server.accept()`'d socket.
|
|
66
|
+
*/
|
|
67
|
+
export declare function acceptCirrusHmrWs(self: RoutesHost, request: Request): Response;
|
|
55
68
|
export declare function handleFetch(self: RoutesHost, request: Request): Promise<Response>;
|
|
56
69
|
export {};
|
|
57
70
|
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/session/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/session/routes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAkEH,KAAK,UAAU,GAAG,GAAG,CAAC;AAyBtB;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,yBAAyB,CAAC,IAAI,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuFlG;AAeD;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC,CAMnB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,QAAQ,CAiB9E;AAmDD,wBAAsB,WAAW,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CA0hCvF"}
|