devframe 0.8.2 → 0.9.0-beta.10

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 (127) hide show
  1. package/dist/_shared-BPmX9fqK.d.mts +41 -0
  2. package/dist/_shared-CbZ_yhbz.mjs +68 -0
  3. package/dist/adapters/build.d.mts +10 -12
  4. package/dist/adapters/build.mjs +26 -29
  5. package/dist/adapters/cac.d.mts +1 -1
  6. package/dist/adapters/cac.mjs +156 -1
  7. package/dist/adapters/dev.d.mts +38 -46
  8. package/dist/adapters/dev.mjs +2 -1
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +224 -0
  11. package/dist/adapters/initiate.mjs +2 -0
  12. package/dist/adapters/mcp.d.mts +24 -7
  13. package/dist/adapters/mcp.mjs +2 -2
  14. package/dist/{agent-tool-name-C3b5vEwJ.mjs → agent-tool-name-EgfoFO8C.mjs} +0 -3
  15. package/dist/cli/main.d.mts +0 -2
  16. package/dist/cli/main.mjs +4 -8
  17. package/dist/client/index.d.mts +71 -7
  18. package/dist/client/index.mjs +720 -1540
  19. package/dist/constants.d.mts +28 -7
  20. package/dist/constants.mjs +28 -7
  21. package/dist/{context-DoSozfvx.d.mts → context-B3YUu_95.d.mts} +19 -1
  22. package/dist/{host-h3-D2hYZvDK.mjs → context-CEe4PBz1.mjs} +25 -557
  23. package/dist/{context-CBg8iRbQ.mjs → context-xdynGSLP.mjs} +10 -1
  24. package/dist/dev--rAPzbvO.mjs +239 -0
  25. package/dist/{devframe-mQ4qZAuo.d.mts → devframe-DJMg0WLe.d.mts} +181 -134
  26. package/dist/{diagnostics-hwjXp_UV.mjs → diagnostics-CD8nlgll.mjs} +1 -3
  27. package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DOQPnQmX.mjs} +51 -5
  28. package/dist/dist-eNI9qM_I.mjs +228 -0
  29. package/dist/host-agent-xLCSe65D.mjs +264 -0
  30. package/dist/host-h3-Crkzm42q.mjs +26 -0
  31. package/dist/{fetch-BKaD0Vb3.mjs → http-DYpK0cUi.mjs} +42 -11
  32. package/dist/{index-b64Uuhy2.d.mts → index-CB-fSUyn.d.mts} +2 -2
  33. package/dist/{index-Dbw1p5ch.d.mts → index-tdrk1bzV.d.mts} +3 -32
  34. package/dist/index.d.mts +12 -5
  35. package/dist/index.mjs +8 -1
  36. package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-WvQkt42E.mjs} +9 -8
  37. package/dist/instance-shell-CMFyszWJ.mjs +486 -0
  38. package/dist/instance-shell-CljiKq1c.d.mts +255 -0
  39. package/dist/internal/index.d.mts +397 -0
  40. package/dist/internal/index.mjs +9 -0
  41. package/dist/node/auth.d.mts +2 -2
  42. package/dist/node/auth.mjs +1 -1
  43. package/dist/node/hub-internals.d.mts +2 -14
  44. package/dist/node/hub-internals.mjs +2 -2
  45. package/dist/node/index.d.mts +4 -268
  46. package/dist/node/index.mjs +3 -5
  47. package/dist/nostics-CzECRXpE.mjs +59 -0
  48. package/dist/nostics-SPGDgHEP.d.mts +33 -0
  49. package/dist/recipes/common-rpc-functions.d.mts +4 -4
  50. package/dist/recipes/interactive-auth.d.mts +3 -3
  51. package/dist/recipes/interactive-auth.mjs +8 -8
  52. package/dist/remote-assets-Bg4gCUZ_.d.mts +123 -0
  53. package/dist/remote-assets-nTTxY2MU.mjs +636 -0
  54. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  55. package/dist/rpc/dump.d.mts +77 -1
  56. package/dist/rpc/dump.mjs +1 -1
  57. package/dist/rpc/index.d.mts +3 -3
  58. package/dist/rpc/index.mjs +4 -18
  59. package/dist/rpc/transports/sse-client.d.mts +2 -0
  60. package/dist/rpc/transports/sse-client.mjs +142 -0
  61. package/dist/rpc/transports/sse-server.d.mts +61 -0
  62. package/dist/rpc/transports/sse-server.mjs +209 -0
  63. package/dist/rpc/transports/ws-bun.d.mts +35 -0
  64. package/dist/rpc/transports/ws-bun.mjs +31 -0
  65. package/dist/rpc/transports/ws-client.d.mts +1 -1
  66. package/dist/rpc/transports/ws-client.mjs +10 -18
  67. package/dist/rpc/transports/ws-server.d.mts +2 -2
  68. package/dist/rpc/transports/ws-server.mjs +2 -235
  69. package/dist/rpc-core-Cc4lucI0.d.mts +48 -0
  70. package/dist/rpc-core-DMTe-hLo.mjs +81 -0
  71. package/dist/{serialization-C8Mnw9hK.mjs → serialization-BGzEwAdr.mjs} +2 -2
  72. package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
  73. package/dist/sse-client-CFBOIaUi.d.mts +37 -0
  74. package/dist/state-Cc1IeSmQ.mjs +1291 -0
  75. package/dist/{dump-CgZShDRB.mjs → static-BPZ1_OPA.mjs} +3 -266
  76. package/dist/storage-CYye7P7L.mjs +137 -0
  77. package/dist/types/index.d.mts +6 -4
  78. package/dist/types/index.mjs +1 -6
  79. package/dist/{types-CnJSgRVa.d.mts → types-Djq7CBXh.d.mts} +0 -7
  80. package/dist/utils/agent-tool-name.d.mts +0 -3
  81. package/dist/utils/agent-tool-name.mjs +0 -3
  82. package/dist/utils/events.d.mts +1 -1
  83. package/dist/utils/get-port.d.mts +17 -0
  84. package/dist/utils/get-port.mjs +2 -0
  85. package/dist/utils/launch-editor.mjs +1 -25
  86. package/dist/utils/nostics.d.mts +2 -0
  87. package/dist/utils/nostics.mjs +2 -0
  88. package/dist/utils/open.d.mts +2 -10
  89. package/dist/utils/open.mjs +56 -1
  90. package/dist/utils/remote-assets.d.mts +15 -0
  91. package/dist/utils/remote-assets.mjs +2 -0
  92. package/dist/utils/serve-static.d.mts +13 -5
  93. package/dist/utils/serve-static.mjs +119 -9
  94. package/dist/utils/shared-state.d.mts +1 -1
  95. package/dist/utils/shared-state.mjs +1 -1
  96. package/dist/utils/simple-schema.d.mts +101 -1
  97. package/dist/utils/streaming-channel.d.mts +1 -1
  98. package/dist/validation-CpXFB6Dz.mjs +266 -0
  99. package/dist/wire-codec-0K-o5MYW.mjs +56 -0
  100. package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-DhjNyQo3.d.mts} +7 -2
  101. package/dist/ws-server-BdSLrhxE.mjs +267 -0
  102. package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-D1d3QM9f.d.mts} +90 -9
  103. package/package.json +13 -12
  104. package/skills/devframe/SKILL.md +91 -93
  105. package/dist/_shared-bWRzeSa0.mjs +0 -19
  106. package/dist/adapters/cli.d.mts +0 -11
  107. package/dist/adapters/cli.mjs +0 -6
  108. package/dist/cac-D4_9ok0z.mjs +0 -157
  109. package/dist/dev-Bc49u3qg.mjs +0 -439
  110. package/dist/diagnostics-reporter-CsIG85Q5.mjs +0 -38
  111. package/dist/helpers/vite.d.mts +0 -100
  112. package/dist/helpers/vite.mjs +0 -105
  113. package/dist/http-C4T68YV6.mjs +0 -38
  114. package/dist/index-vAX6bYuc.d.mts +0 -78
  115. package/dist/open-Deb5xmIT.mjs +0 -547
  116. package/dist/recipes/open-helpers.d.mts +0 -6
  117. package/dist/recipes/open-helpers.mjs +0 -6
  118. package/dist/server-CD8XmBXl.mjs +0 -167
  119. package/dist/server-UHWae43Q.d.mts +0 -149
  120. package/dist/simple-schema-BDzLeJDk.d.mts +0 -102
  121. package/dist/state-BeUHDDjk.mjs +0 -108
  122. package/dist/storage-CTNVbuFB.mjs +0 -1122
  123. package/dist/utils/promise.d.mts +0 -8
  124. package/dist/utils/promise.mjs +0 -15
  125. package/dist/utils/scope.d.mts +0 -11
  126. package/dist/utils/scope.mjs +0 -15
  127. package/skills/devframe/templates/spa-devframe.ts +0 -38
@@ -1,9 +1,7 @@
1
- import { t as devframeReporter } from "./diagnostics-reporter-CsIG85Q5.mjs";
2
- import { defineDiagnostics } from "nostics";
1
+ import { i as defineDiagnostics } from "./nostics-CzECRXpE.mjs";
3
2
  //#region src/node/diagnostics.ts
4
3
  const diagnostics = defineDiagnostics({
5
4
  docsBase: "https://devfra.me/errors",
6
- reporters: [devframeReporter],
7
5
  codes: {
8
6
  DF0006: { why: (p) => `RPC function "${p.name}" is not registered` },
9
7
  DF0007: { why: "AsyncLocalStorage is not set, it likely to be an internal bug of the Devframe foundation" },
@@ -38,7 +36,7 @@ const diagnostics = defineDiagnostics({
38
36
  },
39
37
  DF0033: {
40
38
  why: (p) => `Failed to start dev RPC bridge for "${p.id}": ${p.reason}`,
41
- fix: "Verify the bridge port is free and the devframe setup function does not throw. Pin a port via `cli.port` / `cli.portRange` on the definition, or via `devMiddleware.port` on `viteDevBridge`."
39
+ fix: "Verify the bridge port is free and the devframe setup function does not throw. Pin a port via `cli.port` / `cli.portRange` on the definition, or via `port` on `devframeViteBridge` (`@devframes/vite`)."
42
40
  },
43
41
  DF0034: {
44
42
  why: (p) => `Scoped RPC registration for namespace "${p.namespace}" received an already-namespaced function name "${p.name}".`,
@@ -90,7 +88,55 @@ const diagnostics = defineDiagnostics({
90
88
  },
91
89
  DF0052: {
92
90
  why: (p) => `Failed to listen on ${p.host}:${p.port}: ${p.reason}`,
93
- fix: "The port is likely already taken by another process (often a previous devframe instance). Free it, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `devMiddleware.port` on `viteDevBridge`. The original node error is available as `error.cause`."
91
+ fix: "The port is likely already taken by another process (often a previous devframe instance). Free it, or pick another via `--port`, `cli.port` / `cli.portRange` on the definition, or `port` on `devframeViteBridge` (`@devframes/vite`). The original node error is available as `error.cause`."
92
+ },
93
+ DF0054: {
94
+ why: (p) => `connectionMeta() was called before initDevframe("${p.id}") finished initializing.`,
95
+ fix: "Await `instance.ready` (or any request through `instance.handler`) before reading `connectionMeta()` — the WebSocket binding it describes is only known once initialization completes."
96
+ },
97
+ DF0055: {
98
+ why: (p) => `This instance already owns its WebSocket transport (${p.tier}), so it cannot take over the host's upgrade events.`,
99
+ fix: "Drop `handleUpgrade`/`attach` and let the configured transport serve the socket, or remove `server` / `ws.port` / `ws.sidecar` from the options so the instance leaves the binding to you."
100
+ },
101
+ DF0056: {
102
+ why: (p) => `This instance advertises an external WebSocket endpoint (${p.url}), so it serves no socket of its own.`,
103
+ fix: "The server behind `ws.url` owns the transport (and its auth). Drop `ws.url` to have the instance serve the socket, or pair it with `server` / `ws.port` / `ws.sidecar` for the tunnel pattern, where a local binding is advertised through the relay."
104
+ },
105
+ DF0057: {
106
+ why: () => "This instance disables its WebSocket transport (`ws: false`), so there is no socket to drive upgrades into.",
107
+ fix: "Clients connect over the SSE endpoint instead — no upgrade wiring is needed. Remove `ws: false` if the instance should serve a WebSocket after all."
108
+ },
109
+ DF0058: {
110
+ why: (p) => `"${p.id}" declares \`capabilities.dev: false\` — it does not support a live dev server (its value is a static export only).`,
111
+ fix: "Pass `{ force: true }` to `createDevServer()` to run it anyway, or drop `capabilities.dev: false` on the definition."
112
+ },
113
+ DF0059: {
114
+ why: (p) => `Failed to fetch the file listing for "${p.package}@${p.version}" from ${p.provider}: ${p.reason}`,
115
+ fix: "Requests fall back to probing the provider per file. Check network access to the provider, or install the assets package locally so no listing is needed."
116
+ },
117
+ DF0060: {
118
+ why: (p) => `Failed to fetch a remote asset of "${p.package}" (${p.url}): ${p.reason}`,
119
+ fix: "Install the assets package locally (`npm install <package>`) to serve it with zero network, or check network access to the configured provider."
120
+ },
121
+ DF0061: {
122
+ why: (p) => `The locally installed "${p.package}@${p.installed}" is a different major version than the required "${p.required}".`,
123
+ fix: "Align the installed assets package with the version its node package declares — they are published in lockstep."
124
+ },
125
+ DF0062: {
126
+ why: (p) => `The locally installed "${p.package}@${p.installed}" differs from the required "${p.required}" — serving the installed one.`,
127
+ fix: "Install the exact declared version to serve byte-identical assets."
128
+ },
129
+ DF0063: {
130
+ why: (p) => `Failed to persist a remote asset into the cache at "${p.filepath}": ${p.reason}`,
131
+ fix: "The response was still served; only caching failed. Check that the cache directory is writable and has free space."
132
+ },
133
+ DF0064: {
134
+ why: (p) => `Failed to materialize the remote assets of "${p.package}@${p.version}": ${p.reason}`,
135
+ fix: "Static builds need every asset file up front. Install the assets package locally, or ensure the provider (and its file-listing API) is reachable during the build."
136
+ },
137
+ DF0065: {
138
+ why: (p) => `Invalid remote-assets ${p.field} "${p.value}".`,
139
+ fix: "A remote-assets `package` must be a valid npm package name and `version` an exact semver version (e.g. `1.2.3`) — they are interpolated into CDN URLs and the cache path."
94
140
  }
95
141
  }
96
142
  });
@@ -0,0 +1,228 @@
1
+ import { n as __exportAll } from "./rolldown-runtime-JspESFgx.mjs";
2
+ import "node:fs/promises";
3
+ import { createServer } from "node:net";
4
+ import { networkInterfaces } from "node:os";
5
+ //#region ../../node_modules/.pnpm/get-port-please@3.2.0/node_modules/get-port-please/dist/index.mjs
6
+ var dist_exports = /* @__PURE__ */ __exportAll({
7
+ checkPort: () => checkPort,
8
+ getPort: () => getPort,
9
+ getRandomPort: () => getRandomPort,
10
+ isSafePort: () => isSafePort,
11
+ isUnsafePort: () => isUnsafePort
12
+ });
13
+ const unsafePorts = /* @__PURE__ */ new Set([
14
+ 1,
15
+ 7,
16
+ 9,
17
+ 11,
18
+ 13,
19
+ 15,
20
+ 17,
21
+ 19,
22
+ 20,
23
+ 21,
24
+ 22,
25
+ 23,
26
+ 25,
27
+ 37,
28
+ 42,
29
+ 43,
30
+ 53,
31
+ 69,
32
+ 77,
33
+ 79,
34
+ 87,
35
+ 95,
36
+ 101,
37
+ 102,
38
+ 103,
39
+ 104,
40
+ 109,
41
+ 110,
42
+ 111,
43
+ 113,
44
+ 115,
45
+ 117,
46
+ 119,
47
+ 123,
48
+ 135,
49
+ 137,
50
+ 139,
51
+ 143,
52
+ 161,
53
+ 179,
54
+ 389,
55
+ 427,
56
+ 465,
57
+ 512,
58
+ 513,
59
+ 514,
60
+ 515,
61
+ 526,
62
+ 530,
63
+ 531,
64
+ 532,
65
+ 540,
66
+ 548,
67
+ 554,
68
+ 556,
69
+ 563,
70
+ 587,
71
+ 601,
72
+ 636,
73
+ 989,
74
+ 990,
75
+ 993,
76
+ 995,
77
+ 1719,
78
+ 1720,
79
+ 1723,
80
+ 2049,
81
+ 3659,
82
+ 4045,
83
+ 5060,
84
+ 5061,
85
+ 6e3,
86
+ 6566,
87
+ 6665,
88
+ 6666,
89
+ 6667,
90
+ 6668,
91
+ 6669,
92
+ 6697,
93
+ 10080
94
+ ]);
95
+ function isUnsafePort(port) {
96
+ return unsafePorts.has(port);
97
+ }
98
+ function isSafePort(port) {
99
+ return !isUnsafePort(port);
100
+ }
101
+ var GetPortError = class extends Error {
102
+ constructor(message, opts) {
103
+ super(message, opts);
104
+ this.message = message;
105
+ }
106
+ name = "GetPortError";
107
+ };
108
+ function _log(verbose, message) {
109
+ if (verbose) console.log(`[get-port] ${message}`);
110
+ }
111
+ function _generateRange(from, to) {
112
+ if (to < from) return [];
113
+ const r = [];
114
+ for (let index = from; index <= to; index++) r.push(index);
115
+ return r;
116
+ }
117
+ function _tryPort(port, host) {
118
+ return new Promise((resolve) => {
119
+ const server = createServer();
120
+ server.unref();
121
+ server.on("error", () => {
122
+ resolve(false);
123
+ });
124
+ server.listen({
125
+ port,
126
+ host
127
+ }, () => {
128
+ const { port: port2 } = server.address();
129
+ server.close(() => {
130
+ resolve(isSafePort(port2) && port2);
131
+ });
132
+ });
133
+ });
134
+ }
135
+ function _getLocalHosts(additional) {
136
+ const hosts = new Set(additional);
137
+ for (const _interface of Object.values(networkInterfaces())) for (const config of _interface || []) if (config.address && !config.internal && !config.address.startsWith("fe80::") && !config.address.startsWith("169.254")) hosts.add(config.address);
138
+ return [...hosts];
139
+ }
140
+ async function _findPort(ports, host) {
141
+ for (const port of ports) {
142
+ const r = await _tryPort(port, host);
143
+ if (r) return r;
144
+ }
145
+ }
146
+ function _fmtOnHost(hostname) {
147
+ return hostname ? `on host ${JSON.stringify(hostname)}` : "on any host";
148
+ }
149
+ const HOSTNAME_RE = /^(?!-)[\d.:A-Za-z-]{1,63}(?<!-)$/;
150
+ function _validateHostname(hostname, _public, verbose) {
151
+ if (hostname && !HOSTNAME_RE.test(hostname)) {
152
+ const fallbackHost = _public ? "0.0.0.0" : "127.0.0.1";
153
+ _log(verbose, `Invalid hostname: ${JSON.stringify(hostname)}. Using ${JSON.stringify(fallbackHost)} as fallback.`);
154
+ return fallbackHost;
155
+ }
156
+ return hostname;
157
+ }
158
+ async function getPort(_userOptions = {}) {
159
+ if (typeof _userOptions === "number" || typeof _userOptions === "string") _userOptions = { port: Number.parseInt(_userOptions + "") || 0 };
160
+ const _port = Number(_userOptions.port ?? process.env.PORT);
161
+ const _userSpecifiedAnyPort = Boolean(_userOptions.port || _userOptions.ports?.length || _userOptions.portRange?.length);
162
+ const options = {
163
+ random: _port === 0,
164
+ ports: [],
165
+ portRange: [],
166
+ alternativePortRange: _userSpecifiedAnyPort ? [] : [3e3, 3100],
167
+ verbose: false,
168
+ ..._userOptions,
169
+ port: _port,
170
+ host: _validateHostname(_userOptions.host ?? process.env.HOST, _userOptions.public, _userOptions.verbose)
171
+ };
172
+ if (options.random && !_userSpecifiedAnyPort) return getRandomPort(options.host);
173
+ const portsToCheck = [
174
+ options.port,
175
+ ...options.ports,
176
+ ..._generateRange(...options.portRange)
177
+ ].filter((port) => {
178
+ if (!port) return false;
179
+ if (!isSafePort(port)) {
180
+ _log(options.verbose, `Ignoring unsafe port: ${port}`);
181
+ return false;
182
+ }
183
+ return true;
184
+ });
185
+ if (portsToCheck.length === 0) portsToCheck.push(3e3);
186
+ let availablePort = await _findPort(portsToCheck, options.host);
187
+ if (!availablePort && options.alternativePortRange.length > 0) {
188
+ availablePort = await _findPort(_generateRange(...options.alternativePortRange), options.host);
189
+ if (portsToCheck.length > 0) {
190
+ let message = `Unable to find an available port (tried ${portsToCheck.join("-")} ${_fmtOnHost(options.host)}).`;
191
+ if (availablePort) message += ` Using alternative port ${availablePort}.`;
192
+ _log(options.verbose, message);
193
+ }
194
+ }
195
+ if (!availablePort && _userOptions.random !== false) {
196
+ availablePort = await getRandomPort(options.host);
197
+ if (availablePort) _log(options.verbose, `Using random port ${availablePort}`);
198
+ }
199
+ if (!availablePort) {
200
+ const triedRanges = [
201
+ options.port,
202
+ options.portRange.join("-"),
203
+ options.alternativePortRange.join("-")
204
+ ].filter(Boolean).join(", ");
205
+ throw new GetPortError(`Unable to find an available port ${_fmtOnHost(options.host)} (tried ${triedRanges})`);
206
+ }
207
+ return availablePort;
208
+ }
209
+ async function getRandomPort(host) {
210
+ const port = await checkPort(0, host);
211
+ if (port === false) throw new GetPortError(`Unable to find a random port ${_fmtOnHost(host)}`);
212
+ return port;
213
+ }
214
+ async function checkPort(port, host = process.env.HOST, verbose) {
215
+ if (!host) host = _getLocalHosts([void 0, "0.0.0.0"]);
216
+ if (!Array.isArray(host)) return _tryPort(port, host);
217
+ for (const _host of host) {
218
+ const _port = await _tryPort(port, _host);
219
+ if (_port === false) {
220
+ if (port < 1024 && verbose) _log(verbose, `Unable to listen to the privileged port ${port} ${_fmtOnHost(_host)}`);
221
+ return false;
222
+ }
223
+ if (port === 0 && _port !== 0) port = _port;
224
+ }
225
+ return port;
226
+ }
227
+ //#endregion
228
+ export { getPort as n, dist_exports as t };
@@ -0,0 +1,264 @@
1
+ import { t as diagnostics } from "./diagnostics-DOQPnQmX.mjs";
2
+ //#region src/utils/events.ts
3
+ /**
4
+ * Create event emitter.
5
+ */
6
+ function createEventEmitter() {
7
+ const _listeners = {};
8
+ function emit(event, ...args) {
9
+ const callbacks = _listeners[event] || [];
10
+ for (let i = 0, length = callbacks.length; i < length; i++) {
11
+ const callback = callbacks[i];
12
+ if (callback) callback(...args);
13
+ }
14
+ }
15
+ function emitOnce(event, ...args) {
16
+ emit(event, ...args);
17
+ delete _listeners[event];
18
+ }
19
+ function on(event, cb) {
20
+ (_listeners[event] ||= []).push(cb);
21
+ return () => {
22
+ _listeners[event] = _listeners[event]?.filter((i) => cb !== i);
23
+ };
24
+ }
25
+ function once(event, cb) {
26
+ const unsubscribe = on(event, ((...args) => {
27
+ unsubscribe();
28
+ return cb(...args);
29
+ }));
30
+ return unsubscribe;
31
+ }
32
+ return {
33
+ _listeners,
34
+ emit,
35
+ emitOnce,
36
+ on,
37
+ once
38
+ };
39
+ }
40
+ //#endregion
41
+ //#region src/node/agent-args.ts
42
+ /**
43
+ * Map the args payload an agent surface receives (MCP sends an object
44
+ * keyed `arg0`/`arg1`/…, matching the schema the adapter advertises) onto
45
+ * a handler's positional parameters. Shared by the agent host's RPC
46
+ * bridge and the hub's command-derived tools so the coercion cannot
47
+ * drift between them.
48
+ *
49
+ * - an array passes through as-is
50
+ * - `null`/`undefined` become a zero-argument call
51
+ * - with declared schemas, each schema reads its own `argN` key, in order
52
+ * - without schemas, `arg0`/`arg1`/… keys are collected when present
53
+ * - an empty object becomes a zero-argument call
54
+ * - anything else follows the {@link AgentArgsFallback}
55
+ */
56
+ function coerceAgentPositionalArgs(args, schemas, fallback = "wrap") {
57
+ if (Array.isArray(args)) return args;
58
+ if (args === void 0 || args === null) return [];
59
+ if (typeof args === "object") {
60
+ const obj = args;
61
+ if (schemas && schemas.length) return schemas.map((_, i) => obj[`arg${i}`]);
62
+ if ("arg0" in obj) {
63
+ const out = [];
64
+ let i = 0;
65
+ while (`arg${i}` in obj) {
66
+ out.push(obj[`arg${i}`]);
67
+ i++;
68
+ }
69
+ return out;
70
+ }
71
+ if (Object.keys(obj).length === 0) return [];
72
+ }
73
+ return fallback === "drop" ? [] : [args];
74
+ }
75
+ //#endregion
76
+ //#region src/node/host-agent.ts
77
+ /**
78
+ * Framework-neutral host aggregating the agent-exposed surface of a
79
+ * devframe. Auto-discovers RPC functions with an `agent` field from
80
+ * `ctx.rpc.definitions`, and accepts plugin-registered tools /
81
+ * resources via `registerTool` / `registerResource`.
82
+ */
83
+ var DevframeAgentHost = class {
84
+ context;
85
+ events = createEventEmitter();
86
+ tools = /* @__PURE__ */ new Map();
87
+ resources = /* @__PURE__ */ new Map();
88
+ providers = /* @__PURE__ */ new Set();
89
+ _rpcUnsubscribe;
90
+ constructor(context) {
91
+ this.context = context;
92
+ this._rpcUnsubscribe = context.rpc.onChanged(() => {
93
+ this.events.emit("agent:manifest:changed");
94
+ });
95
+ }
96
+ registerTool(input) {
97
+ this._validateToolId(input.id);
98
+ const tool = this._projectTool(input);
99
+ this.tools.set(tool.id, {
100
+ tool,
101
+ handler: input.handler
102
+ });
103
+ this.events.emit("agent:tool:registered", tool);
104
+ this.events.emit("agent:manifest:changed");
105
+ return { unregister: () => this.unregisterTool(tool.id) };
106
+ }
107
+ unregisterTool(id) {
108
+ const existed = this.tools.delete(id);
109
+ if (existed) {
110
+ this.events.emit("agent:tool:unregistered", id);
111
+ this.events.emit("agent:manifest:changed");
112
+ }
113
+ return existed;
114
+ }
115
+ registerToolProvider(provider) {
116
+ this.providers.add(provider);
117
+ this.events.emit("agent:manifest:changed");
118
+ const notifyChanged = () => {
119
+ if (this.providers.has(provider)) this.events.emit("agent:manifest:changed");
120
+ };
121
+ return {
122
+ notifyChanged,
123
+ unregister: () => {
124
+ if (this.providers.delete(provider)) this.events.emit("agent:manifest:changed");
125
+ }
126
+ };
127
+ }
128
+ registerResource(input) {
129
+ if (this.resources.has(input.id)) throw diagnostics.DF0016({ id: input.id });
130
+ const resource = {
131
+ id: input.id,
132
+ name: input.name,
133
+ description: input.description,
134
+ mimeType: input.mimeType ?? "application/json",
135
+ uri: input.uri ?? `devframe://resource/${encodeURIComponent(input.id)}`
136
+ };
137
+ this.resources.set(resource.id, {
138
+ resource,
139
+ read: input.read
140
+ });
141
+ this.events.emit("agent:resource:registered", resource);
142
+ this.events.emit("agent:manifest:changed");
143
+ return { unregister: () => this.unregisterResource(resource.id) };
144
+ }
145
+ unregisterResource(id) {
146
+ const existed = this.resources.delete(id);
147
+ if (existed) {
148
+ this.events.emit("agent:resource:unregistered", id);
149
+ this.events.emit("agent:manifest:changed");
150
+ }
151
+ return existed;
152
+ }
153
+ list() {
154
+ const rpcTools = this._collectRpcTools();
155
+ const plainTools = Array.from(this.tools.values()).map((t) => t.tool);
156
+ const resources = Array.from(this.resources.values()).map((r) => r.resource);
157
+ const seen = new Set([...rpcTools, ...plainTools].map((t) => t.id));
158
+ const providerTools = [];
159
+ for (const { tool } of this._collectProviderTools()) {
160
+ if (seen.has(tool.id)) continue;
161
+ seen.add(tool.id);
162
+ providerTools.push(tool);
163
+ }
164
+ return {
165
+ tools: [
166
+ ...rpcTools,
167
+ ...plainTools,
168
+ ...providerTools
169
+ ],
170
+ resources
171
+ };
172
+ }
173
+ getTool(id) {
174
+ const plain = this.tools.get(id);
175
+ if (plain) return plain.tool;
176
+ const rpc = this._collectRpcTools().find((t) => t.id === id);
177
+ if (rpc) return rpc;
178
+ return this._collectProviderTools().find((t) => t.tool.id === id)?.tool;
179
+ }
180
+ getResource(id) {
181
+ return this.resources.get(id)?.resource;
182
+ }
183
+ async invoke(id, args) {
184
+ const plain = this.tools.get(id);
185
+ if (plain?.handler) return await plain.handler(args);
186
+ const rpcDef = this._findRpcDefinition(id);
187
+ if (rpcDef) {
188
+ const positional = coerceAgentPositionalArgs(args, rpcDef.args, "wrap");
189
+ return await this.context.rpc.invokeLocal(id, ...positional);
190
+ }
191
+ const provided = this._collectProviderTools().find((t) => t.tool.id === id);
192
+ if (provided) return await provided.input.handler(args);
193
+ throw new Error(`[devframe/agent] tool "${id}" not found`);
194
+ }
195
+ async read(id) {
196
+ const entry = this.resources.get(id);
197
+ if (!entry) throw new Error(`[devframe/agent] resource "${id}" not found`);
198
+ return await entry.read();
199
+ }
200
+ /** @internal */
201
+ _dispose() {
202
+ this._rpcUnsubscribe?.();
203
+ this._rpcUnsubscribe = void 0;
204
+ }
205
+ _validateToolId(id) {
206
+ if (this.tools.has(id)) throw diagnostics.DF0015({ id });
207
+ if (this.context.rpc.definitions.get(id)?.agent) throw diagnostics.DF0015({ id });
208
+ }
209
+ _projectTool(input) {
210
+ if (!input.description || typeof input.description !== "string") throw diagnostics.DF0014({ name: input.id });
211
+ return {
212
+ id: input.id,
213
+ kind: "tool",
214
+ title: input.title ?? input.id,
215
+ description: input.description,
216
+ safety: input.safety ?? "action",
217
+ tags: input.tags,
218
+ args: input.args,
219
+ inputSchema: input.inputSchema,
220
+ outputSchema: input.outputSchema,
221
+ examples: input.examples
222
+ };
223
+ }
224
+ /** Query every registered provider, projecting inputs to serializable tools. */
225
+ _collectProviderTools() {
226
+ const out = [];
227
+ for (const provider of this.providers) for (const input of provider()) out.push({
228
+ input,
229
+ tool: this._projectTool(input)
230
+ });
231
+ return out;
232
+ }
233
+ _collectRpcTools() {
234
+ const out = [];
235
+ for (const [name, def] of this.context.rpc.definitions) {
236
+ const agent = def.agent;
237
+ if (!agent) continue;
238
+ if (!agent.description || typeof agent.description !== "string") throw diagnostics.DF0014({ name });
239
+ const type = def.type ?? "query";
240
+ const safety = agent.safety ?? inferSafety(type);
241
+ out.push({
242
+ id: name,
243
+ kind: "rpc",
244
+ title: agent.title ?? name,
245
+ description: agent.description,
246
+ safety,
247
+ tags: agent.tags,
248
+ rpcName: name,
249
+ examples: agent.examples
250
+ });
251
+ }
252
+ return out;
253
+ }
254
+ _findRpcDefinition(id) {
255
+ const def = this.context.rpc.definitions.get(id);
256
+ if (def?.agent) return def;
257
+ }
258
+ };
259
+ function inferSafety(type) {
260
+ if (type === "static" || type === "query") return "read";
261
+ return "action";
262
+ }
263
+ //#endregion
264
+ export { coerceAgentPositionalArgs as n, createEventEmitter as r, DevframeAgentHost as t };
@@ -0,0 +1,26 @@
1
+ import { join } from "pathe";
2
+ import process from "node:process";
3
+ import { homedir } from "node:os";
4
+ //#region src/node/host-h3.ts
5
+ /**
6
+ * h3-backed {@link DevframeHost} — used by the standalone CLI adapter.
7
+ */
8
+ function createH3DevframeHost(options) {
9
+ const workspaceRoot = options.workspaceRoot ?? process.cwd();
10
+ return {
11
+ mountStatic(base, source) {
12
+ return options.mount?.(base, source);
13
+ },
14
+ resolveOrigin() {
15
+ return typeof options.origin === "function" ? options.origin() : options.origin;
16
+ },
17
+ getStorageDir(scope) {
18
+ const namespace = `.${options.appName}/devframe`;
19
+ if (scope === "workspace") return join(workspaceRoot, ".devframe");
20
+ if (scope === "project") return join(workspaceRoot, "node_modules", namespace);
21
+ return join(homedir(), namespace);
22
+ }
23
+ };
24
+ }
25
+ //#endregion
26
+ export { createH3DevframeHost as t };
@@ -1,12 +1,14 @@
1
- import { isAllowedOrigin } from "./rpc/transports/ws-server.mjs";
2
- import { n as createHostContext } from "./host-h3-D2hYZvDK.mjs";
3
- import { t as diagnostics } from "./diagnostics-D7wM2cwM.mjs";
4
- import { t as toAgentToolName } from "./agent-tool-name-C3b5vEwJ.mjs";
1
+ import { n as __exportAll } from "./rolldown-runtime-JspESFgx.mjs";
2
+ import { t as Diagnostic } from "./nostics-CzECRXpE.mjs";
3
+ import { i as isAllowedOrigin } from "./ws-server-BdSLrhxE.mjs";
4
+ import { t as diagnostics } from "./diagnostics-DOQPnQmX.mjs";
5
+ import { t as createHostContext } from "./context-CEe4PBz1.mjs";
6
+ import { t as toAgentToolName } from "./agent-tool-name-EgfoFO8C.mjs";
5
7
  import { randomUUID } from "node:crypto";
6
- import { Diagnostic } from "nostics";
7
8
  import { join } from "pathe";
8
9
  import process from "node:process";
9
10
  import { homedir } from "node:os";
11
+ import { defineHandler } from "h3";
10
12
  import { Server, WebStandardStreamableHTTPServerTransport, isInitializeRequest } from "@modelcontextprotocol/server";
11
13
  //#region src/adapters/mcp/stringify.ts
12
14
  /**
@@ -188,9 +190,6 @@ function buildMcpServerFromContext(ctx, options) {
188
190
  /**
189
191
  * Build an MCP server over the agent surface of a devframe definition.
190
192
  * Currently supports `stdio` transport only.
191
- *
192
- * @experimental The agent-native surface is experimental and may change
193
- * without a major version bump until it stabilizes.
194
193
  */
195
194
  async function createMcpServer(definition, options = {}) {
196
195
  const transport = options.transport ?? "stdio";
@@ -461,8 +460,6 @@ function parseResourceUri(uri) {
461
460
  * loopback-default DNS-rebinding protection that — unlike the WS upgrade's
462
461
  * `isAllowedOrigin` — also rejects `Origin`-less requests, so a route-based
463
462
  * endpoint isn't reachable by an arbitrary local process.
464
- *
465
- * @experimental
466
463
  */
467
464
  function createMcpFetchHandler(ctx, options) {
468
465
  const sessions = /* @__PURE__ */ new Map();
@@ -531,4 +528,38 @@ function createMcpFetchHandler(ctx, options) {
531
528
  };
532
529
  }
533
530
  //#endregion
534
- export { createMcpServer as n, createMcpFetchHandler as t };
531
+ //#region src/adapters/mcp/http.ts
532
+ var http_exports = /* @__PURE__ */ __exportAll({ mountMcpHttp: () => mountMcpHttp });
533
+ /**
534
+ * Mount an MCP Streamable-HTTP endpoint on an h3 app at `path` — the h3
535
+ * binding over {@link createMcpFetchHandler}, which owns the sessions, the
536
+ * origin gate, and the transport plumbing.
537
+ *
538
+ * The handler is web-standard — it takes the h3 event's web `Request` and
539
+ * returns a web `Response` (an SSE `ReadableStream` body for the
540
+ * server→client stream). We copy that response onto `event.res` and return
541
+ * its body rather than returning the `Response` object directly, so a
542
+ * legitimate MCP 404 (unknown session) isn't swallowed by h3's
543
+ * "Response-with-404 falls through to the next handler" rule (which would
544
+ * otherwise hand the request to the SPA static catch-all).
545
+ */
546
+ function mountMcpHttp(app, ctx, path, options) {
547
+ const handler = createMcpFetchHandler(ctx, options);
548
+ app.use(path, defineHandler(async (event) => respond(event, await handler.fetch(event.req))));
549
+ return { dispose: handler.dispose };
550
+ }
551
+ /**
552
+ * Copy a web `Response` from the MCP transport onto the h3 event's response
553
+ * and return its body. Returning the body (a `ReadableStream` or `null`)
554
+ * rather than the `Response` object avoids h3's 404-fall-through behavior.
555
+ */
556
+ function respond(event, response) {
557
+ event.res.status = response.status;
558
+ event.res.statusText = response.statusText;
559
+ response.headers.forEach((value, key) => {
560
+ event.res.headers.set(key, value);
561
+ });
562
+ return response.body ?? "";
563
+ }
564
+ //#endregion
565
+ export { createMcpServer as i, mountMcpHttp as n, createMcpFetchHandler as r, http_exports as t };
@@ -1,5 +1,5 @@
1
- import { W as DevframeNodeRpcSession, b as DevframeNodeContext, ht as SharedState } from "./devframe-mQ4qZAuo.mjs";
2
- import { n as InternalAnonymousAuthStorage } from "./context-DoSozfvx.mjs";
1
+ import { R as DevframeNodeRpcSession, g as DevframeNodeContext, lt as SharedState } from "./devframe-DJMg0WLe.mjs";
2
+ import { n as InternalAnonymousAuthStorage } from "./context-B3YUu_95.mjs";
3
3
  //#region src/node/auth/revoke.d.ts
4
4
  /**
5
5
  * Flip `isTrusted` to false on any live WS clients connected with `token`