Skip to content

Latest commit

 

History

History
334 lines (286 loc) · 27.8 KB

machine-readable-cli.md

File metadata and controls

334 lines (286 loc) · 27.8 KB

Machine-readable CLI

The supported agent polling loop uses flisk status --json; add --tilt to include Tilt's live resource list in tilt.resources. Use flisk resources --json for the discovered manifest view, flisk stacks --json for stack names and counts and flisk networks --json for routed URLs and reachability. flisk doctor --json reports environment readiness and actionable checks.

Every response uses this envelope:

{
  "schemaVersion": 1,
  "data": {},
  "errors": []
}

data is null when a command cannot produce its report. Errors have stable code and message fields. Diagnostics are written to stderr; stdout contains one JSON document. Exit code 0 means the report succeeded (doctor readiness permits warnings), 1 means a blocking finding or command failure, and 2 means invalid arguments or an unexpected internal error (flisk up also exits 2 when hand-edited generated files drift from service.json, while native Windows startup refusal exits 1). Consumers should check the exit code and still parse the envelope when one is emitted.

Command data:

  • status: tilt.available, tilt.resourcesQueried, tilt.readiness, tilt.tiltfileError, tilt.staleDisabled (resource names the Tiltfile enables that Tilt still reports as Disabled until flisk down && flisk up), optional live tilt.resources, total resourceCount, discovered manifest resources (name, stack, type, port), stacks (name, resourceCount, resources containing resource names, healthyCount), unhealthy (names of failing or pending services), unassignedResources (resource names), and capabilities (names of the project capabilities that are on, such as database; a project with capabilities.database: false in .flisk/project.json has no database here and sharedInfra is null). --tilt controls the live tilt.resources list: without it, tilt.resourcesQueried is false and tilt.resources is null. The manifest resources array is always available and does not report live health. tilt.readiness retains its existing behavior and may be populated whenever Tilt is available. If Tilt is unavailable, tilt.available is false and no live resource-list query is made.
  • resources: resources with stable service name, stack, type, port, and manifest path.
  • stacks: stacks with name and resourceCount. --services adds services (service names; its length equals resourceCount), and --verbose adds description; --services alone does not include description. A project with no stacks returns stacks: []. Outside a project it emits the same envelope as resources --json (data: null, one errors entry, exit 2).
  • networks: services with stable service name, stack, base path, URL, optional Windows loopback URL, service port, and reachability state (running, stopped, or unknown). An empty list means no discovered resource currently has a routable base path.
  • projects: projectRoot (the absolute path of the project), configured (true when all master configuration files exist), missingFiles (the master files that are missing from .flisk/.flisk-out; empty when configured is true), resources (the number of discovered resources, not their names), stacks (the stack names) and unassignedResources (the number of resources that do not belong to a stack). Use flisk resources --json or flisk stacks --json when you need the names and details. flisk info --json is an alias. See the example below.
  • doctor: ready, inProject, ordered checks, capabilities (names of the capabilities that are on), and a ports plan with http, https, and postgres entries (no postgres entry when the database capability is off, from capabilities.database: false in .flisk/project.json or --no-database). Each port entry contains canonical requested, selected chosen, explicit, and reason fields. Doctor is read-only and does not persist the plan, unless --fix is given (see doctor-contract.md). Structured errors cover usage/internal failures. Check entries preserve name, didPass, message, optional fix, isWarning, and isSkipped. isSkipped indicates that a check could not run meaningfully; skipped checks are non-blocking, even when didPass is false.
  • up: ok (true when all expected resources are built and running), stack (the stack name argument, or null), services (the started service names), optional requested and dependencies (when --only is used), and tiltUrl (http://localhost:<port>). flisk up --json emits one JSON object once startup reaches its ready state while Tilt continues running in the background. On timeout or resource failure, data.ok is false with data.tiltUrl and data.failures (a list of affected resources, each with name and message), and errors[].code is UP_TIMEOUT or UP_RESOURCE_FAILED. On smoke-check failure, data.ok is false, errors[].code is UP_SMOKE_FAILED, and Tilt is stopped. Hand-edited generated file drift (DRIFT_DETECTED, DRIFT_EXIT_CODE = 2) and unknown --only service names (UNKNOWN_SERVICE) exit with code 2, while flisk config verify exits 1 for drift; other startup, preflight, and host/platform refusals (including native Windows startup refusal) exit with code 1. Parsers should read the failure category from errors[].code and the affected resources from data.failures. See the examples below.
  • up --dry-run: ok, dryRun (true), stack (the stack name argument, or null), services (the services the run would start, in discovery order) and command (the tilt up command line that would run). With --only, it also has requested (the names you passed) and declaredDependencies (services pulled in because a requested service lists them in dependsOn). See the example below.
  • up --env-file: the path selects the project env file of the run (see environment.md). A path that is missing, unreadable, not a file, outside the project, or that misses a key the platform needs is refused before anything starts: errors[].code is ENV_FILE_INVALID and the exit code is 2. A project whose vendored engine predates the option is refused with errors[].code ENV_FILE_ENGINE_TOO_OLD and exit code 1. A stack that is already running keeps the env file it started with, so the option is refused with errors[].code ENV_FILE_ALREADY_RUNNING and exit code 1. The path is named in the message and in the human output; no value from the file is ever printed.
  • down: ok (true) and stoppedTilt (the process IDs of any tilt up run that flisk down ended first; an empty list when none was running). With --prune-networks, it also has removedNetworks (the project's Docker networks that were removed) and keptNetworks (networks left in place, each with name and reason, for example because a container is still attached). When tilt down fails (for example because the generated Tiltfile no longer evaluates), flisk down stops the project's containers with docker compose down instead and adds composeFallback: tiltDownExitCode and stopped (the Compose projects it stopped). flisk down --dry-run --json returns ok and dryRun (true) and stops nothing. See the examples below.
  • logs: services (selected service names), tail (defaults to 200, accepts 1–10000), since (duration such as 30s, 5m, or 1h, or null when omitted), and lines (normalized log entries). Each line contains time, resource, level, source, and text; the first four fields may be null, while text is a string.

Example output for flisk logs --json --service orders-api --tail 50 --since 5m:

{
  "schemaVersion": 1,
  "data": {
    "services": ["orders-api"],
    "tail": 50,
    "since": "5m",
    "lines": [
      {
        "time": "2026-10-15T12:30:00Z",
        "resource": "orders-api",
        "level": "info",
        "source": "stdout",
        "text": "Request completed"
      }
    ]
  },
  "errors": []
}

This is an illustrative example. --tail limits the number of recent lines, and --since filters logs to the specified duration. JSON mode returns a snapshot and exits rather than following a live stream. On failure, data is null and errors contains entries with code and message, optionally with suggestions. Usage errors exit with code 2; other failures use the applicable command exit code. Error diagnostics are also written to stderr.

  • config verify: valid, errors, warnings, and diffs. Drift or missing tracked master outputs exits 1 and includes a unified diff; a valid project exits 0. A missing project exits 1 with a structured command error; invalid project JSON or an unexpected failure exits 2 with a structured internal error. JSON verification does not regenerate files.
  • config regenerate: dryRun, hasChanges, staleServiceOutputs (the service.json files whose generated compose and Dockerfile are older than the file; flisk up writes those, not regenerate) and changes, one entry per master file with file, status (new, modified or unchanged) and lineDiff (new minus current lines, 0 for a new file). With --dry-run nothing is written; without it the files are rewritten (hand edits in them are discarded) and changes says how each one differed before the run.
  • config migrate: schemaVersion (the version the files now declare), migrated (the service.json paths that were rewritten, relative to the project root) and warnings. When any file cannot be migrated, nothing is written: data is null, errors has one MIGRATE_FAILED entry per problem, and the exit code is 1.
  • config enable-infra and config disable-infra: service, enabled (the new value) and wasEnabled (the value before the run). An unknown service, or verdaccio without a license that grants it, is a COMMAND_FAILED error with suggestions and exit code 1.
  • project: --json implies --yes and --quiet, so it never prompts and takes the defaults (with --force it overwrites .flisk/project.json without asking, like --force --yes). action says what it did. created (a new project) has projectRoot, projectName, autoEnabledStacks, generatedFiles, env (created, and added for the keys added to an existing .env), missingEnv, gitignoreAdded, agentSkills (the files written) and runtime (hasDockerCli, hasTiltCli). regenerated (run inside an existing project) has projectRoot, autoEnabledStacks, generatedFiles, env, gitignoreAdded and runtime. cloned (with a template) has template, path (the absolute directory it landed in) and runtime. check (with --check) has projectRoot, valid, hasProjectJson and missingFiles; an incomplete project also gets a PROJECT_INCOMPLETE error and exit code 1.
  • resource: --json implies --yes, so the name is required and, when several stacks exist, --stack too. action (created, or registered for a directory that already had a service.json), name, type, stack, port (null for an SDK), path (relative to the project root), framework and language (null when they do not apply) and discovered (whether discovery.paths covers path, so flisk up starts it). With --frameworks, data is the inventory instead: type (frontend or backend), frameworks (the rows the text listing prints, as objects) and, for a backend, language.
  • stack: --json never prompts, so it needs --list, or a stack name with --resources. stacks (name, resourceCount) and unassigned (name, configPath) describe the project after the run, stack is the stack assigned to (null with --list) and assigned lists the resources this run assigned (name, configPath).
  • import and import compose (the same importer): --json implies --yes. flisk-import runs with its output captured (and copied to stderr), and data has dir, dryRun, force, written (the service.json paths the importer reports writing) and output (what it printed). A failed import (nothing to import, an unreadable docker-compose.yml, a core older than the importer needs, or no npx) has data: null, one IMPORT_FAILED error with the importer's message and hints, and the importer's exit code (127 when npx cannot run).
  • import tiltfile: needs flisk-import 0.4.1 or later. It runs flisk-import tiltfile the same way, so --json implies --yes and data has path (the path argument as given, Tiltfile when omitted), dryRun, force, written ([".flisk/project.json"] when the importer reports writing it, empty in a dry run) and output (what it printed). A refusal (a missing Tiltfile, a path inside .flisk/, a path with a quote or control character, or a .flisk/project.json that already names an orchestration source without --force) has data: null, one IMPORT_FAILED error with the importer's message and hints, and the importer's exit code.
  • eject: --json implies --yes. dryRun, created (whether this run wrote EJECTED.md, or would in a dry run; false when it already existed), ejectedFile, generatedFiles (the files under .flisk/.flisk-out, which stay where they are), userFiles (a root Tiltfile, when there is one) and tiltUpCommand. A project without generated Tilt files is a COMMAND_FAILED error with exit code 1.
  • mcp check: url (the endpoint checked), resource (the resource it was resolved from, or null for a URL), iconUrl (or null with --no-icon), serverName, didPass, failures and checks (each {check, didPass, detail}, with isSkipped: true on a check that did not run, such as tools/call without --call). Any failing check adds one MCP_CHECK_FAILED error whose suggestions name the failing checks, and the exit code is 1; the report is still in data. A target that is neither an https URL (plain http only for loopback hosts) nor a resource of this project is an error with data: null.
  • os: starts FliskOS, the local desktop app (see the cheat sheet). via (command for the fliskos command, app for FliskOS.app), path (the executable or the app bundle), args (what it was started with), project (the project passed with --project, or null), url (where FliskOS listens, read from the command's own output and left exactly as printed, so it carries FliskOS's access token in its query string: keep it out of logs and chats; null for the app, which opens its own window) and ignored (the options the app cannot take, such as --port; empty for the command). The envelope goes out once FliskOS is listening and flisk os keeps running until it stops, so read the first line and leave the process alone; the command's own output goes to stderr, and the exit code is that of fliskos once it ends. Neither fliskos nor the app on the machine is a FLISKOS_NOT_INSTALLED error with the install steps in suggestions and exit code 1; fliskos exiting or staying silent before it listens (30 seconds) is FLISKOS_FAILED; a --port that is not an integer from 0 to 65535, or a --project that is not a Flisk project, is COMMAND_FAILED. Nothing is ever installed. With --no-open, FliskOS stops when its stdin closes, so start it with stdin open.
  • run: runs the command without input. service, runtime (container for docker exec in the running container, native for a local process), command (the argv given after --), exitCode (the command's, also flisk's exit code), and stdout and stderr (the last 64 KB of each). An unknown service, a missing command, or a container service that is not running is RUN_FAILED with exit code 1.
  • restart: resource (the name) and restarted (true) once Tilt accepted the trigger. An invalid --port or TILT_PORT is a USAGE error with exit code 2; no running Tilt is TILT_NOT_RUNNING and a trigger Tilt refused is TILT_TRIGGER_FAILED with Tilt's message, both with exit code 1.
  • agent sessions: agent (the resource name), port, sessions (each with sessionId, lastActivity, status and checkpointBytes; the last two may be null) and total. With --session-id <id> it is agent, port and session (the checkpoint the agent returned). Failures carry their own code: USAGE, UNKNOWN_RESOURCE, NOT_AN_AGENT, NO_PORT, NO_ROUTE, AGENT_NOT_RUNNING, AGENT_REQUEST_FAILED, or SESSION_LIST_UNSUPPORTED for an agent scaffolded before GET /sessions existed.
  • config explain: file (project-relative), ownership, reason, source (the input it is generated from, or null), driftOnUp (what flisk up does when the file was edited), isEditedNow (whether it differs from its snapshot now) and next (suggested steps, in order).
  • runtime: prints a bare object rather than the envelope: ok, assetRoot (the directory the bundled engine and templates were found in, or null), missing (required assets that are absent), layoutErrors and templateErrors. ok is true only when all three lists are empty and an asset root was found.

For project, resource, stack, import, eject and the config subcommands above, the human output goes to stderr without colour and stdout carries the envelope only. Outside a project each prints data: null and one COMMAND_FAILED error with suggestions, and exits 1. When a deeper check stops the command before it can report (for example the machine check of flisk project), the error code is the command's own failure code (PROJECT_FAILED, RESOURCE_FAILED, STACK_FAILED, IMPORT_FAILED, EJECT_FAILED, REGENERATE_FAILED, MIGRATE_FAILED, INFRA_TOGGLE_FAILED) with the message the check printed.

Example output for flisk resource orders-api --type backend --stack shop --json in a project with no other backend:

{
  "schemaVersion": 1,
  "data": {
    "action": "created",
    "name": "orders-api",
    "type": "backend",
    "stack": "shop",
    "port": 4000,
    "path": "services/shop/orders-api",
    "framework": "hono",
    "language": "bun",
    "discovered": true
  },
  "errors": []
}

networks --json previously returned a bare array. It now returns the versioned envelope. During migration, networks --json-legacy returns the old array and is scheduled for removal in CLI version 1.5.0; migrate consumers before that release to data.services. Existing text and raw output remain available.

Within schema version 1, additive fields are allowed. Clients should ignore unknown fields and reject unsupported major schema versions. Removing or renaming fields, changing their meaning, or changing collection shapes requires a versioned transition and migration notes. Text aliases and plural command aliases remain supported.

Minimal polling example:

if flisk status --json >status.json; then
  jq -r '.data.stacks[] | [.name, .resourceCount] | @tsv' status.json
else
  jq -r '.errors[]?.message, .data.resources[]? | strings' status.json >&2
fi

Example output for flisk projects --json in a configured project with a shop stack and one resource that is not in any stack:

{
  "schemaVersion": 1,
  "data": {
    "projectRoot": "/home/dev/orders",
    "configured": true,
    "missingFiles": [],
    "resources": 3,
    "stacks": ["shop"],
    "unassignedResources": 1
  },
  "errors": []
}

The master configuration files are TILT_TECH_STACK.star, TILT_RESOURCE_DEFAULTS.star and spec.master, which flisk project generates in .flisk/.flisk-out. In a project that has not been configured yet, configured is false and missingFiles lists the files that are not there. resources and stacks are still filled in from the service.json files that were discovered, so an unconfigured project looks like this:

{
  "schemaVersion": 1,
  "data": {
    "projectRoot": "/home/dev/orders",
    "configured": false,
    "missingFiles": ["TILT_TECH_STACK.star", "TILT_RESOURCE_DEFAULTS.star", "spec.master"],
    "resources": 0,
    "stacks": [],
    "unassignedResources": 0
  },
  "errors": []
}

Without --check, flisk projects --json exits 0 in both cases, so read data.configured. With --check, the same response is printed and the exit code is 0 when every master file exists and 1 when any is missing, which makes it usable as a CI gate: flisk projects --json --check. --check only looks for the master files. It does not validate service.json files (use flisk config verify --json for that) or check that the containers are healthy.

Outside a project, flisk projects --json (with or without --check) prints data: null and one COMMAND_FAILED error that includes suggestions, writes the message to stderr and exits 1:

{
  "schemaVersion": 1,
  "data": null,
  "errors": [
    {
      "code": "COMMAND_FAILED",
      "message": "Could not find project root (no .flisk/project.json found)",
      "suggestions": [
        "Run this from within a Flisk project",
        "Run `flisk project --yes` to initialize a new project"
      ]
    }
  ]
}

Example output for flisk up --dry-run --json --only orders-api, where orders-api lists orders-db in dependsOn:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "dryRun": true,
    "stack": null,
    "services": ["orders-api", "orders-db"],
    "requested": ["orders-api"],
    "declaredDependencies": ["orders-db"],
    "command": "tilt up -f <path to Tiltfile> -- --focus=orders-api orders-api orders-db"
  },
  "errors": []
}

A dry run reports the plan and exits. It does not start Tilt or any services. Read services as the list that would start, and declaredDependencies as the part of it that comes from dependsOn in service.json. Shared infrastructure that Tilt always enables, such as Postgres and Traefik, is not listed. The command string contains the Tiltfile path on your machine, and is just tilt up when neither a stack nor --only is given. Without --only, requested and declaredDependencies are absent. This is an illustrative example, and a dry run still runs the checks that flisk up runs first (for example, it refuses a project whose generated files were edited by hand). If one of them fails, data is {"ok": false} and errors explains why.

Example output for flisk up --json:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "stack": null,
    "services": ["orders-api", "orders-db"],
    "tiltUrl": "http://localhost:10350"
  },
  "errors": []
}

With --only (for example, flisk up --json --only orders-api), the response also includes requested (the services explicitly requested) and dependencies (the additional services Tilt enabled that you did not name). services lists every service Tilt actually enabled, which is requested plus dependencies:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "stack": null,
    "services": ["orders-api", "orders-db"],
    "requested": ["orders-api"],
    "dependencies": ["orders-db"],
    "tiltUrl": "http://localhost:10350"
  },
  "errors": []
}

Success means startup reached the command's ready state: every expected non-deferred Tilt resource is built and running, and its Docker health check (if declared) has passed. Tilt continues running in the background after the JSON output is emitted, so scripting workflows should run the command in the background (for example, flisk up --json &).

With --only, the readiness guarantee applies to the requested services only (excluding any that are deferred). Dependency services appear in services and dependencies because Tilt enabled them, but they are not checked for readiness, so a dependency that is still starting or has failed does not change ok. Consumers that need a dependency to be ready must check it themselves.

If one or more resources fail to start or the readiness timeout expires (FLISK_UP_READY_TIMEOUT_MS, default 15 minutes), data.ok is false with data.tiltUrl and data.failures, and errors reports UP_RESOURCE_FAILED or UP_TIMEOUT:

{
  "schemaVersion": 1,
  "data": {
    "ok": false,
    "tiltUrl": "http://localhost:10350",
    "failures": [
      {
        "name": "orders-api",
        "message": "Build failed: exit status 1"
      }
    ]
  },
  "errors": [
    {
      "code": "UP_RESOURCE_FAILED",
      "message": "Resources failed (orders-api: Build failed: exit status 1). Tilt is still running; inspect it or run: flisk down"
    }
  ]
}

When services declare a smoke check, the checks are executed against public ingress endpoints after readiness is reached. If a smoke check fails, Tilt is automatically stopped and the command emits UP_SMOKE_FAILED before exiting with status 1:

{
  "schemaVersion": 1,
  "data": {
    "ok": false
  },
  "errors": [
    {
      "code": "UP_SMOKE_FAILED",
      "message": "A smoke check failed; Tilt was stopped"
    }
  ]
}

Example output for flisk down --json:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "stoppedTilt": [48213]
  },
  "errors": []
}

With --prune-cache (flisk down --json --prune-cache -y), the response also reports what was pruned from Docker's build cache:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "stoppedTilt": [],
    "prunedCache": {
      "until": "24h",
      "reclaimed": "2.6GB"
    }
  },
  "errors": []
}

With --dry-run --prune-cache (flisk down --json --dry-run --prune-cache), data.wouldPruneCache reports { "until": "24h" } without executing pruning.

With --prune-networks (flisk down --json --prune-networks), the response also lists what happened to the project's Docker networks. A network that still has a container attached is kept, and reason is the first line of the Docker error:

{
  "schemaVersion": 1,
  "data": {
    "ok": true,
    "stoppedTilt": [],
    "removedNetworks": ["orders_default"],
    "keptNetworks": [
      {
        "name": "orders_shared",
        "reason": "Error response from daemon: error while removing network: network orders_shared has active endpoints"
      }
    ]
  },
  "errors": []
}

removedNetworks and keptNetworks are only present when --prune-networks is given and the networks could be checked. Without the flag, or when the project root or its configuration cannot be read, both fields are absent, so check for them before reading them. In JSON mode the human-readable "Removed networks" and "Kept network" lines are not printed, and a failed network cleanup does not change ok. The process ID and network names above are illustrative.

A failed shutdown is reported in the same envelope. If tilt down exits with a non-zero code, or a prerequisite check fails (Docker or Tilt missing or not responding), flisk down writes the details to stderr, exits with that code (the code from tilt down, or 1 for a missing prerequisite), and stdout contains one object with data.ok set to false and a DOWN_FAILED error:

{
  "schemaVersion": 1,
  "data": {
    "ok": false
  },
  "errors": [
    {
      "code": "DOWN_FAILED",
      "message": "flisk down exited with code 1"
    }
  ]
}

When the command printed an error before it stopped (for example Could not find project root), message is that error and suggestions lists its hints, so a script gets the real reason from the JSON alone. Otherwise message ends with the exit code of the command. A failed shutdown has no stoppedTilt or network fields, and containers may still be running, so run flisk status --json or flisk doctor --json to see what is left. Outside a project, flisk down --json does not use this shape: data is null, errors has one COMMAND_FAILED entry with suggestions, and the exit code is 1.

New resources created by flisk resource include a per-file $schema reference to the hosted service manifest schema. VS Code's JSON language service uses that reference for completion and validation; the generator leaves .vscode/settings.json untouched, so existing project editor settings remain intact. Resource registration adds the reference only when one is absent and preserves a user's existing $schema value.