cockpit 4.0.8
cockpit: ^4.0.8 copied to clipboard
Production E2E automation CLI, daemon, MCP, drivers, and reports for Flutter and black-box apps.
cockpit is the authenticated host control plane for Flutter/Dart development
and headless black-box E2E. It contains the Supervisor daemon, isolated
workspace worker, resource-oriented CLI, MCP server, and public REST/SSE API.
Install #
Cockpit requires Dart 3.8.0 or newer. Flutter workspaces require Flutter 3.32.0 or newer.
dart pub global activate cockpit any
cockpit --help
Run cockpit update to update the CLI and running Supervisor to the latest
verified Pub release while preserving local authorization and durable state.
The package publishes four executables:
cockpit: interactive resource commandscockpit_mcp: MCP stdio servercockpitd: Supervisor daemon and foreground CI runnercockpit_worker: private workspace worker process
Install For AI Agents #
Preferred: ask the current AI host to install the CLI, complete Skill, native adapter, and MCP surface. Copy this prompt:
Install Cockpit for the current AI host, including the CLI, complete cockpit Skill, native adapter, and cockpit_mcp when supported, by following https://github.com/cockpit-dev/cockpit/blob/main/skills/cockpit/INSTALL.md
Complete host-specific installation and verification instructions live in
skills/cockpit/INSTALL.md.
Native adapter and MCP details are documented in the
agent integration guide.
Flutter Fast Path #
Run from the intended checkout. Cockpit owns discovery, the Supervisor, workspace/target registration, the app process, ports, and bridge state:
cockpit dev start cockpit/main.dart --platform macos
cockpit dev status
cockpit dev inspect "Save"
cockpit dev tree
cockpit dev tap "Save"
cockpit dev wait
cockpit dev screenshot
cockpit dev reload
cockpit dev diagnose --view more
Omit the entrypoint and platform when they are inferable. Cockpit stores one
active numeric handle per canonical Flutter project, guarded by checkout
identity; the same project may keep separate platform/target handles. Use
cockpit session list, cockpit session show HANDLE, and cockpit dev use HANDLE when identity needs confirmation or selection. Explicit --session
targets one command without changing the saved active handle. dev starts its
local Supervisor in process-scoped yolo mode. Cockpit does not read a keychain
or secret store, and --env values are process-only.
Flutter inspection walks mounted Elements and RenderObjects without requiring
developer-authored Semantics. Use dev inspect QUERY for a bounded search;
it returns a directly executable sel, for example #save or
Dialog >> FilledButton["Continue"]. Conditions intersect, and ambiguous targets
fail instead of guessing. dev tree returns a compact selector index; use
dev tree --view more or dev tree --view full only for structural context.
Both structural views write the tree to an artifact and print only its verified path.
Interactive Workspaces #
The CLI starts the per-user Supervisor when an interactive API command needs it. Register every project root and checkout explicitly:
cockpit daemon start
cockpit root add --path /work/projects --label projects
cockpit workspace register --root-id <rootId> --path /work/projects/app-a
cockpit workspace register --root-id <rootId> --path /work/projects/app-b
cockpit workspace list
CLI Output #
The default is brief canonical LON. Use --view more for additional context and
--view full for the complete response. --format supports
lon|json|yaml|jsonl|path|none; JSON is intended for jq, JSON-only consumers,
and wire inspection. --output and artifact read return the verified output
path.
Workspace commands accept --workspace-id. When it is omitted, Cockpit
resolves the current directory against registered active workspaces and
requires exactly one match. It never selects a global latest run, active
session, or unrelated checkout.
cd /work/projects/app-a
cockpit op list
cockpit case list
op run accepts typed LON, JSON, or YAML and executes an advertised operation.
The descriptor controls scope, idempotency, and transport. Its advertised timeout
is the default; pass --timeout
only for a deliberate override within the advertised maximum.
cockpit op run analyze.workspace \
--workspace-id <workspaceId>
Authorization Policy #
Dangerous operation kinds, operation safety effects, test safety effects, and
production targets require explicit authority.
The strict policy document is stored at COCKPIT_HOME/authorization.json and
is loaded once when the daemon starts.
cockpit daemon policy validate --file authorization.json
cockpit daemon policy apply --file authorization.json --restart
cockpit daemon policy show
Use cockpit daemon start --yolo (or daemon restart --yolo) for an
explicitly unrestricted local daemon. The mode lasts only for that daemon
process. An unflagged start or restart preserves a healthy running daemon's
current mode; when no daemon is running, it starts with the persisted restricted
policy. Stop first when an explicit return to restricted mode is required. The
effective auth is exposed by daemon status and recorded in attempt and suite
reports.
Applying without --restart requires a stopped daemon. The default policy
denies dangerous operations and sensitive test effects; it does not authorize
production or unknown target environments.
Quarantined leases remain blocked by default. Use the advertised lease.list
operation to obtain the exact identity, then a reset-authorized
lease.recover request to retry verified cleanup. forceRelease: true is
limited to explicitly matched logical resources; forwarded ports can only be
released after verified cleanup.
Canonical Case Replay #
Validate a case document. Run the local file directly while developing it, or submit an indexed case using its canonical document digest for durable shared and CI replay.
cockpit case validate \
--workspace-id <workspaceId> \
--file example/cases/flutter_login.yaml
cockpit case run \
--file example/cases/flutter_login.yaml \
--idempotency-key local-login-001
cockpit case run \
--workspace-id <workspaceId> \
--document-id <documentId> \
--case-id flutter-login \
--idempotency-key ci-login-001
cockpit run get --run-id <runId>
cockpit run events --run-id <runId> --after-sequence 0
Run events use authenticated SSE with afterSequence and Last-Event-ID
resume support. Gap, terminal, and disconnect states are explicit. Artifacts
are read with expected size and SHA-256 values and are rejected when response
metadata or bytes differ.
Suites And Black-Box Targets #
Suites reuse indexed cases and add dependency DAGs, scoped fixtures, matrix rows, concurrency, retries, fail-fast behavior, recovery, and aggregate JSON/JUnit/HTML/Markdown reports.
Recovery persists node and attempt checkpoints plus exact fixture/row session
bindings. An attempt active at worker termination becomes interrupted and is
retried only when the suite policy allows it. A missing bound session is an
explicit environment failure, never a silent replacement.
The default restartApp isolation runs before each case's attempt fixtures.
Use resetAppData only when the selected driver advertises it, and choose
sharedSession explicitly only when state sharing is part of the suite design.
Fields in one locator are an intersection; fallbacks are ordered
alternatives. Native black-box targets additionally support state, hierarchy,
and spatial constraints when their inspected accessibility capability reports
them. Unsupported constraints fail explicitly.
Text and label matching defaults to exact; use an explicit matchMode of
contains, typo-tolerant fuzzy, or regex for broader matching. A unique best candidate is selected
by route and match quality. Equal best candidates fail as ambiguousTarget;
use more signals, a relation, or 0-based index to select a list item.
cockpit suite validate --file example/suites/regression.yaml
cockpit suite run \
--file example/suites/regression.yaml \
--idempotency-key local-regression-001
cockpit suite run \
--workspace-id <workspaceId> \
--document-id <documentId> \
--suite-id regression \
--idempotency-key ci-regression-001
cockpit suite report --run-id <runId> \
--output-dir cockpit-report
The local --file and indexed --suite-id forms are mutually exclusive.
Register installed native applications and other system-controlled surfaces as workspace-owned targets. Put the stable platform app/package id on the target; a case may override it in its target requirements when necessary. Android uses ADB accessibility and device controls. iOS uses WebDriverAgent for accessibility and interaction; assign a distinct WDA endpoint when multiple devices or workspaces run concurrently.
An installed Flutter app or native/Flutter mixed stack uses targetKind: flutterApp, a real appId, no entrypoint, and a native-plane case. It is
launched and driven through system accessibility with Flutter-aware duplicate
semantics normalization. Native screens and embedded platform views remain in
the same tree. Entrypoint-backed targets use the optional bridge and semantic
plane for development-only Widget, route, and runtime inspection.
cockpit target register \
--workspace-id <workspaceId> \
--platform android \
--device-id emulator-5554 \
--target-kind nativeApp \
--app-id com.example.app \
--environment test \
--mode automation \
--idempotency-key android-target-001
cockpit target register \
--workspace-id <workspaceId> \
--platform ios \
--device-id <deviceUdid> \
--target-kind nativeApp \
--app-id com.example.app \
--wda-url http://127.0.0.1:8101 \
--environment test \
--mode automation \
--idempotency-key ios-target-001
Use target list and target get to recover registered resources, target launch to activate one, and target inspect to read its live capabilities.
For a launched Flutter or mixed-stack target, cockpit target inspect returns
the secondary native driver profile as system.
Flutter target launches accept repeatable --dart-define,
--dart-define-from-file, --flutter-arg, and --env KEY=VALUE options plus a
--timeout budget (20 minutes by default, 31 minutes maximum). MCP and generic
operations use the
same nested launchConfiguration fields: dartDefines,
dartDefineFromFiles, flutterArgs, and environment. Cockpit-managed launch
arguments cannot be overridden, and configuration values are not returned.
On Android and iOS, environment configures the Flutter build process; mobile
application processes do not inherit arbitrary host variables. Use Dart defines
or an application-owned configuration channel for values the app must read.
Operation descriptors publish executionMode, defaultTimeoutMs, and
maximumTimeoutMs. Synchronous operations block to a result and accept a
single --timeout duration such as 90s or 20m. Case and suite submissions
are asynchronous durable jobs that return runId; --timeout controls the
overall run budget (case: 30 minutes by default,
6 hours maximum; suite: 2 hours by default, 24 hours maximum).
Case setup, main steps, finally, and suite fixtures can use type: system
with an advertised system action name and parameters. This keeps install,
activation, permissions, device state, and cleanup inside the same safety,
timeout, event, and report pipeline as UI actions.
A step-level plane may override the case default with semantic, native,
visual, or coordinate. The runtime otherwise derives the plane from the
action and locator. Flutter bridge sessions retain a system driver for the same
target, allowing semantic Widget steps and native, visual, or coordinate steps
to run in one case. Conditions and nested fragment/if/retry/loop steps inherit
the effective plane unless they override it.
Text control includes copyText, eraseText, and pasteText. travel applies
a bounded sequence of latitude/longitude points with per-route or per-point
delays. A visual locator names a workspace-confined template file; an
assertScreenshot names a workspace-confined baseline and emits the actual,
baseline, and deterministic diff files into the attempt evidence. Select each
baseline by a stable platform/device/viewport, pixel-ratio, and orientation
profile. Dimension mismatches indicate a wrong profile or a layout regression.
Use suite
fixtures, case setup/finally, step evidence, and explicit recording
operations to express pre/post capture at the scope that owns it.
Foreground CI #
CI uses the same HTTP API and worker boundary as interactive mode. Foreground
mode owns the daemon lifetime, registers the supplied checkout, submits the
provided CockpitRunSubmission JSON, waits for terminal run truth, and exits
with a process status derived from the run outcome.
cockpitd \
--home=/tmp/cockpit-ci \
--foreground-workspace=/workspace/app \
--foreground-submission=/workspace/run-submission.json
The submission contains the canonical case source, idempotency key, inputs,
and required features. Foreground mode fills the registered workspaceId.
API Discovery #
CockpitDaemonLifecycleClient.ensure() initializes the Cockpit home, validates
process identity, and returns the current discovery record. Production clients
then:
- send its bearer token only to the discovered loopback endpoint;
- read
GET /api/v2/server; - negotiate API major/minor and required features;
- decode public foundation DTOs strictly;
- use only advertised
/api/v2resources and operations.
The complete generic client surface is:
GET /api/v2/operations
GET /api/v2/workspaces/{workspaceId}/operations
GET /api/v2/operations/schema
POST /api/v2/operations
POST /api/v2/workspaces/{workspaceId}/operations
GET /api/v2/runs/{runId}/events
REST owns commands and resources. Authenticated SSE owns durable resumable run
events. WebSocket is reserved for the internal Flutter Web bridge and is not a
public client command transport. The operation invocation envelope owns scope,
idempotency, and deadline; input contains only fields from the selected live
request schema.
The shared CockpitSupervisorApiClient implements this flow for the CLI and
MCP server, including 1 MiB response limits, bounded pagination, SSE resume,
structured API errors, and artifact integrity checks.
MCP #
Run the CLI command or the dedicated executable:
cockpit serve-mcp
cockpit_mcp
cockpit serve-mcp --profile dart
{
"mcpServers": {
"cockpit": {
"command": "cockpit_mcp",
"args": []
}
}
}
MCP exposes bounded resources for server, capabilities, roots, workspaces, operations, targets, documents, cases, suites, runs, and artifacts. Its tools cover root/workspace lifecycle, advertised operations, target lifecycle, case/suite validation and execution, run get/cancel/events, artifact listing, and verified artifact downloads to explicit files. Every tool crosses the authenticated Supervisor HTTP boundary; the MCP process does not construct application services.
Profiles keep tool injection intentional: core is the default, while dart,
flutter, app, e2e, and all add their capability domains. flutter
includes dart; e2e includes app. Use repeatable --enable and --disable
overrides for exact feature names or categories. The Dart profile provides
analyze, format, fix, test, LSP, pub, package URI/search, and project creation
without embedding or forwarding the official Dart MCP server.
Client Boundary #
The public /api/v2 resources, SSE stream, foundation DTOs, and artifact
integrity contract are the only client boundary. A future Flutter GUI or
third-party SDK must use that protocol and must not link Supervisor application
services in-process.
Exported cockpit-report/ directories are complete offline run artifacts, not
server UI. suite report --output-dir cockpit-report downloads the manifest
and every declared report artifact with verified size and SHA-256, then commits
the directory only after it is complete; the destination must not already
exist. index.html embeds its CSS, JavaScript, and canonical report
data while media uses bundle-relative paths. report.json is the stable
single-file rendering input, and root manifest.json covers every exported
file with ownership, size, media type, and SHA-256. Clients must preserve the
directory structure and verify the manifest; no HTML route in cockpitd is
required. Summary, Coverage, Executions, Evidence, Diagnostics, and
Environment/files are task views over one fact graph, not persona-specific
copies.
See ../../docs/contracts for protocol material and
example/cases for canonical YAML and JSON cases.