cockpit 3.0.9
cockpit: ^3.0.9 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, and a thin MCP server. It does not
bundle a GUI or a web dashboard.
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 install and verify the latest release while requiring
Pub to resolve a version at least as new as the running executable, so stale
package indexes fail safely instead of downgrading Cockpit. It safely hands any
source-installed native executable back to Pub, restores one optimized AOT
executable, removes retired and temporary update payloads, and replaces an older
running Supervisor while preserving its authorization mode and durable state.
Dart Pub continues to own its shared download cache; Cockpit never deletes
unrelated cached packages.
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 tap "Save"
cockpit dev wait
cockpit dev screenshot
cockpit dev reload
cockpit dev diagnose --verbosity standard
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.
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 minimal canonical LON, so normal commands omit output options.
--verbosity standard|full adds context without changing operation accuracy;
--format json|yaml|jsonl|path|none changes encoding or delivery. Use
--verbosity full --output <file>.lon for the complete object. Request JSON
only for jq, a JSON-only consumer, or JSON wire inspection.
--output prints only the verified path. artifact read requires --output;
binary data, Base64, hashes, and decision-irrelevant byte counts never enter
terminal output.
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 and idempotency; there is no arbitrary URL or
HTTP method 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; starting without the flag returns to the persisted restricted policy.
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, then submit an indexed case using its canonical document digest. Replays use explicit workspace, document, case, and idempotency identities.
cockpit case validate \
--workspace-id <workspaceId> \
--file example/cases/flutter_login.yaml
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. A runtime rejects unsupported constraints instead of dropping them.
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 \
--workspace-id <workspaceId> \
--document-id <documentId> \
--suite-id regression \
--idempotency-key ci-regression-001
cockpit suite report --run-id <runId> \
--output-dir cockpit-report
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, the sanitized
output.systemControl profile in the target.inspect operation result is the
authority for its secondary native driver. Do not reconstruct it from
app.get, which intentionally redacts platform app and process identities.
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 are not auto-resized because they 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.
The repository release gate runs formatting, analysis, every package and example test suite, publication dry-runs, and real Android, iOS, macOS, Linux, web, and Windows regressions in parallel. Android and iOS must prove native locator/action/assertion control rather than screenshot fallback. Publication requires every job to reach a successful terminal state. Each platform regression proves a business mutation, the complete Flutter gesture/text/keyboard/semantics command surface, suite control flow, evidence, and the offline report bundle through observable assertions. Wait for the complete matrix, then diagnose from its reports, events, artifacts, and daemon logs in one pass.
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.