run402 4.93.2 → 4.94.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -41
- package/cli.mjs +34 -29
- package/core-dist/config.js +52 -18
- package/core-dist/control-plane-session.js +31 -6
- package/core-dist/keystore.js +7 -7
- package/core-dist/profiles.js +9 -7
- package/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{sdk/core-dist/allowance.js → core-dist/wallet.js} +20 -20
- package/{sdk/core-dist/write-auth-session.js → core-dist/write-approvals.js} +19 -19
- package/gitvault-surface.json +1 -1
- package/lib/admin.mjs +1 -1
- package/lib/agent.mjs +20 -18
- package/lib/approve.mjs +81 -0
- package/lib/apps.mjs +3 -3
- package/lib/assets.mjs +2 -2
- package/lib/billing.mjs +6 -6
- package/lib/buzz-doctor.mjs +1 -1
- package/lib/buzz-notifications.mjs +1 -1
- package/lib/buzz.mjs +1 -1
- package/lib/buzz.test.mjs +1 -1
- package/lib/ci.mjs +4 -4
- package/lib/cold-start.mjs +21 -21
- package/lib/command-manifest.mjs +20 -34
- package/lib/config.mjs +24 -24
- package/lib/contacts.mjs +13 -13
- package/lib/contracts.mjs +1 -1
- package/lib/credentials.mjs +2 -2
- package/lib/daemon-path.mjs +1 -1
- package/lib/delegates.mjs +1 -1
- package/lib/deliveries.mjs +4 -4
- package/lib/deploy-rehearse-discovery.test.mjs +7 -7
- package/lib/deploy-v2.mjs +232 -184
- package/lib/deploy.mjs +21 -53
- package/lib/doctor-source-scan.mjs +1 -1
- package/lib/doctor.mjs +57 -57
- package/lib/escalations.mjs +1 -1
- package/lib/feedback.mjs +10 -10
- package/lib/gitvault-daemon.mjs +1 -1
- package/lib/image.mjs +3 -3
- package/lib/init.mjs +66 -66
- package/lib/lightning-wallet.mjs +28 -28
- package/lib/login.mjs +207 -0
- package/lib/logout.mjs +53 -0
- package/lib/next-actions.mjs +15 -15
- package/lib/org.mjs +100 -13
- package/lib/pay.mjs +1 -1
- package/lib/projects.mjs +26 -29
- package/lib/redeem.mjs +7 -8
- package/lib/remote-helper-session.mjs +12 -12
- package/lib/repos.mjs +62 -62
- package/lib/rooms.mjs +103 -67
- package/lib/sdk-errors.mjs +9 -4
- package/lib/sdk.mjs +3 -3
- package/lib/service.mjs +5 -5
- package/lib/session-proofs.mjs +21 -0
- package/lib/sign-in.mjs +296 -0
- package/lib/sites.mjs +8 -8
- package/lib/stats.mjs +2 -2
- package/lib/status.mjs +25 -25
- package/lib/subdomains.mjs +19 -19
- package/lib/subscriptions.mjs +8 -8
- package/lib/tier.mjs +29 -22
- package/lib/transfer.mjs +58 -85
- package/lib/up.mjs +7 -7
- package/lib/wallet-context.mjs +3 -3
- package/lib/wallet-context.test.mjs +3 -3
- package/lib/wallets.mjs +196 -31
- package/lib/webhook-secret.mjs +6 -6
- package/lib/whoami.mjs +75 -0
- package/package.json +1 -1
- package/sdk/core-dist/config.js +52 -18
- package/sdk/core-dist/control-plane-session.js +31 -6
- package/sdk/core-dist/keystore.js +7 -7
- package/sdk/core-dist/profiles.js +9 -7
- package/sdk/core-dist/{allowance-auth.js → wallet-auth.js} +15 -15
- package/{core-dist/allowance.js → sdk/core-dist/wallet.js} +20 -20
- package/{core-dist/write-auth-session.js → sdk/core-dist/write-approvals.js} +19 -19
- package/sdk/dist/actions.d.ts +1 -1
- package/sdk/dist/actions.d.ts.map +1 -1
- package/sdk/dist/control-plane-credentials.d.ts +8 -8
- package/sdk/dist/control-plane-credentials.js +7 -7
- package/sdk/dist/credentials.d.ts +19 -19
- package/sdk/dist/credentials.d.ts.map +1 -1
- package/sdk/dist/credentials.js +1 -1
- package/sdk/dist/errors.d.ts +24 -26
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +34 -37
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/index.d.ts +29 -16
- package/sdk/dist/index.d.ts.map +1 -1
- package/sdk/dist/index.js +29 -16
- package/sdk/dist/index.js.map +1 -1
- package/sdk/dist/kernel.d.ts +1 -1
- package/sdk/dist/kernel.d.ts.map +1 -1
- package/sdk/dist/kernel.js +10 -9
- package/sdk/dist/kernel.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +51 -120
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/admin.js +41 -45
- package/sdk/dist/namespaces/admin.js.map +1 -1
- package/sdk/dist/namespaces/agent.d.ts +2 -2
- package/sdk/dist/namespaces/agent.d.ts.map +1 -1
- package/sdk/dist/namespaces/agent.js +2 -2
- package/sdk/dist/namespaces/agent.js.map +1 -1
- package/sdk/dist/namespaces/ai.d.ts +1 -1
- package/sdk/dist/namespaces/ai.js +1 -1
- package/sdk/dist/namespaces/billing.d.ts +6 -3
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/buzz-notifications.types.d.ts +1 -1
- package/sdk/dist/namespaces/buzz.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.d.ts.map +1 -1
- package/sdk/dist/namespaces/buzz.js +2 -2
- package/sdk/dist/namespaces/buzz.js.map +1 -1
- package/sdk/dist/namespaces/buzz.types.d.ts +2 -2
- package/sdk/dist/namespaces/buzz.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/credentials.types.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.d.ts +1 -1
- package/sdk/dist/namespaces/deploy.js +5 -5
- package/sdk/dist/namespaces/deploy.js.map +1 -1
- package/sdk/dist/namespaces/deploy.types.d.ts +4 -4
- package/sdk/dist/namespaces/escalations.d.ts +1 -1
- package/sdk/dist/namespaces/escalations.js +1 -1
- package/sdk/dist/namespaces/escalations.types.d.ts +2 -2
- package/sdk/dist/namespaces/escalations.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/events.types.d.ts +1 -1
- package/sdk/dist/namespaces/functions.d.ts +1 -1
- package/sdk/dist/namespaces/functions.js +1 -1
- package/sdk/dist/namespaces/gitvault.crypto.d.ts +1 -1
- package/sdk/dist/namespaces/gitvault.d.ts +24 -24
- package/sdk/dist/namespaces/gitvault.d.ts.map +1 -1
- package/sdk/dist/namespaces/gitvault.js +109 -109
- package/sdk/dist/namespaces/gitvault.js.map +1 -1
- package/sdk/dist/namespaces/me.d.ts +120 -0
- package/sdk/dist/namespaces/me.d.ts.map +1 -0
- package/sdk/dist/namespaces/me.js +34 -0
- package/sdk/dist/namespaces/me.js.map +1 -0
- package/sdk/dist/namespaces/org.d.ts +108 -6
- package/sdk/dist/namespaces/org.d.ts.map +1 -1
- package/sdk/dist/namespaces/org.js +63 -7
- package/sdk/dist/namespaces/org.js.map +1 -1
- package/sdk/dist/namespaces/org.types.d.ts +29 -9
- package/sdk/dist/namespaces/org.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/pay.js +3 -3
- package/sdk/dist/namespaces/pay.js.map +1 -1
- package/sdk/dist/namespaces/projects.d.ts +9 -9
- package/sdk/dist/namespaces/projects.js +15 -15
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +28 -12
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/rooms.d.ts +7 -7
- package/sdk/dist/namespaces/rooms.js +17 -17
- package/sdk/dist/namespaces/rooms.js.map +1 -1
- package/sdk/dist/namespaces/rooms.types.d.ts +9 -9
- package/sdk/dist/namespaces/rooms.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/service.d.ts +1 -1
- package/sdk/dist/namespaces/service.js +1 -1
- package/sdk/dist/namespaces/{operator-session.d.ts → session.d.ts} +135 -51
- package/sdk/dist/namespaces/session.d.ts.map +1 -0
- package/sdk/dist/namespaces/{operator-session.js → session.js} +118 -30
- package/sdk/dist/namespaces/session.js.map +1 -0
- package/sdk/dist/namespaces/subdomains.d.ts +5 -5
- package/sdk/dist/namespaces/subdomains.d.ts.map +1 -1
- package/sdk/dist/namespaces/subdomains.js +6 -6
- package/sdk/dist/namespaces/subdomains.js.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +43 -20
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.js +7 -7
- package/sdk/dist/namespaces/tier.js.map +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +74 -71
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js +31 -38
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/namespaces/vouchers.d.ts +9 -10
- package/sdk/dist/namespaces/vouchers.d.ts.map +1 -1
- package/sdk/dist/namespaces/vouchers.js +4 -5
- package/sdk/dist/namespaces/vouchers.js.map +1 -1
- package/sdk/dist/namespaces/wallets.d.ts +72 -3
- package/sdk/dist/namespaces/wallets.d.ts.map +1 -1
- package/sdk/dist/namespaces/wallets.js +141 -3
- package/sdk/dist/namespaces/wallets.js.map +1 -1
- package/sdk/dist/namespaces/write-approval.d.ts +101 -0
- package/sdk/dist/namespaces/write-approval.d.ts.map +1 -0
- package/sdk/dist/namespaces/write-approval.js +62 -0
- package/sdk/dist/namespaces/write-approval.js.map +1 -0
- package/sdk/dist/node/actions-node.d.ts +3 -3
- package/sdk/dist/node/actions-node.d.ts.map +1 -1
- package/sdk/dist/node/actions-node.js +36 -36
- package/sdk/dist/node/actions-node.js.map +1 -1
- package/sdk/dist/node/{bearer-claim-key.d.ts → bearer-redeem-key.d.ts} +12 -12
- package/sdk/dist/node/bearer-redeem-key.d.ts.map +1 -0
- package/sdk/dist/node/{bearer-claim-key.js → bearer-redeem-key.js} +21 -21
- package/sdk/dist/node/bearer-redeem-key.js.map +1 -0
- package/sdk/dist/node/ci.d.ts +1 -1
- package/sdk/dist/node/ci.d.ts.map +1 -1
- package/sdk/dist/node/ci.js +6 -6
- package/sdk/dist/node/ci.js.map +1 -1
- package/sdk/dist/node/credentials.d.ts +17 -16
- package/sdk/dist/node/credentials.d.ts.map +1 -1
- package/sdk/dist/node/credentials.js +28 -27
- package/sdk/dist/node/credentials.js.map +1 -1
- package/sdk/dist/node/funding-recovery.d.ts +1 -1
- package/sdk/dist/node/funding-recovery.d.ts.map +1 -1
- package/sdk/dist/node/funding-recovery.js +1 -1
- package/sdk/dist/node/funding-recovery.js.map +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts +1 -1
- package/sdk/dist/node/gitvault-apply.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.d.ts +22 -22
- package/sdk/dist/node/gitvault-handoff.d.ts.map +1 -1
- package/sdk/dist/node/gitvault-handoff.js +45 -45
- package/sdk/dist/node/gitvault-handoff.js.map +1 -1
- package/sdk/dist/node/gitvault-prewarm.js +7 -7
- package/sdk/dist/node/gitvault-prewarm.js.map +1 -1
- package/sdk/dist/node/gitvault-restore.d.ts +1 -1
- package/sdk/dist/node/gitvault-restore.js +1 -1
- package/sdk/dist/node/index.d.ts +16 -16
- package/sdk/dist/node/index.d.ts.map +1 -1
- package/sdk/dist/node/index.js +15 -15
- package/sdk/dist/node/index.js.map +1 -1
- package/sdk/dist/node/org-adopt.d.ts +49 -0
- package/sdk/dist/node/org-adopt.d.ts.map +1 -0
- package/sdk/dist/node/org-adopt.js +77 -0
- package/sdk/dist/node/org-adopt.js.map +1 -0
- package/sdk/dist/node/paid-fetch.d.ts +7 -7
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +54 -54
- package/sdk/dist/node/paid-fetch.js.map +1 -1
- package/sdk/dist/node/preflight.d.ts +1 -1
- package/sdk/dist/node/preflight.d.ts.map +1 -1
- package/sdk/dist/node/preflight.js +1 -1
- package/sdk/dist/node/preflight.js.map +1 -1
- package/sdk/dist/node/source-scan.d.ts +1 -1
- package/sdk/dist/node/source-scan.js +2 -2
- package/sdk/dist/node/source-scan.js.map +1 -1
- package/sdk/dist/scoped.d.ts +3 -3
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +2 -2
- package/sdk/dist/scoped.js.map +1 -1
- package/core-dist/operator-session.js +0 -154
- package/lib/allowance.mjs +0 -355
- package/lib/operator-proofs.mjs +0 -22
- package/lib/operator.mjs +0 -771
- package/sdk/core-dist/operator-session.js +0 -154
- package/sdk/dist/namespaces/allowance.d.ts +0 -79
- package/sdk/dist/namespaces/allowance.d.ts.map +0 -1
- package/sdk/dist/namespaces/allowance.js +0 -149
- package/sdk/dist/namespaces/allowance.js.map +0 -1
- package/sdk/dist/namespaces/operator-session.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator-session.js.map +0 -1
- package/sdk/dist/namespaces/operator.d.ts +0 -359
- package/sdk/dist/namespaces/operator.d.ts.map +0 -1
- package/sdk/dist/namespaces/operator.js +0 -272
- package/sdk/dist/namespaces/operator.js.map +0 -1
- package/sdk/dist/node/bearer-claim-key.d.ts.map +0 -1
- package/sdk/dist/node/bearer-claim-key.js.map +0 -1
- package/sdk/dist/node/operator-claim.d.ts +0 -49
- package/sdk/dist/node/operator-claim.d.ts.map +0 -1
- package/sdk/dist/node/operator-claim.js +0 -77
- package/sdk/dist/node/operator-claim.js.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
Command-line interface for [Run402](https://run402.com) — provision Postgres databases, deploy static sites, run Node 22 serverless functions, host content-addressed CDN assets, send email, sign on-chain. Paid autonomously with x402 (USDC on Base) or MPP (pathUSD on Tempo, or sats over Bitcoin Lightning). **Prototype tier is free on testnet.**
|
|
6
6
|
|
|
7
|
-
The CLI lets an agent act as its own first-class Run402 principal rather than borrowing a human account. Identity records who invoked an action; organization roles, grants, delegates, freshness, and spend policy determine what that principal may do. People remain first-class owners and
|
|
7
|
+
The CLI lets an agent act as its own first-class Run402 principal rather than borrowing a human account. Identity records who invoked an action; organization roles, grants, delegates, freshness, and spend policy determine what that principal may do. People remain first-class owners and members without having to run the CLI themselves.
|
|
8
8
|
|
|
9
9
|
For the full CLI reference (every flag, every subcommand) start at **<https://docs.run402.com/llms-cli.txt>** — the index holds the first-deploy contract and links one fetchable `/llms-cli-<slice>.txt` per topic (deploy, commands, repos, orgs, functions, assets, ops, errors, frontend, platform); <https://docs.run402.com/llms-cli-full.txt> is the whole reference as one document.
|
|
10
10
|
|
|
@@ -25,8 +25,8 @@ npx -y run402@latest <command>
|
|
|
25
25
|
## 30-second start
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
|
-
run402 up --name my-app -y # bootstrap
|
|
29
|
-
run402 subdomains
|
|
28
|
+
run402 up --name my-app -y # bootstrap wallet/tier/project/link, then deploy
|
|
29
|
+
run402 subdomains add my-app # → https://my-app.run402.com
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
That's a real Postgres database + a deployed static site, paid for autonomously with testnet USDC.
|
|
@@ -51,13 +51,13 @@ run402 up verify --project prj_...
|
|
|
51
51
|
run402 up --nested -y # app root inside another repo: its own nested repo + run402 remote
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
`up` is a thin CLI shim over the Node SDK action runner. It discovers `run402.json`, `run402.deploy.json`, then `app.json`. A `run402.json` carrying the app schema/app markers uses the app-install graph; a release-shaped `run402.json` is normalized through the same ReleaseSpec path as `deploy
|
|
54
|
+
`up` is a thin CLI shim over the Node SDK action runner. It discovers `run402.json`, `run402.deploy.json`, then `app.json`. A `run402.json` carrying the app schema/app markers uses the app-install graph; a release-shaped `run402.json` is normalized through the same ReleaseSpec path as `run402 deploy` for compatibility. It validates the deploy input before any mutation, resolves the project as `--project` → `.run402/project.json` → manifest `project_id` → approved creation from `--name` → approved active-project fallback, then applies the manifest. `--name` is creation/link metadata only; it is not a manifest field and never renames an existing project. When everything is already configured, plain `run402 up` deploys. Non-interactive recursive prerequisites/local writes require `-y/--yes`.
|
|
55
55
|
|
|
56
56
|
For app manifests with `verify.http[]`, `up` runs HTTP checks after deploy. Fresh Run402 edge sentinel misses (`x-run402-edge` or sentinel JSON bodies) become `propagation_pending` with diagnostics instead of hard failure while the host binding is still converging. Use `--propagation-budget-s <seconds>` to tune the default 120 second wait, `--no-propagation-wait` to return immediately, and `run402 up verify` to rerun the same checks without upload, deploy, project creation, or resource mutation.
|
|
57
57
|
|
|
58
|
-
For typed `run402.deploy.ts` configs, pass `--manifest` explicitly because TypeScript/JavaScript configs execute local code. Use `--check` for local-only import/normalize/file validation, `--print-spec` to inspect the normalized `ReleaseSpec`, `--plan` for a gateway-reviewed non-deploying plan, and `--require-plan <plan_id>` to apply only that reviewed intent. Warning flags are not used with `--require-plan`; the reviewed plan binds the exact warning/destructive set. Run402 Core skips Cloud
|
|
58
|
+
For typed `run402.deploy.ts` configs, pass `--manifest` explicitly because TypeScript/JavaScript configs execute local code. Use `--check` for local-only import/normalize/file validation, `--print-spec` to inspect the normalized `ReleaseSpec`, `--plan` for a gateway-reviewed non-deploying plan, and `--require-plan <plan_id>` to apply only that reviewed intent. Warning flags are not used with `--require-plan`; the reviewed plan binds the exact warning/destructive set. Run402 Core skips Cloud wallet/tier prerequisites and fails closed when no Core project is selected.
|
|
59
59
|
|
|
60
|
-
Every mode (and `deploy
|
|
60
|
+
Every mode (and `run402 deploy`) verifies the files the manifest references before any gateway call: migration `sql_path`/`sql_file`, function `source`/`files`, site `{ path }` entries and `dir()` targets, `assets.put` sources. A missing one is `MANIFEST_FILE_MISSING` (`details.missing[]` = `{ field_path, path, kind }`, one `create_file` next action per file). With no manifest in the working directory, `UP_MANIFEST_REQUIRED` looks one directory down (`details.nearby_manifests[]`, a leading `run_in_directory` next action such as `run402 up --check --dir <dir>`); `--manifest <path>` to a missing file is a typed `MANIFEST_NOT_FOUND` with a `create_manifest` next action. An app root that lies inside another repository is never scaffolded into it: the skip carries a `create_nested_repo` next action, and `--nested` gives the app its own nested repository (`git init -b main`, the `run402` remote, the first push) plus exactly one line in the enclosing repository's local `.git/info/exclude`, nothing else touched. `up` also sets this principal's display name when it has none: `RUN402_AGENT_NAME` (overrides an existing name), else the detected client (`claude-code`, `codex`, `cursor`, `grok`, or `RUN402_CLIENT=<name>` for one with no marker), and `result.identity` reports `detected` plus `detection: { applied, reason }` either way.
|
|
61
61
|
|
|
62
62
|
### repos — host-blind encrypted git repos (zero deploy ceremony)
|
|
63
63
|
|
|
@@ -78,19 +78,19 @@ run402 repos delete --project prj_xyz --force # refuses without --force while th
|
|
|
78
78
|
|
|
79
79
|
A hosted git remote, encrypted before it leaves the machine — no deploy, no manifest, no app. `origin` is claimed additively — an existing `origin` is never touched, the run402 remote falls back to `run402` instead. A directory inside another repository is never scaffolded into that repository: the skip carries a `create_nested_repo` next action, `--nested` makes it its own nested repository (one line appended to the enclosing repository's local `.git/info/exclude`, nothing else touched), and `create` prints a `git push` next action only for a remote it actually added. Every mutating `repos` verb (`create`, `rename`, `delete`, `snapshot`, `policy`, `mirror`, `gc`, `handoff`, `resume`, `invite`, `join`) is CLI/OpenClaw-only by design — no MCP tool exists or will exist for them (one-shot recovery receipts, immutable generations, irreversible delete, single-use bearer keys). Three READ-ONLY tools do exist — `repos_view`, `repos_list_heads`, `repos_fsck` — teaching only `repos` spellings. See `run402 repos --help` for the full tiered surface (common: `create`/`view`/`list`; occasional: `snapshot`/`mirror`/`recover`/`handoff`/`resume`/`invite`/`join`; lifecycle: `rename`/`delete`; maintenance: `fsck`/`gc`/`access`/`policy`), and the CLI reference's `repos` section for the terminal-loss statement and the progressive backup warning.
|
|
80
80
|
|
|
81
|
-
**Handoff / resume.** `run402 repos handoff [--note-file <path>]` captures the actual working tree (staged/unstaged/untracked, like `git stash push -u`) into the vault and mints a single-use bearer key (`kgh1_…`, printed to stdout exactly once); `run402 repos resume <kgh1_…>` claims it on another machine, clones fresh, and reapplies the exact dirty state with `git stash apply --index`. No shared keystore, no shared
|
|
81
|
+
**Handoff / resume.** `run402 repos handoff [--note-file <path>]` captures the actual working tree (staged/unstaged/untracked, like `git stash push -u`) into the vault and mints a single-use bearer key (`kgh1_…`, printed to stdout exactly once); `run402 repos resume <kgh1_…>` claims it on another machine, clones fresh, and reapplies the exact dirty state with `git stash apply --index`. No shared keystore, no shared wallet — the key is the whole handoff. On a wallet with no active tier, `resume` first folds the same cold-start chain `create` does (wallet → faucet → one x402 prototype payment); `--no-init` opts out, and the claim itself never waits on it. Sensitive untracked files (`.env`, `*.pem`, SSH/AWS/GPG dirs, …) are excluded from capture by default (`--include-sensitive <glob>` re-admits one). See the reference `run402.com/llms-cli.txt` for the full note-schema and flag reference.
|
|
82
82
|
|
|
83
83
|
**Invite / join.** The second claim kind beside handoff/resume: a Handoff passes the work on and the sender stops; an Invite grows the team while the sender keeps working. `run402 repos invite [--room <key>] [--note-file <path>]` captures the checkpoint exactly like `handoff` — the inviter's own worktree, index, branch, refs, and access are all untouched — registers the inviter's presence in a coordination room (the project's default room, or `--room <key>` for a named org room), mints a single-use bearer key (`kgi1_…`, printed to stdout exactly once), and posts ONE room message naming the checkpoint (never the key). `run402 repos join <kgi1_…>` folds the SAME cold-start chain `resume` does before the claim, clones fresh, restores the exact dirty state, pins the invite's room locally, registers its own presence, posts ONE arrival message, and reports the inviter (name, labels, liveness), the room's other live presences, and the last few messages. `run402 messages wait` is the room's blocking read from there — the agent's ear, never an error on silence. Neither `invite` nor `join` has an MCP tool, same reasoning as `handoff`/`resume`.
|
|
84
84
|
|
|
85
|
-
**Named addressing.** `run402 org slug <slug>` (owner-only, small one-time fee)
|
|
85
|
+
**Named addressing.** `run402 org slug <slug>` (owner-only, small one-time fee) sets an org's globally-unique, address-form slug, after which `run402::<slug>/<name>` addresses any repo under it — `git push` to a name that doesn't exist yet push-to-creates it. `run402 repos rename <name> [--project <id>]` claims the per-org-unique `<name>` half explicitly (no fee); `repos create` claims one automatically, best-effort, when the org already has a slug. Also CLI/SDK-only — no MCP tool.
|
|
86
86
|
|
|
87
|
-
###
|
|
87
|
+
### Wallet
|
|
88
88
|
|
|
89
89
|
```bash
|
|
90
|
-
run402
|
|
91
|
-
run402
|
|
92
|
-
run402
|
|
93
|
-
run402
|
|
90
|
+
run402 wallets new default # generate the local wallet without funding it
|
|
91
|
+
run402 wallets fund # request testnet USDC from the faucet
|
|
92
|
+
run402 wallets balance # mainnet + testnet USDC, plus the organization's allowance
|
|
93
|
+
run402 wallets current # the active wallet's address (for funding), rail, and file path
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
### Promo codes
|
|
@@ -100,9 +100,9 @@ run402 redeem R402-K8F3-Q2W9 # any time, before or after `run402 init`
|
|
|
100
100
|
run402 init --voucher R402-K8F3-Q2W9 # same redemption, folded into setup
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
A promo code
|
|
103
|
+
A promo code adds to the organization's allowance, which settles tiers and
|
|
104
104
|
priced calls with no on-chain payment. Case-insensitive, hyphens optional.
|
|
105
|
-
Retrying is safe — a repeat returns the original result and never
|
|
105
|
+
Retrying is safe — a repeat returns the original result and never adds twice.
|
|
106
106
|
A bad code never blocks `init`; it warns and records `voucher_error` in the JSON
|
|
107
107
|
summary.
|
|
108
108
|
|
|
@@ -155,14 +155,14 @@ run402 projects schema <id> # introspect tables + R
|
|
|
155
155
|
|
|
156
156
|
```bash
|
|
157
157
|
run402 sites deploy-dir ./dist # incremental upload (plan/commit transport)
|
|
158
|
-
run402 deploy
|
|
159
|
-
run402
|
|
158
|
+
run402 deploy --manifest app.json # deploy only (`run402 up` does missing setup first, then this)
|
|
159
|
+
run402 deploy status op_... --project prj_...
|
|
160
160
|
run402 deploy verify op_... --project prj_... --wait
|
|
161
|
-
run402 deploy
|
|
162
|
-
run402 deploy
|
|
163
|
-
run402 deploy
|
|
164
|
-
run402 deploy resolve --
|
|
165
|
-
run402 subdomains
|
|
161
|
+
run402 deploy releases active # inspect current-live release inventory
|
|
162
|
+
run402 deploy releases diff --from empty --to active
|
|
163
|
+
run402 deploy resolve https://example.com/events --project prj_123 --method GET
|
|
164
|
+
run402 deploy resolve --url https://example.com/events?utm=x#hero --project prj_123 --method GET
|
|
165
|
+
run402 subdomains add my-app # → my-app.run402.com (auto-reassigns on next deploy)
|
|
166
166
|
```
|
|
167
167
|
|
|
168
168
|
For pointer-swap recovery, verify the returned operation before declaring the public rollback complete:
|
|
@@ -175,13 +175,13 @@ run402 deploy verify --operation OPERATION_ID --wait
|
|
|
175
175
|
Promote success means the origin pointer is active; mutable public URLs may still be converging. The result's `edge.state` reports convergence, and `edge.verify_url` is the direct operation-scoped HTTP verification endpoint.
|
|
176
176
|
|
|
177
177
|
`deploy-dir` hashes each file client-side and only uploads bytes the gateway doesn't already have. Re-deploying an unchanged tree returns immediately with `bytes_uploaded: 0`. Progress events stream to stderr.
|
|
178
|
-
Release inspection commands print `{ release: ... }` or `{ diff: ... }` (raw payload, no envelope — see the "Output Contract" section in [llms-cli.txt](llms-cli.txt)); use them after deploys to compare release inventory without starting another mutation. `deploy verify` prints the canonical edge-coherence report and exits 2 when the report is valid but not yet coherent. Inventories include `release_generation`, `static_manifest_sha256`, and nullable `static_manifest_metadata`; diffs include `static_assets` counters such as unchanged/changed/added/removed and CAS byte reuse. `deploy
|
|
178
|
+
Release inspection commands print `{ release: ... }` or `{ diff: ... }` (raw payload, no envelope — see the "Output Contract" section in [llms-cli.txt](llms-cli.txt)); use them after deploys to compare release inventory without starting another mutation. `deploy verify` prints the canonical edge-coherence report and exits 2 when the report is valid but not yet coherent. Inventories include `release_generation`, `static_manifest_sha256`, and nullable `static_manifest_metadata`; diffs include `static_assets` counters such as unchanged/changed/added/removed and CAS byte reuse. `deploy resolve <url>` (or `--url` / `--host` + `--path`) prints URL diagnostics with `would_serve`, `diagnostic_status`, `match`, warnings, `edge_propagation`, and next steps; host misses are successful diagnostic calls with `would_serve: false`. Stable-host resolve fields can include `authorization_result`, `cas_object`, `response_variant`, `allow`, `route_pattern`, `target_type`, `target_name`, `target_file`, and `edge_propagation` (`settled`, `propagating`, or `sync_pending`).
|
|
179
179
|
|
|
180
|
-
For database-bearing changes,
|
|
180
|
+
For database-bearing changes, rehearsal happens before commit without a flag: `run402 deploy --manifest app.json --json` (or `run402 up`) creates a contained branch, applies the candidate plan there, runs checks, and stops on a failed rehearsal; `--no-rehearse` skips it and `run402 deploy rehearse <plan_id>` rehearses a persisted plan by hand. The automatic rehearsal inside `up` / `deploy` reports `rehearsal: { status: "skipped", reason }` when there is nothing to protect: `no_live_release` on a first deploy, `no_migrations`, or `migrations_unchanged` when every migration is already applied with an identical checksum (a page-only redeploy that still carries its migrations ships in seconds). `PUBLIC_ROUTED_FUNCTION` is informational (`requires_confirmation: false`) and needs no `--allow-warning`; only warnings with `requires_confirmation: true` do. Manual restore points live under `run402 snapshots create|list|get|restore|delete`; temporary data branches live under `run402 branches create|list|renew|delete`.
|
|
181
181
|
|
|
182
182
|
### GitHub Actions OIDC deploys
|
|
183
183
|
|
|
184
|
-
Link once from a local shell that has your Run402
|
|
184
|
+
Link once from a local shell that has your Run402 wallet, then commit the generated workflow and manifest:
|
|
185
185
|
|
|
186
186
|
```bash
|
|
187
187
|
run402 ci link github --project prj_... --manifest run402.deploy.json
|
|
@@ -203,10 +203,10 @@ jobs:
|
|
|
203
203
|
steps:
|
|
204
204
|
- uses: actions/checkout@v4
|
|
205
205
|
- name: Deploy to run402
|
|
206
|
-
run: npx --yes run402@3.7.5 deploy
|
|
206
|
+
run: npx --yes run402@3.7.5 deploy --manifest 'run402.deploy.json' --project 'prj_...'
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
CI deploys can ship `site`, `functions`, `database`, and absent/current `base` changes. Route declarations are allowed only when the binding was linked with covering `--route-scope` patterns (`/admin` exact, `/api/*` final wildcard); no scopes means no CI route authority. Keep secrets, domains, subdomains, checks, non-current base changes, and out-of-scope routes in a local `run402 deploy
|
|
209
|
+
CI deploys can ship `site`, `functions`, `database`, and absent/current `base` changes. Route declarations are allowed only when the binding was linked with covering `--route-scope` patterns (`/admin` exact, `/api/*` final wildcard); no scopes means no CI route authority. Keep secrets, domains, subdomains, checks, non-current base changes, and out-of-scope routes in a local `run402 deploy` where the full wallet-backed authority is present.
|
|
210
210
|
|
|
211
211
|
### Storage (paste-and-go CDN assets)
|
|
212
212
|
|
|
@@ -253,7 +253,7 @@ import { db, adminDb, auth, email, ai } from "@run402/functions";
|
|
|
253
253
|
|
|
254
254
|
### Same-origin web routes
|
|
255
255
|
|
|
256
|
-
`run402 deploy
|
|
256
|
+
`run402 deploy` accepts `site.public_paths` for clean static browser URLs and `routes.replace` for function ingress or exact method-aware static aliases. Release asset paths such as `events.html` are distinct from public paths such as `/events`: prefer `{ "site": { "replace": { "events.html": { "data": "<h1>Events</h1>" } }, "public_paths": { "mode": "explicit", "replace": { "/events": { "asset": "events.html" } } } } }` for ordinary clean static URLs. In explicit mode `/events.html` is not public unless separately declared; `mode: "implicit"` restores filename-derived reachability and can widen access. `site.embedding.frame_ancestors` (catalog keys, e.g. `["localhost"]`, never raw origins) lets the named embedders put the site in an iframe: the gateway sends `Content-Security-Policy: frame-ancestors http://localhost:* http://127.0.0.1:*` and drops `X-Frame-Options` on every response of the host; omitted on a later apply carries the previous declaration forward, `null` returns to deny, unknown keys fail `INVALID_SPEC` naming the valid ones, and `/_run402/config.json` discloses the effective policy cross-origin so an embedder can check before framing. `routes.replace` is an array of route entries, not a path-keyed map. Use exact `/admin` plus final-wildcard `/admin/*` for a routed section root, narrow `/api/*` methods such as `["GET","POST","OPTIONS"]`, POST-only `/login` function routes, and exact static route targets like `{ "pattern": "/events", "methods": ["GET","HEAD"], "target": { "type": "static", "file": "events.html" } }` only for route-table alias behavior. Static route `file` is a release asset path, not a public path, URL, CAS hash, rewrite, or redirect. Recipe — static home page + SPA shell: a root alias `{ "pattern": "/", "target": { "type": "static", "file": "home.html" } }` (with `home.html` at the site root) serves real static bytes at `GET /` (`route_static_alias`) while unmatched app routes keep the `index.html` shell (`spa_fallback`); expect non-blocking `STATIC_ALIAS_SHADOWS_STATIC_PATH` (warn) / `STATIC_ALIAS_DUPLICATE_CANONICAL_URL` (info) plan lints and verify with `run402 deploy resolve --url https://<your-site>/ --method GET`. Routed functions use Node 22 Fetch Request -> Response; `req.url` is the full public URL on managed subdomains, deployment hosts, and verified custom domains. Direct `/functions/v1/:name` remains API-key protected. Known resolve literals include `host_missing`, `manifest_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `none`, `static_exact`, `static_index`, `spa_fallback`, `spa_fallback_missing`, `route_function`, `route_static_alias`, and `route_method_miss`; known `authorization_result` values include `authorized`, `not_public`, `not_applicable`, `manifest_missing`, `target_missing`, `active_release_missing`, `unsupported_manifest_version`, `path_error`, `missing_cas_object`, `unfinalized_or_deleting_cas_object`, `size_mismatch`, and `unauthorized_cas_object`. Known `fallback_state` values include `active_release_missing`, `unsupported_manifest_version`, and `negative_cache_hit`; preserve unknown future strings. Release inventories may include `static_public_paths` with `public_path`, `asset_path`, `reachability_authority`, and `direct`; resolve may return the same reachability fields plus `authorization_result`, `cas_object`, `response_variant`, and route/static fields like `allow`, `route_pattern`, `target_type`, `target_name`, and `target_file`. Static route target warnings include `STATIC_ALIAS_SHADOWS_STATIC_PATH`, `STATIC_ALIAS_RELATIVE_ASSET_RISK`, `STATIC_ALIAS_DUPLICATE_CANONICAL_URL`, `STATIC_ALIAS_EXTENSIONLESS_NON_HTML`, and `STATIC_ALIAS_TABLE_NEAR_LIMIT`; inspect active routes, `static_public_paths`, and the backing `asset_path`. Runtime route failure codes to branch on: `ROUTE_MANIFEST_LOAD_FAILED`, `ROUTED_INVOKE_WORKER_SECRET_MISSING`, `ROUTED_INVOKE_AUTH_FAILED`, `ROUTED_ROUTE_STALE`, `ROUTE_METHOD_NOT_ALLOWED`, and `ROUTED_RESPONSE_TOO_LARGE`.
|
|
257
257
|
|
|
258
258
|
### Secrets
|
|
259
259
|
|
|
@@ -261,7 +261,7 @@ import { db, adminDb, auth, email, ai } from "@run402/functions";
|
|
|
261
261
|
run402 secrets set <id> OPENAI_API_KEY --file ./.secrets/openai-key
|
|
262
262
|
printf %s "$OPENAI_API_KEY" | run402 secrets set <id> OPENAI_API_KEY --stdin
|
|
263
263
|
run402 secrets list <id>
|
|
264
|
-
run402 deploy
|
|
264
|
+
run402 deploy --manifest run402.deploy.json # manifest uses secrets.require, not values
|
|
265
265
|
```
|
|
266
266
|
|
|
267
267
|
Secret values are write-only. `list` returns keys and timestamps only; deploy manifests should declare dependencies with `secrets.require` and never contain values.
|
|
@@ -289,7 +289,7 @@ run402 email send --to user@example.com --subject "Welcome" --html "<h1>Hi</h1>"
|
|
|
289
289
|
run402 email send --to user@example.com --template notification --var project_name="My App"
|
|
290
290
|
```
|
|
291
291
|
|
|
292
|
-
For Run402 Core, use the same commands after `run402 init --api-base=http://my-core:4020`.
|
|
292
|
+
For Run402 Core, use the same commands after `run402 init --api-base=http://my-core:4020`. Whoever runs the Core gateway must configure an outbound provider such as SES first; `run402 email mailboxes` surfaces `provider_readiness`, `can_send`, `send_blocked_reason`, and `next_actions` when setup is missing. Core's first email slice supports raw outbound mail with attachments; managed templates, inbound reply handling, sender-domain automation, and delivery operations may remain Cloud-only until the Core gateway adds those capabilities.
|
|
293
293
|
|
|
294
294
|
### Image generation
|
|
295
295
|
|
|
@@ -323,18 +323,18 @@ run402 doctor # { ok, blocking[], warnings[], checks[] }
|
|
|
323
323
|
run402 doctor --refresh # live npm check for a newer run402
|
|
324
324
|
```
|
|
325
325
|
|
|
326
|
-
`ok` is structural: `true` exactly when `blocking[]` is empty. Every check carries `severity: "blocking" | "advisory" | "info"`; advisory findings (an unbound
|
|
326
|
+
`ok` is structural: `true` exactly when `blocking[]` is empty. Every check carries `severity: "blocking" | "advisory" | "info"`; advisory findings (an unbound contact passkey, a degraded recovery posture on another org, a stale CLI, gitvault gaps) land in `warnings[]` without changing `ok` or the exit code. The `tier` check's `status` is a fixed vocabulary (`ok | inactive | frozen | past_due | dormant | purged | missing | unknown | error`, never a tier name; see `value.tier` / `value.lifecycle`), and a wallet whose own org holds no tier but can reach another org's projects reports `missing` as advisory (`TIER_MISSING_ON_OWN_ORG`).
|
|
327
327
|
|
|
328
328
|
## State
|
|
329
329
|
|
|
330
330
|
Local state lives at:
|
|
331
331
|
|
|
332
332
|
- `~/.config/run402/projects.json` (`0600`) — project credentials (`anon_key`, `service_key`, `tier`, `lease_expires_at`)
|
|
333
|
-
- `~/.config/run402/
|
|
333
|
+
- `~/.config/run402/wallet.json` (`0600`) — wallet for x402 signing
|
|
334
334
|
- `~/.config/run402/config.json` (`0600`) — global default wallet pointer (`active_wallet`)
|
|
335
|
-
- `~/.config/run402/profiles/<name>/` (`0700`) — named wallets, each with its own `
|
|
335
|
+
- `~/.config/run402/profiles/<name>/` (`0700`) — named wallets, each with its own `wallet.json` + `projects.json` + non-secret `meta.json`
|
|
336
336
|
|
|
337
|
-
Override the base directory with `RUN402_CONFIG_DIR` or the
|
|
337
|
+
Override the base directory with `RUN402_CONFIG_DIR` or the wallet file with `RUN402_WALLET_PATH`. Override the API base with `RUN402_API_BASE`.
|
|
338
338
|
|
|
339
339
|
### Named wallets (profiles)
|
|
340
340
|
|
|
@@ -346,16 +346,18 @@ Hold several wallets on one machine and select between them:
|
|
|
346
346
|
|
|
347
347
|
The CLI handles all x402 / MPP payment signing automatically — never ask the human for a private key or set up payment libraries by hand.
|
|
348
348
|
|
|
349
|
-
###
|
|
349
|
+
### Sign-in session (a person)
|
|
350
350
|
|
|
351
|
-
|
|
351
|
+
A person signs in; the agent is your wallet (`run402 status` shows the wallet's own organization state). There is one sign-in session, graded by how it was minted.
|
|
352
352
|
|
|
353
|
-
- `run402
|
|
354
|
-
- `run402
|
|
355
|
-
- `run402
|
|
356
|
-
- `run402
|
|
353
|
+
- `run402 login` — browser passkey sign-in over loopback PKCE (RFC 8252, like `aws sso login`): the CLI listens on `127.0.0.1`, the browser runs the ceremony, and the session (grade `loopback`: full, step-up-able) is cached at the base config dir (shared across named wallets). Signing in again is the step-up.
|
|
354
|
+
- `run402 login --device` — RFC 8628 device code for a terminal with no browser; approve it in the console. The session is grade `device`: read-only.
|
|
355
|
+
- `run402 whoami` — principal, memberships, `session.grade`, and the write approvals cached on this machine.
|
|
356
|
+
- `run402 approve --action <capability> (--org <org_id> | --project <project_id>)` — passkey-signed write approval for one action on one target; a person without a wallet needs it to provision, deploy, or write secrets (an interactive `provision`/`deploy` opens it for you).
|
|
357
|
+
- `run402 logout` — revoke the session server-side and clear it and every write approval locally.
|
|
358
|
+
- `run402 org list` — your orgs joined with the account overview (tier, lifecycle, quotas, allowance, advisories).
|
|
357
359
|
|
|
358
|
-
Not exposed as MCP tools by design — MCP authenticates as the agent (wallet), and
|
|
360
|
+
Not exposed as MCP tools by design — MCP authenticates as the agent (wallet), and a person's session must not become its ambient authority.
|
|
359
361
|
|
|
360
362
|
## Active project (sticky default)
|
|
361
363
|
|
package/cli.mjs
CHANGED
|
@@ -31,20 +31,18 @@ Usage:
|
|
|
31
31
|
Commands, grouped by when you reach for them:
|
|
32
32
|
|
|
33
33
|
SET UP — get an agent funded and entitled
|
|
34
|
-
init Set up
|
|
34
|
+
init Set up the local wallet, funding, and check tier status (x402 default)
|
|
35
35
|
init mpp Set up with MPP payment rail (Tempo Moderato testnet)
|
|
36
|
-
wallets Manage
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
redeem Redeem a promo code for run402 credit
|
|
36
|
+
wallets Manage local wallets (list, current, new, use, fund, balance, import)
|
|
37
|
+
tier Manage your tier and its lease (status, set)
|
|
38
|
+
redeem Redeem a promo code into your organization's allowance
|
|
40
39
|
pay Call an arbitrary x402-priced URL with a bounded payment
|
|
41
40
|
|
|
42
41
|
BUILD & SHIP — the app and everything it serves
|
|
43
42
|
up Provision/link/deploy the current app with SDK orchestration
|
|
44
43
|
projects Manage projects (provision, list, get, sql, delete)
|
|
45
44
|
repos Host-blind encrypted git repos: create/view/list, snapshot/mirror/recover, fsck/gc/access/policy (repo aliases repos)
|
|
46
|
-
deploy
|
|
47
|
-
apply Alias for deploy apply; supports --rehearse for migration rehearsal
|
|
45
|
+
deploy Deploy a release; subcommands inspect and steer deploys (requires active tier)
|
|
48
46
|
functions Manage serverless functions (deploy, invoke, logs, list, delete)
|
|
49
47
|
secrets Manage project secrets (set, list, delete)
|
|
50
48
|
sites Deploy static sites
|
|
@@ -57,7 +55,7 @@ OPERATE — what happened, and is it healthy
|
|
|
57
55
|
live Change hints for live tables as NDJSON (SSE; --once for one held read)
|
|
58
56
|
errors Grouped error fingerprints + a promote/revert verdict (release-baselined)
|
|
59
57
|
logs Fetch function logs by request id (--request-id req_...)
|
|
60
|
-
status Show full account state (
|
|
58
|
+
status Show full account state (wallet, allowance, tier, projects)
|
|
61
59
|
doctor Health and config diagnostics (JSON by default; includes --buzz preflight)
|
|
62
60
|
service Run402 service health and availability (status, health)
|
|
63
61
|
snapshots Create/list/restore/delete project data snapshots
|
|
@@ -78,13 +76,16 @@ AUTHORITY — who may act, and with what credential
|
|
|
78
76
|
identity Public proof-backed external agent identity links
|
|
79
77
|
auth Manage project user authentication (magic link, passwords, settings)
|
|
80
78
|
ci Link GitHub Actions OIDC deploy bindings
|
|
81
|
-
|
|
79
|
+
login Sign in as a person (browser passkey; --device for a read-only session)
|
|
80
|
+
logout End your sign-in session and clear it from this machine
|
|
81
|
+
whoami Who this CLI acts as: principal, memberships, session grade
|
|
82
|
+
approve Write approval for one action on one target (provision, deploy, secrets)
|
|
82
83
|
|
|
83
84
|
DELIVER — reach a human when something happens
|
|
84
85
|
deliveries Did a notification actually land (list, get)
|
|
85
86
|
contacts Where a human is reachable — paging ladder + Telegram (list, add, connect, rm, preferences)
|
|
86
87
|
subscriptions Which events go where (add, list, rm)
|
|
87
|
-
webhook-secret Rotate
|
|
88
|
+
webhook-secret Rotate your webhook signing secret
|
|
88
89
|
email Send template-based emails from your project
|
|
89
90
|
|
|
90
91
|
PLATFORM — everything else, and the things still finding a home
|
|
@@ -92,7 +93,7 @@ PLATFORM — everything else, and the things still finding a home
|
|
|
92
93
|
billing Email organizations, Stripe tier checkout, email packs
|
|
93
94
|
contracts KMS signers ($0.04/day rental + $0.000005/sign)
|
|
94
95
|
jobs Submit and inspect platform-managed jobs
|
|
95
|
-
transfer
|
|
96
|
+
transfer Project transfer to a wallet, an email, or an owned org (init, preview, list, accept, cancel)
|
|
96
97
|
cloud Cloud portability archive export (archives create/download/status)
|
|
97
98
|
archives Inspect and verify portable project archives locally
|
|
98
99
|
gitvault RETIRED — moved to repos (repo-surface-consolidation); every spelling answers COMMAND_MOVED/COMMAND_REMOVED
|
|
@@ -115,11 +116,10 @@ Run 'run402 <command> --help' for detailed usage of each command.
|
|
|
115
116
|
|
|
116
117
|
Examples:
|
|
117
118
|
run402 up --name my-app -y
|
|
118
|
-
run402
|
|
119
|
-
run402
|
|
119
|
+
run402 init
|
|
120
|
+
run402 wallets fund
|
|
120
121
|
run402 pay https://seller.example/resource --max-usd 0.05
|
|
121
|
-
run402 deploy
|
|
122
|
-
run402 apply --manifest app.json --rehearse --json
|
|
122
|
+
run402 deploy --manifest app.json
|
|
123
123
|
run402 snapshots list --project prj_...
|
|
124
124
|
run402 branches create --project prj_... --ttl-days 7 --json
|
|
125
125
|
run402 cloud archives create --project prj_... --wait --output ./project.r402ar --json
|
|
@@ -134,7 +134,7 @@ Examples:
|
|
|
134
134
|
Getting started:
|
|
135
135
|
run402 init Set up with x402 (Base Sepolia)
|
|
136
136
|
run402 init mpp Set up with MPP (Tempo Moderato)
|
|
137
|
-
run402 tier set prototype
|
|
137
|
+
run402 tier set prototype Set a tier (prototype is free)
|
|
138
138
|
run402 up --name my-app -y Create and deploy this app (explicit destination)
|
|
139
139
|
run402 ci link github --project prj_... --manifest run402.deploy.json
|
|
140
140
|
`;
|
|
@@ -219,11 +219,6 @@ switch (cmd) {
|
|
|
219
219
|
await run(sub, rest);
|
|
220
220
|
break;
|
|
221
221
|
}
|
|
222
|
-
case "allowance": {
|
|
223
|
-
const { run } = await import("./lib/allowance.mjs");
|
|
224
|
-
await run(sub, rest);
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
222
|
case "tier": {
|
|
228
223
|
const { run } = await import("./lib/tier.mjs");
|
|
229
224
|
await run(sub, rest);
|
|
@@ -247,11 +242,6 @@ switch (cmd) {
|
|
|
247
242
|
await run(sub, rest);
|
|
248
243
|
break;
|
|
249
244
|
}
|
|
250
|
-
case "apply": {
|
|
251
|
-
const { runDeployV2 } = await import("./lib/deploy-v2.mjs");
|
|
252
|
-
await runDeployV2("apply", [sub, ...rest].filter(Boolean));
|
|
253
|
-
break;
|
|
254
|
-
}
|
|
255
245
|
case "snapshots": {
|
|
256
246
|
const { run } = await import("./lib/snapshots.mjs");
|
|
257
247
|
await run(sub, rest);
|
|
@@ -450,9 +440,24 @@ switch (cmd) {
|
|
|
450
440
|
await run(sub, rest);
|
|
451
441
|
break;
|
|
452
442
|
}
|
|
453
|
-
case "
|
|
454
|
-
const { run } = await import("./lib/
|
|
455
|
-
await run(sub, rest);
|
|
443
|
+
case "login": {
|
|
444
|
+
const { run } = await import("./lib/login.mjs");
|
|
445
|
+
await run([sub, ...rest].filter(Boolean));
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
case "logout": {
|
|
449
|
+
const { run } = await import("./lib/logout.mjs");
|
|
450
|
+
await run([sub, ...rest].filter(Boolean));
|
|
451
|
+
break;
|
|
452
|
+
}
|
|
453
|
+
case "whoami": {
|
|
454
|
+
const { run } = await import("./lib/whoami.mjs");
|
|
455
|
+
await run([sub, ...rest].filter(Boolean));
|
|
456
|
+
break;
|
|
457
|
+
}
|
|
458
|
+
case "approve": {
|
|
459
|
+
const { run } = await import("./lib/approve.mjs");
|
|
460
|
+
await run([sub, ...rest].filter(Boolean));
|
|
456
461
|
break;
|
|
457
462
|
}
|
|
458
463
|
case "source-access": {
|
package/core-dist/config.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
|
-
import { existsSync, renameSync, mkdirSync, chmodSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { existsSync, renameSync, mkdirSync, chmodSync, readFileSync, readdirSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { randomBytes } from "node:crypto";
|
|
5
5
|
import { describeRejectedValue } from "./redact.js";
|
|
6
6
|
export const DEFAULT_API_BASE = "https://api.run402.com";
|
|
@@ -118,7 +118,7 @@ export function getProfilesDir() {
|
|
|
118
118
|
* The effective config directory for the *active* wallet. The `default` wallet
|
|
119
119
|
* resolves to the base dir (zero migration for existing single-wallet
|
|
120
120
|
* installs); any named wallet resolves to `{base}/profiles/<name>`. Because
|
|
121
|
-
* keystore,
|
|
121
|
+
* keystore, wallet, and meta paths all derive from this, switching the
|
|
122
122
|
* profile env var moves the whole wallet bundle atomically — and the SDK/MCP
|
|
123
123
|
* inherit profile selection for free.
|
|
124
124
|
*/
|
|
@@ -232,27 +232,61 @@ export function isCoreApiTarget() {
|
|
|
232
232
|
return true;
|
|
233
233
|
return false;
|
|
234
234
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
235
|
+
/**
|
|
236
|
+
* The file that holds a wallet's key, in the base config dir (the `default`
|
|
237
|
+
* wallet) and in each `profiles/<name>/` directory.
|
|
238
|
+
*/
|
|
239
|
+
export const WALLET_FILE_NAME = "wallet.json";
|
|
240
|
+
/** The name the wallet key file had before it was `wallet.json`. */
|
|
241
|
+
const PREVIOUS_WALLET_FILE_NAME = "allowance.json";
|
|
242
|
+
const walletFilesMovedFor = new Set();
|
|
243
|
+
/**
|
|
244
|
+
* One-time local move of the wallet key file to its current name: in the base
|
|
245
|
+
* config dir and every `profiles/<name>/` directory, `allowance.json` is
|
|
246
|
+
* renamed to `wallet.json` (an atomic same-directory rename) when
|
|
247
|
+
* `wallet.json` is absent, then tightened to 0600. A directory that already
|
|
248
|
+
* holds `wallet.json` is left untouched. Runs once per base dir per process;
|
|
249
|
+
* nothing reads the previous name afterwards.
|
|
250
|
+
*/
|
|
251
|
+
export function moveWalletFiles() {
|
|
252
|
+
const base = getConfigBaseDir();
|
|
253
|
+
if (walletFilesMovedFor.has(base))
|
|
254
|
+
return;
|
|
255
|
+
walletFilesMovedFor.add(base);
|
|
256
|
+
const dirs = [base];
|
|
257
|
+
try {
|
|
258
|
+
for (const entry of readdirSync(join(base, "profiles"), { withFileTypes: true })) {
|
|
259
|
+
if (entry.isDirectory())
|
|
260
|
+
dirs.push(join(base, "profiles", entry.name));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch {
|
|
264
|
+
// No profiles directory yet.
|
|
265
|
+
}
|
|
266
|
+
for (const dir of dirs) {
|
|
267
|
+
const from = join(dir, PREVIOUS_WALLET_FILE_NAME);
|
|
268
|
+
const to = join(dir, WALLET_FILE_NAME);
|
|
269
|
+
if (existsSync(to) || !existsSync(from))
|
|
270
|
+
continue;
|
|
271
|
+
try {
|
|
272
|
+
renameSync(from, to);
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
248
277
|
try {
|
|
249
|
-
chmodSync(
|
|
278
|
+
chmodSync(to, 0o600);
|
|
250
279
|
}
|
|
251
280
|
catch {
|
|
252
281
|
// Best-effort (e.g. Windows / exotic filesystems). Read-time self-heal
|
|
253
|
-
// in
|
|
282
|
+
// in readWallet() is the backstop.
|
|
254
283
|
}
|
|
255
284
|
}
|
|
256
|
-
|
|
285
|
+
}
|
|
286
|
+
export function getWalletPath() {
|
|
287
|
+
if (process.env.RUN402_WALLET_PATH)
|
|
288
|
+
return process.env.RUN402_WALLET_PATH;
|
|
289
|
+
moveWalletFiles();
|
|
290
|
+
return join(getConfigDir(), WALLET_FILE_NAME);
|
|
257
291
|
}
|
|
258
292
|
//# sourceMappingURL=config.js.map
|
|
@@ -10,6 +10,14 @@ export function getControlPlaneSessionPath() {
|
|
|
10
10
|
return (process.env.RUN402_CONTROL_PLANE_SESSION_PATH ||
|
|
11
11
|
join(getConfigBaseDir(), "control-plane-session.json"));
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Read a wire grade. An unknown or absent value is treated as `device` (the
|
|
15
|
+
* read-only grade) so a cache the client cannot classify never claims more
|
|
16
|
+
* authority than the gateway will grant.
|
|
17
|
+
*/
|
|
18
|
+
function toGrade(raw) {
|
|
19
|
+
return raw === "browser" || raw === "loopback" || raw === "device" ? raw : "device";
|
|
20
|
+
}
|
|
13
21
|
/** Tighten 0600 if group/other-readable, warning once. Best-effort, POSIX-only. */
|
|
14
22
|
function selfHealPermissions(p) {
|
|
15
23
|
if (process.platform === "win32")
|
|
@@ -50,20 +58,20 @@ export function readControlPlaneSession(path) {
|
|
|
50
58
|
return null;
|
|
51
59
|
}
|
|
52
60
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
53
|
-
throw new Error("control-plane-session.json must contain a JSON object. Delete it and run 'run402
|
|
61
|
+
throw new Error("control-plane-session.json must contain a JSON object. Delete it and run 'run402 login' to recreate it.");
|
|
54
62
|
}
|
|
55
63
|
const data = parsed;
|
|
56
64
|
if (typeof data.control_plane_session_token !== "string" ||
|
|
57
65
|
data.control_plane_session_token.length === 0) {
|
|
58
|
-
throw new Error("control-plane-session.json missing valid 'control_plane_session_token'. Run 'run402
|
|
66
|
+
throw new Error("control-plane-session.json missing valid 'control_plane_session_token'. Run 'run402 login' to refresh it.");
|
|
59
67
|
}
|
|
60
68
|
if (typeof data.expires_at !== "number" || !Number.isFinite(data.expires_at)) {
|
|
61
|
-
throw new Error("control-plane-session.json missing valid 'expires_at'. Run 'run402
|
|
69
|
+
throw new Error("control-plane-session.json missing valid 'expires_at'. Run 'run402 login' to refresh it.");
|
|
62
70
|
}
|
|
63
71
|
return {
|
|
64
72
|
control_plane_session_token: data.control_plane_session_token,
|
|
65
73
|
token_type: typeof data.token_type === "string" ? data.token_type : "Bearer",
|
|
66
|
-
|
|
74
|
+
grade: toGrade(data.grade),
|
|
67
75
|
principal_id: typeof data.principal_id === "string" ? data.principal_id : "",
|
|
68
76
|
amr: Array.isArray(data.amr) ? data.amr.filter((a) => typeof a === "string") : [],
|
|
69
77
|
expires_at: data.expires_at,
|
|
@@ -79,7 +87,7 @@ export function saveControlPlaneSession(data, path) {
|
|
|
79
87
|
renameSync(tmp, p);
|
|
80
88
|
chmodSync(p, 0o600);
|
|
81
89
|
}
|
|
82
|
-
/** Delete the cached
|
|
90
|
+
/** Delete the cached sign-in session — local half of `run402 logout`. Idempotent. */
|
|
83
91
|
export function clearControlPlaneSession(path) {
|
|
84
92
|
const p = path ?? getControlPlaneSessionPath();
|
|
85
93
|
try {
|
|
@@ -105,10 +113,27 @@ export function controlPlaneSessionFromTokenResponse(resp, nowMs = Date.now()) {
|
|
|
105
113
|
return {
|
|
106
114
|
control_plane_session_token: resp.control_plane_session_token,
|
|
107
115
|
token_type: resp.token_type ?? "Bearer",
|
|
108
|
-
|
|
116
|
+
grade: toGrade(resp.grade),
|
|
109
117
|
principal_id: resp.principal_id ?? "",
|
|
110
118
|
amr: Array.isArray(resp.amr) ? resp.amr.filter((a) => typeof a === "string") : [],
|
|
111
119
|
expires_at: nowMs + (typeof resp.expires_in === "number" ? resp.expires_in : 0) * 1000,
|
|
112
120
|
};
|
|
113
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Delete the session caches of the retired email-scoped session
|
|
124
|
+
* (`{base}/operator-session.json`) and the retired approval cache file
|
|
125
|
+
* (`{base}/write-auth-session.json`). Neither is read any more; `run402 login`
|
|
126
|
+
* and `run402 logout` call this so a stale token does not linger on disk.
|
|
127
|
+
* Best-effort and idempotent.
|
|
128
|
+
*/
|
|
129
|
+
export function clearRetiredSessionCaches() {
|
|
130
|
+
for (const name of ["operator-session.json", "write-auth-session.json"]) {
|
|
131
|
+
try {
|
|
132
|
+
rmSync(join(getConfigBaseDir(), name), { force: true });
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
// Best-effort: a failed unlink never blocks login or logout.
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
114
139
|
//# sourceMappingURL=control-plane-session.js.map
|
package/core-dist/keystore.js
CHANGED
|
@@ -3,7 +3,7 @@ import { dirname, join } from "node:path";
|
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { getLegacyProjectsPath, getProjectCredentialsPath } from "./config.js";
|
|
5
5
|
import { clearActiveProjectId as clearProfileActiveProjectId, getActiveProjectId as getProfileActiveProjectId, recordMigration, setActiveProjectId as setProfileActiveProjectId, } from "./profile-state.js";
|
|
6
|
-
import {
|
|
6
|
+
import { readWallet } from "./wallet.js";
|
|
7
7
|
function withFileLock(path, fn, { retries = 200, delayMs = 20 } = {}) {
|
|
8
8
|
const lockDir = path + ".lock";
|
|
9
9
|
mkdirSync(dirname(path), { recursive: true });
|
|
@@ -99,7 +99,7 @@ function migrateLegacyProjectsJson(targetPath) {
|
|
|
99
99
|
// Scoped the same way every other write in this module now is (see
|
|
100
100
|
// `currentPrincipal`'s doc comment) — an unscoped write here is exactly
|
|
101
101
|
// what poisons the "unknown"-principal bucket for good on a machine that
|
|
102
|
-
// migrates before its wallet
|
|
102
|
+
// migrates before its wallet exists yet.
|
|
103
103
|
setProfileActiveProjectId(legacy.active_project_id, undefined, { principal: currentPrincipal() });
|
|
104
104
|
}
|
|
105
105
|
recordMigration("projects_json_import", {
|
|
@@ -174,7 +174,7 @@ export function removeProject(projectId, path) {
|
|
|
174
174
|
}
|
|
175
175
|
/**
|
|
176
176
|
* The active-project scope's `principal`, derived from the CURRENT wallet's
|
|
177
|
-
*
|
|
177
|
+
* wallet — matching exactly what `NodeCredentialsProvider.setActiveProject`
|
|
178
178
|
* (sdk/src/node/credentials.ts) writes after `projects.provision` /
|
|
179
179
|
* `projects.use`. Without this, every reader here used the profile-state
|
|
180
180
|
* module's own default (empty) scope, which resolves to a FIXED
|
|
@@ -185,7 +185,7 @@ export function removeProject(projectId, path) {
|
|
|
185
185
|
* `profile-state.ts#getActiveProjectId` happens to agree), but once the
|
|
186
186
|
* "unknown" bucket is EVER populated by any principal-less write — the
|
|
187
187
|
* one-time legacy `projects.json` migration below, or any provision/`use`
|
|
188
|
-
* call made before this machine had a wallet
|
|
188
|
+
* call made before this machine had a wallet — it never gets
|
|
189
189
|
* updated again (real wallet operations write to the principal-keyed
|
|
190
190
|
* bucket, not "unknown") and PERMANENTLY shadows every later wallet-scoped
|
|
191
191
|
* activation for every caller that reads through this module: `resolveProjectId`
|
|
@@ -197,13 +197,13 @@ export function removeProject(projectId, path) {
|
|
|
197
197
|
* stale "unknown"-bucket entry first and never falls through to the
|
|
198
198
|
* freshly-updated flat value.
|
|
199
199
|
*
|
|
200
|
-
* Best-effort: an unreadable/malformed
|
|
200
|
+
* Best-effort: an unreadable/malformed wallet degrades to `null` (the
|
|
201
201
|
* "unknown" bucket callers tolerate), never a
|
|
202
|
-
* throw — this is a read-scoping concern, not
|
|
202
|
+
* throw — this is a read-scoping concern, not a wallet-validity one.
|
|
203
203
|
*/
|
|
204
204
|
function currentPrincipal() {
|
|
205
205
|
try {
|
|
206
|
-
return
|
|
206
|
+
return readWallet()?.address ?? null;
|
|
207
207
|
}
|
|
208
208
|
catch {
|
|
209
209
|
return null;
|