devframe 0.9.12 → 0.9.14

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 (67) hide show
  1. package/dist/{_shared-DEAMiPfH.d.mts → _shared-Bf2FcfJ4.d.mts} +1 -1
  2. package/dist/adapters/build.d.mts +5 -6
  3. package/dist/adapters/build.mjs +2 -2
  4. package/dist/adapters/cac.d.mts +5 -5
  5. package/dist/adapters/cac.mjs +1 -1
  6. package/dist/adapters/dev.d.mts +6 -6
  7. package/dist/adapters/dev.mjs +1 -1
  8. package/dist/adapters/embedded.d.mts +4 -5
  9. package/dist/adapters/initiate.d.mts +10 -11
  10. package/dist/adapters/initiate.mjs +1 -1
  11. package/dist/adapters/mcp.d.mts +5 -5
  12. package/dist/adapters/mcp.mjs +1 -1
  13. package/dist/cli/main.d.mts +2 -3
  14. package/dist/client/index.d.mts +144 -36
  15. package/dist/client/index.mjs +223 -34
  16. package/dist/constants.d.mts +20 -21
  17. package/dist/{context-BBWPLDHh.d.mts → context-B7411Bem.d.mts} +25 -5
  18. package/dist/{context-_temOGDB.d.mts → context-CIsx_QnV.d.mts} +1 -1
  19. package/dist/{context-DwGevWqT.mjs → context-Cdj9z9m2.mjs} +9 -2
  20. package/dist/{dev-CZ1fu4J1.mjs → dev-DpfkCVqo.mjs} +2 -2
  21. package/dist/in-page-channel/index.d.mts +76 -38
  22. package/dist/in-page-channel/index.mjs +56 -22
  23. package/dist/{index-1ezCLThh.d.mts → index--pZ3fdAq.d.mts} +23 -3
  24. package/dist/index.d.mts +5 -5
  25. package/dist/{instance-shell-xuu6jg51.mjs → instance-shell-370Oee9K.mjs} +4 -12
  26. package/dist/{instance-shell-BUyYrKlu.d.mts → instance-shell-B-FE8NY5.d.mts} +5 -5
  27. package/dist/internal/index.d.mts +15 -15
  28. package/dist/internal/index.mjs +1 -1
  29. package/dist/node/auth.d.mts +3 -3
  30. package/dist/node/auth.mjs +2 -2
  31. package/dist/node/hub-internals.d.mts +2 -2
  32. package/dist/node/index.d.mts +6 -6
  33. package/dist/node/index.mjs +1 -1
  34. package/dist/nostics-DD6gsDZA.mjs +108 -0
  35. package/dist/recipes/common-rpc-functions.d.mts +7 -8
  36. package/dist/recipes/interactive-auth.d.mts +32 -22
  37. package/dist/recipes/interactive-auth.mjs +58 -18
  38. package/dist/rpc/client.d.mts +2 -3
  39. package/dist/rpc/dump.d.mts +16 -17
  40. package/dist/rpc/dump.mjs +1 -1
  41. package/dist/rpc/server.d.mts +2 -3
  42. package/dist/rpc/transports/sse-server.d.mts +4 -5
  43. package/dist/rpc/transports/ws-bun.d.mts +6 -7
  44. package/dist/rpc/transports/ws-deno.d.mts +5 -6
  45. package/dist/{rpc-core-DiCr5etS.d.mts → rpc-core-D7WvbvK0.d.mts} +2 -2
  46. package/dist/{shared-state-CaxB1q0h.mjs → shared-state-eQVYHNqA.mjs} +4 -1
  47. package/dist/{state-Mwt6--el.mjs → state-W4wI6X5X.mjs} +14 -1
  48. package/dist/types/index.d.mts +1 -1
  49. package/dist/utils/agent-tool-name.d.mts +2 -3
  50. package/dist/utils/colors.d.mts +4 -5
  51. package/dist/utils/crypto-token.d.mts +4 -5
  52. package/dist/utils/events.d.mts +3 -4
  53. package/dist/utils/hash.d.mts +2 -3
  54. package/dist/utils/launch-editor.d.mts +2 -3
  55. package/dist/utils/nanoid.d.mts +2 -3
  56. package/dist/utils/open.d.mts +2 -3
  57. package/dist/utils/remote-assets.d.mts +2 -3
  58. package/dist/utils/serve-static.d.mts +6 -7
  59. package/dist/utils/shared-state.d.mts +1 -1
  60. package/dist/utils/shared-state.mjs +1 -1
  61. package/dist/utils/simple-schema.d.mts +17 -18
  62. package/dist/utils/streaming-channel.d.mts +1 -1
  63. package/dist/utils/streaming-channel.mjs +4 -1
  64. package/dist/utils/structured-clone.d.mts +5 -6
  65. package/dist/utils/when.d.mts +6 -7
  66. package/dist/{write-BuLCrn9h.mjs → write-D2AJqVT8.mjs} +1 -1
  67. package/package.json +4 -4
@@ -1,12 +1,10 @@
1
- import { t as DEVFRAME_EVENTS } from "../events-BV4Dj59a.mjs";
1
+ import { n as DEVFRAME_EVENTS, t as defineDiagnostics } from "../nostics-DD6gsDZA.mjs";
2
2
  import { t as hash } from "../hash-DdPnc4k3.mjs";
3
- import { colors } from "../utils/colors.mjs";
4
3
  import { createEventEmitter } from "../utils/events.mjs";
5
- import { t as createSharedState } from "../shared-state-CaxB1q0h.mjs";
4
+ import { t as createSharedState } from "../shared-state-eQVYHNqA.mjs";
6
5
  import { i as structuredCloneStringify, n as structuredCloneParse, t as structuredCloneDeserialize } from "../structured-clone-CgtQ61SA.mjs";
7
6
  import { createStreamReader, createStreamSink } from "../utils/streaming-channel.mjs";
8
- import { defineDiagnostics } from "nostics";
9
- import { ansiFormatter } from "nostics/formatters/ansi";
7
+ import { toAgentToolName } from "../utils/agent-tool-name.mjs";
10
8
  import { withBase, withProtocol } from "ufo";
11
9
  import { createBirpc } from "birpc";
12
10
  //#region src/constants.ts
@@ -96,34 +94,8 @@ var RpcCacheManager = class {
96
94
  }
97
95
  };
98
96
  //#endregion
99
- //#region src/utils/nostics.ts
100
- const formatAnsi = ansiFormatter(colors);
101
- /**
102
- * The reporter every {@link defineDiagnostics} call below wires in ahead of
103
- * any caller-supplied ones: prints the diagnostic through devframe's own
104
- * ANSI colors via `console[method]` (default `'warn'`).
105
- */
106
- function devframeReporter(d, { method = "warn" } = {}) {
107
- console[method](formatAnsi(d));
108
- }
109
- /**
110
- * Drop-in replacement for `nostics`'s `defineDiagnostics()` with devframe's
111
- * ANSI console reporter pre-wired ahead of any `reporters` passed in. Every
112
- * `diagnostics.ts` in devframe core, `@devframes/hub`, `@devframes/json-render`,
113
- * and the built-in plugins defines its codes through this instead of
114
- * `nostics`'s own `defineDiagnostics`; the reporter registration lives
115
- * here, once, so none of them need to build their own reporter (`colors`,
116
- * `ansiFormatter`) or take a direct dependency on `nostics` themselves.
117
- */
118
- function defineDiagnostics$1(options) {
119
- return defineDiagnostics({
120
- ...options,
121
- reporters: [devframeReporter, ...options.reporters ?? []]
122
- });
123
- }
124
- //#endregion
125
97
  //#region src/rpc/diagnostics.ts
126
- const diagnostics = defineDiagnostics$1({
98
+ const diagnostics = defineDiagnostics({
127
99
  docsBase: "https://devfra.me/errors",
128
100
  codes: {
129
101
  DF0019: {
@@ -1103,6 +1075,13 @@ function createLiveRpcClientMode(options) {
1103
1075
  }
1104
1076
  return token;
1105
1077
  }
1078
+ async function requestAuthCode(options = {}) {
1079
+ await serverRpc.$call("anonymous:devframe:auth:request-code", {
1080
+ ua: navigator.userAgent,
1081
+ origin: location.origin,
1082
+ ...options.reissue ? { reissue: true } : {}
1083
+ });
1084
+ }
1106
1085
  async function requestTrust() {
1107
1086
  if (isTrusted) return true;
1108
1087
  return requestTrustWithToken(currentAuthToken ?? "");
@@ -1133,6 +1112,7 @@ function createLiveRpcClientMode(options) {
1133
1112
  requestTrust,
1134
1113
  requestTrustWithToken,
1135
1114
  requestTrustWithCode,
1115
+ requestAuthCode,
1136
1116
  ensureTrusted,
1137
1117
  call: (...args) => {
1138
1118
  const method = String(args[0]);
@@ -1333,6 +1313,8 @@ async function createStaticRpcClientMode(options) {
1333
1313
  requestTrustWithToken: async () => true,
1334
1314
  /** Static backends are always trusted, so there's nothing to exchange. */
1335
1315
  requestTrustWithCode: async () => null,
1316
+ /** No server terminal to print a code in. */
1317
+ requestAuthCode: async () => {},
1336
1318
  ensureTrusted: async () => true,
1337
1319
  call: (...args) => staticCaller.call(args[0], args.slice(1)),
1338
1320
  callEvent: (...args) => staticCaller.callEvent(args[0], args.slice(1)),
@@ -1675,6 +1657,207 @@ function createScopedClientContext(rpc, namespace) {
1675
1657
  };
1676
1658
  }
1677
1659
  //#endregion
1660
+ //#region src/adapters/mcp/to-json-schema.ts
1661
+ const FALLBACK_OBJECT_SCHEMA = Object.freeze({
1662
+ type: "object",
1663
+ additionalProperties: true
1664
+ });
1665
+ /**
1666
+ * Convert a Standard Schema to JSON Schema for the agent/MCP surface.
1667
+ *
1668
+ * Devframe stays validator-neutral, so conversion uses the schema's own
1669
+ * [Standard JSON Schema](https://standardschema.dev/) converter
1670
+ * (`~standard.jsonSchema`) when the validator provides one; zod 4 does,
1671
+ * for example. Validators without a native converter (e.g. valibot) degrade
1672
+ * to a permissive object schema rather than pulling in a converter library.
1673
+ */
1674
+ function safeToJsonSchema(schema) {
1675
+ const standard = schema["~standard"];
1676
+ if (standard.jsonSchema) try {
1677
+ return standard.jsonSchema.input({ target: "draft-2020-12" });
1678
+ } catch {
1679
+ return FALLBACK_OBJECT_SCHEMA;
1680
+ }
1681
+ return FALLBACK_OBJECT_SCHEMA;
1682
+ }
1683
+ /**
1684
+ * JSON Schema for an RPC function's positional args on the agent/MCP
1685
+ * surface. Each positional arg is advertised under `arg0` / `arg1` / …,
1686
+ * matching how the agent bridge coerces the incoming object payload back
1687
+ * into positional arguments.
1688
+ *
1689
+ * Returns `{ type: 'object', properties: {} }` when there are no args.
1690
+ * @internal
1691
+ */
1692
+ function argsToJsonSchema(args) {
1693
+ if (!args || args.length === 0) return {
1694
+ type: "object",
1695
+ properties: {}
1696
+ };
1697
+ const properties = {};
1698
+ const required = [];
1699
+ for (let i = 0; i < args.length; i++) {
1700
+ const key = `arg${i}`;
1701
+ properties[key] = safeToJsonSchema(args[i]);
1702
+ required.push(key);
1703
+ }
1704
+ return {
1705
+ type: "object",
1706
+ properties,
1707
+ required,
1708
+ additionalProperties: false
1709
+ };
1710
+ }
1711
+ //#endregion
1712
+ //#region src/node/agent-args.ts
1713
+ /**
1714
+ * Map the args payload an agent surface receives (MCP sends an object
1715
+ * keyed `arg0`/`arg1`/…, matching the schema the adapter advertises) onto
1716
+ * a handler's positional parameters. Shared by the agent host's RPC
1717
+ * bridge and the hub's command-derived tools so the coercion cannot
1718
+ * drift between them.
1719
+ *
1720
+ * - an array passes through as-is
1721
+ * - `null`/`undefined` become a zero-argument call
1722
+ * - with declared schemas, each schema reads its own `argN` key, in order
1723
+ * - without schemas, `arg0`/`arg1`/… keys are collected when present
1724
+ * - an empty object becomes a zero-argument call
1725
+ * - anything else follows the {@link AgentArgsFallback}
1726
+ */
1727
+ function coerceAgentPositionalArgs(args, schemas, fallback = "wrap") {
1728
+ if (Array.isArray(args)) return args;
1729
+ if (args === void 0 || args === null) return [];
1730
+ if (typeof args === "object") {
1731
+ const obj = args;
1732
+ if (schemas && schemas.length) return schemas.map((_, i) => obj[`arg${i}`]);
1733
+ if ("arg0" in obj) {
1734
+ const out = [];
1735
+ let i = 0;
1736
+ while (`arg${i}` in obj) {
1737
+ out.push(obj[`arg${i}`]);
1738
+ i++;
1739
+ }
1740
+ return out;
1741
+ }
1742
+ if (Object.keys(obj).length === 0) return [];
1743
+ }
1744
+ return fallback === "drop" ? [] : [args];
1745
+ }
1746
+ //#endregion
1747
+ //#region src/client/webmcp.ts
1748
+ /**
1749
+ * The page's WebMCP model context, when the browser (or a polyfill)
1750
+ * provides one. Checks `document.modelContext` (current draft) first,
1751
+ * then `navigator.modelContext` (earlier drafts and polyfills).
1752
+ */
1753
+ function resolveWebMcpModelContext() {
1754
+ if (typeof document !== "undefined") {
1755
+ const context = document.modelContext;
1756
+ if (context) return context;
1757
+ }
1758
+ if (typeof navigator !== "undefined") {
1759
+ const context = navigator.modelContext;
1760
+ if (context) return context;
1761
+ }
1762
+ }
1763
+ /**
1764
+ * Mirror the `agent`-flagged RPC functions of a browser-side collector
1765
+ * (`rpc.client`) onto the page's WebMCP model context as callable tools,
1766
+ * using the same `agent` signature, wire names, and `arg0`/`arg1`/… input
1767
+ * schema as the node-side MCP adapter. Functions without an `agent` field
1768
+ * stay unexposed (default-deny), and the tool set follows later
1769
+ * `register`/`update` calls. Returns a dispose that unregisters every tool.
1770
+ */
1771
+ function registerWebMcpTools(clientRpc, options = {}) {
1772
+ const resolved = options.modelContext ?? resolveWebMcpModelContext();
1773
+ if (!resolved) return () => {};
1774
+ const modelContext = resolved;
1775
+ /** Unregister callbacks keyed by definition name. */
1776
+ const registered = /* @__PURE__ */ new Map();
1777
+ /** Wire name → definition name, to detect sanitization collisions. */
1778
+ const wireNames = /* @__PURE__ */ new Map();
1779
+ function register(def, agent) {
1780
+ const name = toAgentToolName(def.name);
1781
+ const owner = wireNames.get(name);
1782
+ if (owner && owner !== def.name) {
1783
+ console.warn(`[devframe] WebMCP tool name "${name}" (from "${def.name}") collides with "${owner}"; keeping the first registration.`);
1784
+ return;
1785
+ }
1786
+ const controller = new AbortController();
1787
+ const result = modelContext.registerTool({
1788
+ name,
1789
+ description: agent.description,
1790
+ inputSchema: argsToJsonSchema(def.args),
1791
+ annotations: {
1792
+ title: agent.title ?? def.name,
1793
+ readOnlyHint: resolveSafety(def, agent) === "read",
1794
+ destructiveHint: resolveSafety(def, agent) === "destructive"
1795
+ },
1796
+ execute: (args) => executeRpcTool(def, clientRpc.context, args)
1797
+ }, { signal: controller.signal });
1798
+ if (result && "then" in result) result.then(() => {}, () => {});
1799
+ wireNames.set(name, def.name);
1800
+ registered.set(def.name, () => {
1801
+ controller.abort();
1802
+ if (result && "unregister" in result && typeof result.unregister === "function") result.unregister();
1803
+ wireNames.delete(name);
1804
+ });
1805
+ }
1806
+ function sync(id) {
1807
+ const names = id ? [id] : [...clientRpc.definitions.keys()];
1808
+ for (const name of names) {
1809
+ registered.get(name)?.();
1810
+ registered.delete(name);
1811
+ const def = clientRpc.definitions.get(name);
1812
+ const agent = def?.agent;
1813
+ if (def && agent) register(def, agent);
1814
+ }
1815
+ }
1816
+ sync();
1817
+ const unsubscribe = clientRpc.onChanged((id) => sync(id));
1818
+ return () => {
1819
+ unsubscribe();
1820
+ for (const unregister of registered.values()) unregister();
1821
+ registered.clear();
1822
+ };
1823
+ }
1824
+ function resolveSafety(def, agent) {
1825
+ if (agent.safety) return agent.safety;
1826
+ const type = def.type ?? "query";
1827
+ return type === "static" || type === "query" ? "read" : "action";
1828
+ }
1829
+ async function executeRpcTool(def, context, args) {
1830
+ try {
1831
+ const positional = coerceAgentPositionalArgs(args, def.args, "wrap");
1832
+ return { content: [{
1833
+ type: "text",
1834
+ text: stringifyResult(await (await getRpcHandler(def, context))(...positional))
1835
+ }] };
1836
+ } catch (error) {
1837
+ return {
1838
+ isError: true,
1839
+ content: [{
1840
+ type: "text",
1841
+ text: formatError(error)
1842
+ }]
1843
+ };
1844
+ }
1845
+ }
1846
+ /**
1847
+ * Agent-exposed functions are strict-JSON by contract (`jsonSerializable:
1848
+ * true` is enforced at registration), so plain `JSON.stringify` suffices.
1849
+ */
1850
+ function stringifyResult(value) {
1851
+ if (value === void 0) return "undefined";
1852
+ if (typeof value === "string") return value;
1853
+ return JSON.stringify(value, null, 2);
1854
+ }
1855
+ function formatError(error) {
1856
+ if (!(error instanceof Error)) return String(error);
1857
+ const cause = error.cause instanceof Error ? ` (cause: ${error.cause.message})` : "";
1858
+ return `${error.name}: ${error.message}${cause}`;
1859
+ }
1860
+ //#endregion
1678
1861
  //#region src/client/rpc.ts
1679
1862
  /**
1680
1863
  * Resolve the requested `transport` option against what the server
@@ -1716,6 +1899,7 @@ async function getDevframeRpcClient(options = {}) {
1716
1899
  });
1717
1900
  const context = { rpc: void 0 };
1718
1901
  const clientRpc = new RpcFunctionsCollectorBase(context);
1902
+ const disposeWebMcp = options.webmcp === false ? void 0 : registerWebMcpTools(clientRpc);
1719
1903
  async function fetchJsonFromBases(path) {
1720
1904
  const candidates = [resolvedBaseURL, ...bases.filter((base) => base !== resolvedBaseURL)].filter((x) => x != null);
1721
1905
  const errors = [];
@@ -1812,6 +1996,7 @@ async function getDevframeRpcClient(options = {}) {
1812
1996
  } catch {}
1813
1997
  return true;
1814
1998
  },
1999
+ requestAuthCode: (options) => mode.requestAuthCode(options),
1815
2000
  call: gateOnBootstrapAuth(mode.call),
1816
2001
  callEvent: gateOnBootstrapAuth(mode.callEvent),
1817
2002
  callOptional: gateOnBootstrapAuth(mode.callOptional),
@@ -1821,7 +2006,10 @@ async function getDevframeRpcClient(options = {}) {
1821
2006
  streaming: void 0,
1822
2007
  cacheManager,
1823
2008
  scope: void 0,
1824
- close: () => mode.close?.()
2009
+ close: () => {
2010
+ disposeWebMcp?.();
2011
+ mode.close?.();
2012
+ }
1825
2013
  };
1826
2014
  rpc.sharedState = createRpcSharedStateClientHost(rpc);
1827
2015
  rpc.streaming = createRpcStreamingClientHost(rpc);
@@ -1847,6 +2035,7 @@ async function getDevframeRpcClient(options = {}) {
1847
2035
  async function runSimpleAuthPrompt() {
1848
2036
  if (options.simpleAuth === false || !isTopLevelUnframed()) return;
1849
2037
  if (typeof globalThis.prompt !== "function") return;
2038
+ await rpc.requestAuthCode().catch(() => {});
1850
2039
  while (!rpc.isTrusted) {
1851
2040
  const code = globalThis.prompt("devframe: enter the authentication code shown in your terminal");
1852
2041
  if (code == null) return;
@@ -1876,4 +2065,4 @@ async function getDevframeRpcClient(options = {}) {
1876
2065
  //#region src/client/index.ts
1877
2066
  const connectDevframe = getDevframeRpcClient;
1878
2067
  //#endregion
1879
- export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, resolveClientTransport, resolveSseUrl, resolveWsUrl, setupDevframeConnection };
2068
+ export { DevframeConnectionError, authenticateWithUrlOtp, connectDevframe, consumeOtpFromUrl, createClientSettings, createRpcStreamingClientHost, createScopedClientContext, getDevframeConnection, getDevframeRpcClient, isCallableStatus, readOtpFromUrl, registerDevframeViewerOrigin, registerWebMcpTools, resolveClientTransport, resolveSseUrl, resolveWebMcpModelContext, resolveWsUrl, setupDevframeConnection };
@@ -22,7 +22,7 @@
22
22
  * keys (a literal is unavoidable in a type position); those declarations mirror
23
23
  * this map and move with it.
24
24
  */
25
- declare const DEVFRAME_EVENTS: {
25
+ export declare const DEVFRAME_EVENTS: {
26
26
  /**
27
27
  * Node-side host `EventEmitter` events. The agent host (`ctx.agent.events`)
28
28
  * emits these as its tool/resource surface changes; protocol adapters (e.g.
@@ -78,13 +78,13 @@ declare const DEVFRAME_EVENTS: {
78
78
  //#endregion
79
79
  //#region src/constants.d.ts
80
80
  /** Devframe runtime routes and static output conventions. */
81
- declare const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
81
+ export declare const DEVFRAME_CONNECTION_META_FILENAME = "__connection.json";
82
82
  /**
83
83
  * Global key holding the serializable connection prepared by
84
84
  * `setupDevframeConnection()`. External viewers can use this key to read the
85
85
  * connection from another JavaScript realm.
86
86
  */
87
- declare const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
87
+ export declare const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
88
88
  /**
89
89
  * Route the WebSocket RPC endpoint is bound to, relative to a devframe's
90
90
  * base path. Sits next to `__connection.json` so the deployed SPA can reach
@@ -92,7 +92,7 @@ declare const DEVFRAME_CONNECTION_KEY = "__DEVFRAME_CONNECTION__";
92
92
  * both HTTP and WS, and a host server (Vite, etc.) can mount the WS upgrade
93
93
  * handler here without colliding with its own routes (HMR, asset serving).
94
94
  */
95
- declare const DEVFRAME_WS_ROUTE = "__ws";
95
+ export declare const DEVFRAME_WS_ROUTE = "__ws";
96
96
  /**
97
97
  * Route the SSE RPC endpoint is bound to, relative to a devframe's base
98
98
  * path. A single method-dispatched route: `GET` opens the event stream
@@ -101,36 +101,36 @@ declare const DEVFRAME_WS_ROUTE = "__ws";
101
101
  * loaded from; the transport for hosts and proxies where the WebSocket
102
102
  * upgrade isn't available.
103
103
  */
104
- declare const DEVFRAME_SSE_ROUTE = "__sse";
104
+ export declare const DEVFRAME_SSE_ROUTE = "__sse";
105
105
  /**
106
106
  * Request header carrying the SSE session id on every RPC `POST`. The
107
107
  * server mints the id as the stream's first event (`event: session`);
108
108
  * the client echoes it here so the POST joins that stream's birpc
109
109
  * channel. Mirrors birpc's own SSE helpers for wire compatibility.
110
110
  */
111
- declare const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
111
+ export declare const DEVFRAME_SSE_SESSION_HEADER = "x-birpc-session";
112
112
  /**
113
113
  * Route the Streamable-HTTP MCP endpoint is bound to, relative to a
114
114
  * devframe's base path. Sits next to `__connection.json` and the WS route
115
115
  * so an MCP client reaches it on the same origin the SPA loaded from; the
116
116
  * dev server shares one port for HTTP, WS, and MCP. Opt-in via `cli.mcp`.
117
117
  */
118
- declare const DEVFRAME_MCP_ROUTE = "__mcp";
119
- declare const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
120
- declare const DEVFRAME_DOCK_IMPORTS_FILENAME = "__client-imports.js";
121
- declare const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
118
+ export declare const DEVFRAME_MCP_ROUTE = "__mcp";
119
+ export declare const DEVFRAME_RPC_DUMP_MANIFEST_FILENAME = "__rpc-dump/index.json";
120
+ export declare const DEVFRAME_DOCK_IMPORTS_FILENAME = "__client-imports.js";
121
+ export declare const DEVFRAME_RPC_DUMP_DIRNAME = "__rpc-dump";
122
122
  /**
123
123
  * Shared-state key carrying the wire-service advertisements (package name →
124
124
  * `DevframeServiceMeta`). Written by the node services host; mirrored to
125
125
  * clients for feature-detection via `client.services`.
126
126
  */
127
- declare const DEVFRAME_SERVICES_STATE_KEY = "devframe:services";
127
+ export declare const DEVFRAME_SERVICES_STATE_KEY = "devframe:services";
128
128
  /**
129
129
  * URL fragment / query parameter name carrying the remote dock
130
130
  * connection descriptor (defined as `RemoteConnectionInfo` in
131
131
  * `@vitejs/devtools-kit`) injected into remote-UI iframe dock URLs.
132
132
  */
133
- declare const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
133
+ export declare const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
134
134
  /**
135
135
  * Page-URL **fragment** parameter carrying a one-time authentication code (OTP)
136
136
  * for "magic link" auth. A host can print a link like
@@ -141,7 +141,7 @@ declare const REMOTE_CONNECTION_KEY = "devframe-remote-connection";
141
141
  * (node) and the `authenticateWithUrlOtp` / `consumeOtpFromUrl` client utilities
142
142
  * (or `connectDevframe`'s `otpParam`).
143
143
  */
144
- declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
144
+ export declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
145
145
  /**
146
146
  * WS upgrade-URL query parameter carrying a previously-issued bearer token.
147
147
  * Set by `createWsRpcChannel` (browser transport) whenever `authToken` is
@@ -149,11 +149,11 @@ declare const DEVFRAME_OTP_URL_PARAM = "devframe_otp";
149
149
  * `recipes/interactive-auth`'s `onConnect`) so a returning client can be
150
150
  * trusted before its own `anonymous:devframe:auth` handshake call arrives.
151
151
  */
152
- declare const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
152
+ export declare const DEVFRAME_AUTH_TOKEN_QUERY_PARAM = "devframe_auth_token";
153
153
  /** External viewer origin requested during connection bootstrap. */
154
- declare const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
154
+ export declare const DEVFRAME_VIEWER_ORIGIN_QUERY_PARAM = "devframe_viewer_origin";
155
155
  /** Token that authorizes an external viewer origin registration. */
156
- declare const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
156
+ export declare const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin_token";
157
157
  /**
158
158
  * `postMessage` type the remote-assets fallback page posts to `window.parent`
159
159
  * on load. That page is what a devframe serves (with a 502) when its client
@@ -162,7 +162,7 @@ declare const DEVFRAME_VIEWER_ORIGIN_TOKEN_QUERY_PARAM = "devframe_viewer_origin
162
162
  * (`@devframes/hub-ui` does, in its iframe view). Payload shape:
163
163
  * `RemoteAssetsErrorMessage` (`devframe/types`).
164
164
  */
165
- declare const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE: string;
165
+ export declare const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE: string;
166
166
  /**
167
167
  * Prefix that marks an RPC method as callable before a connection is
168
168
  * trusted. This is the *only* rule the pre-trust gate applies; there is no
@@ -170,13 +170,12 @@ declare const DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE: string;
170
170
  * before authentication must be named `anonymous:<rest>` (e.g.
171
171
  * `anonymous:devframe:auth`).
172
172
  */
173
- declare const ANONYMOUS_RPC_PREFIX = "anonymous:";
173
+ export declare const ANONYMOUS_RPC_PREFIX = "anonymous:";
174
174
  /**
175
175
  * Whether `name` is callable before a connection is trusted, i.e. it starts
176
176
  * with {@link ANONYMOUS_RPC_PREFIX}. Used by the resolver gate in
177
177
  * the RPC server binding (via an `authorize` function) and by host adapters that
178
178
  * implement their own transport.
179
179
  */
180
- declare function isAnonymousRpcMethod(name: string): boolean;
181
- //#endregion
182
- export { ANONYMOUS_RPC_PREFIX, DEVFRAME_AUTH_TOKEN_QUERY_PARAM, DEVFRAME_CONNECTION_KEY, DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_EVENTS, DEVFRAME_MCP_ROUTE, DEVFRAME_OTP_URL_PARAM, DEVFRAME_REMOTE_ASSETS_ERROR_MESSAGE_TYPE, DEVFRAME_RPC_DUMP_DIRNAME, DEVFRAME_RPC_DUMP_MANIFEST_FILENAME, DEVFRAME_SERVICES_STATE_KEY, 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 };
180
+ export declare function isAnonymousRpcMethod(name: string): boolean;
181
+ //#endregion
@@ -335,8 +335,9 @@ declare function parseCliFlags(schema: CliFlagsSchema, raw: Record<string, unkno
335
335
  interface DevframeAuthHandler {
336
336
  /**
337
337
  * `anonymous:devframe:auth` + `anonymous:devframe:auth:exchange` (the
338
- * handshake) and `devframe:auth:revoke` (self-revoke); register these on
339
- * the RPC host (e.g. `rpcHost.register(fn)` for each).
338
+ * handshake), `anonymous:devframe:auth:request-code` (client-requested
339
+ * banner print), and `devframe:auth:revoke` (self-revoke); register these
340
+ * on the RPC host (e.g. `rpcHost.register(fn)` for each).
340
341
  */
341
342
  rpcFunctions: RpcFunctionDefinitionAny[];
342
343
  /**
@@ -354,9 +355,11 @@ interface DevframeAuthHandler {
354
355
  */
355
356
  onConnect: (connection: DevframeRpcConnection, session: DevframeNodeRpcSession) => void;
356
357
  /**
357
- * Print the current one-time code and its magic-link URL. Devframe stays
358
- * headless, so call this yourself once the server is listening. Safe to
359
- * call repeatedly; it only prints once per code.
358
+ * Print the current one-time code and its magic-link URL. An untrusted
359
+ * browser client triggers this itself over
360
+ * `anonymous:devframe:auth:request-code`; call it directly when the host
361
+ * wants the code on screen without waiting for a client. Safe to call
362
+ * repeatedly; it only prints once per code.
360
363
  */
361
364
  printBanner: () => void;
362
365
  /**
@@ -589,6 +592,23 @@ interface DevframeRpcServerFunctions {
589
592
  }) => Promise<{
590
593
  authToken: string | null;
591
594
  }>;
595
+ /**
596
+ * Ask the server to print its auth banner (code + magic link) for this
597
+ * client, e.g. when an auth UI first shows; `reissue: true` rotates the code
598
+ * first (the manual "re-issue" action). Registered by
599
+ * `recipes/interactive-auth`; a repeat request for an already-printed code
600
+ * is a no-op.
601
+ *
602
+ * Named with the `anonymous:` prefix (see `isAnonymousRpcMethod`) so it is
603
+ * reachable before the connection is trusted.
604
+ *
605
+ * @internal
606
+ */
607
+ 'anonymous:devframe:auth:request-code': (params: {
608
+ ua: string;
609
+ origin: string;
610
+ reissue?: boolean;
611
+ }) => Promise<void>;
592
612
  /**
593
613
  * Self-revoke: the caller asks the server to revoke its own bearer token
594
614
  * (if any) and drop to untrusted. Requires an already-trusted caller, so
@@ -1,4 +1,4 @@
1
- import { k as SharedState, o as DevframeNodeContext } from "./context-BBWPLDHh.mjs";
1
+ import { k as SharedState, o as DevframeNodeContext } from "./context-B7411Bem.mjs";
2
2
  //#region src/node/hub-internals/context.d.ts
3
3
  interface InternalAnonymousAuthStorage {
4
4
  trusted: Record<string, {
@@ -143,7 +143,11 @@ function createRpcSharedStateServerHost(rpc) {
143
143
  name: "devframe:rpc:server-state:set",
144
144
  type: "query",
145
145
  handler: async (key, value, syncId) => {
146
- (await host.get(key, { initialValue: value })).mutate(() => value, syncId);
146
+ (await host.get(key, { initialValue: value })).patch([{
147
+ op: "replace",
148
+ path: [],
149
+ value
150
+ }], syncId);
147
151
  }
148
152
  });
149
153
  rpc.register({
@@ -192,7 +196,10 @@ function createStreamSink(options = {}) {
192
196
  seq: lastSeq,
193
197
  chunk
194
198
  });
195
- if (buffer.length > replayWindow) buffer.splice(0, buffer.length - replayWindow);
199
+ if (buffer.length > replayWindow) {
200
+ if (buffer.length - replayWindow === 1) buffer.shift();
201
+ else buffer.splice(0, buffer.length - replayWindow);
202
+ }
196
203
  }
197
204
  events.emit("chunk", lastSeq, chunk);
198
205
  }
@@ -1,11 +1,11 @@
1
1
  import { t as resolveClientAssets } from "./client-assets-DNdhagGm.mjs";
2
2
  import { DEVFRAME_CONNECTION_META_FILENAME } from "./constants.mjs";
3
3
  import { t as diagnostics } from "./diagnostics-DZA6KQ_Z.mjs";
4
- import { t as createHostContext } from "./context-DwGevWqT.mjs";
4
+ import { t as createHostContext } from "./context-Cdj9z9m2.mjs";
5
5
  import { t as resolveStaticAssetsSource } from "./remote-assets-DMFzLSoH.mjs";
6
6
  import { a as resolveMcpConfig, i as resolveDevServerPort, n as normalizeBasePath, r as resolveBasePath, s as importRuntimeModule, t as loadAutoMcpAdapter } from "./_shared-U8D-xzrp.mjs";
7
7
  import { t as createH3DevframeHost } from "./host-h3-BROI6SQ4.mjs";
8
- import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-xuu6jg51.mjs";
8
+ import { i as normalizeHttpServerUrl, n as resolveInstanceRegister, t as createInstanceShell } from "./instance-shell-370Oee9K.mjs";
9
9
  import { open } from "./utils/open.mjs";
10
10
  import { mountStaticHandler } from "./utils/serve-static.mjs";
11
11
  import { resolve } from "pathe";