diff --git a/alias.ts b/alias.ts index 6e2d67173..90c945255 100644 --- a/alias.ts +++ b/alias.ts @@ -47,7 +47,10 @@ export const alias = { 'devframe/adapters/build': r('devframe/src/adapters/build.ts'), 'devframe/adapters/embedded': r('devframe/src/adapters/embedded.ts'), 'devframe/initiate': r('devframe/src/adapters/initiate.ts'), - 'devframe/adapters/mcp': r('devframe/src/adapters/mcp/index.ts'), + 'devframe/adapters/mcp': r('devframe/src/adapters/mcp.ts'), + '@devframes/agentic/mcp': r('agentic/src/mcp/index.ts'), + '@devframes/agentic/connect': r('agentic/src/connect/index.ts'), + '@devframes/agentic': r('agentic/src/index.ts'), '@devframes/hub/build': r('hub/src/node/build.ts'), '@devframes/hub/client': r('hub/src/client/index.ts'), '@devframes/hub/constants': r('hub/src/constants.ts'), diff --git a/docs/content/1.guide/15.agent-native.md b/docs/content/1.guide/15.agent-native.md index ab89d47a3..4ed4357d5 100644 --- a/docs/content/1.guide/15.agent-native.md +++ b/docs/content/1.guide/15.agent-native.md @@ -100,7 +100,7 @@ Every `ctx.rpc.sharedState` key is exposed as a `devframe://state/` resourc ## Starting the MCP server -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. +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) and the optional [`@devframes/agentic`](/adapters/mcp) peer is installed - one flagged function plus one install is the whole setup. See the [MCP adapter](/adapters/mcp#route-based-server) for forcing it on or off and hardening the route. For a stdio server instead, via the CLI: diff --git a/docs/content/2.adapters/7.mcp.md b/docs/content/2.adapters/7.mcp.md index a39cda8e2..b45adf132 100644 --- a/docs/content/2.adapters/7.mcp.md +++ b/docs/content/2.adapters/7.mcp.md @@ -7,6 +7,12 @@ description: 'Exposes a devframe''s agent-facing API as a Model Context Protocol Exposes a devframe's agent-facing API as a [Model Context Protocol](https://modelcontextprotocol.io) server: coding agents call flagged RPCs and read resources. +The implementation (and the MCP SDK behind it) lives in **`@devframes/agentic`**, an optional peer of `devframe`: install it to enable the agent surface, and keep importing everything from `devframe/adapters/mcp` - the peer is loaded for you, never imported directly. A devframe without an agent surface ships with neither the peer nor the SDK installed: + +```sh +npm install @devframes/agentic +``` + ```ts import { createMcpServer } from 'devframe/adapters/mcp' import myDevframe from './my-tool' @@ -18,7 +24,7 @@ await createMcpServer(myDevframe, { transport: 'stdio' }) ## Route-based server -The dev server exposes the same MCP API over HTTP, live. The default setting is **`'auto'`**: the route mounts once the devframe exposes an agent surface (an `agent`-flagged RPC, a registered tool or resource) - flag your first function and the agent view is on. A devframe with nothing flagged mounts no route and loads no MCP code. +The dev server exposes the same MCP API over HTTP, live. The default setting is **`'auto'`**: the route mounts once the devframe exposes an agent surface (an `agent`-flagged RPC, a registered tool or resource) *and* `@devframes/agentic` is installed - flag your first function, install the peer, and the agent view is on. A devframe with nothing flagged mounts no route and loads no MCP code; an agent surface without the peer warns once ([DF0078](/errors/DF0078)) and mounts nothing, while an explicit `mcp` setting without the peer throws ([DF0079](/errors/DF0079)). `mcp: false` stays silent either way. Pin the behavior where you host the tool - it's a hosting decision, so pass `mcp` to `createCac` when you assemble the CLI (or to `createDevServer` / `initDevframe` / `initHub` when you host it programmatically): `true` always mounts, `false` never mounts, an object customises the route: @@ -103,6 +109,8 @@ Two gateway tools (`devframe:connect:*` ids; see [tool ids and wire names](/guid Discovery reads the **instance registry**: every `createDevServer` writes `~/.devframe/instances/-.json`, dialed with a loopback origin. In-process host frameworks register via `registerDevframeInstance` (`devframe/node`). `--port ` probes a port; `DEVFRAME_INSTANCES_DIR` relocates the registry, `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` opts out. -Most instances trust same-machine callers, so the connector reaches them with no credential. For an instance you *hardened* with a bearer, the connector reads `DEVFRAME_MCP_AUTH_TOKEN` and presents it (never a CLI flag, since command-line arguments are visible to other processes). Connect to a fleet with distinct credentials by driving `startConnectServer` with a per-instance `authToken` resolver. +The connector needs the same optional `@devframes/agentic` peer as the adapter; `devframe connect` without it throws [DF0046](/errors/DF0046). + +Most instances trust same-machine callers, so the connector reaches them with no credential. For an instance you *hardened* with a bearer, the connector reads `DEVFRAME_MCP_AUTH_TOKEN` and presents it (never a CLI flag, since command-line arguments are visible to other processes). See [Agent-Native](/guide/agent-native) for the API and safety model. diff --git a/docs/content/6.errors/DF0046.md b/docs/content/6.errors/DF0046.md index 6cfc61519..296da9158 100644 --- a/docs/content/6.errors/DF0046.md +++ b/docs/content/6.errors/DF0046.md @@ -1,25 +1,25 @@ --- -title: 'DF0046: Connector Requires the MCP SDK' -description: 'devframe connect requires the optional peer dependency @modelcontextprotocol/client: {reason}' +title: 'DF0046: Connector Requires @devframes/agentic' +description: 'devframe connect requires the optional peer dependency @devframes/agentic: {reason}' --- ## Message -> `devframe connect` requires the optional peer dependency @modelcontextprotocol/client: `{reason}` +> `devframe connect` requires the optional peer dependency @devframes/agentic: `{reason}` ## Cause -`devframe connect` was started but `@modelcontextprotocol/client` could not be imported. The client SDK is an optional peer dependency of `devframe`: only the connector dials other instances, so only it needs the package installed. +`devframe connect` was started but `@devframes/agentic/connect` could not be imported. The connector lives in `@devframes/agentic` (together with the MCP SDK), an optional peer dependency of `devframe`: only agent-facing features need the package installed. ## Fix -Install the SDK next to devframe and run the connector again: +Install the package next to devframe and run the connector again: ```sh -npm install @modelcontextprotocol/client +npm install @devframes/agentic devframe connect ``` ## Source -- [`packages/devframe/src/cli/connect.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/connect.ts): `startConnectServer()` throws this when the dynamic SDK import fails. +- [`packages/devframe/src/cli/main.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/cli/main.ts): the `connect` subcommand throws this when the dynamic `@devframes/agentic/connect` import fails. diff --git a/docs/content/6.errors/DF0078.md b/docs/content/6.errors/DF0078.md new file mode 100644 index 000000000..7ed1352ca --- /dev/null +++ b/docs/content/6.errors/DF0078.md @@ -0,0 +1,28 @@ +--- +title: 'DF0078: Agent Surface Without @devframes/agentic' +description: 'This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed.' +--- + +## Message + +> This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed. + +## Cause + +The devframe (or hub) left a non-empty agent surface (RPC functions with an `agent` field, registered agent tools, resources, or providers) and the `mcp` setting is the omitted `'auto'` default, which would mount the MCP route. But `@devframes/agentic`, the optional peer carrying the MCP adapter and the MCP SDK, is not installed, so no route can be served. + +The warning is reported once per process; the instance keeps running without an MCP endpoint. + +## Fix + +Install the peer so the agent surface is served over MCP: + +```sh +npm install @devframes/agentic +``` + +Or, if the tools should deliberately stay unexposed, set `mcp: false` to opt out silently. + +## Source + +- [`packages/devframe/src/adapters/_shared.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/adapters/_shared.ts): `loadAutoMcpAdapter()` reports this (once) when the agent surface is non-empty but the peer probe fails. diff --git a/docs/content/6.errors/DF0079.md b/docs/content/6.errors/DF0079.md new file mode 100644 index 000000000..21ff658ac --- /dev/null +++ b/docs/content/6.errors/DF0079.md @@ -0,0 +1,26 @@ +--- +title: 'DF0079: MCP Enabled Without @devframes/agentic' +description: 'The mcp option is enabled, but the optional peer "@devframes/agentic" could not be loaded: {reason}' +--- + +## Message + +> The `mcp` option is enabled, but the optional peer "@devframes/agentic" could not be loaded: `{reason}` + +## Cause + +An explicit `mcp` setting (`true`, a route options object, the `--mcp` flag, or the `mcp` CLI subcommand) asked for an MCP surface, but the implementation could not be loaded from the optional `@devframes/agentic` peer, typically because it is not installed. Unlike the omitted `'auto'` default (which degrades to a one-time [DF0078](/errors/DF0078) warning), an explicit opt-in fails fast rather than silently running without MCP. + +## Fix + +Install the peer next to devframe: + +```sh +npm install @devframes/agentic +``` + +Or remove the explicit `mcp` setting (or pass `mcp: false`) if the endpoint isn't wanted. The underlying import error is attached as `cause`. + +## Source + +- [`packages/devframe/src/node/agentic.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/agentic.ts): `importAgenticMcp()` maps a failed load of `@devframes/agentic/mcp` to this error. diff --git a/docs/content/6.errors/index.md b/docs/content/6.errors/index.md index 3bd55a16f..b70aabb01 100644 --- a/docs/content/6.errors/index.md +++ b/docs/content/6.errors/index.md @@ -54,7 +54,7 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi | [DF0043](/errors/DF0043) | error | Invalid RPC Argument | | [DF0044](/errors/DF0044) | error | Invalid RPC Return Value | | [DF0045](/errors/DF0045) | warn | Instance Registry Update Failed | -| [DF0046](/errors/DF0046) | error | Connector Requires the MCP SDK | +| [DF0046](/errors/DF0046) | error | Connector Requires @devframes/agentic | | [DF0047](/errors/DF0047) | warn | Agent Tool Wire-Name Collision | | [DF0048](/errors/DF0048) | error | Unknown Shared-State Key | | [DF0049](/errors/DF0049) | error | Connector Call Requires Port and Tool | @@ -84,6 +84,8 @@ Emitted by `devframe`: the framework-neutral host, RPC, streaming, assets, servi | [DF0075](/errors/DF0075) | warn | No RPC Transport On This Runtime | | [DF0076](/errors/DF0076) | error | WebSocket Upgrade Unsupported On This Runtime | | [DF0077](/errors/DF0077) | error | In-Page Channel Function Not Registered | +| [DF0078](/errors/DF0078) | warn | Agent Surface Without @devframes/agentic | +| [DF0079](/errors/DF0079) | error | MCP Enabled Without @devframes/agentic | ## Hub: context & lifecycle (DF80xx) diff --git a/docs/content/7.migrations/1.migration-0.10.md b/docs/content/7.migrations/1.migration-0.10.md new file mode 100644 index 000000000..4ef2aca3a --- /dev/null +++ b/docs/content/7.migrations/1.migration-0.10.md @@ -0,0 +1,45 @@ +--- +title: 'Migrating to 0.10' +description: '0.10 moves the MCP implementation and the MCP SDK into @devframes/agentic, a new optional peer: install it to serve agent tools over MCP; imports are unchanged.' +--- + +0.10 moves the MCP implementation and the MCP SDK out of `devframe` into [`@devframes/agentic`](/adapters/mcp), a new **optional peer**. A devframe without an agent surface ships slimmer (no MCP SDK installed at all); one that exposes agent tools adds a single install. Your imports do not change. + +## MCP requires `@devframes/agentic` + +`devframe/adapters/mcp` stays the user-facing API and now lazy-loads its implementation from the `@devframes/agentic` peer; the peer itself is never imported directly. Install it wherever an MCP surface should be served: + +```sh +npm install @devframes/agentic +``` + +Every surface reacts to the missing peer the same way: + +| `mcp` setting | peer installed | peer missing | +| ------------- | -------------- | ------------ | +| omitted / `'auto'` | mounts once the agent surface is non-empty | non-empty surface: warns once ([DF0078](/errors/DF0078)), mounts nothing; empty surface: silent, no MCP code loads | +| `true` / route options object | always mounts | throws [DF0079](/errors/DF0079) | +| `false` | never mounts, never probes | same | + +This applies everywhere the `mcp` setting exists: `createCac` / `--mcp`, `createDevServer`, `initDevframe`, `initHub`'s aggregate endpoint, and the framework kits. + +The exports are unchanged - `createMcpServer`, `createMcpFetchHandler`, `mountMcpHttp`, and their option types (also importable from `devframe/types`). Importing `devframe/adapters/mcp` without the peer installed throws the usual module-not-found error, exactly like `devframe/adapters/cac` with its optional `cac` peer. The ` mcp` stdio subcommand keeps working with the peer installed. + +## `devframe connect` requires `@devframes/agentic` + +The connector's gateway moved into the same peer, replacing the former `@modelcontextprotocol/client` optional peer. `devframe connect` without it throws [DF0046](/errors/DF0046): + +| 0.9.x | 0.10 | +|-------|------| +| `npm install @modelcontextprotocol/client` (optional peer for `devframe connect`) | `npm install @devframes/agentic` | + +## `devframe` no longer depends on the MCP SDK + +`@modelcontextprotocol/server` left `devframe`'s dependencies and `@modelcontextprotocol/client` left its optional peers; both are now regular dependencies of `@devframes/agentic`, so installing the peer is the whole story. If you had installed `@modelcontextprotocol/client` only for `devframe connect`, you can drop it: + +```sh +npm uninstall @modelcontextprotocol/client +npm install @devframes/agentic +``` + +Code that imported SDK types directly for devframe's MCP options no longer needs to: the full option surface is typed on `devframe/adapters/mcp` and `devframe/types` without any SDK types. diff --git a/docs/content/7.migrations/1.migration-0.9.md b/docs/content/7.migrations/2.migration-0.9.md similarity index 100% rename from docs/content/7.migrations/1.migration-0.9.md rename to docs/content/7.migrations/2.migration-0.9.md diff --git a/docs/content/7.migrations/2.migration-0.8.md b/docs/content/7.migrations/3.migration-0.8.md similarity index 100% rename from docs/content/7.migrations/2.migration-0.8.md rename to docs/content/7.migrations/3.migration-0.8.md diff --git a/docs/content/7.migrations/3.migration-0.7.md b/docs/content/7.migrations/4.migration-0.7.md similarity index 100% rename from docs/content/7.migrations/3.migration-0.7.md rename to docs/content/7.migrations/4.migration-0.7.md diff --git a/docs/content/7.migrations/4.migration-0.6.md b/docs/content/7.migrations/5.migration-0.6.md similarity index 100% rename from docs/content/7.migrations/4.migration-0.6.md rename to docs/content/7.migrations/5.migration-0.6.md diff --git a/docs/content/7.migrations/index.md b/docs/content/7.migrations/index.md index bcee572e0..b0ffcb050 100644 --- a/docs/content/7.migrations/index.md +++ b/docs/content/7.migrations/index.md @@ -7,6 +7,7 @@ Upgrade guides for devframe and `@devframes/hub`, newest first. Each one lists e | Version | What changed | | ------- | ------------ | +| [Migrating to 0.10](/migrations/migration-0.10) | Moves the MCP implementation and the MCP SDK into `@devframes/agentic`, a new optional peer; imports are unchanged. | | [Migrating to 0.9](/migrations/migration-0.9) | Removes the compatibility shims deprecated across the 0.7 series, trims the public API, and moves the MCP surface to the stateless MCP 2026-07-28 protocol. | | [Migrating to 0.8](/migrations/migration-0.8) | Makes RPC schemas validator-neutral and runtime-validated, and adds the agent-native MCP API. | | [Migrating to 0.7](/migrations/migration-0.7) | Makes `cac` an optional peer and moves json-render into an opt-in package. | diff --git a/examples/custom-hub-next/package.json b/examples/custom-hub-next/package.json index be4b3a8b3..53a78af41 100644 --- a/examples/custom-hub-next/package.json +++ b/examples/custom-hub-next/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@antfu/design": "catalog:frontend", + "@devframes/agentic": "workspace:*", "@devframes/hub": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", diff --git a/examples/custom-hub-vite/package.json b/examples/custom-hub-vite/package.json index eb21efdd0..004439771 100644 --- a/examples/custom-hub-vite/package.json +++ b/examples/custom-hub-vite/package.json @@ -12,6 +12,7 @@ }, "dependencies": { "@antfu/design": "catalog:frontend", + "@devframes/agentic": "workspace:*", "@devframes/hub": "workspace:*", "@devframes/json-render": "workspace:*", "@devframes/json-render-ui": "workspace:*", diff --git a/knip.jsonc b/knip.jsonc index b664838e7..44782507e 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -136,8 +136,7 @@ // listed explicitly instead. Keep this in sync with the `exports` map. "entry": [ "src/{index,constants}.ts", - "src/adapters/{build,cac,dev,embedded,initiate}.ts", - "src/adapters/mcp/index.ts", + "src/adapters/{build,cac,dev,embedded,initiate,mcp}.ts", "src/client/index.ts", "src/in-page-channel/index.ts", "src/internal/index.ts", @@ -151,6 +150,9 @@ "src/utils/*.ts" ] }, + "packages/agentic": { + "entry": ["src/{mcp,connect}/index.ts"] + }, "packages/hub": { "entry": ["src/{index,constants}.ts", "src/{client,node,types}/index.ts", "src/node/{index,initiate,build}.ts"] }, diff --git a/packages/agentic/package.json b/packages/agentic/package.json new file mode 100644 index 000000000..b7334ec5e --- /dev/null +++ b/packages/agentic/package.json @@ -0,0 +1,55 @@ +{ + "name": "@devframes/agentic", + "type": "module", + "version": "0.9.19", + "description": "Agent-surface implementation for devframe: installing it next to devframe enables the MCP adapter (devframe/adapters/mcp) and the devframe connect gateway.", + "author": "Anthony Fu ", + "license": "MIT", + "homepage": "https://github.com/devframes/devframe#readme", + "repository": { + "directory": "packages/agentic", + "type": "git", + "url": "git+https://github.com/devframes/devframe.git" + }, + "bugs": "https://github.com/devframes/devframe/issues", + "keywords": [ + "devtools", + "devframe", + "mcp", + "agent" + ], + "sideEffects": false, + "exports": { + ".": "./dist/index.mjs", + "./mcp": "./dist/mcp/index.mjs", + "./connect": "./dist/connect/index.mjs", + "./package.json": "./package.json" + }, + "types": "./dist/index.d.mts", + "files": [ + "dist" + ], + "scripts": { + "build": "tsdown", + "watch": "tsdown --watch", + "typecheck": "tsc --noEmit", + "prepack": "turbo run build --filter=@devframes/agentic" + }, + "peerDependencies": { + "devframe": "workspace:*" + }, + "dependencies": { + "@modelcontextprotocol/client": "catalog:deps", + "@modelcontextprotocol/server": "catalog:deps", + "@standard-schema/spec": "catalog:deps", + "h3": "catalog:deps", + "pathe": "catalog:deps", + "ufo": "catalog:deps" + }, + "devDependencies": { + "@types/node": "catalog:types", + "devframe": "workspace:*", + "tsdown": "catalog:build", + "valibot": "catalog:deps" + } +} diff --git a/packages/devframe/src/cli/connect.test.ts b/packages/agentic/src/connect/__tests__/connect.test.ts similarity index 94% rename from packages/devframe/src/cli/connect.test.ts rename to packages/agentic/src/connect/__tests__/connect.test.ts index 9644247d0..6eadea7ca 100644 --- a/packages/devframe/src/cli/connect.test.ts +++ b/packages/agentic/src/connect/__tests__/connect.test.ts @@ -1,10 +1,9 @@ -import type { DevframeInstanceRecord } from '../node/instance-registry' -import type { StartedServer } from '../node/instance-shell' -import type { DevframeDefinition } from '../types/devframe' +import type { DevframeInstanceRecord, StartedServer } from 'devframe/internal' +import type { DevframeDefinition } from 'devframe/types' import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { createDevServer } from 'devframe/adapters/dev' import { afterEach, describe, expect, it } from 'vitest' -import { createDevServer } from '../adapters/dev' -import { buildInstanceRequestHeaders, resolveAuthToken } from './connect' +import { buildInstanceRequestHeaders, resolveAuthToken } from '../index' const TOKEN = 'a-high-entropy-connect-test-token' diff --git a/packages/devframe/src/cli/connect.ts b/packages/agentic/src/connect/index.ts similarity index 79% rename from packages/devframe/src/cli/connect.ts rename to packages/agentic/src/connect/index.ts index f79370299..c27bf0026 100644 --- a/packages/devframe/src/cli/connect.ts +++ b/packages/agentic/src/connect/index.ts @@ -1,11 +1,12 @@ import type { Tool } from '@modelcontextprotocol/server' -import type { DevframeInstanceRecord } from '../node/instance-registry' -import process from 'node:process' +import type { DevframeInstanceRecord } from 'devframe/internal' +import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { Server } from '@modelcontextprotocol/server' +import { StdioServerTransport } from '@modelcontextprotocol/server/stdio' +import { diagnostics, listLiveDevframeInstances, probeDevframeOrigin } from 'devframe/internal' import { toAgentToolName } from 'devframe/utils/agent-tool-name' import { Diagnostic } from 'devframe/utils/nostics' import { joinURL } from 'ufo' -import { diagnostics } from '../node/diagnostics' -import { listLiveDevframeInstances, probeDevframeOrigin } from '../node/instance-registry' export interface ConnectServerOptions { /** @@ -75,14 +76,6 @@ interface IndexedInstance extends Omit { hint?: string } -/** The lazily imported MCP SDK surface `devframe connect` needs. */ -interface ConnectSdk { - Server: typeof import('@modelcontextprotocol/server').Server - StdioServerTransport: typeof import('@modelcontextprotocol/server/stdio').StdioServerTransport - Client: typeof import('@modelcontextprotocol/client').Client - StreamableHTTPClientTransport: typeof import('@modelcontextprotocol/client').StreamableHTTPClientTransport -} - // Gateway tool ids follow the `devframe::` convention; the wire // names are their sanitized forms (`devframe_connect_list-instances`, …). const INDEX_TOOL = toAgentToolName('devframe:connect:list-instances') @@ -127,9 +120,7 @@ const GATEWAY_TOOLS: Tool[] = [ * Streamable-HTTP endpoint), and holds no domain knowledge of its own. */ export async function startConnectServer(options: ConnectServerOptions = {}): Promise { - const sdk = await importSdk() - - const server = new sdk.Server( + const server = new Server( { name: 'devframe-connect', version: '0.0.0' }, { capabilities: { tools: {} } }, ) @@ -140,9 +131,9 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr const { name, arguments: args } = request.params try { if (name === INDEX_TOOL) - return textResult(await index(sdk, options)) + return textResult(await index(options)) if (name === CALL_TOOL) - return textResult(await call(sdk, options, args ?? {})) + return textResult(await call(options, args ?? {})) return errorResult({ message: `unknown tool "${name}"`, fix: `Call ${INDEX_TOOL} or ${CALL_TOOL}.` }) } catch (error) { @@ -150,7 +141,7 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr } }) - const transport = new sdk.StdioServerTransport() + const transport = new StdioServerTransport() await server.connect(transport) return { @@ -160,28 +151,8 @@ export async function startConnectServer(options: ConnectServerOptions = {}): Pr } } -async function importSdk(): Promise { - try { - const [serverMod, stdioMod, clientMod] = await Promise.all([ - import('@modelcontextprotocol/server'), - import('@modelcontextprotocol/server/stdio'), - import('@modelcontextprotocol/client'), - ]) - return { - Server: serverMod.Server, - StdioServerTransport: stdioMod.StdioServerTransport, - Client: clientMod.Client, - StreamableHTTPClientTransport: clientMod.StreamableHTTPClientTransport, - } - } - catch (error) { - const reason = error instanceof Error ? error.message : String(error) - throw diagnostics.DF0046({ reason, cause: error }) - } -} - /** Discover instances: registry (prune-on-read) + explicit port probes. */ -async function index(sdk: ConnectSdk, options: ConnectServerOptions): Promise { +async function index(options: ConnectServerOptions): Promise { const { live } = await listLiveDevframeInstances({ instancesDir: options.instancesDir, timeoutMs: options.timeoutMs, @@ -205,7 +176,7 @@ async function index(sdk: ConnectSdk, options: ConnectServerOptions): Promise { - return withInstanceClient(sdk, url, token, async (client) => { +async function listInstanceTools(url: string, token: string | undefined): Promise<{ name: string, description?: string }[]> { + return withInstanceClient(url, token, async (client) => { const listed = await client.listTools() return listed.tools.map((tool: { name: string, description?: string }) => ({ name: tool.name, @@ -254,7 +225,6 @@ async function listInstanceTools(sdk: ConnectSdk, url: string, token: string | u } async function call( - sdk: ConnectSdk, options: ConnectServerOptions, args: { port?: number, tool?: string, args?: Record }, ): Promise { @@ -272,7 +242,7 @@ async function call( throw diagnostics.DF0051({ port: args.port }) const url = `${record.origin}${record.mcp.path}` - return withInstanceClient(sdk, url, resolveAuthToken(options.authToken, record), async (client) => { + return withInstanceClient(url, resolveAuthToken(options.authToken, record), async (client) => { const result = await client.callTool({ name: args.tool!, arguments: args.args ?? {} }) return { instance: { id: record.id, port: record.port }, @@ -285,16 +255,15 @@ async function call( } async function withInstanceClient( - sdk: ConnectSdk, url: string, token: string | undefined, - fn: (client: InstanceType) => Promise, + fn: (client: Client) => Promise, ): Promise { // The instance's own (loopback) origin (so the route's origin gate, which // rejects `Origin`-less requests, accepts this native client) plus the // bearer (when configured), the `Authorization` header being the only place // the credential ever appears. - const transport = new sdk.StreamableHTTPClientTransport( + const transport = new StreamableHTTPClientTransport( new URL(url), { requestInit: { headers: buildInstanceRequestHeaders(url, token) } }, ) @@ -302,7 +271,7 @@ async function withInstanceClient( // `initialize` handshake for a 2025-only instance. Devframe's own route is // stateless 2026-07-28, but a mixed fleet (older instances, third-party // MCP servers reached by port) may still be 2025-era. - const client = new sdk.Client( + const client = new Client( { name: 'devframe-connect', version: '0.0.0' }, { versionNegotiation: { mode: 'auto' } }, ) @@ -355,17 +324,3 @@ function errorResult(error: ConnectErrorPayload): { content: [{ type: 'text', text: JSON.stringify({ error }, null, 2) }], } } - -/** Parse the repeatable `--port` flag value(s) from cac into numbers. */ -export function parsePortsFlag(value: unknown): number[] { - const values = Array.isArray(value) ? value : value === undefined ? [] : [value] - return values - .map(v => Number(v)) - .filter(n => Number.isInteger(n) && n > 0 && n < 65536) -} - -/** Keep the connector process alive until the stdio transport closes it. */ -export function keepAlive(): void { - // stdin stays open while the MCP client holds the pipe; nothing else to do. - process.stdin.resume() -} diff --git a/packages/agentic/src/index.ts b/packages/agentic/src/index.ts new file mode 100644 index 000000000..caa76c68e --- /dev/null +++ b/packages/agentic/src/index.ts @@ -0,0 +1,14 @@ +// `@devframes/agentic` is not imported by users: it carries the MCP +// implementation (and the MCP SDK) that `devframe` loads through its own +// `devframe/adapters/mcp` entry and the `devframe connect` command. Installing +// the package next to devframe is what enables those surfaces; the `/mcp` and +// `/connect` subpaths exist for devframe's loaders. +// +// Importing the bare package is always a mistake, so it throws with a pointer +// at the user-facing API instead of silently resolving to nothing. (devframe's +// probe for this optional peer resolves `package.json`, never this entry.) +throw new Error( + '[@devframes/agentic] is not imported directly; installing it enables devframe\'s agent surfaces.\n' + + ' • import from "devframe/adapters/mcp" to serve a devframe over MCP\n' + + ' • run "devframe connect" for the stdio discovery gateway\n', +) diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts b/packages/agentic/src/mcp/__tests__/mcp-http.test.ts similarity index 97% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-http.test.ts index 4a41495ce..709109b90 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-http.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-http.test.ts @@ -1,8 +1,8 @@ -import type { StartedServer } from '../../../node/instance-shell' -import type { DevframeDefinition, McpRouteOptions } from '../../../types/devframe' +import type { StartedServer } from 'devframe/internal' +import type { DevframeDefinition, McpRouteOptions } from 'devframe/types' import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client' +import { createDevServer } from 'devframe/adapters/dev' import { afterEach, describe, expect, it } from 'vitest' -import { createDevServer } from '../../dev' const TOKEN = 'a-high-entropy-test-bearer-token' diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts b/packages/agentic/src/mcp/__tests__/mcp-locality.test.ts similarity index 96% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-locality.test.ts index 183e61f52..02456d659 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-locality.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-locality.test.ts @@ -1,6 +1,4 @@ -import type { McpAuthorization } from '../../../types/devframe' -import type { DevframeHost } from '../../../types/host' -import type { McpConnectionInfo } from '../fetch' +import type { DevframeHost, McpAuthorization, McpConnectionInfo } from 'devframe/types' import { createHostContext } from 'devframe/node' import { isLoopbackAddress } from 'devframe/utils/origin' import { afterEach, describe, expect, it } from 'vitest' diff --git a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts b/packages/agentic/src/mcp/__tests__/mcp-server.test.ts similarity index 99% rename from packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts rename to packages/agentic/src/mcp/__tests__/mcp-server.test.ts index 9c95c3fac..ba1efe25d 100644 --- a/packages/devframe/src/adapters/mcp/__tests__/mcp-server.test.ts +++ b/packages/agentic/src/mcp/__tests__/mcp-server.test.ts @@ -1,4 +1,4 @@ -import type { DevframeHost } from '../../../types/host' +import type { DevframeHost } from 'devframe/types' import { Client, InMemoryTransport } from '@modelcontextprotocol/client' import { createHostContext } from 'devframe/node' import { describe, expect, it } from 'vitest' diff --git a/packages/devframe/src/adapters/mcp/build-server.ts b/packages/agentic/src/mcp/build-server.ts similarity index 91% rename from packages/devframe/src/adapters/mcp/build-server.ts rename to packages/agentic/src/mcp/build-server.ts index 2ca735d80..0860f5da1 100644 --- a/packages/devframe/src/adapters/mcp/build-server.ts +++ b/packages/agentic/src/mcp/build-server.ts @@ -1,44 +1,15 @@ import type { Tool } from '@modelcontextprotocol/server' import type { StandardSchemaV1 } from '@standard-schema/spec' import type { RpcFunctionDefinitionAnyWithContext } from 'devframe/rpc' -import type { AgentTool, DevframeDefinition, DevframeHost, DevframeNodeContext } from 'devframe/types' +import type { AgentTool, CreateMcpServerOptions, DevframeDefinition, DevframeHost, DevframeNodeContext, McpServerHandle } from 'devframe/types' import { homedir } from 'node:os' import process from 'node:process' import { Server } from '@modelcontextprotocol/server' +import { DEVFRAME_EVENTS } from 'devframe/constants' +import { argsToJsonSchema, diagnostics, formatMcpError, returnToJsonSchema, stringifyForMcp } from 'devframe/internal' import { createHostContext } from 'devframe/node' import { toAgentToolName } from 'devframe/utils/agent-tool-name' import { join } from 'pathe' -import { DEVFRAME_EVENTS } from '../../events' -import { diagnostics } from '../../node/diagnostics' -import { formatMcpError, stringifyForMcp } from './stringify' -import { argsToJsonSchema, returnToJsonSchema } from './to-json-schema' - -export interface CreateMcpServerOptions { - /** - * Transport to use. `createMcpServer` itself runs `'stdio'` (a standalone - * process with its own host context); the Streamable-HTTP transport is - * served route-based by the dev server instead; see `mountMcpHttp` and - * the `mcp` option on `createDevServer` / `createCac`'s `--mcp` flag. - */ - transport?: 'stdio' - /** - * Expose shared-state keys as MCP resources. - * - `true` (default): every key the host publishes - * - `false`: none - * - `(key) => boolean`: filter - */ - exposeSharedState?: boolean | ((key: string) => boolean) - /** Override the name reported in the MCP handshake. */ - serverName?: string - /** Override the version reported in the MCP handshake. Defaults to `definition.version ?? '0.0.0'`. */ - serverVersion?: string - /** Called once the transport is connected. */ - onReady?: (info: { transport: 'stdio' }) => void -} - -export interface McpServerHandle { - stop: () => Promise -} export interface BuildMcpServerOptions { serverName: string diff --git a/packages/devframe/src/adapters/mcp/fetch.ts b/packages/agentic/src/mcp/fetch.ts similarity index 72% rename from packages/devframe/src/adapters/mcp/fetch.ts rename to packages/agentic/src/mcp/fetch.ts index 8ff63c9c8..f8378dd05 100644 --- a/packages/devframe/src/adapters/mcp/fetch.ts +++ b/packages/agentic/src/mcp/fetch.ts @@ -1,37 +1,9 @@ -import type { DevframeNodeContext, McpAuthorization } from 'devframe/types' +import type { CreateMcpFetchHandlerOptions, DevframeNodeContext, McpAuthorization, McpConnectionInfo, McpFetchHandler } from 'devframe/types' import { createMcpHandler } from '@modelcontextprotocol/server' import { timingSafeEqual } from 'devframe/utils/crypto-token' import { isAllowedOrigin, isLoopbackAddress } from 'devframe/utils/origin' import { bridgeListChanged, buildMcpServerFromContext } from './build-server' -export interface CreateMcpFetchHandlerOptions { - /** Name reported in the MCP handshake. */ - serverName: string - /** Version reported in the MCP handshake. */ - serverVersion: string - /** Expose shared-state keys as MCP resources; see `buildMcpServerFromContext`. */ - exposeSharedState: boolean | ((key: string) => boolean) - /** - * Optional identity check, layered on top of the origin gate and checked - * **after** it: a bearer token string (matched in constant time against - * `Authorization: Bearer `), a `(request) => boolean` callback, or - * `false` (the default) for origin-only, trusting same-machine callers. A - * callback governs identity only and cannot relax the origin gate. See - * {@link McpAuthorization}. - */ - authorization?: McpAuthorization - /** - * Origin allow-list beyond the loopback default. `false` disables the - * origin gate entirely. Default: loopback-only. - * - * Unlike the WS transport, the MCP route does **not** allow `Origin`-less - * requests: a route-based endpoint is reachable by any local process, so a - * request must carry an `Origin` that passes the gate. Native clients - * (e.g. `devframe connect`) send their loopback origin explicitly. - */ - allowedOrigins?: readonly string[] | false -} - /** * Parse exactly one `Authorization: Bearer ` credential, returning the * token or `undefined` for a missing, malformed, empty, or multi-credential @@ -62,31 +34,6 @@ async function isAuthorized(req: Request, authorization: McpAuthorization): Prom return timingSafeEqual(token, authorization) } -/** Connection facts a host knows about a request beyond the `Request` itself. */ -export interface McpConnectionInfo { - /** - * The connecting peer's remote address (a node socket's `remoteAddress`), - * used to prove a same-machine caller when the endpoint relies on the - * loopback origin default with no identity check. A host that can resolve a - * trustworthy peer address (the h3/node mount) supplies it; when it's - * omitted the origin gate stays the only locality signal. - */ - remoteAddress?: string -} - -export interface McpFetchHandler { - /** - * WHATWG-`fetch` handler for the MCP endpoint. Hand every method - * (POST/GET/DELETE) on the endpoint's path to it; routing by path is the - * host's job. Pass {@link McpConnectionInfo} when the host can resolve the - * peer address so the default trust boundary can enforce same-machine - * locality. - */ - fetch: (request: Request, connection?: McpConnectionInfo) => Promise - /** Tear down the handler (aborts in-flight exchanges, drops the change bridge). */ - dispose: () => Promise -} - /** * Build a framework-agnostic MCP endpoint over a devframe context: a * web-standard `Request → Response` handler any host can mount: h3 (see diff --git a/packages/devframe/src/adapters/mcp/http.ts b/packages/agentic/src/mcp/http.ts similarity index 88% rename from packages/devframe/src/adapters/mcp/http.ts rename to packages/agentic/src/mcp/http.ts index e6e150026..821a5ef2b 100644 --- a/packages/devframe/src/adapters/mcp/http.ts +++ b/packages/agentic/src/mcp/http.ts @@ -1,16 +1,9 @@ +import type { MountedMcpHttp, MountMcpHttpOptions } from 'devframe/internal' import type { DevframeNodeContext } from 'devframe/types' import type { H3, H3Event } from 'h3' -import type { CreateMcpFetchHandlerOptions } from './fetch' import { defineHandler, getRequestIP } from 'h3' import { createMcpFetchHandler } from './fetch' -export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} - -export interface MountedMcpHttp { - /** Tear down the MCP handler (aborts in-flight exchanges, drops the change bridge). */ - dispose: () => Promise -} - /** * Mount a stateless MCP endpoint on an h3 app at `path`: the h3 binding over * {@link createMcpFetchHandler}, which owns the per-request serving, the diff --git a/packages/agentic/src/mcp/index.ts b/packages/agentic/src/mcp/index.ts new file mode 100644 index 000000000..04a3c898d --- /dev/null +++ b/packages/agentic/src/mcp/index.ts @@ -0,0 +1,23 @@ +// The devframe MCP adapter implementation: translates the agent-host surface +// of a DevframeDefinition into an MCP server. Users import it through +// `devframe/adapters/mcp` (which lazy-loads this entry); this subpath exists +// for devframe's loaders, not for direct consumption. +// +// The MCP SDK behind it is a regular dependency of `@devframes/agentic`, an +// optional peer of `devframe`; first-party adapters load this entry lazily +// (`importRuntimeModule`) so neither enters a consumer bundle graph. The +// exported signatures are typed against devframe's own `types/mcp.ts` +// contract - no SDK type leaks, so the SDK stays swappable. + +export { createMcpServer } from './build-server' +export { createMcpFetchHandler } from './fetch' +export { mountMcpHttp } from './http' + +export type { MountedMcpHttp, MountMcpHttpOptions } from 'devframe/internal' +export type { + CreateMcpFetchHandlerOptions, + CreateMcpServerOptions, + McpConnectionInfo, + McpFetchHandler, + McpServerHandle, +} from 'devframe/types' diff --git a/packages/agentic/tsconfig.json b/packages/agentic/tsconfig.json new file mode 100644 index 000000000..6646b7b4e --- /dev/null +++ b/packages/agentic/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["esnext", "dom"], + "types": ["node"] + }, + "include": ["src", "tsdown.config.ts"], + "exclude": ["dist", "node_modules"] +} diff --git a/packages/agentic/tsdown.config.ts b/packages/agentic/tsdown.config.ts new file mode 100644 index 000000000..f1cdda1ad --- /dev/null +++ b/packages/agentic/tsdown.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'tsdown' + +export default defineConfig({ + entry: { + 'index': 'src/index.ts', + 'mcp/index': 'src/mcp/index.ts', + 'connect/index': 'src/connect/index.ts', + }, + outExtensions: () => ({ js: '.mjs', dts: '.d.mts' }), + clean: true, + tsconfig: '../../tsconfig.base.json', + dts: true, + platform: 'node', +}) diff --git a/packages/agentic/vitest.config.ts b/packages/agentic/vitest.config.ts new file mode 100644 index 000000000..729f6dbe9 --- /dev/null +++ b/packages/agentic/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' +import { alias } from '../../alias' + +export default defineConfig({ + resolve: { alias }, + test: { + name: 'agentic', + testTimeout: 10_000, + }, +}) diff --git a/packages/devframe/package.json b/packages/devframe/package.json index fd760c556..5d77dea61 100644 --- a/packages/devframe/package.json +++ b/packages/devframe/package.json @@ -82,11 +82,11 @@ "prepack": "turbo run build --filter=devframe && mkdir -p ./skills && cp -r ../../skills/devframe ./skills/devframe" }, "peerDependencies": { - "@modelcontextprotocol/client": "^2.0.0", + "@devframes/agentic": "workspace:*", "cac": "^7.0.0" }, "peerDependenciesMeta": { - "@modelcontextprotocol/client": { + "@devframes/agentic": { "optional": true }, "cac": { @@ -94,7 +94,6 @@ } }, "dependencies": { - "@modelcontextprotocol/server": "catalog:deps", "@standard-schema/spec": "catalog:deps", "birpc": "catalog:deps", "crossws": "catalog:deps", @@ -105,7 +104,7 @@ "ufo": "catalog:deps" }, "devDependencies": { - "@modelcontextprotocol/client": "catalog:deps", + "@devframes/agentic": "workspace:*", "cac": "catalog:deps", "get-port-please": "catalog:deps", "immer": "catalog:deps", diff --git a/packages/devframe/src/adapters/_shared.ts b/packages/devframe/src/adapters/_shared.ts index c68d6f8ae..7c22a84ff 100644 --- a/packages/devframe/src/adapters/_shared.ts +++ b/packages/devframe/src/adapters/_shared.ts @@ -1,10 +1,11 @@ +import type { AgenticMcpModule } from '../node/agentic' import type { DevframeAgentHost } from '../types/agent' import type { ConnectionMeta } from '../types/context' import type { DevframeDefinition, DevframeDeploymentKind, McpAuthorization, McpSetting } from '../types/devframe' import { getPort } from 'get-port-please' import { cleanDoubleSlashes, withLeadingSlash, withoutLeadingSlash, withTrailingSlash } from 'ufo' import { DEVFRAME_MCP_ROUTE } from '../constants' -import { importRuntimeModule } from '../node/import-runtime-module' +import { importAgenticMcp, isAgenticInstalled, warnAgenticMcpMissingOnce } from '../node/agentic' const DEFAULT_PORT = 9999 @@ -101,21 +102,24 @@ export function resolveMcpConfig(mcp: McpSetting | undefined): ResolvedMcpConfig /** * Resolve the `mcp: 'auto'` default at mount time: import the MCP adapter - * when the devframe's agent surface is non-empty, or return `undefined` - * (mount nothing) when the surface is empty - the zero-cost path, loading - * no MCP code at all. The adapter (and the MCP SDK behind it) loads through - * `importRuntimeModule`, so it never enters a consumer's bundle graph. - * - * Generic like `importRuntimeModule`: the caller names the module type - * (`typeof import('devframe/adapters/mcp')`) so this shared helper carries - * no type-level dependency on the MCP adapter. + * from the optional `@devframes/agentic` peer when the devframe's agent + * surface is non-empty, or return `undefined` (mount nothing) when the + * surface is empty - the zero-cost path, loading no MCP code at all. A + * non-empty surface with the peer absent also mounts nothing, reporting a + * one-time DF0078 warning instead. The adapter (and the MCP SDK behind it) + * loads through `importRuntimeModule`, so it never enters a consumer's + * bundle graph. */ -export async function loadAutoMcpAdapter( +export async function loadAutoMcpAdapter( agent: Pick, -): Promise { +): Promise { if (!agent.hasSurface()) return undefined - return await importRuntimeModule('devframe/adapters/mcp') + if (!isAgenticInstalled()) { + warnAgenticMcpMissingOnce() + return undefined + } + return await importAgenticMcp() } /** diff --git a/packages/devframe/src/adapters/cac.ts b/packages/devframe/src/adapters/cac.ts index 225f5136e..82379df8b 100644 --- a/packages/devframe/src/adapters/cac.ts +++ b/packages/devframe/src/adapters/cac.ts @@ -12,7 +12,7 @@ import type { DevframeDefinition, McpSetting } from '../types/devframe' import process from 'node:process' import cac from 'cac' import { colors as c } from 'devframe/utils/colors' -import { importRuntimeModule } from '../node/import-runtime-module' +import { importAgenticMcp } from '../node/agentic' import { createBuild } from './build' import { createDevServer, resolveDevServerPort } from './dev' import { flagKeyToOption, isBooleanFlag, parseCliFlags } from './flags' @@ -138,7 +138,8 @@ export function createCac(d: DevframeDefinition, options: CreateCacOptions = {}) // MCP clients expect JSON-RPC on stdout, so route welcome/logging // noise out of the way. Logs-SDK diagnostics land on stderr by // default, so nothing extra needed beyond not printing here. - const { createMcpServer } = await importRuntimeModule('devframe/adapters/mcp') + // Explicit invocation: a missing `@devframes/agentic` throws DF0079. + const { createMcpServer } = await importAgenticMcp() await createMcpServer(d, { transport: 'stdio', /** Deliberately go to stderr: stdout is the MCP transport. */ diff --git a/packages/devframe/src/adapters/initiate.ts b/packages/devframe/src/adapters/initiate.ts index 6b9549c71..dd00e68be 100644 --- a/packages/devframe/src/adapters/initiate.ts +++ b/packages/devframe/src/adapters/initiate.ts @@ -3,6 +3,7 @@ import type { ConnectionMeta, DevframeNodeContext, DevframeNodeRpcSession, Devfr import type { Buffer } from 'node:buffer' import type { IncomingMessage, Server as NodeHttpServer, ServerResponse } from 'node:http' import type { Duplex } from 'node:stream' +import type { AgenticMcpModule } from '../node/agentic' import type { DevframeAuthHandler } from '../node/auth/handler' import type { DevframeInstanceRecord } from '../node/instance-registry' import type { InstanceShellInternals, StartedServer } from '../node/instance-shell' @@ -17,10 +18,10 @@ import { resolve } from 'pathe' import { joinURL, withoutLeadingSlash } from 'ufo' import { resolveClientAssets } from '../client-assets' import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_MCP_ROUTE } from '../constants' +import { importAgenticMcp } from '../node/agentic' import { createHostContext } from '../node/context' import { diagnostics } from '../node/diagnostics' import { createH3DevframeHost } from '../node/host-h3' -import { importRuntimeModule } from '../node/import-runtime-module' import { createInstanceShell, resolveInstanceRegister } from '../node/instance-shell' import { loadAutoMcpAdapter, normalizeBasePath, resolveMcpConfig } from './_shared' import { resolveDevServerPort } from './dev' @@ -345,11 +346,14 @@ export function initDevframe( /** * Mount the route-based MCP server at `__mcp`, before the SPA static - * catch-all so the exact route wins. The MCP SDK is pulled in dynamically - * only when the route mounts, keeping it out of consumer bundles: `'auto'` - * mounts once `setup()` left a non-empty agent surface (an empty surface - * loads no MCP code); an explicit setting mounts unconditionally. The - * resolved config is origin-only unless it opts into a bearer/callback. + * catch-all so the exact route wins. The adapter (from the optional + * `@devframes/agentic` peer) is pulled in dynamically only when the route + * mounts, keeping it and the MCP SDK out of consumer bundles: `'auto'` + * mounts once `setup()` left a non-empty agent surface AND the peer is + * installed (an empty surface loads no MCP code; a non-empty one without the + * peer warns once and mounts nothing); an explicit setting mounts + * unconditionally, throwing DF0079 when the peer is absent. The resolved + * config is origin-only unless it opts into a bearer/callback. */ async function mountMcpRoute( app: H3, @@ -358,10 +362,10 @@ async function mountMcpRoute( base: string, setting: McpSetting, ): Promise<{ meta: ConnectionMeta['mcp'], dispose: () => Promise } | undefined> { - let module: typeof import('./mcp') | undefined + let module: AgenticMcpModule | undefined let config: ResolvedMcpConfig | undefined if (setting === 'auto') { - module = await loadAutoMcpAdapter(context.agent) + module = await loadAutoMcpAdapter(context.agent) if (module) config = { authorization: false } } @@ -372,15 +376,7 @@ async function mountMcpRoute( return undefined const route = withoutLeadingSlash(config.path ?? DEVFRAME_MCP_ROUTE) - if (!module) { - try { - module = await importRuntimeModule('devframe/adapters/mcp') - } - catch (error) { - const reason = error instanceof Error ? error.message : String(error) - throw diagnostics.DF0017({ transport: 'http', reason, cause: error }) - } - } + module ??= await importAgenticMcp() const mounted = module.mountMcpHttp(app, context, joinURL(base, route), { serverName: `${def.id} (devframe)`, serverVersion: def.version ?? '0.0.0', diff --git a/packages/devframe/src/adapters/mcp.ts b/packages/devframe/src/adapters/mcp.ts new file mode 100644 index 000000000..437f5b843 --- /dev/null +++ b/packages/devframe/src/adapters/mcp.ts @@ -0,0 +1,14 @@ +// The user-facing MCP adapter entry: a re-export of the implementation in +// the optional `@devframes/agentic` peer (which also carries the MCP SDK). +// Like `devframe/adapters/cac` with its optional `cac` peer, importing it +// without the peer throws module-not-found. First-party adapters lazy-load +// the peer through `node/agentic.ts` (DF0078/DF0079) instead. +export type { MountedMcpHttp, MountMcpHttpOptions } from '../node/agentic' +export type { + CreateMcpFetchHandlerOptions, + CreateMcpServerOptions, + McpConnectionInfo, + McpFetchHandler, + McpServerHandle, +} from '../types/mcp' +export { createMcpFetchHandler, createMcpServer, mountMcpHttp } from '@devframes/agentic/mcp' diff --git a/packages/devframe/src/adapters/mcp/index.ts b/packages/devframe/src/adapters/mcp/index.ts deleted file mode 100644 index 0482cf1e8..000000000 --- a/packages/devframe/src/adapters/mcp/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Public entry for the devframe MCP adapter. Translates the agent-host -// surface of a DevframeDefinition into an MCP server. -// -// Usage: -// import { createMcpServer } from 'devframe/adapters/mcp' -// await createMcpServer(definition, { transport: 'stdio' }) -// -// The MCP SDK behind it is a regular dependency of `devframe`; first-party -// adapters still load this entry lazily (`importRuntimeModule`) so the SDK -// stays out of consumer bundle graphs. - -export { - createMcpServer, - type CreateMcpServerOptions, - type McpServerHandle, -} from './build-server' - -export { - createMcpFetchHandler, - type CreateMcpFetchHandlerOptions, - type McpConnectionInfo, - type McpFetchHandler, -} from './fetch' - -export { - type MountedMcpHttp, - mountMcpHttp, - type MountMcpHttpOptions, -} from './http' diff --git a/packages/devframe/src/adapters/mcp/__tests__/stringify.test.ts b/packages/devframe/src/agent/__tests__/stringify.test.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/__tests__/stringify.test.ts rename to packages/devframe/src/agent/__tests__/stringify.test.ts diff --git a/packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts b/packages/devframe/src/agent/__tests__/to-json-schema.test.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/__tests__/to-json-schema.test.ts rename to packages/devframe/src/agent/__tests__/to-json-schema.test.ts diff --git a/packages/devframe/src/adapters/mcp/stringify.ts b/packages/devframe/src/agent/stringify.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/stringify.ts rename to packages/devframe/src/agent/stringify.ts diff --git a/packages/devframe/src/adapters/mcp/to-json-schema.ts b/packages/devframe/src/agent/to-json-schema.ts similarity index 100% rename from packages/devframe/src/adapters/mcp/to-json-schema.ts rename to packages/devframe/src/agent/to-json-schema.ts diff --git a/packages/devframe/src/cli/main.ts b/packages/devframe/src/cli/main.ts index 95243c7a3..984758ef9 100644 --- a/packages/devframe/src/cli/main.ts +++ b/packages/devframe/src/cli/main.ts @@ -1,6 +1,39 @@ import process from 'node:process' import { cac } from 'cac' -import { keepAlive, parsePortsFlag, startConnectServer } from './connect' +import { diagnostics } from '../node/diagnostics' +import { importRuntimeModule } from '../node/import-runtime-module' + +/** The surface `devframe connect` consumes from `@devframes/agentic/connect`. */ +interface AgenticConnectModule { + startConnectServer: (options: { + ports?: number[] + instancesDir?: string + timeoutMs?: number + authToken?: string + }) => Promise<{ stop: () => Promise }> +} + +/** Parse the repeatable `--port` flag value(s) from cac into numbers. */ +function parsePortsFlag(value: unknown): number[] { + const values = Array.isArray(value) ? value : value === undefined ? [] : [value] + return values + .map(v => Number(v)) + .filter(n => Number.isInteger(n) && n > 0 && n < 65536) +} + +/** + * Load the connector from the optional `@devframes/agentic` peer, mapping a + * failed load (typically: the peer is not installed) to a thrown `DF0046`. + */ +async function importConnect(): Promise { + try { + return await importRuntimeModule('@devframes/agentic/connect') + } + catch (error) { + const reason = error instanceof Error ? error.message : String(error) + throw diagnostics.DF0046({ reason, cause: error }) + } +} /** * The `devframe` bin is the framework's own CLI, distinct from the per-app @@ -16,6 +49,7 @@ export async function runDevframeCli(argv: string[] = process.argv): Promise', 'Override the instance registry directory (default: ~/.devframe/instances, or $DEVFRAME_INSTANCES_DIR)') .option('--timeout ', 'Probe timeout per instance in milliseconds', { default: 1000 }) .action(async (options: { port?: unknown, instancesDir?: string, timeout?: number }) => { + const { startConnectServer } = await importConnect() await startConnectServer({ ports: parsePortsFlag(options.port), instancesDir: options.instancesDir, @@ -27,7 +61,9 @@ export async function runDevframeCli(argv: string[] = process.argv): Promise { + it('resolves the installed optional peer without importing it', () => { + expect(isAgenticInstalled()).toBe(true) + }) + + it('is false for an unresolvable package', () => { + expect(isAgenticInstalled('@devframes/definitely-not-installed')).toBe(false) + }) +}) + +describe('importAgenticMcp', () => { + it('maps a failed load to a thrown DF0079', async () => { + const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}) + try { + const error = await importAgenticMcp('@devframes/definitely-not-installed/mcp').catch(e => e) + expect(error).toBeInstanceOf(Diagnostic) + expect((error as Diagnostic).code).toBe('DF0079') + } + finally { + warn.mockRestore() + } + }) +}) diff --git a/packages/devframe/src/node/agentic.ts b/packages/devframe/src/node/agentic.ts new file mode 100644 index 000000000..926887ba5 --- /dev/null +++ b/packages/devframe/src/node/agentic.ts @@ -0,0 +1,69 @@ +import type { H3 } from 'h3' +import type { DevframeNodeContext } from '../types/context' +import type { DevframeDefinition } from '../types/devframe' +import type { CreateMcpFetchHandlerOptions, CreateMcpServerOptions, McpFetchHandler, McpServerHandle } from '../types/mcp' +import { diagnostics } from './diagnostics' +import { importRuntimeModule, isRuntimeModuleResolvable } from './import-runtime-module' + +/** + * The h3-bound half of the MCP adapter contract (the SDK-neutral half lives + * in `types/mcp.ts`, which must stay lib-neutral; h3's declarations are not). + */ +export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} + +export interface MountedMcpHttp { + /** Tear down the MCP handler (aborts in-flight exchanges, drops the change bridge). */ + dispose: () => Promise +} + +/** The surface `@devframes/agentic/mcp` exports, as devframe's loaders consume it. */ +export interface AgenticMcpModule { + /** Build an MCP server over the agent surface of a devframe definition (stdio). */ + createMcpServer: (definition: DevframeDefinition, options?: CreateMcpServerOptions) => Promise + /** Build a framework-agnostic `Request → Response` MCP endpoint over a devframe context. */ + createMcpFetchHandler: (ctx: DevframeNodeContext, options: CreateMcpFetchHandlerOptions) => McpFetchHandler + /** Mount a stateless MCP endpoint on an h3 app at `path`. */ + mountMcpHttp: (app: H3, ctx: DevframeNodeContext, path: string, options: MountMcpHttpOptions) => MountedMcpHttp +} + +// `@devframes/agentic` is devframe's optional peer carrying the MCP adapter +// and the MCP SDK. These helpers implement the enable matrix around it: +// probe (never import) to decide, warn once under `'auto'` when the peer is +// absent, throw a coded error when an explicit `mcp` setting needs it. + +const AGENTIC_PACKAGE = '@devframes/agentic' + +/** + * Whether `@devframes/agentic` is resolvable from devframe's own location + * (where its optional peer is linked). A pure `require.resolve` probe: no + * module is loaded, so the `'auto'` miss path stays zero-cost. + */ +export function isAgenticInstalled(pkg: string = AGENTIC_PACKAGE): boolean { + return isRuntimeModuleResolvable(`${pkg}/package.json`) +} + +let warnedMissing = false + +/** Report DF0078 (agent surface without `@devframes/agentic`) once per process. */ +export function warnAgenticMcpMissingOnce(): void { + if (warnedMissing) + return + warnedMissing = true + diagnostics.DF0078() +} + +/** + * Load the MCP adapter from `@devframes/agentic/mcp`, mapping a failed load + * (typically: the optional peer is not installed) to a thrown `DF0079`. + * Loads through `importRuntimeModule`, so the adapter and the MCP SDK behind + * it never enter a consumer's bundle graph. + */ +export async function importAgenticMcp(specifier: string = `${AGENTIC_PACKAGE}/mcp`): Promise { + try { + return await importRuntimeModule(specifier) + } + catch (error) { + const reason = error instanceof Error ? error.message : String(error) + throw diagnostics.DF0079({ reason, cause: error }) + } +} diff --git a/packages/devframe/src/node/diagnostics.ts b/packages/devframe/src/node/diagnostics.ts index a075e3d47..ef2c8dea6 100644 --- a/packages/devframe/src/node/diagnostics.ts +++ b/packages/devframe/src/node/diagnostics.ts @@ -88,8 +88,8 @@ export const diagnostics = defineDiagnostics({ fix: 'Discovery tooling (`devframe connect`) will not see this instance. Check that the registry directory is writable, point `DEVFRAME_INSTANCES_DIR` at a writable directory, or set `DEVFRAME_DISABLE_INSTANCE_REGISTRY=1` to opt out of registration.', }, DF0046: { - why: (p: { reason: string }) => `\`devframe connect\` requires the optional peer dependency @modelcontextprotocol/client: ${p.reason}`, - fix: 'Install it next to devframe (e.g. `npm install @modelcontextprotocol/client`) and run `devframe connect` again.', + why: (p: { reason: string }) => `\`devframe connect\` requires the optional peer dependency @devframes/agentic: ${p.reason}`, + fix: 'Install it next to devframe (e.g. `npm install @devframes/agentic`) and run `devframe connect` again.', }, DF0047: { why: (p: { name: string, id: string, existing: string }) => @@ -211,5 +211,14 @@ export const diagnostics = defineDiagnostics({ `\`attach\` / \`handleUpgrade\` drive a raw \`node:http\` upgrade into crossws's Node adapter, which refuses to run on ${p.runtime}.`, fix: 'On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno example), or connect over the SSE endpoint instead.', }, + DF0078: { + why: 'This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer "@devframes/agentic" is not installed.', + fix: 'Install `@devframes/agentic` next to devframe to serve the MCP endpoint, or set `mcp: false` to opt out silently.', + }, + DF0079: { + why: (p: { reason: string }) => + `The \`mcp\` option is enabled, but the optional peer "@devframes/agentic" could not be loaded: ${p.reason}`, + fix: 'Install `@devframes/agentic` next to devframe (the MCP adapter and the MCP SDK live there), or remove the explicit `mcp` setting.', + }, }, }) diff --git a/packages/devframe/src/node/import-runtime-module.ts b/packages/devframe/src/node/import-runtime-module.ts index 1f056d69b..bc4ed34c6 100644 --- a/packages/devframe/src/node/import-runtime-module.ts +++ b/packages/devframe/src/node/import-runtime-module.ts @@ -1,4 +1,4 @@ -import { importServicePackage } from './services-install' +import { importServicePackage, resolveServicePackage } from './services-install' /** * Resolve and import a package at runtime without adding it to a consumer's @@ -11,3 +11,13 @@ import { importServicePackage } from './services-install' export async function importRuntimeModule(specifier: string): Promise { return await importServicePackage(specifier, [import.meta.url]) as T } + +/** + * Whether {@link importRuntimeModule} would resolve `specifier`, without + * importing anything. The zero-cost probe behind optional-peer decisions. + * + * @internal + */ +export function isRuntimeModuleResolvable(specifier: string): boolean { + return resolveServicePackage(specifier, [import.meta.url]) !== undefined +} diff --git a/packages/devframe/src/node/services-install.ts b/packages/devframe/src/node/services-install.ts index d661ddb1c..e69aef570 100644 --- a/packages/devframe/src/node/services-install.ts +++ b/packages/devframe/src/node/services-install.ts @@ -41,6 +41,29 @@ export function expandResolveFrom(resolveFrom: string, cwd: string): string | un return undefined } +/** + * Resolve a package specifier against each `resolveFrom` candidate in order, + * returning the resolved absolute path or `undefined`. A pure probe: nothing + * is imported. The resolution base arrives as a parameter (not a literal + * `createRequire(import.meta.url)`), which keeps bundlers that special-case + * that pattern (turbopack rewrites its `.resolve()` of a dynamic specifier + * into a throwing stub) from touching the runtime resolution. + */ +export function resolveServicePackage( + pkg: string, + resolveFrom: readonly (string | null | undefined)[], +): string | undefined { + for (const from of resolveFrom) { + if (typeof from !== 'string' || from.length === 0) + continue + try { + return createRequire(toRequireBase(from)).resolve(pkg) + } + catch {} + } + return undefined +} + /** * Import a service package's module, trying each `resolveFrom` candidate in * order (so a plugin-declared service resolves against the plugin's own diff --git a/packages/devframe/src/types/devframe.ts b/packages/devframe/src/types/devframe.ts index 6e39f18d5..97323eadb 100644 --- a/packages/devframe/src/types/devframe.ts +++ b/packages/devframe/src/types/devframe.ts @@ -111,12 +111,15 @@ export type McpAuthorization * * - `'auto'`, the default: mount the route when the devframe exposes an * agent surface (an `agent`-flagged RPC function, or a tool / resource / - * provider registered on `ctx.agent`). An empty agent surface mounts - * nothing and loads no MCP code. - * - `true`: always mount at the default `__mcp` route. - * - `false`: never mount. - * - {@link McpRouteOptions}: always mount, with a custom route path, origin - * allow-list, or {@link McpAuthorization} identity check. + * provider registered on `ctx.agent`) AND the optional `@devframes/agentic` + * peer (the MCP adapter and SDK) is installed. An empty agent surface + * mounts nothing and loads no MCP code; a non-empty one without the peer + * warns once (DF0078) and mounts nothing. + * - `true`: always mount at the default `__mcp` route; a missing + * `@devframes/agentic` throws DF0079. + * - `false`: never mount, and never probe or warn. + * - {@link McpRouteOptions}: always mount (like `true`), with a custom route + * path, origin allow-list, or {@link McpAuthorization} identity check. * * A mounted route trusts same-machine callers by default (the loopback * origin gate), exactly like `mcp: true`. diff --git a/packages/devframe/src/types/index.ts b/packages/devframe/src/types/index.ts index ce4024d10..a406a920d 100644 --- a/packages/devframe/src/types/index.ts +++ b/packages/devframe/src/types/index.ts @@ -4,6 +4,7 @@ export * from './devframe' export * from './diagnostics' export * from './events' export * from './host' +export * from './mcp' export * from './remote-assets' export * from './rpc' export * from './rpc-augments' diff --git a/packages/devframe/src/types/mcp.ts b/packages/devframe/src/types/mcp.ts new file mode 100644 index 000000000..cdb9e003a --- /dev/null +++ b/packages/devframe/src/types/mcp.ts @@ -0,0 +1,91 @@ +import type { McpAuthorization } from './devframe' + +/** + * The MCP adapter surface, typed here so `devframe` (which probes for and + * lazily loads `@devframes/agentic`) and `@devframes/agentic/mcp` (which + * implements it) share one contract without a type-level dependency cycle. + * None of these shapes reference the MCP SDK: the SDK is an implementation + * detail of `@devframes/agentic`, freely swappable behind this surface. + * The h3-bound `mountMcpHttp` shapes live in `node/agentic.ts` (exported via + * `devframe/internal`) instead: `devframe/types` must stay lib-neutral, and + * h3's declarations are not. + */ +export interface CreateMcpServerOptions { + /** + * Transport to use. `createMcpServer` itself runs `'stdio'` (a standalone + * process with its own host context); the Streamable-HTTP transport is + * served route-based by the dev server instead; see `mountMcpHttp` and + * the `mcp` option on `createDevServer` / `createCac`'s `--mcp` flag. + */ + transport?: 'stdio' + /** + * Expose shared-state keys as MCP resources. + * - `true` (default): every key the host publishes + * - `false`: none + * - `(key) => boolean`: filter + */ + exposeSharedState?: boolean | ((key: string) => boolean) + /** Override the name reported in the MCP handshake. */ + serverName?: string + /** Override the version reported in the MCP handshake. Defaults to `definition.version ?? '0.0.0'`. */ + serverVersion?: string + /** Called once the transport is connected. */ + onReady?: (info: { transport: 'stdio' }) => void +} + +export interface McpServerHandle { + stop: () => Promise +} + +export interface CreateMcpFetchHandlerOptions { + /** Name reported in the MCP handshake. */ + serverName: string + /** Version reported in the MCP handshake. */ + serverVersion: string + /** Expose shared-state keys as MCP resources; see {@link CreateMcpServerOptions.exposeSharedState}. */ + exposeSharedState: boolean | ((key: string) => boolean) + /** + * Optional identity check, layered on top of the origin gate and checked + * **after** it: a bearer token string (matched in constant time against + * `Authorization: Bearer `), a `(request) => boolean` callback, or + * `false` (the default) for origin-only, trusting same-machine callers. A + * callback governs identity only and cannot relax the origin gate. See + * {@link McpAuthorization}. + */ + authorization?: McpAuthorization + /** + * Origin allow-list beyond the loopback default. `false` disables the + * origin gate entirely. Default: loopback-only. + * + * Unlike the WS transport, the MCP route does **not** allow `Origin`-less + * requests: a route-based endpoint is reachable by any local process, so a + * request must carry an `Origin` that passes the gate. Native clients + * (e.g. `devframe connect`) send their loopback origin explicitly. + */ + allowedOrigins?: readonly string[] | false +} + +/** Connection facts a host knows about a request beyond the `Request` itself. */ +export interface McpConnectionInfo { + /** + * The connecting peer's remote address (a node socket's `remoteAddress`), + * used to prove a same-machine caller when the endpoint relies on the + * loopback origin default with no identity check. A host that can resolve a + * trustworthy peer address (the h3/node mount) supplies it; when it's + * omitted the origin gate stays the only locality signal. + */ + remoteAddress?: string +} + +export interface McpFetchHandler { + /** + * WHATWG-`fetch` handler for the MCP endpoint. Hand every method + * (POST/GET/DELETE) on the endpoint's path to it; routing by path is the + * host's job. Pass {@link McpConnectionInfo} when the host can resolve the + * peer address so the default trust boundary can enforce same-machine + * locality. + */ + fetch: (request: Request, connection?: McpConnectionInfo) => Promise + /** Tear down the handler (aborts in-flight exchanges, drops the change bridge). */ + dispose: () => Promise +} diff --git a/packages/devframe/tsdown.config.ts b/packages/devframe/tsdown.config.ts index f41216f41..4a87c2500 100644 --- a/packages/devframe/tsdown.config.ts +++ b/packages/devframe/tsdown.config.ts @@ -116,7 +116,7 @@ const serverEntries = { 'adapters/build': 'src/adapters/build.ts', 'adapters/embedded': 'src/adapters/embedded.ts', 'adapters/initiate': 'src/adapters/initiate.ts', - 'adapters/mcp': 'src/adapters/mcp/index.ts', + 'adapters/mcp': 'src/adapters/mcp.ts', 'cli/main': 'src/cli/main.ts', 'recipes/common-rpc-functions': 'src/recipes/common-rpc-functions.ts', 'recipes/interactive-auth': 'src/recipes/interactive-auth.ts', diff --git a/packages/hub/package.json b/packages/hub/package.json index 621510ca3..cda7d690e 100644 --- a/packages/hub/package.json +++ b/packages/hub/package.json @@ -41,8 +41,14 @@ "prepack": "turbo run build --filter=@devframes/hub" }, "peerDependencies": { + "@devframes/agentic": "workspace:*", "devframe": "workspace:*" }, + "peerDependenciesMeta": { + "@devframes/agentic": { + "optional": true + } + }, "dependencies": { "@standard-schema/spec": "catalog:deps", "destr": "catalog:deps", @@ -54,6 +60,7 @@ "zigpty": "catalog:deps" }, "devDependencies": { + "@devframes/agentic": "workspace:*", "@types/node": "catalog:types", "devframe": "workspace:*", "get-port-please": "catalog:deps", diff --git a/packages/hub/src/node/initiate.ts b/packages/hub/src/node/initiate.ts index 78519220b..c43626958 100644 --- a/packages/hub/src/node/initiate.ts +++ b/packages/hub/src/node/initiate.ts @@ -1,4 +1,4 @@ -import type { DevframeInstanceRecord, InstanceShellApi, ResolvedMcpConfig } from 'devframe/internal' +import type { AgenticMcpModule, DevframeInstanceRecord, InstanceShellApi, ResolvedMcpConfig } from 'devframe/internal' import type { DevframeAuthHandler } from 'devframe/node/auth' import type { AuthBannerFunction } from 'devframe/recipes/interactive-auth' import type { WsOriginRegistry } from 'devframe/rpc/transports/ws-server' @@ -12,7 +12,7 @@ import type { InstallDevframeOptions } from './install-devframe' import { readFile } from 'node:fs/promises' import process from 'node:process' import { DEVFRAME_CONNECTION_META_FILENAME, DEVFRAME_DOCK_IMPORTS_FILENAME, DEVFRAME_MCP_ROUTE } from 'devframe/constants' -import { createH3DevframeHost, createInstanceShell, importRuntimeModule, loadAutoMcpAdapter, resolveInstanceRegister, resolveMcpConfig } from 'devframe/internal' +import { createH3DevframeHost, createInstanceShell, importAgenticMcp, loadAutoMcpAdapter, resolveInstanceRegister, resolveMcpConfig } from 'devframe/internal' import { createInteractiveAuth } from 'devframe/recipes/interactive-auth' import { mountStaticHandler } from 'devframe/utils/serve-static' import { H3 } from 'h3' @@ -500,14 +500,15 @@ export function initHub(options: InitHubOptions): HubInstance { // Aggregate MCP: one Streamable-HTTP endpoint over the shared // context's whole registry. The omitted `'auto'` default mounts when - // the devframes (or an agent-flagged hub command) left a non-empty - // agent surface; an empty surface loads no MCP code. Origin-only - // unless the config opts into a bearer/callback. + // the agent surface is non-empty AND `@devframes/agentic` is installed + // (missing peer: one DF0078 warning); an explicit setting throws + // DF0079 without the peer. Origin-only unless the config opts into a + // bearer/callback. const mcpSetting = options.mcp ?? 'auto' - let mcpModule: typeof import('devframe/adapters/mcp') | undefined + let mcpModule: AgenticMcpModule | undefined let mcpConfig: ResolvedMcpConfig | undefined if (mcpSetting === 'auto') { - mcpModule = await loadAutoMcpAdapter(ctx.agent) + mcpModule = await loadAutoMcpAdapter(ctx.agent) if (mcpModule) mcpConfig = { authorization: false } } @@ -518,7 +519,7 @@ export function initHub(options: InitHubOptions): HubInstance { return { context: ctx } const mcpRoute = withoutLeadingSlash(mcpConfig.path ?? DEVFRAME_MCP_ROUTE) - mcpModule ??= await importRuntimeModule('devframe/adapters/mcp') + mcpModule ??= await importAgenticMcp() const mounted = mcpModule.mountMcpHttp(app, ctx, joinURL(base, mcpRoute), { serverName: options.name ?? 'devframes-hub', serverVersion: options.version ?? '0.0.0', diff --git a/packages/next/src/host.ts b/packages/next/src/host.ts index 62107e9e6..cdc506b35 100644 --- a/packages/next/src/host.ts +++ b/packages/next/src/host.ts @@ -1,6 +1,6 @@ import type { ConnectionMeta, DevframeHost, DevframeNodeContext, DevframeStorageScope, McpAuthorization } from 'devframe' import { DEVFRAME_CONNECTION_META_FILENAME } from 'devframe/constants' -import { importRuntimeModule } from 'devframe/internal' +import { importAgenticMcp } from 'devframe/internal' import { serveStaticHandler } from 'devframe/utils/serve-static' import { H3 } from 'h3' @@ -81,8 +81,9 @@ export interface DevframeNextHost { * Serve an MCP Streamable-HTTP endpoint at `path` **in-process**, on the * Next app's own origin, through the same catch-all route as the SPAs (the * `/_next/mcp` shape). Built on `createMcpFetchHandler` from - * `devframe/adapters/mcp` (imported lazily, so the MCP SDK stays out of - * the app's bundle graph). Advertise the path in the connection meta + * `@devframes/agentic/mcp` (imported lazily through the optional peer, so + * neither it nor the MCP SDK enters the app's bundle graph; a missing peer + * throws DF0079). Advertise the path in the connection meta * (`mcp: { path }`, same origin, no port) and register the instance via * `registerDevframeInstance` so `devframe connect` can discover it. */ @@ -174,7 +175,7 @@ export function createDevframeNextHost( connectionMeta = meta }, async mountMcp(ctx, path, mcpOptions = {}) { - const { createMcpFetchHandler } = await importRuntimeModule('devframe/adapters/mcp') + const { createMcpFetchHandler } = await importAgenticMcp() const handler = createMcpFetchHandler(ctx, { serverName: mcpOptions.serverName ?? 'devframe (next)', serverVersion: mcpOptions.serverVersion ?? '0.0.0', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e399b34cb..de406d783 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -555,6 +555,7 @@ catalogs: version: 8.18.1 overrides: + '@devframes/agentic': workspace:* '@devframes/hub': workspace:* '@devframes/hub-ui': workspace:* '@devframes/json-render': workspace:* @@ -596,7 +597,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@antfu/eslint-config': specifier: catalog:tooling version: 9.5.1(@typescript-eslint/typescript-estree@8.69.0(supports-color@10.2.2)(typescript@6.0.3))(@typescript-eslint/utils@8.69.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3))(@vue/compiler-sfc@3.5.42)(eslint-plugin-slop@0.1.3(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint-plugin-sonarjs@4.2.0(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2)))(eslint@10.10.0(jiti@2.7.0)(supports-color@10.2.2))(supports-color@10.2.2)(typescript@6.0.3)(vitest@5.0.0(@opentelemetry/api@1.9.1)(@types/node@26.5.1)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) @@ -722,7 +723,7 @@ importers: version: 4.4.3(react@19.2.8)(shiki@4.4.3)(solid-js@1.9.15)(svelte@5.57.0(@typescript-eslint/types@8.69.0))(vue@3.5.42(typescript@6.0.3)) comark-docs: specifier: catalog:docs - version: https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(c0fd9af7c481da357e4beac4fc989fc5) + version: https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(be895fb79464905e24531015ffa32a57) d3-shape: specifier: catalog:frontend version: 3.2.0 @@ -744,7 +745,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -775,7 +776,10 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/agentic': + specifier: workspace:* + version: link:../../packages/agentic '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -875,7 +879,10 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + '@devframes/agentic': + specifier: workspace:* + version: link:../../packages/agentic '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -961,7 +968,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 @@ -1441,7 +1448,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/next': specifier: workspace:* version: link:../../packages/next @@ -1496,7 +1503,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) devframe: specifier: workspace:* version: link:../../packages/devframe @@ -1521,7 +1528,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) cac: specifier: catalog:deps version: 7.0.0 @@ -1560,11 +1567,42 @@ importers: specifier: catalog:deps version: 8.21.3 - packages/devframe: + packages/agentic: dependencies: + '@modelcontextprotocol/client': + specifier: catalog:deps + version: 2.0.0 '@modelcontextprotocol/server': specifier: catalog:deps version: 2.0.0 + '@standard-schema/spec': + specifier: catalog:deps + version: 1.1.0 + h3: + specifier: catalog:deps + version: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) + pathe: + specifier: catalog:deps + version: 2.0.3 + ufo: + specifier: catalog:deps + version: 1.6.4 + devDependencies: + '@types/node': + specifier: catalog:types + version: 26.5.1 + devframe: + specifier: workspace:* + version: link:../devframe + tsdown: + specifier: catalog:build + version: 0.23.0(@volar/typescript@2.4.28(typescript@6.0.3))(oxc-resolver@11.24.2)(tsx@4.23.13)(typescript@6.0.3) + valibot: + specifier: catalog:deps + version: 1.5.0(typescript@6.0.3) + + packages/devframe: + dependencies: '@standard-schema/spec': specifier: catalog:deps version: 1.1.0 @@ -1590,9 +1628,9 @@ importers: specifier: catalog:deps version: 1.6.4 devDependencies: - '@modelcontextprotocol/client': - specifier: catalog:deps - version: 2.0.0 + '@devframes/agentic': + specifier: workspace:* + version: link:../agentic cac: specifier: catalog:deps version: 7.0.0 @@ -1672,6 +1710,9 @@ importers: specifier: catalog:deps version: 0.2.1 devDependencies: + '@devframes/agentic': + specifier: workspace:* + version: link:../agentic '@types/node': specifier: catalog:types version: 26.5.1 @@ -1695,7 +1736,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../hub @@ -1786,7 +1827,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../hub @@ -1946,7 +1987,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-a11y--assets': specifier: workspace:* version: link:assets-pkg @@ -2024,7 +2065,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-assets--assets': specifier: workspace:* version: link:assets-pkg @@ -2060,7 +2101,7 @@ importers: version: link:../../packages/devframe floating-vue: specifier: catalog:frontend - version: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + version: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) h3: specifier: catalog:deps version: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) @@ -2099,7 +2140,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-code-server--assets': specifier: workspace:* version: link:assets-pkg @@ -2159,7 +2200,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-data-inspector--assets': specifier: workspace:* version: link:assets-pkg @@ -2249,7 +2290,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-git--assets': specifier: workspace:* version: link:assets-pkg @@ -2336,7 +2377,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -2414,7 +2455,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../../packages/hub @@ -2492,7 +2533,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-og--assets': specifier: workspace:* version: link:assets-pkg @@ -2558,7 +2599,7 @@ importers: devDependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/plugin-terminals--assets': specifier: workspace:* version: link:assets-pkg @@ -2722,7 +2763,7 @@ importers: dependencies: '@antfu/design': specifier: catalog:frontend - version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) + version: 0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3)) '@devframes/hub': specifier: workspace:* version: link:../packages/hub @@ -13008,7 +13049,7 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13017,6 +13058,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.14 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13024,7 +13066,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13033,6 +13075,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.15 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13040,7 +13083,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.6.1)(dompurify@3.4.15)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13049,6 +13092,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.6.1 dompurify: 3.4.15 floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) @@ -13056,7 +13100,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13065,6 +13109,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -13072,7 +13117,7 @@ snapshots: reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13081,14 +13126,15 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 - floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) playwright: 1.62.1 reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.14)(floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13097,14 +13143,15 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.14 - floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) + floating-vue: 5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)) playwright: 1.62.1 reka-ui: 2.10.4(vue@3.5.42(typescript@6.0.3)) splitpanes: 4.1.2(vue@3.5.42(typescript@6.0.3)) - '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': + '@antfu/design@0.4.0(@antfu/utils@9.3.0)(@iconify-json/catppuccin@1.2.17)(@tanstack/vue-virtual@3.13.36(vue@3.5.42(typescript@6.0.3)))(@unocss/core@66.10.2)(colorjs.io@0.7.1)(dompurify@3.4.15)(floating-vue@5.2.2(vue@3.5.42(typescript@6.0.3)))(playwright@1.62.1)(reka-ui@2.10.4(vue@3.5.42(typescript@6.0.3)))(splitpanes@4.1.2(vue@3.5.42(typescript@6.0.3)))(unocss@66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vue@3.5.42(typescript@6.0.3))': dependencies: '@vueuse/core': 14.4.0(vue@3.5.42(typescript@6.0.3)) unocss: 66.10.2(@unocss/postcss@66.10.2(postcss@8.5.28))(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) @@ -13113,6 +13160,7 @@ snapshots: '@antfu/utils': 9.3.0 '@iconify-json/catppuccin': 1.2.17 '@tanstack/vue-virtual': 3.13.36(vue@3.5.42(typescript@6.0.3)) + '@unocss/core': 66.10.2 colorjs.io: 0.7.1 dompurify: 3.4.15 floating-vue: 5.2.2(vue@3.5.42(typescript@6.0.3)) @@ -14361,7 +14409,7 @@ snapshots: '@modelcontextprotocol/server@2.0.0': dependencies: '@modelcontextprotocol/core': 2.0.0 - zod: 4.5.4 + zod: 4.6.5 '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: @@ -14877,7 +14925,7 @@ snapshots: - rolldown - unplugin - '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14897,7 +14945,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -14908,7 +14956,7 @@ snapshots: - unplugin optional: true - '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14928,7 +14976,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -14939,7 +14987,7 @@ snapshots: - unplugin optional: true - '@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': + '@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))': dependencies: c12: 3.3.4(magicast@0.5.4) consola: 3.4.2 @@ -14959,7 +15007,7 @@ snapshots: scule: 1.3.0 tinyglobby: 0.2.17 ufo: 1.6.4 - unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + unctx: 3.0.0(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) untyped: 2.0.0 verkit: 0.3.2 transitivePeerDependencies: @@ -15443,12 +15491,12 @@ snapshots: - rolldown - unplugin - '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5)': + '@nuxtjs/mcp-toolkit@0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5)': dependencies: '@modelcontextprotocol/sdk': 1.30.0(supports-color@10.2.2)(zod@4.6.5) '@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) '@vitejs/plugin-vue': 6.0.8(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3)) - h3: 2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)) + h3: 2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)) tinyglobby: 0.2.17 vite-plugin-singlefile: 2.3.3(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)) zod: 4.6.5 @@ -18785,7 +18833,7 @@ snapshots: - srvx - uploadthing - comark-docs@https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(c0fd9af7c481da357e4beac4fc989fc5): + comark-docs@https://codeload.github.com/comarkdown/comark-docs/tar.gz/f97dc864b0aa702d54c600f868589b7a2c185b3a(patch_hash=5f1078be206123f957de74141ae02eaae510555cc7444404bffbfaae8d407e33)(be895fb79464905e24531015ffa32a57): dependencies: '@ai-sdk/gateway': 4.0.69(zod@4.6.5) '@ai-sdk/vue': 4.0.85(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) @@ -18796,7 +18844,7 @@ snapshots: '@iconify/vue': 5.0.1(vue@3.5.42(typescript@6.0.3)) '@nuxt/kit': 4.5.2(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) '@nuxt/ui': 4.11.0(2f33558f9603787933b49f7068ea6162) - '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) + '@nuxtjs/mcp-toolkit': 0.18.1(@vue/compiler-sfc@3.5.42)(h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)))(magic-string@1.2.3)(magicast@0.5.4)(oxc-parser@0.143.0)(rolldown@1.2.7)(rollup@4.60.3)(supports-color@10.2.2)(unplugin@3.3.0(@rspack/core@2.2.1(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0)))(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))(vue@3.5.42(typescript@6.0.3))(zod@4.6.5) '@nuxtjs/robots': 6.2.0(990ae1d11bf7f0267e37b5cee9a36fdb) '@nuxtjs/sitemap': 8.5.0(990ae1d11bf7f0267e37b5cee9a36fdb) '@octokit/webhooks-methods': 6.0.0 @@ -19991,26 +20039,26 @@ snapshots: vue-resize: 2.0.0-alpha.1(vue@3.5.42(typescript@6.0.3)) optional: true - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3)(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) - floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): + floating-vue@5.4.0(@nuxt/kit@4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))))(vue@3.5.42(typescript@6.0.3)): dependencies: '@floating-ui/dom': 1.8.0 vue: 3.5.42(typescript@6.0.3) optionalDependencies: - '@nuxt/kit': 4.5.2(magic-string@1.3.1)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(@rspack/core@2.2.3(@swc/helpers@0.5.23))(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) + '@nuxt/kit': 4.5.2(magic-string@1.3.1)(magicast@0.5.4)(oxc-parser@0.148.0)(rolldown@1.2.7)(unplugin@3.3.0(esbuild@0.28.2)(rolldown@1.2.7)(rollup@4.60.3)(vite@8.3.0(@types/node@26.5.1)(esbuild@0.28.2)(jiti@2.7.0)(terser@5.47.1)(tsx@4.23.13)(yaml@2.9.0))) fnv1a-64@0.1.2: {} @@ -20248,6 +20296,13 @@ snapshots: optionalDependencies: crossws: 0.4.12(srvx@0.11.22) + h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.11.22)): + dependencies: + rou3: 0.9.2 + srvx: 1.0.3 + optionalDependencies: + crossws: 0.4.12(srvx@0.11.22) + h3@2.0.1-rc.31(crossws@0.4.12(srvx@0.12.7)): dependencies: rou3: 0.9.2 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0e25b04fc..11d061986 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -48,6 +48,7 @@ packages: - docs overrides: + '@devframes/agentic': 'workspace:*' '@devframes/hub': 'workspace:*' '@devframes/hub-ui': 'workspace:*' '@devframes/json-render': 'workspace:*' diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts new file mode 100644 index 000000000..72bbef30a --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.d.ts @@ -0,0 +1,20 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/connect` + */ +// #region Interfaces +export interface ConnectServerHandle { + stop: () => Promise; +} +export interface ConnectServerOptions { + ports?: number[]; + instancesDir?: string; + timeoutMs?: number; + authToken?: string | ((_: DevframeInstanceRecord) => string | undefined); +} +// #endregion + +// #region Functions +export declare function buildInstanceRequestHeaders(_: string, _: string | undefined): Record; +export declare function resolveAuthToken(_: ConnectServerOptions['authToken'], _: DevframeInstanceRecord): string | undefined; +export declare function startConnectServer(_?: ConnectServerOptions): Promise; +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js new file mode 100644 index 000000000..d08928240 --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/connect.snapshot.js @@ -0,0 +1,8 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/connect` + */ +// #region Functions +export function buildInstanceRequestHeaders(_, _) {} +export function resolveAuthToken(_, _) {} +export async function startConnectServer(_) {} +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts new file mode 100644 index 000000000..3813681bc --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.d.ts @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic` + */ +/* no exports */ \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js new file mode 100644 index 000000000..3813681bc --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/index.snapshot.js @@ -0,0 +1,4 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic` + */ +/* no exports */ \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts new file mode 100644 index 000000000..35be0c19d --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.d.ts @@ -0,0 +1,18 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/mcp` + */ +// #region Functions +export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions$1): McpFetchHandler$1; +export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions$1): Promise; +export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions$1): MountedMcpHttp$1; +// #endregion + +// #region Other +export { CreateMcpFetchHandlerOptions } +export { CreateMcpServerOptions } +export { McpConnectionInfo } +export { McpFetchHandler } +export { McpServerHandle } +export { MountedMcpHttp } +export { MountMcpHttpOptions } +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js new file mode 100644 index 000000000..61a4e375e --- /dev/null +++ b/tests/__snapshots__/tsnapi/@devframes/agentic/mcp.snapshot.js @@ -0,0 +1,8 @@ +/** + * Generated by tsnapi — public API snapshot of `@devframes/agentic/mcp` + */ +// #region Functions +export function createMcpFetchHandler(_, _) {} +export async function createMcpServer(_, _) {} +export function mountMcpHttp(_, _, _, _) {} +// #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts index a69263e97..6ab424823 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.d.ts @@ -1,41 +1,15 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Interfaces -export interface CreateMcpFetchHandlerOptions { - serverName: string; - serverVersion: string; - exposeSharedState: boolean | ((_: string) => boolean); - authorization?: McpAuthorization; - allowedOrigins?: readonly string[] | false; -} -export interface CreateMcpServerOptions { - transport?: 'stdio'; - exposeSharedState?: boolean | ((_: string) => boolean); - serverName?: string; - serverVersion?: string; - onReady?: (_: { - transport: 'stdio'; - }) => void; -} -export interface McpConnectionInfo { - remoteAddress?: string; -} -export interface McpFetchHandler { - fetch: (_: Request, _?: McpConnectionInfo) => Promise; - dispose: () => Promise; -} -export interface McpServerHandle { - stop: () => Promise; -} -export interface MountedMcpHttp { - dispose: () => Promise; -} -export interface MountMcpHttpOptions extends CreateMcpFetchHandlerOptions {} -// #endregion - -// #region Functions -export declare function createMcpFetchHandler(_: DevframeNodeContext, _: CreateMcpFetchHandlerOptions): McpFetchHandler; -export declare function createMcpServer(_: DevframeDefinition, _?: CreateMcpServerOptions): Promise; -export declare function mountMcpHttp(_: H3, _: DevframeNodeContext, _: string, _: MountMcpHttpOptions): MountedMcpHttp; +// #region Other +export { createMcpFetchHandler } +export { CreateMcpFetchHandlerOptions } +export { createMcpServer } +export { CreateMcpServerOptions } +export { McpConnectionInfo } +export { McpFetchHandler } +export { McpServerHandle } +export { MountedMcpHttp } +export { mountMcpHttp } +export { MountMcpHttpOptions } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js index bd0cf3670..6ad22c4cd 100644 --- a/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/adapters/mcp.snapshot.js @@ -1,8 +1,8 @@ /** * Generated by tsnapi — public API snapshot of `devframe/adapters/mcp` */ -// #region Functions -export function createMcpFetchHandler(_, _) {} -export async function createMcpServer(_, _) {} -export function mountMcpHttp(_, _, _, _) {} +// #region Other +export { createMcpFetchHandler } +export { createMcpServer } +export { mountMcpHttp } // #endregion \ No newline at end of file diff --git a/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts index 7dbdabed2..0f8c1490c 100644 --- a/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts @@ -87,6 +87,22 @@ export interface ConnectionMetaWebsocket { port?: number; host?: string; } +export interface CreateMcpFetchHandlerOptions { + serverName: string; + serverVersion: string; + exposeSharedState: boolean | ((_: string) => boolean); + authorization?: McpAuthorization; + allowedOrigins?: readonly string[] | false; +} +export interface CreateMcpServerOptions { + transport?: 'stdio'; + exposeSharedState?: boolean | ((_: string) => boolean); + serverName?: string; + serverVersion?: string; + onReady?: (_: { + transport: 'stdio'; + }) => void; +} export interface DevframeAgentHost { readonly events: EventEmitter; registerTool: (_: AgentToolInput) => AgentHandle; @@ -415,11 +431,21 @@ export interface EventsMap { export interface EventUnsubscribe { (): void; } +export interface McpConnectionInfo { + remoteAddress?: string; +} +export interface McpFetchHandler { + fetch: (_: Request, _?: McpConnectionInfo) => Promise; + dispose: () => Promise; +} export interface McpRouteOptions { path?: string; authorization?: McpAuthorization; allowedOrigins?: readonly string[] | false; } +export interface McpServerHandle { + stop: () => Promise; +} export interface RemoteAssets { package: string; version: string; diff --git a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts index 4e01745e3..00cc7f772 100644 --- a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.d.ts @@ -49,10 +49,12 @@ export declare class DevframeAgentHost implements DevframeAgentHost$1 { // #endregion // #region Functions +export declare function argsToJsonSchema(_: readonly StandardSchemaV1[] | undefined): unknown; /** @deprecated */ export declare function coerceAgentPositionalArgs(_: unknown, _: readonly unknown[] | undefined, _?: AgentArgsFallback): unknown[]; export declare function createH3DevframeHost(_: CreateH3DevframeHostOptions): DevframeHost; export declare function createRpcWireCodec(_?: ReadonlyMap>): RpcWireCodec; +export declare function formatMcpError(_: unknown): string; export declare function importRuntimeModule(_: string): Promise; export declare function normalizeHttpServerUrl(_: string, _: number | string): string; export declare function peekRpcWireFrame(_: string): { @@ -60,6 +62,8 @@ export declare function peekRpcWireFrame(_: string): { i?: string; }; export declare function resolveClientAssets(_: DevframeDefinition): StaticAssetsSource | undefined; +export declare function returnToJsonSchema(_: StandardSchemaV1 | undefined): unknown; +export declare function stringifyForMcp(_: unknown): string; export declare function toolInputToCommandArgs(_: unknown, _?: number): unknown[]; // #endregion @@ -188,7 +192,7 @@ export declare const diagnostics: import("nostics").Diagnostics<{ readonly why: (p: { reason: string; }) => string; - readonly fix: "Install it next to devframe (e.g. `npm install @modelcontextprotocol/client`) and run `devframe connect` again."; + readonly fix: "Install it next to devframe (e.g. `npm install @devframes/agentic`) and run `devframe connect` again."; }; readonly DF0047: { readonly why: (p: { @@ -365,12 +369,23 @@ export declare const diagnostics: import("nostics").Diagnostics<{ }) => string; readonly fix: "On Bun/Deno, serve the advertised `__ws` route from `Bun.serve` / `Deno.serve` with `attachBunWsTransport` / `attachDenoWsTransport` (see the hub-deno example), or connect over the SSE endpoint instead."; }; + readonly DF0078: { + readonly why: "This devframe exposes agent tools, but the MCP endpoint stays off: the optional peer \"@devframes/agentic\" is not installed."; + readonly fix: "Install `@devframes/agentic` next to devframe to serve the MCP endpoint, or set `mcp: false` to opt out silently."; + }; + readonly DF0079: { + readonly why: (p: { + reason: string; + }) => string; + readonly fix: "Install `@devframes/agentic` next to devframe (the MCP adapter and the MCP SDK live there), or remove the explicit `mcp` setting."; + }; }, readonly [(d: import("nostics").Diagnostic, { method }?: { method?: "log" | "warn" | "error"; }) => void]>; // #endregion // #region Other +export { AgenticMcpModule } export { ContextRpcServer } export { createContextRpcServer } export { CreateContextRpcServerOptions } @@ -378,6 +393,7 @@ export { createInstanceShell } export { CreateInstanceShellOptions } export { DevframeInstanceRecord } export { DevframeInstanceRegistration } +export { importAgenticMcp } export { InstanceRegisterConfig } export { InstanceShell } export { InstanceShellApi } @@ -386,7 +402,10 @@ export { InstanceShellInternals } export { InstanceWsTier } export { listLiveDevframeInstances } export { loadAutoMcpAdapter } +export { MountedMcpHttp } +export { MountMcpHttpOptions } export { normalizeBasePath } +export { probeDevframeOrigin } export { registerDevframeInstance } export { resolveBasePath } export { ResolvedMcpConfig } diff --git a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js index 1dc5bcb58..a4f777409 100644 --- a/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js +++ b/tests/__snapshots__/tsnapi/devframe/internal.snapshot.js @@ -1,6 +1,13 @@ /** * Generated by tsnapi — public API snapshot of `devframe/internal` */ +// #region Functions +export function argsToJsonSchema(_) {} +export function formatMcpError(_) {} +export function returnToJsonSchema(_) {} +export function stringifyForMcp(_) {} +// #endregion + // #region Other export { coerceAgentPositionalArgs } export { createContextRpcServer } @@ -9,12 +16,14 @@ export { createInstanceShell } export { createRpcWireCodec } export { DevframeAgentHost } export { diagnostics } +export { importAgenticMcp } export { importRuntimeModule } export { listLiveDevframeInstances } export { loadAutoMcpAdapter } export { normalizeBasePath } export { normalizeHttpServerUrl } export { peekRpcWireFrame } +export { probeDevframeOrigin } export { registerDevframeInstance } export { resolveBasePath } export { resolveClientAssets } diff --git a/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts index c062d1213..2604d37fd 100644 --- a/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts +++ b/tests/__snapshots__/tsnapi/devframe/types.snapshot.d.ts @@ -14,6 +14,8 @@ export { AgentToolProviderHandle } export { ConnectionMeta } export { ConnectionMetaSse } export { ConnectionMetaWebsocket } +export { CreateMcpFetchHandlerOptions } +export { CreateMcpServerOptions } export { DevframeAgentHost } export { DevframeAgentHostEvents } export { DevframeCapabilities } @@ -66,7 +68,10 @@ export { EventEmitter } export { EventsMap } export { EventUnsubscribe } export { McpAuthorization } +export { McpConnectionInfo } +export { McpFetchHandler } export { McpRouteOptions } +export { McpServerHandle } export { McpSetting } export { RemoteAssets } export { RemoteAssetsErrorMessage } diff --git a/tests/optional-mcp-bundles.test.ts b/tests/optional-mcp-bundles.test.ts index 352f1a48a..ba39241c1 100644 --- a/tests/optional-mcp-bundles.test.ts +++ b/tests/optional-mcp-bundles.test.ts @@ -6,6 +6,10 @@ import { build } from 'esbuild' import { afterEach, describe, expect, it } from 'vitest' const root = fileURLToPath(new URL('..', import.meta.url)) +// `packages/devframe/dist/adapters/mcp.mjs` is deliberately absent: that +// entry is a static re-export of `@devframes/agentic/mcp` (like +// `adapters/cac` with its optional `cac` peer), so it resolves the peer by +// design; only implicit paths must stay lazy. const entries = [ 'packages/devframe/dist/adapters/cac.mjs', 'packages/devframe/dist/adapters/initiate.mjs', @@ -23,12 +27,15 @@ afterEach(() => { describe('the MCP SDK stays out of consumer bundles', () => { it.each(entries)('bundles %s without resolving the MCP SDK', async (entry) => { const resolvedMcpImports: string[] = [] + // Both the SDK and `@devframes/agentic` (the optional peer wrapping it) + // must stay behind `importRuntimeModule`, never statically resolvable + // from a consumer entry. const rejectMcpSdk: Plugin = { name: 'reject-mcp-sdk', setup(context) { - context.onResolve({ filter: /^@modelcontextprotocol\// }, (args) => { + context.onResolve({ filter: /^(?:@modelcontextprotocol|@devframes\/agentic)(?:\/|$)/ }, (args) => { resolvedMcpImports.push(args.path) - return { errors: [{ text: `Unexpected MCP SDK import: ${args.path}` }] } + return { errors: [{ text: `Unexpected MCP import: ${args.path}` }] } }) }, } diff --git a/tsconfig.base.json b/tsconfig.base.json index 1d9e90c58..f273c2b41 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -125,7 +125,16 @@ "./packages/devframe/src/adapters/initiate.ts" ], "devframe/adapters/mcp": [ - "./packages/devframe/src/adapters/mcp/index.ts" + "./packages/devframe/src/adapters/mcp.ts" + ], + "@devframes/agentic/mcp": [ + "./packages/agentic/src/mcp/index.ts" + ], + "@devframes/agentic/connect": [ + "./packages/agentic/src/connect/index.ts" + ], + "@devframes/agentic": [ + "./packages/agentic/src/index.ts" ], "@devframes/hub/build": [ "./packages/hub/src/node/build.ts" diff --git a/turbo.json b/turbo.json index e3c5c8990..52908e9e1 100644 --- a/turbo.json +++ b/turbo.json @@ -5,6 +5,17 @@ "typecheck": { "dependsOn": ["^typecheck", "@devframes/hub-ui#build:css", "@devframes/json-render-ui#build:css"] }, + // `devframe` ⇄ `@devframes/agentic` are circular in the package graph + // (agentic peers on devframe; devframe devDeps agentic so tests can + // exercise the installed-peer path), which `^typecheck` would turn into + // a task cycle. Both typecheck from source via `paths` aliases, so + // explicit dependency-free tasks break the cycle. + "devframe#typecheck": { + "dependsOn": [] + }, + "@devframes/agentic#typecheck": { + "dependsOn": [] + }, "@devframes/hub-ui#build:css": { "outputLogs": "new-only", "outputs": ["src/client/.generated/**"] @@ -17,6 +28,11 @@ "outputLogs": "new-only", "outputs": ["dist/**"] }, + "@devframes/agentic#build": { + "outputLogs": "new-only", + "dependsOn": ["devframe#build"], + "outputs": ["dist/**"] + }, "@devframes/hub#build": { "outputLogs": "new-only", "dependsOn": ["devframe#build"], diff --git a/vitest.config.ts b/vitest.config.ts index c16bb3b7c..8d4eaad52 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -13,6 +13,7 @@ export default defineConfig({ }, test: { projects: [ + 'packages/agentic', 'packages/devframe', 'packages/hub', 'packages/hub-ui',