@nimbus-sh/worker 0.3.0 → 0.5.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 +46 -43
- package/dist/_shared/preview-host.d.ts +14 -1
- package/dist/_shared/preview-host.d.ts.map +1 -1
- package/dist/_shared/preview-host.js +28 -3
- package/dist/_shared/session-router.d.ts +43 -0
- package/dist/_shared/session-router.d.ts.map +1 -1
- package/dist/_shared/session-router.js +59 -12
- package/dist/esbuild-wasm-bundle.generated.d.ts +1 -1
- package/dist/esbuild-wasm-bundle.generated.js +1 -1
- package/dist/facets/cirrus-real.js +1 -1
- package/dist/facets/durable-images.d.ts +50 -0
- package/dist/facets/durable-images.d.ts.map +1 -0
- package/dist/facets/durable-images.js +75 -0
- package/dist/facets/durable-slots.d.ts +45 -0
- package/dist/facets/durable-slots.d.ts.map +1 -0
- package/dist/facets/durable-slots.js +94 -0
- package/dist/facets/esbuild-transform.d.ts +10 -0
- package/dist/facets/esbuild-transform.d.ts.map +1 -0
- package/dist/facets/esbuild-transform.js +36 -0
- package/dist/facets/manager.d.ts +167 -12
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +575 -113
- package/dist/facets/opencode-staging.d.ts.map +1 -1
- package/dist/facets/opencode-staging.js +3 -4
- package/dist/facets/real-vite-hmr.js +1 -1
- package/dist/facets/vite-dev-server.d.ts +4 -4
- package/dist/facets/vite-dev-server.js +5 -5
- package/dist/git/commands.d.ts +22 -0
- package/dist/git/commands.d.ts.map +1 -1
- package/dist/git/commands.js +431 -424
- package/dist/git/network-facet.d.ts.map +1 -1
- package/dist/git/network-facet.js +6 -5
- 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.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -2
- 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 +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 -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/node-shims-artifact.generated.js +3 -3
- package/dist/npm/install-batch-facet.d.ts +1 -1
- package/dist/npm/install-batch-facet.js +1 -1
- package/dist/npm/installer.d.ts +6 -6
- package/dist/npm/installer.d.ts.map +1 -1
- package/dist/npm/installer.js +28 -27
- package/dist/npm/pre-bundle-facet.d.ts +2 -2
- package/dist/npm/pre-bundle-facet.js +5 -5
- package/dist/npm/r2-cache.d.ts +1 -1
- 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.d.ts.map +1 -1
- package/dist/router/index.js +61 -4
- package/dist/router/public-directory-do.d.ts +17 -0
- package/dist/router/public-directory-do.d.ts.map +1 -0
- package/dist/router/public-directory-do.js +16 -0
- package/dist/router/public-directory.d.ts +75 -0
- package/dist/router/public-directory.d.ts.map +1 -0
- package/dist/router/public-directory.js +115 -0
- package/dist/router/remote-api.d.ts +9 -1
- package/dist/router/remote-api.d.ts.map +1 -1
- package/dist/router/remote-api.js +20 -3
- package/dist/runtime/bun-repl.d.ts +1 -1
- package/dist/runtime/bun-repl.js +3 -3
- package/dist/runtime/esbuild-wasm-bytes.js +1 -1
- package/dist/runtime/facet-loader-host.d.ts +2 -2
- package/dist/runtime/facet-loader-host.js +3 -3
- package/dist/runtime/node-repl.js +2 -2
- package/dist/runtime/node-shims-artifact.js +1 -1
- package/dist/runtime/node-shims.d.ts +4 -1
- package/dist/runtime/node-shims.d.ts.map +1 -1
- package/dist/runtime/node-shims.js +326 -7
- package/dist/runtime/opencode-artifact.js +1 -1
- package/dist/runtime/opentui-wasm-bytes.js +1 -1
- package/dist/runtime/python-repl.js +3 -3
- package/dist/runtime/ruby-repl.js +2 -2
- package/dist/runtime/sqlite-wasm-bytes.js +1 -1
- package/dist/session/agent.d.ts +11 -0
- package/dist/session/agent.d.ts.map +1 -1
- package/dist/session/diag.js +1 -1
- package/dist/session/helpers.js +1 -1
- package/dist/session/hibernation.d.ts +8 -8
- package/dist/session/hibernation.d.ts.map +1 -1
- package/dist/session/hibernation.js +15 -15
- package/dist/session/init-phases.d.ts +19 -26
- package/dist/session/init-phases.d.ts.map +1 -1
- package/dist/session/init-phases.js +20 -43
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +24 -19
- package/dist/session/keys.d.ts +8 -0
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +12 -3
- package/dist/session/nimbus-session.d.ts +33 -19
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +78 -37
- package/dist/session/port-capability.d.ts +95 -8
- package/dist/session/port-capability.d.ts.map +1 -1
- package/dist/session/port-capability.js +165 -8
- package/dist/session/programmatic.d.ts +50 -4
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +112 -15
- package/dist/session/routes.d.ts +17 -0
- package/dist/session/routes.d.ts.map +1 -1
- package/dist/session/routes.js +111 -43
- package/dist/session/rpc.d.ts +7 -7
- package/dist/session/rpc.d.ts.map +1 -1
- package/dist/session/rpc.js +34 -23
- package/dist/session/shell-socket.d.ts +76 -0
- package/dist/session/shell-socket.d.ts.map +1 -0
- package/dist/session/shell-socket.js +172 -0
- package/dist/session/start-real-vite.js +2 -2
- package/dist/session/supervisor-op.d.ts +5 -0
- package/dist/session/supervisor-op.d.ts.map +1 -0
- package/dist/session/supervisor-op.js +74 -0
- package/dist/session/supervisor-rpc.d.ts +6 -10
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +98 -79
- package/dist/session/ws.d.ts +1 -3
- package/dist/session/ws.d.ts.map +1 -1
- package/dist/session/ws.js +23 -7
- package/package.json +4 -3
- package/public/_assets/runtime/{node-shims-fea906843f6d594e.js → node-shims-9e1380d9ee430fcc.js} +320 -5
- package/public/s/index.html +101 -5
- package/scripts/bundle-esbuild-wasm.mjs +2 -2
- package/scripts/bundle-facet-workers.mjs +5 -7
- package/scripts/bundle-sqlite-wasm.mjs +1 -1
|
@@ -29,15 +29,15 @@
|
|
|
29
29
|
* prefetch(cwd, entryCode) → Record<string, string>
|
|
30
30
|
*/
|
|
31
31
|
import { WorkerEntrypoint } from 'cloudflare:workers';
|
|
32
|
-
import {
|
|
32
|
+
import { hostNamespace, hostDispatchMethod } from '@nimbus-sh/platform/composition.js';
|
|
33
33
|
// W5: OOM discriminator — record last-known RPC frame on writeBatch entry
|
|
34
|
-
import { setLastRpcFrame } from '@nimbus-sh/
|
|
34
|
+
import { setLastRpcFrame } from '@nimbus-sh/platform/oom-discriminator.js';
|
|
35
35
|
// Phase 2 A'.2 — supervisor in-flight RPC payload byte tracking.
|
|
36
|
-
import { rpcPayloadStart, rpcPayloadEnd } from '@nimbus-sh/
|
|
36
|
+
import { rpcPayloadStart, rpcPayloadEnd } from '@nimbus-sh/platform/diag-counters.js';
|
|
37
37
|
// W4: R2 cross-tenant npm cache (tarballs + packuments)
|
|
38
38
|
import { R2CacheClient, MAX_R2_TARBALL_BYTES } from '../npm/r2-cache.js';
|
|
39
|
-
import { useRpcResource } from '@nimbus-sh/
|
|
40
|
-
import { W7_MAX_RECORD_BYTES } from '@nimbus-sh/
|
|
39
|
+
import { useRpcResource } from '@nimbus-sh/platform/rpc-dispose.js';
|
|
40
|
+
import { W7_MAX_RECORD_BYTES } from '@nimbus-sh/platform/w7-frame.js';
|
|
41
41
|
/**
|
|
42
42
|
* Drain the per-call event list captured by R2CacheClient during this
|
|
43
43
|
* SupervisorRPC call.
|
|
@@ -75,26 +75,45 @@ function _estimateWriteBatchBytes(payload) {
|
|
|
75
75
|
n += 80 + (i?.path?.length ?? 0);
|
|
76
76
|
return n;
|
|
77
77
|
}
|
|
78
|
-
// The fabric mints `env.SUPERVISOR` bindings for the programs it hosts;
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
setSupervisorEntrypointName('SupervisorRPC');
|
|
78
|
+
// The fabric mints `env.SUPERVISOR` bindings for the programs it hosts; the
|
|
79
|
+
// worker's composition root (src/index.ts) names this class to the fabric
|
|
80
|
+
// with composeFabric.
|
|
82
81
|
export class SupervisorRPC extends WorkerEntrypoint {
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
*
|
|
86
|
-
* Minted per call, because that is the only lifetime an instance of this
|
|
87
|
-
* class has: workerd constructs a NEW WorkerEntrypoint for every RPC call,
|
|
88
|
-
* not one per facet invocation, so a field cached here is written and
|
|
89
|
-
* discarded by the same call and never read by another (measured: two calls
|
|
90
|
-
* on one stub produced instances #3 and #4, each having served one call).
|
|
91
|
-
*/
|
|
92
|
-
_getStub() {
|
|
82
|
+
/** Resolve the composed host anew for each WorkerEntrypoint invocation. */
|
|
83
|
+
_dispatch() {
|
|
93
84
|
const doId = this.ctx.props?.doId;
|
|
94
|
-
if (
|
|
85
|
+
if (typeof doId !== 'string' || doId.length === 0) {
|
|
95
86
|
throw new Error('SupervisorRPC: missing doId in props');
|
|
96
|
-
|
|
97
|
-
|
|
87
|
+
}
|
|
88
|
+
const binding = hostNamespace();
|
|
89
|
+
const namespace = Reflect.get(this.env, binding);
|
|
90
|
+
if (!namespace || typeof namespace.idFromString !== 'function') {
|
|
91
|
+
throw new Error(`SupervisorRPC: env.${binding} is not a Durable Object namespace; `
|
|
92
|
+
+ "a workspace host names its own with composeFabric({ hostNamespace: '<binding>' })");
|
|
93
|
+
}
|
|
94
|
+
const stub = namespace.get(namespace.idFromString(doId));
|
|
95
|
+
const method = hostDispatchMethod();
|
|
96
|
+
const dispatch = Reflect.get(stub, method);
|
|
97
|
+
if (typeof dispatch !== 'function') {
|
|
98
|
+
throw new Error(`SupervisorRPC: the workspace host mounts no ${method}(); `
|
|
99
|
+
+ 'a host forwards one method to workspace.supervisorOp(op)');
|
|
100
|
+
}
|
|
101
|
+
// Never `.call(stub, …)` on an RpcStub: the proxy treats `call` as a
|
|
102
|
+
// property get, which becomes an RPC to a method literally named "call".
|
|
103
|
+
// `Reflect.apply` reaches the proxied member's apply trap instead, and on
|
|
104
|
+
// a plain-object host it still invokes the method with the stub as `this`.
|
|
105
|
+
return (envelope) => Reflect.apply(dispatch, stub, [envelope]);
|
|
106
|
+
}
|
|
107
|
+
_op(op, args = [], extra = {}) {
|
|
108
|
+
return this._dispatch()({ op, args, ...extra });
|
|
109
|
+
}
|
|
110
|
+
/** Stamp filesystem credentials from the binding, not the supplied arguments. */
|
|
111
|
+
_fsOp(op, args = []) {
|
|
112
|
+
return this._op(op, args, { pid: this._pid() });
|
|
113
|
+
}
|
|
114
|
+
_reportingPid() {
|
|
115
|
+
const pid = this.ctx.props?.pid;
|
|
116
|
+
return typeof pid === 'number' && Number.isInteger(pid) && pid > 0 ? pid : 0;
|
|
98
117
|
}
|
|
99
118
|
_call(promise) {
|
|
100
119
|
return useRpcResource(promise, (value) => value);
|
|
@@ -115,14 +134,14 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
115
134
|
}
|
|
116
135
|
// ── Filesystem RPC ────────────────────────────────────────────────────
|
|
117
136
|
async readFile(path) {
|
|
118
|
-
return this._call(this.
|
|
137
|
+
return this._call(this._fsOp('readFile', [path]));
|
|
119
138
|
}
|
|
120
139
|
/**
|
|
121
140
|
* Read a file as raw bytes. Used by the git network facet for binary
|
|
122
141
|
* object/pack files where the text readFile would corrupt content.
|
|
123
142
|
*/
|
|
124
143
|
async readFileBytes(path) {
|
|
125
|
-
return this._call(this.
|
|
144
|
+
return this._call(this._fsOp('readFileBytes', [path]));
|
|
126
145
|
}
|
|
127
146
|
async writeFile(path, content) {
|
|
128
147
|
// binary-fs wave: accept Uint8Array natively. Pre-fix this RPC was
|
|
@@ -130,55 +149,55 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
130
149
|
// decode every Uint8Array write — mangling bytes ≥ 0x80 to U+FFFD
|
|
131
150
|
// and corrupting binary content. RPC structured-clone handles
|
|
132
151
|
// Uint8Array transparently; downstream _rpcWriteFile also accepts
|
|
133
|
-
return this._call(this.
|
|
152
|
+
return this._call(this._fsOp('writeFile', [path, content]));
|
|
134
153
|
}
|
|
135
154
|
async stat(path) {
|
|
136
|
-
return this._call(this.
|
|
155
|
+
return this._call(this._fsOp('stat', [path]));
|
|
137
156
|
}
|
|
138
157
|
async lstat(path) {
|
|
139
|
-
return this._call(this.
|
|
158
|
+
return this._call(this._fsOp('lstat', [path]));
|
|
140
159
|
}
|
|
141
160
|
async hasLegacySymlinkUnder(path) {
|
|
142
|
-
return this._call(this.
|
|
161
|
+
return this._call(this._fsOp('hasLegacySymlinkUnder', [path]));
|
|
143
162
|
}
|
|
144
163
|
async utimes(path, atimeMs, mtimeMs) {
|
|
145
|
-
return this._call(this.
|
|
164
|
+
return this._call(this._fsOp('utimes', [path, atimeMs, mtimeMs]));
|
|
146
165
|
}
|
|
147
166
|
async chmod(path, mode) {
|
|
148
|
-
return this._call(this.
|
|
167
|
+
return this._call(this._fsOp('chmod', [path, mode]));
|
|
149
168
|
}
|
|
150
169
|
async access(path, mode) {
|
|
151
|
-
return this._call(this.
|
|
170
|
+
return this._call(this._fsOp('access', [path, mode]));
|
|
152
171
|
}
|
|
153
172
|
async chown(path, uid, gid, options) {
|
|
154
|
-
return this._call(this.
|
|
173
|
+
return this._call(this._fsOp('chown', [path, uid, gid, options]));
|
|
155
174
|
}
|
|
156
175
|
async setUmask(mask) {
|
|
157
|
-
return this._call(this.
|
|
176
|
+
return this._call(this._fsOp('setUmask', [mask]));
|
|
158
177
|
}
|
|
159
178
|
async readdir(path) {
|
|
160
|
-
return this._call(this.
|
|
179
|
+
return this._call(this._fsOp('readdir', [path]));
|
|
161
180
|
}
|
|
162
181
|
async exists(path) {
|
|
163
|
-
return this._call(this.
|
|
182
|
+
return this._call(this._fsOp('exists', [path]));
|
|
164
183
|
}
|
|
165
184
|
async mkdir(path) {
|
|
166
|
-
return this._call(this.
|
|
185
|
+
return this._call(this._fsOp('mkdir', [path]));
|
|
167
186
|
}
|
|
168
187
|
async rmdir(path) {
|
|
169
|
-
return this._call(this.
|
|
188
|
+
return this._call(this._fsOp('rmdir', [path]));
|
|
170
189
|
}
|
|
171
190
|
async rename(from, to) {
|
|
172
|
-
return this._call(this.
|
|
191
|
+
return this._call(this._fsOp('rename', [from, to]));
|
|
173
192
|
}
|
|
174
193
|
async unlink(path) {
|
|
175
|
-
return this._call(this.
|
|
194
|
+
return this._call(this._fsOp('unlink', [path]));
|
|
176
195
|
}
|
|
177
196
|
async readlink(path) {
|
|
178
|
-
return this._call(this.
|
|
197
|
+
return this._call(this._fsOp('readlink', [path]));
|
|
179
198
|
}
|
|
180
199
|
async symlink(target, path) {
|
|
181
|
-
return this._call(this.
|
|
200
|
+
return this._call(this._fsOp('symlink', [target, path]));
|
|
182
201
|
}
|
|
183
202
|
/**
|
|
184
203
|
* ACQUIRE: the paths mutated since the facet's cursor, plus a fresh
|
|
@@ -192,10 +211,10 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
192
211
|
* disposal, which targets the returned value.
|
|
193
212
|
*/
|
|
194
213
|
async fsAcquire(epoch, cursor) {
|
|
195
|
-
return this._call(this.
|
|
214
|
+
return this._call(this._fsOp('fsAcquire', [epoch, cursor]));
|
|
196
215
|
}
|
|
197
216
|
async fsRevision(path) {
|
|
198
|
-
return this._call(this.
|
|
217
|
+
return this._call(this._fsOp('fsRevision', [path]));
|
|
199
218
|
}
|
|
200
219
|
/**
|
|
201
220
|
* Enumerate the session filesystem, one bounded page at a time.
|
|
@@ -215,7 +234,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
215
234
|
* never mistaken for a complete listing.
|
|
216
235
|
*/
|
|
217
236
|
async fsList(after, limit) {
|
|
218
|
-
return this._call(this.
|
|
237
|
+
return this._call(this._fsOp('fsList', [after ?? null, limit ?? null]));
|
|
219
238
|
}
|
|
220
239
|
/**
|
|
221
240
|
* WebSocket relay. A facet does not open its own sockets: the supervisor
|
|
@@ -226,28 +245,28 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
226
245
|
* facet's next synchronous read serves bytes the authority has replaced.
|
|
227
246
|
*/
|
|
228
247
|
async wsOpen(url, protocols) {
|
|
229
|
-
return this._call(this.
|
|
248
|
+
return this._call(this._fsOp('wsOpen', [url, protocols]));
|
|
230
249
|
}
|
|
231
250
|
async wsPoll(id, waitMs) {
|
|
232
|
-
return this._call(this.
|
|
251
|
+
return this._call(this._fsOp('wsPoll', [id, waitMs]));
|
|
233
252
|
}
|
|
234
253
|
async wsSend(id, text, bytes) {
|
|
235
|
-
return this._call(this.
|
|
254
|
+
return this._call(this._fsOp('wsSend', [id, text, bytes]));
|
|
236
255
|
}
|
|
237
256
|
async wsClose(id, code, reason) {
|
|
238
|
-
return this._call(this.
|
|
257
|
+
return this._call(this._fsOp('wsClose', [id, code, reason]));
|
|
239
258
|
}
|
|
240
259
|
async fsOpen(path, flags) {
|
|
241
|
-
return this._call(this.
|
|
260
|
+
return this._call(this._fsOp('fsOpen', [path, flags]));
|
|
242
261
|
}
|
|
243
262
|
async fsRead(handleId, offset, length) {
|
|
244
|
-
return this._call(this.
|
|
263
|
+
return this._call(this._fsOp('fsRead', [handleId, offset, length]));
|
|
245
264
|
}
|
|
246
265
|
async fsWrite(handleId, offset, bytes) {
|
|
247
|
-
return this._call(this.
|
|
266
|
+
return this._call(this._fsOp('fsWrite', [handleId, offset, bytes]));
|
|
248
267
|
}
|
|
249
268
|
async fsClose(handleId) {
|
|
250
|
-
return this._call(this.
|
|
269
|
+
return this._call(this._fsOp('fsClose', [handleId]));
|
|
251
270
|
}
|
|
252
271
|
/**
|
|
253
272
|
* Stateless ranged ops. Unlike fsOpen/fsRead/fsWrite they carry no
|
|
@@ -255,7 +274,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
255
274
|
* hibernation and never rewrite whole files for partial updates.
|
|
256
275
|
*/
|
|
257
276
|
async fsReadRange(path, offset, length) {
|
|
258
|
-
return this._call(this.
|
|
277
|
+
return this._call(this._fsOp('fsReadRange', [path, offset, length]));
|
|
259
278
|
}
|
|
260
279
|
/**
|
|
261
280
|
* The same read with the session's content cache bypassed, for a boot spec's
|
|
@@ -264,7 +283,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
264
283
|
* session's heap for the rest of its life.
|
|
265
284
|
*/
|
|
266
285
|
async fsReadRangeUncached(path, offset, length) {
|
|
267
|
-
return this._call(this.
|
|
286
|
+
return this._call(this._fsOp('fsReadRangeUncached', [path, offset, length]));
|
|
268
287
|
}
|
|
269
288
|
/**
|
|
270
289
|
* Read many ranges in ONE round trip — the read-side counterpart to
|
|
@@ -288,23 +307,23 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
288
307
|
setLastRpcFrame('fsReadBatch', payloadBytes);
|
|
289
308
|
rpcPayloadStart(payloadBytes);
|
|
290
309
|
try {
|
|
291
|
-
return await this._call(this.
|
|
310
|
+
return await this._call(this._fsOp('fsReadBatch', [requests]));
|
|
292
311
|
}
|
|
293
312
|
finally {
|
|
294
313
|
rpcPayloadEnd(payloadBytes);
|
|
295
314
|
}
|
|
296
315
|
}
|
|
297
316
|
async fsWriteRange(path, offset, bytes) {
|
|
298
|
-
return this._call(this.
|
|
317
|
+
return this._call(this._fsOp('fsWriteRange', [path, offset, bytes]));
|
|
299
318
|
}
|
|
300
319
|
async fsAppend(path, moduleId, operationId, bytes) {
|
|
301
|
-
return this._call(this.
|
|
320
|
+
return this._call(this._op('fsAppend', [path, moduleId, operationId, bytes], { pid: this._pid(), writerId: this._writerId() }));
|
|
302
321
|
}
|
|
303
322
|
async fsAppendAck(moduleId, operationId) {
|
|
304
|
-
return this._call(this.
|
|
323
|
+
return this._call(this._fsOp('fsAppendAck', [this._writerId(), moduleId, operationId]));
|
|
305
324
|
}
|
|
306
325
|
async fsTruncate(path, size) {
|
|
307
|
-
return this._call(this.
|
|
326
|
+
return this._call(this._fsOp('fsTruncate', [path, size]));
|
|
308
327
|
}
|
|
309
328
|
/**
|
|
310
329
|
* Bulk-write all inodes + chunks in ONE transactionSync on the supervisor.
|
|
@@ -327,7 +346,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
327
346
|
setLastRpcFrame('writeBatch', payloadBytes);
|
|
328
347
|
rpcPayloadStart(payloadBytes);
|
|
329
348
|
try {
|
|
330
|
-
return await this._call(this.
|
|
349
|
+
return await this._call(this._fsOp('writeBatch', [payload]));
|
|
331
350
|
}
|
|
332
351
|
finally {
|
|
333
352
|
rpcPayloadEnd(payloadBytes);
|
|
@@ -358,7 +377,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
358
377
|
rpcPayloadStart(STREAM_RESIDENT_BYTES);
|
|
359
378
|
try {
|
|
360
379
|
const mutationOwner = this.ctx.props?.mutationOwner;
|
|
361
|
-
return await this._call(this.
|
|
380
|
+
return await this._call(this._op('writeBatchStream', [], { pid: this._pid(), mutationOwner: typeof mutationOwner === 'string' ? mutationOwner : undefined, stream }));
|
|
362
381
|
}
|
|
363
382
|
finally {
|
|
364
383
|
rpcPayloadEnd(STREAM_RESIDENT_BYTES);
|
|
@@ -386,7 +405,7 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
386
405
|
const payloadBytes = (Array.isArray(entries) ? entries.length : 0) * REGISTRY_ENTRY_BYTES;
|
|
387
406
|
rpcPayloadStart(payloadBytes);
|
|
388
407
|
try {
|
|
389
|
-
return await this._call(this.
|
|
408
|
+
return await this._call(this._op('putRegistryEntries', [entries]));
|
|
390
409
|
}
|
|
391
410
|
finally {
|
|
392
411
|
rpcPayloadEnd(payloadBytes);
|
|
@@ -479,10 +498,10 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
479
498
|
}
|
|
480
499
|
// ── Process I/O ───────────────────────────────────────────────────────
|
|
481
500
|
async stdout(data) {
|
|
482
|
-
return this._call(this.
|
|
501
|
+
return this._call(this._op('stdout', [data], { pid: this._reportingPid() }));
|
|
483
502
|
}
|
|
484
503
|
async stderr(data) {
|
|
485
|
-
return this._call(this.
|
|
504
|
+
return this._call(this._op('stderr', [data], { pid: this._reportingPid() }));
|
|
486
505
|
}
|
|
487
506
|
/**
|
|
488
507
|
* Report process exit to the supervisor. Called from the facet's own
|
|
@@ -495,18 +514,18 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
495
514
|
*/
|
|
496
515
|
async reportExit(code, tail) {
|
|
497
516
|
const pid = this.ctx.props?.pid || 0;
|
|
498
|
-
return this._call(this.
|
|
517
|
+
return this._call(this._op('reportExit', [code, tail || ''], { pid }));
|
|
499
518
|
}
|
|
500
519
|
// ── Prefetch ──────────────────────────────────────────────────────────
|
|
501
520
|
async prefetch(cwd, entryCode) {
|
|
502
|
-
return this._call(this.
|
|
521
|
+
return this._call(this._op('prefetch', [cwd, entryCode]));
|
|
503
522
|
}
|
|
504
523
|
// ── Port registration ─────────────────────────────────────────────────
|
|
505
524
|
async registerPort(port) {
|
|
506
|
-
return this._call(this.
|
|
525
|
+
return this._call(this._op('registerPort', [port], { pid: this._reportingPid() }));
|
|
507
526
|
}
|
|
508
527
|
async unregisterPort(port) {
|
|
509
|
-
return this._call(this.
|
|
528
|
+
return this._call(this._op('unregisterPort', [port]));
|
|
510
529
|
}
|
|
511
530
|
/**
|
|
512
531
|
* Route an in-session loopback HTTP request (a facet's fetch to
|
|
@@ -521,11 +540,11 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
521
540
|
* exactly how PortRegistry.routeRequest returns the facet's Response as-is.
|
|
522
541
|
*/
|
|
523
542
|
async routeLoopback(port, request) {
|
|
524
|
-
return this.
|
|
543
|
+
return this._op('routeLoopback', [port, request]);
|
|
525
544
|
}
|
|
526
545
|
// ── Esbuild transform ─────────────────────────────────────────────────
|
|
527
546
|
async transform(code, loader) {
|
|
528
|
-
return this._call(this.
|
|
547
|
+
return this._call(this._op('transform', [code, loader]));
|
|
529
548
|
}
|
|
530
549
|
// ── child_process [W8 Phase 1] ────────────────────────────────────────
|
|
531
550
|
//
|
|
@@ -534,28 +553,28 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
534
553
|
// route through the shared FacetProcessManager.
|
|
535
554
|
//
|
|
536
555
|
async cpSpawn(req) {
|
|
537
|
-
return this._call(this.
|
|
556
|
+
return this._call(this._op('cpSpawn', [{ ...req, parentPid: this._pid() }]));
|
|
538
557
|
}
|
|
539
558
|
async cpStdinWrite(childPid, data) {
|
|
540
|
-
return this._call(this.
|
|
559
|
+
return this._call(this._op('cpStdinWrite', [childPid, data]));
|
|
541
560
|
}
|
|
542
561
|
async cpStdinEnd(childPid) {
|
|
543
|
-
return this._call(this.
|
|
562
|
+
return this._call(this._op('cpStdinEnd', [childPid]));
|
|
544
563
|
}
|
|
545
564
|
async cpReadStdin(childPid, waitMs) {
|
|
546
|
-
return this._call(this.
|
|
565
|
+
return this._call(this._op('cpReadStdin', [childPid, waitMs]));
|
|
547
566
|
}
|
|
548
567
|
async cpReadOutput(childPid, fd, sinceSeq, waitMs) {
|
|
549
|
-
return this._call(this.
|
|
568
|
+
return this._call(this._op('cpReadOutput', [childPid, fd, sinceSeq, waitMs]));
|
|
550
569
|
}
|
|
551
570
|
async cpDrainOutput(childPid) {
|
|
552
|
-
return this._call(this.
|
|
571
|
+
return this._call(this._op('cpDrainOutput', [childPid]));
|
|
553
572
|
}
|
|
554
573
|
async cpKill(childPid, signal) {
|
|
555
|
-
return this._call(this.
|
|
574
|
+
return this._call(this._op('cpKill', [childPid, signal]));
|
|
556
575
|
}
|
|
557
576
|
async cpWait(childPid, waitMs) {
|
|
558
|
-
return this._call(this.
|
|
577
|
+
return this._call(this._op('cpWait', [childPid, waitMs]));
|
|
559
578
|
}
|
|
560
579
|
/**
|
|
561
580
|
* child-process isolation gap #1: dispatch a single cp.spawn request inline using
|
|
@@ -566,6 +585,6 @@ export class SupervisorRPC extends WorkerEntrypoint {
|
|
|
566
585
|
* supervisor while keeping the dispatch envelope in a fresh isolate.
|
|
567
586
|
*/
|
|
568
587
|
async cpDispatchInline(req, kind) {
|
|
569
|
-
return this._call(this.
|
|
588
|
+
return this._call(this._op('cpDispatchInline', [req, kind]));
|
|
570
589
|
}
|
|
571
590
|
}
|
package/dist/session/ws.d.ts
CHANGED
|
@@ -60,15 +60,13 @@ export interface WsHost {
|
|
|
60
60
|
_w9PersistWired: boolean;
|
|
61
61
|
_w9FlushTimer: any;
|
|
62
62
|
_w9SchemaInit: boolean;
|
|
63
|
-
_isolateGen: number;
|
|
64
|
-
_isolateGenPersisted: boolean;
|
|
65
63
|
_w9WsConfig: any;
|
|
66
64
|
_diagPeakRss: number;
|
|
67
65
|
_diagPeakHeapUsed: number;
|
|
68
66
|
_w5LastPersistAt: number;
|
|
69
67
|
_w5LastPersistRingSize: number;
|
|
70
68
|
/** [B'.4] live phase indicator — see nimbus-session-internal.d.ts */
|
|
71
|
-
_b4Phase: import('@nimbus-sh/
|
|
69
|
+
_b4Phase: import('@nimbus-sh/platform/oom-discriminator.js').SessionState | null;
|
|
72
70
|
_w5PersistRing(): Promise<void> | null;
|
|
73
71
|
_w9FlushOnClose(): void;
|
|
74
72
|
}
|
package/dist/session/ws.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/session/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/session/ws.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAOH,OAAO,EAIL,KAAK,UAAU,EAChB,MAAM,eAAe,CAAC;AAIvB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uDAAuD,CAAC;AACtG,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mCAAmC,CAAC;AACnE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAG7E;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,SAAS,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC;IAC9B,mBAAmB,EAAE,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACnD;;;;8DAI0D;IAC1D,YAAY,CAAC,EAAE,GAAG,CAAC,SAAS,EAAE,UAAU,EAAE,CAAC,CAAC;IAC5C,SAAS,EAAE,wBAAwB,CAAC;IACpC,wBAAwB,EAAE,OAAO,CAAC;IAClC,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,GAAG,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,GAAG,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,EAAE,OAAO,0CAA0C,EAAE,YAAY,GAAG,IAAI,CAAC;IACjF,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvC,eAAe,IAAI,IAAI,CAAC;CACzB;AAgDD;;;;;;;GAOG;AACH,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AA0CD,wBAAgB,MAAM,CAAC,EAAE,EAAE,SAAS,GAAG,YAAY,CAElD;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA4EzG;AA+CD,wBAAsB,OAAO,CAC3B,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,SAAS,EACb,KAAK,CAAC,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,OAAO,GAClB,OAAO,CAAC,IAAI,CAAC,CAiGf;AAED,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CA4EtF;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAUlD"}
|
package/dist/session/ws.js
CHANGED
|
@@ -33,12 +33,14 @@
|
|
|
33
33
|
* touches ctx.waitUntil; uses `(host.ctx as any)` cast).
|
|
34
34
|
*/
|
|
35
35
|
import { dec } from '@nimbus-sh/core/_shared/bytes.js';
|
|
36
|
-
import { recordFailure, getLastRpcFrame, getLastFacetId, recordRecoveryEvent } from '@nimbus-sh/
|
|
36
|
+
import { recordFailure, getLastRpcFrame, getLastFacetId, recordRecoveryEvent } from '@nimbus-sh/platform/oom-discriminator.js';
|
|
37
|
+
import { generation } from '@nimbus-sh/fabric/generation.js';
|
|
37
38
|
import { persistShellState } from './state-store.js';
|
|
38
39
|
import { handleFsWatchSubscribe, handleFsWatchUnsubscribe, cleanupFsWatchOnClose, } from './fs-watch.js';
|
|
39
40
|
import { parseProcessLogClientFrame } from '@nimbus-sh/core/runtime/process-io-protocol.js';
|
|
40
41
|
import { applyProcessClientFrame } from '@nimbus-sh/core/runtime/process-input-routing.js';
|
|
41
42
|
import { z } from 'zod/v4';
|
|
43
|
+
import { clearShellSocketStamp, noteShellSocketActivity } from './shell-socket.js';
|
|
42
44
|
import { PRIOR_GENERATION_EXIT_REASON } from './rpc.js';
|
|
43
45
|
/**
|
|
44
46
|
* Snapshot the live Shell state and write it through to DO SQLite
|
|
@@ -150,6 +152,12 @@ export async function wsMessage(self, ws, message) {
|
|
|
150
152
|
await routeProcessLogClientMessage(self, ws, attach, message);
|
|
151
153
|
return;
|
|
152
154
|
}
|
|
155
|
+
// A frame arriving is the peer proving it is still on this socket.
|
|
156
|
+
// The stamp goes in the attachment because isolate memory does not
|
|
157
|
+
// survive hibernation and the /ws upgrade has to read it afterwards.
|
|
158
|
+
// Rate-limited inside; see src/session/shell-socket.ts.
|
|
159
|
+
if (attach?.kind === 'shell')
|
|
160
|
+
noteShellSocketActivity(ws);
|
|
153
161
|
const data = typeof message === 'string' ? message : dec.decode(message);
|
|
154
162
|
const value = JSON.parse(data);
|
|
155
163
|
// file-tree-watch (2026-05-15): handle fs-watch-* on this WS BEFORE
|
|
@@ -303,6 +311,11 @@ export async function wsClose(self, ws, _code, _reason, _wasClean) {
|
|
|
303
311
|
// wrangler dev) + long-running facets must still survive the
|
|
304
312
|
// terminal reconnect (see 607e472 — do NOT kill running processes
|
|
305
313
|
// here). Only reap per-tab state.
|
|
314
|
+
// The runtime says this socket is finished, so it no longer holds the
|
|
315
|
+
// terminal. Drop its liveness stamp before anything else: the frame
|
|
316
|
+
// that triggered this close stamped it moments ago, and leaving that
|
|
317
|
+
// behind would make the reconnect look like a second tab.
|
|
318
|
+
clearShellSocketStamp(ws);
|
|
306
319
|
// ── Phase 3 B'.1: transitionTo('drained') ──────────────────────────
|
|
307
320
|
// The Track B' state-machine transition. Persist final shell
|
|
308
321
|
// state + record a recovery_event BEFORE we null the in-memory
|
|
@@ -320,7 +333,7 @@ export async function wsClose(self, ws, _code, _reason, _wasClean) {
|
|
|
320
333
|
fromState: 'active',
|
|
321
334
|
toState: 'drained',
|
|
322
335
|
trigger: 'ws-close',
|
|
323
|
-
isolateGen: self.
|
|
336
|
+
isolateGen: generation(self.ctx),
|
|
324
337
|
dataLoss: false,
|
|
325
338
|
snapshotKeysRehydrated: 0,
|
|
326
339
|
});
|
|
@@ -351,10 +364,9 @@ export async function wsClose(self, ws, _code, _reason, _wasClean) {
|
|
|
351
364
|
// upgrade calls terminal.attach(newWs) to swap in the new socket.
|
|
352
365
|
//
|
|
353
366
|
// Pre-B'.5 we nulled these three fields to avoid two-tab cross-
|
|
354
|
-
// wiring
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
// rejoin" (warmJoin path) from "active session busy" (still 409).
|
|
367
|
+
// wiring, which cost every reconnect a full rebuild. The upgrade now
|
|
368
|
+
// reads the sockets instead: a shell socket a peer still holds is the
|
|
369
|
+
// busy session it refuses, and this one is closing.
|
|
358
370
|
// Reset the one-shot "wrangler alias" banner so a reconnecting user
|
|
359
371
|
// sees it again — terminal-lifetime state, not session-lifetime.
|
|
360
372
|
self.wranglerAliasBannerShown = false;
|
|
@@ -386,6 +398,10 @@ export async function wsError(self, ws, _error) {
|
|
|
386
398
|
catch { /* best-effort */ }
|
|
387
399
|
return;
|
|
388
400
|
}
|
|
401
|
+
// Same reasoning as wsClose: this socket stops holding the terminal
|
|
402
|
+
// here. It matters more on this path, because the handler that just
|
|
403
|
+
// blew the 5-s cap is the one that stamped the socket.
|
|
404
|
+
clearShellSocketStamp(ws);
|
|
389
405
|
// ── Phase 3 B'.1: transitionTo('drained') ──────────────────────────
|
|
390
406
|
// Same architectural step as wsClose: persist shell state + record
|
|
391
407
|
// a drained event before nulling. wsError is a different physical
|
|
@@ -400,7 +416,7 @@ export async function wsError(self, ws, _error) {
|
|
|
400
416
|
fromState: 'active',
|
|
401
417
|
toState: 'drained',
|
|
402
418
|
trigger: 'ws-error',
|
|
403
|
-
isolateGen: self.
|
|
419
|
+
isolateGen: generation(self.ctx),
|
|
404
420
|
dataLoss: false,
|
|
405
421
|
snapshotKeysRehydrated: 0,
|
|
406
422
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nimbus-sh/worker",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Nimbus runtime package for Cloudflare Workers, re-exported publicly through @nimbus-sh/sdk/worker.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -84,8 +84,9 @@
|
|
|
84
84
|
},
|
|
85
85
|
"dependencies": {
|
|
86
86
|
"@ai-sdk/openai-compatible": "^2.0.41",
|
|
87
|
-
"@nimbus-sh/core": "^0.
|
|
88
|
-
"@nimbus-sh/fabric": "^0.
|
|
87
|
+
"@nimbus-sh/core": "^0.7.0",
|
|
88
|
+
"@nimbus-sh/fabric": "^0.3.0",
|
|
89
|
+
"@nimbus-sh/platform": "^0.2.0",
|
|
89
90
|
"@renovatebot/pep440": "^5.0.0",
|
|
90
91
|
"ai": "^6.0.182",
|
|
91
92
|
"clean-git-ref": "^2.0.1",
|