devframe 0.7.16 → 0.8.0

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 (73) 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 +45 -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-B-R_tDGJ.mjs} +18 -13
  15. package/dist/cli/main.d.mts +11 -0
  16. package/dist/cli/main.mjs +283 -0
  17. package/dist/client/index.d.mts +4 -4
  18. package/dist/client/index.mjs +88 -4
  19. package/dist/{context-CzqPrJSz.d.mts → context-7BbaIUSI.d.mts} +1 -1
  20. package/dist/{context-DY72brRI.mjs → context-C9Cgm1hP.mjs} +1 -1
  21. package/dist/{dev-BCeaQuGE.mjs → dev-D4M7Veo7.mjs} +22 -11
  22. package/dist/{devframe-Ckhf3kFw.d.mts → devframe-Dsjn_Xtq.d.mts} +68 -15
  23. package/dist/diagnostics-B5-qHeqD.mjs +94 -0
  24. package/dist/{diagnostics-BXwBQmoN.mjs → diagnostics-hwjXp_UV.mjs} +8 -0
  25. package/dist/{dump-Cz7yVvsB.mjs → dump-CgZShDRB.mjs} +82 -6
  26. package/dist/{build-server-C7vTKjiQ.mjs → fetch-BZyK4v6W.mjs} +225 -51
  27. package/dist/helpers/vite.d.mts +1 -1
  28. package/dist/helpers/vite.mjs +2 -2
  29. package/dist/{host-h3-CgOetlNE.mjs → host-h3-Kz7t5Xab.mjs} +83 -28
  30. package/dist/http-BghOqfTD.mjs +38 -0
  31. package/dist/{index-8dndvIxR.d.mts → index-CeBbry0R.d.mts} +2 -2
  32. package/dist/{index-DgsLFhZg.d.mts → index-Dbw1p5ch.d.mts} +27 -3
  33. package/dist/{index-YDSJgsyG.d.mts → index-vAX6bYuc.d.mts} +1 -1
  34. package/dist/index.d.mts +5 -5
  35. package/dist/instance-registry-D6fxYu36.mjs +190 -0
  36. package/dist/node/auth.d.mts +2 -2
  37. package/dist/node/hub-internals.d.mts +2 -2
  38. package/dist/node/hub-internals.mjs +1 -1
  39. package/dist/node/index.d.mts +104 -6
  40. package/dist/node/index.mjs +5 -4
  41. package/dist/recipes/common-rpc-functions.d.mts +33 -33
  42. package/dist/recipes/common-rpc-functions.mjs +6 -6
  43. package/dist/recipes/interactive-auth.d.mts +2 -2
  44. package/dist/recipes/interactive-auth.mjs +13 -13
  45. package/dist/rpc/dump.d.mts +1 -1
  46. package/dist/rpc/dump.mjs +1 -1
  47. package/dist/rpc/index.d.mts +3 -3
  48. package/dist/rpc/index.mjs +4 -4
  49. package/dist/rpc/transports/ws-client.d.mts +1 -1
  50. package/dist/rpc/transports/ws-client.mjs +1 -1
  51. package/dist/rpc/transports/ws-server.d.mts +1 -1
  52. package/dist/rpc/transports/ws-server.mjs +1 -1
  53. package/dist/{serialization-DpLXCy13.mjs → serialization-C8Mnw9hK.mjs} +1 -1
  54. package/dist/{server-DykHCza_.mjs → server-CcAPxuoT.mjs} +2 -2
  55. package/dist/{server-B0wiP_Oi.d.mts → server-VLQJouOO.d.mts} +2 -2
  56. package/dist/simple-schema-BDzLeJDk.d.mts +102 -0
  57. package/dist/simple-schema-DQPZrAaZ.mjs +154 -0
  58. package/dist/{storage-gEnj9ASQ.mjs → storage-Dzoc3NVC.mjs} +2 -66
  59. package/dist/{transports-BmDVn4SF.mjs → transports-vhizgqXM.mjs} +1 -1
  60. package/dist/types/index.d.mts +4 -4
  61. package/dist/{types-CrzNxXKq.d.mts → types-CnJSgRVa.d.mts} +33 -19
  62. package/dist/utils/agent-tool-name.d.mts +27 -0
  63. package/dist/utils/agent-tool-name.mjs +34 -0
  64. package/dist/utils/events.d.mts +1 -1
  65. package/dist/utils/shared-state.d.mts +1 -1
  66. package/dist/utils/simple-schema.d.mts +2 -0
  67. package/dist/utils/simple-schema.mjs +154 -0
  68. package/dist/utils/streaming-channel.d.mts +1 -1
  69. package/dist/{ws-client-DSXiI2h7.d.mts → ws-client-B6Tnr0ic.d.mts} +1 -1
  70. package/dist/{ws-server-9-wn7MNQ.d.mts → ws-server-D_Vjtums.d.mts} +1 -1
  71. package/package.json +18 -7
  72. package/skills/devframe/SKILL.md +1 -1
  73. package/dist/http-_pPbryTQ.mjs +0 -114
@@ -68,6 +68,14 @@ const diagnostics = defineDiagnostics({
68
68
  DF0028: {
69
69
  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
70
  fix: "Remove `snapshot: true`, or change the function type to `query`."
71
+ },
72
+ DF0043: {
73
+ why: (p) => `RPC function "${p.name}" received an invalid argument at position ${p.index}: ${p.issues}`,
74
+ fix: "Pass a value that satisfies the `args` schema declared for this function."
75
+ },
76
+ DF0044: {
77
+ why: (p) => `RPC function "${p.name}" returned a value that failed its \`returns\` schema: ${p.issues}`,
78
+ fix: "Make the handler return a value that satisfies the `returns` schema, or relax the schema."
71
79
  }
72
80
  }
73
81
  });
@@ -92,6 +100,70 @@ function isPlainErrorShape(value) {
92
100
  return typeof value === "object" && value !== null && typeof value.message === "string" && typeof value.name === "string";
93
101
  }
94
102
  //#endregion
103
+ //#region src/rpc/validate-io.ts
104
+ /**
105
+ * Run a single [Standard Schema](https://standardschema.dev) validator,
106
+ * awaiting the result when the validator is asynchronous.
107
+ */
108
+ async function runStandardSchema(schema, value) {
109
+ const result = schema["~standard"].validate(value);
110
+ return result instanceof Promise ? await result : result;
111
+ }
112
+ /**
113
+ * Render Standard Schema issues into a single human-readable line for a
114
+ * diagnostic message, prefixing each with its dotted path when present.
115
+ */
116
+ function formatIssues(issues) {
117
+ return issues.map((issue) => {
118
+ const path = issue.path?.map((segment) => typeof segment === "object" ? segment.key : segment).join(".");
119
+ return path ? `${path}: ${issue.message}` : issue.message;
120
+ }).join("; ");
121
+ }
122
+ /**
123
+ * Validate positional arguments against their declared schemas. Only
124
+ * indices with a schema are checked; extra arguments pass through
125
+ * untouched. Throws `DF0038` on the first failing argument.
126
+ *
127
+ * Validation guards the payload without rewriting it: the original values
128
+ * are handed to the handler unchanged, so a schema that describes a subset
129
+ * of an object never silently strips the sender's extra fields (and any
130
+ * declared transforms stay a purely type-level concern).
131
+ *
132
+ * @internal
133
+ */
134
+ async function validateRpcArgs(name, argsSchema, args) {
135
+ const original = args.slice();
136
+ if (!argsSchema || argsSchema.length === 0) return original;
137
+ for (let index = 0; index < argsSchema.length; index++) {
138
+ const schema = argsSchema[index];
139
+ if (!schema) continue;
140
+ const result = await runStandardSchema(schema, args[index]);
141
+ if (result.issues) throw diagnostics.DF0043({
142
+ name,
143
+ index,
144
+ issues: formatIssues(result.issues)
145
+ });
146
+ }
147
+ return original;
148
+ }
149
+ /**
150
+ * Validate a handler's resolved return value against its declared schema.
151
+ * Throws `DF0039` when the value fails the schema, otherwise returns the
152
+ * original value unchanged (guard-only, never rewriting the payload — see
153
+ * {@link validateRpcArgs}). Passes through when no return schema is set.
154
+ *
155
+ * @internal
156
+ */
157
+ async function validateRpcReturn(name, returnSchema, value) {
158
+ if (!returnSchema) return value;
159
+ const result = await runStandardSchema(returnSchema, value);
160
+ if (result.issues) throw diagnostics.DF0044({
161
+ name,
162
+ issues: formatIssues(result.issues)
163
+ });
164
+ return value;
165
+ }
166
+ //#endregion
95
167
  //#region src/rpc/handler.ts
96
168
  async function getRpcResolvedSetupResult(definition, context) {
97
169
  if (!definition.setup) return {};
@@ -118,10 +190,22 @@ async function getRpcResolvedSetupResult(definition, context) {
118
190
  return await definition.__promise;
119
191
  }
120
192
  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;
193
+ let handler = definition.handler;
194
+ if (!handler) {
195
+ const result = await getRpcResolvedSetupResult(definition, context);
196
+ if (!result.handler) throw diagnostics.DF0024({ name: definition.name });
197
+ handler = result.handler;
198
+ }
199
+ const argsSchema = definition.args;
200
+ const returnSchema = definition.returns;
201
+ if (!argsSchema && !returnSchema) return handler;
202
+ const inner = handler;
203
+ const validating = async (...args) => {
204
+ const validatedArgs = await validateRpcArgs(definition.name, argsSchema, args);
205
+ const output = await inner(...validatedArgs);
206
+ return await validateRpcReturn(definition.name, returnSchema, output);
207
+ };
208
+ return validating;
125
209
  }
126
210
  //#endregion
127
211
  //#region src/rpc/cache.ts
@@ -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-Dsjn_Xtq.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -1,4 +1,4 @@
1
- import { t as createStorage } from "./storage-gEnj9ASQ.mjs";
1
+ import { t as createStorage } from "./storage-Dzoc3NVC.mjs";
2
2
  import { i as randomToken, n as revokeAuthToken, t as revokeActiveConnectionsForToken } from "./revoke-BtQDKTp7.mjs";
3
3
  import { join } from "pathe";
4
4
  //#region src/node/hub-internals/context.ts
@@ -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-CcAPxuoT.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-BghOqfTD.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 { t as DevframeNodeRpcSessionMeta } from "./ws-server-D_Vjtums.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. */
@@ -1579,10 +1631,11 @@ interface DevframeCliOptions {
1579
1631
  */
1580
1632
  configure?: (cli: CAC) => void;
1581
1633
  /**
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 })`.
1634
+ * Typed CLI flags for the default `dev` command, backed by any
1635
+ * [Standard Schema](https://standardschema.dev/) validator (valibot,
1636
+ * zod, arktype, or devframe's built-in `s`). The adapter registers
1637
+ * matching `--kebab-key` options on CAC, validates the parsed values,
1638
+ * and forwards the typed bag to `setup(ctx, { flags })`.
1586
1639
  *
1587
1640
  * Use {@link defineCliFlags} to preserve the literal schema-map
1588
1641
  * shape, and {@link InferCliFlags} to recover the typed output at the
@@ -1737,4 +1790,4 @@ interface DevframeDefinition {
1737
1790
  }
1738
1791
  declare function defineDevframe(d: DevframeDefinition): DevframeDefinition;
1739
1792
  //#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 };
1793
+ 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 };
@@ -0,0 +1,94 @@
1
+ import { t as devframeReporter } from "./diagnostics-reporter-CsIG85Q5.mjs";
2
+ import { defineDiagnostics } from "nostics";
3
+ //#region src/node/diagnostics.ts
4
+ const diagnostics = defineDiagnostics({
5
+ docsBase: "https://devfra.me/errors",
6
+ reporters: [devframeReporter],
7
+ codes: {
8
+ DF0006: { why: (p) => `RPC function "${p.name}" is not registered` },
9
+ DF0007: { why: "AsyncLocalStorage is not set, it likely to be an internal bug of the Devframe foundation" },
10
+ DF0008: { why: (p) => `distDir ${p.distDir} does not exist` },
11
+ DF0012: { why: (p) => `Failed to parse storage file: ${p.filepath}, falling back to defaults.` },
12
+ DF0013: { why: (p) => `Shared state of "${p.key}" is not found, please provide an initial value for the first time` },
13
+ DF0014: {
14
+ why: (p) => `RPC function "${p.name}" has an invalid \`agent\` field — \`description\` must be a non-empty string.`,
15
+ fix: "Provide a short description (~1–3 sentences) explaining what the tool does and when agents should invoke it."
16
+ },
17
+ DF0015: {
18
+ why: (p) => `Agent tool "${p.id}" is already registered.`,
19
+ fix: "Tool ids must be unique across RPC functions with an `agent` field and tools registered via `ctx.agent.registerTool()`."
20
+ },
21
+ DF0016: { why: (p) => `Agent resource "${p.id}" is already registered.` },
22
+ DF0017: { why: (p) => `Failed to start MCP server (${p.transport}): ${p.reason}` },
23
+ DF0029: {
24
+ why: (p) => `Stream "${p.channel}#${p.id}" dropped ${p.dropped} chunk(s) after exceeding the client high-water mark.`,
25
+ fix: "The consumer is too slow for the producer. Raise `highWaterMark` on the subscription, slow the producer, or batch chunks."
26
+ },
27
+ DF0030: {
28
+ why: (p) => `Stream "${p.channel}#${p.id}" is unknown — no producer has called \`channel.start({ id: "${p.id}" })\`.`,
29
+ fix: "Ensure the server-side producer is running before clients subscribe, or check for typos in the stream id."
30
+ },
31
+ DF0031: {
32
+ why: (p) => `Cannot write to closed stream "${p.channel}#${p.id}".`,
33
+ fix: "Track the producer lifecycle — guard writes with the `stream.signal.aborted` flag."
34
+ },
35
+ DF0032: {
36
+ why: (p) => `Streaming channel "${p.channel}" is already registered.`,
37
+ fix: "Each channel name must be unique within a context. Pick a different name or reuse the existing channel handle."
38
+ },
39
+ DF0033: {
40
+ why: (p) => `Failed to start dev RPC bridge for "${p.id}": ${p.reason}`,
41
+ fix: "Verify the bridge port is free and the devframe setup function does not throw. Pin a port via `cli.port` / `cli.portRange` on the definition, or via `devMiddleware.port` on `viteDevBridge`."
42
+ },
43
+ DF0034: {
44
+ why: (p) => `Scoped RPC registration for namespace "${p.namespace}" received an already-namespaced function name "${p.name}".`,
45
+ fix: "A scoped context auto-namespaces ids. Pass a bare name without a \":\" separator (e.g. `register({ name: \"get-cwd\" })`), or use the unscoped `ctx.base.rpc.register` for a fully-qualified name."
46
+ },
47
+ DF0035: {
48
+ why: (p) => `Failed to persist storage file: ${p.filepath}`,
49
+ fix: "Check that the storage directory is writable and has free space."
50
+ },
51
+ DF0036: {
52
+ why: (p) => `RPC call to "${p.name}" was rejected: the caller is not authorized.`,
53
+ fix: "Complete the auth handshake (or connect with a static/pre-shared token) before calling a trusted method. Untrusted callers may only call `anonymous:`-prefixed methods — see `isAnonymousRpcMethod`."
54
+ },
55
+ DF0037: {
56
+ why: (p) => `A service is already provided under "${p.id}".`,
57
+ fix: "Service ids are unique per context. Revoke the existing provider first (the `provide()` call returns a revoke function), or namespace the id with your plugin id to avoid collisions."
58
+ },
59
+ DF0042: {
60
+ why: (p) => `"${p.id}" declares \`capabilities.build: false\` — its static export is not meaningful (writes are excluded and any live-served data won't be there).`,
61
+ fix: "Pass `{ force: true }` to `createBuild()` if the degraded export is still useful to you, or drop `capabilities.build: false` on the definition."
62
+ },
63
+ DF0045: {
64
+ why: (p) => `Failed to update the devframe instance registry at "${p.file}": ${p.reason}`,
65
+ fix: "Discovery tooling (`devframe connect`) will not see this instance. Check that the registry directory is writable, point `DEVFRAME_INSTANCES_DIR` at a writable directory, or set `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` to opt out of registration."
66
+ },
67
+ DF0046: {
68
+ why: (p) => `\`devframe connect\` requires the optional peer dependency @modelcontextprotocol/server: ${p.reason}`,
69
+ fix: "Install it next to devframe (e.g. `npm install @modelcontextprotocol/server`) and run `devframe connect` again."
70
+ },
71
+ DF0047: {
72
+ why: (p) => `Agent tool "${p.id}" is hidden from the MCP surface: its wire name "${p.name}" collides with the tool "${p.existing}".`,
73
+ fix: "Wire names derive from tool ids (characters outside [a-zA-Z0-9_-] become \"_\"). Rename one of the two ids so they sanitize to distinct names."
74
+ },
75
+ DF0048: {
76
+ why: (p) => `Unknown shared-state key "${p.key}".`,
77
+ fix: "Call the devframe_state_read tool without arguments to list the available keys, then retry with one of them."
78
+ },
79
+ DF0049: {
80
+ why: "The devframe_connect_call-tool tool requires { port: number, tool: string }.",
81
+ fix: "Call devframe_connect_list-instances to get the port and tool names, then retry."
82
+ },
83
+ DF0050: {
84
+ why: (p) => `No running devframe instance on port ${p.port}.`,
85
+ fix: "Call devframe_connect_list-instances for the current instance list — the instance may have stopped or changed port."
86
+ },
87
+ DF0051: {
88
+ why: (p) => `The devframe instance on port ${p.port} has no MCP endpoint.`,
89
+ fix: "Restart the instance with the --mcp flag (or set `cli.mcp: true` on its definition) to expose its tools, then list instances again."
90
+ }
91
+ }
92
+ });
93
+ //#endregion
94
+ export { diagnostics as t };
@@ -26,6 +26,14 @@ const diagnostics = defineDiagnostics({
26
26
  DF0028: {
27
27
  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.`,
28
28
  fix: "Remove `snapshot: true`, or change the function type to `query`."
29
+ },
30
+ DF0043: {
31
+ why: (p) => `RPC function "${p.name}" received an invalid argument at position ${p.index}: ${p.issues}`,
32
+ fix: "Pass a value that satisfies the `args` schema declared for this function."
33
+ },
34
+ DF0044: {
35
+ why: (p) => `RPC function "${p.name}" returned a value that failed its \`returns\` schema: ${p.issues}`,
36
+ fix: "Make the handler return a value that satisfies the `returns` schema, or relax the schema."
29
37
  }
30
38
  }
31
39
  });
@@ -1,4 +1,4 @@
1
- import { t as diagnostics } from "./diagnostics-BXwBQmoN.mjs";
1
+ import { t as diagnostics } from "./diagnostics-hwjXp_UV.mjs";
2
2
  import { DEVFRAME_RPC_DUMP_DIRNAME } from "./constants.mjs";
3
3
  import { createHash } from "node:crypto";
4
4
  //#region ../../node_modules/.pnpm/ohash@2.0.11/node_modules/ohash/dist/shared/ohash.D__AXeF1.mjs
@@ -498,6 +498,70 @@ function getDefinitionsWithDumps(definitions) {
498
498
  return definitions.filter((def) => def.dump !== void 0);
499
499
  }
500
500
  //#endregion
501
+ //#region src/rpc/validate-io.ts
502
+ /**
503
+ * Run a single [Standard Schema](https://standardschema.dev) validator,
504
+ * awaiting the result when the validator is asynchronous.
505
+ */
506
+ async function runStandardSchema(schema, value) {
507
+ const result = schema["~standard"].validate(value);
508
+ return result instanceof Promise ? await result : result;
509
+ }
510
+ /**
511
+ * Render Standard Schema issues into a single human-readable line for a
512
+ * diagnostic message, prefixing each with its dotted path when present.
513
+ */
514
+ function formatIssues(issues) {
515
+ return issues.map((issue) => {
516
+ const path = issue.path?.map((segment) => typeof segment === "object" ? segment.key : segment).join(".");
517
+ return path ? `${path}: ${issue.message}` : issue.message;
518
+ }).join("; ");
519
+ }
520
+ /**
521
+ * Validate positional arguments against their declared schemas. Only
522
+ * indices with a schema are checked; extra arguments pass through
523
+ * untouched. Throws `DF0038` on the first failing argument.
524
+ *
525
+ * Validation guards the payload without rewriting it: the original values
526
+ * are handed to the handler unchanged, so a schema that describes a subset
527
+ * of an object never silently strips the sender's extra fields (and any
528
+ * declared transforms stay a purely type-level concern).
529
+ *
530
+ * @internal
531
+ */
532
+ async function validateRpcArgs(name, argsSchema, args) {
533
+ const original = args.slice();
534
+ if (!argsSchema || argsSchema.length === 0) return original;
535
+ for (let index = 0; index < argsSchema.length; index++) {
536
+ const schema = argsSchema[index];
537
+ if (!schema) continue;
538
+ const result = await runStandardSchema(schema, args[index]);
539
+ if (result.issues) throw diagnostics.DF0043({
540
+ name,
541
+ index,
542
+ issues: formatIssues(result.issues)
543
+ });
544
+ }
545
+ return original;
546
+ }
547
+ /**
548
+ * Validate a handler's resolved return value against its declared schema.
549
+ * Throws `DF0039` when the value fails the schema, otherwise returns the
550
+ * original value unchanged (guard-only, never rewriting the payload — see
551
+ * {@link validateRpcArgs}). Passes through when no return schema is set.
552
+ *
553
+ * @internal
554
+ */
555
+ async function validateRpcReturn(name, returnSchema, value) {
556
+ if (!returnSchema) return value;
557
+ const result = await runStandardSchema(returnSchema, value);
558
+ if (result.issues) throw diagnostics.DF0044({
559
+ name,
560
+ issues: formatIssues(result.issues)
561
+ });
562
+ return value;
563
+ }
564
+ //#endregion
501
565
  //#region src/rpc/handler.ts
502
566
  async function getRpcResolvedSetupResult(definition, context) {
503
567
  if (!definition.setup) return {};
@@ -524,10 +588,22 @@ async function getRpcResolvedSetupResult(definition, context) {
524
588
  return await definition.__promise;
525
589
  }
526
590
  async function getRpcHandler(definition, context) {
527
- if (definition.handler) return definition.handler;
528
- const result = await getRpcResolvedSetupResult(definition, context);
529
- if (!result.handler) throw diagnostics.DF0024({ name: definition.name });
530
- return result.handler;
591
+ let handler = definition.handler;
592
+ if (!handler) {
593
+ const result = await getRpcResolvedSetupResult(definition, context);
594
+ if (!result.handler) throw diagnostics.DF0024({ name: definition.name });
595
+ handler = result.handler;
596
+ }
597
+ const argsSchema = definition.args;
598
+ const returnSchema = definition.returns;
599
+ if (!argsSchema && !returnSchema) return handler;
600
+ const inner = handler;
601
+ const validating = async (...args) => {
602
+ const validatedArgs = await validateRpcArgs(definition.name, argsSchema, args);
603
+ const output = await inner(...validatedArgs);
604
+ return await validateRpcReturn(definition.name, returnSchema, output);
605
+ };
606
+ return validating;
531
607
  }
532
608
  //#endregion
533
609
  //#region src/rpc/dump/static.ts
@@ -607,4 +683,4 @@ async function collectStaticRpcDump(definitions, context) {
607
683
  };
608
684
  }
609
685
  //#endregion
610
- export { dumpFunctions as a, serializeDumpError as c, hash as d, createClientFromDump as i, validateDefinition as l, getRpcHandler as n, getDefinitionsWithDumps as o, getRpcResolvedSetupResult as r, reviveDumpError as s, collectStaticRpcDump as t, validateDefinitions as u };
686
+ export { validateRpcReturn as a, getDefinitionsWithDumps as c, validateDefinition as d, validateDefinitions as f, validateRpcArgs as i, reviveDumpError as l, getRpcHandler as n, createClientFromDump as o, hash as p, getRpcResolvedSetupResult as r, dumpFunctions as s, collectStaticRpcDump as t, serializeDumpError as u };