A CLI-only walkthrough (E2E_TODO_REPORT.md) found two control-plane
operations with no `pic` command — every CLI-created app 404'd until it
claimed a domain, and external SSE feeds needed a raw topic-registration
call — plus several friction points. All admin APIs already existed; this
adds thin wrappers mirroring `routes`/`triggers`:
- B1: `pic apps domains {ls,add,rm}` over apps/{id}/domains.
- B2: `pic topics {ls,create,update,rm}` over apps/{id}/topics, and a
triggers help note distinguishing a pubsub trigger from topic
registration.
- F2: `pic users {ls,show,reset-password,revoke-sessions}` over the app
end-user admin surface (read + the two admin actions; create/invitations
deferred).
- F1: `apps create` and `deploy` now honor `--output json`, emitting the
created object so scripts can capture the id.
- F3: non-interactive login via `--username` + `--password-stdin` (inline
passwords still rejected, mirroring the `--token` rule).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pic admins create/set --password and pic login --token accepted secrets
on argv, where they leak into shell history, ps aux, and
/proc/<pid>/cmdline. Now only --password -/--token - (stdin) is allowed;
an inline value is rejected with guidance. PICLOUD_TOKEN env still covers
CI for login.
Also (M2): the interactive admins password prompt used read_line, which
echoes despite a 'no echo' claim — switched to rpassword::prompt_password
(already a dep via login). Removed the now-dead ReadLine trait + Write
import.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- F-T-003 actually closed: clients/typescript/src/subscribe.ts now
bounds the 401-refresh loop at 3 consecutive refusals, surfaces an
onError describing the loop, and resets on any successful stream
open. New test covers the cap. The Stage 2 dashboard fix only
addressed adminRequest in dashboard/src/lib/api.ts; the originally-
cited TS client file was untouched.
- users/invitations subtab dropped the redundant api.apps.get fetch
the other 5 subtabs already shed in Stage 6.
- Queue visibility-timeout validator pulled out as
validate_queue_visibility_timeout, with two thresholds: hard-reject
below MIN_QUEUE_VISIBILITY_TIMEOUT_SECS (30s — catches typos), warn-
log between MIN and SAFE_VISIBILITY_VS_EXEC_BUDGET_SECS (300s) so
operators see when their visibility is below the dispatcher's
per-message executor budget. Stage 6 only had the hard floor; the
reviewer caught that a 60s handler still races a 30s visibility
even after the floor. Four new unit tests cover none/above-safe/
between/below-min.
- pic dead-letters count subcommand: cheap headless probe for
unresolved DL totals, parallels the dashboard's badge query.
- pic dead-letters replay now has a happy-path integration test
(replay_against_real_dl_row_succeeds): inserts a synthetic DL row
directly via the rust-postgres sync driver (added as dev-dep),
drives `pic dead-letters replay`, asserts the row is resolved with
reason=replayed and count drops back to 0. Plus a count smoke test.
All 75 CLI integration tests + 16 TS client tests + 4 new
visibility-timeout unit tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the audit's High-severity CLI coverage gap, raising the count
from ~25 to ~40+ subcommands and bringing the integration test count
from 63 to 73.
- pic triggers {ls, rm, create-kv, create-cron, create-dead-letter,
create-from-json}: three per-kind wrappers cover the most common
trigger shapes; the generic create-from-json is the escape hatch
for docs/files/pubsub/email/queue and any future advanced retry
knobs — body JSON inline, via @<file>, or "-" for stdin.
- pic dead-letters {ls, show, replay, resolve}: full operator
workflow for the dispatcher's dead_letters rows, including the
--unresolved filter and the per-row replay + manual-resolve actions.
- pic secrets {ls, set, rm}: list names + updated_at, set values
via stdin (the only secure channel — inline values would leak
into shell history), and delete by name. --json on set treats
stdin as raw JSON for non-string values.
Ten new integration tests follow the established #[ignore] pattern,
gated on DATABASE_URL. All 73 ignored tests pass against the local
dev stack.
The `pic admin reset-password` server-binary subcommand exists on
the picloud binary side already; the audit's "surface it in pic
--help" note is a one-line addition deferred to Stage 6 with the
other small UX touches.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the audit's Critical "operator can't deploy a serverless endpoint
end-to-end from the CLI" finding. Two new subcommand families bring the
CLI coverage from 14 commands to ~25:
- pic routes {ls, create, rm, check, match}: full route CRUD plus the
dry-run conflict checker and the URL matcher already exposed by the
dashboard. The create form accepts --path-kind, --host-kind, and
--dispatch (sync|async) so async routes can finally be created
headlessly. The ls output adds a dispatch column.
- pic admins {ls, create, show, set, rm}: per-instance admin user
management. Create reads passwords from stdin via --password - so
shell history never sees the cleartext. Set is a JSON-Merge-Patch
shape that lets operators deactivate accounts or rotate roles
without touching the dashboard.
Six new ignored integration tests follow the established #[ignore]
pattern (DATABASE_URL gates them). They cover the happy-path round
trip, the async-dispatch persistence path, the password-required
error path, and the capability gate (a Member sees HTTP 403). All
pass against the local dev stack with PICLOUD_DEV_MODE=true +
PICLOUD_DEV_INSECURE_KEY=i-understand-this-is-insecure.
The `pic members` and `pic domains` subcommands the audit mentioned
are deferred — the apps_api shape may shift in v1.2 with per-app
roles and rebuilding the CLI surface twice would be churn.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address the review findings on the CLI surface:
* `pic login` now prompts for username + password and POSTs to
`/api/v1/admin/auth/login`. `--token` (and `PICLOUD_TOKEN`) still
works for paste-a-bearer flows (CI, long-lived API keys). Falls
back to a plain stdin read when no controlling tty is attached.
* `pic logout` revokes the session server-side and deletes the local
credentials file. Idempotent.
* `PICLOUD_URL` / `PICLOUD_TOKEN` now override the on-disk credentials
file for every command via `config::resolve`, not just for
`pic login`. Matches gcloud/aws/kubectl semantics.
* New commands: `pic apps delete [--force]`, `pic apps show`,
`pic scripts delete`, `pic api-keys mint|ls|rm`, plus top-level
`pic invoke` / `pic deploy` shortcuts.
* `pic scripts ls` (no `--app`) now issues a single
`GET /admin/scripts` + one `apps_list` in parallel and joins
client-side, instead of walking N+1 per-app calls that aborted on
the first 404 — the bug the test suite was retrying around.
* Global `--output tsv|json` flag wired through every list/show and
through `whoami` / `logs`. TSV stays pipe-friendly; JSON is a real
array of objects (or a flat object for single-row views).
* `whoami` and `logs` now emit labeled output instead of headerless
tab lines, consistent with the existing `apps ls` / `scripts ls`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a new workspace crate `picloud-cli` shipping a `pic` binary that
drives the edit-deploy-invoke-tail-logs loop against PiCloud's admin
and execute HTTP surface. Eight subcommands cover the minimum a
developer needs to never open the dashboard:
pic login (paste URL + bearer token, validates via /auth/me)
pic whoami (re-validates and prints principal)
pic apps ls | create
pic scripts ls | deploy | invoke
pic logs <id>
Credentials persist as TOML under the platform config dir (resolved
via `directories`); on POSIX the file is forced to mode 0600.
PICLOUD_URL + PICLOUD_TOKEN env vars short-circuit interactive prompts
for CI and integration tests.
The CLI redeclares minimal request/response structs in `client.rs`
rather than depending on `manager-core` — keeps the blast radius
contained without touching the existing crate boundaries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>