devframe 0.9.7 → 0.9.9

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 (116) hide show
  1. package/dist/{_shared-BM3PdYli.mjs → _shared-C08SdQRl.mjs} +15 -4
  2. package/dist/{_shared-BJ6AmwTu.d.mts → _shared-CK3czkH4.d.mts} +25 -2
  3. package/dist/adapters/build.d.mts +6 -6
  4. package/dist/adapters/build.mjs +32 -22
  5. package/dist/adapters/cac.d.mts +16 -1
  6. package/dist/adapters/cac.mjs +8 -7
  7. package/dist/adapters/dev.d.mts +13 -13
  8. package/dist/adapters/dev.mjs +2 -2
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +28 -26
  11. package/dist/adapters/initiate.mjs +1 -1
  12. package/dist/adapters/mcp.d.mts +28 -14
  13. package/dist/adapters/mcp.mjs +75 -39
  14. package/dist/{agent-tool-name-EgfoFO8C.mjs → agent-tool-name-DGzeuWcO.mjs} +2 -2
  15. package/dist/cli/main.d.mts +1 -1
  16. package/dist/cli/main.mjs +41 -17
  17. package/dist/client/index.d.mts +36 -36
  18. package/dist/client/index.mjs +83 -51
  19. package/dist/client-assets-DNdhagGm.mjs +16 -0
  20. package/dist/constants.d.mts +8 -7
  21. package/dist/constants.mjs +7 -6
  22. package/dist/{context-BhBzOXVa.d.mts → context-4AIVRLYF.d.mts} +173 -122
  23. package/dist/{context-omlma_1n.mjs → context-CwPUinjn.mjs} +126 -88
  24. package/dist/{context-KUqAfawQ.mjs → context-DIUvJW4D.mjs} +3 -3
  25. package/dist/{context-HOnG-WvG.d.mts → context-rgBSPFHS.d.mts} +5 -5
  26. package/dist/{crypto-token-XCqTSMg9.mjs → crypto-token-TVbGGFuj.mjs} +1 -1
  27. package/dist/{dev-Czit5IP4.mjs → dev-Eqq5PX0k.mjs} +37 -18
  28. package/dist/{diagnostics-BXWW3VK-.mjs → diagnostics-BMfN8mb1.mjs} +30 -17
  29. package/dist/{diagnostics-CD8nlgll.mjs → diagnostics-D0EVm5M6.mjs} +2 -2
  30. package/dist/{events-4daw0uK9.mjs → events-BV4Dj59a.mjs} +2 -2
  31. package/dist/{host-h3-fRbF9yor.mjs → host-h3-BJ4siyz7.mjs} +1 -1
  32. package/dist/in-page-channel/index.d.mts +59 -22
  33. package/dist/in-page-channel/index.mjs +71 -56
  34. package/dist/{index-BzyopqxV.d.mts → index-pIVt92jP.d.mts} +9 -9
  35. package/dist/{index-DcKxIMoI.d.mts → index-pKl67H6w.d.mts} +4 -4
  36. package/dist/index.d.mts +11 -13
  37. package/dist/index.mjs +6 -7
  38. package/dist/{instance-registry-BjlMQbFB.mjs → instance-registry-C7-41RSp.mjs} +8 -8
  39. package/dist/{instance-shell-Bh4l9Vz_.d.mts → instance-shell-7Z52PE4_.d.mts} +18 -18
  40. package/dist/{instance-shell-CpVW4zG0.mjs → instance-shell-cdVETpfz.mjs} +227 -58
  41. package/dist/internal/index.d.mts +51 -21
  42. package/dist/internal/index.mjs +10 -9
  43. package/dist/node/auth.d.mts +2 -2
  44. package/dist/node/auth.mjs +2 -2
  45. package/dist/node/hub-internals.d.mts +2 -2
  46. package/dist/node/hub-internals.mjs +2 -2
  47. package/dist/node/index.d.mts +5 -5
  48. package/dist/node/index.mjs +2 -2
  49. package/dist/{nostics-SPGDgHEP.d.mts → nostics-0sKe5HIj.d.mts} +2 -2
  50. package/dist/{nostics-CzECRXpE.mjs → nostics-D0PvLZsn.mjs} +1 -1
  51. package/dist/origin-DGDH8BI9.d.mts +45 -0
  52. package/dist/origin-DZit9g29.mjs +80 -0
  53. package/dist/recipes/common-rpc-functions.d.mts +5 -5
  54. package/dist/recipes/common-rpc-functions.mjs +3 -3
  55. package/dist/recipes/interactive-auth.d.mts +6 -6
  56. package/dist/recipes/interactive-auth.mjs +6 -6
  57. package/dist/{remote-assets-DezQmPQU.mjs → remote-assets-BIFWOlBW.mjs} +26 -6
  58. package/dist/{remote-assets-B13_vRkJ.d.mts → remote-assets-eBvSKZMS.d.mts} +7 -7
  59. package/dist/{revoke-COLW0cOi.mjs → revoke-cqztiCX4.mjs} +1 -1
  60. package/dist/rpc/dump.d.mts +2 -11
  61. package/dist/rpc/dump.mjs +1 -1
  62. package/dist/rpc/index.d.mts +2 -2
  63. package/dist/rpc/index.mjs +3 -3
  64. package/dist/rpc/transports/sse-client.d.mts +1 -1
  65. package/dist/rpc/transports/sse-client.mjs +30 -14
  66. package/dist/rpc/transports/sse-server.d.mts +4 -4
  67. package/dist/rpc/transports/sse-server.mjs +7 -7
  68. package/dist/rpc/transports/ws-bun.d.mts +5 -5
  69. package/dist/rpc/transports/ws-bun.mjs +8 -2
  70. package/dist/rpc/transports/ws-client.d.mts +1 -1
  71. package/dist/rpc/transports/ws-client.mjs +2 -2
  72. package/dist/rpc/transports/ws-deno.d.mts +4 -4
  73. package/dist/rpc/transports/ws-deno.mjs +8 -2
  74. package/dist/rpc/transports/ws-server.d.mts +2 -1
  75. package/dist/rpc/transports/ws-server.mjs +232 -1
  76. package/dist/{rpc-core-BHUsDl7s.d.mts → rpc-core-Ceu6AUz8.d.mts} +4 -4
  77. package/dist/{rpc-core-DlHd276T.mjs → rpc-core-DVQ7xi89.mjs} +14 -2
  78. package/dist/{serialization-BGzEwAdr.mjs → serialization-DTZnPQ1-.mjs} +20 -18
  79. package/dist/{services-install-CqqQVls5.mjs → services-install-P_DV7VYL.mjs} +32 -21
  80. package/dist/session-DPfj6zQG.mjs +16 -0
  81. package/dist/{shared-state-DoXutg_U.mjs → shared-state-CaxB1q0h.mjs} +4 -3
  82. package/dist/{simple-schema-DQPZrAaZ.mjs → simple-schema-Bi40l-P6.mjs} +2 -2
  83. package/dist/{sse-client-DwX021ST.d.mts → sse-client-BQ4jS3eO.d.mts} +4 -4
  84. package/dist/{state-CK9LjrnT.mjs → state-CeQQWzM-.mjs} +4 -4
  85. package/dist/{static-DV_qUSRS.mjs → static-DZ8P2Kn7.mjs} +130 -135
  86. package/dist/{storage-BXNVhKyR.mjs → storage-Dccf_IyD.mjs} +1 -1
  87. package/dist/types/index.d.mts +6 -6
  88. package/dist/{types-CMdPYhOx.d.mts → types-Dq4i156u.d.mts} +17 -17
  89. package/dist/utils/agent-tool-name.d.mts +2 -2
  90. package/dist/utils/agent-tool-name.mjs +2 -2
  91. package/dist/utils/colors.d.mts +1 -1
  92. package/dist/utils/crypto-token.d.mts +1 -1
  93. package/dist/utils/crypto-token.mjs +1 -1
  94. package/dist/utils/events.d.mts +1 -1
  95. package/dist/utils/nostics.d.mts +1 -1
  96. package/dist/utils/nostics.mjs +1 -1
  97. package/dist/utils/open.mjs +1 -1
  98. package/dist/utils/origin.d.mts +2 -0
  99. package/dist/utils/origin.mjs +80 -0
  100. package/dist/utils/remote-assets.d.mts +2 -2
  101. package/dist/utils/remote-assets.mjs +1 -1
  102. package/dist/utils/serve-static.d.mts +4 -4
  103. package/dist/utils/serve-static.mjs +56 -19
  104. package/dist/utils/shared-state.d.mts +1 -1
  105. package/dist/utils/shared-state.mjs +1 -1
  106. package/dist/utils/simple-schema.d.mts +5 -5
  107. package/dist/utils/simple-schema.mjs +2 -2
  108. package/dist/utils/streaming-channel.d.mts +1 -1
  109. package/dist/utils/streaming-channel.mjs +2 -2
  110. package/dist/utils/when.d.mts +5 -5
  111. package/dist/{validation-YrZH6atx.mjs → validation-BOy8IoFZ.mjs} +2 -2
  112. package/dist/{wire-codec-0K-o5MYW.mjs → wire-codec-dcCeXcSo.mjs} +3 -3
  113. package/dist/{ws-client-7_lZi-B9.d.mts → ws-client-Xpxt0oPA.d.mts} +3 -3
  114. package/dist/{ws-server-Di2oqr4q.d.mts → ws-server-D2nv68Hg.d.mts} +13 -22
  115. package/package.json +6 -5
  116. package/dist/ws-server-BdSLrhxE.mjs +0 -267
@@ -1,5 +1,5 @@
1
- import { n as WsOriginRegistry } from "../../ws-server-Di2oqr4q.mjs";
2
- import { t as ContextRpcServer } from "../../rpc-core-BHUsDl7s.mjs";
1
+ import { n as WsOriginRegistry } from "../../ws-server-D2nv68Hg.mjs";
2
+ import { t as ContextRpcServer } from "../../rpc-core-Ceu6AUz8.mjs";
3
3
  //#region src/rpc/transports/ws-bun.d.ts
4
4
  interface AttachBunWsTransportOptions {
5
5
  /** Same contract as `WsRpcTransportOptions.allowedOrigins`. */
@@ -7,7 +7,7 @@ interface AttachBunWsTransportOptions {
7
7
  }
8
8
  /**
9
9
  * Structural view of the Bun `Bun.serve({ websocket })` handler object the
10
- * crossws Bun adapter produces — typed loosely so devframe carries no
10
+ * crossws Bun adapter produces, typed loosely so devframe carries no
11
11
  * dependency on Bun's own types.
12
12
  */
13
13
  interface BunWsTierWebSocket {
@@ -17,14 +17,14 @@ interface BunWsTierWebSocket {
17
17
  drain?: (ws: unknown) => unknown;
18
18
  }
19
19
  interface BunWsTier {
20
- /** Complete a WS upgrade request — `Bun.serve`'s server as 2nd argument. */
20
+ /** Complete a WS upgrade request; `Bun.serve`'s server as 2nd argument. */
21
21
  handleUpgrade: (request: Request, server: unknown) => Promise<Response | undefined>;
22
22
  /** The handlers to spread into `Bun.serve({ websocket })`. */
23
23
  websocket: BunWsTierWebSocket;
24
24
  close: () => Promise<void>;
25
25
  }
26
26
  /**
27
- * The Bun fetch-upgrade WebSocket tier for `initDevframe` / `initHub` — the
27
+ * The Bun fetch-upgrade WebSocket tier for `initDevframe` / `initHub`: the
28
28
  * same RPC peer wiring as `attachWsRpcTransport`, driven by crossws's Bun
29
29
  * adapter so upgrades complete through `handler(request, server)` on the
30
30
  * app's own origin, with no side-car server. Load it dynamically so the Bun
@@ -1,7 +1,8 @@
1
- import { i as isAllowedOrigin, r as createWsRpcPeerHooks } from "../../ws-server-BdSLrhxE.mjs";
1
+ import { t as isAllowedOrigin } from "../../origin-DZit9g29.mjs";
2
+ import { createWsRpcPeerHooks } from "./ws-server.mjs";
2
3
  //#region src/rpc/transports/ws-bun.ts
3
4
  /**
4
- * The Bun fetch-upgrade WebSocket tier for `initDevframe` / `initHub` — the
5
+ * The Bun fetch-upgrade WebSocket tier for `initDevframe` / `initHub`: the
5
6
  * same RPC peer wiring as `attachWsRpcTransport`, driven by crossws's Bun
6
7
  * adapter so upgrades complete through `handler(request, server)` on the
7
8
  * app's own origin, with no side-car server. Load it dynamically so the Bun
@@ -15,6 +16,11 @@ async function attachBunWsTransport(core, options = {}) {
15
16
  onConnected: core.onConnected,
16
17
  onDisconnected: core.onDisconnected
17
18
  }),
19
+ /**
20
+ * The same origin policy `routeUpgrades` applies for the Node
21
+ * transport, enforced at the upgrade hook since Bun upgrades arrive
22
+ * as fetch requests rather than `upgrade` socket events.
23
+ */
18
24
  upgrade(request) {
19
25
  const origin = request.headers.get("origin") ?? void 0;
20
26
  const allowed = allowedOrigins && !Array.isArray(allowedOrigins) ? allowedOrigins.isAllowed(origin) : isAllowedOrigin(origin, allowedOrigins || []);
@@ -1,2 +1,2 @@
1
- import { n as createWsRpcChannel, t as WsRpcChannelOptions } from "../../ws-client-7_lZi-B9.mjs";
1
+ import { n as createWsRpcChannel, t as WsRpcChannelOptions } from "../../ws-client-Xpxt0oPA.mjs";
2
2
  export { WsRpcChannelOptions, createWsRpcChannel };
@@ -1,5 +1,5 @@
1
1
  import { DEVFRAME_AUTH_TOKEN_QUERY_PARAM } from "../../constants.mjs";
2
- import { t as createRpcWireCodec } from "../../wire-codec-0K-o5MYW.mjs";
2
+ import { t as createRpcWireCodec } from "../../wire-codec-dcCeXcSo.mjs";
3
3
  //#region src/rpc/transports/ws-client.ts
4
4
  function NOOP() {}
5
5
  const EMPTY_DEFS = /* @__PURE__ */ new Map();
@@ -7,7 +7,7 @@ const EMPTY_DEFS = /* @__PURE__ */ new Map();
7
7
  * Build a birpc `ChannelOptions` object backed by a browser `WebSocket`.
8
8
  * Pass the result straight to `createRpcClient`'s `channel` option.
9
9
  *
10
- * Also returns `close()`, closing the underlying socket — mirroring the server transport's
10
+ * Also returns `close()`, closing the underlying socket, mirroring the server transport's
11
11
  * existing `WsRpcTransport.close()`. `birpc`'s own `ChannelOptions` has no teardown of its own.
12
12
  */
13
13
  function createWsRpcChannel(options) {
@@ -1,5 +1,5 @@
1
- import { n as WsOriginRegistry } from "../../ws-server-Di2oqr4q.mjs";
2
- import { t as ContextRpcServer } from "../../rpc-core-BHUsDl7s.mjs";
1
+ import { n as WsOriginRegistry } from "../../ws-server-D2nv68Hg.mjs";
2
+ import { t as ContextRpcServer } from "../../rpc-core-Ceu6AUz8.mjs";
3
3
  //#region src/rpc/transports/ws-deno.d.ts
4
4
  interface AttachDenoWsTransportOptions {
5
5
  /** Same contract as `WsRpcTransportOptions.allowedOrigins`. */
@@ -7,7 +7,7 @@ interface AttachDenoWsTransportOptions {
7
7
  }
8
8
  interface DenoWsTier {
9
9
  /**
10
- * Complete a WS upgrade request — `Deno.serve`'s handler info as the 2nd
10
+ * Complete a WS upgrade request; `Deno.serve`'s handler info as the 2nd
11
11
  * argument. Unlike Bun, Deno's adapter attaches the socket to the returned
12
12
  * `Response` itself, so there is no `websocket` handler object to register.
13
13
  */
@@ -15,7 +15,7 @@ interface DenoWsTier {
15
15
  close: () => Promise<void>;
16
16
  }
17
17
  /**
18
- * The Deno fetch-upgrade WebSocket tier for `initDevframe` / `initHub` — the
18
+ * The Deno fetch-upgrade WebSocket tier for `initDevframe` / `initHub`: the
19
19
  * same RPC peer wiring as `attachWsRpcTransport`, driven by crossws's Deno
20
20
  * adapter so upgrades complete through `handleUpgrade(request, info)` on the
21
21
  * app's own origin, with no side-car server. Load it dynamically so the Deno
@@ -1,7 +1,8 @@
1
- import { i as isAllowedOrigin, r as createWsRpcPeerHooks } from "../../ws-server-BdSLrhxE.mjs";
1
+ import { t as isAllowedOrigin } from "../../origin-DZit9g29.mjs";
2
+ import { createWsRpcPeerHooks } from "./ws-server.mjs";
2
3
  //#region src/rpc/transports/ws-deno.ts
3
4
  /**
4
- * The Deno fetch-upgrade WebSocket tier for `initDevframe` / `initHub` — the
5
+ * The Deno fetch-upgrade WebSocket tier for `initDevframe` / `initHub`: the
5
6
  * same RPC peer wiring as `attachWsRpcTransport`, driven by crossws's Deno
6
7
  * adapter so upgrades complete through `handleUpgrade(request, info)` on the
7
8
  * app's own origin, with no side-car server. Load it dynamically so the Deno
@@ -15,6 +16,11 @@ async function attachDenoWsTransport(core, options = {}) {
15
16
  onConnected: core.onConnected,
16
17
  onDisconnected: core.onDisconnected
17
18
  }),
19
+ /**
20
+ * The same origin policy `routeUpgrades` applies for the Node
21
+ * transport, enforced at the upgrade hook since Deno upgrades arrive
22
+ * as fetch requests rather than `upgrade` socket events.
23
+ */
18
24
  upgrade(request) {
19
25
  const origin = request.headers.get("origin") ?? void 0;
20
26
  const allowed = allowedOrigins && !Array.isArray(allowedOrigins) ? allowedOrigins.isAllowed(origin) : isAllowedOrigin(origin, allowedOrigins || []);
@@ -1,2 +1,3 @@
1
- import { a as attachWsRpcTransport, c as isAllowedOrigin, d as DevframeRpcConnection, f as DevframeRpcConnectionRequest, i as WsRpcTransportOptions, l as isLoopbackHostname, n as WsOriginRegistry, o as createWsOriginRegistry, p as DevframeRpcTransportKind, r as WsRpcTransport, s as createWsRpcPeerHooks, t as CreateWsOriginRegistryOptions, u as DevframeNodeRpcSessionMeta } from "../../ws-server-Di2oqr4q.mjs";
1
+ import { a as attachWsRpcTransport, c as DevframeNodeRpcSessionMeta, d as DevframeRpcTransportKind, i as WsRpcTransportOptions, l as DevframeRpcConnection, n as WsOriginRegistry, o as createWsOriginRegistry, r as WsRpcTransport, s as createWsRpcPeerHooks, t as CreateWsOriginRegistryOptions, u as DevframeRpcConnectionRequest } from "../../ws-server-D2nv68Hg.mjs";
2
+ import { n as isLoopbackHostname, t as isAllowedOrigin } from "../../origin-DGDH8BI9.mjs";
2
3
  export { CreateWsOriginRegistryOptions, type DevframeNodeRpcSessionMeta, type DevframeRpcConnection, type DevframeRpcConnectionRequest, type DevframeRpcTransportKind, WsOriginRegistry, WsRpcTransport, WsRpcTransportOptions, attachWsRpcTransport, createWsOriginRegistry, createWsRpcPeerHooks, isAllowedOrigin, isLoopbackHostname };
@@ -1,2 +1,233 @@
1
- import { a as isLoopbackHostname, i as isAllowedOrigin, n as createWsOriginRegistry, r as createWsRpcPeerHooks, t as attachWsRpcTransport } from "../../ws-server-BdSLrhxE.mjs";
1
+ import "../../constants.mjs";
2
+ import { t as createRpcWireCodec } from "../../wire-codec-dcCeXcSo.mjs";
3
+ import { n as isLoopbackHostname, t as isAllowedOrigin } from "../../origin-DZit9g29.mjs";
4
+ import { t as createRpcSessionMeta } from "../../session-DPfj6zQG.mjs";
5
+ import { n as randomToken, r as timingSafeEqual } from "../../crypto-token-TVbGGFuj.mjs";
6
+ import { createServer } from "node:http";
7
+ import { createServer as createServer$1 } from "node:https";
8
+ import crossws from "crossws/adapters/node";
9
+ //#region src/rpc/transports/ws-server.ts
10
+ /**
11
+ * Create a live, token-protected origin allowlist for external browser
12
+ * viewers. Pass it to {@link WsRpcTransportOptions.allowedOrigins}, then use
13
+ * `registerFromUrl()` in the connection metadata handler to authorize a
14
+ * viewer without sharing a mutable array or disabling DNS-rebinding protection.
15
+ */
16
+ function createWsOriginRegistry(options = {}) {
17
+ const token = randomToken();
18
+ const origins = new Set(options.allowedOrigins ?? []);
19
+ function normalizeOrigin(origin) {
20
+ if (!origin) return;
21
+ try {
22
+ const url = new URL(origin);
23
+ const normalized = url.origin === "null" ? `${url.protocol}//${url.host}` : url.origin;
24
+ return origin === normalized ? normalized : void 0;
25
+ } catch {}
26
+ }
27
+ function registerOrigin(origin, candidateToken) {
28
+ const normalized = normalizeOrigin(origin);
29
+ if (!normalized || !candidateToken || !timingSafeEqual(token, candidateToken)) return false;
30
+ if (options.validateOrigin && !options.validateOrigin(normalized)) return false;
31
+ origins.add(normalized);
32
+ return true;
33
+ }
34
+ return {
35
+ token,
36
+ registerFromUrl(url) {
37
+ let parsed;
38
+ try {
39
+ parsed = new URL(url, "http://localhost");
40
+ } catch {
41
+ return;
42
+ }
43
+ const origin = parsed.searchParams.get("devframe_viewer_origin") ?? void 0;
44
+ return registerOrigin(origin, parsed.searchParams.get("devframe_viewer_origin_token") ?? void 0) ? origin : void 0;
45
+ },
46
+ isAllowed(origin) {
47
+ return isAllowedOrigin(origin, [...origins]);
48
+ }
49
+ };
50
+ }
51
+ const EMPTY_DEFS = /* @__PURE__ */ new Map();
52
+ function NOOP() {}
53
+ function listen(server, port, host) {
54
+ return new Promise((resolve, reject) => {
55
+ const onError = (error) => reject(error);
56
+ server.once("error", onError);
57
+ try {
58
+ server.listen(port, host, () => {
59
+ server.off("error", onError);
60
+ resolve();
61
+ });
62
+ } catch (error) {
63
+ server.off("error", onError);
64
+ reject(error);
65
+ }
66
+ });
67
+ }
68
+ /** Compare two URL paths ignoring a trailing slash. */
69
+ function pathMatches(a, b) {
70
+ const strip = (p) => p.length > 1 && p.endsWith("/") ? p.slice(0, -1) : p;
71
+ return strip(a) === strip(b);
72
+ }
73
+ function isWsOriginRegistry(value) {
74
+ return !!value && !Array.isArray(value);
75
+ }
76
+ /**
77
+ * Build the `upgrade` listener that hands a request to the crossws adapter,
78
+ * optionally filtered to a single `path`. Non-matching requests are left
79
+ * untouched so other upgrade listeners (e.g. a Vite dev server's HMR socket)
80
+ * can claim them, unless `destroyUnmatched` is set.
81
+ */
82
+ function createUpgradeListener(ws, path, destroyUnmatched, allowedOrigins) {
83
+ return (req, socket, head) => {
84
+ socket.on("error", () => {});
85
+ if (path) {
86
+ let pathname = req.url ?? "/";
87
+ try {
88
+ pathname = new URL(req.url ?? "/", "http://localhost").pathname;
89
+ } catch {}
90
+ if (!pathMatches(pathname, path)) {
91
+ if (destroyUnmatched) {
92
+ socket.write("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
93
+ socket.destroy();
94
+ }
95
+ return;
96
+ }
97
+ }
98
+ const originAllowed = isWsOriginRegistry(allowedOrigins) ? allowedOrigins.isAllowed(req.headers.origin) : isAllowedOrigin(req.headers.origin, allowedOrigins || []);
99
+ if (allowedOrigins !== false && !originAllowed) {
100
+ socket.write("HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n");
101
+ socket.destroy();
102
+ return;
103
+ }
104
+ ws.handleUpgrade(req, socket, head);
105
+ };
106
+ }
107
+ /**
108
+ * The per-peer lifecycle hooks driving a devframe RPC WebSocket, shaped for
109
+ * any [crossws](https://crossws.h3.dev) adapter. {@link attachWsRpcTransport}
110
+ * feeds them to the Node adapter; runtime-specific attachments (e.g. Bun's
111
+ * fetch-upgrade adapter) reuse the same hooks so every transport speaks the
112
+ * identical wire protocol: one birpc channel per peer, per-method
113
+ * `jsonSerializable` dispatch between strict JSON and structured-clone.
114
+ */
115
+ function createWsRpcPeerHooks(rpcGroup, options = {}) {
116
+ const { onConnected = NOOP, onDisconnected = NOOP, definitions = EMPTY_DEFS, serialize: serializeOverride, deserialize: deserializeOverride } = options;
117
+ const states = /* @__PURE__ */ new WeakMap();
118
+ return {
119
+ open: (peer) => {
120
+ const meta = createRpcSessionMeta();
121
+ meta.peer = peer;
122
+ const connection = {
123
+ id: meta.id,
124
+ transport: "websocket",
125
+ request: peer.request,
126
+ send: (data) => peer.send(data),
127
+ close: (code, reason) => peer.close(code, reason),
128
+ peer
129
+ };
130
+ const codec = createRpcWireCodec(definitions);
131
+ const state = {
132
+ meta,
133
+ connection
134
+ };
135
+ const channel = {
136
+ post: (data) => {
137
+ peer.send(data);
138
+ },
139
+ on: (fn) => {
140
+ state.onMessage = fn;
141
+ },
142
+ serialize: serializeOverride ?? codec.serialize,
143
+ deserialize: deserializeOverride ?? codec.deserialize,
144
+ meta
145
+ };
146
+ state.channel = channel;
147
+ states.set(peer, state);
148
+ rpcGroup.updateChannels((channels) => {
149
+ channels.push(channel);
150
+ });
151
+ onConnected(connection, meta);
152
+ },
153
+ message: (peer, message) => {
154
+ states.get(peer)?.onMessage?.(message.text());
155
+ },
156
+ close: (peer) => {
157
+ const state = states.get(peer);
158
+ if (!state) return;
159
+ states.delete(peer);
160
+ rpcGroup.updateChannels((channels) => {
161
+ const index = state.channel ? channels.indexOf(state.channel) : -1;
162
+ if (index >= 0) channels.splice(index, 1);
163
+ });
164
+ onDisconnected(state.connection, state.meta);
165
+ }
166
+ };
167
+ }
168
+ /**
169
+ * Attach a WebSocket transport to an existing RPC group, powered by
170
+ * [crossws](https://crossws.h3.dev). Either attach to an existing HTTP(S)
171
+ * `server` (sharing its port, optionally scoped to a `path`), or let this
172
+ * helper create a standalone server from `port` / `host` / `https`.
173
+ *
174
+ * Returns the crossws node adapter, standalone-server readiness/address
175
+ * accessors, `detach` (remove the upgrade listener from a shared `server`),
176
+ * and `close` (full deterministic teardown).
177
+ */
178
+ function attachWsRpcTransport(rpcGroup, options = {}) {
179
+ const { server, port, host = "localhost", path, destroyUnmatched = false, unbound, https, allowedOrigins } = options;
180
+ const ws = crossws({ hooks: createWsRpcPeerHooks(rpcGroup, options) });
181
+ const sharedUpgradeListener = createUpgradeListener(ws, path, destroyUnmatched, allowedOrigins);
182
+ const ownedUpgradeListener = createUpgradeListener(ws, path, true, allowedOrigins);
183
+ /** Bind a server's `upgrade` events, tracked so `close()` detaches them. */
184
+ const attachments = /* @__PURE__ */ new Set();
185
+ function attachTo(target, listener) {
186
+ target.on("upgrade", listener);
187
+ const detachOne = () => {
188
+ target.off("upgrade", listener);
189
+ attachments.delete(detachOne);
190
+ };
191
+ attachments.add(detachOne);
192
+ return detachOne;
193
+ }
194
+ let ready = Promise.resolve();
195
+ let ownedServer;
196
+ if (unbound) {} else if (server) attachTo(server, sharedUpgradeListener);
197
+ else if (https) {
198
+ ownedServer = createServer$1(https);
199
+ attachTo(ownedServer, ownedUpgradeListener);
200
+ ready = listen(ownedServer, port ?? 0, host);
201
+ } else {
202
+ ownedServer = createServer((_req, res) => {
203
+ res.writeHead(426, { "content-type": "text/plain" });
204
+ res.end("Upgrade Required");
205
+ });
206
+ attachTo(ownedServer, ownedUpgradeListener);
207
+ ready = listen(ownedServer, port ?? 0, host);
208
+ }
209
+ const activeServer = server ?? ownedServer;
210
+ function detachAll() {
211
+ for (const detachOne of [...attachments]) detachOne();
212
+ }
213
+ return {
214
+ ws,
215
+ ready,
216
+ address: () => activeServer?.address() ?? null,
217
+ handleUpgrade: sharedUpgradeListener,
218
+ attach: (target) => attachTo(target, sharedUpgradeListener),
219
+ detach: detachAll,
220
+ async close() {
221
+ detachAll();
222
+ ws.closeAll(void 0, void 0, true);
223
+ if (ownedServer) {
224
+ const srv = ownedServer;
225
+ await ready.catch(() => {});
226
+ if (!srv.listening) return;
227
+ await new Promise((r) => srv.close(() => r()));
228
+ }
229
+ }
230
+ };
231
+ }
232
+ //#endregion
2
233
  export { attachWsRpcTransport, createWsOriginRegistry, createWsRpcPeerHooks, isAllowedOrigin, isLoopbackHostname };
@@ -1,6 +1,6 @@
1
- import { d as DevframeRpcConnection, u as DevframeNodeRpcSessionMeta } from "./ws-server-Di2oqr4q.mjs";
2
- import { Ct as DevframeAuthHandler, _t as DevframeRpcServerFunctions, gt as DevframeRpcClientFunctions, o as DevframeNodeContext, s as DevframeNodeRpcSession } from "./context-BhBzOXVa.mjs";
3
- import "./index-DcKxIMoI.mjs";
1
+ import { c as DevframeNodeRpcSessionMeta, l as DevframeRpcConnection } from "./ws-server-D2nv68Hg.mjs";
2
+ import { _t as DevframeRpcClientFunctions, o as DevframeNodeContext, s as DevframeNodeRpcSession, vt as DevframeRpcServerFunctions, wt as DevframeAuthHandler } from "./context-4AIVRLYF.mjs";
3
+ import "./index-pKl67H6w.mjs";
4
4
  import { BirpcGroup, EventOptions } from "birpc";
5
5
  //#region src/node/rpc-core.d.ts
6
6
  interface CreateContextRpcServerOptions {
@@ -33,7 +33,7 @@ interface ContextRpcServer {
33
33
  }
34
34
  /**
35
35
  * Bind a devframe context's registered RPC functions to a birpc group,
36
- * transport-agnostically — the shared core under the instance shell's own
36
+ * transport-agnostically: the shared core under the instance shell's own
37
37
  * HTTP+WS binding (Node http + WS) and the Bun fetch-upgrade tier of
38
38
  * `createHandler`.
39
39
  *
@@ -1,12 +1,12 @@
1
1
  import { n as __exportAll } from "./rolldown-runtime-B4iAMlE-.mjs";
2
2
  import { createRpcServer } from "./rpc/server.mjs";
3
- import { t as diagnostics } from "./diagnostics-BXWW3VK-.mjs";
3
+ import { t as diagnostics } from "./diagnostics-BMfN8mb1.mjs";
4
4
  import { AsyncLocalStorage } from "node:async_hooks";
5
5
  //#region src/node/rpc-core.ts
6
6
  var rpc_core_exports = /* @__PURE__ */ __exportAll({ createContextRpcServer: () => createContextRpcServer });
7
7
  /**
8
8
  * Bind a devframe context's registered RPC functions to a birpc group,
9
- * transport-agnostically — the shared core under the instance shell's own
9
+ * transport-agnostically: the shared core under the instance shell's own
10
10
  * HTTP+WS binding (Node http + WS) and the Bun fetch-upgrade tier of
11
11
  * `createHandler`.
12
12
  *
@@ -26,8 +26,20 @@ function createContextRpcServer(options) {
26
26
  for (const fn of authHandler.rpcFunctions) if (!rpcHost.definitions.has(fn.name)) rpcHost.register(fn);
27
27
  }
28
28
  const rpcGroup = createRpcServer(rpcHost.functions, { rpcOptions: {
29
+ /**
30
+ * Forwarded as-is so a host with its own structured diagnostics
31
+ * keeps seeing RPC failures.
32
+ */
29
33
  onFunctionError: options.rpcOptions?.onFunctionError,
30
34
  onGeneralError: options.rpcOptions?.onGeneralError,
35
+ /**
36
+ * Wrap each RPC handler in an AsyncLocalStorage context so
37
+ * `ctx.rpc.getCurrentRpcSession()` works inside handlers (used
38
+ * by streaming subscribe/unsubscribe/cancel and shared-state
39
+ * sync), and (when an `authorize` gate is configured) reject
40
+ * the call before it ever reaches the handler. Mirrors
41
+ * `packages/core/src/node/ws.ts`'s resolver.
42
+ */
31
43
  resolver(name, fn) {
32
44
  const rpc = this;
33
45
  if (!fn) return void 0;
@@ -1,4 +1,4 @@
1
- import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
1
+ import { t as diagnostics } from "./diagnostics-D0EVm5M6.mjs";
2
2
  //#region src/rpc/serialization.ts
3
3
  /**
4
4
  * Wire format used by the live RPC transports (WebSocket and SSE).
@@ -14,7 +14,7 @@ import { t as diagnostics } from "./diagnostics-CD8nlgll.mjs";
14
14
  *
15
15
  * birpc envelopes always start with `{`, so a leading byte that is not
16
16
  * `s` is unambiguously JSON. Each direction independently chooses its
17
- * encoding from local definitions — request and response are not
17
+ * encoding from local definitions; request and response are not
18
18
  * coupled by a mirror rule.
19
19
  */
20
20
  const STRUCTURED_CLONE_PREFIX = "s:";
@@ -26,16 +26,16 @@ const STRUCTURED_CLONE_PREFIX = "s:";
26
26
  * instances, or `undefined` inside an array (silently becomes `null`).
27
27
  *
28
28
  * Native pass-throughs (no extra work needed):
29
- * - circular references — `JSON.stringify` raises `TypeError`.
30
- * - `BigInt` at top level — caught here for a friendlier error path.
29
+ * - circular references: `JSON.stringify` raises `TypeError`.
30
+ * - `BigInt` at top level: caught here for a friendlier error path.
31
31
  *
32
32
  * Lenient cases (allowed without throwing):
33
- * - `undefined` as an object property — legitimate optional field;
33
+ * - `undefined` as an object property: legitimate optional field;
34
34
  * JSON.stringify just omits it.
35
- * - `undefined` at the root — legitimate "action returned nothing".
36
- * - `Symbol` / `Function` values — semantically "drop me" in JSON.
35
+ * - `undefined` at the root: legitimate "action returned nothing".
36
+ * - `Symbol` / `Function` values: semantically "drop me" in JSON.
37
37
  *
38
- * `fnName` is used only for the diagnostic message — pass the RPC
38
+ * `fnName` is used only for the diagnostic message; pass the RPC
39
39
  * function name when calling from a wire serializer / dump writer so
40
40
  * the error points at the offending function.
41
41
  */
@@ -47,19 +47,21 @@ function strictJsonStringify(value, fnName = "") {
47
47
  if (Array.isArray(holder)) throw nonJsonAt(fnName, "undefined", holder, key);
48
48
  return val;
49
49
  }
50
- if (original === null) return val;
51
- if (typeof original === "bigint") throw nonJsonAt(fnName, "BigInt", holder, key);
52
- if (typeof original === "object") {
53
- if (original instanceof Map) throw nonJsonAt(fnName, "Map", holder, key);
54
- if (original instanceof Set) throw nonJsonAt(fnName, "Set", holder, key);
55
- if (original instanceof Date) throw nonJsonAt(fnName, "Date", holder, key);
56
- if (Array.isArray(original)) return val;
57
- const proto = Object.getPrototypeOf(original);
58
- if (proto !== null && proto !== Object.prototype) throw nonJsonAt(fnName, original.constructor?.name ?? "class instance", holder, key);
59
- }
50
+ if (original !== null) assertJsonSafe(original, holder, key, fnName);
60
51
  return val;
61
52
  });
62
53
  }
54
+ /** Throw `DF0020` when `original` is a type JSON can't round-trip losslessly. */
55
+ function assertJsonSafe(original, holder, key, fnName) {
56
+ if (typeof original === "bigint") throw nonJsonAt(fnName, "BigInt", holder, key);
57
+ if (typeof original !== "object") return;
58
+ if (original instanceof Map) throw nonJsonAt(fnName, "Map", holder, key);
59
+ if (original instanceof Set) throw nonJsonAt(fnName, "Set", holder, key);
60
+ if (original instanceof Date) throw nonJsonAt(fnName, "Date", holder, key);
61
+ if (Array.isArray(original)) return;
62
+ const proto = Object.getPrototypeOf(original);
63
+ if (proto !== null && proto !== Object.prototype) throw nonJsonAt(fnName, original.constructor?.name ?? "class instance", holder, key);
64
+ }
63
65
  function nonJsonAt(fnName, type, parent, key) {
64
66
  const path = formatPath(parent, key);
65
67
  return diagnostics.DF0020({
@@ -1,5 +1,5 @@
1
- import { t as DEVFRAME_EVENTS } from "./events-4daw0uK9.mjs";
2
- import { t as diagnostics } from "./diagnostics-BXWW3VK-.mjs";
1
+ import { t as DEVFRAME_EVENTS } from "./events-BV4Dj59a.mjs";
2
+ import { t as diagnostics } from "./diagnostics-BMfN8mb1.mjs";
3
3
  import { createRequire } from "node:module";
4
4
  import { pathToFileURL } from "node:url";
5
5
  import { isAbsolute, join } from "pathe";
@@ -219,6 +219,12 @@ var DevframeAgentHost = class {
219
219
  description: input.description,
220
220
  safety: input.safety ?? "action",
221
221
  tags: input.tags,
222
+ /**
223
+ * Standard Schema `args` are carried raw (mirroring how an RPC-backed
224
+ * tool defers to `ctx.rpc.definitions`); consumers (the MCP adapter)
225
+ * convert to JSON Schema on demand. An explicit `inputSchema` override
226
+ * wins when given.
227
+ */
222
228
  args: input.args,
223
229
  inputSchema: input.inputSchema,
224
230
  outputSchema: input.outputSchema,
@@ -268,7 +274,7 @@ function inferSafety(type) {
268
274
  //#region src/node/services-install.ts
269
275
  /**
270
276
  * Turn a `resolveFrom` value (a file path, a file URL like `import.meta.url`,
271
- * or a directory) into something `createRequire` accepts — a directory gets a
277
+ * or a directory) into something `createRequire` accepts; a directory gets a
272
278
  * synthetic filename appended so resolution starts inside it.
273
279
  */
274
280
  function toRequireBase(resolveFrom) {
@@ -341,6 +347,24 @@ function compareVersions(a, b) {
341
347
  if (a.prerelease && b.prerelease) return a.prerelease < b.prerelease ? -1 : a.prerelease > b.prerelease ? 1 : 0;
342
348
  return 0;
343
349
  }
350
+ /** Whether `version` and `base` share their first `lockUpTo + 1` parts. */
351
+ function partsMatchUpTo(version, base, lockUpTo) {
352
+ for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
353
+ return true;
354
+ }
355
+ function satisfiesCaret(version, base) {
356
+ if (compareVersions(version, base) < 0) return false;
357
+ const fixedIndex = base.parts.findIndex((part) => part !== 0);
358
+ return partsMatchUpTo(version, base, fixedIndex === -1 ? base.parts.length - 1 : fixedIndex);
359
+ }
360
+ function satisfiesTilde(version, base, segments) {
361
+ if (compareVersions(version, base) < 0) return false;
362
+ return partsMatchUpTo(version, base, segments.length >= 2 ? 1 : 0);
363
+ }
364
+ function satisfiesExactOrPrefix(version, base, segments) {
365
+ for (let i = 0; i < Math.max(segments.length, 3); i++) if (i < segments.length && (version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
366
+ return segments.length >= 3 ? compareVersions(version, base) === 0 : true;
367
+ }
344
368
  function satisfiesComparator(version, comparator) {
345
369
  const raw = comparator.trim();
346
370
  if (!raw || raw === "*" || raw === "x") return true;
@@ -356,26 +380,13 @@ function satisfiesComparator(version, comparator) {
356
380
  case ">=": return compareVersions(version, base) >= 0;
357
381
  case "<": return compareVersions(version, base) < 0;
358
382
  case "<=": return compareVersions(version, base) <= 0;
359
- case "^": {
360
- if (compareVersions(version, base) < 0) return false;
361
- const fixedIndex = base.parts.findIndex((part) => part !== 0);
362
- const lockUpTo = fixedIndex === -1 ? base.parts.length - 1 : fixedIndex;
363
- for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
364
- return true;
365
- }
366
- case "~": {
367
- if (compareVersions(version, base) < 0) return false;
368
- const lockUpTo = segments.length >= 2 ? 1 : 0;
369
- for (let i = 0; i <= lockUpTo; i++) if ((version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
370
- return true;
371
- }
372
- default:
373
- for (let i = 0; i < Math.max(segments.length, 3); i++) if (i < segments.length && (version.parts[i] ?? 0) !== (base.parts[i] ?? 0)) return false;
374
- return segments.length >= 3 ? compareVersions(version, base) === 0 : true;
383
+ case "^": return satisfiesCaret(version, base);
384
+ case "~": return satisfiesTilde(version, base, segments);
385
+ default: return satisfiesExactOrPrefix(version, base, segments);
375
386
  }
376
387
  }
377
388
  /**
378
- * Pragmatic semver range check for service version declarations — supports
389
+ * Pragmatic semver range check for service version declarations; supports
379
390
  * the common forms (`1.2.3`, `^1.2.3`, `~1.2`, `>=1 <3`, `1.x`, `*`, and
380
391
  * `||`-joined alternatives) without pulling in a semver dependency. An
381
392
  * unparseable version or range reads as **not satisfied**.
@@ -402,7 +413,7 @@ function deepMergeTwo(a, b) {
402
413
  }
403
414
  /**
404
415
  * Default option-set merge when a service declares no `mergeOptions`:
405
- * deep-merge in declaration order — objects recurse, arrays union-dedupe,
416
+ * deep-merge in declaration order: objects recurse, arrays union-dedupe,
406
417
  * scalars take the later value. Covers the built-in services (`roots` /
407
418
  * `langs` union, `themes` per-key last-wins) without a custom hook.
408
419
  */
@@ -0,0 +1,16 @@
1
+ //#region src/rpc/transports/session.ts
2
+ let sessionId = 0;
3
+ /**
4
+ * Mint the per-connection session meta every transport binding shares:
5
+ * one id space across transports, so session bookkeeping (streaming
6
+ * subscriptions, shared-state sync, auth trust) never collides between a
7
+ * WS peer and an SSE session on the same server.
8
+ */
9
+ function createRpcSessionMeta() {
10
+ return {
11
+ id: sessionId++,
12
+ subscribedStates: /* @__PURE__ */ new Set()
13
+ };
14
+ }
15
+ //#endregion
16
+ export { createRpcSessionMeta as t };
@@ -1,6 +1,6 @@
1
1
  import { createEventEmitter } from "./utils/events.mjs";
2
2
  import { nanoid } from "./utils/nanoid.mjs";
3
- //#region ../../node_modules/.pnpm/immer@11.1.17/node_modules/immer/dist/immer.mjs
3
+ //#region ../../node_modules/.pnpm/immer@11.1.18/node_modules/immer/dist/immer.mjs
4
4
  var NOTHING = Symbol.for("immer-nothing");
5
5
  var DRAFTABLE = Symbol.for("immer-draftable");
6
6
  var DRAFT_STATE = Symbol.for("immer-state");
@@ -891,6 +891,7 @@ function enablePatches() {
891
891
  getPath
892
892
  });
893
893
  }
894
+ globalThis.Iterator?.from;
894
895
  var immer = new Immer2();
895
896
  var produce = immer.produce;
896
897
  var produceWithPatches = /* @__PURE__ */ immer.produceWithPatches.bind(immer);
@@ -930,8 +931,8 @@ function createSharedState(options) {
930
931
  if (syncIds.has(syncId)) return;
931
932
  rememberSyncId(syncIds, syncId);
932
933
  if (enablePatches$1) {
933
- const [newState, patches] = produceWithPatches(state, fn);
934
- state = newState;
934
+ const [nextState, patches] = produceWithPatches(state, fn);
935
+ state = nextState;
935
936
  events.emit("updated", state, patches, syncId);
936
937
  } else {
937
938
  state = produce(state, fn);
@@ -37,7 +37,7 @@ function number() {
37
37
  function boolean() {
38
38
  return make("boolean", (v) => typeof v === "boolean" ? ok(v) : fail("Expected a boolean"));
39
39
  }
40
- /** `undefined` — mirrors valibot's `void`. */
40
+ /** `undefined`, mirroring valibot's `void`. */
41
41
  function voidType() {
42
42
  return make("void", (v) => v === void 0 ? ok(void 0) : fail("Expected undefined"));
43
43
  }
@@ -130,7 +130,7 @@ function describe(schema, description) {
130
130
  };
131
131
  }
132
132
  /**
133
- * Grouped access to every builder — `s.string()`, `s.object({ ... })`,
133
+ * Grouped access to every builder: `s.string()`, `s.object({ ... })`,
134
134
  * `s.void()`, etc. Handy for a valibot-like `import { s } from
135
135
  * 'devframe/utils/simple-schema'` call site.
136
136
  */