devframe 0.8.2 → 0.9.0-beta.1

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.
Files changed (103) hide show
  1. package/dist/_shared-CttmCMBi.mjs +70 -0
  2. package/dist/_shared-DmpWW-HH.d.mts +43 -0
  3. package/dist/adapters/build.d.mts +1 -1
  4. package/dist/adapters/build.mjs +6 -5
  5. package/dist/adapters/cac.d.mts +1 -1
  6. package/dist/adapters/cac.mjs +157 -1
  7. package/dist/adapters/dev.d.mts +20 -42
  8. package/dist/adapters/dev.mjs +2 -1
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +223 -0
  11. package/dist/adapters/initiate.mjs +2 -0
  12. package/dist/adapters/mcp.d.mts +26 -2
  13. package/dist/adapters/mcp.mjs +2 -2
  14. package/dist/cli/main.mjs +2 -2
  15. package/dist/client/index.d.mts +70 -6
  16. package/dist/client/index.mjs +743 -408
  17. package/dist/constants.d.mts +19 -6
  18. package/dist/constants.mjs +19 -6
  19. package/dist/{context-CBg8iRbQ.mjs → context-C33rR7n0.mjs} +1 -1
  20. package/dist/{context-DoSozfvx.d.mts → context-CSCwoABx.d.mts} +1 -1
  21. package/dist/{host-h3-D2hYZvDK.mjs → context-Du11TLcO.mjs} +5 -255
  22. package/dist/dev-BEveeA5H.mjs +233 -0
  23. package/dist/{devframe-mQ4qZAuo.d.mts → devframe-Bdd9YXTy.d.mts} +88 -25
  24. package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DLYH5oD3.mjs} +16 -0
  25. package/dist/dist-D8jAMwxU.mjs +227 -0
  26. package/dist/helpers/vite.d.mts +43 -39
  27. package/dist/helpers/vite.mjs +26 -38
  28. package/dist/host-agent-CpbAoqBx.mjs +230 -0
  29. package/dist/host-h3-CzCZwtuV.mjs +26 -0
  30. package/dist/{fetch-BKaD0Vb3.mjs → http-DLr_cJZ7.mjs} +42 -4
  31. package/dist/{index-b64Uuhy2.d.mts → index-CZRd2K1C.d.mts} +2 -2
  32. package/dist/{index-Dbw1p5ch.d.mts → index-DSCq4M4q.d.mts} +2 -31
  33. package/dist/index.d.mts +9 -4
  34. package/dist/index.mjs +8 -1
  35. package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-BQqGoDO9.mjs} +9 -2
  36. package/dist/instance-shell-DBTXTSjN.d.mts +264 -0
  37. package/dist/instance-shell-KRdee1QF.mjs +485 -0
  38. package/dist/internal/index.d.mts +150 -0
  39. package/dist/internal/index.mjs +7 -0
  40. package/dist/node/auth.d.mts +2 -2
  41. package/dist/node/hub-internals.d.mts +2 -14
  42. package/dist/node/hub-internals.mjs +2 -2
  43. package/dist/node/index.d.mts +3 -267
  44. package/dist/node/index.mjs +3 -5
  45. package/dist/recipes/common-rpc-functions.d.mts +3 -3
  46. package/dist/recipes/interactive-auth.d.mts +3 -3
  47. package/dist/recipes/interactive-auth.mjs +6 -6
  48. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  49. package/dist/rpc/dump.d.mts +77 -1
  50. package/dist/rpc/dump.mjs +1 -1
  51. package/dist/rpc/index.d.mts +2 -2
  52. package/dist/rpc/index.mjs +3 -17
  53. package/dist/rpc/transports/sse-client.d.mts +2 -0
  54. package/dist/rpc/transports/sse-client.mjs +142 -0
  55. package/dist/rpc/transports/sse-server.d.mts +61 -0
  56. package/dist/rpc/transports/sse-server.mjs +209 -0
  57. package/dist/rpc/transports/ws-bun.d.mts +35 -0
  58. package/dist/rpc/transports/ws-bun.mjs +31 -0
  59. package/dist/rpc/transports/ws-client.d.mts +1 -1
  60. package/dist/rpc/transports/ws-client.mjs +10 -18
  61. package/dist/rpc/transports/ws-server.d.mts +2 -2
  62. package/dist/rpc/transports/ws-server.mjs +2 -235
  63. package/dist/rpc-core-CGctXToO.d.mts +48 -0
  64. package/dist/rpc-core-CqcJkAu9.mjs +81 -0
  65. package/dist/{serialization-C8Mnw9hK.mjs → serialization-BHY8M5NQ.mjs} +1 -1
  66. package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
  67. package/dist/sse-client-BsIK3hMD.d.mts +37 -0
  68. package/dist/{dump-CgZShDRB.mjs → static-CvjkO8QE.mjs} +2 -265
  69. package/dist/{storage-CTNVbuFB.mjs → storage-BcmVEJRc.mjs} +2 -2
  70. package/dist/types/index.d.mts +3 -3
  71. package/dist/types/index.mjs +1 -6
  72. package/dist/utils/events.d.mts +1 -1
  73. package/dist/utils/launch-editor.mjs +1 -25
  74. package/dist/utils/open.mjs +1 -1
  75. package/dist/utils/serve-static.mjs +1 -1
  76. package/dist/utils/shared-state.d.mts +1 -1
  77. package/dist/utils/shared-state.mjs +1 -1
  78. package/dist/utils/simple-schema.d.mts +101 -1
  79. package/dist/utils/streaming-channel.d.mts +1 -1
  80. package/dist/validation-BiuDXTKW.mjs +266 -0
  81. package/dist/wire-codec-9VhIKnyp.mjs +58 -0
  82. package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-CdNuATYa.d.mts} +6 -1
  83. package/dist/ws-server-Bk1hOG3X.mjs +267 -0
  84. package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-Ces08fjP.d.mts} +89 -8
  85. package/package.json +10 -9
  86. package/skills/devframe/SKILL.md +84 -85
  87. package/dist/_shared-bWRzeSa0.mjs +0 -19
  88. package/dist/adapters/cli.d.mts +0 -11
  89. package/dist/adapters/cli.mjs +0 -6
  90. package/dist/cac-D4_9ok0z.mjs +0 -157
  91. package/dist/dev-Bc49u3qg.mjs +0 -439
  92. package/dist/http-C4T68YV6.mjs +0 -38
  93. package/dist/index-vAX6bYuc.d.mts +0 -78
  94. package/dist/recipes/open-helpers.d.mts +0 -6
  95. package/dist/recipes/open-helpers.mjs +0 -6
  96. package/dist/server-CD8XmBXl.mjs +0 -167
  97. package/dist/server-UHWae43Q.d.mts +0 -149
  98. package/dist/simple-schema-BDzLeJDk.d.mts +0 -102
  99. package/dist/utils/promise.d.mts +0 -8
  100. package/dist/utils/promise.mjs +0 -15
  101. package/dist/utils/scope.d.mts +0 -11
  102. package/dist/utils/scope.mjs +0 -15
  103. package/dist/{open-Deb5xmIT.mjs → open-Dz4V4irZ.mjs} +1 -1
@@ -0,0 +1,485 @@
1
+ import "./constants.mjs";
2
+ import { t as diagnostics } from "./diagnostics-DLYH5oD3.mjs";
3
+ import { t as getInternalContext } from "./context-C33rR7n0.mjs";
4
+ import { createInteractiveAuth } from "./recipes/interactive-auth.mjs";
5
+ import { createServer } from "node:http";
6
+ import process from "node:process";
7
+ import { isIP } from "node:net";
8
+ import { joinURL, withLeadingSlash, withoutLeadingSlash, withoutTrailingSlash } from "ufo";
9
+ import { H3, defineHandler, toNodeHandler } from "h3";
10
+ //#region src/node/utils.ts
11
+ const NON_DIALABLE_HOSTS = /* @__PURE__ */ new Set([
12
+ "0.0.0.0",
13
+ "127.0.0.1",
14
+ "::",
15
+ "0000:0000:0000:0000:0000:0000:0000:0000",
16
+ ""
17
+ ]);
18
+ /** Map a bind host to a host a client can actually connect to. */
19
+ function toDialableHost(host) {
20
+ return NON_DIALABLE_HOSTS.has(host) ? "localhost" : host;
21
+ }
22
+ /** Format a bind host for use in a URL authority (dialable, IPv6-bracketed). */
23
+ function formatHostForUrl(host) {
24
+ const dialable = toDialableHost(host);
25
+ return isIP(dialable) === 6 ? `[${dialable}]` : dialable;
26
+ }
27
+ function normalizeHttpServerUrl(host, port) {
28
+ return `http://${formatHostForUrl(host)}:${port}`;
29
+ }
30
+ //#endregion
31
+ //#region src/node/instance-shell.ts
32
+ /**
33
+ * Compose an h3 + WebSocket RPC server for a devframe context — the low-level
34
+ * "listen on a port (or share one) + attach the WS transport" binding the
35
+ * side-car and shared-server tiers below are built on. Owns and listens on a
36
+ * fresh `node:http` server unless `server` is supplied, in which case it only
37
+ * attaches the upgrade listener and leaves that server's lifecycle to its
38
+ * owner.
39
+ */
40
+ async function bindHttpAndWs(options) {
41
+ const { context, port, core } = options;
42
+ const bindHost = options.host;
43
+ const app = new H3();
44
+ const ownsHttpServer = !options.server;
45
+ const httpServer = options.server ?? createServer(toNodeHandler(app));
46
+ const rpcHost = context.rpc;
47
+ const websocket = options.websocket !== false;
48
+ let ws;
49
+ let closeWs = async () => {};
50
+ if (websocket) {
51
+ const { attachWsRpcTransport } = await import("./rpc/transports/ws-server.mjs");
52
+ const transport = attachWsRpcTransport(core.rpcGroup, {
53
+ server: httpServer,
54
+ path: options.path,
55
+ destroyUnmatched: options.destroyUnmatched ?? ownsHttpServer,
56
+ allowedOrigins: options.allowedOrigins,
57
+ onConnected: core.onConnected,
58
+ onDisconnected: core.onDisconnected
59
+ });
60
+ ws = transport.ws;
61
+ closeWs = transport.close;
62
+ }
63
+ if (ownsHttpServer) try {
64
+ await new Promise((resolve, reject) => {
65
+ const onError = (error) => reject(error);
66
+ httpServer.once("error", onError);
67
+ httpServer.listen(port, bindHost, () => {
68
+ httpServer.removeListener("error", onError);
69
+ resolve();
70
+ });
71
+ });
72
+ } catch (error) {
73
+ await closeWs().catch(() => {});
74
+ throw diagnostics.DF0052({
75
+ host: bindHost,
76
+ port,
77
+ reason: error instanceof Error ? error.message : String(error),
78
+ cause: error
79
+ });
80
+ }
81
+ const address = httpServer.address();
82
+ const resolvedPort = typeof address === "object" && address ? address.port : port;
83
+ const origin = normalizeHttpServerUrl(bindHost, resolvedPort);
84
+ const internal = getInternalContext(context);
85
+ const wsUrl = `ws://${formatHostForUrl(bindHost)}:${resolvedPort}${options.path ?? ""}`;
86
+ if (websocket) internal.wsEndpoint = { url: wsUrl };
87
+ function connectionMeta() {
88
+ const jsonSerializableMethods = [];
89
+ for (const def of rpcHost.definitions.values()) if (def.jsonSerializable === true) jsonSerializableMethods.push(def.name);
90
+ return {
91
+ backend: "websocket",
92
+ websocket: { path: options.path },
93
+ jsonSerializableMethods
94
+ };
95
+ }
96
+ return {
97
+ origin,
98
+ port: resolvedPort,
99
+ app,
100
+ ws,
101
+ rpcGroup: core.rpcGroup,
102
+ connectionMeta,
103
+ async close() {
104
+ await closeWs();
105
+ if (ownsHttpServer) await new Promise((r) => httpServer.close(() => r()));
106
+ if (websocket && getInternalContext(context).wsEndpoint?.url === wsUrl) getInternalContext(context).wsEndpoint = void 0;
107
+ }
108
+ };
109
+ }
110
+ /**
111
+ * Translate the public `register?: boolean | Partial<DevframeInstanceRecord>`
112
+ * option into a shell {@link InstanceRegisterConfig}, or `undefined` when
113
+ * registration is opted out. The object form supplies record overrides on top
114
+ * of the caller-provided identity defaults.
115
+ */
116
+ function resolveInstanceRegister(option, defaults) {
117
+ if (!option) return void 0;
118
+ return {
119
+ id: defaults.id,
120
+ ...defaults.name !== void 0 ? { name: defaults.name } : {},
121
+ ...defaults.rootDir !== void 0 ? { rootDir: defaults.rootDir } : {},
122
+ ...typeof option === "object" ? { overrides: option } : {}
123
+ };
124
+ }
125
+ /** Compare two URL paths ignoring a trailing slash. */
126
+ function samePath(a, b) {
127
+ return withoutTrailingSlash(a) === withoutTrailingSlash(b);
128
+ }
129
+ /**
130
+ * Copy a web `Response` from a fetch-style transport handler onto the h3
131
+ * event's response and return its body — mirroring the MCP route's bridge.
132
+ * Returning the body (a `ReadableStream`, or `''` for an empty one — h3
133
+ * middleware only falls through on `undefined`) terminates the chain with
134
+ * the status/headers set here instead of continuing to the SPA catch-all.
135
+ */
136
+ function respondWith(event, response) {
137
+ event.res.status = response.status;
138
+ event.res.statusText = response.statusText;
139
+ response.headers.forEach((value, key) => {
140
+ event.res.headers.set(key, value);
141
+ });
142
+ return response.body ?? "";
143
+ }
144
+ /**
145
+ * The shared machinery behind `initDevframe` and `initHub`: one mount base,
146
+ * one h3 app, one lazily-derived public origin (and the auth banner that waits
147
+ * for it), one WebSocket binding, and the fetch / connect-middleware pair that
148
+ * serves them. Each factory supplies only what makes it itself — its context,
149
+ * its routes, its diagnostics — through `init` / `mount`.
150
+ *
151
+ * Nothing here listens on a port unless a side-car was explicitly requested:
152
+ * the default tier leaves the socket `unbound`, so a host chains it onto its
153
+ * own server through {@link InstanceShell.attach} /
154
+ * {@link InstanceShell.handleUpgrade}.
155
+ *
156
+ * @internal
157
+ */
158
+ function createInstanceShell(options) {
159
+ const base = options.base;
160
+ const baseNoSlash = withoutTrailingSlash(base);
161
+ const app = options.app ?? new H3();
162
+ const wsDisabled = options.ws === false;
163
+ const ws = options.ws === false ? {} : options.ws ?? {};
164
+ const route = withoutLeadingSlash(ws.route ?? "__ws");
165
+ /** Where an upgrade lands on the host's own origin. */
166
+ const routePath = joinURL(base, route);
167
+ /** What `__connection.json` advertises for a same-origin socket. */
168
+ const advertisedPath = options.absoluteWsPath ? routePath : route;
169
+ const sidecarRequested = ws.port != null || ws.sidecar === true;
170
+ const tier = wsDisabled ? "disabled" : sidecarRequested ? "sidecar" : options.server ? "server" : ws.url ? "external" : "unbound";
171
+ const sseEnabled = options.sse !== false && tier !== "external";
172
+ const sseRoute = withoutLeadingSlash((typeof options.sse === "object" ? options.sse.route : void 0) ?? "__sse");
173
+ const sseRoutePath = joinURL(base, sseRoute);
174
+ const advertisedSsePath = options.absoluteWsPath ? sseRoutePath : sseRoute;
175
+ let derivedOrigin;
176
+ function currentOrigin() {
177
+ return (typeof options.origin === "function" ? options.origin() : options.origin) || derivedOrigin;
178
+ }
179
+ let authHandler;
180
+ let bannerPrinted = false;
181
+ function maybePrintBanner() {
182
+ if (bannerPrinted || !authHandler || !currentOrigin()) return;
183
+ bannerPrinted = true;
184
+ authHandler.printBanner();
185
+ }
186
+ let meta;
187
+ let registration;
188
+ let registerPromise;
189
+ /**
190
+ * Publish the instance in the global registry the moment both its origin
191
+ * and connection meta are known — at init end for a pinned origin, or on
192
+ * the first request for a derived one. Registration never throws (the
193
+ * registry writer degrades to a coded warning), so failures never surface.
194
+ */
195
+ function maybeRegister() {
196
+ const cfg = options.register;
197
+ const origin = currentOrigin();
198
+ if (!cfg || registerPromise || !origin || !meta) return;
199
+ const resolvedMeta = meta;
200
+ registerPromise = import("./instance-registry-BQqGoDO9.mjs").then((n) => n.t).then(({ registerDevframeInstance }) => {
201
+ let port = 0;
202
+ try {
203
+ const url = new URL(origin);
204
+ port = Number(url.port) || (url.protocol === "https:" ? 443 : 80);
205
+ } catch {}
206
+ registration = registerDevframeInstance({
207
+ pid: process.pid,
208
+ port,
209
+ origin,
210
+ basePath: base,
211
+ id: cfg.id,
212
+ ...cfg.name !== void 0 ? { name: cfg.name } : {},
213
+ rootDir: cfg.rootDir ?? process.cwd(),
214
+ mcp: resolvedMeta.mcp ? { path: joinURL(base, resolvedMeta.mcp.path) } : null,
215
+ startedAt: Date.now(),
216
+ ...cfg.overrides
217
+ });
218
+ }).catch(() => {});
219
+ }
220
+ function noteOrigin(origin) {
221
+ derivedOrigin ??= origin;
222
+ maybePrintBanner();
223
+ maybeRegister();
224
+ }
225
+ let started;
226
+ let transport;
227
+ let dispose;
228
+ let ctx;
229
+ const api = {
230
+ base,
231
+ app,
232
+ origin: currentOrigin,
233
+ connectionMeta: () => meta
234
+ };
235
+ /**
236
+ * Auth resolution: gate by default, `false` opts out, a handler object
237
+ * installs a custom scheme. The `external` tier has no local transport to
238
+ * gate — the server behind `ws.url` owns auth — so it resolves to nothing.
239
+ */
240
+ function resolveAuth() {
241
+ if (options.auth === false) return false;
242
+ if (typeof options.auth === "object") {
243
+ authHandler = options.auth;
244
+ return options.auth;
245
+ }
246
+ authHandler = createInteractiveAuth(ctx);
247
+ return authHandler;
248
+ }
249
+ /**
250
+ * The context's RPC core (birpc group, session lifecycle, auth gate) —
251
+ * one per instance, shared by every transport binding (WS and SSE), so a
252
+ * WS peer and an SSE session live in the same session/broadcast space.
253
+ * Built lazily: an `unbound` host that never wires a transport pays
254
+ * nothing for it, not even the imports. `resolvedAuth` and `ctx` are
255
+ * assigned during `init()` before any caller can reach this.
256
+ */
257
+ let resolvedAuth = false;
258
+ let corePromise;
259
+ function ensureCore() {
260
+ corePromise ??= import("./rpc-core-CqcJkAu9.mjs").then((n) => n.n).then(({ createContextRpcServer }) => createContextRpcServer({
261
+ context: ctx,
262
+ auth: resolvedAuth,
263
+ onPeerConnect: options.onPeerConnect,
264
+ onPeerDisconnect: options.onPeerDisconnect
265
+ }));
266
+ return corePromise;
267
+ }
268
+ /**
269
+ * The SSE transport, built on the first request to its route so an
270
+ * instance nobody dials over SSE never loads it.
271
+ */
272
+ let ssePromise;
273
+ function ensureSse() {
274
+ ssePromise ??= (async () => {
275
+ const [core, { attachSseRpcTransport }] = await Promise.all([ensureCore(), import("./rpc/transports/sse-server.mjs")]);
276
+ return attachSseRpcTransport(core.rpcGroup, {
277
+ allowedOrigins: options.allowedOrigins,
278
+ onConnected: core.onConnected,
279
+ onDisconnected: core.onDisconnected
280
+ });
281
+ })();
282
+ return ssePromise;
283
+ }
284
+ /**
285
+ * A side-car server on its own port. `getPort` probes and the bind can
286
+ * still race (or disagree across the v4/v6 duals of `localhost`), so an
287
+ * auto-port side-car retries on a fresh random port instead of failing
288
+ * init; a pinned `ws.port` is honored as given and fails loudly.
289
+ */
290
+ async function startSidecar(core) {
291
+ const sidecarHost = options.host ?? "localhost";
292
+ const start = (port) => bindHttpAndWs({
293
+ context: ctx,
294
+ core,
295
+ host: sidecarHost,
296
+ port,
297
+ path: withLeadingSlash(route),
298
+ allowedOrigins: options.allowedOrigins
299
+ });
300
+ if (ws.port != null) return await start(ws.port);
301
+ const { getPort } = await import("./dist-D8jAMwxU.mjs").then((n) => n.t);
302
+ let lastError;
303
+ for (let attempt = 0; attempt < 3; attempt++) {
304
+ const port = attempt === 0 && options.resolveSidecarPort ? await options.resolveSidecarPort(sidecarHost) : await getPort({
305
+ random: true,
306
+ host: sidecarHost
307
+ });
308
+ try {
309
+ return await start(port);
310
+ } catch (error) {
311
+ lastError = error;
312
+ }
313
+ }
314
+ throw lastError;
315
+ }
316
+ async function init() {
317
+ const result = await options.init(api);
318
+ ctx = result.context;
319
+ dispose = result.dispose;
320
+ resolvedAuth = tier === "external" ? false : resolveAuth();
321
+ let websocketMeta;
322
+ if (tier === "sidecar") {
323
+ started = await startSidecar(await ensureCore());
324
+ websocketMeta = {
325
+ port: started.port,
326
+ path: route
327
+ };
328
+ } else if (tier === "server") {
329
+ started = await bindHttpAndWs({
330
+ context: ctx,
331
+ core: await ensureCore(),
332
+ host: options.host ?? "localhost",
333
+ port: 0,
334
+ server: options.server,
335
+ path: routePath,
336
+ allowedOrigins: options.allowedOrigins,
337
+ destroyUnmatched: options.destroyUnmatchedUpgrades
338
+ });
339
+ websocketMeta = { path: advertisedPath };
340
+ } else if (tier === "external") websocketMeta = ws.url;
341
+ else if (tier === "unbound") websocketMeta = { path: advertisedPath };
342
+ if (!wsDisabled && ws.url) websocketMeta = ws.url;
343
+ if (sseEnabled) app.use(sseRoutePath, defineHandler(async (event) => respondWith(event, await (await ensureSse()).handler(event.req))));
344
+ meta = {
345
+ backend: wsDisabled ? sseEnabled ? "sse" : "none" : "websocket",
346
+ ...websocketMeta !== void 0 ? { websocket: websocketMeta } : {},
347
+ ...sseEnabled ? { sse: { path: advertisedSsePath } } : {},
348
+ ...result.mcp ? { mcp: result.mcp } : {}
349
+ };
350
+ await options.mount?.(ctx, meta, api);
351
+ maybePrintBanner();
352
+ maybeRegister();
353
+ }
354
+ const initPromise = init();
355
+ initPromise.catch(() => {});
356
+ const contextPromise = initPromise.then(() => ctx);
357
+ contextPromise.catch(() => {});
358
+ /**
359
+ * The `unbound` tier: the RPC core and its crossws adapter, bound to
360
+ * nothing. Built on the first `attach` / `handleUpgrade` — a host that
361
+ * never wires the socket (or whose runtime brings its own WS transport)
362
+ * pays nothing for it, not even the adapter's imports.
363
+ */
364
+ let transportPromise;
365
+ function ensureTransport() {
366
+ transportPromise ??= initPromise.then(async () => {
367
+ const [core, { attachWsRpcTransport }] = await Promise.all([ensureCore(), import("./rpc/transports/ws-server.mjs")]);
368
+ transport = attachWsRpcTransport(core.rpcGroup, {
369
+ unbound: true,
370
+ path: routePath,
371
+ allowedOrigins: options.allowedOrigins,
372
+ onConnected: core.onConnected,
373
+ onDisconnected: core.onDisconnected
374
+ });
375
+ return transport;
376
+ });
377
+ return transportPromise;
378
+ }
379
+ async function handleRequest(request) {
380
+ await initPromise;
381
+ noteOrigin(new URL(request.url).origin);
382
+ const response = await app.fetch(request);
383
+ if (response.status === 404) return new Response(null, { status: 404 });
384
+ return response;
385
+ }
386
+ let nodeHandler;
387
+ function nodeMiddleware(req, res, next) {
388
+ let pathname = req.url ?? "/";
389
+ try {
390
+ pathname = new URL(pathname, "http://localhost").pathname;
391
+ } catch {}
392
+ if (!(samePath(pathname, baseNoSlash) || pathname.startsWith(base))) {
393
+ if (next) {
394
+ next();
395
+ return;
396
+ }
397
+ res.statusCode = 404;
398
+ res.end();
399
+ return;
400
+ }
401
+ initPromise.then(async () => {
402
+ const host = req.headers.host;
403
+ if (host) {
404
+ const encrypted = req.socket.encrypted;
405
+ noteOrigin(`${encrypted ? "https" : "http"}://${host}`);
406
+ }
407
+ if (!nodeHandler) {
408
+ const { toNodeHandler } = await import("h3/node");
409
+ nodeHandler = toNodeHandler(app);
410
+ }
411
+ return nodeHandler(req, res);
412
+ }).catch((err) => {
413
+ if (next) {
414
+ next(err);
415
+ return;
416
+ }
417
+ res.statusCode = 500;
418
+ res.end();
419
+ });
420
+ }
421
+ /** The `unbound` tier is the only one whose socket the host may drive. */
422
+ function assertUnbound() {
423
+ if (tier === "disabled") throw diagnostics.DF0057();
424
+ if (tier === "external") throw diagnostics.DF0056({ url: ws.url });
425
+ if (tier !== "unbound") throw diagnostics.DF0055({ tier });
426
+ }
427
+ /**
428
+ * Publish the socket's absolute URL on the context, so surfaces that hand
429
+ * out a complete endpoint (the hub's remote docks) work on this tier too.
430
+ * {@link bindHttpAndWs} does the same for the tiers it owns.
431
+ */
432
+ function publishWsEndpoint(server) {
433
+ const record = () => {
434
+ const address = server.address();
435
+ if (typeof address !== "object" || !address) return;
436
+ const host = options.host ?? (address.address === "::" || address.address === "0.0.0.0" ? "localhost" : address.address);
437
+ getInternalContext(ctx).wsEndpoint = { url: `ws://${formatHostForUrl(host)}:${address.port}${routePath}` };
438
+ };
439
+ if (server.listening) record();
440
+ else server.once("listening", record);
441
+ }
442
+ function handleUpgrade(req, socket, head) {
443
+ assertUnbound();
444
+ if (transport) {
445
+ transport.handleUpgrade(req, socket, head);
446
+ return;
447
+ }
448
+ ensureTransport().then((live) => live.handleUpgrade(req, socket, head)).catch(() => socket.destroy());
449
+ }
450
+ function attach(server) {
451
+ assertUnbound();
452
+ server.on("upgrade", handleUpgrade);
453
+ ensureTransport().then(() => publishWsEndpoint(server)).catch(() => {});
454
+ return () => server.off("upgrade", handleUpgrade);
455
+ }
456
+ return {
457
+ base,
458
+ handler: handleRequest,
459
+ nodeMiddleware,
460
+ ready: initPromise,
461
+ context: contextPromise,
462
+ connectionMeta: () => meta ?? options.onMetaUnavailable(),
463
+ handleUpgrade,
464
+ attach,
465
+ async close() {
466
+ await initPromise.catch(() => {});
467
+ await registerPromise?.catch(() => {});
468
+ registration?.unregister();
469
+ await dispose?.();
470
+ await ssePromise?.then((live) => live.close()).catch(() => {});
471
+ await started?.close();
472
+ await transportPromise?.then((live) => live.close()).catch(() => {});
473
+ },
474
+ internals: {
475
+ get started() {
476
+ return started;
477
+ },
478
+ get authHandler() {
479
+ return authHandler;
480
+ }
481
+ }
482
+ };
483
+ }
484
+ //#endregion
485
+ export { normalizeHttpServerUrl as i, resolveInstanceRegister as n, samePath as r, createInstanceShell as t };
@@ -0,0 +1,150 @@
1
+ import { At as DevframeAgentHost$1, Ct as AgentResource, Dt as AgentToolInput, Et as AgentTool, Mt as EventEmitter, Ot as AgentToolProvider, St as AgentManifest, Tt as AgentResourceInput, jt as DevframeAgentHostEvents, kt as AgentToolProviderHandle, wt as AgentResourceContent, x as DevframeNodeContext, xt as AgentHandle, z as DevframeHost } from "../devframe-Bdd9YXTy.mjs";
2
+ import { v as RpcFunctionDefinitionAny } from "../types-CnJSgRVa.mjs";
3
+ import { a as InstanceShellInit, c as StartedServer, d as samePath, f as DevframeInstanceRecord, h as registerDevframeInstance, i as InstanceShellApi, l as createInstanceShell, m as listLiveDevframeInstances, n as InstanceRegisterConfig, o as InstanceShellInternals, p as DevframeInstanceRegistration, r as InstanceShell, s as InstanceWsTier, t as CreateInstanceShellOptions, u as resolveInstanceRegister } from "../instance-shell-DBTXTSjN.mjs";
4
+ import { n as CreateContextRpcServerOptions, r as createContextRpcServer, t as ContextRpcServer } from "../rpc-core-CGctXToO.mjs";
5
+ //#region src/node/agent-args.d.ts
6
+ /**
7
+ * How {@link coerceAgentPositionalArgs} treats an args object that carries
8
+ * neither declared schemas nor `arg0`/`arg1`/… keys:
9
+ *
10
+ * - `'wrap'` — pass the object itself as the single positional argument.
11
+ * RPC-backed tools use this: an untyped RPC may take one raw object.
12
+ * - `'drop'` — call with zero arguments. Command-backed tools use this:
13
+ * a handler's positional parameters come solely from its declared
14
+ * `agent.args` schemas, so undeclared payload is ignored.
15
+ */
16
+ type AgentArgsFallback = 'wrap' | 'drop';
17
+ /**
18
+ * Map the args payload an agent surface receives (MCP sends an object
19
+ * keyed `arg0`/`arg1`/…, matching the schema the adapter advertises) onto
20
+ * a handler's positional parameters. Shared by the agent host's RPC
21
+ * bridge and the hub's command-derived tools so the coercion cannot
22
+ * drift between them.
23
+ *
24
+ * - an array passes through as-is
25
+ * - `null`/`undefined` become a zero-argument call
26
+ * - with declared schemas, each schema reads its own `argN` key, in order
27
+ * - without schemas, `arg0`/`arg1`/… keys are collected when present
28
+ * - an empty object becomes a zero-argument call
29
+ * - anything else follows the {@link AgentArgsFallback}
30
+ *
31
+ * @experimental
32
+ */
33
+ declare function coerceAgentPositionalArgs(args: unknown, schemas: readonly unknown[] | undefined, fallback?: AgentArgsFallback): unknown[];
34
+ //#endregion
35
+ //#region src/node/host-agent.d.ts
36
+ /**
37
+ * Framework-neutral host aggregating the agent-exposed surface of a
38
+ * devframe. Auto-discovers RPC functions with an `agent` field from
39
+ * `ctx.rpc.definitions`, and accepts plugin-registered tools /
40
+ * resources via `registerTool` / `registerResource`.
41
+ *
42
+ * @experimental
43
+ */
44
+ declare class DevframeAgentHost implements DevframeAgentHost$1 {
45
+ readonly context: DevframeNodeContext;
46
+ readonly events: EventEmitter<DevframeAgentHostEvents>;
47
+ private readonly tools;
48
+ private readonly resources;
49
+ private readonly providers;
50
+ private _rpcUnsubscribe;
51
+ constructor(context: DevframeNodeContext);
52
+ registerTool(input: AgentToolInput): AgentHandle;
53
+ unregisterTool(id: string): boolean;
54
+ registerToolProvider(provider: AgentToolProvider): AgentToolProviderHandle;
55
+ registerResource(input: AgentResourceInput): AgentHandle;
56
+ unregisterResource(id: string): boolean;
57
+ list(): AgentManifest;
58
+ getTool(id: string): AgentTool | undefined;
59
+ getResource(id: string): AgentResource | undefined;
60
+ invoke(id: string, args: unknown): Promise<unknown>;
61
+ read(id: string): Promise<AgentResourceContent>;
62
+ /** @internal */
63
+ _dispose(): void;
64
+ private _validateToolId;
65
+ private _projectTool;
66
+ /** Query every registered provider, projecting inputs to serializable tools. */
67
+ private _collectProviderTools;
68
+ private _collectRpcTools;
69
+ private _findRpcDefinition;
70
+ }
71
+ //#endregion
72
+ //#region src/node/host-h3.d.ts
73
+ interface CreateH3DevframeHostOptions {
74
+ /** The h3 app instance — registered once the CLI adapter lands. */
75
+ app?: unknown;
76
+ /**
77
+ * Host the standalone server listens on, e.g. `http://localhost:9999`.
78
+ * Consumed by `resolveOrigin` for dock entries that need an absolute URL.
79
+ * Pass a function for hosts that only learn their public origin later
80
+ * (e.g. `createHandler` derives it from the first incoming request).
81
+ */
82
+ origin: string | (() => string);
83
+ /**
84
+ * Register a static-file handler at `base` serving files from `distDir`.
85
+ * Wired into the h3 app once the CLI adapter lands (commit 5). For now
86
+ * the CLI isn't running, so the default is a no-op.
87
+ */
88
+ mount?: (base: string, distDir: string) => void | Promise<void>;
89
+ /**
90
+ * Namespace for storage paths returned by `getStorageDir`. Workspace
91
+ * state (committable) lives under `${workspaceRoot}/.devframe/`, project
92
+ * state under `${workspaceRoot}/node_modules/.<appName>/devframe/`, and
93
+ * global state under `${homedir()}/.<appName>/devframe/`. Pick the
94
+ * devtool's id (or another stable, filesystem-safe identifier) so the
95
+ * standalone host doesn't collide with other tools' storage.
96
+ */
97
+ appName: string;
98
+ /**
99
+ * Workspace root used as the parent of the per-project storage
100
+ * directory. Defaults to `process.cwd()`.
101
+ */
102
+ workspaceRoot?: string;
103
+ }
104
+ /**
105
+ * h3-backed {@link DevframeHost} — used by the standalone CLI adapter.
106
+ */
107
+ declare function createH3DevframeHost(options: CreateH3DevframeHostOptions): DevframeHost;
108
+ //#endregion
109
+ //#region src/node/utils.d.ts
110
+ declare function normalizeHttpServerUrl(host: string, port: number | string): string;
111
+ //#endregion
112
+ //#region src/rpc/wire-codec.d.ts
113
+ /**
114
+ * The per-connection `serialize`/`deserialize` pair for a live RPC wire.
115
+ *
116
+ * @internal
117
+ * implementations; not part of the stable public API.
118
+ */
119
+ interface RpcWireCodec {
120
+ serialize: (msg: any) => string;
121
+ deserialize: (raw: string) => any;
122
+ }
123
+ /**
124
+ * Build the per-connection wire codec every live transport (WS server, WS
125
+ * client, SSE server, SSE client) shares: per-method dispatch between strict
126
+ * JSON (methods declared `jsonSerializable: true`) and `s:`-prefixed
127
+ * structured-clone (everything else, including all error envelopes), with a
128
+ * request-id → method map so a response independently picks the same
129
+ * encoder as its request. One codec per connection — request-id spaces
130
+ * don't collide across connections.
131
+ *
132
+ * @internal
133
+ * implementations; not part of the stable public API.
134
+ */
135
+ declare function createRpcWireCodec(definitions?: ReadonlyMap<string, Pick<RpcFunctionDefinitionAny, 'jsonSerializable'>>): RpcWireCodec;
136
+ /**
137
+ * Peek at a wire frame's birpc envelope without engaging a codec's
138
+ * request-id bookkeeping — used by the SSE transport to route a frame
139
+ * (park a POST for its response / answer with a bare 202) before it is
140
+ * handed to birpc proper.
141
+ *
142
+ * @internal
143
+ * implementations; not part of the stable public API.
144
+ */
145
+ declare function peekRpcWireFrame(raw: string): {
146
+ t?: string;
147
+ i?: string;
148
+ };
149
+ //#endregion
150
+ export { type AgentArgsFallback, type ContextRpcServer, type CreateContextRpcServerOptions, CreateH3DevframeHostOptions, type CreateInstanceShellOptions, DevframeAgentHost, type DevframeInstanceRecord, type DevframeInstanceRegistration, type InstanceRegisterConfig, type InstanceShell, type InstanceShellApi, type InstanceShellInit, type InstanceShellInternals, type InstanceWsTier, type RpcWireCodec, type StartedServer, coerceAgentPositionalArgs, createContextRpcServer, createH3DevframeHost, createInstanceShell, createRpcWireCodec, listLiveDevframeInstances, normalizeHttpServerUrl, peekRpcWireFrame, registerDevframeInstance, resolveInstanceRegister, samePath };
@@ -0,0 +1,7 @@
1
+ import { n as peekRpcWireFrame, t as createRpcWireCodec } from "../wire-codec-9VhIKnyp.mjs";
2
+ import { n as coerceAgentPositionalArgs, t as DevframeAgentHost } from "../host-agent-CpbAoqBx.mjs";
3
+ import { t as createH3DevframeHost } from "../host-h3-CzCZwtuV.mjs";
4
+ import { i as registerDevframeInstance, n as listLiveDevframeInstances } from "../instance-registry-BQqGoDO9.mjs";
5
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, r as samePath, t as createInstanceShell } from "../instance-shell-KRdee1QF.mjs";
6
+ import { t as createContextRpcServer } from "../rpc-core-CqcJkAu9.mjs";
7
+ export { DevframeAgentHost, coerceAgentPositionalArgs, createContextRpcServer, createH3DevframeHost, createInstanceShell, createRpcWireCodec, listLiveDevframeInstances, normalizeHttpServerUrl, peekRpcWireFrame, registerDevframeInstance, resolveInstanceRegister, samePath };
@@ -1,3 +1,3 @@
1
- import { p as DevframeAuthHandler } from "../devframe-mQ4qZAuo.mjs";
2
- import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, o as revokeActiveConnectionsForToken, r as getTempAuthCode, s as revokeAuthToken, t as buildOtpAuthUrl } from "../index-b64Uuhy2.mjs";
1
+ import { p as DevframeAuthHandler } from "../devframe-Bdd9YXTy.mjs";
2
+ import { a as verifyAuthToken, i as refreshTempAuthCode, n as exchangeTempAuthCode, o as revokeActiveConnectionsForToken, r as getTempAuthCode, s as revokeAuthToken, t as buildOtpAuthUrl } from "../index-CZRd2K1C.mjs";
3
3
  export { DevframeAuthHandler, buildOtpAuthUrl, exchangeTempAuthCode, getTempAuthCode, refreshTempAuthCode, revokeActiveConnectionsForToken, revokeAuthToken, verifyAuthToken };
@@ -1,15 +1,3 @@
1
- import { i as DevframeDeploymentKind, r as DevframeDefinition } from "../devframe-mQ4qZAuo.mjs";
2
- import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-DoSozfvx.mjs";
3
- //#region src/adapters/_shared.d.ts
4
- /**
5
- * Resolve the mount base path for a devframe's SPA. Hosted adapters
6
- * (`vite`, `embedded`) default to `/__<id>/` so they don't collide
7
- * with the host app; standalone adapters (`cli`, `spa`, `build`)
8
- * default to `/` because they own the origin.
9
- *
10
- * The devframe author can override with `basePath` on the definition.
11
- */
12
- declare function resolveBasePath(def: DevframeDefinition, kind: DevframeDeploymentKind): string;
13
- declare function normalizeBasePath(base: string): string;
14
- //#endregion
1
+ import { a as internalContextMap, i as getInternalContext, n as InternalAnonymousAuthStorage, r as RemoteTokenRecord, t as DevframeInternalContext } from "../context-CSCwoABx.mjs";
2
+ import { n as normalizeBasePath, r as resolveBasePath } from "../_shared-DmpWW-HH.mjs";
15
3
  export { type DevframeInternalContext, type InternalAnonymousAuthStorage, type RemoteTokenRecord, getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };
@@ -1,3 +1,3 @@
1
- import { n as internalContextMap, t as getInternalContext } from "../context-CBg8iRbQ.mjs";
2
- import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-bWRzeSa0.mjs";
1
+ import { n as resolveBasePath, t as normalizeBasePath } from "../_shared-CttmCMBi.mjs";
2
+ import { n as internalContextMap, t as getInternalContext } from "../context-C33rR7n0.mjs";
3
3
  export { getInternalContext, internalContextMap, normalizeBasePath, resolveBasePath };