Skip to content

Commit 57070d1

Browse files
committed
Merge remote-tracking branch 'origin/main' into few-clowns-shout
# Conflicts: # docs/content/6.errors/DF8005.md # examples/next-runtime-snapshot/scripts/build-spa.mjs # packages/hub/src/node/initiate.ts # plugins/code-server/src/index.ts # plugins/git/scripts/build-spa.mjs # plugins/terminals/src/index.ts
2 parents d58c0c1 + c2bd9c8 commit 57070d1

709 files changed

Lines changed: 5057 additions & 4021 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎alias.ts‎

Lines changed: 42 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -78,64 +78,53 @@ export const alias = {
7878
'@devframes/json-render-ui/hub': r('json-render-ui/src/hub.ts'),
7979
'@devframes/json-render-ui/spa': r('json-render-ui/src/spa.ts'),
8080
'@devframes/json-render-ui': r('json-render-ui/src/index.ts'),
81-
'json-render/dashboard': fileURLToPath(new URL('./examples/json-render/src/dashboard.ts', import.meta.url)),
82-
'@devframes/plugin-code-server/client': p('code-server/src/client/index.ts'),
83-
'@devframes/plugin-code-server/node': p('code-server/src/node/index.ts'),
84-
'@devframes/plugin-code-server/constants': p('code-server/src/constants.ts'),
85-
'@devframes/plugin-code-server/types': p('code-server/src/types.ts'),
86-
'@devframes/plugin-code-server/rpc': p('code-server/src/rpc/index.ts'),
87-
'@devframes/plugin-code-server/cli': p('code-server/src/cli.ts'),
88-
'@devframes/plugin-code-server/vite': p('code-server/src/vite.ts'),
89-
'@devframes/plugin-code-server': p('code-server/src/index.ts'),
90-
'@devframes/plugin-terminals/client': p('terminals/src/client/index.ts'),
91-
'@devframes/plugin-terminals/node': p('terminals/src/node/index.ts'),
92-
'@devframes/plugin-terminals/constants': p('terminals/src/constants.ts'),
93-
'@devframes/plugin-terminals/types': p('terminals/src/types.ts'),
94-
'@devframes/plugin-terminals/cli': p('terminals/src/cli.ts'),
95-
'@devframes/plugin-terminals/vite': p('terminals/src/vite.ts'),
96-
'@devframes/plugin-terminals': p('terminals/src/index.ts'),
97-
'@devframes/plugin-git': p('git/src/index.ts'),
81+
'json-render/dashboard': fileURLToPath(new URL('./examples/json-render/src/node/dashboard.ts', import.meta.url)),
82+
'@devframes/plugin-code-server/node': p('code-server/src/node/setup.ts'),
83+
'@devframes/plugin-code-server/constants': p('code-server/src/node/constants.ts'),
84+
'@devframes/plugin-code-server/types': p('code-server/src/node/types.ts'),
85+
'@devframes/plugin-code-server/rpc': p('code-server/src/node/rpc/index.ts'),
86+
'@devframes/plugin-code-server/cli': p('code-server/src/node/cli.ts'),
87+
'@devframes/plugin-code-server/vite': p('code-server/src/node/vite.ts'),
88+
'@devframes/plugin-code-server': p('code-server/src/node/index.ts'),
89+
'@devframes/plugin-terminals/client': p('terminals/app/client/index.ts'),
90+
'@devframes/plugin-terminals/node': p('terminals/src/node/setup.ts'),
91+
'@devframes/plugin-terminals/constants': p('terminals/src/node/constants.ts'),
92+
'@devframes/plugin-terminals/types': p('terminals/src/node/types.ts'),
93+
'@devframes/plugin-terminals/cli': p('terminals/src/node/cli.ts'),
94+
'@devframes/plugin-terminals/vite': p('terminals/src/node/vite.ts'),
95+
'@devframes/plugin-terminals': p('terminals/src/node/index.ts'),
96+
'@devframes/plugin-git': p('git/src/node/index.ts'),
9897
'devframe/recipes/interactive-auth': r('devframe/src/recipes/interactive-auth.ts'),
9998
'devframe/recipes/common-rpc-functions': r('devframe/src/recipes/common-rpc-functions.ts'),
10099
'devframe/client': r('devframe/src/client/index.ts'),
101100
'devframe': r('devframe/src'),
102-
'@devframes/plugin-data-inspector/client': p('data-inspector/src/client/index.ts'),
103-
'@devframes/plugin-data-inspector/node': p('data-inspector/src/node/index.ts'),
104-
'@devframes/plugin-data-inspector/registry': p('data-inspector/src/registry/index.ts'),
105-
'@devframes/plugin-data-inspector/engine': p('data-inspector/src/engine/index.ts'),
106101
'@devframes/plugin-data-inspector/inject': p('data-inspector/src/inject/index.ts'),
107-
'@devframes/plugin-data-inspector/cli': p('data-inspector/src/cli.ts'),
108-
'@devframes/plugin-data-inspector/vite': p('data-inspector/src/vite.ts'),
109-
'@devframes/plugin-data-inspector': p('data-inspector/src/index.ts'),
110-
'@devframes/plugin-inspect/client': p('inspect/src/client/index.ts'),
111-
'@devframes/plugin-inspect/node': p('inspect/src/node/index.ts'),
112-
'@devframes/plugin-inspect/cli': p('inspect/src/cli.ts'),
113-
'@devframes/plugin-inspect/vite': p('inspect/src/vite.ts'),
114-
'@devframes/plugin-inspect': p('inspect/src/index.ts'),
115-
'@devframes/plugin-og/client': p('og/src/client/index.ts'),
116-
'@devframes/plugin-og/node': p('og/src/node/index.ts'),
117-
'@devframes/plugin-og/rpc': p('og/src/rpc/index.ts'),
118-
'@devframes/plugin-og/cli': p('og/src/cli.ts'),
119-
'@devframes/plugin-og/vite': p('og/src/vite.ts'),
120-
'@devframes/plugin-og': p('og/src/index.ts'),
121-
'@devframes/plugin-a11y/client': p('a11y/src/client/index.ts'),
122-
'@devframes/plugin-a11y/node': p('a11y/src/node/index.ts'),
123-
'@devframes/plugin-a11y/cli': p('a11y/src/cli.ts'),
124-
'@devframes/plugin-a11y/vite': p('a11y/src/vite.ts'),
125-
'@devframes/plugin-a11y': p('a11y/src/index.ts'),
126-
'@devframes/plugin-messages/client': p('messages/src/client/index.ts'),
127-
'@devframes/plugin-messages/node': p('messages/src/node/index.ts'),
128-
'@devframes/plugin-messages/constants': p('messages/src/constants.ts'),
129-
'@devframes/plugin-messages/rpc': p('messages/src/rpc/index.ts'),
130-
'@devframes/plugin-messages/cli': p('messages/src/cli.ts'),
131-
'@devframes/plugin-messages/vite': p('messages/src/vite.ts'),
132-
'@devframes/plugin-messages': p('messages/src/index.ts'),
133-
'@devframes/plugin-assets/client': p('assets/src/client/index.ts'),
134-
'@devframes/plugin-assets/node': p('assets/src/node/index.ts'),
135-
'@devframes/plugin-assets/rpc': p('assets/src/rpc/index.ts'),
136-
'@devframes/plugin-assets/cli': p('assets/src/cli.ts'),
137-
'@devframes/plugin-assets/vite': p('assets/src/vite.ts'),
138-
'@devframes/plugin-assets': p('assets/src/index.ts'),
102+
'@devframes/plugin-data-inspector/node': p('data-inspector/src/node/setup.ts'),
103+
'@devframes/plugin-data-inspector/registry': p('data-inspector/src/node/registry/index.ts'),
104+
'@devframes/plugin-data-inspector/engine': p('data-inspector/src/node/engine/index.ts'),
105+
'@devframes/plugin-data-inspector/cli': p('data-inspector/src/node/cli.ts'),
106+
'@devframes/plugin-data-inspector': p('data-inspector/src/node/index.ts'),
107+
'@devframes/plugin-inspect/node': p('inspect/src/node/setup.ts'),
108+
'@devframes/plugin-inspect/cli': p('inspect/src/node/cli.ts'),
109+
'@devframes/plugin-inspect': p('inspect/src/node/index.ts'),
110+
'@devframes/plugin-og/node': p('og/src/node/setup.ts'),
111+
'@devframes/plugin-og/rpc': p('og/src/node/rpc/index.ts'),
112+
'@devframes/plugin-og/cli': p('og/src/node/cli.ts'),
113+
'@devframes/plugin-og': p('og/src/node/index.ts'),
114+
'@devframes/plugin-a11y/client-script': p('a11y/src/client-script/index.ts'),
115+
'@devframes/plugin-a11y/node': p('a11y/src/node/setup.ts'),
116+
'@devframes/plugin-a11y/cli': p('a11y/src/node/cli.ts'),
117+
'@devframes/plugin-a11y': p('a11y/src/node/index.ts'),
118+
'@devframes/plugin-messages/client': p('messages/app/client/index.ts'),
119+
'@devframes/plugin-messages/node': p('messages/src/node/setup.ts'),
120+
'@devframes/plugin-messages/constants': p('messages/src/node/constants.ts'),
121+
'@devframes/plugin-messages/rpc': p('messages/src/node/rpc/index.ts'),
122+
'@devframes/plugin-messages/cli': p('messages/src/node/cli.ts'),
123+
'@devframes/plugin-messages': p('messages/src/node/index.ts'),
124+
'@devframes/plugin-assets/node': p('assets/src/node/setup.ts'),
125+
'@devframes/plugin-assets/rpc': p('assets/src/node/rpc/index.ts'),
126+
'@devframes/plugin-assets/cli': p('assets/src/node/cli.ts'),
127+
'@devframes/plugin-assets': p('assets/src/node/index.ts'),
139128
'@devframes/service-git': s('git/src/index.ts'),
140129
'@devframes/service-open': s('open/src/index.ts'),
141130
'@devframes/service-shiki': s('shiki/src/index.ts'),

‎docs/app/assets/css/devframe.css‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,8 @@
113113
.dark .terminal-window {
114114
box-shadow: 0 10px 30px rgb(0 0 0 / 27%);
115115
}
116+
117+
.\[\&\>code\]\:border-current > code {
118+
border-color: color-mix(in oklab, var(--ui-primary) 50%, transparent);
119+
outline: none;
120+
}

‎docs/app/components/global/DevframeEcosystemMap.vue‎

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,57 +62,61 @@ const nodes: EcosystemNode[] = [
6262
href: '/adapters/embedded',
6363
color: 'surface',
6464
shape: 'card',
65-
position: { x: 0, y: 50 },
65+
position: { x: 0, y: 80 },
6666
width: 160,
6767
height: HEIGHT,
6868
},
6969
{
7070
id: 'cli',
71-
linksFrom: 'devframe',
71+
linksFrom: [
72+
'dev-server',
73+
'mcp',
74+
'static-build',
75+
],
7276
label: 'Standalone CLI',
7377
icon: 'i-ph-terminal-window-duotone',
7478
href: '/guide/standalone-cli',
7579
color: 'surface',
7680
shape: 'card',
77-
position: { x: 200, y: 50 },
81+
position: { x: 350, y: -10 },
7882
width: 160,
7983
height: HEIGHT,
8084
},
8185
{
8286
id: 'dev-server',
83-
linksFrom: 'cli',
87+
linksFrom: 'devframe',
8488
label: 'Dev server',
8589
icon: 'i-ph-browser-duotone',
8690
href: '/adapters/dev',
8791
color: 'surface',
8892
shape: 'card',
89-
position: { x: 450, y: 0 },
90-
width: 180,
93+
position: { x: 100, y: -10 },
94+
width: 150,
9195
height: HEIGHT,
9296
},
9397
{
9498
id: 'static-build',
95-
linksFrom: 'cli',
99+
linksFrom: 'devframe',
96100
label: 'Static build',
97101
note: 'Snapshot to a SPA',
98102
icon: 'i-ph-package-duotone',
99103
href: '/adapters/build',
100104
color: 'surface',
101105
shape: 'card',
102-
position: { x: 450, y: 50 },
103-
width: 180,
106+
position: { x: 200, y: 80 },
107+
width: 150,
104108
height: HEIGHT,
105109
},
106110
{
107111
id: 'mcp',
108-
linksFrom: 'cli',
112+
linksFrom: 'devframe',
109113
label: 'MCP server',
110114
icon: 'i-octicon-mcp-24',
111115
href: '/adapters/mcp',
112116
color: 'surface',
113117
shape: 'card',
114-
position: { x: 450, y: 100 },
115-
width: 180,
118+
position: { x: 400, y: 80 },
119+
width: 150,
116120
height: HEIGHT,
117121
},
118122
{

‎docs/content/1.guide/1.tutorial-server-data-inspector.md‎

Lines changed: 24 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ You'll need [Node 24+](https://nodejs.org/) and a terminal. Every code block is
1313

1414
A devframe is two halves talking over a typed connection: the **node side** exposes functions, and the **browser side** calls them and renders the results. Devframe is everything in between: the wire, the UI hosting, auth, builds, and a CLI.
1515

16+
The two halves live in their own folders: the node side under `src/node/`, the web app under `app/`. A `playgrounds/` folder holds hosts that boot the built tool. We'll fill these in as we go.
17+
1618
## Step 1: Define the tool
1719

1820
Everything starts with `defineDevframe`: your tool's name, plus a `setup` where you register what it can do. Create the project and the definition:
@@ -23,7 +25,7 @@ npm init -y && npm pkg set type=module
2325
npm install devframe && npm install -D typescript
2426
```
2527

26-
```ts [src/data-inspector.ts]
28+
```ts [src/node/data-inspector.ts]
2729
import { defineDevframe } from 'devframe'
2830

2931
// Some example server-side data, whatever you want to peek at while your
@@ -95,7 +97,7 @@ npm install react react-dom @devframes/vite
9597
npm install -D vite @vitejs/plugin-react @types/react @types/react-dom
9698
```
9799

98-
```html [client/index.html]
100+
```html [app/index.html]
99101
<!doctype html>
100102
<html>
101103
<head>
@@ -109,14 +111,14 @@ npm install -D vite @vitejs/plugin-react @types/react @types/react-dom
109111
</html>
110112
```
111113

112-
```tsx [client/main.tsx]
114+
```tsx [app/main.tsx]
113115
import { createRoot } from 'react-dom/client'
114116
import { App } from './App'
115117

116118
createRoot(document.getElementById('app')!).render(<App />)
117119
```
118120

119-
```tsx [client/App.tsx]
121+
```tsx [app/App.tsx]
120122
import type { DevframeRpcClient } from 'devframe/client'
121123
import { connectDevframe } from 'devframe/client'
122124
import { useEffect, useState } from 'react'
@@ -173,14 +175,13 @@ export function App() {
173175

174176
To try what we've built, let Vite serve the UI and hand RPC traffic to devframe:
175177

176-
```ts [vite.client.config.ts]
178+
```ts [app/vite.config.ts]
177179
import { devframeViteBridge } from '@devframes/vite/single'
178180
import react from '@vitejs/plugin-react'
179181
import { defineConfig } from 'vite'
180-
import dataInspectorFrame from './src/data-inspector.ts'
182+
import dataInspectorFrame from '../src/node/data-inspector.ts'
181183

182184
export default defineConfig({
183-
root: 'client',
184185
base: './', // relative asset URLs, so the built UI works under any mount path
185186
build: { outDir: '../dist/client', emptyOutDir: true },
186187
plugins: [
@@ -193,27 +194,27 @@ export default defineConfig({
193194
```
194195

195196
```sh
196-
npx vite --config vite.client.config.ts
197+
npx vite --config app/vite.config.ts
197198
```
198199

199200
Open the printed URL. The three keys and their types show up, and typing `config.port` or `users.0.name` and hitting **Query** prints the value. Button → `call` → your `handler` → back to the page: that's the whole devframe working.
200201

201202
> [!WARNING]
202203
> `auth: false` trusts anything that can reach the port. It's off here to keep the tutorial simple; turn it on for anything you publish or expose beyond localhost. See [Security](/guide/security).
203204
204-
From here on we reuse this same `src/data-inspector.ts` and `client/` unchanged; all that changes is where they run.
205+
From here on we reuse this same `src/node/data-inspector.ts` and `app/` unchanged; all that changes is where they run.
205206

206207
## Step 4: Dock it in a hub
207208

208209
A [hub](/guide/hub) puts many devframes behind one interface, each a **dock entry** you switch between, the tool's own UI in an iframe. Since our SPA uses a bare `connectDevframe()`, it already works anywhere; the hub just needs the built UI, so point the definition at it:
209210

210-
```ts [src/data-inspector.ts]
211+
```ts [src/node/data-inspector.ts]
211212
import { fileURLToPath } from 'node:url'
212213
// …
213214
const dataInspectorFrame = defineDevframe({
214215
id: 'data-inspector',
215216
/** … */
216-
clientAssets: fileURLToPath(new URL('../dist/client', import.meta.url)),
217+
clientAssets: fileURLToPath(new URL('../../dist/client', import.meta.url)),
217218
setup(ctx) { /* unchanged */ },
218219
})
219220
```
@@ -222,14 +223,14 @@ Build the UI and stand up a one-devframe hub:
222223

223224
```sh
224225
npm install @devframes/hub @devframes/hub-ui
225-
npx vite build --config vite.client.config.ts
226+
npx vite build --config app/vite.config.ts
226227
```
227228

228-
```ts [vite.hub.config.ts]
229+
```ts [playgrounds/hub.config.ts]
229230
import { createUi } from '@devframes/hub-ui'
230231
import { viteDevframeHub } from '@devframes/vite/hub'
231232
import { defineConfig } from 'vite'
232-
import dataInspectorFrame from './src/data-inspector.ts'
233+
import dataInspectorFrame from '../src/node/data-inspector.ts'
233234

234235
export default defineConfig({
235236
plugins: [
@@ -242,7 +243,7 @@ export default defineConfig({
242243
```
243244

244245
```sh
245-
npx vite --config vite.hub.config.ts
246+
npx vite --config playgrounds/hub.config.ts
246247
```
247248

248249
Your inspector now sits in the hub's dock rail as a dock entry. Add more to `devframes: [...]` (your own or the [built-in devframes](/add-ons)) and each gets its own. (The hub prints a code to authorize on first connect.)
@@ -265,14 +266,14 @@ ctx.rpc.register({
265266

266267
```js [scripts/build.mjs]
267268
import { createBuild } from 'devframe/adapters/build'
268-
import dataInspectorFrame from '../src/data-inspector.ts'
269+
import dataInspectorFrame from '../src/node/data-inspector.ts'
269270

270271
await createBuild(dataInspectorFrame, { outDir: 'dist-static' })
271272
```
272273

273274
```sh
274-
npx vite build # refresh dist/client
275-
node scripts/build.mjs # → dist-static/
275+
npx vite build --config app/vite.config.ts # refresh dist/client
276+
node scripts/build.mjs # → dist-static/
276277
```
277278

278279
Serve `dist-static/` anywhere and the meta list renders from the baked snapshot, no Node in sight. `query` takes an argument, so it still needs the live node side (next), or you can bake specific inputs ([Client Assets](/guide/client-assets)).
@@ -281,16 +282,16 @@ Serve `dist-static/` anywhere and the meta list renders from the baked snapshot,
281282

282283
The definition never depended on Vite. `createDevServer` runs the tool on its own, serving the UI from `clientAssets` and answering RPC live:
283284

284-
```js [scripts/serve.mjs]
285+
```js [playgrounds/serve.mjs]
285286
import { createDevServer } from 'devframe/adapters/dev'
286-
import dataInspectorFrame from '../src/data-inspector.ts'
287+
import dataInspectorFrame from '../src/node/data-inspector.ts'
287288

288289
await createDevServer(dataInspectorFrame, { openBrowser: true })
289290
```
290291

291292
```sh
292-
npx vite build
293-
node scripts/serve.mjs
293+
npx vite build --config app/vite.config.ts
294+
node playgrounds/serve.mjs
294295
```
295296

296297
Same UI, same live calls, no bundler in the loop: this is what you'd drop into your own Node program.
@@ -302,7 +303,7 @@ Finally, wrap that dev server in a CLI. `devframe/adapters/cac` turns a devframe
302303
```js [bin.mjs]
303304
#!/usr/bin/env node
304305
import { createCac } from 'devframe/adapters/cac'
305-
import dataInspectorFrame from './src/data-inspector.ts'
306+
import dataInspectorFrame from './src/node/data-inspector.ts'
306307

307308
createCac(dataInspectorFrame).parse()
308309
```

‎docs/content/1.guide/14.security.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ For your own auth UI, disable built-in handling with `otpParam: false`, then cal
7575

7676
- **Stay on loopback.** Bind to a routable address only intentionally, and require authentication when you do.
7777
- **Keep `auth: false` local.** The hosted bridges (`devframeViteBridge`, `@devframes/next`'s handler) gate their side-car by default; opt out with an explicit `auth: false` only when the host framework owns the trust boundary another way.
78-
- **The MCP route trusts same-machine callers, harden it when that's not your boundary.** The origin gate keeps browsers and remote hosts out (loopback-only, `Origin`-less rejected), so `mcp: true` is enough for a local dev tool. `Origin` proves nothing about *which* local process is calling, though, so when the route is reachable beyond loopback (a widened `allowedOrigins`, a hosted app) or exposes destructive tools, add an identity check with `mcp: { authorization }` (a bearer from an env var, or a callback). See [MCP](/adapters/mcp).
78+
- **The MCP route trusts same-machine callers, harden it when that's not your boundary.** The origin gate keeps browsers and remote hosts out (loopback-only, `Origin`-less rejected), so the `'auto'` default - which mounts the route once agent tools exist - and `mcp: true` are enough for a local dev tool. `Origin` proves nothing about *which* local process is calling, though, so when the route is reachable beyond loopback (a widened `allowedOrigins`, a hosted app) or exposes destructive tools, add an identity check with `mcp: { authorization }` (a bearer from an env var, or a callback), or turn the route off with `mcp: false`. See [MCP](/adapters/mcp).
7979
- **Treat tokens as secrets.** Never log the bearer token or the one-time code, or bake either into build output.
8080
- **Authorize every handler.** Validate inputs, and mark state-changing functions `type: 'destructive'` so MCP and agent clients prompt before invoking them.
8181
- **Origin-lock remote docks.** When a hub embeds a remote-UI dock, keep `originLock` on (the default) so its session token is only honored on a connection whose `Origin` matches the dock's own.

‎docs/content/1.guide/15.agent-native.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ Every `ctx.rpc.sharedState` key is exposed as a `devframe://state/<key>` resourc
100100

101101
## Starting the MCP server
102102

103-
CLI:
103+
The dev server serves the agent surface over HTTP on its own: the `mcp: 'auto'` default mounts the route at `/__mcp` once anything above exists (an `agent`-flagged RPC, a registered tool or resource) - one flagged function is the whole setup. See the [MCP adapter](/adapters/mcp#route-based-server) for forcing it on or off and hardening the route.
104+
105+
For a stdio server instead, via the CLI:
104106

105107
```sh
106108
# Run your devtool with an MCP stdio server attached.
@@ -118,8 +120,6 @@ const myDevframe = defineDevframe({ /* … */ })
118120
await createMcpServer(myDevframe, { transport: 'stdio' })
119121
```
120122

121-
`@modelcontextprotocol/server` is a peer dependency.
122-
123123
## Connecting Claude Desktop
124124

125125
In `claude_desktop_config.json`:

0 commit comments

Comments
 (0)