Skip to content

Latest commit

 

History

History
202 lines (164 loc) · 7.32 KB

File metadata and controls

202 lines (164 loc) · 7.32 KB
outline deep

Devtool Definition

Every DevFrame tool starts with a single defineDevtool call. The returned DevtoolDefinition is a portable value that any of the seven adapters can consume — the same definition runs under createCli, createKitPlugin, createBuild, createMcpServer, and so on.

Minimal Definition

import { defineDevtool } from 'devframe'

export default defineDevtool({
  id: 'my-devtool',
  name: 'My Devtool',
  setup(ctx) {
    // Register RPC, docks, commands, logs, terminals, agents here.
    ctx.docks.register({
      id: 'my-devtool:main',
      title: 'My Devtool',
      icon: 'ph:gauge-duotone',
      type: 'iframe',
      url: '/.devtools/',
    })
  },
})

Definition Fields

Field Type Description
id string Required. Unique, namespaced identifier (kebab-case). Used as a prefix for RPC names, dock IDs, and MCP tool names.
name string Required. Display name shown in the dock and agent manifests.
icon string | { light, dark } Optional Iconify name or URL; supports light/dark pairs.
version string Optional version string surfaced to clients.
basePath string Optional mount path override. Defaults depend on the adapter: / for standalone (cli / spa / build), /.<id>/ for hosted (vite / kit / embedded).
capabilities { dev?, build?, spa? } Per-runtime feature flags. A boolean applies to the runtime as a whole; an object enables individual features.
setup (ctx, info?) => void | Promise<void> Required. Server-side entry point. Runs in every runtime. The optional second argument carries runtime metadata — most notably the parsed CLI flags when running under createCli.
setupBrowser (ctx) => void | Promise<void> Browser-only entry used by the SPA adapter.
cli DevtoolCliOptions Defaults for the CLI adapter. See CLI options below.
spa DevtoolSpaOptions Defaults for the SPA adapter (base, loader).

Runtime Flags

The ctx.mode field is either 'dev' or 'build'. Use it to gate work that should only run in one runtime:

defineDevtool({
  id: 'my-devtool',
  name: 'My Devtool',
  setup(ctx) {
    if (ctx.mode === 'build') {
      // Static-only work — baked into the RPC dump.
    }
    else {
      // Dev-mode wiring, file watchers, etc.
    }
  },
})

The CLI dev server sets mode: 'dev'; createBuild sets mode: 'build'.

The Setup Context

setup(ctx) receives a DevToolsNodeContext:

interface DevToolsNodeContext {
  readonly cwd: string
  readonly workspaceRoot: string
  readonly mode: 'dev' | 'build'

  host: DevToolsHost // runtime abstraction (mountStatic / resolveOrigin)
  rpc: RpcFunctionsHost // register + broadcast + sharedState
  docks: DevToolsDockHost // dock entries
  views: DevToolsViewHost // static file hosting
  terminals: DevToolsTerminalHost
  messages: DevToolsMessagesHost
  diagnostics: DevToolsDiagnosticsHost
  commands: DevToolsCommandsHost
  agent: DevToolsAgentHost // experimental

  createJsonRenderer: (spec) => JsonRenderer
  utils: DevToolsNodeUtils
}

Each host has a dedicated page:

Browser Setup

The SPA adapter supports a setupBrowser(ctx) hook that runs inside the deployed client bundle. Use it for tools that perform their own in-browser work rather than fetching from a server (e.g. parsing a dropped file, calling public APIs from the client).

defineDevtool({
  id: 'my-devtool',
  name: 'My Devtool',
  setup(ctx) { /* server-side */ },
  setupBrowser(ctx) {
    // `ctx.rpc` is the write-disabled static client in SPA mode.
  },
})

Note

Automatic bundling of setupBrowser into the SPA output is not yet implemented. Until then, deployed SPAs that use it must ship their own client entry that registers the handlers.

CLI Options

cli lets you configure the CLI adapter's defaults and plug additional flags/commands into the CAC instance:

defineDevtool({
  id: 'my-devtool',
  name: 'My Devtool',
  cli: {
    command: 'my-devtool', // binary name; default: the `id`
    distDir: './client/dist', // required for dev / build / spa
    port: 9876, // preferred port; default: 9999
    portRange: [9876, 10000], // forwarded to get-port-please
    random: false, // forwarded to get-port-please
    host: 'localhost', // default host; --host overrides
    open: true, // auto-open the browser on dev start
    auth: false, // skip the trust handshake (single-user localhost)
    configure(cli) { // contribute capability flags/commands
      cli
        .option('--my-flag <value>', 'Tool-specific flag')
    },
  },
  setup(ctx, { flags }) {
    // `flags` carries the parsed cac bag — contains built-in flags
    // (`--port`, `--host`, `--open`, `--no-open`) and anything you added
    // in `configure`.
  },
})
Field Type Description
command string Binary name surfaced in --help. Default: the definition's id.
distDir string SPA dist directory. Required for dev / build / spa.
port number Preferred port for the dev server.
portRange [number, number] Port scan range, passed through to get-port-please.
random boolean Prefer a random open port.
host string Default bind host.
open boolean | string true opens the origin, a string opens a specific path, false disables. Matches the --open / --no-open flags.
auth boolean Disable the WS trust flow when the tool is localhost-only and single-user. Default true.
configure (cli: CAC) => void Contribute capability flags/commands. Runs before createCli's configureCli option so the final tool author always has the last word.

setup(ctx, info) receives info.flags populated from both devframe's built-in flags and any you declare via configure. Use this to avoid duplicating flag parsing yourself.

SPA Options

defineDevtool({
  id: 'my-devtool',
  spa: {
    base: '/',
    loader: 'query', // 'query' | 'upload' | 'none'
  },
})

See Adapters for how each adapter consumes these.

Multiple Runtimes, One Definition

Because the definition is a plain value, you can wire it into multiple adapters from the same file:

import { createBuild } from 'devframe/adapters/build'
import { createCli } from 'devframe/adapters/cli'
import { createKitPlugin } from 'devframe/adapters/kit'

const devtool = defineDevtool({ id: 'my-devtool', name: 'My Devtool', setup() {} })

// 1. Standalone CLI:
await createCli(devtool).parse()

// 2. Embedded in a Vite project (from `vite.config.ts`):
export const myPlugin = () => createKitPlugin(devtool)

// 3. Offline snapshot:
await createBuild(devtool, { outDir: 'dist-static' })

What's Next