devframe 0.8.2 → 0.9.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/_shared-BPmX9fqK.d.mts +41 -0
  2. package/dist/_shared-CbZ_yhbz.mjs +68 -0
  3. package/dist/adapters/build.d.mts +10 -12
  4. package/dist/adapters/build.mjs +26 -29
  5. package/dist/adapters/cac.d.mts +1 -1
  6. package/dist/adapters/cac.mjs +156 -1
  7. package/dist/adapters/dev.d.mts +38 -46
  8. package/dist/adapters/dev.mjs +2 -1
  9. package/dist/adapters/embedded.d.mts +1 -1
  10. package/dist/adapters/initiate.d.mts +224 -0
  11. package/dist/adapters/initiate.mjs +2 -0
  12. package/dist/adapters/mcp.d.mts +24 -7
  13. package/dist/adapters/mcp.mjs +2 -2
  14. package/dist/{agent-tool-name-C3b5vEwJ.mjs → agent-tool-name-EgfoFO8C.mjs} +0 -3
  15. package/dist/cli/main.d.mts +0 -2
  16. package/dist/cli/main.mjs +4 -8
  17. package/dist/client/index.d.mts +71 -7
  18. package/dist/client/index.mjs +720 -1540
  19. package/dist/constants.d.mts +28 -7
  20. package/dist/constants.mjs +28 -7
  21. package/dist/{context-DoSozfvx.d.mts → context-B3YUu_95.d.mts} +19 -1
  22. package/dist/{host-h3-D2hYZvDK.mjs → context-CEe4PBz1.mjs} +25 -557
  23. package/dist/{context-CBg8iRbQ.mjs → context-xdynGSLP.mjs} +10 -1
  24. package/dist/dev--rAPzbvO.mjs +239 -0
  25. package/dist/{devframe-mQ4qZAuo.d.mts → devframe-DJMg0WLe.d.mts} +181 -134
  26. package/dist/{diagnostics-hwjXp_UV.mjs → diagnostics-CD8nlgll.mjs} +1 -3
  27. package/dist/{diagnostics-D7wM2cwM.mjs → diagnostics-DOQPnQmX.mjs} +51 -5
  28. package/dist/dist-eNI9qM_I.mjs +228 -0
  29. package/dist/host-agent-xLCSe65D.mjs +264 -0
  30. package/dist/host-h3-Crkzm42q.mjs +26 -0
  31. package/dist/{fetch-BKaD0Vb3.mjs → http-DYpK0cUi.mjs} +42 -11
  32. package/dist/{index-b64Uuhy2.d.mts → index-CB-fSUyn.d.mts} +2 -2
  33. package/dist/{index-Dbw1p5ch.d.mts → index-tdrk1bzV.d.mts} +3 -32
  34. package/dist/index.d.mts +12 -5
  35. package/dist/index.mjs +8 -1
  36. package/dist/{instance-registry-BnnA2Eoh.mjs → instance-registry-WvQkt42E.mjs} +9 -8
  37. package/dist/instance-shell-CMFyszWJ.mjs +486 -0
  38. package/dist/instance-shell-CljiKq1c.d.mts +255 -0
  39. package/dist/internal/index.d.mts +397 -0
  40. package/dist/internal/index.mjs +9 -0
  41. package/dist/node/auth.d.mts +2 -2
  42. package/dist/node/auth.mjs +1 -1
  43. package/dist/node/hub-internals.d.mts +2 -14
  44. package/dist/node/hub-internals.mjs +2 -2
  45. package/dist/node/index.d.mts +4 -268
  46. package/dist/node/index.mjs +3 -5
  47. package/dist/nostics-CzECRXpE.mjs +59 -0
  48. package/dist/nostics-SPGDgHEP.d.mts +33 -0
  49. package/dist/recipes/common-rpc-functions.d.mts +4 -4
  50. package/dist/recipes/interactive-auth.d.mts +3 -3
  51. package/dist/recipes/interactive-auth.mjs +8 -8
  52. package/dist/remote-assets-Bg4gCUZ_.d.mts +123 -0
  53. package/dist/remote-assets-nTTxY2MU.mjs +636 -0
  54. package/dist/rolldown-runtime-JspESFgx.mjs +35 -0
  55. package/dist/rpc/dump.d.mts +77 -1
  56. package/dist/rpc/dump.mjs +1 -1
  57. package/dist/rpc/index.d.mts +3 -3
  58. package/dist/rpc/index.mjs +4 -18
  59. package/dist/rpc/transports/sse-client.d.mts +2 -0
  60. package/dist/rpc/transports/sse-client.mjs +142 -0
  61. package/dist/rpc/transports/sse-server.d.mts +61 -0
  62. package/dist/rpc/transports/sse-server.mjs +209 -0
  63. package/dist/rpc/transports/ws-bun.d.mts +35 -0
  64. package/dist/rpc/transports/ws-bun.mjs +31 -0
  65. package/dist/rpc/transports/ws-client.d.mts +1 -1
  66. package/dist/rpc/transports/ws-client.mjs +10 -18
  67. package/dist/rpc/transports/ws-server.d.mts +2 -2
  68. package/dist/rpc/transports/ws-server.mjs +2 -235
  69. package/dist/rpc-core-Cc4lucI0.d.mts +48 -0
  70. package/dist/rpc-core-DMTe-hLo.mjs +81 -0
  71. package/dist/{serialization-C8Mnw9hK.mjs → serialization-BGzEwAdr.mjs} +2 -2
  72. package/dist/{shared-state-6kHolYZy.mjs → shared-state-fRRbRUtD.mjs} +1 -1
  73. package/dist/sse-client-CFBOIaUi.d.mts +37 -0
  74. package/dist/state-Cc1IeSmQ.mjs +1291 -0
  75. package/dist/{dump-CgZShDRB.mjs → static-BPZ1_OPA.mjs} +3 -266
  76. package/dist/storage-CYye7P7L.mjs +137 -0
  77. package/dist/types/index.d.mts +6 -4
  78. package/dist/types/index.mjs +1 -6
  79. package/dist/{types-CnJSgRVa.d.mts → types-Djq7CBXh.d.mts} +0 -7
  80. package/dist/utils/agent-tool-name.d.mts +0 -3
  81. package/dist/utils/agent-tool-name.mjs +0 -3
  82. package/dist/utils/events.d.mts +1 -1
  83. package/dist/utils/get-port.d.mts +17 -0
  84. package/dist/utils/get-port.mjs +2 -0
  85. package/dist/utils/launch-editor.mjs +1 -25
  86. package/dist/utils/nostics.d.mts +2 -0
  87. package/dist/utils/nostics.mjs +2 -0
  88. package/dist/utils/open.d.mts +2 -10
  89. package/dist/utils/open.mjs +56 -1
  90. package/dist/utils/remote-assets.d.mts +15 -0
  91. package/dist/utils/remote-assets.mjs +2 -0
  92. package/dist/utils/serve-static.d.mts +13 -5
  93. package/dist/utils/serve-static.mjs +119 -9
  94. package/dist/utils/shared-state.d.mts +1 -1
  95. package/dist/utils/shared-state.mjs +1 -1
  96. package/dist/utils/simple-schema.d.mts +101 -1
  97. package/dist/utils/streaming-channel.d.mts +1 -1
  98. package/dist/validation-CpXFB6Dz.mjs +266 -0
  99. package/dist/wire-codec-0K-o5MYW.mjs +56 -0
  100. package/dist/{ws-client-B6Tnr0ic.d.mts → ws-client-DhjNyQo3.d.mts} +7 -2
  101. package/dist/ws-server-BdSLrhxE.mjs +267 -0
  102. package/dist/{ws-server-uKQcClcJ.d.mts → ws-server-D1d3QM9f.d.mts} +90 -9
  103. package/package.json +13 -12
  104. package/skills/devframe/SKILL.md +91 -93
  105. package/dist/_shared-bWRzeSa0.mjs +0 -19
  106. package/dist/adapters/cli.d.mts +0 -11
  107. package/dist/adapters/cli.mjs +0 -6
  108. package/dist/cac-D4_9ok0z.mjs +0 -157
  109. package/dist/dev-Bc49u3qg.mjs +0 -439
  110. package/dist/diagnostics-reporter-CsIG85Q5.mjs +0 -38
  111. package/dist/helpers/vite.d.mts +0 -100
  112. package/dist/helpers/vite.mjs +0 -105
  113. package/dist/http-C4T68YV6.mjs +0 -38
  114. package/dist/index-vAX6bYuc.d.mts +0 -78
  115. package/dist/open-Deb5xmIT.mjs +0 -547
  116. package/dist/recipes/open-helpers.d.mts +0 -6
  117. package/dist/recipes/open-helpers.mjs +0 -6
  118. package/dist/server-CD8XmBXl.mjs +0 -167
  119. package/dist/server-UHWae43Q.d.mts +0 -149
  120. package/dist/simple-schema-BDzLeJDk.d.mts +0 -102
  121. package/dist/state-BeUHDDjk.mjs +0 -108
  122. package/dist/storage-CTNVbuFB.mjs +0 -1122
  123. package/dist/utils/promise.d.mts +0 -8
  124. package/dist/utils/promise.mjs +0 -15
  125. package/dist/utils/scope.d.mts +0 -11
  126. package/dist/utils/scope.mjs +0 -15
  127. package/skills/devframe/templates/spa-devframe.ts +0 -38
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  name: devframe
3
3
  description: >
4
- Use when building a devtool with devframe — the
4
+ Use when building a devtool with devframe - the
5
5
  framework- and build-tool-agnostic foundation for defining a
6
6
  devtool once and serving it in many places. Covers
7
7
  DevframeDefinition, picking the right deployment adapter
8
- (cli / build / spa / vite / embedded / mcp), designing RPC
8
+ (cli / build / vite / embedded / mcp), designing RPC
9
9
  contracts, exposing an agent-native surface over MCP, and
10
10
  wiring the author's SPA client. For host-level features (docks,
11
11
  terminals, palette, etc.), the devframe can be mounted into a
12
- host that provides them — Vite DevTools is one supported target,
12
+ host that provides them - Vite DevTools is one supported target,
13
13
  reached via the `vite` adapter. Triggers on `devframe` imports,
14
14
  `defineDevframe`, `createCac`, `createMcpServer`,
15
15
  `connectDevframe`, and on migrations of existing inspectors
@@ -19,9 +19,9 @@ description: >
19
19
 
20
20
  # devframe skill
21
21
 
22
- **Devframe is an asset: define your devtool once, serve it anywhere.** A devtool built on devframe is a single `DevframeDefinition` plus an author-provided SPA — the same definition deploys through a set of pluggable adapters (standalone CLI, static report, embedded SPA, MCP server, mounted into a host, etc.). Devframe is framework- and build-tool-agnostic; it has no Vite dependency and makes no UI-framework assumption.
22
+ **Devframe is an asset: define your devtool once, serve it anywhere.** A devtool built on devframe is a single `DevframeDefinition` plus an author-provided SPA - the same definition deploys through a set of pluggable adapters (standalone CLI, static report, embedded SPA, MCP server, mounted into a host, etc.). Devframe is framework- and build-tool-agnostic; it has no Vite dependency and makes no UI-framework assumption.
23
23
 
24
- Devframe describes one tool. If you need host-level features (cross-tool palette, integrated terminals, dock aggregation), mount the devframe into a host that provides them — [Vite DevTools](https://devtools.vite.dev/) is the canonical example, reached via the `vite` adapter — or build your own host adapter. `devframe` itself must not depend on Vite or any `@vitejs/*` package.
24
+ Devframe describes one tool. If you need host-level features (cross-tool palette, integrated terminals, dock aggregation), mount the devframe into a host that provides them - [Vite DevTools](https://devtools.vite.dev/) is the canonical example, reached via the `vite` adapter - or build your own host adapter. `devframe` itself must not depend on Vite or any `@vitejs/*` package.
25
25
 
26
26
  Full reference: [devfra.me/](https://devfra.me/).
27
27
 
@@ -36,11 +36,11 @@ All adapter factories share the shape `createXxx(devframeDef, options?)`.
36
36
  | Self-contained static deploy with baked data | `createBuild(def, options?)` | `devframe/adapters/build` |
37
37
  | Mount into a host (Vite DevTools or any compatible host) | `createPluginFromDevframe(def, options?)` | `@vitejs/devtools-kit/node` |
38
38
  | Register dynamically at runtime | `createEmbedded(def, { ctx })` | `devframe/adapters/embedded` |
39
- | Expose to coding agents (MCP) | `createMcpServer(def, options?)` | `devframe/adapters/mcp` *(experimental)* |
39
+ | Expose to coding agents (MCP) | `createMcpServer(def, options?)` | `devframe/adapters/mcp` |
40
40
 
41
- The same `DevframeDefinition` runs under every adapter — pick based on deployment, not on what the tool does.
41
+ The same `DevframeDefinition` runs under every adapter - pick based on deployment, not on what the tool does.
42
42
 
43
- For Vite-based hosts that don't use the kit (Nuxt, Astro, SolidStart, plain Vite apps), `devframe/helpers/vite` exports `viteDevBridge(def, options?)` — a Vite plugin that mounts the SPA (static mode) or starts the RPC + WS bridge alongside the host's dev server (`devMiddleware: true`). Not an adapter; just a Vite integration helper.
43
+ For Vite-based hosts that don't use the kit (Nuxt, Astro, SolidStart, plain Vite apps), `@devframes/vite` exports `devframeVitePlugin(def, options?)` (static SPA mount) and `devframeViteBridge(def, options?)` (RPC + WS bridge alongside the host's dev server) - plus `devframeVite(def, { bridge, ...options })` as a convenience wrapper over both. Not an adapter; just a Vite integration helper.
44
44
 
45
45
  ## Minimum viable devframe
46
46
 
@@ -57,7 +57,7 @@ export default defineDevframe({
57
57
  icon: 'ph:magnifying-glass-duotone',
58
58
  cli: { distDir: './client/dist' },
59
59
  setup(ctx) {
60
- const my = ctx.scope('my-inspector') // preferred — auto-namespaces ids
60
+ const my = ctx.scope('my-inspector') // preferred - auto-namespaces ids
61
61
  my.rpc.register(defineRpcFunction({
62
62
  name: 'get-stats', // stored as `my-inspector:get-stats`
63
63
  type: 'static',
@@ -83,28 +83,28 @@ export default defineDevframe({
83
83
  })
84
84
  ```
85
85
 
86
- `setup(ctx)` registers RPC functions, shared state, diagnostics, and any other devframe-level wiring. Host adapters can augment `ctx` with extra surfaces — for example, mounting into Vite DevTools via `createPluginFromDevframe(d)` exposes `docks`, `terminals`, `messages`, and `commands` on the augmented context, and the kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`. For richer host-side behaviour (custom-render, terminals, palette commands) pass `options.setup` to `createPluginFromDevframe`.
86
+ `setup(ctx)` registers RPC functions, shared state, diagnostics, and any other devframe-level wiring. Host adapters can augment `ctx` with extra surfaces - for example, mounting into Vite DevTools via `createPluginFromDevframe(d)` exposes `docks`, `terminals`, `messages`, and `commands` on the augmented context, and the kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`. For richer host-side behaviour (custom-render, terminals, palette commands) pass `options.setup` to `createPluginFromDevframe`.
87
87
 
88
- See `templates/counter-devframe.ts` for a runnable counter example, `templates/spa-devframe.ts` for an SPA-ready shape, and `templates/vite-client.ts` for the author's client entry.
88
+ See `templates/counter-devframe.ts` for a runnable counter example and `templates/vite-client.ts` for the author's client entry.
89
89
 
90
90
  ## Scoped context (preferred)
91
91
 
92
- `ctx.scope(id)` (server) and `client.scope(id)` (browser) return a namespace-scoped view that auto-prefixes every RPC id, shared-state key, and streaming channel with `id:`, and adds a top-level persisted `settings` store. Prefer it over the raw `ctx.rpc` / client — you name the namespace once and register / call by bare name.
92
+ `ctx.scope(id)` (server) and `client.scope(id)` (browser) return a namespace-scoped view that auto-prefixes every RPC id, shared-state key, and streaming channel with `id:`, and adds a top-level persisted `settings` store. Prefer it over the raw `ctx.rpc` / client - you name the namespace once and register / call by bare name.
93
93
 
94
94
  ```ts
95
- // server — setup(ctx)
95
+ // server - setup(ctx)
96
96
  const my = ctx.scope('my-inspector')
97
97
  my.rpc.register(getStats) // -> my-inspector:get-stats
98
98
  await my.rpc.call('get-stats') // invokeLocal, namespaced
99
99
  const state = await my.rpc.sharedState('view') // -> my-inspector:view
100
100
  await my.settings.project.set('theme', 'dark')
101
101
 
102
- // browser — connectDevframe()
102
+ // browser - connectDevframe()
103
103
  const my = (await connectDevframe()).scope('my-inspector')
104
104
  const stats = await my.rpc.call('get-stats')
105
105
  ```
106
106
 
107
- - **Auto-namespacing.** Bare names get `id:` prepended; a name already containing `:` is treated as fully-qualified and passed through (so `my.rpc.call('other-tool:fn')` works). `register` only accepts bare names — passing a namespaced one throws `DF0034`.
107
+ - **Auto-namespacing.** Bare names get `id:` prepended; a name already containing `:` is treated as fully-qualified and passed through (so `my.rpc.call('other-tool:fn')` works). `register` only accepts bare names - passing a namespaced one throws `DF0034`.
108
108
  - **Typed bare calls.** Define functions with bare names and augment the registry with `RpcDefinitionsToFunctionsWithNamespace<'my-inspector', typeof serverFunctions>` so the registry keys match the namespaced runtime ids; scoped `call('get-stats')` then stays typed.
109
109
  - **`base`.** The scoped context keeps the raw context as `my.base` (and re-exposes `views` / `diagnostics` / `agent` / `host` / `cwd` / `mode` on the server).
110
110
 
@@ -112,8 +112,8 @@ const stats = await my.rpc.call('get-stats')
112
112
 
113
113
  `my.settings` is a persisted key-value store at the **top level** of the scoped context (a sibling of `my.rpc`, not under it). Two scopes:
114
114
 
115
- - `project` — per-workspace, persisted under the host's `workspace` storage dir.
116
- - `global` — per-user, persisted under the host's `global` storage dir.
115
+ - `project` - per-workspace, persisted under the host's `workspace` storage dir.
116
+ - `global` - per-user, persisted under the host's `global` storage dir.
117
117
 
118
118
  Both are file-backed on the server and synced to clients over the shared-state protocol, so a `set` on either side propagates everywhere and survives restarts. All methods are async.
119
119
 
@@ -137,7 +137,7 @@ declare module 'devframe' {
137
137
 
138
138
  ## Project layout
139
139
 
140
- Once a devframe grows past a handful of RPC functions, split them out — one file per function under `src/rpc/functions/`, with `src/rpc/index.ts` as the barrel. The `functions/` subdirectory leaves room for sibling files like `src/rpc/utils.ts` (helpers, type aliases) as the surface grows. Each function file exports a named const with a **bare** name; the barrel collects them into a `const serverFunctions = [...] as const` and feeds the type-safe client registry recipe with the namespace-aware helper `RpcDefinitionsToFunctionsWithNamespace<'my-tool', typeof serverFunctions>` (it prefixes each bare name to match the namespaced runtime id the scope registers).
140
+ Once a devframe grows past a handful of RPC functions, split them out - one file per function under `src/rpc/functions/`, with `src/rpc/index.ts` as the barrel. The `functions/` subdirectory leaves room for sibling files like `src/rpc/utils.ts` (helpers, type aliases) as the surface grows. Each function file exports a named const with a **bare** name; the barrel collects them into a `const serverFunctions = [...] as const` and feeds the type-safe client registry recipe with the namespace-aware helper `RpcDefinitionsToFunctionsWithNamespace<'my-tool', typeof serverFunctions>` (it prefixes each bare name to match the namespaced runtime id the scope registers).
141
141
 
142
142
  ```ts
143
143
  // src/rpc/functions/list-files.ts
@@ -145,7 +145,7 @@ import { defineRpcFunction } from 'devframe'
145
145
  import { getMyToolContext } from '../../context'
146
146
 
147
147
  export const listFiles = defineRpcFunction({
148
- name: 'list-files', // bare — the scope namespaces it to `my-tool:list-files`
148
+ name: 'list-files', // bare - the scope namespaces it to `my-tool:list-files`
149
149
  type: 'query',
150
150
  jsonSerializable: true,
151
151
  setup: (ctx) => {
@@ -170,7 +170,7 @@ declare module 'devframe' {
170
170
 
171
171
  ```ts
172
172
  // src/devframe.ts
173
- import { defineDevframe } from 'devframe/types'
173
+ import { defineDevframe } from 'devframe'
174
174
  import pkg from '../package.json' with { type: 'json' }
175
175
  import { setMyToolContext } from './context'
176
176
  import { serverFunctions } from './rpc'
@@ -190,15 +190,15 @@ export default defineDevframe({
190
190
  })
191
191
  ```
192
192
 
193
- Note `setMyToolContext(ctx, …)` keys off the raw `ctx` (the same object the function `setup(ctx)` receives) — store the per-tool context on `ctx`, register through `my.rpc`.
193
+ Note `setMyToolContext(ctx, …)` keys off the raw `ctx` (the same object the function `setup(ctx)` receives) - store the per-tool context on `ctx`, register through `my.rpc`.
194
194
 
195
195
  ### Sharing setup-time state via `src/context.ts`
196
196
 
197
- When per-file RPCs need access to runtime values that `setup(ctx)` constructs once — streaming channels, shared state handles, watchers, loaders, caches — expose them through a `WeakMap<DevframeNodeContext, T>` in a sibling `src/context.ts`. This mirrors the framework's own `internalContextMap` in `packages/devframe/src/node/hub-internals/context.ts`. The WeakMap keys off the existing `DevframeNodeContext` so contexts are garbage-collected automatically when the host tears down.
197
+ When per-file RPCs need access to runtime values that `setup(ctx)` constructs once - streaming channels, shared state handles, watchers, loaders, caches - expose them through a `WeakMap<DevframeNodeContext, T>` in a sibling `src/context.ts`. This mirrors the framework's own `internalContextMap` in `packages/devframe/src/node/hub-internals/context.ts`. The WeakMap keys off the existing `DevframeNodeContext` so contexts are garbage-collected automatically when the host tears down.
198
198
 
199
199
  ```ts
200
200
  // src/context.ts
201
- import type { DevframeNodeContext } from 'devframe/types'
201
+ import type { DevframeNodeContext } from 'devframe'
202
202
 
203
203
  export interface MyToolContext {
204
204
  loaders: { list: () => Promise<string[]> }
@@ -214,12 +214,12 @@ export function setMyToolContext(ctx: DevframeNodeContext, value: MyToolContext)
214
214
  export function getMyToolContext(ctx: DevframeNodeContext): MyToolContext {
215
215
  const value = map.get(ctx)
216
216
  if (!value)
217
- throw new Error('my-tool context not initialised — call setMyToolContext in devframe.setup')
217
+ throw new Error('my-tool context not initialised - call setMyToolContext in devframe.setup')
218
218
  return value
219
219
  }
220
220
  ```
221
221
 
222
- Stateless RPCs and tiny demos can keep the inline shorthand inside `setup(ctx)` — reach for `src/rpc/functions/` and `src/context.ts` once you have more than one or two functions, or any shared setup state.
222
+ Stateless RPCs and tiny demos can keep the inline shorthand inside `setup(ctx)` - reach for `src/rpc/functions/` and `src/context.ts` once you have more than one or two functions, or any shared setup state.
223
223
 
224
224
  ## Namespacing
225
225
 
@@ -228,10 +228,10 @@ Stateless RPCs and tiny demos can keep the inline shorthand inside `setup(ctx)`
228
228
  ```ts
229
229
  'my-inspector:get-modules' // ✓
230
230
  'my-inspector:state' // ✓
231
- 'get-modules' // ✗ — may collide with other devframes sharing the host
231
+ 'get-modules' // ✗ - may collide with other devframes sharing the host
232
232
  ```
233
233
 
234
- A [scoped context](#scoped-context-preferred) applies this prefix for you — `ctx.scope('my-inspector').rpc.register({ name: 'get-modules' })` registers `my-inspector:get-modules`. Define and call by bare name through the scope; reach for full ids only via `ctx.base` or when targeting another tool. Dock / command IDs are host-level (not part of the scoped `rpc` surface) — prefix those by hand.
234
+ A [scoped context](#scoped-context-preferred) applies this prefix for you - `ctx.scope('my-inspector').rpc.register({ name: 'get-modules' })` registers `my-inspector:get-modules`. Define and call by bare name through the scope; reach for full ids only via `ctx.base` or when targeting another tool. Dock / command IDs are host-level (not part of the scoped `rpc` surface) - prefix those by hand.
235
235
 
236
236
  ## DevframeNodeContext at a glance
237
237
 
@@ -239,15 +239,15 @@ A [scoped context](#scoped-context-preferred) applies this prefix for you — `c
239
239
 
240
240
  | Host | Purpose |
241
241
  |------|---------|
242
- | `ctx.scope(id)` | **Preferred** namespace-scoped view — auto-prefixed `rpc` + top-level `settings` store |
242
+ | `ctx.scope(id)` | **Preferred** namespace-scoped view - auto-prefixed `rpc` + top-level `settings` store |
243
243
  | `ctx.rpc` | Register RPC functions, broadcast, shared state, streaming channels |
244
244
  | `ctx.views` | Serve static files via `hostStatic(base, distDir)` |
245
- | `ctx.diagnostics` | Structured diagnostics host (nostics) — register custom error codes |
246
- | `ctx.agent` | Expose tools + resources to coding agents (experimental) |
247
- | `ctx.host` | Runtime abstraction — `mountStatic`, `resolveOrigin`, `getStorageDir` |
248
- | `ctx.mode` | `'dev'` or `'build'` — gate setup work per runtime |
245
+ | `ctx.diagnostics` | Structured diagnostics host (nostics) - register custom error codes |
246
+ | `ctx.agent` | Expose tools + resources to coding agents |
247
+ | `ctx.host` | Runtime abstraction - `mountStatic`, `resolveOrigin`, `getStorageDir` |
248
+ | `ctx.mode` | `'dev'` or `'build'` - gate setup work per runtime |
249
249
 
250
- > Hosts can augment `ctx` with additional surfaces (e.g. Vite DevTools' `docks`, `terminals`, `messages`, `commands`, `createJsonRenderer`). Consult the host's docs — for Vite DevTools, see the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit).
250
+ > Hosts can augment `ctx` with additional surfaces (e.g. Vite DevTools' `docks`, `terminals`, `messages`, `commands`). Consult the host's docs - for Vite DevTools, see the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit).
251
251
 
252
252
  ## RPC contracts
253
253
 
@@ -256,7 +256,7 @@ import { defineRpcFunction } from 'devframe'
256
256
  import * as v from 'valibot'
257
257
 
258
258
  const getModules = defineRpcFunction({
259
- name: 'get-modules', // bare — registered via `ctx.scope('my-inspector').rpc.register`
259
+ name: 'get-modules', // bare - registered via `ctx.scope('my-inspector').rpc.register`
260
260
  type: 'query',
261
261
  jsonSerializable: true,
262
262
  args: [v.object({ limit: v.number() })],
@@ -269,12 +269,12 @@ const getModules = defineRpcFunction({
269
269
 
270
270
  | Type | Use when | Cached | Static dump |
271
271
  |------|----------|--------|-------------|
272
- | `'static'` | Data constant for a given input — dump at build time | Indefinitely | Automatic |
272
+ | `'static'` | Data constant for a given input - dump at build time | Indefinitely | Automatic |
273
273
  | `'query'` | Read that may change; optional `dump` for build adapters | Opt-in via `cacheable` | Manual |
274
274
  | `'action'` | Server-state mutation | Never | Never |
275
275
  | `'event'` | Fire-and-forget; no response | Never | Never |
276
276
 
277
- Add valibot schemas when the RPC is user-facing, when you want static dumps, or when you expose it to agents. Prefer a **single object arg** (`args: [v.object({ ... })]`) over positional args — property names self-document and agents rely on them.
277
+ Add valibot schemas when the RPC is user-facing, when you want static dumps, or when you expose it to agents. Prefer a **single object arg** (`args: [v.object({ ... })]`) over positional args - property names self-document and agents rely on them.
278
278
 
279
279
  ### `jsonSerializable` (wire + dump format)
280
280
 
@@ -285,9 +285,9 @@ Add valibot schemas when the RPC is user-facing, when you want static dumps, or
285
285
  | `false` (default) | `structured-clone-es` | `s:` | `Map`, `Set`, `Date`, `BigInt`, cycles, class instances |
286
286
  | `true` (opt-in) | strict `JSON.stringify` | _(unprefixed)_ | JSON-only |
287
287
 
288
- Set `jsonSerializable: true` when your handler returns plain JSON shapes — the strict serializer **throws `DF0020`** synchronously on the offending call when it sees a value JSON cannot round-trip (Map/Set/Date/BigInt/class instance/`undefined`-in-array). Errors surface in dev next to the call that introduced them, not silently at build time.
288
+ Set `jsonSerializable: true` when your handler returns plain JSON shapes - the strict serializer **throws `DF0020`** synchronously on the offending call when it sees a value JSON cannot round-trip (Map/Set/Date/BigInt/class instance/`undefined`-in-array). Errors surface in dev next to the call that introduced them, not silently at build time.
289
289
 
290
- `agent: {...}` requires `jsonSerializable: true` (registration throws `DF0019` otherwise). MCP tools speak JSON — opting into the agent surface is also opting into JSON-only data.
290
+ `agent: {...}` requires `jsonSerializable: true` (registration throws `DF0019` otherwise). MCP tools speak JSON - opting into the agent surface is also opting into JSON-only data.
291
291
 
292
292
  Through the scope, `my.rpc.broadcast({ method, args, optional?, event?, filter? })` pushes to every connected client (method name namespaced), and `my.rpc.call(name, ...args)` invokes a server function locally without going through transport (the scoped form of `ctx.rpc.invokeLocal`, useful for cross-function composition).
293
293
 
@@ -305,13 +305,13 @@ state.mutate((draft) => {
305
305
  })
306
306
  ```
307
307
 
308
- - Values must be serializable — no functions, no circular refs.
308
+ - Values must be serializable - no functions, no circular refs.
309
309
  - Mutations round-trip to all clients; the host tracks `syncIds` to avoid replay loops.
310
310
  - Prefer shared state over ad-hoc RPC events for UI that must reappear after reconnect.
311
311
 
312
312
  ## Streaming channels
313
313
 
314
- For chunk-style data flowing in either direction — LLM deltas, log tails, build progress, file uploads, mic / screen-share frames — use a streaming channel instead of inventing `action + delta/end` events. The same `channel` object handles both directions:
314
+ For chunk-style data flowing in either direction - LLM deltas, log tails, build progress, file uploads, mic / screen-share frames - use a streaming channel instead of inventing `action + delta/end` events. The same `channel` object handles both directions:
315
315
 
316
316
  ```ts
317
317
  const my = ctx.scope('my-inspector')
@@ -324,18 +324,18 @@ const channel = my.rpc.streaming.create<string>('tokens', { // -> my-inspector:t
324
324
  ### Server-to-client (the common case)
325
325
 
326
326
  ```ts
327
- // Server — typically inside an action handler that returns the stream id
327
+ // Server - typically inside an action handler that returns the stream id
328
328
  const stream = channel.start({ id: 'optional-stream-id' })
329
329
  stream.write(token) // imperative
330
330
  stream.error(err) // terminal failure
331
331
  stream.close() // terminal success
332
- stream.signal // AbortSignal — flips when consumers cancel or all subscribers drop
332
+ stream.signal // AbortSignal - flips when consumers cancel or all subscribers drop
333
333
  stream.writable // WritableStream<T> for `pipeTo`-style consumption
334
334
 
335
- // Convenience — start + pipe in one call:
335
+ // Convenience - start + pipe in one call:
336
336
  await channel.pipeFrom(sourceReadable, { id: 'optional' })
337
337
 
338
- // Client — my = (await connectDevframe()).scope('my-inspector')
338
+ // Client - my = (await connectDevframe()).scope('my-inspector')
339
339
  const reader = my.rpc.streaming.subscribe<string>('tokens', streamId) // -> my-inspector:tokens
340
340
  for await (const token of reader) renderToken(token)
341
341
  // Or: reader.readable.pipeTo(domWritable)
@@ -347,7 +347,7 @@ reader.cancel() // server `stream.signal` aborts
347
347
  The same channel exposes `openInbound()` for the server side of a client→server upload. Pair it with a normal action that returns the id:
348
348
 
349
349
  ```ts
350
- // Server — my = ctx.scope('my-inspector')
350
+ // Server - my = ctx.scope('my-inspector')
351
351
  my.rpc.register(defineRpcFunction({
352
352
  name: 'upload-file', // -> my-inspector:upload-file
353
353
  type: 'action',
@@ -362,7 +362,7 @@ my.rpc.register(defineRpcFunction({
362
362
  },
363
363
  }))
364
364
 
365
- // Client — my = (await connectDevframe()).scope('my-inspector')
365
+ // Client - my = (await connectDevframe()).scope('my-inspector')
366
366
  const { uploadId } = await my.rpc.call('upload-file', { name: 'foo' })
367
367
  const upload = my.rpc.streaming.upload<Uint8Array>('files', uploadId) // -> my-inspector:files
368
368
  fileReadable.pipeTo(upload.writable, { signal: upload.signal })
@@ -403,7 +403,7 @@ For chat-style UIs that combine both: keep the **conversation log** in shared st
403
403
 
404
404
  ## Mounting into a host (Vite DevTools example)
405
405
 
406
- A portable devframe can be mounted into any host that ships an adapter for it. Vite DevTools is one supported target — `createPluginFromDevframe` is the Vite-DevTools-specific bridge; other hosts can implement equivalent factories. Example:
406
+ A portable devframe can be mounted into any host that ships an adapter for it. Vite DevTools is one supported target - `createPluginFromDevframe` is the Vite-DevTools-specific bridge; other hosts can implement equivalent factories. Example:
407
407
 
408
408
  ```ts
409
409
  // vite.config.ts
@@ -413,7 +413,7 @@ import myInspector from './my-inspector'
413
413
  export default {
414
414
  plugins: [
415
415
  createPluginFromDevframe(myInspector, {
416
- // Optional kit-only setup — runs after the auto-derived dock entry.
416
+ // Optional kit-only setup - runs after the auto-derived dock entry.
417
417
  setup(kitCtx) {
418
418
  kitCtx.commands.register({
419
419
  id: 'my-inspector:clear-cache',
@@ -426,11 +426,11 @@ export default {
426
426
  }
427
427
  ```
428
428
 
429
- The kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`. For dock variations (custom-render, launcher, action, json-render), terminals, palette commands, and toasts, use the `options.setup` hook — those APIs live on the host-augmented context, not on the devframe-level `setup`. See the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit) for the Vite-DevTools-specific reference.
429
+ The kit auto-derives an iframe dock entry from `id` / `name` / `icon` / `basePath`. For dock variations (custom-render, launcher, action, json-render), terminals, palette commands, and toasts, use the `options.setup` hook - those APIs live on the host-augmented context, not on the devframe-level `setup`. See the [`vite-devtools-kit` skill](../../skills/vite-devtools-kit) for the Vite-DevTools-specific reference.
430
430
 
431
431
  ## When clauses
432
432
 
433
- Gate kit-side dock / command visibility with VS Code-style expressions. The runtime + types ship bundled from `devframe/utils/when` — no separate install. The consumers (`when` field on docks and commands) live in the kit:
433
+ Gate kit-side dock / command visibility with VS Code-style expressions. The runtime + types ship bundled from `devframe/utils/when` - no separate install. The consumers (`when` field on docks and commands) live in the kit:
434
434
 
435
435
  ```ts
436
436
  when: 'clientType == embedded'
@@ -440,9 +440,9 @@ when: 'my-inspector.ready && count >= 10'
440
440
 
441
441
  Built-in context: `clientType` (`'embedded' | 'standalone'`), `dockOpen`, `paletteOpen`, `dockSelectedId`. Plugins can add namespaced keys (`.` or `:` separators). Both the types (`WhenExpression<Ctx, S>`) and runtime (`evaluateWhen`, `resolveContextValue`) come from `devframe/utils/when`.
442
442
 
443
- ## Agent-native surface (experimental)
443
+ ## Agent-native surface
444
444
 
445
- Opt an RPC function into the agent surface with an `agent` field — default-deny otherwise. Agent-exposed functions **must declare `jsonSerializable: true`** (registration throws `DF0019` otherwise):
445
+ Opt an RPC function into the agent surface with an `agent` field - default-deny otherwise. Agent-exposed functions **must declare `jsonSerializable: true`** (registration throws `DF0019` otherwise):
446
446
 
447
447
  ```ts
448
448
  defineRpcFunction({
@@ -485,7 +485,7 @@ import { createMcpServer } from 'devframe/adapters/mcp'
485
485
  await createMcpServer(devframe, { transport: 'stdio' })
486
486
  ```
487
487
 
488
- `@modelcontextprotocol/server` is a peer dependency. The CLI adapter also exposes `my-devframe mcp` — route host logs to stderr (stdout is the MCP transport). Safety classifications (`'read' | 'action' | 'destructive'`) drive MCP hint annotations that agent clients use to prompt for confirmation.
488
+ `@modelcontextprotocol/server` is a peer dependency. The CLI adapter also exposes `my-devframe mcp` - route host logs to stderr (stdout is the MCP transport). Safety classifications (`'read' | 'action' | 'destructive'`) drive MCP hint annotations that agent clients use to prompt for confirmation.
489
489
 
490
490
  ## Author SPA
491
491
 
@@ -495,16 +495,16 @@ Authors bring their own SPA (any framework or plain HTML). Client entry:
495
495
  import { connectDevframe } from 'devframe/client'
496
496
 
497
497
  const client = await connectDevframe()
498
- // await client.ensureTrusted() // WS mode only — blocks until server accepts
499
- const my = client.scope('my-inspector') // preferred — namespaced calls
498
+ // await client.ensureTrusted() // WS mode only - blocks until server accepts
499
+ const my = client.scope('my-inspector') // preferred - namespaced calls
500
500
 
501
501
  const data = await my.rpc.call('get-stats', { limit: 10 })
502
502
  ```
503
503
 
504
504
  `connectDevframe` auto-detects the backend via `/.devframe/.connection.json`:
505
505
 
506
- - **websocket** (dev mode) — full read/write, requires auth handshake. Listen for token updates on the `devframe-auth` BroadcastChannel.
507
- - **static** (build / spa output) — read-only, resolves calls from the baked RPC dump.
506
+ - **websocket** (dev mode) - full read/write, requires auth handshake. Listen for token updates on the `devframe-auth` BroadcastChannel.
507
+ - **static** (build output) - read-only, resolves calls from the baked RPC dump.
508
508
 
509
509
  Use `my.rpc.sharedState(key)` for observable state, `my.rpc.register(defineRpcFunction(...))` to receive server broadcasts, `my.rpc.callOptional(...)` when a missing handler should resolve to `undefined` instead of throwing, and `my.settings.{project,global}` for persisted per-workspace / per-user settings synced from the server.
510
510
 
@@ -530,50 +530,48 @@ At runtime, static clients look up the argument hash in the dump; misses resolve
530
530
 
531
531
  ## CLI adapter subcommands
532
532
 
533
- `createCac(devframe).parse()` gives the tool four subcommands out of the box:
533
+ `createCac(devframe).parse()` gives the tool three subcommands out of the box:
534
534
 
535
535
  | Subcommand | Action |
536
536
  |------------|--------|
537
- | *(default)* | Dev server on port 9999 (or `--port`) — WebSocket RPC, `cli.distDir` served at `/.devframe/` |
537
+ | *(default)* | Dev server on port 9999 (or `--port`) - WebSocket RPC, `cli.distDir` served at `/.devframe/` |
538
538
  | `build` | Static snapshot → `./dist-static/` (configurable via `--out-dir`) |
539
- | `spa` | Deployable SPA → `./dist-spa/` |
540
- | `mcp` | stdio MCP server (experimental) |
539
+ | `mcp` | stdio MCP server |
541
540
 
542
- **Bring your own CLI framework?** `createCac` (`devframe/adapters/cac`) is just a cac wrapper around three peer factories — `createDevServer` (`devframe/adapters/dev`), `createBuild` (`devframe/adapters/build`), and `createMcpServer` (`devframe/adapters/mcp`). Use them directly with commander/yargs/oclif when `createCac`'s baked-in command structure doesn't fit. `cac` is an optional peer dependency pulled in only through `devframe/adapters/cac`, so bring-your-own-CLI tools run without installing it. (The legacy `devframe/adapters/cli` entry / `createCli` remains as a deprecated alias.) `createDevServer` returns a `StartedServer` handle (`origin`, `port`, `app`, `wss`, `close()`) so you can wire SIGINT / hot-reload teardown into the surrounding program. `parseCliFlags(schema, raw)` and `defineCliFlags(...)` (both from `devframe/adapters/cac`) validate an arbitrary flag bag against a `CliFlagsSchema` — the helpers are framework-agnostic.
541
+ **Bring your own CLI framework?** `createCac` (`devframe/adapters/cac`) is just a cac wrapper around three peer factories - `createDevServer` (`devframe/adapters/dev`), `createBuild` (`devframe/adapters/build`), and `createMcpServer` (`devframe/adapters/mcp`). Use them directly with commander/yargs/oclif when `createCac`'s baked-in command structure doesn't fit. `cac` is an optional peer dependency pulled in only through `devframe/adapters/cac`, so bring-your-own-CLI tools run without installing it. `createDevServer` returns a `StartedServer` handle (`origin`, `port`, `app`, `wss`, `close()`) so you can wire SIGINT / hot-reload teardown into the surrounding program. `parseCliFlags(schema, raw)` and `defineCliFlags(...)` (both from `devframe/adapters/cac`) validate an arbitrary flag bag against a `CliFlagsSchema` - the helpers are framework-agnostic.
543
542
 
544
543
  ## Bundled utilities
545
544
 
546
- Devframe re-exports a curated set of helpers under `devframe/utils/*`. They are bundled — never add the underlying packages to a devtool's own `package.json`:
545
+ Devframe re-exports a curated set of helpers under `devframe/utils/*`. They are bundled - never add the underlying packages to a devtool's own `package.json`:
547
546
 
548
547
  | Import | Wraps | Use for |
549
548
  |--------|-------|---------|
550
549
  | `colors` from `devframe/utils/colors` | `ansis` | Terminal ANSI colors (`c.red`, `c.green`, tagged templates) |
551
550
  | `open` from `devframe/utils/open` | `open` | Open URLs / files in the OS default handler |
552
551
  | `launchEditor` from `devframe/utils/launch-editor` | `launch-editor` | Open `file:line:column` in the user's editor (optional `editor` arg) |
553
- | `hash` from `devframe/utils/hash` | `ohash` | Stable structural hash — cache keys, dedup |
552
+ | `hash` from `devframe/utils/hash` | `ohash` | Stable structural hash - cache keys, dedup |
554
553
  | `structuredClone{Serialize,Deserialize,Stringify,Parse}` from `devframe/utils/structured-clone` | `structured-clone-es` | JSON-safe round-trip of `Map` / `Set` / `Date` / `BigInt` / cycles |
555
554
  | `nanoid` from `devframe/utils/nanoid` | (vendored) | URL-safe random IDs |
556
555
  | `randomToken` / `randomDigits` / `timingSafeEqual` from `devframe/utils/crypto-token` | (native WebCrypto) | CSPRNG bearer tokens, one-time codes, constant-time compare |
557
- | `promiseWithResolver` from `devframe/utils/promise` | — | Externally-controlled `Promise` |
558
- | `createEventEmitter` from `devframe/utils/events` | — | Typed event bus |
556
+ | `createEventEmitter` from `devframe/utils/events` | - | Typed event bus |
559
557
  | `createSharedState` from `devframe/utils/shared-state` | (immer internal) | Immutable state container (see `ctx.rpc.sharedState`) |
560
- | `createStreamSink` / `createStreamReader` from `devframe/utils/streaming-channel` | — | Low-level streaming primitives |
558
+ | `createStreamSink` / `createStreamReader` from `devframe/utils/streaming-channel` | - | Low-level streaming primitives |
561
559
  | `evaluateWhen` / `WhenExpression` from `devframe/utils/when` | `whenexpr` | When-clause expressions |
562
560
 
563
- For "open file in editor" + "reveal in finder", prefer the prebuilt `commonRpcFunctions` RPC recipe (`devframe/recipes/common-rpc-functions`) — it wires the two utilities into named RPC functions ready to register.
561
+ For "open file in editor" + "reveal in finder", prefer the prebuilt `commonRpcFunctions` RPC recipe (`devframe/recipes/common-rpc-functions`) - it wires the two utilities into named RPC functions ready to register.
564
562
 
565
563
  ## Security (secure by default)
566
564
 
567
565
  RPC handlers run with the full privileges of the host process, so the boundary that matters is who may connect. Keep that boundary tight:
568
566
 
569
- - **`auth` defaults to `true`** — dev-mode connections must authenticate before calls are accepted. Devframe ships the node primitives (`exchangeTempAuthCode`, `verifyAuthToken` in `devframe/node/auth`); the host adapter (e.g. Vite DevTools) provides the interactive `devframe:anonymous:auth` + `devframe:auth:exchange` handlers and auth UI.
567
+ - **`auth` defaults to `true`** - dev-mode connections must authenticate before calls are accepted. Devframe ships the node primitives (`exchangeTempAuthCode`, `verifyAuthToken` in `devframe/node/auth`); the host adapter (e.g. Vite DevTools) provides the interactive `devframe:anonymous:auth` + `devframe:auth:exchange` handlers and auth UI.
570
568
  - **`auth: false` trusts every reachable connection.** Use it only for single-user `localhost` tools. Never combine it with a non-loopback bind host, a tunnel, or a shared/CI environment. The default bind host is already `localhost`.
571
- - **Authentication** exchanges a 6-digit one-time code (shown in the developer's terminal) for a node-issued bearer token via `requestTrustWithCode(code)`. The code is single-use, expires in 5 min, compared in constant time, and rotates after repeated failures — show it only in the terminal, never over the network.
572
- - **Magic-link (optional):** print `buildOtpAuthUrl(origin)` — `<origin>/#devframe_otp=<code>`. The code rides the URL **fragment**, which the browser never sends to the server, so it stays out of access logs and `Referer`. `connectDevframe` reads the code, exchanges it, and strips it from the URL. Integrations can opt out (`otpParam: false`) and drive it via the exposed `authenticateWithUrlOtp(rpc)` / `consumeOtpFromUrl()` client utilities. Only the single-use code rides the URL, never the bearer; treat the printed link like the code itself. The standalone CLI's `--open` does this automatically via `DevframeAuthHandler.buildOpenUrl` — the launched tab already carries the OTP, no prompt needed.
573
- - **Tokens are secrets.** The bearer token rides the WS URL (`?devframe_auth_token=…`) — serve over `wss://`/`https://` beyond loopback. Never log the token or code, never bake them into build output. Revoke via `revokeAuthToken(...)`; clients drop to untrusted on `devframe:auth:revoked`.
574
- - **Authorize handlers.** Any trusted client can call any registered function — validate inputs, and mark state-changing functions `type: 'destructive'` so MCP/agent clients prompt first.
575
- - **Origin-lock remote docks** (`originLock`, on by default) so a dock's session token is honored only on a connection whose `Origin` matches the dock — the connect-time gate enforces it.
576
- - **The MCP route requires an Origin.** The route-based MCP server (`cli.mcp`, `viteDevBridge`/Next handler `mcp`, `createMcpFetchHandler`) rejects `Origin`-less requests — a request must carry a loopback (or allow-listed) `Origin`, so it isn't reachable by an arbitrary local process. `devframe connect` sends each instance's own loopback origin automatically.
569
+ - **Authentication** exchanges a 6-digit one-time code (shown in the developer's terminal) for a node-issued bearer token via `requestTrustWithCode(code)`. The code is single-use, expires in 5 min, compared in constant time, and rotates after repeated failures - show it only in the terminal, never over the network.
570
+ - **Magic-link (optional):** print `buildOtpAuthUrl(origin)` - `<origin>/#devframe_otp=<code>`. The code rides the URL **fragment**, which the browser never sends to the server, so it stays out of access logs and `Referer`. `connectDevframe` reads the code, exchanges it, and strips it from the URL. Integrations can opt out (`otpParam: false`) and drive it via the exposed `authenticateWithUrlOtp(rpc)` / `consumeOtpFromUrl()` client utilities. Only the single-use code rides the URL, never the bearer; treat the printed link like the code itself. The standalone CLI's `--open` does this automatically via `DevframeAuthHandler.buildOpenUrl` - the launched tab already carries the OTP, no prompt needed.
571
+ - **Tokens are secrets.** The bearer token rides the WS URL (`?devframe_auth_token=…`) - serve over `wss://`/`https://` beyond loopback. Never log the token or code, never bake them into build output. Revoke via `revokeAuthToken(...)`; clients drop to untrusted on `devframe:auth:revoked`.
572
+ - **Authorize handlers.** Any trusted client can call any registered function - validate inputs, and mark state-changing functions `type: 'destructive'` so MCP/agent clients prompt first.
573
+ - **Origin-lock remote docks** (`originLock`, on by default) so a dock's session token is honored only on a connection whose `Origin` matches the dock - the connect-time gate enforces it.
574
+ - **The MCP route requires an Origin.** The route-based MCP server (`cli.mcp`, `devframeViteBridge`/Next handler `mcp`, `createMcpFetchHandler`) rejects `Origin`-less requests - a request must carry a loopback (or allow-listed) `Origin`, so it isn't reachable by an arbitrary local process. `devframe connect` sends each instance's own loopback origin automatically.
577
575
 
578
576
  See [Security](https://devfra.me/security) for the full reference.
579
577
 
@@ -587,23 +585,23 @@ See [Security](https://devfra.me/security) for the full reference.
587
585
 
588
586
  Devframe-level pages (one-tool, portable surface):
589
587
 
590
- - [Devframe Definition](https://devfra.me/devframe-definition) — fields, runtime flags, multi-adapter wiring
591
- - [Scoped Context](https://devfra.me/scoped-context) — `ctx.scope(id)`, auto-namespacing, the `settings` store
592
- - [Adapters](https://devfra.me/adapters) — full reference for all deployment adapters
593
- - [RPC](https://devfra.me/rpc) — types, schema, broadcasts, dumps
594
- - [Shared State](https://devfra.me/shared-state) — patches, events, client-side mutation
595
- - [Streaming](https://devfra.me/streaming) — chunked feeds, uploads, replay, Web/Node Streams interop
596
- - [When Clauses](https://devfra.me/when-clauses) — syntax, context, type-safe wrappers
597
- - [Structured Diagnostics](https://devfra.me/diagnostics) — coded errors via `ctx.diagnostics`, register custom codes
598
- - [Utilities](https://devfra.me/utilities) — bundled `devframe/utils/*` helpers (colors, hash, launchEditor, structured-clone, …)
599
- - [Client](https://devfra.me/client) — auth handshake, modes, discovery
600
- - [Security](https://devfra.me/security) — trust model, authentication, secure-by-default practices
601
- - [Agent-Native](https://devfra.me/agent-native) — agent field, tools/resources, MCP + Claude Desktop
602
-
603
- Host-specific extras (when mounting into Vite DevTools — other hosts have their own equivalents):
588
+ - [Devframe Definition](https://devfra.me/devframe-definition) - fields, runtime flags, multi-adapter wiring
589
+ - [Scoped Context](https://devfra.me/scoped-context) - `ctx.scope(id)`, auto-namespacing, the `settings` store
590
+ - [Adapters](https://devfra.me/adapters) - full reference for all deployment adapters
591
+ - [RPC](https://devfra.me/rpc) - types, schema, broadcasts, dumps
592
+ - [Shared State](https://devfra.me/shared-state) - patches, events, client-side mutation
593
+ - [Streaming](https://devfra.me/streaming) - chunked feeds, uploads, replay, Web/Node Streams interop
594
+ - [When Clauses](https://devfra.me/when-clauses) - syntax, context, type-safe wrappers
595
+ - [Structured Diagnostics](https://devfra.me/diagnostics) - coded errors via `ctx.diagnostics`, register custom codes
596
+ - [Utilities](https://devfra.me/utilities) - bundled `devframe/utils/*` helpers (colors, hash, launchEditor, structured-clone, …)
597
+ - [Client](https://devfra.me/client) - auth handshake, modes, discovery
598
+ - [Security](https://devfra.me/security) - trust model, authentication, secure-by-default practices
599
+ - [Agent-Native](https://devfra.me/agent-native) - agent field, tools/resources, MCP + Claude Desktop
600
+
601
+ Host-specific extras (when mounting into Vite DevTools - other hosts have their own equivalents):
604
602
 
605
603
  - [Vite DevTools Kit overview](https://devtools.vite.dev/kit/)
606
- - [Dock System](https://devtools.vite.dev/kit/dock-system) — every entry type + remote docks
607
- - [Commands](https://devtools.vite.dev/kit/commands) — palette, keybindings, sub-commands
608
- - [Messages & Notifications](https://devtools.vite.dev/kit/messages) — entry fields, positional hints
609
- - [Terminals](https://devtools.vite.dev/kit/terminals) — child processes, external sessions
604
+ - [Dock System](https://devtools.vite.dev/kit/dock-system) - every entry type + remote docks
605
+ - [Commands](https://devtools.vite.dev/kit/commands) - palette, keybindings, sub-commands
606
+ - [Messages & Notifications](https://devtools.vite.dev/kit/messages) - entry fields, positional hints
607
+ - [Terminals](https://devtools.vite.dev/kit/terminals) - child processes, external sessions
@@ -1,19 +0,0 @@
1
- import { cleanDoubleSlashes, withLeadingSlash, withTrailingSlash } from "ufo";
2
- //#region src/adapters/_shared.ts
3
- /**
4
- * Resolve the mount base path for a devframe's SPA. Hosted adapters
5
- * (`vite`, `embedded`) default to `/__<id>/` so they don't collide
6
- * with the host app; standalone adapters (`cli`, `spa`, `build`)
7
- * default to `/` because they own the origin.
8
- *
9
- * The devframe author can override with `basePath` on the definition.
10
- */
11
- function resolveBasePath(def, kind) {
12
- if (def.basePath) return normalizeBasePath(def.basePath);
13
- return kind === "standalone" ? "/" : `/__${def.id}/`;
14
- }
15
- function normalizeBasePath(base) {
16
- return cleanDoubleSlashes(withTrailingSlash(withLeadingSlash(base)));
17
- }
18
- //#endregion
19
- export { resolveBasePath as n, normalizeBasePath as t };
@@ -1,11 +0,0 @@
1
- import { Ft as InferCliFlags, It as defineCliFlags, Lt as parseCliFlags, Pt as CliFlagsSchema } from "../devframe-mQ4qZAuo.mjs";
2
- import { CacHandle, CreateCacOptions, createCac } from "./cac.mjs";
3
- //#region src/adapters/cli.d.ts
4
- /** @deprecated Use `createCac` from `devframe/adapters/cac` instead. */
5
- declare const createCli: typeof createCac;
6
- /** @deprecated Use `CreateCacOptions` from `devframe/adapters/cac` instead. */
7
- type CreateCliOptions = CreateCacOptions;
8
- /** @deprecated Use `CacHandle` from `devframe/adapters/cac` instead. */
9
- type CliHandle = CacHandle;
10
- //#endregion
11
- export { type CliFlagsSchema, CliHandle, CreateCliOptions, type InferCliFlags, createCli, defineCliFlags, parseCliFlags };
@@ -1,6 +0,0 @@
1
- import { n as defineCliFlags, r as parseCliFlags, t as createCac } from "../cac-D4_9ok0z.mjs";
2
- //#region src/adapters/cli.ts
3
- /** @deprecated Use `createCac` from `devframe/adapters/cac` instead. */
4
- const createCli = createCac;
5
- //#endregion
6
- export { createCli, defineCliFlags, parseCliFlags };