devframe 0.8.1 → 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 +25 -34
  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-CQefP2jR.mjs → context-C33rR7n0.mjs} +1 -1
  20. package/dist/{context-DFzmxCLa.d.mts → context-CSCwoABx.d.mts} +1 -1
  21. package/dist/{host-h3-Kz7t5Xab.mjs → context-Du11TLcO.mjs} +5 -255
  22. package/dist/dev-BEveeA5H.mjs +233 -0
  23. package/dist/{devframe-qgCKL683.d.mts → devframe-Bdd9YXTy.d.mts} +88 -25
  24. package/dist/{diagnostics-B5-qHeqD.mjs → diagnostics-DLYH5oD3.mjs} +20 -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-CK0S253E.mjs → http-DLr_cJZ7.mjs} +42 -4
  31. package/dist/{index-B7RgLw--.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-D6fxYu36.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 -214
  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-Dzoc3NVC.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-J-zkiOyp.d.mts → ws-server-Ces08fjP.d.mts} +102 -13
  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-B1LM3zit.mjs +0 -157
  91. package/dist/dev-BSHFZGZr.mjs +0 -437
  92. package/dist/http-Dvgxujcu.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-CXaDDUl0.mjs +0 -148
  97. package/dist/server-E9z4ZWbd.d.mts +0 -132
  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
@@ -1,7 +1,4 @@
1
1
  //#region src/constants.d.ts
2
- declare const DEVFRAME_MOUNT_PATH = "/__devframe/";
3
- declare const DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH = "/__devframe";
4
- declare const DEVFRAME_DIRNAME = "__devframe";
5
2
  declare const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
6
3
  /**
7
4
  * Global key holding the serializable connection prepared by
@@ -16,7 +13,23 @@ declare const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
16
13
  * both HTTP and WS, and a host server (Vite, etc.) can mount the WS upgrade
17
14
  * handler here without colliding with its own routes (HMR, asset serving).
18
15
  */
19
- declare const DEVFRAME_WS_ROUTE = "__devframe_ws";
16
+ declare const DEVFRAME_WS_ROUTE = "__ws";
17
+ /**
18
+ * Route the SSE RPC endpoint is bound to, relative to a devframe's base
19
+ * path. A single method-dispatched route: `GET` opens the event stream
20
+ * (server→client), `POST` carries RPC frames (client→server). Sits next to
21
+ * `__connection.json` so the deployed SPA reaches it on the same origin it
22
+ * loaded from — the transport for hosts and proxies where the WebSocket
23
+ * upgrade isn't available.
24
+ */
25
+ declare const DEVFRAME_SSE_ROUTE = "__sse";
26
+ /**
27
+ * Request header carrying the SSE session id on every RPC `POST`. The
28
+ * server mints the id as the stream's first event (`event: session`);
29
+ * the client echoes it here so the POST joins that stream's birpc
30
+ * channel. Mirrors birpc's own SSE helpers for wire compatibility.
31
+ */
32
+ declare const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
20
33
  /**
21
34
  * Route the Streamable-HTTP MCP endpoint is bound to, relative to a
22
35
  * devframe's base path. Sits next to `__connection.json` and the WS route
@@ -68,9 +81,9 @@ declare const ANONYMOUS_RPC_PREFIX = "anonymous:";
68
81
  /**
69
82
  * Whether `name` is callable before a connection is trusted, i.e. it starts
70
83
  * with {@link ANONYMOUS_RPC_PREFIX}. Used by the resolver gate in
71
- * `startHttpAndWs` (via an `authorize` function) and by host adapters that
84
+ * the RPC server binding (via an `authorize` function) and by host adapters that
72
85
  * implement their own transport.
73
86
  */
74
87
  declare function isAnonymousRpcMethod(name: string): boolean;
75
88
  //#endregion
76
- export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
89
+ export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_SSE_ROUTE, DEVFRAME_SSE_SESSION_HEADER, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
@@ -1,7 +1,4 @@
1
1
  //#region src/constants.ts
2
- const DEVFRAME_MOUNT_PATH = "/__devframe/";
3
- const DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH = "/__devframe";
4
- const DEVFRAME_DIRNAME = "__devframe";
5
2
  const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
6
3
  /**
7
4
  * Global key holding the serializable connection prepared by
@@ -16,7 +13,23 @@ const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
16
13
  * both HTTP and WS, and a host server (Vite, etc.) can mount the WS upgrade
17
14
  * handler here without colliding with its own routes (HMR, asset serving).
18
15
  */
19
- const DEVFRAME_WS_ROUTE = "__devframe_ws";
16
+ const DEVFRAME_WS_ROUTE = "__ws";
17
+ /**
18
+ * Route the SSE RPC endpoint is bound to, relative to a devframe's base
19
+ * path. A single method-dispatched route: `GET` opens the event stream
20
+ * (server→client), `POST` carries RPC frames (client→server). Sits next to
21
+ * `__connection.json` so the deployed SPA reaches it on the same origin it
22
+ * loaded from — the transport for hosts and proxies where the WebSocket
23
+ * upgrade isn't available.
24
+ */
25
+ const DEVFRAME_SSE_ROUTE = "__sse";
26
+ /**
27
+ * Request header carrying the SSE session id on every RPC `POST`. The
28
+ * server mints the id as the stream's first event (`event: session`);
29
+ * the client echoes it here so the POST joins that stream's birpc
30
+ * channel. Mirrors birpc's own SSE helpers for wire compatibility.
31
+ */
32
+ const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
20
33
  /**
21
34
  * Route the Streamable-HTTP MCP endpoint is bound to, relative to a
22
35
  * devframe's base path. Sits next to `__connection.json` and the WS route
@@ -68,11 +81,11 @@ const ANONYMOUS_RPC_PREFIX = "anonymous:";
68
81
  /**
69
82
  * Whether `name` is callable before a connection is trusted, i.e. it starts
70
83
  * with {@link ANONYMOUS_RPC_PREFIX}. Used by the resolver gate in
71
- * `startHttpAndWs` (via an `authorize` function) and by host adapters that
84
+ * the RPC server binding (via an `authorize` function) and by host adapters that
72
85
  * implement their own transport.
73
86
  */
74
87
  function isAnonymousRpcMethod(name) {
75
88
  return name.startsWith(ANONYMOUS_RPC_PREFIX);
76
89
  }
77
90
  //#endregion
78
- export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
91
+ export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_SSE_ROUTE, DEVFRAME_SSE_SESSION_HEADER, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
@@ -1,5 +1,5 @@
1
1
  import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
2
- import { t as createStorage } from "./storage-Dzoc3NVC.mjs";
2
+ import { t as createStorage } from "./storage-BcmVEJRc.mjs";
3
3
  import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-DZbJ7Cl0.mjs";
4
4
  import { join } from "pathe";
5
5
  //#region src/node/hub-internals/context.ts
@@ -1,4 +1,4 @@
1
- import { b as DevframeNodeContext, ht as SharedState } from "./devframe-qgCKL683.mjs";
1
+ import { gt as SharedState, x as DevframeNodeContext } from "./devframe-Bdd9YXTy.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -2,242 +2,14 @@ import { t as devframeReporter } from "./diagnostics-reporter-CsIG85Q5.mjs";
2
2
  import { t as diagnostics } from "./diagnostics-hwjXp_UV.mjs";
3
3
  import { RpcFunctionsCollectorBase } from "./rpc/index.mjs";
4
4
  import { defineRpcFunction } from "./index.mjs";
5
- import { t as diagnostics$1 } from "./diagnostics-B5-qHeqD.mjs";
6
- import { i as createEventEmitter, n as createSharedState, r as nanoid, t as createStorage } from "./storage-Dzoc3NVC.mjs";
5
+ import { t as diagnostics$1 } from "./diagnostics-DLYH5oD3.mjs";
6
+ import { i as createEventEmitter, n as createSharedState, r as nanoid, t as createStorage } from "./storage-BcmVEJRc.mjs";
7
+ import { t as DevframeAgentHost } from "./host-agent-CpbAoqBx.mjs";
7
8
  import { defineDiagnostics } from "nostics";
8
9
  import { isatty } from "node:tty";
9
10
  import { formatWithOptions, inspect } from "node:util";
10
11
  import { existsSync } from "node:fs";
11
12
  import { join } from "pathe";
12
- import process$1 from "node:process";
13
- import { homedir } from "node:os";
14
- //#region src/node/agent-args.ts
15
- /**
16
- * Map the args payload an agent surface receives (MCP sends an object
17
- * keyed `arg0`/`arg1`/…, matching the schema the adapter advertises) onto
18
- * a handler's positional parameters. Shared by the agent host's RPC
19
- * bridge and the hub's command-derived tools so the coercion cannot
20
- * drift between them.
21
- *
22
- * - an array passes through as-is
23
- * - `null`/`undefined` become a zero-argument call
24
- * - with declared schemas, each schema reads its own `argN` key, in order
25
- * - without schemas, `arg0`/`arg1`/… keys are collected when present
26
- * - an empty object becomes a zero-argument call
27
- * - anything else follows the {@link AgentArgsFallback}
28
- *
29
- * @experimental
30
- */
31
- function coerceAgentPositionalArgs(args, schemas, fallback = "wrap") {
32
- if (Array.isArray(args)) return args;
33
- if (args === void 0 || args === null) return [];
34
- if (typeof args === "object") {
35
- const obj = args;
36
- if (schemas && schemas.length) return schemas.map((_, i) => obj[`arg${i}`]);
37
- if ("arg0" in obj) {
38
- const out = [];
39
- let i = 0;
40
- while (`arg${i}` in obj) {
41
- out.push(obj[`arg${i}`]);
42
- i++;
43
- }
44
- return out;
45
- }
46
- if (Object.keys(obj).length === 0) return [];
47
- }
48
- return fallback === "drop" ? [] : [args];
49
- }
50
- //#endregion
51
- //#region src/node/host-agent.ts
52
- /**
53
- * Framework-neutral host aggregating the agent-exposed surface of a
54
- * devframe. Auto-discovers RPC functions with an `agent` field from
55
- * `ctx.rpc.definitions`, and accepts plugin-registered tools /
56
- * resources via `registerTool` / `registerResource`.
57
- *
58
- * @experimental
59
- */
60
- var DevframeAgentHost = class {
61
- context;
62
- events = createEventEmitter();
63
- tools = /* @__PURE__ */ new Map();
64
- resources = /* @__PURE__ */ new Map();
65
- providers = /* @__PURE__ */ new Set();
66
- _rpcUnsubscribe;
67
- constructor(context) {
68
- this.context = context;
69
- this._rpcUnsubscribe = context.rpc.onChanged(() => {
70
- this.events.emit("agent:manifest:changed");
71
- });
72
- }
73
- registerTool(input) {
74
- this._validateToolId(input.id);
75
- const tool = this._projectTool(input);
76
- this.tools.set(tool.id, {
77
- tool,
78
- handler: input.handler
79
- });
80
- this.events.emit("agent:tool:registered", tool);
81
- this.events.emit("agent:manifest:changed");
82
- return { unregister: () => this.unregisterTool(tool.id) };
83
- }
84
- unregisterTool(id) {
85
- const existed = this.tools.delete(id);
86
- if (existed) {
87
- this.events.emit("agent:tool:unregistered", id);
88
- this.events.emit("agent:manifest:changed");
89
- }
90
- return existed;
91
- }
92
- registerToolProvider(provider) {
93
- this.providers.add(provider);
94
- this.events.emit("agent:manifest:changed");
95
- const notifyChanged = () => {
96
- if (this.providers.has(provider)) this.events.emit("agent:manifest:changed");
97
- };
98
- return {
99
- notifyChanged,
100
- unregister: () => {
101
- if (this.providers.delete(provider)) this.events.emit("agent:manifest:changed");
102
- }
103
- };
104
- }
105
- registerResource(input) {
106
- if (this.resources.has(input.id)) throw diagnostics$1.DF0016({ id: input.id });
107
- const resource = {
108
- id: input.id,
109
- name: input.name,
110
- description: input.description,
111
- mimeType: input.mimeType ?? "application/json",
112
- uri: input.uri ?? `devframe://resource/${encodeURIComponent(input.id)}`
113
- };
114
- this.resources.set(resource.id, {
115
- resource,
116
- read: input.read
117
- });
118
- this.events.emit("agent:resource:registered", resource);
119
- this.events.emit("agent:manifest:changed");
120
- return { unregister: () => this.unregisterResource(resource.id) };
121
- }
122
- unregisterResource(id) {
123
- const existed = this.resources.delete(id);
124
- if (existed) {
125
- this.events.emit("agent:resource:unregistered", id);
126
- this.events.emit("agent:manifest:changed");
127
- }
128
- return existed;
129
- }
130
- list() {
131
- const rpcTools = this._collectRpcTools();
132
- const plainTools = Array.from(this.tools.values()).map((t) => t.tool);
133
- const resources = Array.from(this.resources.values()).map((r) => r.resource);
134
- const seen = new Set([...rpcTools, ...plainTools].map((t) => t.id));
135
- const providerTools = [];
136
- for (const { tool } of this._collectProviderTools()) {
137
- if (seen.has(tool.id)) continue;
138
- seen.add(tool.id);
139
- providerTools.push(tool);
140
- }
141
- return {
142
- tools: [
143
- ...rpcTools,
144
- ...plainTools,
145
- ...providerTools
146
- ],
147
- resources
148
- };
149
- }
150
- getTool(id) {
151
- const plain = this.tools.get(id);
152
- if (plain) return plain.tool;
153
- const rpc = this._collectRpcTools().find((t) => t.id === id);
154
- if (rpc) return rpc;
155
- return this._collectProviderTools().find((t) => t.tool.id === id)?.tool;
156
- }
157
- getResource(id) {
158
- return this.resources.get(id)?.resource;
159
- }
160
- async invoke(id, args) {
161
- const plain = this.tools.get(id);
162
- if (plain?.handler) return await plain.handler(args);
163
- const rpcDef = this._findRpcDefinition(id);
164
- if (rpcDef) {
165
- const positional = coerceAgentPositionalArgs(args, rpcDef.args, "wrap");
166
- return await this.context.rpc.invokeLocal(id, ...positional);
167
- }
168
- const provided = this._collectProviderTools().find((t) => t.tool.id === id);
169
- if (provided) return await provided.input.handler(args);
170
- throw new Error(`[devframe/agent] tool "${id}" not found`);
171
- }
172
- async read(id) {
173
- const entry = this.resources.get(id);
174
- if (!entry) throw new Error(`[devframe/agent] resource "${id}" not found`);
175
- return await entry.read();
176
- }
177
- /** @internal */
178
- _dispose() {
179
- this._rpcUnsubscribe?.();
180
- this._rpcUnsubscribe = void 0;
181
- }
182
- _validateToolId(id) {
183
- if (this.tools.has(id)) throw diagnostics$1.DF0015({ id });
184
- if (this.context.rpc.definitions.get(id)?.agent) throw diagnostics$1.DF0015({ id });
185
- }
186
- _projectTool(input) {
187
- if (!input.description || typeof input.description !== "string") throw diagnostics$1.DF0014({ name: input.id });
188
- return {
189
- id: input.id,
190
- kind: "tool",
191
- title: input.title ?? input.id,
192
- description: input.description,
193
- safety: input.safety ?? "action",
194
- tags: input.tags,
195
- args: input.args,
196
- inputSchema: input.inputSchema,
197
- outputSchema: input.outputSchema,
198
- examples: input.examples
199
- };
200
- }
201
- /** Query every registered provider, projecting inputs to serializable tools. */
202
- _collectProviderTools() {
203
- const out = [];
204
- for (const provider of this.providers) for (const input of provider()) out.push({
205
- input,
206
- tool: this._projectTool(input)
207
- });
208
- return out;
209
- }
210
- _collectRpcTools() {
211
- const out = [];
212
- for (const [name, def] of this.context.rpc.definitions) {
213
- const agent = def.agent;
214
- if (!agent) continue;
215
- if (!agent.description || typeof agent.description !== "string") throw diagnostics$1.DF0014({ name });
216
- const type = def.type ?? "query";
217
- const safety = agent.safety ?? inferSafety(type);
218
- out.push({
219
- id: name,
220
- kind: "rpc",
221
- title: agent.title ?? name,
222
- description: agent.description,
223
- safety,
224
- tags: agent.tags,
225
- rpcName: name,
226
- examples: agent.examples
227
- });
228
- }
229
- return out;
230
- }
231
- _findRpcDefinition(id) {
232
- const def = this.context.rpc.definitions.get(id);
233
- if (def?.agent) return def;
234
- }
235
- };
236
- function inferSafety(type) {
237
- if (type === "static" || type === "query") return "read";
238
- return "action";
239
- }
240
- //#endregion
241
13
  //#region src/node/host-diagnostics.ts
242
14
  var DevframeDiagnosticsHost = class {
243
15
  context;
@@ -1217,7 +989,7 @@ const debugBroadcast = createDebug("devframe:rpc:broadcast");
1217
989
  * Concrete implementation backing `ctx.rpc`. Internal: consumers should
1218
990
  * depend on the structural {@link RpcFunctionsHost} type, never this class.
1219
991
  * Its `@internal` members (`_rpcGroup`, `_asyncStorage`,
1220
- * `_emitSessionDisconnected`) are wired by `startHttpAndWs` and must not
992
+ * `_emitSessionDisconnected`) are wired by `createContextRpcServer` and must not
1221
993
  * widen the public surface.
1222
994
  *
1223
995
  * @internal
@@ -1550,26 +1322,4 @@ async function createHostContext(options) {
1550
1322
  return context;
1551
1323
  }
1552
1324
  //#endregion
1553
- //#region src/node/host-h3.ts
1554
- /**
1555
- * h3-backed {@link DevframeHost} — used by the standalone CLI adapter.
1556
- */
1557
- function createH3DevframeHost(options) {
1558
- const workspaceRoot = options.workspaceRoot ?? process$1.cwd();
1559
- return {
1560
- mountStatic(base, distDir) {
1561
- return options.mount?.(base, distDir);
1562
- },
1563
- resolveOrigin() {
1564
- return options.origin;
1565
- },
1566
- getStorageDir(scope) {
1567
- const namespace = `.${options.appName}/devframe`;
1568
- if (scope === "workspace") return join(workspaceRoot, ".devframe");
1569
- if (scope === "project") return join(workspaceRoot, "node_modules", namespace);
1570
- return join(homedir(), namespace);
1571
- }
1572
- };
1573
- }
1574
- //#endregion
1575
- export { DevframeViewHost as a, createRpcSharedStateServerHost as c, coerceAgentPositionalArgs as d, createNodeSettings as i, DevframeDiagnosticsHost as l, createHostContext as n, DevframeServicesHostImpl as o, createScopedNodeContext as r, createRpcStreamingServerHost as s, createH3DevframeHost as t, DevframeAgentHost as u };
1325
+ export { createHostContext as t };
@@ -0,0 +1,233 @@
1
+ import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
2
+ import { t as diagnostics } from "./diagnostics-DLYH5oD3.mjs";
3
+ import { t as createHostContext } from "./context-Du11TLcO.mjs";
4
+ import { i as resolveMcpConnectionMeta, n as resolveBasePath, r as resolveDevServerPort, t as normalizeBasePath } from "./_shared-CttmCMBi.mjs";
5
+ import { t as createH3DevframeHost } from "./host-h3-CzCZwtuV.mjs";
6
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-KRdee1QF.mjs";
7
+ import { t as open } from "./open-Dz4V4irZ.mjs";
8
+ import { mountStaticHandler } from "./utils/serve-static.mjs";
9
+ import { createServer } from "node:http";
10
+ import { resolve } from "pathe";
11
+ import process from "node:process";
12
+ import { joinURL, withBase } from "ufo";
13
+ import { H3, toNodeHandler } from "h3";
14
+ //#region src/adapters/initiate.ts
15
+ const INSTANCE_INTERNALS = /* @__PURE__ */ new WeakMap();
16
+ /** @internal */
17
+ function getInstanceInternals(handler) {
18
+ return INSTANCE_INTERNALS.get(handler) ?? {};
19
+ }
20
+ /**
21
+ * Serve a devframe through one framework-agnostic, web-standard handler —
22
+ * the SPA, `__connection.json` discovery, the WebSocket RPC endpoint, the
23
+ * auth gate, and the optional MCP route, all under a single mount base.
24
+ * Mount `handler` on any framework's catch-all route (or `nodeMiddleware` on
25
+ * a connect stack) and the devframe is live inside that app.
26
+ *
27
+ * The factory is synchronous and kicks off initialization eagerly;
28
+ * `handler`/`nodeMiddleware` await readiness internally. Nothing binds a port
29
+ * on its own: the WebSocket resolves in precedence order — `ws.port` (pinned
30
+ * side-car) > `server` (shared upgrade at `<base>__ws`) > `ws.sidecar`
31
+ * (auto-port side-car) > the host driving upgrades itself through
32
+ * {@link DevframeInstance.attach} — while `ws.url`, when set, overrides the
33
+ * advertised* endpoint (the tunnel pattern) and on its own hands the whole
34
+ * transport to an external server. `__connection.json` reflects whichever
35
+ * combination is active.
36
+ */
37
+ function initDevframe(def, options) {
38
+ const base = normalizeBasePath(options.base);
39
+ const distDir = options.distDir === false ? void 0 : options.distDir ?? def.cli?.distDir;
40
+ const app = options.app ?? new H3();
41
+ const shell = createInstanceShell({
42
+ base,
43
+ app,
44
+ host: options.host ?? def.cli?.host ?? "localhost",
45
+ origin: options.origin,
46
+ auth: options.auth !== void 0 ? options.auth : def.cli?.auth,
47
+ server: options.server,
48
+ ws: options.ws ?? def.cli?.ws,
49
+ sse: options.sse ?? def.cli?.sse,
50
+ allowedOrigins: options.allowedOrigins,
51
+ destroyUnmatchedUpgrades: options.destroyUnmatchedUpgrades,
52
+ onPeerConnect: options.onPeerConnect,
53
+ onPeerDisconnect: options.onPeerDisconnect,
54
+ register: resolveInstanceRegister(options.register, {
55
+ id: def.id,
56
+ name: def.name
57
+ }),
58
+ resolveSidecarPort: (sidecarHost) => resolveDevServerPort(def, { host: sidecarHost }),
59
+ onMetaUnavailable: () => {
60
+ throw diagnostics.DF0054({ id: def.id });
61
+ },
62
+ async init(api) {
63
+ const h3Host = createH3DevframeHost({
64
+ origin: () => api.origin() ?? "http://localhost",
65
+ appName: def.id,
66
+ mount: (mountBase, dir) => {
67
+ mountStaticHandler(app, mountBase, dir);
68
+ }
69
+ });
70
+ const hostImpl = options.getStorageDir ? {
71
+ ...h3Host,
72
+ getStorageDir: options.getStorageDir
73
+ } : h3Host;
74
+ const context = await createHostContext({
75
+ cwd: process.cwd(),
76
+ mode: "dev",
77
+ host: hostImpl
78
+ });
79
+ const setupInfo = { flags: options.flags ?? {} };
80
+ await def.setup(context, setupInfo);
81
+ const mcpOption = options.mcp ?? def.cli?.mcp;
82
+ const mcpMeta = resolveMcpConnectionMeta(def, mcpOption);
83
+ let mcpDispose;
84
+ if (mcpMeta) {
85
+ const mcpConfig = mcpOption === true || mcpOption === void 0 ? {} : mcpOption;
86
+ const mcpPath = joinURL(base, mcpMeta.path);
87
+ let mountMcpHttp;
88
+ try {
89
+ ({mountMcpHttp} = await import("./http-DLr_cJZ7.mjs").then((n) => n.t));
90
+ } catch (error) {
91
+ const reason = error instanceof Error ? error.message : String(error);
92
+ throw diagnostics.DF0017({
93
+ transport: "http",
94
+ reason,
95
+ cause: error
96
+ });
97
+ }
98
+ mcpDispose = mountMcpHttp(app, context, mcpPath, {
99
+ serverName: `${def.id} (devframe)`,
100
+ serverVersion: def.version ?? "0.0.0",
101
+ exposeSharedState: true,
102
+ allowedOrigins: mcpConfig.allowedOrigins
103
+ }).dispose;
104
+ }
105
+ return {
106
+ context,
107
+ ...mcpMeta ? { mcp: mcpMeta } : {},
108
+ ...mcpDispose ? { dispose: mcpDispose } : {}
109
+ };
110
+ },
111
+ mount(_context, meta) {
112
+ app.use(joinURL(base, DEVFRAME_CONNECTION_META_FILENAME), () => meta);
113
+ if (distDir) mountStaticHandler(app, base, resolve(distDir));
114
+ }
115
+ });
116
+ const instance = {
117
+ base: shell.base,
118
+ handler: shell.handler,
119
+ nodeMiddleware: shell.nodeMiddleware,
120
+ attach: shell.attach,
121
+ handleUpgrade: shell.handleUpgrade,
122
+ ready: shell.ready,
123
+ context: shell.context,
124
+ connectionMeta: shell.connectionMeta,
125
+ close: shell.close
126
+ };
127
+ INSTANCE_INTERNALS.set(instance, shell.internals);
128
+ return instance;
129
+ }
130
+ //#endregion
131
+ //#region src/adapters/dev.ts
132
+ /**
133
+ * Start a devframe dev server for a {@link DevframeDefinition} —
134
+ * h3 + WebSocket RPC + (optionally) the author's SPA mounted at the
135
+ * resolved base path.
136
+ *
137
+ * When `distDir` is omitted (and `def.cli?.distDir` is unset) the
138
+ * server runs in **bridge mode**: only `__connection.json` and the WS
139
+ * endpoint are mounted, with no SPA mount. The SPA is expected to be
140
+ * hosted elsewhere (e.g. by a parent Vite/Nuxt dev server) — see
141
+ * `viteDevBridge({ devMiddleware })`.
142
+ *
143
+ * Returns the underlying {@link StartedServer} handle so callers can
144
+ * close it gracefully (SIGINT, hot-reload, test teardown).
145
+ *
146
+ * Use this directly when integrating devframe into an existing CLI
147
+ * framework (commander, yargs, hand-rolled CAC). For the all-in-one
148
+ * `dev` / `build` / `mcp` shell, reach for {@link createCac} instead.
149
+ */
150
+ async function createDevServer(def, options = {}) {
151
+ const host = options.host ?? def.cli?.host ?? "localhost";
152
+ const requestedPort = options.port ?? await resolveDevServerPort(def, { host });
153
+ const flags = options.flags ?? {};
154
+ const basePath = options.basePath ? normalizeBasePath(options.basePath) : resolveBasePath(def, "standalone");
155
+ const app = options.app ?? new H3();
156
+ const server = createServer(toNodeHandler(app));
157
+ try {
158
+ await new Promise((resolveListen, rejectListen) => {
159
+ const onError = (error) => rejectListen(error);
160
+ server.once("error", onError);
161
+ server.listen(requestedPort, host, () => {
162
+ server.removeListener("error", onError);
163
+ resolveListen();
164
+ });
165
+ });
166
+ } catch (error) {
167
+ throw diagnostics.DF0052({
168
+ host,
169
+ port: requestedPort,
170
+ reason: error instanceof Error ? error.message : String(error),
171
+ cause: error
172
+ });
173
+ }
174
+ const address = server.address();
175
+ const port = typeof address === "object" && address ? address.port : requestedPort;
176
+ const origin = normalizeHttpServerUrl(host, port);
177
+ const devframe = initDevframe(def, {
178
+ base: basePath,
179
+ distDir: options.distDir,
180
+ app,
181
+ server,
182
+ host,
183
+ origin,
184
+ ws: options.ws,
185
+ sse: options.sse,
186
+ auth: flags.auth === false ? false : options.auth,
187
+ mcp: options.mcp,
188
+ flags,
189
+ onPeerConnect: options.onPeerConnect,
190
+ onPeerDisconnect: options.onPeerDisconnect,
191
+ register: true,
192
+ destroyUnmatchedUpgrades: true
193
+ });
194
+ try {
195
+ await devframe.ready;
196
+ } catch (error) {
197
+ await new Promise((resolveClose) => server.close(() => resolveClose()));
198
+ throw error;
199
+ }
200
+ const internals = getInstanceInternals(devframe);
201
+ const transport = internals.started;
202
+ await options.onReady?.({
203
+ origin,
204
+ port,
205
+ app
206
+ });
207
+ await maybeOpenBrowser(def, flags, `${origin}${basePath}`, options.openBrowser, internals.authHandler);
208
+ return {
209
+ origin,
210
+ port,
211
+ app,
212
+ ws: transport.ws,
213
+ rpcGroup: transport.rpcGroup,
214
+ connectionMeta: transport.connectionMeta,
215
+ async close() {
216
+ await devframe.close();
217
+ await new Promise((resolveClose) => server.close(() => resolveClose()));
218
+ }
219
+ };
220
+ }
221
+ async function maybeOpenBrowser(def, flags, origin, override, authHandler) {
222
+ const flagsOpen = flags.open;
223
+ const cliOpen = def.cli?.open;
224
+ const resolved = override ?? flagsOpen ?? cliOpen;
225
+ if (resolved === void 0 || resolved === false) return;
226
+ const target = typeof resolved === "string" ? withBase(resolved, origin) : origin;
227
+ const authorizedTarget = authHandler?.buildOpenUrl?.(target) ?? target;
228
+ try {
229
+ await open(authorizedTarget);
230
+ } catch {}
231
+ }
232
+ //#endregion
233
+ export { getInstanceInternals as n, initDevframe as r, createDevServer as t };