run402 4.120.0 → 4.122.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.
Files changed (93) hide show
  1. package/core-dist/wallet-auth.js +77 -38
  2. package/core-dist/wallet.js +21 -0
  3. package/lib/ci.mjs +1 -1
  4. package/lib/config.mjs +8 -0
  5. package/lib/contacts.mjs +3 -3
  6. package/lib/pay.mjs +9 -1
  7. package/lib/session-proofs.mjs +8 -4
  8. package/lib/subscriptions.mjs +2 -2
  9. package/lib/wallets.mjs +41 -1
  10. package/lib/webhook-secret.mjs +1 -1
  11. package/package.json +2 -1
  12. package/sdk/core-dist/wallet-auth.js +77 -38
  13. package/sdk/core-dist/wallet.js +21 -0
  14. package/sdk/dist/credentials.d.ts +15 -1
  15. package/sdk/dist/credentials.d.ts.map +1 -1
  16. package/sdk/dist/errors.d.ts +14 -0
  17. package/sdk/dist/errors.d.ts.map +1 -1
  18. package/sdk/dist/errors.js +22 -0
  19. package/sdk/dist/errors.js.map +1 -1
  20. package/sdk/dist/index.d.ts +1 -1
  21. package/sdk/dist/index.d.ts.map +1 -1
  22. package/sdk/dist/index.js +1 -1
  23. package/sdk/dist/index.js.map +1 -1
  24. package/sdk/dist/namespaces/deploy.d.ts.map +1 -1
  25. package/sdk/dist/namespaces/deploy.js +3 -0
  26. package/sdk/dist/namespaces/deploy.js.map +1 -1
  27. package/sdk/dist/namespaces/deploy.types.d.ts +4 -2
  28. package/sdk/dist/namespaces/deploy.types.d.ts.map +1 -1
  29. package/sdk/dist/namespaces/deploy.types.js +4 -0
  30. package/sdk/dist/namespaces/deploy.types.js.map +1 -1
  31. package/sdk/dist/namespaces/pay.d.ts +7 -0
  32. package/sdk/dist/namespaces/pay.d.ts.map +1 -1
  33. package/sdk/dist/namespaces/pay.js +5 -0
  34. package/sdk/dist/namespaces/pay.js.map +1 -1
  35. package/sdk/dist/node/_paid-stack.d.ts +26 -3
  36. package/sdk/dist/node/_paid-stack.d.ts.map +1 -1
  37. package/sdk/dist/node/_paid-stack.js +19 -0
  38. package/sdk/dist/node/_paid-stack.js.map +1 -1
  39. package/sdk/dist/node/ci.d.ts +1 -1
  40. package/sdk/dist/node/ci.d.ts.map +1 -1
  41. package/sdk/dist/node/ci.js +8 -6
  42. package/sdk/dist/node/ci.js.map +1 -1
  43. package/sdk/dist/node/credentials.d.ts.map +1 -1
  44. package/sdk/dist/node/credentials.js +5 -4
  45. package/sdk/dist/node/credentials.js.map +1 -1
  46. package/sdk/dist/node/index.d.ts +2 -1
  47. package/sdk/dist/node/index.d.ts.map +1 -1
  48. package/sdk/dist/node/index.js +2 -1
  49. package/sdk/dist/node/index.js.map +1 -1
  50. package/sdk/dist/node/lightning-paid-fetch.d.ts.map +1 -1
  51. package/sdk/dist/node/lightning-paid-fetch.js +2 -0
  52. package/sdk/dist/node/lightning-paid-fetch.js.map +1 -1
  53. package/sdk/dist/node/mpp-pay-executor.d.ts +40 -0
  54. package/sdk/dist/node/mpp-pay-executor.d.ts.map +1 -0
  55. package/sdk/dist/node/mpp-pay-executor.js +352 -0
  56. package/sdk/dist/node/mpp-pay-executor.js.map +1 -0
  57. package/sdk/dist/node/org-adopt.d.ts +1 -1
  58. package/sdk/dist/node/org-adopt.d.ts.map +1 -1
  59. package/sdk/dist/node/org-adopt.js +9 -7
  60. package/sdk/dist/node/org-adopt.js.map +1 -1
  61. package/sdk/dist/node/paid-fetch.d.ts +2 -1
  62. package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
  63. package/sdk/dist/node/paid-fetch.js +50 -22
  64. package/sdk/dist/node/paid-fetch.js.map +1 -1
  65. package/sdk/dist/node/payment-attempts.d.ts +2 -2
  66. package/sdk/dist/node/payment-attempts.d.ts.map +1 -1
  67. package/sdk/dist/node/payment-attempts.js +2 -2
  68. package/sdk/dist/node/payment-attempts.js.map +1 -1
  69. package/sdk/dist/node/payment-offers.d.ts +35 -0
  70. package/sdk/dist/node/payment-offers.d.ts.map +1 -0
  71. package/sdk/dist/node/payment-offers.js +145 -0
  72. package/sdk/dist/node/payment-offers.js.map +1 -0
  73. package/sdk/dist/node/signers/index.d.ts +26 -0
  74. package/sdk/dist/node/signers/index.d.ts.map +1 -0
  75. package/sdk/dist/node/signers/index.js +35 -0
  76. package/sdk/dist/node/signers/index.js.map +1 -0
  77. package/sdk/dist/node/signers/kms.d.ts +54 -0
  78. package/sdk/dist/node/signers/kms.d.ts.map +1 -0
  79. package/sdk/dist/node/signers/kms.js +236 -0
  80. package/sdk/dist/node/signers/kms.js.map +1 -0
  81. package/sdk/dist/node/wallets-send.d.ts +62 -0
  82. package/sdk/dist/node/wallets-send.d.ts.map +1 -0
  83. package/sdk/dist/node/wallets-send.js +149 -0
  84. package/sdk/dist/node/wallets-send.js.map +1 -0
  85. package/sdk/dist/node/wallets.d.ts +33 -5
  86. package/sdk/dist/node/wallets.d.ts.map +1 -1
  87. package/sdk/dist/node/wallets.js +78 -12
  88. package/sdk/dist/node/wallets.js.map +1 -1
  89. package/sdk/dist/secret-gate.d.ts +1 -1
  90. package/sdk/dist/secret-gate.d.ts.map +1 -1
  91. package/sdk/dist/secret-gate.js +7 -2
  92. package/sdk/dist/secret-gate.js.map +1 -1
  93. package/vault-surface.json +1 -1
@@ -6,7 +6,7 @@ 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 { readWallet } from "./wallet.js";
9
+ import { isSignerWallet, readWallet } from "./wallet.js";
10
10
  import { getApiBase } from "./config.js";
11
11
  /**
12
12
  * EIP-55 mixed-case checksum encoding.
@@ -57,6 +57,33 @@ function personalSign(privateKeyHex, address, message) {
57
57
  const vHex = (recovery + 27).toString(16).padStart(2, "0");
58
58
  return "0x" + r + s + vHex;
59
59
  }
60
+ export function prepareSIWx(opts) {
61
+ const message = formatSIWEMessage(opts, opts.address);
62
+ return {
63
+ message,
64
+ finish(signature) {
65
+ const payload = {
66
+ domain: opts.domain,
67
+ address: toChecksumAddress(opts.address),
68
+ statement: opts.statement,
69
+ uri: opts.uri,
70
+ version: opts.version ?? "1",
71
+ chainId: payloadChainId(opts.chainId),
72
+ type: opts.type ?? "eip191",
73
+ nonce: opts.nonce,
74
+ issuedAt: opts.issuedAt,
75
+ expirationTime: opts.expirationTime,
76
+ signature,
77
+ };
78
+ if (opts.resources !== undefined) {
79
+ payload.resources = opts.resources;
80
+ }
81
+ return {
82
+ "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64"),
83
+ };
84
+ },
85
+ };
86
+ }
60
87
  /**
61
88
  * Format an EIP-4361 (SIWE) message. Must be byte-for-byte compatible
62
89
  * with the `siwe` library's message format used server-side for verification.
@@ -85,29 +112,54 @@ export function formatSIWEMessage(opts, address) {
85
112
  }
86
113
  return lines.join("\n");
87
114
  }
115
+ /** SIWX headers signed with a local private key (synchronous). */
88
116
  export function buildSIWxAuthHeaders(opts) {
89
- const message = formatSIWEMessage(opts, opts.wallet.address);
90
- const signature = personalSign(opts.wallet.privateKey, opts.wallet.address, message);
91
- const payload = {
92
- domain: opts.domain,
93
- address: toChecksumAddress(opts.wallet.address),
94
- statement: opts.statement,
95
- uri: opts.uri,
96
- version: opts.version ?? "1",
97
- chainId: payloadChainId(opts.chainId),
98
- type: opts.type ?? "eip191",
99
- nonce: opts.nonce,
100
- issuedAt: opts.issuedAt,
101
- expirationTime: opts.expirationTime,
102
- signature,
103
- };
104
- if (opts.resources !== undefined) {
105
- payload.resources = opts.resources;
117
+ if (!opts.wallet.privateKey) {
118
+ throw new SignerWalletError();
106
119
  }
120
+ const prepared = prepareSIWx({ ...opts, address: opts.wallet.address });
121
+ return prepared.finish(personalSign(opts.wallet.privateKey, opts.wallet.address, prepared.message));
122
+ }
123
+ /**
124
+ * Thrown where a synchronous signature is required but the wallet signs
125
+ * through a signer (`wallet.json` `signer`). Callers that can wait use
126
+ * `getWalletAuthHeadersAsync` with the SDK's signer instead.
127
+ */
128
+ export class SignerWalletError extends Error {
129
+ code = "SIGNER_WALLET_ASYNC";
130
+ constructor() {
131
+ super("This wallet signs through a signer (wallet.json 'signer'), which this synchronous path cannot use.");
132
+ this.name = "SignerWalletError";
133
+ }
134
+ }
135
+ function walletSIWxOptions(path) {
136
+ const apiBase = getApiBase();
137
+ const url = new URL(apiBase);
138
+ const now = new Date();
107
139
  return {
108
- "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64"),
140
+ domain: url.hostname,
141
+ uri: `${apiBase}${path}`,
142
+ statement: "Sign in to Run402",
143
+ chainId: "eip155:84532",
144
+ nonce: randomBytes(16).toString("hex"),
145
+ issuedAt: now.toISOString(),
146
+ expirationTime: new Date(now.getTime() + 5 * 60 * 1000).toISOString(),
109
147
  };
110
148
  }
149
+ /**
150
+ * SIWX headers for any wallet: a key-backed wallet signs locally; a
151
+ * signer-backed one through `signerFor(wallet)`. Null when no wallet exists.
152
+ */
153
+ export async function getWalletAuthHeadersAsync(path, walletPath, signerFor) {
154
+ const localWallet = readWallet(walletPath);
155
+ if (!localWallet || !localWallet.address)
156
+ return null;
157
+ if (!isSignerWallet(localWallet))
158
+ return getWalletAuthHeaders(path, walletPath);
159
+ const prepared = prepareSIWx({ ...walletSIWxOptions(path), address: localWallet.address });
160
+ const sign = await signerFor(localWallet);
161
+ return prepared.finish(await sign(prepared.message));
162
+ }
111
163
  /**
112
164
  * Get SIWX auth headers for the Run402 API.
113
165
  * Returns null if no local wallet is configured.
@@ -121,26 +173,13 @@ export function getWalletAuthHeaders(path, walletPath) {
121
173
  // this helper returns SIWxAuthHeaders | null. Re-throw so callers above
122
174
  // the CLI's wrapper (e.g. SDK paid-fetch) can decide whether to swallow it.
123
175
  const localWallet = readWallet(walletPath);
124
- if (!localWallet || !localWallet.address || !localWallet.privateKey)
176
+ if (!localWallet || !localWallet.address)
125
177
  return null;
126
- const apiBase = getApiBase();
127
- const url = new URL(apiBase);
128
- const domain = url.hostname;
129
- const uri = `${apiBase}${path}`;
130
- const nonce = randomBytes(16).toString("hex");
131
- const now = new Date();
132
- const issuedAt = now.toISOString();
133
- const expirationTime = new Date(now.getTime() + 5 * 60 * 1000).toISOString();
134
- return buildSIWxAuthHeaders({
135
- wallet: localWallet,
136
- domain,
137
- uri,
138
- statement: "Sign in to Run402",
139
- chainId: "eip155:84532",
140
- nonce,
141
- issuedAt,
142
- expirationTime,
143
- });
178
+ if (isSignerWallet(localWallet))
179
+ throw new SignerWalletError();
180
+ if (!localWallet.privateKey)
181
+ return null;
182
+ return buildSIWxAuthHeaders({ wallet: localWallet, ...walletSIWxOptions(path) });
144
183
  }
145
184
  function messageChainId(chainId) {
146
185
  if (typeof chainId === "number")
@@ -6,6 +6,12 @@ import { getWalletPath } from "./config.js";
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
+ // arn:aws:kms:<region>:<account>:key/<id>
10
+ export const KMS_KEY_ARN_RE = /^arn:aws[a-z-]*:kms:([a-z0-9-]+):\d{12}:key\/[A-Za-z0-9-]+$/;
11
+ /** True when the wallet signs through a signer instead of a local key. */
12
+ export function isSignerWallet(wallet) {
13
+ return wallet.signer !== undefined;
14
+ }
9
15
  /**
10
16
  * Load the agent wallet from disk.
11
17
  *
@@ -78,6 +84,21 @@ export function readWallet(path) {
78
84
  throw new Error("wallet.json missing valid 'address' (expected 0x-prefixed 40-hex string). " +
79
85
  "Back up the file and run 'run402 init' to recreate it.");
80
86
  }
87
+ if (data.privateKey !== undefined && data.signer !== undefined) {
88
+ throw new Error(`${p} has both 'privateKey' and 'signer'; a wallet is backed by exactly one. ` +
89
+ "Remove the one that does not belong to this wallet.");
90
+ }
91
+ if (data.signer !== undefined) {
92
+ const signer = data.signer;
93
+ if (!signer || typeof signer !== "object" || signer.kind !== "aws_kms" ||
94
+ typeof signer.key_arn !== "string" || !KMS_KEY_ARN_RE.test(signer.key_arn) ||
95
+ typeof signer.region !== "string" || signer.region.length === 0 ||
96
+ (signer.aws_profile !== undefined && (typeof signer.aws_profile !== "string" || signer.aws_profile.length === 0))) {
97
+ throw new Error(`${p} has a malformed 'signer' (expected { kind: "aws_kms", key_arn, region, aws_profile? }). ` +
98
+ "Re-import the wallet with 'run402 wallets import <name> --kms <key-arn>'.");
99
+ }
100
+ return data;
101
+ }
81
102
  if (typeof data.privateKey !== "string" || !PRIVATE_KEY_RE.test(data.privateKey)) {
82
103
  throw new Error("wallet.json missing valid 'privateKey' (expected 0x-prefixed 64-hex string). " +
83
104
  "Back up the file and run 'run402 init' to recreate it.");
package/lib/ci.mjs CHANGED
@@ -325,7 +325,7 @@ async function linkGithub(args) {
325
325
 
326
326
  let signedDelegation;
327
327
  try {
328
- signedDelegation = signCiDelegation(values, { apiBase: API });
328
+ signedDelegation = await signCiDelegation(values, { apiBase: API });
329
329
  } catch (err) {
330
330
  fail({
331
331
  code: "NO_WALLET",
package/lib/config.mjs CHANGED
@@ -81,7 +81,15 @@ export function saveWallet(data) {
81
81
  coreSaveWallet(data);
82
82
  }
83
83
 
84
+ /**
85
+ * The wallet's SIWX headers for `path`, or the NO_WALLET failure. Most
86
+ * callers use this only as a "a wallet is configured" check before handing
87
+ * the request to the SDK. A signer-backed wallet (KMS) signs asynchronously
88
+ * inside the SDK, so here it only proves the wallet exists and returns no
89
+ * headers; callers that need its signature use the SDK.
90
+ */
84
91
  export function walletAuthHeaders(path) {
92
+ if (readWallet()?.signer) return {};
85
93
  const headers = coreGetWalletAuthHeaders(path);
86
94
  if (!headers) {
87
95
  fail({
package/lib/contacts.mjs CHANGED
@@ -104,7 +104,7 @@ async function preferences(args) {
104
104
  }
105
105
  }
106
106
  try {
107
- const data = await getSdk().admin.setNotificationPreferences(patch, sessionProofs("/agent/v1/notifications/preferences"));
107
+ const data = await getSdk().admin.setNotificationPreferences(patch, await sessionProofs("/agent/v1/notifications/preferences"));
108
108
  console.log(JSON.stringify(data, null, 2));
109
109
  } catch (err) {
110
110
  reportSdkError(err);
@@ -185,7 +185,7 @@ async function channelsConnect(args) {
185
185
  fail({ code: "BAD_USAGE", message: `Unexpected argument for notifications channels connect: ${positionals[1]}` });
186
186
  }
187
187
 
188
- const proofs = sessionProofs("/agent/v1/notifications/channels/telegram");
188
+ const proofs = await sessionProofs("/agent/v1/notifications/channels/telegram");
189
189
  let pending;
190
190
  try {
191
191
  pending = await getSdk().admin.channels.connectTelegram(label ? { label } : {}, proofs);
@@ -234,7 +234,7 @@ async function channelsRevoke(args) {
234
234
  });
235
235
  walletAuthHeaders("/agent/v1/notifications/channels/telegram");
236
236
  try {
237
- console.log(JSON.stringify(await getSdk().admin.channels.revokeTelegram(bindingId, sessionProofs("/agent/v1/notifications/channels/telegram")), null, 2));
237
+ console.log(JSON.stringify(await getSdk().admin.channels.revokeTelegram(bindingId, await sessionProofs("/agent/v1/notifications/channels/telegram")), null, 2));
238
238
  } catch (err) {
239
239
  reportSdkError(err);
240
240
  }
package/lib/pay.mjs CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  positionalArgs,
12
12
  } from "./argparse.mjs";
13
13
 
14
- const HELP = `run402 pay — Call an x402-priced URL
14
+ const HELP = `run402 pay — Call an x402- or MPP-priced URL
15
15
 
16
16
  Usage:
17
17
  run402 pay <url> [options]
@@ -45,6 +45,14 @@ The command uses the same wallet and bounded buyer as the SDK. On the lightning
45
45
  rail a 402 naming an lnbtc entry on the wallet's network is paid in sats; one on
46
46
  another network is refused (LIGHTNING_NETWORK_MISMATCH) unless
47
47
  --lightning-pairing names a pairing on that network.
48
+ On the mpp rail a 402 carrying an MPP Tempo charge (WWW-Authenticate: Payment)
49
+ is paid in pathUSD on the first offered Tempo chain the wallet covers; the
50
+ result carries payment.protocol "mpp" and payment.method "tempo". A 402 that
51
+ offers nothing the wallet's rail pays fails PAYMENT_NETWORK_UNSUPPORTED with
52
+ details.offers and the "run402 init <rail> --switch-rail" that would pay it.
53
+ Over MPP the --idempotency-key is correlation only: an unknown outcome is
54
+ resolved by repeating this identical command from the same client, which
55
+ re-presents the same credential and is never charged twice.
48
56
  An unpriced URL is passed through with payment: null.
49
57
  On trusted Run402 PAYMENT_INTENT_PENDING, wait for Retry-After and repeat this
50
58
  identical command with the same payer and --idempotency-key. Never change the key.
@@ -9,13 +9,17 @@
9
9
  * signed in once is never asked to enroll a second passkey. With no live cached
10
10
  * session this returns `undefined` and the verb uses wallet auth alone.
11
11
  */
12
- import { walletAuthHeaders } from "./config.mjs";
12
+ import { walletAuthHeaders, walletFile } from "./config.mjs";
13
13
  import { loadLiveControlPlaneSession } from "../core-dist/control-plane-session.js";
14
+ import { getWalletAuthHeadersAsync } from "../core-dist/wallet-auth.js";
15
+ import { personalMessageSignerFor } from "#sdk/node";
14
16
 
15
17
  /** `{ siwx, token }` for `path`, or `undefined` when no live sign-in session is cached. */
16
- export function sessionProofs(path) {
17
- const siwx = walletAuthHeaders(path)["SIGN-IN-WITH-X"];
18
+ export async function sessionProofs(path) {
19
+ walletAuthHeaders(path); // NO_WALLET when none is configured
18
20
  const live = loadLiveControlPlaneSession();
19
21
  if (!live || !live.control_plane_session_token) return undefined;
20
- return { siwx, token: live.control_plane_session_token };
22
+ // A key-backed wallet signs locally; a signer-backed one (KMS) through its signer.
23
+ const headers = await getWalletAuthHeadersAsync(path, walletFile(), (w) => personalMessageSignerFor(w));
24
+ return { siwx: headers["SIGN-IN-WITH-X"], token: live.control_plane_session_token };
21
25
  }
@@ -136,7 +136,7 @@ async function rulesAdd(args) {
136
136
 
137
137
  walletAuthHeaders("/agent/v1/notifications/rules");
138
138
  try {
139
- console.log(JSON.stringify(await getSdk().admin.rules.create(input, sessionProofs("/agent/v1/notifications/rules")), null, 2));
139
+ console.log(JSON.stringify(await getSdk().admin.rules.create(input, await sessionProofs("/agent/v1/notifications/rules")), null, 2));
140
140
  } catch (err) {
141
141
  reportSdkError(err);
142
142
  }
@@ -162,7 +162,7 @@ async function rulesRm(args) {
162
162
  });
163
163
  walletAuthHeaders("/agent/v1/notifications/rules");
164
164
  try {
165
- console.log(JSON.stringify(await getSdk().admin.rules.delete(ruleId, sessionProofs("/agent/v1/notifications/rules")), null, 2));
165
+ console.log(JSON.stringify(await getSdk().admin.rules.delete(ruleId, await sessionProofs("/agent/v1/notifications/rules")), null, 2));
166
166
  } catch (err) {
167
167
  reportSdkError(err);
168
168
  }
package/lib/wallets.mjs CHANGED
@@ -20,7 +20,7 @@ import { ALL_ON_CHAIN_BALANCE_KEYS, readOnChainBalances, TEMPO_MODERATO } from "
20
20
  const HELP = `run402 wallets — manage named wallets (profiles)
21
21
 
22
22
  Usage:
23
- run402 wallets list List all wallets (local_label, server_label, address, rail, active)
23
+ run402 wallets list List all wallets (local_label, server_label, address, rail, signer, active)
24
24
  run402 wallets current Show the active wallet: how it was selected, address, rail, file path, faucet use
25
25
  run402 wallets fund Request testnet funds from the faucet into the active wallet (Base Sepolia or Tempo)
26
26
  run402 wallets balance The active wallet's on-chain balances, plus its organization's allowance
@@ -30,6 +30,11 @@ Usage:
30
30
  run402 wallets bind [<name>] Write ./.run402.json binding this directory to a wallet
31
31
  run402 wallets unbind Remove ./.run402.json
32
32
  run402 wallets import <name> --key <path|-> Adopt an existing private key as a named wallet
33
+ run402 wallets import <name> --kms <key-arn> [--aws-profile <p>]
34
+ Adopt an AWS KMS key by reference (no secret on disk)
35
+ run402 wallets send <amount> --to <address> [--from <smart-account>] [--network <n>] [--confirm]
36
+ Send USDC (base, base-sepolia) or pathUSD (tempo, tempo-moderato)
37
+ from the active wallet; previews without --confirm
33
38
  run402 wallets rm <name> --yes Delete a wallet and its keys (requires --yes)
34
39
  run402 wallets lightning status The active wallet's Lightning wallet (balance, budget, custody)
35
40
  run402 wallets lightning mint Mint the Lightning wallet for the active wallet (same as init lightning)
@@ -43,6 +48,12 @@ Options:
43
48
  --rail <rail> (new) x402 (default), mpp, or lightning; a lightning wallet
44
49
  is minted on the platform by: run402 --wallet <name> init lightning
45
50
  --key <path|-> (import) read the private key from a file, or '-' for stdin
51
+ --kms <arn> (import) a KMS key ARN (ECC_SECG_P256K1, SIGN_VERIFY); the key never leaves KMS
52
+ --aws-profile <p> (import) the AWS profile the key is reached through (e.g. an SSO profile)
53
+ --to <address> (send) the recipient
54
+ --from <addr> (send) a CoinbaseSmartWallet the wallet owns, to send from (Base only)
55
+ --network <n> (send) base (default), base-sepolia, tempo, or tempo-moderato
56
+ --confirm (send) sign and send; without it, print the checked preview only
46
57
  --yes (rm) confirm deletion
47
58
 
48
59
  Notes:
@@ -97,6 +108,14 @@ const cmdBind = (args) => call((w) => w.bind(firstPositional(args)));
97
108
  const cmdUnbind = () => call((w) => w.unbind());
98
109
 
99
110
  function cmdImport(args) {
111
+ const kms = flagVal(args, "--kms");
112
+ if (kms) {
113
+ if (args.includes("--key")) {
114
+ fail({ code: "BAD_USAGE", message: "Pass either --key or --kms, not both." });
115
+ }
116
+ const awsProfile = flagVal(args, "--aws-profile") ?? undefined;
117
+ return call((w) => w.import(firstPositional(args), { kms: { keyId: kms, awsProfile } }));
118
+ }
100
119
  // The key is read only after the SDK has accepted the name.
101
120
  return call((w) => w.import(firstPositional(args), () => {
102
121
  const keyArg = flagVal(args, "--key");
@@ -113,6 +132,26 @@ function cmdImport(args) {
113
132
 
114
133
  const cmdRm = (args) => call((w) => w.remove(firstPositional(args), { confirm: args.includes("--yes") }));
115
134
 
135
+ function cmdSend(args) {
136
+ const valueFlags = new Set(["--to", "--from", "--network"]);
137
+ const amount = args.find((a, i) => a && !a.startsWith("-") && !valueFlags.has(args[i - 1]));
138
+ const to = flagVal(args, "--to");
139
+ if (!amount || !to) {
140
+ fail({ code: "BAD_USAGE", message: "Usage: run402 wallets send <amount> --to <address> [--from <smart-account>] [--network <n>] [--confirm]" });
141
+ }
142
+ const opts = {
143
+ amount,
144
+ to,
145
+ from: flagVal(args, "--from") ?? undefined,
146
+ network: flagVal(args, "--network") ?? undefined,
147
+ confirm: args.includes("--confirm"),
148
+ };
149
+ return call(async (w) => {
150
+ const result = await w.send(opts);
151
+ return result.preview ? { ...result, hint: "Nothing was signed. Re-run with --confirm to sign and send." } : result;
152
+ });
153
+ }
154
+
116
155
  export async function run(sub, args = []) {
117
156
  const rest = Array.isArray(args) ? args : [];
118
157
  if (!sub || sub === "--help" || sub === "-h" || rest.includes("--help") || rest.includes("-h")) {
@@ -129,6 +168,7 @@ export async function run(sub, args = []) {
129
168
  case "unbind": return cmdUnbind();
130
169
  case "import": return cmdImport(rest);
131
170
  case "rm": return cmdRm(rest);
171
+ case "send": return cmdSend(rest);
132
172
  case "fund": return cmdFund();
133
173
  case "balance": return cmdBalance();
134
174
  case "lightning": return cmdLightning(rest);
@@ -39,7 +39,7 @@ export async function run(sub, args = []) {
39
39
  }
40
40
  const parsedArgs = normalizeArgv(args);
41
41
  assertKnownFlags(parsedArgs, ["--help", "-h"]);
42
- const proofs = sessionProofs("/agent/v1/webhook-secret/rotate");
42
+ const proofs = await sessionProofs("/agent/v1/webhook-secret/rotate");
43
43
  try {
44
44
  const data = await getSdk().admin.rotateWebhookSecret(proofs);
45
45
  console.log(JSON.stringify(data, null, 2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "4.120.0",
3
+ "version": "4.122.0",
4
4
  "description": "CLI for Run402 — full-stack backend infrastructure for AI agents: Postgres, auth, storage, serverless functions and atomic deploys. Paid with x402/MPP. Includes $0.03 image generation.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,6 +23,7 @@
23
23
  "vault-surface.json"
24
24
  ],
25
25
  "dependencies": {
26
+ "@aws-sdk/client-kms": "^3.1147.0",
26
27
  "@hpke/chacha20poly1305": "^1.8.0",
27
28
  "@hpke/core": "^1.9.0",
28
29
  "@humanwhocodes/momoa": "3.3.10",
@@ -6,7 +6,7 @@ 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 { readWallet } from "./wallet.js";
9
+ import { isSignerWallet, readWallet } from "./wallet.js";
10
10
  import { getApiBase } from "./config.js";
11
11
  /**
12
12
  * EIP-55 mixed-case checksum encoding.
@@ -57,6 +57,33 @@ function personalSign(privateKeyHex, address, message) {
57
57
  const vHex = (recovery + 27).toString(16).padStart(2, "0");
58
58
  return "0x" + r + s + vHex;
59
59
  }
60
+ export function prepareSIWx(opts) {
61
+ const message = formatSIWEMessage(opts, opts.address);
62
+ return {
63
+ message,
64
+ finish(signature) {
65
+ const payload = {
66
+ domain: opts.domain,
67
+ address: toChecksumAddress(opts.address),
68
+ statement: opts.statement,
69
+ uri: opts.uri,
70
+ version: opts.version ?? "1",
71
+ chainId: payloadChainId(opts.chainId),
72
+ type: opts.type ?? "eip191",
73
+ nonce: opts.nonce,
74
+ issuedAt: opts.issuedAt,
75
+ expirationTime: opts.expirationTime,
76
+ signature,
77
+ };
78
+ if (opts.resources !== undefined) {
79
+ payload.resources = opts.resources;
80
+ }
81
+ return {
82
+ "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64"),
83
+ };
84
+ },
85
+ };
86
+ }
60
87
  /**
61
88
  * Format an EIP-4361 (SIWE) message. Must be byte-for-byte compatible
62
89
  * with the `siwe` library's message format used server-side for verification.
@@ -85,29 +112,54 @@ export function formatSIWEMessage(opts, address) {
85
112
  }
86
113
  return lines.join("\n");
87
114
  }
115
+ /** SIWX headers signed with a local private key (synchronous). */
88
116
  export function buildSIWxAuthHeaders(opts) {
89
- const message = formatSIWEMessage(opts, opts.wallet.address);
90
- const signature = personalSign(opts.wallet.privateKey, opts.wallet.address, message);
91
- const payload = {
92
- domain: opts.domain,
93
- address: toChecksumAddress(opts.wallet.address),
94
- statement: opts.statement,
95
- uri: opts.uri,
96
- version: opts.version ?? "1",
97
- chainId: payloadChainId(opts.chainId),
98
- type: opts.type ?? "eip191",
99
- nonce: opts.nonce,
100
- issuedAt: opts.issuedAt,
101
- expirationTime: opts.expirationTime,
102
- signature,
103
- };
104
- if (opts.resources !== undefined) {
105
- payload.resources = opts.resources;
117
+ if (!opts.wallet.privateKey) {
118
+ throw new SignerWalletError();
106
119
  }
120
+ const prepared = prepareSIWx({ ...opts, address: opts.wallet.address });
121
+ return prepared.finish(personalSign(opts.wallet.privateKey, opts.wallet.address, prepared.message));
122
+ }
123
+ /**
124
+ * Thrown where a synchronous signature is required but the wallet signs
125
+ * through a signer (`wallet.json` `signer`). Callers that can wait use
126
+ * `getWalletAuthHeadersAsync` with the SDK's signer instead.
127
+ */
128
+ export class SignerWalletError extends Error {
129
+ code = "SIGNER_WALLET_ASYNC";
130
+ constructor() {
131
+ super("This wallet signs through a signer (wallet.json 'signer'), which this synchronous path cannot use.");
132
+ this.name = "SignerWalletError";
133
+ }
134
+ }
135
+ function walletSIWxOptions(path) {
136
+ const apiBase = getApiBase();
137
+ const url = new URL(apiBase);
138
+ const now = new Date();
107
139
  return {
108
- "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64"),
140
+ domain: url.hostname,
141
+ uri: `${apiBase}${path}`,
142
+ statement: "Sign in to Run402",
143
+ chainId: "eip155:84532",
144
+ nonce: randomBytes(16).toString("hex"),
145
+ issuedAt: now.toISOString(),
146
+ expirationTime: new Date(now.getTime() + 5 * 60 * 1000).toISOString(),
109
147
  };
110
148
  }
149
+ /**
150
+ * SIWX headers for any wallet: a key-backed wallet signs locally; a
151
+ * signer-backed one through `signerFor(wallet)`. Null when no wallet exists.
152
+ */
153
+ export async function getWalletAuthHeadersAsync(path, walletPath, signerFor) {
154
+ const localWallet = readWallet(walletPath);
155
+ if (!localWallet || !localWallet.address)
156
+ return null;
157
+ if (!isSignerWallet(localWallet))
158
+ return getWalletAuthHeaders(path, walletPath);
159
+ const prepared = prepareSIWx({ ...walletSIWxOptions(path), address: localWallet.address });
160
+ const sign = await signerFor(localWallet);
161
+ return prepared.finish(await sign(prepared.message));
162
+ }
111
163
  /**
112
164
  * Get SIWX auth headers for the Run402 API.
113
165
  * Returns null if no local wallet is configured.
@@ -121,26 +173,13 @@ export function getWalletAuthHeaders(path, walletPath) {
121
173
  // this helper returns SIWxAuthHeaders | null. Re-throw so callers above
122
174
  // the CLI's wrapper (e.g. SDK paid-fetch) can decide whether to swallow it.
123
175
  const localWallet = readWallet(walletPath);
124
- if (!localWallet || !localWallet.address || !localWallet.privateKey)
176
+ if (!localWallet || !localWallet.address)
125
177
  return null;
126
- const apiBase = getApiBase();
127
- const url = new URL(apiBase);
128
- const domain = url.hostname;
129
- const uri = `${apiBase}${path}`;
130
- const nonce = randomBytes(16).toString("hex");
131
- const now = new Date();
132
- const issuedAt = now.toISOString();
133
- const expirationTime = new Date(now.getTime() + 5 * 60 * 1000).toISOString();
134
- return buildSIWxAuthHeaders({
135
- wallet: localWallet,
136
- domain,
137
- uri,
138
- statement: "Sign in to Run402",
139
- chainId: "eip155:84532",
140
- nonce,
141
- issuedAt,
142
- expirationTime,
143
- });
178
+ if (isSignerWallet(localWallet))
179
+ throw new SignerWalletError();
180
+ if (!localWallet.privateKey)
181
+ return null;
182
+ return buildSIWxAuthHeaders({ wallet: localWallet, ...walletSIWxOptions(path) });
144
183
  }
145
184
  function messageChainId(chainId) {
146
185
  if (typeof chainId === "number")
@@ -6,6 +6,12 @@ import { getWalletPath } from "./config.js";
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
+ // arn:aws:kms:<region>:<account>:key/<id>
10
+ export const KMS_KEY_ARN_RE = /^arn:aws[a-z-]*:kms:([a-z0-9-]+):\d{12}:key\/[A-Za-z0-9-]+$/;
11
+ /** True when the wallet signs through a signer instead of a local key. */
12
+ export function isSignerWallet(wallet) {
13
+ return wallet.signer !== undefined;
14
+ }
9
15
  /**
10
16
  * Load the agent wallet from disk.
11
17
  *
@@ -78,6 +84,21 @@ export function readWallet(path) {
78
84
  throw new Error("wallet.json missing valid 'address' (expected 0x-prefixed 40-hex string). " +
79
85
  "Back up the file and run 'run402 init' to recreate it.");
80
86
  }
87
+ if (data.privateKey !== undefined && data.signer !== undefined) {
88
+ throw new Error(`${p} has both 'privateKey' and 'signer'; a wallet is backed by exactly one. ` +
89
+ "Remove the one that does not belong to this wallet.");
90
+ }
91
+ if (data.signer !== undefined) {
92
+ const signer = data.signer;
93
+ if (!signer || typeof signer !== "object" || signer.kind !== "aws_kms" ||
94
+ typeof signer.key_arn !== "string" || !KMS_KEY_ARN_RE.test(signer.key_arn) ||
95
+ typeof signer.region !== "string" || signer.region.length === 0 ||
96
+ (signer.aws_profile !== undefined && (typeof signer.aws_profile !== "string" || signer.aws_profile.length === 0))) {
97
+ throw new Error(`${p} has a malformed 'signer' (expected { kind: "aws_kms", key_arn, region, aws_profile? }). ` +
98
+ "Re-import the wallet with 'run402 wallets import <name> --kms <key-arn>'.");
99
+ }
100
+ return data;
101
+ }
81
102
  if (typeof data.privateKey !== "string" || !PRIVATE_KEY_RE.test(data.privateKey)) {
82
103
  throw new Error("wallet.json missing valid 'privateKey' (expected 0x-prefixed 64-hex string). " +
83
104
  "Back up the file and run 'run402 init' to recreate it.");
@@ -31,9 +31,23 @@ export interface ProjectCredentialCacheInfo {
31
31
  wallet?: string;
32
32
  profile?: string;
33
33
  }
34
+ /**
35
+ * A wallet is backed by exactly one of: a local private key (`privateKey`),
36
+ * or a signer reference (`signer`) naming where the key lives (an AWS KMS
37
+ * key). A signer reference holds no secret.
38
+ */
34
39
  export interface WalletData {
35
40
  address: string;
36
- privateKey: string;
41
+ /** Local signing key. Absent on a signer-backed wallet. */
42
+ privateKey?: string;
43
+ /** Where a signer-backed wallet's key lives. Absent on a key-backed wallet. */
44
+ signer?: {
45
+ kind: "aws_kms";
46
+ key_arn: string;
47
+ region: string;
48
+ /** The AWS profile the key is reached through; the default credential chain when absent. */
49
+ aws_profile?: string;
50
+ };
37
51
  created?: string;
38
52
  funded?: boolean;
39
53
  lastFaucet?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"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"}