Skip to main content
ClaudeWave
Skill1.7k repo starsupdated 3d ago

fp-cloud-cli

|-

Install in Claude Code
Copy
git clone --depth 1 https://github.com/FailproofAI/failproofai /tmp/fp-cloud-cli && cp -r /tmp/fp-cloud-cli/fp-cloud-cli/skill ~/.claude/skills/fp-cloud-cli
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# FailproofAI Cloud CLI

`fp` is a command-line client for a FailproofAI Cloud deployment. It authenticates
either as a signed-in **user** or with a scoped **API key** (§2), and every command
takes `--json`, so it's built to be driven by an agent.

## 1. Find how to invoke it

Resolve this once, then reuse it for every call:

1. If `fp` is on `PATH` (`command -v fp`) → use **`fp`** (it's
   installed via pipx / uv tool / pip). This is the normal case.
2. Else, if you're in (or under) a repo with an `fp-cloud-cli/` directory containing the
   `fp_cli` package → run it from there with **`uv run fp`** (a local dev
   build). The first run after a code change prints `Building…`/`Installed…` on
   stderr — that's `uv`, not CLI output; ignore it.
3. Else the CLI isn't available here → tell the user to install it
   (`pipx install fp-cloud-cli` or `uv tool install fp-cloud-cli`) and stop. Don't try to
   reach the dashboard another way.

Don't go spelunking in the CLI source tree for flags — if you're unsure of one,
run `fp <group> <cmd> --help`. The source is not the documented contract
and reading it wastes effort.

Throughout this skill, `fp` means "whichever form you resolved."

## 2. The contract (the CLI enforces it, work with it, don't fight it)

- **Global options go BEFORE the command:** `fp --json events`, never
  `fp events --json`. Globals are `--base-url`, `--org`, `--token`,
  `--api-key`, `--json`, `--insecure`/`--secure`. After the command they're a
  usage error.
- **Two ways to authenticate, and they are not interchangeable:**

  | | How you supply it | What it is |
  |---|---|---|
  | **Session** | `fp login` (interactive; it emails a one-time code) | a signed-in **user**, carrying that person's org memberships and permissions |
  | **API key** | `--api-key <key>`, or `FP_API_KEY` in the environment | a scoped **credential**, carrying exactly the permissions it was granted |

  A key is what you want in CI or any other non-interactive context: no browser, no
  emailed code, nothing to expire mid-run.

  **Credential precedence, in full** (`resolve_auth`, `fp_cli/_context.py`). Read it
  as a ladder — the first rung that applies wins, and an explicit flag outranks
  *every* environment variable, not just its own:

  0. `--api-key` **and** `--token` together → usage error, exit 2. A silent guess
     about which you meant is the one outcome worth refusing.
  1. `--api-key <key>` → key mode
  2. `--token <tok>` → session mode. **This beats an ambient `FP_API_KEY`** — the
     flag is checked before the environment value, so "`FP_API_KEY` wins" is only
     true between the two env vars.
  3. `FP_API_KEY` → key mode
  4. `FP_TOKEN` → session mode
  5. the saved session from `fp login` → session mode

  The rung that catches people is 2: exporting `FP_API_KEY` in CI and *also*
  passing `--token` runs as that user's saved session, with their org memberships,
  rather than under the scoped key you meant to audit.

  **A key is never written to the CLI's saved config** — pass it every time, from
  the environment. `--api-key ""` means "no override" and does **not** fall back to
  a saved session (Click treats an empty env var as unset, so it falls to rung 4).

- **Some commands need a signed-in user.** `login`, `logout`, `orgs *`, the whole
  `agent` group, `keys update`, and all of `policies *` / `fleet *` /
  `guardrails *` refuse a key with a usage error (**exit 2**) and make **no
  network call at all** — there is no user to sign in, no saved active org to
  switch, no private assistant thread to own, and the enforcement write routes
  are root-only and deliberately absent from `/v1`. `keys update` is in that list
  rather than a special case: `keys:update` can never be granted to a key, so it
  is refused up front like the rest. The key is not the problem to fix — plan
  around them rather than retrying or hunting for a flag.
- **Default to `--json` and parse it.** It prints clean JSON to stdout and
  nothing else. The plain output is a boxed Rich UI meant for human eyes — it
  burns context with box-drawing characters and is awkward to parse. Use the
  rendered output only when the user explicitly wants to *look* at something.
- **Data → stdout, status/errors/prompts → stderr.** So a `--json` stdout
  capture is pure JSON even when a status line is shown.
- **Branch on exit codes — don't scrape error text:**

  | code | meaning | what to do |
  |---|---|---|
  | 0 | ok | parse stdout |
  | 1 | unexpected / server error | report it to the user |
  | 2 | usage error (bad flags/args) | fix the command and retry |
  | 3 | can't reach the dashboard | check base-url / connectivity |
  | 4 | no usable credential — not signed in, session expired, **or the API key was rejected** | session: user must run `fp login`. Key: it's missing, mistyped, disabled, or belongs to another deployment — don't retry, and don't fall back to a session |
  | 5 | authenticated but missing permission | message names the exact permission |
  | 6 | resource not found | the named resource doesn't exist |

## 3. First call: confirm you're connected

Before real work, run `fp --json whoami` and react to the exit code:

- **exit 4** → no usable credential. If the user is working from a session, tell
  them to run `fp login` (it emails a one-time code and prompts
  interactively — you can't complete it for them, and don't fabricate a token).
  If a key was supplied, the key itself was rejected — say so and stop; logging in
  is not the fix, and silently switching to a session would run the command as a
  different identity than the user asked for.
- **base-url** → the CLI defaults to the hosted product,
  `https://app.befailproof.ai`, so a plain `fp login` works out of the box.
  Only pass `--base-url <url>` (or set `FP_DASHBOARD_URL`) for a self-hosted
  or dev deployment — a local dev stack is usually `http://localhost:3000`. A
  scheme-less URL is rejected as a usage error (exit 2).
- **exit 0** → `whoami` ret