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.
- package/dist/_shared-BPmX9fqK.d.mts +41 -0
- package/dist/_shared-CbZ_yhbz.mjs +68 -0
- package/dist/adapters/build.d.mts +10 -12
- package/dist/adapters/build.mjs +26 -29
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +156 -1
- package/dist/adapters/dev.d.mts +38 -46
- package/dist/adapters/dev.mjs +2 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/initiate.d.mts +224 -0
- package/dist/adapters/initiate.mjs +2 -0
- package/dist/adapters/mcp.d.mts +24 -7
- package/dist/adapters/mcp.mjs +2 -2
- package/dist/{agent-tool-name-C3b5vEwJ.mjs → agent-tool-name-EgfoFO8C.mjs} +0 -3
- package/dist/cli/main.d.mts +0 -2
- package/dist/cli/main.mjs +4 -8
- package/dist/client/index.d.mts +71 -7
- package/dist/client/index.mjs +720 -1540
- package/dist/constants.d.mts +28 -7
- package/dist/constants.mjs +28 -7
- package/dist/{context-DoSozfvx.d.mts → context-B3YUu_95.d.mts} +19 -1
- package/dist/{host-h3-D2hYZvDK.mjs → context-CEe4PBz1.mjs} +25 -557
- package/dist/{context-CBg8iRbQ.mjs → context-xdynGSLP.mjs} +10 -1
- package/dist/dev--rAPzbvO.mjs +239 -0
- package/dist/{devframe-mQ4qZAuo.d.mts → devframe-DJMg0WLe.d.mts} +181 -134
- package/dist/{diagnostics-hwjXp_UV.mjs → diagnostics-CD8nlgll.mjs} +1 -3
- package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DOQPnQmX.mjs} +51 -5
- package/dist/dist-eNI9qM_I.mjs +228 -0
- package/dist/host-agent-xLCSe65D.mjs +264 -0
- package/dist/host-h3-Crkzm42q.mjs +26 -0
- package/dist/{fetch-BKaD0Vb3.mjs → http-DYpK0cUi.mjs} +42 -11
- package/dist/{index-b64Uuhy2.d.mts → index-CB-fSUyn.d.mts} +2 -2
- package/dist/{index-Dbw1p5ch.d.mts → index-tdrk1bzV.d.mts} +3 -32
- package/dist/index.d.mts +12 -5
- package/dist/index.mjs +8 -1
- package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-WvQkt42E.mjs} +9 -8
- package/dist/instance-shell-CMFyszWJ.mjs +486 -0
- package/dist/instance-shell-CljiKq1c.d.mts +255 -0
- package/dist/internal/index.d.mts +397 -0
- package/dist/internal/index.mjs +9 -0
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/auth.mjs +1 -1
- package/dist/node/hub-internals.d.mts +2 -14
- package/dist/node/hub-internals.mjs +2 -2
- package/dist/node/index.d.mts +4 -268
- package/dist/node/index.mjs +3 -5
- package/dist/nostics-CzECRXpE.mjs +59 -0
- package/dist/nostics-SPGDgHEP.d.mts +33 -0
- package/dist/recipes/common-rpc-functions.d.mts +4 -4
- package/dist/recipes/interactive-auth.d.mts +3 -3
- package/dist/recipes/interactive-auth.mjs +8 -8
- package/dist/remote-assets-Bg4gCUZ_.d.mts +123 -0
- package/dist/remote-assets-nTTxY2MU.mjs +636 -0
- package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
- package/dist/rpc/dump.d.mts +77 -1
- package/dist/rpc/dump.mjs +1 -1
- package/dist/rpc/index.d.mts +3 -3
- package/dist/rpc/index.mjs +4 -18
- package/dist/rpc/transports/sse-client.d.mts +2 -0
- package/dist/rpc/transports/sse-client.mjs +142 -0
- package/dist/rpc/transports/sse-server.d.mts +61 -0
- package/dist/rpc/transports/sse-server.mjs +209 -0
- package/dist/rpc/transports/ws-bun.d.mts +35 -0
- package/dist/rpc/transports/ws-bun.mjs +31 -0
- package/dist/rpc/transports/ws-client.d.mts +1 -1
- package/dist/rpc/transports/ws-client.mjs +10 -18
- package/dist/rpc/transports/ws-server.d.mts +2 -2
- package/dist/rpc/transports/ws-server.mjs +2 -235
- package/dist/rpc-core-Cc4lucI0.d.mts +48 -0
- package/dist/rpc-core-DMTe-hLo.mjs +81 -0
- package/dist/{serialization-C8Mnw9hK.mjs → serialization-BGzEwAdr.mjs} +2 -2
- package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
- package/dist/sse-client-CFBOIaUi.d.mts +37 -0
- package/dist/state-Cc1IeSmQ.mjs +1291 -0
- package/dist/{dump-CgZShDRB.mjs → static-BPZ1_OPA.mjs} +3 -266
- package/dist/storage-CYye7P7L.mjs +137 -0
- package/dist/types/index.d.mts +6 -4
- package/dist/types/index.mjs +1 -6
- package/dist/{types-CnJSgRVa.d.mts → types-Djq7CBXh.d.mts} +0 -7
- package/dist/utils/agent-tool-name.d.mts +0 -3
- package/dist/utils/agent-tool-name.mjs +0 -3
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/get-port.d.mts +17 -0
- package/dist/utils/get-port.mjs +2 -0
- package/dist/utils/launch-editor.mjs +1 -25
- package/dist/utils/nostics.d.mts +2 -0
- package/dist/utils/nostics.mjs +2 -0
- package/dist/utils/open.d.mts +2 -10
- package/dist/utils/open.mjs +56 -1
- package/dist/utils/remote-assets.d.mts +15 -0
- package/dist/utils/remote-assets.mjs +2 -0
- package/dist/utils/serve-static.d.mts +13 -5
- package/dist/utils/serve-static.mjs +119 -9
- package/dist/utils/shared-state.d.mts +1 -1
- package/dist/utils/shared-state.mjs +1 -1
- package/dist/utils/simple-schema.d.mts +101 -1
- package/dist/utils/streaming-channel.d.mts +1 -1
- package/dist/validation-CpXFB6Dz.mjs +266 -0
- package/dist/wire-codec-0K-o5MYW.mjs +56 -0
- package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-DhjNyQo3.d.mts} +7 -2
- package/dist/ws-server-BdSLrhxE.mjs +267 -0
- package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-D1d3QM9f.d.mts} +90 -9
- package/package.json +13 -12
- package/skills/devframe/SKILL.md +91 -93
- package/dist/_shared-bWRzeSa0.mjs +0 -19
- package/dist/adapters/cli.d.mts +0 -11
- package/dist/adapters/cli.mjs +0 -6
- package/dist/cac-D4_9ok0z.mjs +0 -157
- package/dist/dev-Bc49u3qg.mjs +0 -439
- package/dist/diagnostics-reporter-CsIG85Q5.mjs +0 -38
- package/dist/helpers/vite.d.mts +0 -100
- package/dist/helpers/vite.mjs +0 -105
- package/dist/http-C4T68YV6.mjs +0 -38
- package/dist/index-vAX6bYuc.d.mts +0 -78
- package/dist/open-Deb5xmIT.mjs +0 -547
- package/dist/recipes/open-helpers.d.mts +0 -6
- package/dist/recipes/open-helpers.mjs +0 -6
- package/dist/server-CD8XmBXl.mjs +0 -167
- package/dist/server-UHWae43Q.d.mts +0 -149
- package/dist/simple-schema-BDzLeJDk.d.mts +0 -102
- package/dist/state-BeUHDDjk.mjs +0 -108
- package/dist/storage-CTNVbuFB.mjs +0 -1122
- package/dist/utils/promise.d.mts +0 -8
- package/dist/utils/promise.mjs +0 -15
- package/dist/utils/scope.d.mts +0 -11
- package/dist/utils/scope.mjs +0 -15
- package/skills/devframe/templates/spa-devframe.ts +0 -38
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import {
|
|
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 `
|
|
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 `
|
|
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 {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
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
|
-
|
|
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 {
|
|
2
|
-
import { n as InternalAnonymousAuthStorage } from "./context-
|
|
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`
|