devstack CLI (npm package devstack-bid)
On this page
Source: packages/cli (TypeScript → ESM, Node ≥ 20, no runtime deps, bin devstack, MIT, SemVer 0.1.0).
Server endpoints added#
| Endpoint | Auth | Notes |
|---|---|---|
POST /api/cli/device |
none | Starts device sign-in. 10-min expiry, 5/min per IP. Device code stored as SHA-256 hash only (cli_device_codes, migration 0018). |
POST /api/cli/device/token |
device code | Poll. 428 pending, 429 slow_down, 410 expired, 403 denied. On approval mints one api_clients key (hashed, scopes cli:setup+cli:deploy, 60 req/min), row deleted (single use). Max 20 active keys. |
GET /api/cli/device?code= |
cookie | Request details for the approve page: client name (unverified), time, coarse IP + country, account. |
POST /api/cli/device/approve |
cookie + same-origin | {userCode, approve, deploy}. Page /cli/device (code is typed, never in the URL). cli:deploy is opt-in. Atomic UPDATE … RETURNING; key cap checked first. |
GET /api/cli/activity |
cookie | Recent cli.* audit events (Settings → CLI). |
POST /api/cli/logout |
Bearer | Revokes the calling key. |
GET /api/cli/templates |
none | Starters + repo templates. |
GET /api/cli/whoami |
Bearer | Now accepts cli:deploy or cli:setup; returns scopes. |
Existing routes that now also accept a Bearer key with cli:deploy (opt-in gate(..., { bearer: true })):
GET/POST /api/projects, POST /api/projects/import, POST /api/projects/:id/builds, GET /api/projects/:id/deploy,
GET /api/deployments/:id, GET /api/deployments/:id/logs. The direct-deploy POST (takes Cloudflare tokens), connections,
upload and all other routes stay cookie-only. Settings → CLI keys are now created with both CLI scopes.
Publish plan (not active — needs owner approval and NPM_TOKEN)#
Add .github/workflows/cli-publish.yml after GO:
name: Publish CLI
on:
push:
tags: ["cli-v*"]
permissions:
contents: read
id-token: write # npm provenance
jobs:
publish:
runs-on: ubuntu-latest
environment: npm # require manual approval in repo settings
defaults: { run: { working-directory: packages/cli } }
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 22, registry-url: "https://registry.npmjs.org" }
- run: npm install --no-save typescript@5 @types/node@22
- run: npm test
- run: npm publish --provenance --access public
env: { NODE_AUTH_TOKEN: "${{ secrets.NPM_TOKEN }}" }Owner must: use npm account lsuperdev (2FA on), create a granular automation token limited to devstack-bid
(publish only), add it as repo secret NPM_TOKEN in an npm environment with required reviewers. Then tag cli-v0.1.0.
Hardening (Supdev review of 6bdbc03)#
- Keys:
expires_at(90 days, sliding on use) andlast_used_at, shown in Settings → CLI with sign-in activity. No email is sent (the app has no email sender). - CLI keys (only
cli:*) and MCP keys (nocli:*) are separate: MCP rejects CLI keys, CLI routes reject MCP keys, mixed keys can't be created. - At most 10,000 pending device codes; expired rows are deleted on create and by the 5-minute cron.
- User codes use rejection sampling; UNIQUE collisions are retried.
- The CLI binds the saved token to the server it was issued for.
Source: docs/cli-npm.md · /docs/cli-npm.md