docs · docs/20-control-plane/CLI_DESIGN.md · synced 2026-09-10

CLI

vflin — one command per API method

Purpose: define a first-class CLI that is a thin, 1:1 client of the Local API (ADR-0003). The CLI exists for humans, CI, automation scripts, and AI coding agents (Claude Code) alike.

Principles

  • No business logic. Each command maps onto one (or a short composition of) API calls.
  • Scriptable by default: every command supports --json for machine-readable output and stable exit codes. Human-pretty output is the default for TTY only.
  • Same verbs as the API: vflin <domain> <action> mirrors domain.action.
  • Safe by default: dangerous actions require --mode danger (or power) AND a confirm, unless --yes is passed in non-interactive contexts.

Shape

vflin profile create --name "x" --fingerprint preset:win11-chrome --proxy <id>
vflin profile start <id> [--headless] [--json]
vflin profile stop <id> [--sync]
vflin profile ls [--group y] [--json]
vflin proxy test <id> --json
vflin fingerprint validate -f config.json
vflin automation run --script <id> --group y --var k=v
vflin automation status <runId> --watch
vflin sync push <id> | pull <id> | status <id>
vflin core ls | core select <provider> --version 148.9 | core diagnose
vflin log tail --filter automation
vflin policy set danger        # session-scoped, audited

Output & exit codes

  • 0 success · 1 generic error · 2 usage error · 3 policy denied · 4 conflict/locked · 5 core launch failure. (Mirrors API error codes.)
  • --json emits the API { ok, data, error } envelope verbatim → trivial for agents to parse.

Connection model

  • CLI talks to a running Application Core over the Local API socket.
  • vflin daemon status|start|stop manages the Core if not already up (UI also starts it).

Why first-class from day 1

  • Phase-1 exit criterion is a CLI command opening a real browser through the API.
  • Makes the whole product testable headless in CI before any UI exists.
  • Becomes the substrate the MCP server and Claude Code skills shell out to or mirror.

Non-goals

  • The CLI does not read profile folders, the vault, or cookies directly. Ever.
  • No alternate “fast path” that skips the API.

Sessions (Feature c — ADR-0034)

Bind a saved service session so a profile opens already logged in. Full operator guide: SESSIONS_MODULE.md (internal: docs/20-control-plane/SESSIONS_MODULE.md).

vflin session recipes                              # supported services (green; always prints JSON)
vflin profile session list <id>                    # always prints JSON
vflin profile session get <id> <service>
vflin --mode danger profile session set <id> \
      --service <name> --session-mode <inject|login|companion> \
      --secrets-file <path.json> [--label <l>]
vflin --mode danger profile session clear <id> <service>

Two rules this surface exists to enforce:

  • Secrets come from a FILE, never argv. --secrets-file takes a {"field":"value"} JSON object, so a session token cannot land in shell history or in another user’s ps output.
  • --mode and --session-mode are different flags. --mode is the global policy mode (normal|power|danger), stripped before dispatch; --session-mode is the recipe’s mode. They originally shared the name --mode, which made the documented invocation literally unrunnable — the global validator rejected inject before the command ever saw it (lockstep review 2026-07-19).