CLI (cac)
A cac CLI around a DevframeDefinition with dev, build, and mcp commands. cac is an optional peer:
npm install devframe cacThe lower-level factories need no cac.
import { defineDevframe } from 'devframe'
import { createCac } from 'devframe/adapters/cac'
const myDevframe = defineDevframe({
id: 'my-tool',
name: 'My Tool',
clientAssets: './client/dist',
setup(ctx) { /* register docks, RPC, etc. */ },
})
await createCac(myDevframe).parse()my-tool # dev server at http://localhost:9999/
my-tool --port 8080
my-tool build --out-dir dist-static
my-tool build --out-dir dist-static --base /my-tool/
my-tool mcp # stdio MCP serverThe SPA serves at / standalone, /__my-tool/ hosted (Mount paths).
Options
createCac(def, options?):
| Option | Default | Description |
|---|---|---|
defaultPort | 9999 (or def.cli?.port) | Dev port if --port unset. |
configureCli | — | (cli: CAC) => void — add commands/flags. |
onReady | — | (info: { origin, port, app }) => void | Promise<void> — once listening. |
Returns a CacHandle:
interface CacHandle {
cli: CAC // raw cac instance — mutate before calling parse()
parse: (argv?: string[]) => Promise<void>
}Definition-level cli fields
defineDevframe({
id: 'my-tool',
clientAssets: './client/dist', // built SPA served as the UI
cli: {
command: 'mytool', // binary name; default: the id
port: 7777, // preferred port
portRange: [7777, 9000], // passed through to get-port-please
random: false, // passed through to get-port-please
host: '127.0.0.1', // default bind host; --host overrides
open: true, // auto-open the browser on dev start; embeds the current OTP so the tab lands authenticated
configure(cli) { // contribute capability flags/commands
cli.option('--config <file>', 'Custom config file')
.option('--no-files', 'Skip file matching')
},
},
setup(ctx, { flags }) {
// `flags` is the parsed cac flag bag — includes both devframe's
// built-ins (`--port`, `--host`, `--open`) and anything declared in
// `cli.configure` or `configureCli`.
},
})configure runs before createCac's configureCli.
Headless logging
Print a banner via onReady:
await createCac(myDevframe, {
onReady({ origin }) {
console.log(`ESLint Config Inspector ready at ${origin}`)
},
}).parse()Use your own CLI framework
Peer factories for a commander/yargs program:
| Building block | Entry | Purpose |
|---|---|---|
createDevServer() | devframe/adapters/dev | h3 + WebSocket RPC + SPA mount |
createBuild() | devframe/adapters/build | Static deploy |
createMcpServer() | devframe/adapters/mcp | stdio MCP server |
parseCliFlags(schema, raw) | devframe/adapters/cac | Validate flags (CliFlagsSchema) |
See the Standalone CLI guide.
The Standard Handler
initDevframe() turns a DevframeDefinition into a running devframe whose .handler — a Web Standard (request: Request) => Promise<Response> — carries everything a devframe serves (SPA, __connection.json discovery, RPC socket, auth gate, MCP route) under one mount base. Every other serving path — adapters,…
Dev
createCac's building block: h3 + WebSocket RPC + the SPA at the resolved base path.