Skip to main content
The CLI is published to npm as phonebase. Install it globally, then log in:
phonebase login prints a link and a short code and opens your browser. Sign in to the console, check that the page shows the same code, and approve. The terminal then prints the organization you logged in to, the id of the key it received and when that key expires. No browser on this machine, or connected over SSH? Open the link on any other device where you are signed in; the terminal waits until the code expires, ten minutes after it was printed.
Check phonebase --version. If it prints 0.1.3 or 0.2.0, upgrade with the command above. 0.1.3 writes a run’s worker URL, which carries the run token, to stderr whatever stderr is attached to, including a CI job log. 0.2.0 exits without printing anything. 0.2.1 has neither problem: phonebase run refuses up front when stderr is not a terminal and would have printed the URL. One gap remains on 0.2.1: with an API key, phonebase run --watch without --executor starts a self_hosted run and waits on it until its deadline, holding the device, because no worker connects. Pass --executor phonebase. From 0.3.0 --watch refuses or cancels that run and exits 2. See the changelog.
Installing the CLI gives you the client, not access. phonebase login needs a console account in an organization: request access through phonebase.co. Nothing on this surface requires the CLI, so if you are integrating rather than operating, start at Quickstart and treat this page as optional.

Logging in

What phonebase login gives you is an ordinary API key for your organization, the same kind the console’s /keys screen mints, named after this computer so you can find it there. It is stored in the system keychain: the macOS Keychain, or the Secret Service through secret-tool on Linux. Where neither is available it goes to ~/.config/phonebase/credentials.json, readable only by you. The CLI never prints it.
Need a new key, say after it expired or on a new machine? Run phonebase login again. You can also delete any key from the console’s /keys screen; logout does both halves for the key this machine holds, and if the server cannot be reached it still forgets the key here and tells you it is still live, so you can delete it in the console. Already have a key? phonebase login --with-key < key.txt checks it with the server and stores it without the browser step.

Prerequisites

  • Node 22 or newer. The published package declares engines: { "node": ">=22" }, so an npm install -g on Node 20 or 21 is refused outright under engine-strict. The CLI refuses to pretend an older runtime is fine either: doctor reports it as a failure with the fix.
That is the whole list for an npm install. The two sections below are for people working in the repository, which is private: they apply to you only if you already have a checkout.

Running from source

pnpm cli runs the CLI straight from TypeScript with no build step. This is the right mode while you are working on the repository, because an edit takes effect on the next command.
Everything after cli is passed through, so flags work as documented:

Running the built entry point

Building compiles the repository into dist.
dist/cli/main.js is the same entry point the published package ships: inside the package it sits at cli/main.js, which is what the manifest declares as the phonebase binary. So this is the same program the npm install gives you: same parser, same client, same control plane.

Configuring it

Two things. Which control plane. Defaults to the hosted one, https://mcp.phonebase.co. A control plane running on your own machine, for example a robot hand rig, is --server http://127.0.0.1:8788; to make that the default for a shell, set PHONEBASE_SERVER=http://127.0.0.1:8788. A --server flag always beats the variable. Each server keeps its own login, so logging in to a local one does not replace your hosted key. Which credential. In order: PHONEBASE_API_KEY from the environment, then the key phonebase login stored for that server, then PHONEBASE_DEV_TOKEN (a local control plane’s dev token). The dev token is only ever sent to a loopback server: aimed at any other, the command refuses rather than send it. Use the variable in CI and scripts, where nobody is there to approve a login:
phonebase whoami names which of the three it is using.
From a checkout, corepack pnpm cli substitutes for phonebase in every command below. It is the same program either way.
Sending either credential in the clear to a non loopback server is refused. Use https, or unset both for an unauthenticated call.

Checking the install

doctor runs the checks that catch the setup problems people actually hit, and exits non zero if any of them fail.
It reports on your Node version, whether your device rows parse and where they were read from, whether the control plane answers on /healthz, whether the credential in use is accepted and where it came from, and whether the Android tooling is present. Missing Android tooling is informational rather than a failure, because the mechanical actuator path does not need it. Each failing check prints its own fix. Start there before anything else.

Registering a device

The CLI is also how a control plane running on your own machine learns which devices exist. The hosted fleet does not need this. Rows live in a JSON file the CLI manages for you:
backendKind must be one of the kinds in Capabilities, and what belongs in metadata depends on the kind: a device addressed by configuration needs the address the control plane should reach it at. If PHONEBASE_DEVICE_ROWS is set in your environment it takes precedence over the file, and the CLI warns you that it did, so a forgotten variable does not silently shadow the rows you just edited.
A control plane started over stdio reads device rows once at startup. If you add a row while an MCP client has one running, restart the client. The HTTP control plane rereads rows on every request.