run402 4.93.1 → 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/core-dist/profiles.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* A "wallet" is a whole config directory. The reserved `default` wallet lives
|
|
5
5
|
* at the base config dir (zero migration for existing installs); every named
|
|
6
|
-
* wallet lives under `{base}/profiles/<name>/` with its own `
|
|
6
|
+
* wallet lives under `{base}/profiles/<name>/` with its own `wallet.json`,
|
|
7
7
|
* `projects.json`, and this module's non-secret `meta.json`.
|
|
8
8
|
*
|
|
9
9
|
* Two levels of active state, mirroring the wallet → project model:
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
import { readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync, existsSync, readdirSync, rmSync, } from "node:fs";
|
|
18
18
|
import { dirname, join } from "node:path";
|
|
19
19
|
import { randomBytes } from "node:crypto";
|
|
20
|
-
import { getConfigBaseDir, getProfilesDir, DEFAULT_PROFILE, isValidProfileName, } from "./config.js";
|
|
20
|
+
import { getConfigBaseDir, getProfilesDir, DEFAULT_PROFILE, isValidProfileName, moveWalletFiles, WALLET_FILE_NAME, } from "./config.js";
|
|
21
21
|
import { describeRejectedValue } from "./redact.js";
|
|
22
22
|
function atomicWrite(p, content, mode) {
|
|
23
23
|
const dir = dirname(p);
|
|
@@ -114,17 +114,19 @@ export function writeMeta(name, meta) {
|
|
|
114
114
|
atomicWrite(metaPath(name), JSON.stringify(meta, null, 2), 0o600);
|
|
115
115
|
}
|
|
116
116
|
// --- enumeration + lifecycle ---
|
|
117
|
-
/** True when a wallet's `
|
|
117
|
+
/** True when a wallet's `wallet.json` exists on disk. */
|
|
118
118
|
export function profileExists(name) {
|
|
119
|
-
|
|
119
|
+
moveWalletFiles();
|
|
120
|
+
return existsSync(join(profileDir(name), WALLET_FILE_NAME));
|
|
120
121
|
}
|
|
121
122
|
/**
|
|
122
|
-
* All wallet names on disk: `default` (only if a root
|
|
123
|
+
* All wallet names on disk: `default` (only if a root wallet.json exists)
|
|
123
124
|
* plus every valid directory under `profiles/`.
|
|
124
125
|
*/
|
|
125
126
|
export function listProfileNames() {
|
|
127
|
+
moveWalletFiles();
|
|
126
128
|
const names = [];
|
|
127
|
-
if (existsSync(join(getConfigBaseDir(),
|
|
129
|
+
if (existsSync(join(getConfigBaseDir(), WALLET_FILE_NAME))) {
|
|
128
130
|
names.push(DEFAULT_PROFILE);
|
|
129
131
|
}
|
|
130
132
|
try {
|
|
@@ -184,7 +186,7 @@ export function renameProfile(oldName, newName) {
|
|
|
184
186
|
/* best-effort */
|
|
185
187
|
}
|
|
186
188
|
const base = getConfigBaseDir();
|
|
187
|
-
for (const f of [
|
|
189
|
+
for (const f of [WALLET_FILE_NAME, "projects.json", "meta.json"]) {
|
|
188
190
|
const src = join(base, f);
|
|
189
191
|
if (existsSync(src))
|
|
190
192
|
renameSync(src, join(dest, f));
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* SIWX auth helper — generates SIWX (Sign-In With X / EIP-4361) headers for Run402 API.
|
|
3
3
|
* Uses @noble/curves (lighter than viem) for signing.
|
|
4
4
|
*/
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import { secp256k1 } from "@noble/curves/secp256k1.js";
|
|
7
7
|
import { keccak_256 } from "@noble/hashes/sha3.js";
|
|
8
8
|
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
9
|
-
import {
|
|
9
|
+
import { readWallet } from "./wallet.js";
|
|
10
10
|
import { getApiBase } from "./config.js";
|
|
11
11
|
/**
|
|
12
12
|
* EIP-55 mixed-case checksum encoding.
|
|
@@ -21,7 +21,7 @@ export function toChecksumAddress(address) {
|
|
|
21
21
|
return checksummed;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
* EIP-191 personal_sign: sign a message with the
|
|
24
|
+
* EIP-191 personal_sign: sign a message with the wallet's private key.
|
|
25
25
|
*/
|
|
26
26
|
function personalSign(privateKeyHex, address, message) {
|
|
27
27
|
const msgBytes = new TextEncoder().encode(message);
|
|
@@ -86,11 +86,11 @@ export function formatSIWEMessage(opts, address) {
|
|
|
86
86
|
return lines.join("\n");
|
|
87
87
|
}
|
|
88
88
|
export function buildSIWxAuthHeaders(opts) {
|
|
89
|
-
const message = formatSIWEMessage(opts, opts.
|
|
90
|
-
const signature = personalSign(opts.
|
|
89
|
+
const message = formatSIWEMessage(opts, opts.wallet.address);
|
|
90
|
+
const signature = personalSign(opts.wallet.privateKey, opts.wallet.address, message);
|
|
91
91
|
const payload = {
|
|
92
92
|
domain: opts.domain,
|
|
93
|
-
address: toChecksumAddress(opts.
|
|
93
|
+
address: toChecksumAddress(opts.wallet.address),
|
|
94
94
|
statement: opts.statement,
|
|
95
95
|
uri: opts.uri,
|
|
96
96
|
version: opts.version ?? "1",
|
|
@@ -110,18 +110,18 @@ export function buildSIWxAuthHeaders(opts) {
|
|
|
110
110
|
}
|
|
111
111
|
/**
|
|
112
112
|
* Get SIWX auth headers for the Run402 API.
|
|
113
|
-
* Returns null if no
|
|
113
|
+
* Returns null if no local wallet is configured.
|
|
114
114
|
*
|
|
115
115
|
* @param path - API path (e.g. "/projects/v1") used to build the SIWE uri field.
|
|
116
116
|
*/
|
|
117
|
-
export function
|
|
118
|
-
//
|
|
119
|
-
// CLI's higher-level
|
|
120
|
-
//
|
|
117
|
+
export function getWalletAuthHeaders(path, walletPath) {
|
|
118
|
+
// readWallet throws on a malformed-shape wallet file. The
|
|
119
|
+
// CLI's higher-level readWallet wrapper surfaces this as a structured
|
|
120
|
+
// BAD_WALLET_FILE envelope; here we preserve the public contract that
|
|
121
121
|
// this helper returns SIWxAuthHeaders | null. Re-throw so callers above
|
|
122
122
|
// the CLI's wrapper (e.g. SDK paid-fetch) can decide whether to swallow it.
|
|
123
|
-
const
|
|
124
|
-
if (!
|
|
123
|
+
const localWallet = readWallet(walletPath);
|
|
124
|
+
if (!localWallet || !localWallet.address || !localWallet.privateKey)
|
|
125
125
|
return null;
|
|
126
126
|
const apiBase = getApiBase();
|
|
127
127
|
const url = new URL(apiBase);
|
|
@@ -132,7 +132,7 @@ export function getAllowanceAuthHeaders(path, allowancePath) {
|
|
|
132
132
|
const issuedAt = now.toISOString();
|
|
133
133
|
const expirationTime = new Date(now.getTime() + 5 * 60 * 1000).toISOString();
|
|
134
134
|
return buildSIWxAuthHeaders({
|
|
135
|
-
|
|
135
|
+
wallet: localWallet,
|
|
136
136
|
domain,
|
|
137
137
|
uri,
|
|
138
138
|
statement: "Sign in to Run402",
|
|
@@ -151,4 +151,4 @@ function messageChainId(chainId) {
|
|
|
151
151
|
function payloadChainId(chainId) {
|
|
152
152
|
return typeof chainId === "number" ? `eip155:${chainId}` : chainId;
|
|
153
153
|
}
|
|
154
|
-
//# sourceMappingURL=
|
|
154
|
+
//# sourceMappingURL=wallet-auth.js.map
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, renameSync, statSync } from "node:fs";
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
|
-
import {
|
|
4
|
+
import { getWalletPath } from "./config.js";
|
|
5
5
|
// 0x-prefixed 40-hex EVM address.
|
|
6
6
|
const ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
7
7
|
// 0x-prefixed 64-hex secp256k1 private key (32 bytes).
|
|
8
8
|
const PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
|
|
9
9
|
/**
|
|
10
|
-
* Load the agent
|
|
10
|
+
* Load the agent wallet from disk.
|
|
11
11
|
*
|
|
12
|
-
* Returns `null` for the two "no
|
|
12
|
+
* Returns `null` for the two "no local wallet configured" cases:
|
|
13
13
|
* - the file does not exist
|
|
14
14
|
* - the file exists but is not parseable JSON (preserve existing UX —
|
|
15
|
-
* consumers print "
|
|
15
|
+
* consumers print "no_wallet" and tell the user to run init)
|
|
16
16
|
*
|
|
17
17
|
* Throws a structured `Error` when the file parses as JSON but the
|
|
18
18
|
* shape is wrong (missing/wrong-type/wrong-length fields). Without this guard
|
|
19
19
|
* downstream callers crash with raw stack traces:
|
|
20
|
-
* - `cli/lib/status.mjs` reaches for `
|
|
20
|
+
* - `cli/lib/status.mjs` reaches for `wallet.address.toLowerCase()`
|
|
21
21
|
* and crashes with `TypeError: Cannot read properties of undefined`.
|
|
22
|
-
* - `core/src/
|
|
22
|
+
* - `core/src/wallet-auth.ts` passes a malformed `privateKey` to
|
|
23
23
|
* `@noble/curves` which throws "expected 32 bytes, got N".
|
|
24
24
|
*
|
|
25
|
-
* The CLI's `cli/lib/config.mjs:
|
|
25
|
+
* The CLI's `cli/lib/config.mjs:readWallet()` wrapper and the MCP
|
|
26
26
|
* `src/tools/{status,init}.ts` callers translate the throw into their own
|
|
27
|
-
* structured envelopes (`code:
|
|
27
|
+
* structured envelopes (`code: BAD_WALLET_FILE`).
|
|
28
28
|
*/
|
|
29
29
|
/**
|
|
30
|
-
* If
|
|
30
|
+
* If a wallet file is readable by group or other (any of the low 0o077
|
|
31
31
|
* bits set), tighten it to 0600 and warn once on stderr. This self-heals the
|
|
32
32
|
* historical case where a legacy world-readable `wallet.json` (mode 0644) was
|
|
33
|
-
* migrated to `
|
|
33
|
+
* migrated to `wallet.json` via a mode-preserving rename, leaving the
|
|
34
34
|
* private key exposed on a shared machine. Best-effort: POSIX-only and silent
|
|
35
35
|
* on platforms without meaningful Unix modes (e.g. Windows).
|
|
36
36
|
*/
|
|
@@ -48,8 +48,8 @@ function selfHealPermissions(p) {
|
|
|
48
48
|
// Best-effort; never block a read on a chmod/stat failure.
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
-
export function
|
|
52
|
-
const p = path ??
|
|
51
|
+
export function readWallet(path) {
|
|
52
|
+
const p = path ?? getWalletPath();
|
|
53
53
|
if (!existsSync(p))
|
|
54
54
|
return null;
|
|
55
55
|
selfHealPermissions(p);
|
|
@@ -66,31 +66,31 @@ export function readAllowance(path) {
|
|
|
66
66
|
}
|
|
67
67
|
catch {
|
|
68
68
|
// Preserve historical UX — completely unparseable input reads as "no
|
|
69
|
-
//
|
|
69
|
+
// wallet configured" rather than as an error. Consumers already handle
|
|
70
70
|
// null with a friendly "run 'run402 init'" message.
|
|
71
71
|
return null;
|
|
72
72
|
}
|
|
73
73
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
74
|
-
throw new Error(`
|
|
74
|
+
throw new Error(`wallet.json must contain a JSON object (got ${Array.isArray(parsed) ? "array" : parsed === null ? "null" : typeof parsed}). Back up the file and run 'run402 init' to recreate it.`);
|
|
75
75
|
}
|
|
76
76
|
const data = parsed;
|
|
77
77
|
if (typeof data.address !== "string" || !ADDRESS_RE.test(data.address)) {
|
|
78
|
-
throw new Error("
|
|
78
|
+
throw new Error("wallet.json missing valid 'address' (expected 0x-prefixed 40-hex string). " +
|
|
79
79
|
"Back up the file and run 'run402 init' to recreate it.");
|
|
80
80
|
}
|
|
81
81
|
if (typeof data.privateKey !== "string" || !PRIVATE_KEY_RE.test(data.privateKey)) {
|
|
82
|
-
throw new Error("
|
|
82
|
+
throw new Error("wallet.json missing valid 'privateKey' (expected 0x-prefixed 64-hex string). " +
|
|
83
83
|
"Back up the file and run 'run402 init' to recreate it.");
|
|
84
84
|
}
|
|
85
85
|
return data;
|
|
86
86
|
}
|
|
87
|
-
export function
|
|
88
|
-
const p = path ??
|
|
87
|
+
export function saveWallet(data, path) {
|
|
88
|
+
const p = path ?? getWalletPath();
|
|
89
89
|
const dir = dirname(p);
|
|
90
90
|
mkdirSync(dir, { recursive: true });
|
|
91
|
-
const tmp = join(dir, `.
|
|
91
|
+
const tmp = join(dir, `.wallet.${randomBytes(4).toString("hex")}.tmp`);
|
|
92
92
|
writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });
|
|
93
93
|
renameSync(tmp, p);
|
|
94
94
|
chmodSync(p, 0o600);
|
|
95
95
|
}
|
|
96
|
-
//# sourceMappingURL=
|
|
96
|
+
//# sourceMappingURL=wallet.js.map
|
|
@@ -4,12 +4,12 @@ import { randomBytes, createHash } from "node:crypto";
|
|
|
4
4
|
import { getConfigBaseDir } from "./config.js";
|
|
5
5
|
const DEFAULT_TTL_MS = 30 * 60 * 1000;
|
|
6
6
|
/**
|
|
7
|
-
* Path to the approval cache: `{base}/write-
|
|
8
|
-
* `
|
|
7
|
+
* Path to the approval cache: `{base}/write-approvals.json`.
|
|
8
|
+
* `RUN402_WRITE_APPROVALS_PATH` overrides for testing.
|
|
9
9
|
*/
|
|
10
|
-
export function
|
|
11
|
-
return (process.env.
|
|
12
|
-
join(getConfigBaseDir(), "write-
|
|
10
|
+
export function getWriteApprovalsPath() {
|
|
11
|
+
return (process.env.RUN402_WRITE_APPROVALS_PATH ||
|
|
12
|
+
join(getConfigBaseDir(), "write-approvals.json"));
|
|
13
13
|
}
|
|
14
14
|
/** Stable short hash binding an approval to a control-plane session token. */
|
|
15
15
|
export function hashControlPlaneSession(token) {
|
|
@@ -34,8 +34,8 @@ function isApproval(x) {
|
|
|
34
34
|
if (!x || typeof x !== "object")
|
|
35
35
|
return false;
|
|
36
36
|
const a = x;
|
|
37
|
-
return (typeof a.
|
|
38
|
-
a.
|
|
37
|
+
return (typeof a.write_approval_token === "string" &&
|
|
38
|
+
a.write_approval_token.length > 0 &&
|
|
39
39
|
typeof a.action === "string" &&
|
|
40
40
|
typeof a.api_origin === "string" &&
|
|
41
41
|
typeof a.control_plane_session_hash === "string" &&
|
|
@@ -48,7 +48,7 @@ function isApproval(x) {
|
|
|
48
48
|
* is wrong, so a corrupted cache surfaces a clear fix-it.
|
|
49
49
|
*/
|
|
50
50
|
export function readApprovals(path) {
|
|
51
|
-
const p = path ??
|
|
51
|
+
const p = path ?? getWriteApprovalsPath();
|
|
52
52
|
if (!existsSync(p))
|
|
53
53
|
return [];
|
|
54
54
|
selfHealPermissions(p);
|
|
@@ -67,19 +67,19 @@ export function readApprovals(path) {
|
|
|
67
67
|
return [];
|
|
68
68
|
}
|
|
69
69
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
70
|
-
throw new Error("write-
|
|
70
|
+
throw new Error("write-approvals.json must contain a JSON object. Delete it and re-run 'run402 approve' to recreate it.");
|
|
71
71
|
}
|
|
72
72
|
const approvals = parsed.approvals;
|
|
73
73
|
if (!Array.isArray(approvals)) {
|
|
74
|
-
throw new Error("write-
|
|
74
|
+
throw new Error("write-approvals.json missing an 'approvals' array. Delete it and re-run 'run402 approve'.");
|
|
75
75
|
}
|
|
76
76
|
return approvals.filter(isApproval);
|
|
77
77
|
}
|
|
78
78
|
function writeApprovals(approvals, path) {
|
|
79
|
-
const p = path ??
|
|
79
|
+
const p = path ?? getWriteApprovalsPath();
|
|
80
80
|
const dir = dirname(p);
|
|
81
81
|
mkdirSync(dir, { recursive: true });
|
|
82
|
-
const tmp = join(dir, `.write-
|
|
82
|
+
const tmp = join(dir, `.write-approvals.${randomBytes(4).toString("hex")}.tmp`);
|
|
83
83
|
writeFileSync(tmp, JSON.stringify({ approvals }, null, 2), { mode: 0o600 });
|
|
84
84
|
renameSync(tmp, p);
|
|
85
85
|
chmodSync(p, 0o600);
|
|
@@ -103,9 +103,9 @@ export function saveApproval(approval, path) {
|
|
|
103
103
|
existing.push(approval);
|
|
104
104
|
writeApprovals(existing, path);
|
|
105
105
|
}
|
|
106
|
-
/** Delete the whole approval cache — local half of `
|
|
106
|
+
/** Delete the whole approval cache — local half of `run402 logout`. Idempotent. */
|
|
107
107
|
export function clearApprovals(path) {
|
|
108
|
-
const p = path ??
|
|
108
|
+
const p = path ?? getWriteApprovalsPath();
|
|
109
109
|
try {
|
|
110
110
|
rmSync(p, { force: true });
|
|
111
111
|
}
|
|
@@ -137,7 +137,7 @@ export function loadLiveApproval(q, path, nowMs = Date.now()) {
|
|
|
137
137
|
}
|
|
138
138
|
/** Parse a gateway-returned session expiry (ISO string or epoch) to epoch ms. */
|
|
139
139
|
function sessionExpiryMs(session, nowMs) {
|
|
140
|
-
const raw = session?.expires_at ?? session?.absolute_expires_at;
|
|
140
|
+
const raw = session?.idle_expires_at ?? session?.expires_at ?? session?.absolute_expires_at;
|
|
141
141
|
if (typeof raw === "number" && Number.isFinite(raw))
|
|
142
142
|
return raw > 1e12 ? raw : raw * 1000;
|
|
143
143
|
if (typeof raw === "string") {
|
|
@@ -157,9 +157,9 @@ export function approvalFromTokenResponse(resp, binding, nowMs = Date.now()) {
|
|
|
157
157
|
? resp.session.amr.filter((a) => typeof a === "string")
|
|
158
158
|
: undefined;
|
|
159
159
|
return {
|
|
160
|
-
|
|
161
|
-
token_type: typeof resp.token_type === "string" ? resp.token_type : "
|
|
162
|
-
header: typeof resp.header === "string" ? resp.header : "X-Run402-Write-
|
|
160
|
+
write_approval_token: resp.write_approval_token,
|
|
161
|
+
token_type: typeof resp.token_type === "string" ? resp.token_type : "write_approval",
|
|
162
|
+
header: typeof resp.header === "string" ? resp.header : "X-Run402-Write-Approval",
|
|
163
163
|
action: binding.action,
|
|
164
164
|
...(binding.target.org_id ? { org_id: binding.target.org_id } : {}),
|
|
165
165
|
...(binding.target.project_id ? { project_id: binding.target.project_id } : {}),
|
|
@@ -171,4 +171,4 @@ export function approvalFromTokenResponse(resp, binding, nowMs = Date.now()) {
|
|
|
171
171
|
minted_at: nowMs,
|
|
172
172
|
};
|
|
173
173
|
}
|
|
174
|
-
//# sourceMappingURL=write-
|
|
174
|
+
//# sourceMappingURL=write-approvals.js.map
|
package/gitvault-surface.json
CHANGED
package/lib/admin.mjs
CHANGED
|
@@ -30,7 +30,7 @@ Subcommands:
|
|
|
30
30
|
lease-perpetual above.
|
|
31
31
|
|
|
32
32
|
Notes:
|
|
33
|
-
- All admin subcommands require platform-admin auth — the configured
|
|
33
|
+
- All admin subcommands require platform-admin auth — the configured wallet
|
|
34
34
|
wallet must be a platform admin, or an admin OAuth session cookie must be
|
|
35
35
|
available. Regular project owners cannot run these.
|
|
36
36
|
- Output is JSON.
|
package/lib/agent.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { walletAuthHeaders } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs, validateWebhookUrl, failUnknownSubcommand } from "./argparse.mjs";
|
|
@@ -12,7 +12,7 @@ Usage:
|
|
|
12
12
|
run402 agent passkey enroll
|
|
13
13
|
|
|
14
14
|
Notes:
|
|
15
|
-
- Free with
|
|
15
|
+
- Free with SIWX auth
|
|
16
16
|
- Registers contact info so Run402 can reach your agent
|
|
17
17
|
- Only name is required; email and webhook are optional
|
|
18
18
|
- New or changed emails start reply verification
|
|
@@ -39,7 +39,7 @@ Options:
|
|
|
39
39
|
agent
|
|
40
40
|
|
|
41
41
|
Notes:
|
|
42
|
-
- Free with
|
|
42
|
+
- Free with SIWX auth (run 'run402 init' first)
|
|
43
43
|
- Registers contact info so Run402 can reach your agent
|
|
44
44
|
- New or changed emails start a reply challenge and return
|
|
45
45
|
assurance_level without exposing the challenge secret
|
|
@@ -65,13 +65,15 @@ Usage:
|
|
|
65
65
|
Sends or reuses a reply challenge for the active contact email. The challenge
|
|
66
66
|
secret is emailed and never printed.
|
|
67
67
|
`,
|
|
68
|
-
passkey: `run402 agent passkey —
|
|
68
|
+
passkey: `run402 agent passkey — Bind a passkey to the contact
|
|
69
69
|
|
|
70
70
|
Usage:
|
|
71
71
|
run402 agent passkey enroll
|
|
72
72
|
|
|
73
|
-
Sends a short-lived Run402
|
|
74
|
-
contact email. Requires assurance_level=email_verified first.
|
|
73
|
+
Sends a short-lived Run402 passkey enrollment link to the verified
|
|
74
|
+
contact email. Requires assurance_level=email_verified first. A person
|
|
75
|
+
signed in with 'run402 login' already has passkey assurance for a contact
|
|
76
|
+
whose verified email their principal holds.
|
|
75
77
|
`,
|
|
76
78
|
};
|
|
77
79
|
|
|
@@ -93,12 +95,12 @@ async function contact(args) {
|
|
|
93
95
|
if (!name) {
|
|
94
96
|
fail({ code: "BAD_USAGE", message: "Missing --name <name>" });
|
|
95
97
|
}
|
|
96
|
-
// validate webhook scheme locally BEFORE the
|
|
97
|
-
// bad URLs fail fast even without
|
|
98
|
+
// validate webhook scheme locally BEFORE the wallet check so
|
|
99
|
+
// bad URLs fail fast even without a wallet configured. No-op when
|
|
98
100
|
// --webhook is omitted (it's optional).
|
|
99
101
|
validateWebhookUrl(webhook, "--webhook");
|
|
100
|
-
// Preserve the aggressive early exit when no
|
|
101
|
-
|
|
102
|
+
// Preserve the aggressive early exit when no local wallet is configured.
|
|
103
|
+
walletAuthHeaders("/agent/v1/contact");
|
|
102
104
|
|
|
103
105
|
try {
|
|
104
106
|
const data = await getSdk().admin.setAgentContact({
|
|
@@ -119,20 +121,20 @@ async function status(args = []) {
|
|
|
119
121
|
if (extra.length > 0) {
|
|
120
122
|
fail({ code: "BAD_USAGE", message: `Unexpected argument for agent status: ${extra[0]}` });
|
|
121
123
|
}
|
|
122
|
-
|
|
124
|
+
walletAuthHeaders("/agent/v1/contact/status");
|
|
123
125
|
|
|
124
126
|
try {
|
|
125
127
|
const sdk = getSdk();
|
|
126
128
|
const data = await sdk.admin.getAgentContactStatus();
|
|
127
129
|
// v1.56: augment the response with email_verification.last_challenge from
|
|
128
|
-
// /agent/v1/
|
|
130
|
+
// /agent/v1/me/status so the reader sees per-attempt status
|
|
129
131
|
// (trust_rejected with which verdicts, attempts remaining, hint) without
|
|
130
132
|
// a second command. Best-effort — older gateways without the v1.55+ route
|
|
131
133
|
// skip the augment silently.
|
|
132
134
|
try {
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
135
|
-
data.email_verification =
|
|
135
|
+
const meStatus = await sdk.me.status();
|
|
136
|
+
if (meStatus && meStatus.email_verification) {
|
|
137
|
+
data.email_verification = meStatus.email_verification;
|
|
136
138
|
}
|
|
137
139
|
} catch {
|
|
138
140
|
// Older gateway — keep the original response shape.
|
|
@@ -150,7 +152,7 @@ async function verifyEmail(args = []) {
|
|
|
150
152
|
if (extra.length > 0) {
|
|
151
153
|
fail({ code: "BAD_USAGE", message: `Unexpected argument for agent verify-email: ${extra[0]}` });
|
|
152
154
|
}
|
|
153
|
-
|
|
155
|
+
walletAuthHeaders("/agent/v1/contact/verify-email");
|
|
154
156
|
|
|
155
157
|
try {
|
|
156
158
|
const data = await getSdk().admin.verifyAgentContactEmail();
|
|
@@ -171,10 +173,10 @@ async function passkey(args) {
|
|
|
171
173
|
if (action !== "enroll") {
|
|
172
174
|
fail({ code: "BAD_USAGE", message: "Usage: run402 agent passkey enroll" });
|
|
173
175
|
}
|
|
174
|
-
|
|
176
|
+
walletAuthHeaders("/agent/v1/contact/passkey/enroll");
|
|
175
177
|
|
|
176
178
|
try {
|
|
177
|
-
const data = await getSdk().admin.
|
|
179
|
+
const data = await getSdk().admin.startContactPasskeyEnrollment();
|
|
178
180
|
console.log(JSON.stringify(data, null, 2));
|
|
179
181
|
} catch (err) {
|
|
180
182
|
reportSdkError(err);
|
package/lib/approve.mjs
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `run402 approve --action <capability> (--org <org_id> | --project <project_id>)`
|
|
3
|
+
* — mint a passkey-signed write approval scoped to one (action, target) so a
|
|
4
|
+
* person signed in without a wallet can provision, deploy, or write secrets.
|
|
5
|
+
* See sign-in.mjs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { fail } from "./sdk-errors.mjs";
|
|
9
|
+
import { normalizeArgv, hasHelp, assertKnownFlags, flagValue, positionalArgs } from "./argparse.mjs";
|
|
10
|
+
import { APPROVAL_ACTIONS, mintApproval } from "./sign-in.mjs";
|
|
11
|
+
|
|
12
|
+
const HELP = `run402 approve — mint a write approval for one action on one target
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
run402 approve --action <capability> (--org <org_id> | --project <project_id>) [--no-open]
|
|
16
|
+
|
|
17
|
+
A person signed in with 'run402 login' (no wallet) needs a write approval to
|
|
18
|
+
provision a project, deploy, or write secrets. It is signed with your passkey,
|
|
19
|
+
covers one action on one org or project, lasts 30 minutes idle (4 hours at
|
|
20
|
+
most), and dies with the sign-in session. It never counts as a step-up.
|
|
21
|
+
From an interactive terminal the CLI opens this ceremony for you when a write
|
|
22
|
+
needs it; agents and CI relay the command instead.
|
|
23
|
+
|
|
24
|
+
Actions:
|
|
25
|
+
org.project.create --org <org_id>
|
|
26
|
+
project.deploy --project <project_id>
|
|
27
|
+
project.secret.write --project <project_id>
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
--no-open Do not open the browser; print the URL only.
|
|
31
|
+
|
|
32
|
+
Notes:
|
|
33
|
+
- Requires 'run402 login' (a device-grade session cannot approve).
|
|
34
|
+
- 'run402 whoami' lists the approvals cached on this machine.
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export async function run(args = []) {
|
|
38
|
+
args = normalizeArgv(args);
|
|
39
|
+
if (hasHelp(args)) {
|
|
40
|
+
console.log(HELP);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
const valueFlags = ["--action", "--org", "--project"];
|
|
44
|
+
assertKnownFlags(args, ["--help", "-h", "--no-open", ...valueFlags], valueFlags);
|
|
45
|
+
const extra = positionalArgs(args, valueFlags);
|
|
46
|
+
if (extra.length > 0) {
|
|
47
|
+
fail({ code: "BAD_USAGE", message: `Unexpected argument for approve: ${extra[0]}`, hint: "Use `run402 approve --action <capability> (--org <org_id> | --project <project_id>)`." });
|
|
48
|
+
}
|
|
49
|
+
const action = flagValue(args, "--action");
|
|
50
|
+
const org = flagValue(args, "--org");
|
|
51
|
+
const project = flagValue(args, "--project");
|
|
52
|
+
|
|
53
|
+
if (!action || !APPROVAL_ACTIONS[action]) {
|
|
54
|
+
fail({
|
|
55
|
+
code: "BAD_FLAG",
|
|
56
|
+
message: `--action must be one of: ${Object.keys(APPROVAL_ACTIONS).join(", ")}`,
|
|
57
|
+
details: { flag: "--action", value: action, allowed: Object.keys(APPROVAL_ACTIONS) },
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
const scope = APPROVAL_ACTIONS[action];
|
|
61
|
+
if (scope === "org" && !org) fail({ code: "BAD_FLAG", message: `--org <org_id> is required for --action ${action}` });
|
|
62
|
+
if (scope === "project" && !project) fail({ code: "BAD_FLAG", message: `--project <project_id> is required for --action ${action}` });
|
|
63
|
+
if (scope === "org" && project) fail({ code: "BAD_FLAG", message: `--project is not valid for org-scoped --action ${action}; use --org` });
|
|
64
|
+
if (scope === "project" && org) fail({ code: "BAD_FLAG", message: `--org is not valid for project-scoped --action ${action}; use --project` });
|
|
65
|
+
|
|
66
|
+
const approval = await mintApproval({
|
|
67
|
+
action,
|
|
68
|
+
orgId: org ?? undefined,
|
|
69
|
+
projectId: project ?? undefined,
|
|
70
|
+
noOpen: args.includes("--no-open"),
|
|
71
|
+
});
|
|
72
|
+
console.log(
|
|
73
|
+
JSON.stringify({
|
|
74
|
+
approved: true,
|
|
75
|
+
action,
|
|
76
|
+
...(org ? { org_id: org } : {}),
|
|
77
|
+
...(project ? { project_id: project } : {}),
|
|
78
|
+
expires_at: new Date(approval.expires_at).toISOString(),
|
|
79
|
+
}),
|
|
80
|
+
);
|
|
81
|
+
}
|
package/lib/apps.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { walletAuthHeaders, saveProject } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
4
|
import { assertAllowedValue, assertKnownFlags, flagValue, normalizeArgv, positionalArgs, resolveProjectSelector, failUnknownSubcommand } from "./argparse.mjs";
|
|
@@ -185,8 +185,8 @@ async function fork(versionId, name, args) {
|
|
|
185
185
|
fail({ code: "BAD_USAGE", message: `Unexpected argument for apps fork: ${positionals[expected]}` });
|
|
186
186
|
}
|
|
187
187
|
const opts = { subdomain: flagValue(parsedArgs, "--subdomain") ?? undefined };
|
|
188
|
-
// Preserve the aggressive early exit when no
|
|
189
|
-
|
|
188
|
+
// Preserve the aggressive early exit when no local wallet is configured.
|
|
189
|
+
walletAuthHeaders("/fork/v1");
|
|
190
190
|
|
|
191
191
|
try {
|
|
192
192
|
const data = await getSdk().apps.fork({
|
package/lib/assets.mjs
CHANGED
|
@@ -196,7 +196,7 @@ Options:
|
|
|
196
196
|
Output:
|
|
197
197
|
- Prints the JSON envelope on stdout (parseable by agent shell loops).
|
|
198
198
|
- Vantage caveat ("# probed once from gateway-us-east-1; not a global view")
|
|
199
|
-
on stderr — visible to TTY
|
|
199
|
+
on stderr — visible to a person at a TTY, ignored by piped consumers.
|
|
200
200
|
|
|
201
201
|
Exit codes:
|
|
202
202
|
0 observed SHA matches the gateway's expected SHA
|
|
@@ -575,7 +575,7 @@ async function diagnose(argv) {
|
|
|
575
575
|
const env = await getSdk().assets.diagnoseUrl(resolvedId, url);
|
|
576
576
|
// Always print the JSON envelope for agent consumption (parseable).
|
|
577
577
|
console.log(JSON.stringify(toCliDiagnoseEnvelope(env), null, 2));
|
|
578
|
-
// Vantage caveat to stderr so a TTY
|
|
578
|
+
// Vantage caveat to stderr so a person at a TTY sees it; agent shell loops
|
|
579
579
|
// that pipe stdout into another tool aren't affected.
|
|
580
580
|
process.stderr.write(
|
|
581
581
|
`\n# probed once from ${env.vantage}; not a global view\n`,
|
package/lib/billing.mjs
CHANGED
|
@@ -12,9 +12,9 @@ Subcommands:
|
|
|
12
12
|
create-email <email> Create an email organization
|
|
13
13
|
link-wallet [<org_id>] <wallet_address> Link a wallet to an email organization
|
|
14
14
|
checkout <identifier> --product <p> Create an org checkout
|
|
15
|
-
topup <identifier> --sats <n> [--wait] [--qr <file.png>] Top up the
|
|
15
|
+
topup <identifier> --sats <n> [--wait] [--qr <file.png>] Top up the allowance over Lightning (a bolt11 invoice; --qr also writes a scannable PNG)
|
|
16
16
|
auto-recharge [<org_id>] <on|off> [--threshold <n>]
|
|
17
|
-
balance <identifier>
|
|
17
|
+
balance <identifier> The allowance, by organization id (UUID), wallet (0x...), or email
|
|
18
18
|
history <identifier> [--limit <n>] Ledger history by organization id (UUID), wallet, or email
|
|
19
19
|
|
|
20
20
|
Examples:
|
|
@@ -79,14 +79,14 @@ Options:
|
|
|
79
79
|
|
|
80
80
|
Auth:
|
|
81
81
|
Requires SIWX from a wallet linked to the organization (signed automatically from
|
|
82
|
-
the local
|
|
82
|
+
the local wallet), or an admin key. Email lookups require an admin key.
|
|
83
83
|
|
|
84
84
|
Examples:
|
|
85
85
|
run402 billing history user@example.com
|
|
86
86
|
run402 billing history 0x1234... --limit 100
|
|
87
87
|
run402 billing history 00000000-0000-4000-8000-000000000001
|
|
88
88
|
`,
|
|
89
|
-
balance: `run402 billing balance — Show
|
|
89
|
+
balance: `run402 billing balance — Show an organization's allowance
|
|
90
90
|
|
|
91
91
|
Usage:
|
|
92
92
|
run402 billing balance <identifier>
|
|
@@ -97,7 +97,7 @@ Arguments:
|
|
|
97
97
|
|
|
98
98
|
Auth:
|
|
99
99
|
Requires SIWX from a wallet linked to the organization (signed automatically from
|
|
100
|
-
the local
|
|
100
|
+
the local wallet), or an admin key. Email lookups require an admin key.
|
|
101
101
|
|
|
102
102
|
Examples:
|
|
103
103
|
run402 billing balance user@example.com
|
|
@@ -262,7 +262,7 @@ async function topup(args) {
|
|
|
262
262
|
});
|
|
263
263
|
console.log(JSON.stringify(final, null, 2));
|
|
264
264
|
if (final.status === "paid" || final.status === "paid_late") {
|
|
265
|
-
console.error(`Received — ${final.amount_sats} sats
|
|
265
|
+
console.error(`Received — ${final.amount_sats} sats added $${(final.amount_usd_micros / 1_000_000).toFixed(2)} to the allowance${final.status === "paid_late" ? " (paid after the invoice expired; still credited)" : ""}.`);
|
|
266
266
|
} else if (final.status === "expired") {
|
|
267
267
|
console.error("No payment was observed before the invoice expired. If you already paid, it still credits on its own (the gateway keeps checking for an hour after expiry) — read the top-up again, or rerun with the same --idempotency-key. Otherwise mint a fresh one with the same command.");
|
|
268
268
|
process.exitCode = 2;
|
package/lib/buzz-doctor.mjs
CHANGED
|
@@ -689,7 +689,7 @@ function consoleRepair() {
|
|
|
689
689
|
type: "restore_run402_console_access",
|
|
690
690
|
surface: "buzz_settings",
|
|
691
691
|
command: "Open Buzz Desktop > Settings > Agents, select this agent, allow HTTPS access to https://console.run402.com, restart the agent, then rerun Run402 setup.",
|
|
692
|
-
why: "The no-terminal human adoption
|
|
692
|
+
why: "The no-terminal human adoption offer requires the Run402 console origin.",
|
|
693
693
|
});
|
|
694
694
|
}
|
|
695
695
|
|
|
@@ -69,7 +69,7 @@ What can be routed:
|
|
|
69
69
|
Omitting --event-type/--event-class routes every registered type / every
|
|
70
70
|
non-forbidden class. There is no empty filter: the gateway rejects [].
|
|
71
71
|
|
|
72
|
-
Buzz is never a deadman channel: mandatory
|
|
72
|
+
Buzz is never a deadman channel: mandatory owner notifications keep their
|
|
73
73
|
human paths (email, Telegram) regardless of route state, and a Buzz delivery
|
|
74
74
|
acknowledges nothing.
|
|
75
75
|
|
package/lib/buzz.mjs
CHANGED
|
@@ -122,7 +122,7 @@ async function adopt(args) {
|
|
|
122
122
|
assertKnownFlags(a, values, values);
|
|
123
123
|
const [id] = requirePositionalCount(a, values, { min: 1, max: 1, command: "run402 buzz adopt complete <buzzha_id> --event-file <json>" });
|
|
124
124
|
const event = readJsonFile(requiredFlag(a, "--event-file"), "--event-file");
|
|
125
|
-
return invoke(() => getSdk({ authMode: "
|
|
125
|
+
return invoke(() => getSdk({ authMode: "session" }).buzz.humanAdoptions.complete(id, event, flagValue(a, "--idempotency-key") ?? undefined));
|
|
126
126
|
}
|
|
127
127
|
if (operation === "cancel") {
|
|
128
128
|
const a = normalizeArgv(rest);
|