Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions alias.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export const alias = {
'devframe/adapters/kit': df('devframe/src/adapters/kit.ts'),
'devframe/adapters/embedded': df('devframe/src/adapters/embedded.ts'),
'devframe/adapters/mcp': df('devframe/src/adapters/mcp.ts'),
'devframe/helpers/nuxt/runtime/plugin.client': df('devframe/src/helpers/nuxt/runtime/plugin.client.ts'),
'devframe/helpers/nuxt': df('devframe/src/helpers/nuxt/index.ts'),
'@devframes/nuxt/runtime/plugin.client': df('nuxt/src/runtime/plugin.client.ts'),
'@devframes/nuxt': df('nuxt/src/index.ts'),
'devframe/recipes/open-helpers': df('devframe/src/recipes/open-helpers.ts'),
'devframe/client': df('devframe/src/client/index.ts'),
'devframe': df('devframe/src'),
Expand Down
4 changes: 2 additions & 2 deletions devframe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This directory is staged for extraction into a standalone repository. Until then
| Path | Description |
|------|-------------|
| [`packages/devframe`](./packages/devframe) | The published [`devframe`](https://www.npmjs.com/package/devframe) npm package. |
| [`docs`](./docs) | VitePress documentation site, deployed at https://devtools.vite.dev/devframe/. |
| [`docs`](./docs) | VitePress documentation site, deployed at https://devfra.me/. |
| [`examples`](./examples) | End-to-end demos: [`devframe-counter`](./examples/devframe-counter) (smallest cross-adapter demo) and [`devframe-files-inspector`](./examples/devframe-files-inspector) (CLI dev/build/spa + Vite DevTools dock). |
| [`tests`](./tests) | Public-API snapshot tests via [`tsnapi`](https://github.com/posva/tsnapi). |

Expand All @@ -21,7 +21,7 @@ pnpm add devframe

## Documentation

See [https://devtools.vite.dev/devframe/](https://devtools.vite.dev/devframe/) for the full guide and API reference.
See [https://devfra.me/](https://devfra.me/) for the full guide and API reference.

## Adapters

Expand Down
2 changes: 1 addition & 1 deletion devframe/docs/guide/devtool-definition.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ interface DevToolsNodeContext {
readonly workspaceRoot: string
readonly mode: 'dev' | 'build'

host: DevToolsHost // runtime abstraction (mountStatic / resolveOrigin)
host: DevToolsHost // runtime abstraction (mountStatic / resolveOrigin / getStorageDir)
rpc: RpcFunctionsHost // register + broadcast + sharedState
docks: DevToolsDockHost // dock entries
views: DevToolsViewHost // static file hosting
Expand Down
2 changes: 1 addition & 1 deletion devframe/docs/guide/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ docs/errors/
MYP0002.md
```

Each page covers the message, cause, example, and fix — see any [DF code page](https://devtools.vite.dev/devframe/errors/) for the canonical template. Set `docsBase` on `defineDiagnostics({...})` so the URL is auto-attached to every emitted diagnostic.
Each page covers the message, cause, example, and fix — see any [DF code page](https://devfra.me/errors/) for the canonical template. Set `docsBase` on `defineDiagnostics({...})` so the URL is auto-attached to every emitted diagnostic.

## When to Use What

Expand Down
10 changes: 5 additions & 5 deletions devframe/docs/guide/nuxt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ outline: deep

# Nuxt Helper

The `devframe/helpers/nuxt` module wires a Nuxt-built SPA up as a devframe client. It's an integration helper, not a deployment adapter — it runs inside the Nuxt app that consumes your devtool, not as part of the CLI that serves it.
The `@devframes/nuxt` module wires a Nuxt-built SPA up as a devframe client. It's an integration helper, not a deployment adapter — it runs inside the Nuxt app that consumes your devtool, not as part of the CLI that serves it.

It handles the three things every Nuxt-powered standalone devtool needs to get right:

Expand All @@ -16,7 +16,7 @@ It handles the three things every Nuxt-powered standalone devtool needs to get r

```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['devframe/helpers/nuxt'],
modules: ['@devframes/nuxt'],
})
```

Expand Down Expand Up @@ -44,7 +44,7 @@ export function usePayload() {

```ts [nuxt.config.ts]
export default defineNuxtConfig({
modules: ['devframe/helpers/nuxt'],
modules: ['@devframes/nuxt'],
devframe: {
baseURL: './', // where the devframe snapshot lives, relative to the page
skipAppDefaults: false, // opt out of the app.baseURL / vite.base defaults
Expand Down Expand Up @@ -79,14 +79,14 @@ my-tool/
├── bin.mjs # createCli(devtool).parse()
├── src/
│ ├── cli.ts # defineDevtool + setup(ctx) { ctx.rpc.register(...) }
│ └── app/ # Nuxt SPA — uses `devframe/helpers/nuxt`
│ └── app/ # Nuxt SPA — uses `@devframes/nuxt`
└── dist/
├── cli.mjs # bundled Node entry
└── public/ # Nuxt build output, pointed at by cli.distDir
```

- `createCli` (from `devframe/adapters/cli`) runs the Node side — HTTP + WS + static build + MCP.
- `devframe/helpers/nuxt` handles the client side — RPC connection + base-URL plumbing.
- `@devframes/nuxt` handles the client side — RPC connection + base-URL plumbing.

They're decoupled: swap Nuxt for any other SPA framework as long as it calls `connectDevtool()` in the browser.

Expand Down
2 changes: 1 addition & 1 deletion devframe/docs/guide/standalone-cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ For the Nuxt side, add the devframe helper module — it sets `app.baseURL: './'
```ts [nuxt.config.ts]
export default defineNuxtConfig({
ssr: false,
modules: ['devframe/helpers/nuxt'],
modules: ['@devframes/nuxt'],
nitro: {
preset: 'static',
output: { dir: './dist' }, // matches createCli's distDir of ./dist/public
Expand Down
1 change: 1 addition & 0 deletions devframe/examples/devframe-files-inspector/tests/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export async function startInspectorServer(
const origin = `http://${host}:${port}`
const h3Host = createH3DevToolsHost({
origin,
appName: devtool.id,
mount: (base, dir) =>
app.use(base, fromNodeMiddleware(sirv(dir, { dev: true, single: true }))),
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ describe('kit-plugin (Vite DevTools dock surface)', () => {
const mount = vi.fn()
const host = createH3DevToolsHost({
origin: 'http://test.localhost',
appName: devtool.id,
mount,
})
const ctx = await createHostContext({ cwd: process.cwd(), mode: 'dev', host })
Expand Down
1 change: 1 addition & 0 deletions devframe/examples/devframe-streaming-chat/tests/_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export async function startStreamingChatServer(): Promise<StartedServer & {
const origin = `http://${host}:${port}`
const h3Host = createH3DevToolsHost({
origin,
appName: devtool.id,
mount: (base, dir) =>
app.use(base, fromNodeMiddleware(sirv(dir, { dev: true, single: true }))),
})
Expand Down
4 changes: 2 additions & 2 deletions devframe/packages/devframe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ pnpm add devframe

## Docs

See the [DevFrame documentation](https://devtools.vite.dev/devframe/) for the full guide and API reference.
See the [DevFrame documentation](https://devfra.me/) for the full guide and API reference.

## Agent-Native (experimental)

Expand Down Expand Up @@ -55,7 +55,7 @@ devframe mcp

`@modelcontextprotocol/sdk` is a peer dependency — add it to your package when you want to ship MCP support.

See the [Agent-Native guide](https://devtools.vite.dev/devframe/agent-native) for the full API, safety model, and Claude Desktop integration example.
See the [Agent-Native guide](https://devfra.me/agent-native) for the full API, safety model, and Claude Desktop integration example.

## License

Expand Down
11 changes: 2 additions & 9 deletions devframe/packages/devframe/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@
"./adapters/vite": "./dist/adapters/vite.mjs",
"./client": "./dist/client/index.mjs",
"./constants": "./dist/constants.mjs",
"./helpers/nuxt": "./dist/helpers/nuxt/index.mjs",
"./helpers/nuxt/runtime/plugin.client": "./dist/helpers/nuxt/runtime/plugin.client.mjs",
"./node": "./dist/node/index.mjs",
"./recipes/open-helpers": "./dist/recipes/open-helpers.mjs",
"./rpc": "./dist/rpc/index.mjs",
Expand All @@ -57,20 +55,16 @@
"scripts": {
"build": "tsdown",
"watch": "tsdown --watch",
"prepack": "pnpm build && mkdir -p ./skills && cp -r ../../../skills/devframe ./skills/devframe"
"prepack": "pnpm build && mkdir -p ./skills && cp -r ../../skills/devframe ./skills/devframe"
},
"peerDependencies": {
"@modelcontextprotocol/sdk": "^1.0.0",
"@nuxt/kit": "^3.0.0 || ^4.0.0",
"launch-editor": "^2.0.0"
},
"peerDependenciesMeta": {
"@modelcontextprotocol/sdk": {
"optional": true
},
"@nuxt/kit": {
"optional": true
},
"launch-editor": {
"optional": true
}
Expand All @@ -91,7 +85,6 @@
},
"devDependencies": {
"@modelcontextprotocol/sdk": "catalog:deps",
"@nuxt/kit": "catalog:build",
"launch-editor": "catalog:deps",
"tsdown": "catalog:build",
"whenexpr": "catalog:deps"
Expand All @@ -104,7 +97,7 @@
"define-lazy-prop": "3.0.0",
"get-port-please": "3.2.0",
"human-id": "4.1.3",
"immer": "11.1.4",
"immer": "11.1.7",
"is-docker": "3.0.0",
"is-in-ssh": "1.0.0",
"is-inside-container": "1.0.0",
Expand Down
2 changes: 1 addition & 1 deletion devframe/packages/devframe/src/adapters/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export async function createBuild(d: DevtoolDefinition, options: CreateBuildOpti
const ctx = await createHostContext({
cwd: process.cwd(),
mode: 'build',
host: createH3DevToolsHost({ origin: 'http://localhost' }),
host: createH3DevToolsHost({ origin: 'http://localhost', appName: d.id }),
})
await d.setup(ctx)

Expand Down
1 change: 1 addition & 0 deletions devframe/packages/devframe/src/adapters/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export async function createDevServer(

const h3Host = createH3DevToolsHost({
origin,
appName: def.id,
mount: (base, dir) => {
app.use(base, fromNodeMiddleware(sirv(dir, { dev: true, single: true })))
},
Expand Down
13 changes: 10 additions & 3 deletions devframe/packages/devframe/src/node/__tests__/host-docks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function createMockContext(): DevToolsNodeContext {
host: {
mountStatic: () => {},
resolveOrigin: () => 'http://localhost:5173',
getStorageDir: () => '/tmp/devframe-test-storage',
},
} as unknown as DevToolsNodeContext
}
Expand All @@ -30,7 +31,13 @@ function decodeDescriptor(url: string): RemoteConnectionInfo {
}

describe('devToolsDockHost', () => {
const mockContext = {} as DevToolsNodeContext
const mockContext = {
host: {
mountStatic: () => {},
resolveOrigin: () => 'http://localhost:5173',
getStorageDir: () => '/tmp/devframe-test-storage',
},
} as unknown as DevToolsNodeContext

describe('builtin entries', () => {
it('does not include popup in builtin docks', () => {
Expand Down Expand Up @@ -281,7 +288,7 @@ describe('devToolsDockHost', () => {
internalContextMap.delete(ctx)
})

it('defaults `when` to hide the dock in build mode', () => {
it('does not inject a default `when` clause for remote docks', () => {
const ctx = createMockContext()
const host = new DevToolsDockHost(ctx)
getInternalContext(ctx).wsEndpoint = { url: 'ws://localhost:7812' }
Expand All @@ -295,7 +302,7 @@ describe('devToolsDockHost', () => {
remote: true,
})

expect(host.views.get('remote-dock')?.when).toBe('mode != build')
expect(host.views.get('remote-dock')?.when).toBeUndefined()
internalContextMap.delete(ctx)
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ function nullHost(): DevToolsHost {
return {
mountStatic: () => { /* no-op */ },
resolveOrigin: () => 'http://localhost:0',
getStorageDir: () => '/tmp/devframe-test-storage',
}
}

Expand Down
3 changes: 1 addition & 2 deletions devframe/packages/devframe/src/node/context-internal.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { DevToolsNodeContext } from 'devframe/types'
import type { SharedState } from 'devframe/utils/shared-state'
import { homedir } from 'node:os'
import { humanId } from 'devframe/utils/human-id'
import { join } from 'pathe'
import { revokeActiveConnectionsForToken, revokeAuthToken } from './auth-revoke'
Expand Down Expand Up @@ -61,7 +60,7 @@ export const internalContextMap = new WeakMap<DevToolsNodeContext, DevToolsInter
export function getInternalContext(context: DevToolsNodeContext): DevToolsInternalContext {
if (!internalContextMap.has(context)) {
const storage = createStorage<InternalAnonymousAuthStorage>({
filepath: join(homedir(), '.vite/devtools/auth.json'),
filepath: join(context.host.getStorageDir('global'), 'auth.json'),
initialValue: {
trusted: {},
},
Expand Down
2 changes: 1 addition & 1 deletion devframe/packages/devframe/src/node/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ansiFormatter } from 'logs-sdk/formatters/ansi'
// historical DTK numbers. See `docs/errors/DF\**.md` + the
// `DTK -> DF` redirect table in the migration guide for the mapping.
export const diagnostics = defineDiagnostics({
docsBase: 'https://devtools.vite.dev/devframe/errors',
docsBase: 'https://devfra.me/errors',
codes: {
DF0001: {
message: (p: { id: string }) => `Dock with id "${p.id}" is already registered`,
Expand Down
6 changes: 1 addition & 5 deletions devframe/packages/devframe/src/node/host-docks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export class DevToolsDockHost implements DevToolsDockHostType {
async init() {
this.userSettings = await this.context.rpc.sharedState.get('devframe:user-settings', {
sharedState: createStorage({
filepath: join(this.context.workspaceRoot, 'node_modules/.vite/devtools/settings.json'),
filepath: join(this.context.host.getStorageDir('workspace'), 'settings.json'),
initialValue: DEFAULT_STATE_USER_SETTINGS(),
}),
})
Expand Down Expand Up @@ -193,9 +193,5 @@ export class DevToolsDockHost implements DevToolsDockHostType {
}
const token = internal.allocateRemoteToken(view.id, dockOrigin, options.originLock)
this.remoteDocks.set(view.id, { token, options })

// Default `when` to hide the dock in build mode (no WS server exists).
if (view.when === undefined)
view.when = 'mode != build'
}
}
25 changes: 23 additions & 2 deletions devframe/packages/devframe/src/node/host-h3.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import type { DevToolsHost } from '../types/host'
import { homedir } from 'node:os'
import process from 'node:process'
import { join } from 'pathe'

export interface CreateH3DevToolsHostOptions {
/** The h3 app instance — registered once the CLI adapter lands. */
Expand All @@ -14,20 +17,38 @@ export interface CreateH3DevToolsHostOptions {
* the CLI isn't running, so the default is a no-op.
*/
mount?: (base: string, distDir: string) => void | Promise<void>
/**
* Namespace for storage paths returned by `getStorageDir`. Workspace
* state lives under `${workspaceRoot}/node_modules/.<appName>/devtools/`
* and global state under `${homedir()}/.<appName>/devtools/`. Pick the
* devtool's id (or another stable, filesystem-safe identifier) so the
* standalone host doesn't collide with other tools' storage.
*/
appName: string
/**
* Workspace root used as the parent of the per-project storage
* directory. Defaults to `process.cwd()`.
*/
workspaceRoot?: string
}

/**
* h3-backed {@link DevToolsHost} — used by the standalone CLI adapter.
* This commit adds the shell; the CLI adapter in commit 5 wires it up
* with a real h3 app and sirv handler.
*/
export function createH3DevToolsHost(options: CreateH3DevToolsHostOptions): DevToolsHost {
const workspaceRoot = options.workspaceRoot ?? process.cwd()
return {
mountStatic(base, distDir) {
return options.mount?.(base, distDir)
},
resolveOrigin() {
return options.origin
},
getStorageDir(scope) {
const namespace = `.${options.appName}/devtools`
return scope === 'workspace'
? join(workspaceRoot, 'node_modules', namespace)
: join(homedir(), namespace)
},
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ function nullHost(): DevToolsHost {
return {
mountStatic: () => { /* no-op */ },
resolveOrigin: () => 'mcp://test',
getStorageDir: () => '/tmp/devframe-test-storage',
}
}

Expand Down
5 changes: 5 additions & 0 deletions devframe/packages/devframe/src/node/mcp/build-server.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { RpcFunctionDefinitionAnyWithContext } from 'devframe/rpc'
import type { AgentTool, DevtoolDefinition, DevToolsHost, DevToolsNodeContext } from 'devframe/types'
import type { GenericSchema } from 'valibot'
import { homedir } from 'node:os'
import process from 'node:process'
import { Server } from '@modelcontextprotocol/sdk/server/index.js'
import {
Expand All @@ -9,6 +10,7 @@ import {
ListToolsRequestSchema,
ReadResourceRequestSchema,
} from '@modelcontextprotocol/sdk/types.js'
import { join } from 'pathe'
import { createHostContext } from '../context'
import { logger } from '../diagnostics'
import { valibotArgsToJsonSchema, valibotReturnToJsonSchema } from './to-json-schema'
Expand Down Expand Up @@ -104,6 +106,9 @@ export async function createMcpServer(
const host: DevToolsHost = {
mountStatic: () => { /* MCP has no static surface */ },
resolveOrigin: () => 'mcp://devframe',
getStorageDir: scope => scope === 'workspace'
? join(process.cwd(), `node_modules/.${definition.id}/devtools`)
: join(homedir(), `.${definition.id}/devtools`),
}

const ctx = await createHostContext({
Expand Down
2 changes: 1 addition & 1 deletion devframe/packages/devframe/src/rpc/diagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { consoleReporter, createLogger, defineDiagnostics, plainFormatter } from
// been migrated to `DF` here so all devframe-package codes share one
// numbering space and one docsBase.
export const diagnostics = defineDiagnostics({
docsBase: 'https://devtools.vite.dev/devframe/errors',
docsBase: 'https://devfra.me/errors',
codes: {
DF0019: {
message: (p: { name: string }) =>
Expand Down
Loading
Loading