Launch Station field guide

From one command to a whole local system.

Follow the rail in order. Each step builds on the session definition before it, so the commands stay useful as your project grows.

One command

01 / 08

Install the app, CLI, and local service.

Homebrew installs the signed app and gives your shell the launch command. The local catalog lives in Application Support, so it stays separate from any one project.

Terminal
brew install --cask JakeMawson/tap/launchstation
What to remember
  • Requires macOS 13 or newer.
  • Run this once per Mac, not once per project.
After this step

The launch command is available from Terminal.

Scroll down for more detail

The complete reference

Every command.
Every moving part.

From your first launcher to a multi-service workflow. Syntax, defaults, constraints and practical examples, all in one place.

15 chapters50 topicsmacOS · local development
Chapter 01

Start here

Install once, then give each project a repeatable way to start. Launch Station manages local development on macOS; it does not deploy your application.

1.1Install and verify#

Requires macOS 13 or newer. Homebrew installs the signed, notarized app, the launch command, and its local service. Your project still needs its own tools and dependencies: Node for npm/Expo, Python for Python commands, or Xcode and a downloaded Simulator runtime for iOS.

Install Launch Station
brew install --cask JakeMawson/tap/launchstation
launch --help
launch doctor

Open Launch Station from Applications. launch doctor checks service compatibility and reports the installed service version. This reference covers the current CLI surface, including named endpoints; if a flag is unknown, check your installed version and update.

Update an existing installation
brew update
brew upgrade --cask JakeMawson/tap/launchstation

1.2Project, launcher, action, session#

TermWhat it means
ProjectAn existing directory registered in the local catalog. It groups launchers and has a display name.
LauncherA named, saved definition: description, tags, one or more actions, one primary action, and optional named endpoints.
ActionOne command or target, with a runner, working directory, environment, startup order, port policy, and stop/readiness rules.
Primary actionThe action that receives runtime arguments and the run-time open request. It is not necessarily the first action to start.
SessionOne recorded execution of a launcher. It snapshots the definition/revision, exact process ownership, endpoints, output, and state.
Primary / additionalOne reusable primary session slot per launcher; --new creates a separate additional session.
Catalog / mirrorSQLite is authoritative. launch_details.md is a generated read-only project summary, not a configuration file to edit.

Saving a launcher does not run it. Editing a definition does not change an already-running session. Relaunch creates a fresh session using the updated definition.

1.3Your first working launcher#

In Terminal, move into an existing project folder. This self-contained example serves that folder using Python 3; confirm python3 is installed first. It only exposes a loopback address.

Register, run, inspect, close
cd ~/Projects/my-project
launch init . --project-name "My project"

launch create "My project docs" "Serve this project's files locally" \
  --type process --port auto \
  --url 'http://${HOST}:${PORT}/' \
  --health 'http://${HOST}:${PORT}/' \
  -- python3 -m http.server '${PORT}' --bind '${HOST}'

launch "My project docs" --open
launch status "My project docs"
launch logs "My project docs"
launch close "My project docs"

Choose a globally unique launcher name. Replace the example directory and project name with your own. Serving a directory makes its files available to local browser clients, so choose the directory intentionally.

Chapter 02

Command syntax & inputs

Understand the command boundary once and every example becomes easier to adapt.

2.1Read the examples#

NotationInput type / meaning
NAME / LAUNCHER / ACTIONA string. Quote names containing spaces, for example "Shop web". ACTION is an action name within that launcher.
[OPTION]Optional in a syntax synopsis. Do not type the square brackets.
VALUE...One or more values. The ellipsis is explanatory, not literal.
UUID / SESSION_UUIDA real UUID copied from details/history/status. Never use a PID or port in its place.
N / INDEX / SECONDSA decimal integer. Argument indices start at zero. Accepted ranges are documented per flag.
TEXT / PATH / TEMPLATEOne shell argument, quoted when needed. ~/… is supported for configured directories; prefer an absolute path or a project-relative action directory.
TOKEN / OPTION_IDAn opaque value returned by the relevant command. Copy it exactly; do not construct one.
--jsonA valueless switch on supported commands. Prints structured JSON instead of the usual readable result; it does not mean dry-run.

Flags use separate tokens: --port auto, not --port=auto. Boolean switches have no following true or false. Put positional names before options where the synopsis shows that order. launch --help, launch -h, and launch help show the top-level help; there is no universal per-command --help or dry-run switch.

2.2The two meanings of --#

Create: save an executable and literal arguments
launch create "Shop web" "Run the frontend" --type process --port auto \
  -- npm run dev -- --host '${HOST}' --port '${PORT}' --strictPort

The first -- ends Launch Station's definition options. npm is the executable; everything after it is a stored argument. The second -- belongs to npm and forwards subsequent flags to the script. --host, --port, and --strictPort at that position belong to Vite, not Launch Station.

Run: append arguments for this session only
launch "Shop web" -- --mode demo

On run or relaunch, arguments after -- are appended to the primary action's stored arguments for this session. They are not saved back to the definition. On create/action add, a nonempty executable tail replaces any --executable and --arg values, clears the shell command, and converts a shell runner to process. Do not combine the two command forms.

2.3Quoting, substitution & aliases#

Single quotes preserve ${HOST}, ${PORT}, and linked-action placeholders until Launch Station starts the action. Double quotes around a path such as "$PWD" expand it in your current shell at registration time. Direct actions do not interpret pipes, &&, redirects, glob patterns, or $NAME as shell syntax. Use --command when shell syntax is needed.

Shell command form
launch create "Shop shell" "Start via zsh" \
  --port auto --url 'http://${HOST}:${PORT}/' \
  --command 'npm run dev -- --host "$HOST" --port "$PORT" --strictPort'
AliasEquivalent command
--init, --create, --listinit, create, list
--details, --retrieve, retrievedetails
--update, --delete, --statusupdate, delete, status
--close, --relaunch, --sync, --doctorclose, relaunch, sync, doctor
launch NAMElaunch run NAME

These are top-level command aliases, not additional flags for every command. --dir aliases --directory only on commands that accept that directory option.

Chapter 03

Projects & discovery

Register the project root, find its launchers, and inspect the saved definition before changing it.

3.1Initialize or rename a project#

Syntax
launch init [DIRECTORY] [--project-name NAME] [--json]
launch project update --name NAME [--directory PATH] [--if-revision N] [--json]
InputRequired / defaultBehavior
DIRECTORY or --directory / --dirOptional; current directoryInitialize an existing folder. Paths are canonicalized and symlinks resolved. Re-initialization is idempotent.
--project-name NAMEOptional on init; folder name when omittedHuman-readable project label.
--name NAMERequired on project updateChanges the project display name, not its directory.
--directory / --dir PATHOptional on project update; current directoryResolve the registered project to rename.
--if-revision NOptional on project update; fetched project revisionReject an edit based on an outdated project revision.
--jsonOptional; readable textReturn the project object.

Launcher creation resolves the exact project or nearest registered ancestor of the supplied directory. Initialize the actual repository/application root deliberately. A conflicting unmanaged launch_details.md is not overwritten; inspect it before deciding how to resolve the conflict. There is no CLI project-move or project-delete command.

Rename the label only
launch project update --directory ~/Projects/shop --name "Shop storefront"

3.2List and search#

Syntax
launch list [QUERY] [--tag TAG] [--state STATE] [--directory PATH] [--json]
InputDefaultBehavior
QUERYAll launchersOne search string matching launcher name, description, or tags.
--tag TAGNo tag filterOne normalized tag filter. Unlike create's repeatable --tag, repeating this filter leaves the last value.
--state STATENo state filterMatches an active session or the launcher's latest session. idle is a display label, not an accepted state filter.
--directory / --dir PATHAll projectsResolve and filter to one registered project.
--jsonReadable listReturn complete launcher detail objects.
Combine filters
launch list "web" --tag local --directory ~/Projects/shop
launch list --state running --json

Launcher names are unique across all projects. Matching ignores case, repeated/surrounding whitespace, accents, and character width. Names allow at most 80 characters and 240 UTF-8 bytes, cannot begin with a hyphen, and cannot contain slashes or control characters. CLI command words such as run, status, project, and help are reserved launcher names.

3.3Details, status & health#

Read-only inspection
launch details NAME [--json]
launch retrieve NAME [--json]
launch status [NAME] [--json]
launch doctor [DIRECTORY] [--json]
CommandResult
details / retrieve NAMEThe saved launcher and project, revision, actions, tags, notes, named endpoints, active sessions and last session.
status NAMEThe same detailed launcher view, useful before acting on a specific session.
status without NAMEOnly currently active managed sessions across the catalog.
doctor [DIRECTORY]Service version/schema/compatibility, endpoint, CLI path, and whether that directory resolves to a project. Defaults to the current directory.

These commands inspect the catalog and service; they do not launch your project. JSON is useful for copying exact session IDs and revisions. External listeners are a separate observation surface.

Chapter 04

Create & configure actions

A launcher starts with one action. The tables below cover every definition option and its initial default.

4.1Required inputs and command forms#

Create a launcher
launch create NAME DESCRIPTION [RUN_DETAILS] [OPTIONS] -- EXECUTABLE ARG...
launch create NAME DESCRIPTION [RUN_DETAILS] --command COMMAND [OPTIONS]

NAME and DESCRIPTION are required nonempty strings. The project must already be initialized. Supply a runnable command or target: a direct executable tail, --command, or an explicit runner with --executable. Creation saves a definition only. RUN_DETAILS is optional descriptive prose and is never executed.

Flag / inputDefault / requirementMeaning
NAMERequiredGlobally unique launcher name.
DESCRIPTIONRequiredNon-whitespace description of what the launcher does.
RUN_DETAILS or --run-details TEXTOptional; absentLonger notes. Supply the positional form or the flag, not both.
--directory PATH / --dir PATHCurrent directoryRegistered project scope, not the action's working directory.
--tag TAGNo tags; repeatableAdd a searchable tag.
--tags A,BNo tags; repeatableAdd comma-separated tags. Empty entries are ignored; normalized duplicates collapse.
--jsonOffReturn the complete created detail as JSON.

4.2Runner, executable & action fields#

FlagAccepted input / defaultWhat it does
--action-name NAMEString; main on createNames this action. Action names are unique inside a launcher; command words are allowed.
--action-description TEXTString; Primary launch action on createHuman-readable purpose; cannot be blank. On action add, its positional DESCRIPTION supplies this value.
--cwd PATHExisting directory; .Relative to the registered project, or absolute/tilde-expanded. Does not create a directory.
--order NInteger; 0 on createAscending startup order. Values must be unique inside the launcher. Negative integers are accepted. Added actions default to current maximum + 1.
--type TYPEprocess, shell, app, url, iosDefault model is shell; an executable tail selects process unless app/url/ios was selected. Prefer explicit --type.
--command COMMANDNonempty zsh commandSelects shell and clears executable. Shell actions run through /bin/zsh -lc.
--executable VALUEExecutable path/name or targetFor process/ios: an executable. For app: application path/identifier. For url: an absolute URL or existing absolute file.
--arg VALUEString; empty array; repeatableAppend one stored argument. Even --arg --verbose stores one literal flag. Not allowed for URL actions.
--app-bundle-id IDString; absentOptional native app identifier. Use with an app action; not a command to install the app.
--required / --optionalRequired by defaultRequired failure rolls back earlier owned actions. Optional failure may leave a partial session.
--allow-runtime-args / --deny-runtime-argsAllowed by default, except URLWhether session-specific arguments may reach this action. Only the primary receives them.

Changing --type to shell clears executable; changing to a non-shell runner clears the shell command. Choosing url also clears arguments and disables runtime arguments. Put the type before its command fields, and finish with a valid combination. A bare --executable does not itself switch the default shell runner.

4.3Port, health & opening flags#

FlagAccepted input / defaultWhat it does
--port MODEnone (default), auto / automatic, or 1–65535No managed TCP port, fresh managed allocation, or requested fixed managed port. Only process/shell/ios can use managed ports.
--port-name NAMENonblank string; mainLogical label for the managed port.
--port-env NAMEEnvironment name; PORTName receiving the allocated port.
--host-env NAMEEnvironment name; HOSTName receiving the loopback host. Must differ from port-env.
--url TEMPLATEAbsolute URL after substitution; absentUser-facing endpoint. Preferred source for the session's open target.
--health TEMPLATEAbsolute HTTP(S) URL; absentReadiness probe; HTTP 200–399 succeeds.
--lease DURATIONPositive seconds or number+s/m/h/d; 8hManaged-port lease, for example 90s, 15m, 2h, 1d, or 3600. Decimal positive durations are accepted.
--open TARGETnone (default), browser, application, simulatorStored opening policy. application requires app; browser needs a resolvable HTTP(S) endpoint unless the runner itself is url.
--ready-timeout SECONDSInteger 1–600; 30Maximum startup/readiness time.
--stop-timeout SECONDSInteger 1–60; 8Graceful shutdown budget recorded in the session.

4.4Environment flags#

FlagInput / defaultBehavior
--env KEY=VALUERepeatable; no stored valuesSplit at the first =. Stores a non-secret string. Repeated keys overwrite earlier values; empty values are allowed.
--inherit-env NAMERepeatable; noneCopy that named value from the daemon's environment at launch—not your current Terminal's environment.
Environment nameStarts with a letter or underscore; then letters/numbers/underscoresUse portable ASCII names such as APP_MODE. Invalid names fail validation.
Reserved namesCODEX_PORT, CODEX_HOST, CODEX_SERVICE_IDCannot be configured or inherited; managed lifecycle owns them.
Reserved prefixLAUNCH_STATION_ACTION_Cannot be assigned/inherited manually; earlier compound actions supply these values.

Do not put secrets in any saved field. Names, commands, arguments, environment values, descriptions, tags, and notes are visible in the app, JSON, database, and generated project summary. Have the launched program read credentials from an appropriate secret store.

When a managed port is enabled, its configured port and host aliases cannot also appear in --env or --inherit-env. Baseline PATH includes ~/bin, Apple system paths, /opt/homebrew/bin, and /usr/local/bin. A daemon is not your interactive shell: use a known executable or absolute virtual-environment path rather than relying on aliases, nvm activation, or .zshrc.

Chapter 05

Runner recipes

These are templates for existing projects with dependencies installed. Adapt directories, script names and targets before saving. Flags after the command boundary belong to that tool.

5.1Vite and Next.js#

Vite project with an npm dev script
launch create "Shop Vite" "Run the Vite frontend" \
  --directory ~/Projects/shop --tag web \
  --type process --port auto \
  --url 'http://${HOST}:${PORT}/' --health 'http://${HOST}:${PORT}/' \
  -- npm run dev -- --host '${HOST}' --port '${PORT}' --strictPort

Vite's --strictPort stops it from silently moving to another port. Launch Station must know the actual listener it owns.

Next.js project with a Next dev script
launch create "Shop Next" "Run the Next.js frontend" \
  --directory ~/Projects/shop-next --type process --port auto \
  --url 'http://${HOST}:${PORT}/' --health 'http://${HOST}:${PORT}/' \
  --ready-timeout 90 \
  -- npm run dev -- --hostname '${HOST}' --port '${PORT}'

Next.js uses --hostname, not Vite's --host. Do not add Vite's --strictPort to a different CLI. If your dev script invokes another framework, use that framework's actual host/port syntax.

5.2Python, virtual environments & custom services#

Serve static files
launch create "Research files" "Serve the local public directory" \
  --directory ~/Projects/research --cwd public \
  --type process --port auto \
  --health 'http://${HOST}:${PORT}/' --url 'http://${HOST}:${PORT}/' \
  -- python3 -m http.server '${PORT}' --bind '${HOST}'
A project's installed Uvicorn
launch create "Research API" "Run the ASGI API" \
  --directory ~/Projects/research --type process --port auto \
  --health 'http://${HOST}:${PORT}/health' --url 'http://${HOST}:${PORT}/docs' \
  -- ./.venv/bin/uvicorn app:app --host '${HOST}' --port '${PORT}'

This assumes .venv contains Uvicorn, app:app exists, and the API implements /health. Replace those values to match your code. Slash-containing relative executables resolve from the action's working directory.

Custom variable names
launch create "Custom API" "Run the project server" \
  --directory ~/Projects/custom-api --type process --port auto \
  --port-env APP_PORT --host-env APP_HOST \
  --url 'http://${HOST}:${PORT}/' --health 'http://${HOST}:${PORT}/health' \
  -- node server.mjs --host '${APP_HOST}' --port '${APP_PORT}'

The last recipe assumes your server supports those two flags. --port-env changes the process variable alias, not the ${PORT} spelling used in endpoint templates.

5.3Non-server commands and shell workflows#

Long-lived worker without a TCP listener
launch create "Shop worker" "Run the background worker" \
  --directory ~/Projects/shop --type process --port none \
  --env APP_MODE=development -- node worker.mjs
A shell pipeline
launch create "Shop pipeline" "Transform local input" \
  --directory ~/Projects/shop --type shell \
  --command './scripts/read-input | ./scripts/transform-output'

Use process for exact argument boundaries; use shell when the shell must interpret a pipeline or redirect. Required executables/scripts must already exist. A finite command transitions to exited when it finishes; Launch Station does not keep it running or invent a server endpoint.

Shell stored arguments and runtime arguments are quoted and appended once. Direct executable, argument, and environment strings support ${NAME} substitution from the action environment. Undefined values cannot stand in for required configuration; inspect your command and output.

5.4Native apps, URLs and files#

Start a new Xcode instance
launch create "Shop Xcode" "Open Xcode for this project" \
  --directory ~/Projects/shop --type app --open application \
  --app-bundle-id com.apple.dt.Xcode \
  -- /Applications/Xcode.app

Application arguments are passed through LaunchServices as app arguments. They are not shell commands or guaranteed document-opening instructions. Launch Station only closes an exact newly owned instance; if LaunchServices reuses an existing app, it leaves that instance alone.

Open a document without owning its editor
launch create "Shop readme" "Open the project README" \
  --directory ~/Projects/shop --type url \
  -- "$HOME/Projects/shop/README.md"

A URL runner accepts one absolute URL or existing absolute file path, stores no arguments, and completes after opening it. It does not own or close the receiving browser/editor. Use this form for a workspace/document when you only want it opened.

Chapter 06

Expo & iOS Simulator

Expo has specific port and Simulator behavior. Keep the Metro service, launch target, and device selection distinct.

6.1Expo in Simulator#

Managed Expo Go workflow
launch create "Mobile Expo" "Start Expo in iOS Simulator" \
  --directory ~/Projects/mobile --tag expo --tag ios \
  --action-name metro --action-description "Expo Metro bundler" \
  --type ios --port auto --open simulator \
  --ready-timeout 120 \
  -- npx expo start --ios --go

Requires a working Expo project and Xcode with an available iOS Simulator runtime. Launch Station recognizes expo, npx expo, and supported shell forms. It adds a missing localhost host mode and managed port, and adds --ios when simulator opening is configured. Explicit host modes, ports, --ios, and -i are not duplicated.

Expo's --go selects Expo Go. Replace it with --dev-client when you already have a compatible development build installed. Without either override, Expo chooses according to the project's development-client setup. These are Expo flags, not Launch Station definition options.

6.2Metro without opening Simulator#

Start only the host-side bundler
launch create "Mobile Metro" "Start Metro without opening a device" \
  --directory ~/Projects/mobile --type process \
  --port auto --open none \
  -- npx expo start --localhost --port '${PORT}'

Use process, --open none, no --ios/-i, and no Simulator-specific environment when you want no device interaction. An ios runner can still select/boot a Simulator as part of preparation. The managed process has no attached interactive Terminal UI; use saved flags and session open choices rather than expecting to press Expo's interactive keys.

Expo web differs across bundlers/SDKs. Check npx expo start --help for the project's installed version before storing --web; confirm that the web server actually consumes the allocated port. Do not assume Metro and a separate web listener share a port. Give each persistent listener its own managed action. For exported static web files, use the Python static-server recipe.

6.3Device and app configuration#

Environment keyInput / defaultBehavior
CODEX_SIMULATOR_DEVICEbooted, exact device name, or UDID; absentSelects a device. Without it: prefer an already booted device, otherwise an available iOS device.
CODEX_SIMULATOR_UDIDRuntime outputSelected device UUID supplied to the command.
CODEX_SIMULATOR_NAMERuntime outputSelected device name supplied to the command.
CODEX_SIMULATOR_APP_PATHOptional path to a built .appInstall a local built app during preparation. Prefer an absolute path.
CODEX_SIMULATOR_BUNDLE_IDOptional bundle identifierApp identifier to launch on the selected Simulator.
CODEX_SIMULATOR_APP_ARGUMENTSOptional JSON array of stringsArguments for simulated-app launch, for example ["--ui-testing","--reset-state"].
Choose an installed Simulator by name
launch update "Mobile Expo" \
  --env 'CODEX_SIMULATOR_DEVICE=iPhone 16 Pro'

The device name must exist on your Mac. Use xcrun simctl list devices available to see available names/UDIDs. Exact device IDs are less ambiguous when names repeat.

6.4Custom Simulator commands and close behavior#

Launch a previously installed app and attach its console
launch create "Demo Simulator" "Run the installed simulator app" \
  --directory ~/Projects/demo --type ios --open simulator \
  --env CODEX_SIMULATOR_DEVICE=booted \
  -- xcrun simctl launch --console '${CODEX_SIMULATOR_UDID}' com.example.DemoApp

Replace the bundle identifier with your app's installed identifier. A built app can be prepared using the APP_PATH/BUNDLE_ID/APP_ARGUMENTS environment fields above; it must be a Simulator-compatible build.

Chapter 07

Ports, environment & endpoints

A port is a resource, not proof of ownership. Configure the actual listener and readiness path together.

7.1Managed allocation and readiness#

--port auto asks codex-port for a verified loopback allocation. --port N requests a fixed port and refuses an unrelated listener. --port none uses the process-group runner without a managed TCP port. A URL field alone does not turn an unmanaged listener into a managed one.

The launched command must consume the allocated host/port, through environment variables or arguments. A framework that silently switches ports can invalidate the expected endpoint, so use its strict binding option when available. Managed host defaults to 127.0.0.1; detected Expo uses localhost.

HTTP health readiness succeeds on status 200–399 within the ready timeout. Without an HTTP health URL, successful process start is not proof that your application is usable. A required failure closes earlier owned actions; an optional failure may produce a partial session.

The port lease defaults to eight hours. The daemon renews active managed ownership before expiry while it remains healthy. A fresh lifecycle may reuse the same numeric port after the old listener has fully stopped; fresh means a new verified owner, not necessarily a different number.

7.2Substitution rules#

WhereSupported values
Endpoint / health templates${HOST}, ${PORT}, {host}, {port}, {{host}}, {{port}}. Result must be an absolute URL; health requires HTTP(S).
Direct executable / arguments / --env values${NAME} references from the action's runtime environment.
Shell commandNormal zsh environment expansion, such as $PORT or ${PORT}.
Action --cwdAn existing directory path, not a shell expression to execute.
Cross-action references${LAUNCH_STATION_ACTION_API_URL} and related HOST/PORT values in commands, arguments, or environment—not URL/health templates.

A port placeholder in a URL or health template requires auto/fixed mode. For a no-port static endpoint, HOST aliases resolve to loopback. Browser opening uses the action URL, then health URL; a managed port can otherwise supply a default HTTP endpoint.

7.3Named in-app destinations#

Replace the full named endpoint list
launch update "Shop Vite" \
  --endpoint 'Dashboard: /dashboard' \
  --endpoint 'Settings: /settings' \
  --endpoint 'Documentation: /docs'

launch update "Shop Vite" --clear-endpoints
OptionInput / behavior
--endpoint 'Name: /path'Launcher update only; repeat once per desired destination. Supplying endpoints replaces the entire list, not just one row.
--clear-endpointsLauncher update only; remove all configured named endpoints.
Endpoint nameSame normalized-name limits as an action; no colon. Names and paths must be unique; at most 20 destinations.
Endpoint pathStart with one /; no origin, //, whitespace, query, fragment, backslash, encoded slash/backslash, or ./.. segments. At most 2,048 characters / 8,192 UTF-8 bytes.

These are navigation shortcuts under the primary action's exact running HTTP(S) origin. They do not create server routes, add services, or perform health checks. The app offers them only when the selected session has a usable browser origin. Configure them after creating the launcher and relaunch to give a new session the updated endpoint snapshot.

Chapter 08

Compose a full stack

Each long-lived service gets one action. Explicit order and linked values connect the services without hard-coded ports.

8.1Add, update and remove an action#

Syntax
launch action add LAUNCHER ACTION DESCRIPTION [DEFINITION OPTIONS] -- EXECUTABLE ARG...
launch action update LAUNCHER ACTION [MUTATION OPTIONS] [--if-revision N] [--json]
launch action delete LAUNCHER ACTION [--yes --if-revision N] [--json]

Action add requires the launcher name, action name, and nonempty action description. It supports the action-definition flags in section 04, but not launcher-level --directory, --run-details, --tag, or --tags. It fetches and binds the current launcher revision internally; action add does not accept --if-revision.

Action update accepts --name / --description as aliases for action name/description, plus the mutation flags in section 10. It edits that action without changing which action is primary. Action delete follows the confirmation rules in section 10. The sole remaining action cannot be deleted; deleting the primary in a multi-action launcher selects the first remaining ordered action.

8.2API plus frontend, end to end#

Assumes a project with server/server.mjs accepting host/port arguments and a frontend directory containing a Vite dev script. Install both projects' dependencies first.

Create the provider before its consumer
launch init ~/Projects/shop --project-name "Shop"

launch create "Shop stack" "Start API and frontend together" \
  --directory ~/Projects/shop --tag compound \
  --action-name api --action-description "Local API server" \
  --cwd server --order 10 --type process --port auto --port-name api \
  --url 'http://${HOST}:${PORT}/' --health 'http://${HOST}:${PORT}/health' \
  -- node server.mjs --host '${HOST}' --port '${PORT}'

launch action add "Shop stack" frontend "Vite frontend" \
  --cwd frontend --order 20 --type process --port auto --port-name frontend \
  --url 'http://${HOST}:${PORT}/' --health 'http://${HOST}:${PORT}/' \
  --env 'VITE_API_URL=${LAUNCH_STATION_ACTION_API_URL}' \
  -- npm run dev -- --host '${HOST}' --port '${PORT}' --strictPort

launch update "Shop stack" --primary-action frontend
launch details "Shop stack"
launch "Shop stack" --open
launch close "Shop stack"

API starts and becomes ready first, then frontend. The frontend receives the actual API URL. Closing reverses the order. Runtime arguments and bare --open apply to frontend because it is primary.

For a database or worker, add another action with a unique earlier order and the project's real start command. If it opens a TCP listener, configure that listener's own managed port and explicit binding. A placeholder script does not automatically make database readiness or port management work.

8.3Dependency constraints and failure handling#

RuleReason
Unique order valuesStartup is ascending; shutdown is reverse. Action add defaults to maximum + 1, so gaps are safe.
Unique normalized names and linked tokensapi-worker and api worker both produce API_WORKER; a token collision is rejected.
Provider must be earlier and requiredA consumer cannot depend on an optional or not-yet-started provider.
Use only exposed HOST/PORT/URL valuesManaged providers expose all three. A no-port action with a static endpoint exposes URL only. URL one-shot actions do not expose provider values.
Link only process, shell or ios consumersNative app and URL runners cannot consume linked-action references.
No linked variables in URL/health templatesPut them in arguments or --env instead; each action's readiness template describes its own endpoint.

Token generation uppercases the normalized action name and replaces punctuation with underscores. Prefer simple ASCII action names such as api and frontend. Names without an ASCII alphanumeric token use an ID-derived fallback; inspect the returned definition instead of guessing it.

Chapter 09

Run, open, inspect & close

Use the saved launcher name to start work; use a returned session UUID when choosing an additional instance.

9.1Run and relaunch#

Syntax
launch NAME [--new] [--open] [--json] [-- RUNTIME_ARG...]
launch run NAME [--new] [--open] [--json] [-- RUNTIME_ARG...]
launch relaunch NAME [--session UUID] [--open] [--json] [-- RUNTIME_ARG...]
InputDefaultBehavior
NAMERequiredSaved launcher name.
--newOff; run onlyCreate an additional independently owned session. Does not adopt outside processes.
--openOff; no valueRequest the primary action's opening behavior for a new run.
--session UUIDPrimary; relaunch onlyReplace this exact active session belonging to the named launcher.
-- RUNTIME_ARG...EmptyAppend to the primary action's saved arguments for this session, if allowed.
--jsonOffReturn session data; relaunch returns previousSession and session.

Ordinary run returns an existing active primary unchanged. New runtime arguments and --open do not modify or re-open that reused instance. Use relaunch to apply new arguments, or launch open to select an existing target.

Relaunch is one daemon operation: reserve the exact selection, close it, then start a distinct replacement. It preserves primary/additional role and leaves other sessions alone. From idle it starts a new primary. It refuses an overlapping replacement if close loses ownership or the definition/selection changes during the operation.

9.2Open a specific target#

List, then use a returned option
launch open NAME [--session UUID] [--json]
launch open NAME --option OPTION_ID [--session UUID] [--probe] [--json]
FlagMeaning
--session UUIDOptional; active primary by default. Use the actual additional session UUID to target that instance.
--option OPTION_IDOptional, once. Without it: list choices only. With it: open the exact daemon-derived choice.
--probeRequires --option; supported for existing Expo options. Validate without opening. It is not a general HTTP health checker.
--jsonStructured list/result instead of text.

Option IDs are opaque and session-specific. Browser URLs, native app focus targets, named endpoints, or Simulator choices come from the daemon; do not substitute a raw URL, PID, or invented option ID. An expired or unavailable target must be refreshed from the current session.

9.3Logs and durable history#

Syntax
launch logs NAME [--session UUID]
launch history [NAME] [--state STATE] [--role primary|additional] [--limit N] [--cursor TOKEN] [--json]
InputDefault / constraintBehavior
logs --session UUIDOptionalRead that exact session's bounded combined logs. Otherwise selects primary active, then another active, then last session.
logs outputText onlyNo --json, --follow, or --tail flag. Use the recorded log path if your own tooling needs a live tail.
history NAMEOptional; all launchersFilter durable history to one launcher.
--state STATEOptionalstarting, running, partial, stopping, exited, failed, orphaned.
--role ROLEOptionalprimary or additional.
--limit N50; integer 1–200Maximum page size.
--cursor TOKENAbsentOpaque nextCursor from the previous response.
--jsonOffReturns a page with sessions and optional nextCursor.

History is newest first and records managed runs, including their original revisions and action definitions. It is not a list of every process on your Mac. External listener observations never become managed history automatically.

Inspect additional sessions
launch history "Shop stack" --role additional --state running --limit 20 --json

9.4Close and interpret state#

Close one exact session
launch close NAME [--session UUID] [--json]

Without --session, close targets the active primary only; if none exists, it reports a conflict instead of picking an additional instance. With a UUID, it verifies membership and that the session is active. Closing a multi-action session reverses startup order.

StateMeaning
startingActions are being started or checked for readiness.
runningThe managed session is active.
partialSome action work succeeded, but not every action is running successfully.
stoppingExact owned work is closing.
exitedThe session ended; inspect exit code and logs for context.
failedStartup, readiness, or execution failed; inspect the recorded error and logs.
orphanedExact ownership cannot be proven. It is not permission to kill a similarly named process.
idleA catalog/UI label when no active session exists; not a stored session state.

Process-group close escalates SIGINT → SIGTERM → SIGKILL within the recorded budget only while exact identity holds. Managed-port close delegates to the recorded manager ID. App close excludes reused instances; URL close does not close a receiving app; iOS close never shuts down Simulator.

Chapter 10

Edit & delete definitions

Edits are revision-bound and apply to future runs. Inspect the current definition first, then change only the fields you intend.

10.1Launcher metadata and primary selection#

Syntax
launch update NAME [LAUNCHER FIELDS] [ACTION MUTATIONS] [--if-revision N] [--json]
FlagInput / defaultBehavior
--name NEW_NAMEString; unchangedRename the launcher, subject to global uniqueness.
--description TEXTNonblank string; unchangedReplace the launcher description.
--run-details TEXT / --clear-run-detailsString or valueless clear; unchangedSet or remove notes. Use one form intentionally.
--tags A,B / --clear-tagsList or clear; unchangedReplace or empty the tag list.
--add-tag TAG / --remove-tag TAGRepeatable stringsAdd/remove normalized tag values.
--primary-action ACTIONExisting action name; unchangedSelect primary. Must precede any action mutation flags if used together.
--endpoint 'Name: /path' / --clear-endpointsReplacement list or clear; unchangedSet the complete named endpoint configuration.
--if-revision NInteger; freshly fetched revisionReject stale edits instead of overwriting a changed definition.
--jsonOffReturn updated complete detail.

--tag is a creation flag; use --add-tag or --tags when updating. Project directory is not changed through launcher update. Action edits in this command target the current primary unless --primary-action selects another first.

Change a description and choose primary
launch update "Shop stack" --description "API and frontend for local work" --primary-action frontend

10.2Every action mutation#

All action fields from section 04 can be changed using launch update or launch action update. In addition to those setters, the following flags alter or clear existing values. The command is validated as a complete action before saving.

MutationInput / behavior
--action-name NAME / --action-description TEXTRename/describe the action. action update also accepts --name and --description.
--clear-commandClear the shell command. Pair with a valid runner/executable change; a shell action cannot remain empty.
--clear-executableClear executable/target. Pair with a valid shell command/runner change.
--arg VALUE / --append-arg VALUEAppend one stored string argument; repeatable.
--clear-argsReplace the argument array with an empty array.
--remove-arg VALUERemove every argument exactly equal to VALUE, including duplicates.
--set-arg INDEX VALUEReplace an existing zero-based argument index with one string.
--args-json '["a","b"]'Replace the complete argument array. Must be a JSON array of strings, excluding the executable.
--env KEY=VALUE / --remove-env KEY / --clear-envSet one value, remove one key, or clear the stored environment.
--inherit-env NAME / --remove-inherit-env NAME / --clear-inherit-envAdd, remove, or clear inherited daemon variable names.
--clear-url / --clear-healthRemove the corresponding optional template.
--clear-app-bundle-idRemove the optional application bundle ID.
All other setters--cwd, --order, --type, --command, --executable, --app-bundle-id, --port, --port-name, --port-env, --host-env, --url, --lease, --health, --open, --ready-timeout, --stop-timeout, --required/--optional, --allow-runtime-args/--deny-runtime-args.

Mutation flags are applied in order. For example, --clear-args --arg a --arg b produces ["a","b"]. Prefer an atomic --args-json replacement when changing an entire command. Switching to URL clears arguments and denies runtime arguments.

10.3Safe command replacement examples#

Replace the primary command arguments
launch details "Shop Vite" --json
launch update "Shop Vite" \
  --type process --executable npm \
  --args-json '["run","dev","--","--host","${HOST}","--port","${PORT}","--strictPort"]'
launch relaunch "Shop Vite" --open
Update a supporting action only
launch action update "Shop stack" api \
  --ready-timeout 90 --env LOG_LEVEL=debug
launch action update "Shop stack" api --remove-env LOG_LEVEL

An explicit --if-revision refers to the launcher revision, even for action mutations. On conflict, retrieve fresh details and review the concurrent change before retrying. Do not blindly repeat a mutation after a lost response; it may already have succeeded.

10.4Delete a saved shortcut or action#

Interactive deletion
launch delete NAME
launch action delete LAUNCHER ACTION
Explicit noninteractive form
launch delete NAME --yes --if-revision N [--json]
launch action delete LAUNCHER ACTION --yes --if-revision N [--json]

Interactive deletion requires a Terminal/TTY and the exact displayed launcher/action name. --yes skips the typed prompt only when accompanied by --if-revision N; it is not an unrestricted force option. Supply the revision you just inspected.

Deleting a launcher is refused while managed sessions are active, when confirmation expires, or when its revision changes. The shortcut is removed from the catalog; project source files and durable history are not deleted. An action cannot be removed if it would leave the launcher empty. Do not delete and recreate a launcher merely to change its command.

Chapter 11

Separately started processes

Observe local listeners without confusing discovery with ownership.

11.1Find and review an outside listener#

Syntax
launch external list [--refresh] [--json]
launch external draft OBSERVATION_UUID [--json]

List returns the daemon's cached listener inventory; --refresh requests a current scan. Each observation has an ephemeral UUID, PID identity, endpoints, ownership classification, and available command information. Bare launch external defaults to list.

Draft refreshes the observation and returns a proposal only. It neither saves a launcher nor adopts the process. Review directory, runner, command, arguments, and port policy, then create a launcher through the normal flow if appropriate. Use the real command rather than a redacted placeholder.

11.2Confirmation-bound external close#

Interactive only
launch external close OBSERVATION_UUID

Requires a TTY and the exact freshly displayed confirmation text. It accepts no --json, --yes, arbitrary PID, or port argument. The daemon rechecks identity and closability before signaling.

Stale, already-managed, unverified, or unsafe observations are refused. Observation IDs and close intents are transient and do not survive daemon restart. After saving a reviewed launcher, future managed sessions have their own identities; the previously outside process does not become one of them.

Chapter 12

Use the macOS app

The desktop app and CLI share the same catalog, actions, revisions and managed sessions. You do not need agent configuration to use either.

12.1Find and operate a launcher#

Open Launch Station from Applications. Search by launcher name, project, directory or tags. Select a launcher to inspect its working directory, run details, saved actions, endpoint configuration and active sessions.

ControlWhat it does
LAUNCHStarts an idle primary and requests its primary target to open.
LAUNCH NEWCreates an additional exact managed session.
Runtime argumentsArguments for the next launch/relaunch only; quotes and backslashes group values. Not a shell command field.
OPENChoose a daemon-derived destination for that session, including available named endpoints.
CLOSE / RELAUNCHReview the exact service list and confirm the selected session operation.
Logs / activity / historyInspect current/last output, lifecycle diagnostics, or durable managed sessions.
Copy / revealCopy a displayed command or reveal its working directory in Finder.
Keyboard shortcutAction
Command-FFocus search.
Command-ReturnLaunch selected idle launcher.
Command-.Request close of selected active session.
Shift-Command-ReturnRequest relaunch.
Command-RRefresh catalog.

12.2Edit, add and inspect#

To create a launcher without Terminal, click Add Launcher below the catalog. Enter a name, description, and existing project directory (Browse can select it). Choose Process for a direct executable and arguments, or Shell for an intentional shell command. Review the working directory, port mode and opening behavior; the form marks uncertain fields as Review required. Confirm that you have reviewed the command, directory, and port settings, then save. Saving defines the launcher; it does not start it.

For a managed web server, choose an automatic port and pass its HOST/PORT placeholders to your server command. Enable the HTTP open target only when the action actually serves HTTP. Command arguments are separate values, not arbitrary shell source. The form's validation must pass before Save is available.

Edit Launcher exposes saved metadata, actions, runner/command, ports, opening behavior, named endpoints, and advanced action controls. Save commits a revised definition; running sessions retain the definition they started with. Use the CLI's full flag surface when a specialized property is not exposed in the editor.

Entries marked STARTED SEPARATELY are observed listeners, not managed sessions. Add Launcher opens a reviewable proposal. Saving that proposal does not take over the already-running process. External Close has its own exact confirmation.

The history view can inspect one launcher or all managed launchers. Session rows distinguish primary/additional role, start/end times, state and logs. The app supports light/dark appearance and reduced motion.

12.3Settings and app updates#

Settings → Agent integration can install/reinstall a managed skill, inspect its state, or export the standalone SKILL.md. These integrations are optional and are not required for using the app or CLI.

Application updates shows current availability and a Check for Launch Station updates control. Automatically prepare updates can download/prepare an update but does not restart the app or interrupt a launcher by itself. Apply a prepared update through the app's restart flow once safe.

Closing the app window is not the same as closing managed sessions. Use the session's Close control to stop its owned work. The local service is responsible for session lifecycle and recovery.

Chapter 13

Skills, sync & maintenance

Optional integrations and catalog maintenance have distinct scopes and confirmation rules.

13.1Optional coding-agent integration#

Syntax
launch skill status [--json]
launch skill source [--json]
launch skill install codex|claude-code [--json]
launch skill uninstall codex|claude-code

Bare launch skill defaults to status. status reports product detection and managed file/version state. source prints the canonical standalone SKILL.md; with JSON it returns the source object. install installs or refreshes only the chosen supported product's managed files.

ProductShared location / behavior
codex~/.agents/skills/launchstation; shared by Desktop/CLI and the IDE's skill reader.
claude-code~/.claude/skills/launchstation; shared by local Desktop/CLI.
uninstallInteractive TTY and exact confirmation required; no --json or --yes. Removes only receipt-proven managed files, preserving unrelated files.

Installation refuses unsafe destinations and unavailable supported hosts. Reinstalling preserves sibling skills. Product detection is not the same as file verification: a product may be available while its skill is missing, outdated, or blocked. None of these integrations is a prerequisite for this manual.

13.2Check or repair the generated summary#

Syntax
launch sync [DIRECTORY] [--check | --repair] [--json]

DIRECTORY defaults to the current directory. Check is the default and compares generated content and read-only permissions. Drift returns exit code 4. Repair regenerates the daemon-owned launch_details.md; it does not import handwritten changes into the catalog. If both switches are supplied, the last one wins—prefer one.

The mirror is generated from SQLite after catalog edits. Never hand-edit or chmod it as a configuration workflow. If the project is offline or unwritable, the catalog can report pending/drifted/failed synchronization until the directory becomes available.

Check then repair only if appropriate
launch sync ~/Projects/shop --check
launch sync ~/Projects/shop --repair

13.3Installer-only upgrade reservation#

Advanced installer interface
launch maintenance prepare-upgrade [--json]
launch maintenance cancel-upgrade RESERVATION_TOKEN [--json]

These commands exist for the signed app upgrader, not ordinary project startup. Preparation requires a completely idle daemon and places a temporary mutation reservation. It rejects mutations until cancelled, expired after 120 seconds, or the daemon restarts.

JSON preparation returns a short-lived cancellation capability. Keep it private in installer memory and cancel the exact reservation if installation fails. The non-JSON preparation message does not expose the token. A reservation does not itself install an update or authorize changing app files.

Chapter 14

Local HTTP API

The CLI is the easiest client. This section describes the authenticated JSON interface for building your own local integration.

14.1Connect, authenticate and handle errors#

Discover only the non-secret endpoint
launch api endpoint [--json]

The daemon listens on a dynamic loopback port. launch api endpoint prints the current base URL; with JSON it returns an endpoint field. Do not hard-code the port. Every route, including health, requires the current bearer token held in private service metadata.

A purpose-built client may load ~/Library/Application Support/Launch Station/service.json into private process memory, use its endpoint/token for the request, and avoid printing or logging credentials. Reload metadata after restart/401. Never expose this API to the network: it can run commands as your macOS user.

Requests/responses use JSON; logs return a SessionLogResponse with text and structured diagnoses. Responses use Cache-Control: no-store, timestamps use ISO 8601, and IDs are UUID strings. HTTP/1.1 accepts GET/POST/PATCH/DELETE, with a 16 KiB header and 1 MiB body limit. Mutations carrying expectedRevision must also send the same decimal If-Match header.

HTTP statusInterpretation
401Missing/invalid bearer token; reload current private metadata.
404Object/route not found; inspect names/IDs.
409Duplicate/active-session/reservation conflict.
412Stale revision or mismatched If-Match.
422Invalid definition or unavailable installation host.
5xxService failure; preserve evidence and inspect before retrying.

14.2Catalog routes and bodies#

Method / routeBody or queryResult / purpose
GET /v1/healthNoneService version/schema/PID/endpoint.
GET /v1/snapshotNoneProjects, launcher details and session view.
GET /v1/projectsNoneRegistered projects list.
POST /v1/projects/initdirectory: string, displayName?: stringInitialize project.
GET /v1/projects/resolvedirectory queryResolve exact/nearest project.
PATCH /v1/projects/{id}expectedRevision: integer, displayName: stringRename project label.
POST /v1/projects/{id}/syncrepair: booleanCheck or repair mirror.
GET /v1/launchersq?: search stringLauncher details list.
POST /v1/launchersprojectID, name, description, runDetails?, tags, primaryActionCreate launcher with one complete action.
GET /v1/launchers/by-name/{name}Percent-encoded nameRetrieve by normalized name.
GET /v1/launchers/{id}NoneRetrieve one launcher.
PATCH /v1/launchers/{id}LauncherPatchRequestRevision-bound metadata/action/endpoint update.
POST /v1/launchers/{id}/actionsexpectedRevision, actionAdd action.
PATCH /v1/launchers/{id}/actions/{actionID}expectedRevision, actionReplace action.
DELETE /v1/launchers/{id}/actions/{actionID}expectedRevisionRemove action subject to constraints.
POST /v1/launchers/{id}/delete-intentNoneRetrieve expiring confirmation intent and current definition.
DELETE /v1/launchers/{id}expectedRevision, intentTokenConfirmed shortcut deletion.

LauncherPatchRequest contains required expectedRevision plus optional name, description, runDetails, replaceTags, replaceEndpoints, primaryAction and primaryActionID; boolean clearRunDetails and arrays addTags/removeTags express explicit mutation behavior. Omitted optional replacement fields preserve their current values. Endpoints are objects with id, name and path. Use complete model shapes from the public source when implementing a raw client; constructor defaults are not automatically JSON decoding defaults.

14.3Action and session request shapes#

Complete action shape (illustrative IDs; replace before sending)
{
  "id": "ACTION-UUID",
  "name": "web",
  "normalizedName": "web",
  "description": "Local web server",
  "order": 0,
  "runner": "process",
  "workingDirectory": ".",
  "executable": "python3",
  "arguments": [
    "-m",
    "http.server",
    "${PORT}",
    "--bind",
    "${HOST}"
  ],
  "environment": {},
  "inheritedEnvironment": [],
  "port": {
    "mode": "automatic",
    "logicalName": "web",
    "environmentVariable": "PORT",
    "hostEnvironmentVariable": "HOST",
    "URLTemplate": "http://${HOST}:${PORT}/",
    "lease": "8h"
  },
  "healthCheckURL": "http://${HOST}:${PORT}/",
  "openTarget": "none",
  "readyTimeoutSeconds": 30,
  "stopTimeoutSeconds": 8,
  "required": true,
  "allowsRuntimeArguments": true
}

Optional action fields include shellCommand, executable, healthCheckURL and appBundleIdentifier; optional port fields include fixedPort and URLTemplate. Supply the required fields shown above when using direct Codable clients. For shell, replace executable with shellCommand; for url, use a target, empty arguments and allowsRuntimeArguments false. The same validation rules as the CLI apply.

Start request
{
  "runtimeArguments": [],
  "openRequested": true,
  "expectedLauncherRevision": 4,
  "mode": "reuse-primary"
}
Relaunch request
{
  "runtimeArguments": [
    "--mode",
    "demo"
  ],
  "openRequested": true,
  "expectedSessionID": "CONFIRMED-SESSION-UUID",
  "requireIdle": false,
  "expectedLauncherRevision": 4
}

Start mode is reuse-primary or new-instance. Relaunch uses expectedSessionID to bind the confirmed active instance, or requireIdle:true to bind an idle observation; they are mutually exclusive. expectedLauncherRevision binds the displayed definition and is rechecked after close. Relaunch returns previousSession (when one existed) and the new session.

Session JSON contains id, launcherID/name/revision, launchRole, projectSnapshot, primaryActionID, actionSnapshots, endpointSnapshots, runtimeArguments, state, actionRuns, startedAt and optional endedAt/lastError/exitCode. Action runs expose their own ID/action ID/name, state, manager, exact manager/process identity, log path, endpoint/host/port when available, and timestamps/errors. Treat manager and open-option identities as opaque. Read actual launch details --json or history output rather than constructing a runtime record yourself.

14.4Session, external and integration routes#

Method / routeBody or queryPurpose
POST /v1/launchers/{id}/sessionsSessionStartRequestStart/reuse primary or create additional.
POST /v1/launchers/{id}/relaunchSessionRelaunchRequestAtomic primary/idle relaunch.
GET /v1/sessionsactive=true optionalRecorded sessions, optionally active only.
GET /v1/sessions/{id}NoneOne session record.
POST /v1/sessions/{id}/stop{"ok":true}Stop exact session.
POST /v1/sessions/{id}/relaunchSessionRelaunchRequestReplace exact active session.
GET /v1/sessions/{id}/logsNoneBounded combined log text.
GET /v1/history/sessionslauncherID?, state?, role?, limit?, cursor?Page with sessions and optional nextCursor.
GET /v1/sessions/{id}/open-optionsNoneExact allowed targets.
POST /v1/sessions/{id}/openoptionID: stringOpen returned choice.
POST /v1/sessions/{id}/open-probeoptionID: stringProbe a supported Expo choice.
GET /v1/external-processesNoneCached observation snapshot.
GET /v1/external-processes/refreshNoneRefresh observations.
GET /v1/external-processes/{id}/draftNoneReview-only proposal.
POST /v1/external-processes/{id}/close-intentNoneFresh confirmation capability.
POST /v1/external-processes/{id}/closeobservationID, intentToken, confirmationTextConfirmation-bound close.
GET /v1/skills/statusNoneManaged product installations.
GET /v1/skills/sourceNoneCanonical skill source object.
POST /v1/skills/installhost: codex or claude-codeInstall supported host integration.
POST /v1/skills/uninstall-intenthostReceipt-backed confirmation inspection.
POST /v1/skills/uninstalltoken, host, binding, confirmationTextUse exact returned binding/confirmation; do not fabricate.
POST /v1/maintenance/upgrade/prepareNoneIdle upgrade reservation.
POST /v1/maintenance/upgrade/cancelreservationTokenCancel exact reservation.

SessionOpenRequest uses the returned optionID. External and uninstall intent responses include expiry and exact confirmation material; implement the review/confirmation step rather than treating the API as a bypass. A raw request that times out may have succeeded—read the resulting state before repeating it.

Chapter 15

Troubleshooting & limits

Use the recorded state and exact error before changing a command or stopping a process.

15.1CLI exit codes and common failures#

ExitMeaningNext step
0Command succeededInspect session state as well; a returned record can describe a failed action.
1Unexpected local errorRead the error and logs.
2Usage errorCheck command-specific syntax and accepted flag values.
3Not foundRecheck name, directory, observation or session ID.
4Conflict / stale revision / mirror driftFetch current state; reconcile or repair the mirror intentionally.
5Validation failedCorrect invalid fields, paths, dependencies or port configuration.
6Service/API/transport failureRun doctor; after an ambiguous mutation inspect before retrying.
7Confirmation required or cancelledUse the interactive Terminal flow or documented revision-bound deletion form.

No --version flag is provided by this CLI; use launch doctor to inspect the service version and the app's About/Settings UI for the app version. No generic --force, --dry-run, automatic process restart policy, remote deployment, or session adoption command is provided.

15.2Resolve common problems#

SymptomWhat to check
launch: command not foundVerify the installation and PATH. Open a new shell after installing; do not fabricate a service metadata file.
Service unavailableOpen Launch Station and run doctor. Confirm the app/CLI match and the user service can recover; do not delete its database or token files.
Project not initializedRun init for the existing project root. If a mirror conflict is reported, inspect the pre-existing file.
Command works in Terminal onlyUse the action's actual cwd, an absolute executable/virtualenv path, and explicit non-secret environment. Interactive shell aliases and activation are not inherited.
Ready timeoutRead logs; ensure host/port placeholders are consumed and /health really exists. Increase timeout only for genuinely slow startup.
Fixed port conflictClose the actual owning session or select a fresh managed port. Never kill whatever uses the number.
Wrong browser pageCheck primary action, URL/health template, named endpoint path, and the exact session's open-options.
Simulator not foundInstall an iOS runtime in Xcode and select an available exact device name/UDID.
Stale revisionRetrieve details again and review the concurrent change before updating.
Orphaned sessionOwnership is unproven; inspect exact evidence. Do not use a name/port kill or relaunch an overlapping replacement.
Draft cannot be savedReplace unavailable/redacted command text and ensure the proposed listener consumes its managed port.

15.3Storage, privacy and boundaries#

LocationContents
~/Library/Application Support/Launch Station/launcher.sqlite3Local catalog and durable managed history; private permissions, with SQLite WAL support.
~/Library/Application Support/Launch Station/service.jsonPrivate dynamic endpoint/token/PID metadata. Never share it.
Project root / launch_details.mdGenerated read-only catalog summary.
Session action's logPathExact output location reported by details/history JSON. Paths differ by lifecycle manager; use the recorded value.
~/.agents/skills/launchstation / ~/.claude/skills/launchstationOptional managed product integration files.

Only run trusted commands under your user account. A local bearer token grants command-execution capability; protect it as a credential. Do not save secrets in launcher fields or expose the loopback service. Deleting a shortcut does not erase project files, outside processes, or managed history.

Launch Station coordinates local development; it does not install your project dependencies, create its source code, make missing server routes, replace your secret store, or deploy production. Its ownership guarantees depend on exact recorded identities, not guesses about command names.