devframe 0.7.16 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/bin/devframe.mjs +8 -0
  2. package/dist/adapters/build.d.mts +1 -1
  3. package/dist/adapters/build.mjs +4 -4
  4. package/dist/adapters/cac.d.mts +1 -1
  5. package/dist/adapters/cac.mjs +1 -1
  6. package/dist/adapters/cli.d.mts +1 -1
  7. package/dist/adapters/cli.mjs +1 -1
  8. package/dist/adapters/dev.d.mts +2 -2
  9. package/dist/adapters/dev.mjs +1 -1
  10. package/dist/adapters/embedded.d.mts +1 -1
  11. package/dist/adapters/mcp.d.mts +51 -3
  12. package/dist/adapters/mcp.mjs +2 -2
  13. package/dist/agent-tool-name-C3b5vEwJ.mjs +34 -0
  14. package/dist/{cac-CPaOy3LD.mjs → cac-B1LM3zit.mjs} +18 -13
  15. package/dist/cli/main.d.mts +11 -0
  16. package/dist/cli/main.mjs +284 -0
  17. package/dist/client/index.d.mts +40 -11
  18. package/dist/client/index.mjs +129 -19
  19. package/dist/constants.d.mts +13 -6
  20. package/dist/constants.mjs +13 -6
  21. package/dist/{context-DY72brRI.mjs → context-CQefP2jR.mjs} +3 -2
  22. package/dist/{context-CzqPrJSz.d.mts → context-DFzmxCLa.d.mts} +1 -1
  23. package/dist/{revoke-BtQDKTp7.mjs → crypto-token-XCqTSMg9.mjs} +1 -35
  24. package/dist/{dev-BCeaQuGE.mjs → dev-BSHFZGZr.mjs} +22 -11
  25. package/dist/{devframe-Ckhf3kFw.d.mts → devframe-qgCKL683.d.mts} +77 -15
  26. package/dist/diagnostics-B5-qHeqD.mjs +94 -0
  27. package/dist/{diagnostics-BXwBQmoN.mjs → diagnostics-hwjXp_UV.mjs} +8 -0
  28. package/dist/{dump-Cz7yVvsB.mjs → dump-CgZShDRB.mjs} +82 -6
  29. package/dist/fetch-CK0S253E.mjs +534 -0
  30. package/dist/helpers/vite.d.mts +15 -14
  31. package/dist/helpers/vite.mjs +8 -8
  32. package/dist/{host-h3-CgOetlNE.mjs → host-h3-Kz7t5Xab.mjs} +83 -28
  33. package/dist/http-Dvgxujcu.mjs +38 -0
  34. package/dist/{index-8dndvIxR.d.mts → index-B7RgLw--.d.mts} +11 -6
  35. package/dist/{index-DgsLFhZg.d.mts → index-Dbw1p5ch.d.mts} +27 -3
  36. package/dist/{index-YDSJgsyG.d.mts → index-vAX6bYuc.d.mts} +1 -1
  37. package/dist/index.d.mts +5 -5
  38. package/dist/instance-registry-D6fxYu36.mjs +190 -0
  39. package/dist/node/auth.d.mts +2 -2
  40. package/dist/node/auth.mjs +2 -2
  41. package/dist/node/hub-internals.d.mts +2 -2
  42. package/dist/node/hub-internals.mjs +1 -1
  43. package/dist/node/index.d.mts +124 -6
  44. package/dist/node/index.mjs +5 -4
  45. package/dist/recipes/common-rpc-functions.d.mts +33 -33
  46. package/dist/recipes/common-rpc-functions.mjs +6 -6
  47. package/dist/recipes/interactive-auth.d.mts +2 -2
  48. package/dist/recipes/interactive-auth.mjs +23 -15
  49. package/dist/revoke-DZbJ7Cl0.mjs +35 -0
  50. package/dist/rpc/dump.d.mts +1 -1
  51. package/dist/rpc/dump.mjs +1 -1
  52. package/dist/rpc/index.d.mts +3 -3
  53. package/dist/rpc/index.mjs +4 -4
  54. package/dist/rpc/transports/ws-client.d.mts +1 -1
  55. package/dist/rpc/transports/ws-client.mjs +1 -1
  56. package/dist/rpc/transports/ws-server.d.mts +2 -2
  57. package/dist/rpc/transports/ws-server.mjs +50 -3
  58. package/dist/{serialization-DpLXCy13.mjs → serialization-C8Mnw9hK.mjs} +1 -1
  59. package/dist/{server-DykHCza_.mjs → server-CXaDDUl0.mjs} +2 -2
  60. package/dist/{server-B0wiP_Oi.d.mts → server-E9z4ZWbd.d.mts} +4 -3
  61. package/dist/simple-schema-BDzLeJDk.d.mts +102 -0
  62. package/dist/simple-schema-DQPZrAaZ.mjs +154 -0
  63. package/dist/{state-WX3HT5a3.mjs → state-BeUHDDjk.mjs} +13 -6
  64. package/dist/{storage-gEnj9ASQ.mjs → storage-Dzoc3NVC.mjs} +2 -66
  65. package/dist/{transports-BmDVn4SF.mjs → transports-vhizgqXM.mjs} +1 -1
  66. package/dist/types/index.d.mts +4 -4
  67. package/dist/{types-CrzNxXKq.d.mts → types-CnJSgRVa.d.mts} +33 -19
  68. package/dist/utils/agent-tool-name.d.mts +27 -0
  69. package/dist/utils/agent-tool-name.mjs +34 -0
  70. package/dist/utils/events.d.mts +1 -1
  71. package/dist/utils/shared-state.d.mts +1 -1
  72. package/dist/utils/simple-schema.d.mts +2 -0
  73. package/dist/utils/simple-schema.mjs +154 -0
  74. package/dist/utils/streaming-channel.d.mts +1 -1
  75. package/dist/{ws-client-DSXiI2h7.d.mts → ws-client-B6Tnr0ic.d.mts} +1 -1
  76. package/dist/{ws-server-9-wn7MNQ.d.mts → ws-server-J-zkiOyp.d.mts} +24 -3
  77. package/package.json +18 -7
  78. package/skills/devframe/SKILL.md +4 -3
  79. package/dist/build-server-C7vTKjiQ.mjs +0 -359
  80. package/dist/http-_pPbryTQ.mjs +0 -114
@@ -20,11 +20,14 @@ const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
20
20
  const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
21
21
  const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
22
22
  /**
23
- * Page-URL query parameter carrying a one-time authentication code (OTP) for
24
- * "magic link" auth. A host can print a link like `<origin>/?devframe_otp=<code>`;
25
- * the client reads the code, exchanges it for a token, and strips the parameter
26
- * from the URL. See `buildOtpAuthUrl` (node) and the `authenticateWithUrlOtp` /
27
- * `consumeOtpFromUrl` client utilities (or `connectDevframe`'s `otpParam`).
23
+ * Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
24
+ * for "magic link" auth. A host can print a link like
25
+ * `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
26
+ * token, and strips the parameter from the URL. The code rides the fragment
27
+ * (never the query string) so the browser never transmits it to the server,
28
+ * keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
29
+ * (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
30
+ * (or `connectDevframe`'s `otpParam`).
28
31
  */
29
32
  const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
30
33
  /**
@@ -35,6 +38,10 @@ const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
35
38
  * trusted before its own `anonymous:devframe:auth` handshake call arrives.
36
39
  */
37
40
  const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
41
+ /** External viewer origin requested during connection bootstrap. */
42
+ const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
43
+ /** Token that authorizes an external viewer origin registration. */
44
+ const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
38
45
  //#endregion
39
46
  //#region src/utils/diagnostics-reporter.ts
40
47
  const formatAnsi = ansiFormatter(colors);
@@ -68,6 +75,14 @@ const diagnostics = defineDiagnostics({
68
75
  DF0028: {
69
76
  why: (p) => `Function "${p.name}" with type "${p.type}" cannot use \`snapshot: true\`. Only "query" functions support this sugar; "static" functions have equivalent default behavior already.`,
70
77
  fix: "Remove `snapshot: true`, or change the function type to `query`."
78
+ },
79
+ DF0043: {
80
+ why: (p) => `RPC function "${p.name}" received an invalid argument at position ${p.index}: ${p.issues}`,
81
+ fix: "Pass a value that satisfies the `args` schema declared for this function."
82
+ },
83
+ DF0044: {
84
+ why: (p) => `RPC function "${p.name}" returned a value that failed its \`returns\` schema: ${p.issues}`,
85
+ fix: "Make the handler return a value that satisfies the `returns` schema, or relax the schema."
71
86
  }
72
87
  }
73
88
  });
@@ -92,6 +107,70 @@ function isPlainErrorShape(value) {
92
107
  return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.name === "string";
93
108
  }
94
109
  //#endregion
110
+ //#region src/rpc/validate-io.ts
111
+ /**
112
+ * Run a single [Standard Schema](https://standardschema.dev) validator,
113
+ * awaiting the result when the validator is asynchronous.
114
+ */
115
+ async function runStandardSchema(schema, value) {
116
+ const result = schema["~standard"].validate(value);
117
+ return result instanceof Promise ? await result : result;
118
+ }
119
+ /**
120
+ * Render Standard Schema issues into a single human-readable line for a
121
+ * diagnostic message, prefixing each with its dotted path when present.
122
+ */
123
+ function formatIssues(issues) {
124
+ return issues.map((issue) => {
125
+ const path = issue.path?.map((segment) => typeof segment === "object" ? segment.key : segment).join(".");
126
+ return path ? `${path}: ${issue.message}` : issue.message;
127
+ }).join("; ");
128
+ }
129
+ /**
130
+ * Validate positional arguments against their declared schemas. Only
131
+ * indices with a schema are checked; extra arguments pass through
132
+ * untouched. Throws `DF0038` on the first failing argument.
133
+ *
134
+ * Validation guards the payload without rewriting it: the original values
135
+ * are handed to the handler unchanged, so a schema that describes a subset
136
+ * of an object never silently strips the sender's extra fields (and any
137
+ * declared transforms stay a purely type-level concern).
138
+ *
139
+ * @internal
140
+ */
141
+ async function validateRpcArgs(name, argsSchema, args) {
142
+ const original = args.slice();
143
+ if (!argsSchema || argsSchema.length === 0) return original;
144
+ for (let index = 0; index < argsSchema.length; index++) {
145
+ const schema = argsSchema[index];
146
+ if (!schema) continue;
147
+ const result = await runStandardSchema(schema, args[index]);
148
+ if (result.issues) throw diagnostics.DF0043({
149
+ name,
150
+ index,
151
+ issues: formatIssues(result.issues)
152
+ });
153
+ }
154
+ return original;
155
+ }
156
+ /**
157
+ * Validate a handler's resolved return value against its declared schema.
158
+ * Throws `DF0039` when the value fails the schema, otherwise returns the
159
+ * original value unchanged (guard-only, never rewriting the payload — see
160
+ * {@link validateRpcArgs}). Passes through when no return schema is set.
161
+ *
162
+ * @internal
163
+ */
164
+ async function validateRpcReturn(name, returnSchema, value) {
165
+ if (!returnSchema) return value;
166
+ const result = await runStandardSchema(returnSchema, value);
167
+ if (result.issues) throw diagnostics.DF0044({
168
+ name,
169
+ issues: formatIssues(result.issues)
170
+ });
171
+ return value;
172
+ }
173
+ //#endregion
95
174
  //#region src/rpc/handler.ts
96
175
  async function getRpcResolvedSetupResult(definition, context) {
97
176
  if (!definition.setup) return {};
@@ -118,10 +197,22 @@ async function getRpcResolvedSetupResult(definition, context) {
118
197
  return await definition.__promise;
119
198
  }
120
199
  async function getRpcHandler(definition, context) {
121
- if (definition.handler) return definition.handler;
122
- const result = await getRpcResolvedSetupResult(definition, context);
123
- if (!result.handler) throw diagnostics.DF0024({ name: definition.name });
124
- return result.handler;
200
+ let handler = definition.handler;
201
+ if (!handler) {
202
+ const result = await getRpcResolvedSetupResult(definition, context);
203
+ if (!result.handler) throw diagnostics.DF0024({ name: definition.name });
204
+ handler = result.handler;
205
+ }
206
+ const argsSchema = definition.args;
207
+ const returnSchema = definition.returns;
208
+ if (!argsSchema && !returnSchema) return handler;
209
+ const inner = handler;
210
+ const validating = async (...args) => {
211
+ const validatedArgs = await validateRpcArgs(definition.name, argsSchema, args);
212
+ const output = await inner(...validatedArgs);
213
+ return await validateRpcReturn(definition.name, returnSchema, output);
214
+ };
215
+ return validating;
125
216
  }
126
217
  //#endregion
127
218
  //#region src/rpc/cache.ts
@@ -344,6 +435,21 @@ function storeAuthToken(token) {
344
435
  }
345
436
  //#endregion
346
437
  //#region src/client/connection.ts
438
+ /**
439
+ * Allow an external viewer to connect by registering its browser origin with
440
+ * the Devframe host. Returns `false` if the host did not provide an origin
441
+ * registration token.
442
+ */
443
+ async function registerDevframeViewerOrigin(connection, origin = globalThis.location?.origin) {
444
+ const token = connection.connectionMeta.viewerOriginToken;
445
+ if (!token || !origin) return false;
446
+ const url = new URL(connection.metaBaseUrl);
447
+ url.searchParams.set(DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, origin);
448
+ url.searchParams.set(DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, token);
449
+ const response = await fetch(url, { cache: "no-store" });
450
+ if (!response.ok) throw new Error(`Failed to register external viewer origin (${response.status}).`);
451
+ return true;
452
+ }
347
453
  function resolveMetaBaseUrl(baseURL) {
348
454
  const metaPath = withBase(DEVFRAME_CONNECTION_META_FILENAME, baseURL);
349
455
  try {
@@ -446,12 +552,14 @@ function isCallableStatus(status) {
446
552
  //#endregion
447
553
  //#region src/client/otp.ts
448
554
  /**
449
- * Read a one-time authentication code (OTP) from the current page URL's query
450
- * string, without side effects. Returns `undefined` when the parameter is absent.
555
+ * Read a one-time authentication code (OTP) from the current page URL's
556
+ * fragment, without side effects. Returns `undefined` when the parameter is
557
+ * absent.
451
558
  */
452
559
  function readOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
453
560
  try {
454
- return new URLSearchParams(globalThis.location?.search).get(param) || void 0;
561
+ const hash = globalThis.location?.hash?.replace(/^#/, "") ?? "";
562
+ return new URLSearchParams(hash).get(param) || void 0;
455
563
  } catch {
456
564
  return;
457
565
  }
@@ -459,16 +567,18 @@ function readOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
459
567
  function stripParamFromUrl(param) {
460
568
  try {
461
569
  const url = new URL(globalThis.location.href);
462
- if (!url.searchParams.has(param)) return;
463
- url.searchParams.delete(param);
570
+ const fragment = new URLSearchParams(url.hash.replace(/^#/, ""));
571
+ if (!fragment.has(param)) return;
572
+ fragment.delete(param);
573
+ url.hash = fragment.toString();
464
574
  globalThis.history?.replaceState(globalThis.history.state, "", url.href);
465
575
  } catch {}
466
576
  }
467
577
  /**
468
- * Read the one-time code from the page URL and remove it from the address bar
469
- * (and the current history entry), so the single-use code isn't left in the
470
- * URL, browser history, or a `Referer`. Returns the code, or `undefined` when
471
- * absent.
578
+ * Read the one-time code from the page URL fragment and remove it from the
579
+ * address bar (and the current history entry), so the single-use code isn't
580
+ * left in the URL, browser history, or a `Referer`. Returns the code, or
581
+ * `undefined` when absent.
472
582
  */
473
583
  function consumeOtpFromUrl(param = DEVFRAME_OTP_URL_PARAM) {
474
584
  const code = readOtpFromUrl(param);
@@ -2504,4 +2614,4 @@ async function getDevframeRpcClient(options = {}) {
2504
2614
  //#region src/client/index.ts
2505
2615
  const connectDevframe = getDevframeRpcClient;
2506
2616
  //#endregion
2507
- export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, setupDevframeConnection };
2617
+ export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveWsUrl, setupDevframeConnection };
@@ -35,11 +35,14 @@ declare const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
35
35
  */
36
36
  declare const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
37
37
  /**
38
- * Page-URL query parameter carrying a one-time authentication code (OTP) for
39
- * "magic link" auth. A host can print a link like `<origin>/?devframe_otp=<code>`;
40
- * the client reads the code, exchanges it for a token, and strips the parameter
41
- * from the URL. See `buildOtpAuthUrl` (node) and the `authenticateWithUrlOtp` /
42
- * `consumeOtpFromUrl` client utilities (or `connectDevframe`'s `otpParam`).
38
+ * Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
39
+ * for "magic link" auth. A host can print a link like
40
+ * `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
41
+ * token, and strips the parameter from the URL. The code rides the fragment
42
+ * (never the query string) so the browser never transmits it to the server,
43
+ * keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
44
+ * (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
45
+ * (or `connectDevframe`'s `otpParam`).
43
46
  */
44
47
  declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
45
48
  /**
@@ -50,6 +53,10 @@ declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
50
53
  * trusted before its own `anonymous:devframe:auth` handshake call arrives.
51
54
  */
52
55
  declare const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
56
+ /** External viewer origin requested during connection bootstrap. */
57
+ declare const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
58
+ /** Token that authorizes an external viewer origin registration. */
59
+ declare const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
53
60
  /**
54
61
  * Prefix that marks an RPC method as callable before a connection is
55
62
  * trusted. This is the *only* rule the pre-trust gate applies — there is no
@@ -66,4 +73,4 @@ declare const ANONYMOUS_RPC_PREFIX = "anonymous:";
66
73
  */
67
74
  declare function isAnonymousRpcMethod(name: string): boolean;
68
75
  //#endregion
69
- export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
76
+ export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
@@ -35,11 +35,14 @@ const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
35
35
  */
36
36
  const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
37
37
  /**
38
- * Page-URL query parameter carrying a one-time authentication code (OTP) for
39
- * "magic link" auth. A host can print a link like `<origin>/?devframe_otp=<code>`;
40
- * the client reads the code, exchanges it for a token, and strips the parameter
41
- * from the URL. See `buildOtpAuthUrl` (node) and the `authenticateWithUrlOtp` /
42
- * `consumeOtpFromUrl` client utilities (or `connectDevframe`'s `otpParam`).
38
+ * Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
39
+ * for "magic link" auth. A host can print a link like
40
+ * `<origin>/#devframe_otp=<code>`; the client reads the code, exchanges it for a
41
+ * token, and strips the parameter from the URL. The code rides the fragment
42
+ * (never the query string) so the browser never transmits it to the server,
43
+ * keeping it out of access logs and `Referer` headers. See `buildOtpAuthUrl`
44
+ * (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
45
+ * (or `connectDevframe`'s `otpParam`).
43
46
  */
44
47
  const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
45
48
  /**
@@ -50,6 +53,10 @@ const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
50
53
  * trusted before its own `anonymous:devframe:auth` handshake call arrives.
51
54
  */
52
55
  const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
56
+ /** External viewer origin requested during connection bootstrap. */
57
+ const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
58
+ /** Token that authorizes an external viewer origin registration. */
59
+ const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
53
60
  /**
54
61
  * Prefix that marks an RPC method as callable before a connection is
55
62
  * trusted. This is the *only* rule the pre-trust gate applies — there is no
@@ -68,4 +75,4 @@ function isAnonymousRpcMethod(name) {
68
75
  return name.startsWith(ANONYMOUS_RPC_PREFIX);
69
76
  }
70
77
  //#endregion
71
- export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
78
+ export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DIRNAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_DOCK_IMPORTS_VIRTUAL_ID, DEVFRAME_MCP_ROUTE, DEVFRAME_MOUNT_PATH, DEVFRAME_MOUNT_PATH_NO_TRAILING_SLASH, DEVFRAME_OTP_URL_PARAM, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM, DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM, DEVFRAME_WS_ROUTE, REMOTE_CONNECTION_KEY, isAnonymousRpcMethod };
@@ -1,5 +1,6 @@
1
- import { t as createStorage } from "./storage-gEnj9ASQ.mjs";
2
- import { i as randomToken, n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-BtQDKTp7.mjs";
1
+ import { n as randomToken } from "./crypto-token-XCqTSMg9.mjs";
2
+ import { t as createStorage } from "./storage-Dzoc3NVC.mjs";
3
+ import { n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-DZbJ7Cl0.mjs";
3
4
  import { join } from "pathe";
4
5
  //#region src/node/hub-internals/context.ts
5
6
  const internalContextMap = /* @__PURE__ */ new WeakMap();
@@ -1,4 +1,4 @@
1
- import { b as DevframeNodeContext, ht as SharedState } from "./devframe-Ckhf3kFw.mjs";
1
+ import { b as DevframeNodeContext, ht as SharedState } from "./devframe-qgCKL683.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -42,38 +42,4 @@ function timingSafeEqual(a, b) {
42
42
  return mismatch === 0;
43
43
  }
44
44
  //#endregion
45
- //#region src/node/auth/revoke.ts
46
- /**
47
- * Flip `isTrusted` to false on any live WS clients connected with `token`
48
- * and broadcast the `auth:revoked` event so they can react.
49
- *
50
- * Shared between persisted-auth revocation and remote-dock token revocation.
51
- */
52
- async function revokeActiveConnectionsForToken(context, token) {
53
- const rpcHost = context.rpc;
54
- if (!rpcHost?._rpcGroup) return;
55
- const affectedSessionIds = /* @__PURE__ */ new Set();
56
- for (const client of rpcHost._rpcGroup.clients) if (client.$meta.clientAuthToken === token) {
57
- affectedSessionIds.add(client.$meta.id);
58
- client.$meta.isTrusted = false;
59
- client.$meta.clientAuthToken = void 0;
60
- }
61
- if (affectedSessionIds.size === 0) return;
62
- await rpcHost.broadcast({
63
- method: "devframe:auth:revoked",
64
- args: [],
65
- filter: (client) => affectedSessionIds.has(client.$meta.id)
66
- });
67
- }
68
- /**
69
- * Revoke an auth token: remove from storage and notify all connected clients
70
- * using this token that they are no longer trusted.
71
- */
72
- async function revokeAuthToken(context, storage, token) {
73
- storage.mutate((state) => {
74
- delete state.trusted[token];
75
- });
76
- await revokeActiveConnectionsForToken(context, token);
77
- }
78
- //#endregion
79
- export { timingSafeEqual as a, randomToken as i, revokeAuthToken as n, randomDigits as r, revokeActiveConnectionsForToken as t };
45
+ export { randomToken as n, timingSafeEqual as r, randomDigits as t };
@@ -1,7 +1,8 @@
1
1
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
2
- import { a as diagnostics } from "./storage-gEnj9ASQ.mjs";
3
- import { n as createHostContext, t as createH3DevframeHost } from "./host-h3-CgOetlNE.mjs";
4
- import { i as normalizeHttpServerUrl, t as startHttpAndWs } from "./server-DykHCza_.mjs";
2
+ import { n as createHostContext, t as createH3DevframeHost } from "./host-h3-Kz7t5Xab.mjs";
3
+ import { t as diagnostics } from "./diagnostics-B5-qHeqD.mjs";
4
+ import { r as registerDevframeInstance } from "./instance-registry-D6fxYu36.mjs";
5
+ import { i as normalizeHttpServerUrl, t as startHttpAndWs } from "./server-CXaDDUl0.mjs";
5
6
  import { n as resolveBasePath, t as normalizeBasePath } from "./_shared-bWRzeSa0.mjs";
6
7
  import { t as open } from "./open-Deb5xmIT.mjs";
7
8
  import { mountStaticHandler } from "./utils/serve-static.mjs";
@@ -294,7 +295,7 @@ async function createDevServer(def, options = {}) {
294
295
  const mcpPath = joinURL(basePath, mcpRoute);
295
296
  let mountMcpHttp;
296
297
  try {
297
- ({mountMcpHttp} = await import("./http-_pPbryTQ.mjs"));
298
+ ({mountMcpHttp} = await import("./http-Dvgxujcu.mjs"));
298
299
  } catch (error) {
299
300
  const reason = error instanceof Error ? error.message : String(error);
300
301
  throw diagnostics.DF0017({
@@ -344,13 +345,23 @@ async function createDevServer(def, options = {}) {
344
345
  await maybeOpenBrowser(def, flags, `${info.origin}${basePath}`, options.openBrowser, authHandler);
345
346
  }
346
347
  });
347
- if (mcpDispose) {
348
- const closeServer = started.close;
349
- started.close = async () => {
350
- await mcpDispose();
351
- await closeServer();
352
- };
353
- }
348
+ const registration = registerDevframeInstance({
349
+ pid: process$1.pid,
350
+ port: started.port,
351
+ origin: normalizeHttpServerUrl(host, started.port),
352
+ basePath,
353
+ id: def.id,
354
+ name: def.name,
355
+ rootDir: process$1.cwd(),
356
+ mcp: mcpConfig ? { path: joinURL(basePath, withoutLeadingSlash(mcpConfig.path ?? "__mcp")) } : null,
357
+ startedAt: Date.now()
358
+ });
359
+ const closeServer = started.close;
360
+ started.close = async () => {
361
+ registration.unregister();
362
+ await mcpDispose?.();
363
+ await closeServer();
364
+ };
354
365
  return started;
355
366
  }
356
367
  /**
@@ -1,8 +1,8 @@
1
- import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-CrzNxXKq.mjs";
2
- import { S as RpcFunctionsCollectorBase } from "./index-DgsLFhZg.mjs";
3
- import { t as DevframeNodeRpcSessionMeta } from "./ws-server-9-wn7MNQ.mjs";
1
+ import { _ as RpcFunctionDefinition, v as RpcFunctionDefinitionAny } from "./types-CnJSgRVa.mjs";
2
+ import { w as RpcFunctionsCollectorBase } from "./index-Dbw1p5ch.mjs";
3
+ import { n as DevframeNodeRpcSessionMeta } from "./ws-server-J-zkiOyp.mjs";
4
4
  import { CAC } from "cac";
5
- import { GenericSchema, InferOutput } from "valibot";
5
+ import { StandardSchemaV1 } from "@standard-schema/spec";
6
6
  import { Peer } from "crossws";
7
7
  import { BirpcReturn } from "birpc";
8
8
  import { Diagnostic, DiagnosticDefinition, defineDiagnostics } from "nostics";
@@ -10,11 +10,12 @@ import { Diagnostic, DiagnosticDefinition, defineDiagnostics } from "nostics";
10
10
  /**
11
11
  * Schema map for typed CLI flags. Keys are flag names in camelCase —
12
12
  * this matches CAC's parsed-flag output ( `--no-open` → `noOpen` ). Each
13
- * value is a valibot schema used to both (a) derive the CAC option type
14
- * when the flag is registered and (b) validate / coerce the parsed
15
- * value before it's forwarded to `setup(ctx, { flags })`.
13
+ * value is any [Standard Schema](https://standardschema.dev/) validator
14
+ * (valibot, zod, arktype, devframe's built-in `s`, …) used to both (a)
15
+ * derive the CAC option type when the flag is registered and (b) validate
16
+ * the parsed value before it's forwarded to `setup(ctx, { flags })`.
16
17
  */
17
- type CliFlagsSchema = Record<string, GenericSchema>;
18
+ type CliFlagsSchema = Record<string, StandardSchemaV1>;
18
19
  /**
19
20
  * Identity helper that preserves the literal schema-map type — use this
20
21
  * so `InferCliFlags<typeof myFlags>` resolves to the right object shape.
@@ -37,8 +38,8 @@ type CliFlagsSchema = Record<string, GenericSchema>;
37
38
  */
38
39
  declare function defineCliFlags<T extends CliFlagsSchema>(flags: T): T;
39
40
  /** Extract the parsed-output type from a {@link CliFlagsSchema}. */
40
- type InferCliFlags<T extends CliFlagsSchema> = { [K in keyof T]: InferOutput<T[K]>; };
41
- /** Validate and coerce the raw cac-parsed bag against a {@link CliFlagsSchema}. */
41
+ type InferCliFlags<T extends CliFlagsSchema> = { [K in keyof T]: StandardSchemaV1.InferOutput<T[K]>; };
42
+ /** Validate the raw cac-parsed bag against a {@link CliFlagsSchema}. */
42
43
  declare function parseCliFlags(schema: CliFlagsSchema, raw: Record<string, unknown>): {
43
44
  flags: Record<string, unknown>;
44
45
  issues?: string[];
@@ -138,6 +139,14 @@ interface AgentTool {
138
139
  tags?: readonly string[];
139
140
  /** Present for `kind === 'rpc'` — points to the RPC function name. */
140
141
  rpcName?: string;
142
+ /**
143
+ * Positional Standard Schemas describing a `kind: 'tool'` entry's
144
+ * arguments — carried on the tool itself (mirroring how `rpcName` defers
145
+ * an RPC-backed tool's schemas to `ctx.rpc.definitions`) so consumers
146
+ * (e.g. the MCP adapter) convert Standard Schema → JSON Schema on demand,
147
+ * same as RPC `args`.
148
+ */
149
+ args?: readonly StandardSchemaV1[];
141
150
  /** JSON Schema describing the input (positional args synthesized to an object). */
142
151
  inputSchema?: unknown;
143
152
  /** JSON Schema describing the output. */
@@ -160,6 +169,17 @@ interface AgentToolInput {
160
169
  description: string;
161
170
  safety?: 'read' | 'action' | 'destructive';
162
171
  tags?: readonly string[];
172
+ /**
173
+ * Positional Standard Schemas describing the tool's arguments — the same
174
+ * shape RPC definitions carry (any [Standard Schema](https://standardschema.dev/)
175
+ * validator: valibot, zod, arktype, devframe's built-in `s` builder, …).
176
+ * Each is advertised under `arg0` / `arg1` / … on the tool's JSON-Schema
177
+ * input, matching how the agent bridge coerces the incoming payload back
178
+ * into positional arguments. Purely descriptive: the handler still
179
+ * receives the caller's args object as-is.
180
+ */
181
+ args?: readonly StandardSchemaV1[];
182
+ /** Raw JSON-Schema input override. Prefer {@link args}. */
163
183
  inputSchema?: unknown;
164
184
  outputSchema?: unknown;
165
185
  examples?: readonly {
@@ -227,6 +247,33 @@ interface AgentManifest {
227
247
  interface AgentHandle {
228
248
  unregister: () => void;
229
249
  }
250
+ /**
251
+ * A lazy source of agent tools, queried at `list()` / `getTool()` /
252
+ * `invoke()` time — the same on-demand projection the host applies to
253
+ * `agent`-flagged RPC definitions. Use a provider when tools *derive from*
254
+ * other state (a command registry, a plugin catalog): the underlying state
255
+ * stays the single source of truth and nothing needs to be kept in sync.
256
+ *
257
+ * Providers should namespace tool ids like any other tool; on an id
258
+ * collision the earlier source wins (registered tools, then RPC tools,
259
+ * then providers in registration order).
260
+ *
261
+ * @experimental
262
+ */
263
+ type AgentToolProvider = () => readonly AgentToolInput[];
264
+ /**
265
+ * Handle returned by `registerToolProvider`.
266
+ *
267
+ * @experimental
268
+ */
269
+ interface AgentToolProviderHandle extends AgentHandle {
270
+ /**
271
+ * Signal that the provider's tool set changed. Fires
272
+ * `agent:manifest:changed` so protocol adapters (e.g. MCP) emit
273
+ * `tools/list_changed`.
274
+ */
275
+ notifyChanged: () => void;
276
+ }
230
277
  /**
231
278
  * Events emitted by `DevframeAgentHost`.
232
279
  *
@@ -262,6 +309,11 @@ interface DevframeAgentHost {
262
309
  registerTool: (tool: AgentToolInput) => AgentHandle;
263
310
  /** Unregister a previously registered tool by id. */
264
311
  unregisterTool: (id: string) => boolean;
312
+ /**
313
+ * Register a lazy tool source, queried on demand — see
314
+ * {@link AgentToolProvider}.
315
+ */
316
+ registerToolProvider: (provider: AgentToolProvider) => AgentToolProviderHandle;
265
317
  /** Register a readable resource. */
266
318
  registerResource: (resource: AgentResourceInput) => AgentHandle;
267
319
  /** Unregister a previously registered resource by id. */
@@ -1362,6 +1414,15 @@ interface ConnectionMeta {
1362
1414
  * > from a publicly reachable static `__connection.json`.
1363
1415
  */
1364
1416
  authToken?: string;
1417
+ /**
1418
+ * Session-scoped token that lets a trusted external viewer register its
1419
+ * browser origin before opening the WebSocket. The host includes it in the
1420
+ * connection metadata, which the viewer obtains through the host page.
1421
+ *
1422
+ * Treat this as a bearer credential. Keep connection metadata containing the
1423
+ * token same-origin until the requesting origin has been verified.
1424
+ */
1425
+ viewerOriginToken?: string;
1365
1426
  }
1366
1427
  //#endregion
1367
1428
  //#region src/types/utils.d.ts
@@ -1579,10 +1640,11 @@ interface DevframeCliOptions {
1579
1640
  */
1580
1641
  configure?: (cli: CAC) => void;
1581
1642
  /**
1582
- * Typed CLI flags for the default `dev` command, backed by valibot
1583
- * schemas. The adapter registers matching `--kebab-key` options on
1584
- * CAC, validates the parsed values, and forwards the typed bag to
1585
- * `setup(ctx, { flags })`.
1643
+ * Typed CLI flags for the default `dev` command, backed by any
1644
+ * [Standard Schema](https://standardschema.dev/) validator (valibot,
1645
+ * zod, arktype, or devframe's built-in `s`). The adapter registers
1646
+ * matching `--kebab-key` options on CAC, validates the parsed values,
1647
+ * and forwards the typed bag to `setup(ctx, { flags })`.
1586
1648
  *
1587
1649
  * Use {@link defineCliFlags} to preserve the literal schema-map
1588
1650
  * shape, and {@link InferCliFlags} to recover the typed output at the
@@ -1737,4 +1799,4 @@ interface DevframeDefinition {
1737
1799
  }
1738
1800
  declare function defineDevframe(d: DevframeDefinition): DevframeDefinition;
1739
1801
  //#endregion
1740
- export { DevframeRpcServerFunctions as $, DevframeSettingsStore as A, EventUnsubscribe as At, DevframeDefineDiagnosticsOptions as B, DevframeServicesHost as C, AgentResourceContent as Ct, DevframeScopedStreamingHost as D, DevframeAgentHost as Dt, DevframeScopedNodeRpc as E, AgentToolInput as Et, ScopedSharedStates as F, parseCliFlags as Ft, RpcBroadcastOptions as G, DevframeDiagnosticsHost as H, SettingsForNamespace as I, RpcSharedStateHost as J, RpcFunctionsHost as K, DevframeViewHost as L, ScopedClientFunctions as M, CliFlagsSchema as Mt, ScopedRpcFn as N, InferCliFlags as Nt, DevframeSettings as O, DevframeAgentHostEvents as Ot, ScopedServerFunctions as P, defineCliFlags as Pt, DevframeRpcClientFunctions as Q, DevframeHost as R, DevframeServiceOf as S, AgentResource as St, DevframeScopedNodeContext as T, AgentTool as Tt, DevframeDiagnosticsLogger as U, DevframeDiagnosticsDefinition as V, DevframeNodeRpcSession as W, RpcStreamingChannelOptions as X, RpcStreamingChannel as Y, RpcStreamingHost as Z, ConnectionMeta as _, SharedStateOptions as _t, DevframeDockDefaults as a, StreamReader as at, DevframeNodeContext as b, AgentHandle as bt, DevframeSetupInfo as c, createStreamReader as ct, McpRouteOptions as d, ImmutableArray as dt, DevframeRpcSharedStates as et, defineDevframe as f, ImmutableMap as ft, Thenable as g, SharedStateEvents as gt, PartialWithoutId as h, SharedState as ht, DevframeDeploymentKind as i, StreamErrorPayload as it, ScopedBroadcastOptions as j, EventsMap as jt, DevframeSettingsRegistry as k, EventEmitter as kt, DevframeSpaOptions as l, createStreamSink as lt, EntriesToObject as m, ImmutableSet as mt, DevframeCliOptions as n, CreateStreamReaderOptions as nt, DevframeDuplicationStrategy as o, StreamSink as ot, DevframeAuthHandler as p, ImmutableObject as pt, RpcSharedStateGetOptions as q, DevframeDefinition as r, CreateStreamSinkOptions as rt, DevframeRuntime as s, StreamSinkEvents as st, DevframeBrowserContext as t, BufferedChunk as tt, DevframeWsOptions as u, Immutable as ut, ConnectionMetaWebsocket as v, SharedStatePatch as vt, DevframeServicesRegistry as w, AgentResourceInput as wt, DevframeServiceId as x, AgentManifest as xt, DevframeCapabilities as y, createSharedState as yt, DevframeStorageScope as z };
1802
+ export { DevframeRpcServerFunctions as $, DevframeSettingsStore as A, DevframeAgentHostEvents as At, DevframeDefineDiagnosticsOptions as B, DevframeServicesHost as C, AgentResourceContent as Ct, DevframeScopedStreamingHost as D, AgentToolProvider as Dt, DevframeScopedNodeRpc as E, AgentToolInput as Et, ScopedSharedStates as F, InferCliFlags as Ft, RpcBroadcastOptions as G, DevframeDiagnosticsHost as H, SettingsForNamespace as I, defineCliFlags as It, RpcSharedStateHost as J, RpcFunctionsHost as K, DevframeViewHost as L, parseCliFlags as Lt, ScopedClientFunctions as M, EventUnsubscribe as Mt, ScopedRpcFn as N, EventsMap as Nt, DevframeSettings as O, AgentToolProviderHandle as Ot, ScopedServerFunctions as P, CliFlagsSchema as Pt, DevframeRpcClientFunctions as Q, DevframeHost as R, DevframeServiceOf as S, AgentResource as St, DevframeScopedNodeContext as T, AgentTool as Tt, DevframeDiagnosticsLogger as U, DevframeDiagnosticsDefinition as V, DevframeNodeRpcSession as W, RpcStreamingChannelOptions as X, RpcStreamingChannel as Y, RpcStreamingHost as Z, ConnectionMeta as _, SharedStateOptions as _t, DevframeDockDefaults as a, StreamReader as at, DevframeNodeContext as b, AgentHandle as bt, DevframeSetupInfo as c, createStreamReader as ct, McpRouteOptions as d, ImmutableArray as dt, DevframeRpcSharedStates as et, defineDevframe as f, ImmutableMap as ft, Thenable as g, SharedStateEvents as gt, PartialWithoutId as h, SharedState as ht, DevframeDeploymentKind as i, StreamErrorPayload as it, ScopedBroadcastOptions as j, EventEmitter as jt, DevframeSettingsRegistry as k, DevframeAgentHost as kt, DevframeSpaOptions as l, createStreamSink as lt, EntriesToObject as m, ImmutableSet as mt, DevframeCliOptions as n, CreateStreamReaderOptions as nt, DevframeDuplicationStrategy as o, StreamSink as ot, DevframeAuthHandler as p, ImmutableObject as pt, RpcSharedStateGetOptions as q, DevframeDefinition as r, CreateStreamSinkOptions as rt, DevframeRuntime as s, StreamSinkEvents as st, DevframeBrowserContext as t, BufferedChunk as tt, DevframeWsOptions as u, Immutable as ut, ConnectionMetaWebsocket as v, SharedStatePatch as vt, DevframeServicesRegistry as w, AgentResourceInput as wt, DevframeServiceId as x, AgentManifest as xt, DevframeCapabilities as y, createSharedState as yt, DevframeStorageScope as z };