devframe 0.8.2 → 0.9.0-beta.2
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-CttmCMBi.mjs +70 -0
- package/dist/_shared-DmpWW-HH.d.mts +43 -0
- package/dist/adapters/build.d.mts +1 -1
- package/dist/adapters/build.mjs +6 -5
- package/dist/adapters/cac.d.mts +1 -1
- package/dist/adapters/cac.mjs +157 -1
- package/dist/adapters/dev.d.mts +20 -42
- package/dist/adapters/dev.mjs +2 -1
- package/dist/adapters/embedded.d.mts +1 -1
- package/dist/adapters/initiate.d.mts +223 -0
- package/dist/adapters/initiate.mjs +2 -0
- package/dist/adapters/mcp.d.mts +26 -2
- package/dist/adapters/mcp.mjs +2 -2
- package/dist/cli/main.mjs +2 -2
- package/dist/client/index.d.mts +70 -6
- package/dist/client/index.mjs +743 -408
- package/dist/constants.d.mts +19 -6
- package/dist/constants.mjs +19 -6
- package/dist/{context-CBg8iRbQ.mjs → context-C33rR7n0.mjs} +1 -1
- package/dist/{context-DoSozfvx.d.mts → context-CSCwoABx.d.mts} +1 -1
- package/dist/{host-h3-D2hYZvDK.mjs → context-Du11TLcO.mjs} +5 -255
- package/dist/dev-BEveeA5H.mjs +233 -0
- package/dist/{devframe-mQ4qZAuo.d.mts → devframe-Bdd9YXTy.d.mts} +88 -25
- package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DLYH5oD3.mjs} +16 -0
- package/dist/dist-D8jAMwxU.mjs +227 -0
- package/dist/helpers/vite.d.mts +43 -39
- package/dist/helpers/vite.mjs +26 -38
- package/dist/host-agent-CpbAoqBx.mjs +230 -0
- package/dist/host-h3-CzCZwtuV.mjs +26 -0
- package/dist/{fetch-BKaD0Vb3.mjs → http-DLr_cJZ7.mjs} +42 -4
- package/dist/{index-b64Uuhy2.d.mts → index-CZRd2K1C.d.mts} +2 -2
- package/dist/{index-Dbw1p5ch.d.mts → index-DSCq4M4q.d.mts} +2 -31
- package/dist/index.d.mts +9 -4
- package/dist/index.mjs +8 -1
- package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-BQqGoDO9.mjs} +9 -2
- package/dist/instance-shell-DBTXTSjN.d.mts +264 -0
- package/dist/instance-shell-KRdee1QF.mjs +485 -0
- package/dist/internal/index.d.mts +150 -0
- package/dist/internal/index.mjs +7 -0
- package/dist/node/auth.d.mts +2 -2
- package/dist/node/hub-internals.d.mts +2 -14
- package/dist/node/hub-internals.mjs +2 -2
- package/dist/node/index.d.mts +3 -267
- package/dist/node/index.mjs +3 -5
- package/dist/recipes/common-rpc-functions.d.mts +3 -3
- package/dist/recipes/interactive-auth.d.mts +3 -3
- package/dist/recipes/interactive-auth.mjs +6 -6
- 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 +2 -2
- package/dist/rpc/index.mjs +3 -17
- 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-CGctXToO.d.mts +48 -0
- package/dist/rpc-core-CqcJkAu9.mjs +81 -0
- package/dist/{serialization-C8Mnw9hK.mjs → serialization-BHY8M5NQ.mjs} +1 -1
- package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
- package/dist/sse-client-BsIK3hMD.d.mts +37 -0
- package/dist/{dump-CgZShDRB.mjs → static-CvjkO8QE.mjs} +2 -265
- package/dist/{storage-CTNVbuFB.mjs → storage-BcmVEJRc.mjs} +2 -2
- package/dist/types/index.d.mts +3 -3
- package/dist/types/index.mjs +1 -6
- package/dist/utils/events.d.mts +1 -1
- package/dist/utils/launch-editor.mjs +1 -25
- package/dist/utils/open.mjs +1 -1
- package/dist/utils/serve-static.mjs +1 -1
- 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-BiuDXTKW.mjs +266 -0
- package/dist/wire-codec-9VhIKnyp.mjs +58 -0
- package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-CdNuATYa.d.mts} +6 -1
- package/dist/ws-server-Bk1hOG3X.mjs +267 -0
- package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-Ces08fjP.d.mts} +89 -8
- package/package.json +10 -9
- package/skills/devframe/SKILL.md +84 -85
- 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/http-C4T68YV6.mjs +0 -38
- package/dist/index-vAX6bYuc.d.mts +0 -78
- 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/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/dist/{open-Deb5xmIT.mjs → open-Dz4V4irZ.mjs} +1 -1
package/dist/constants.d.mts
CHANGED
|
@@ -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 = "
|
|
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
|
-
*
|
|
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,
|
|
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 };
|
package/dist/constants.mjs
CHANGED
|
@@ -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 = "
|
|
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
|
-
*
|
|
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,
|
|
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-
|
|
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 {
|
|
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-
|
|
6
|
-
import { i as createEventEmitter, n as createSharedState, r as nanoid, t as createStorage } from "./storage-
|
|
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 `
|
|
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
|
-
|
|
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 };
|