run402 1.40.1 → 1.40.2

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 (121) hide show
  1. package/lib/sdk.mjs +1 -1
  2. package/package.json +4 -3
  3. package/sdk/core-dist/allowance-auth.js +129 -0
  4. package/sdk/core-dist/allowance.js +25 -0
  5. package/sdk/core-dist/client.js +42 -0
  6. package/sdk/core-dist/config.js +26 -0
  7. package/sdk/core-dist/keystore.js +97 -0
  8. package/sdk/core-dist/wallet-auth.js +62 -0
  9. package/sdk/core-dist/wallet.js +25 -0
  10. package/sdk/dist/credentials.d.ts +70 -0
  11. package/sdk/dist/credentials.d.ts.map +1 -0
  12. package/sdk/dist/credentials.js +19 -0
  13. package/sdk/dist/credentials.js.map +1 -0
  14. package/sdk/dist/errors.d.ts +34 -0
  15. package/sdk/dist/errors.d.ts.map +1 -0
  16. package/sdk/dist/errors.js +46 -0
  17. package/sdk/dist/errors.js.map +1 -0
  18. package/sdk/dist/index.d.ts +68 -0
  19. package/sdk/dist/index.d.ts.map +1 -0
  20. package/sdk/dist/index.js +81 -0
  21. package/sdk/dist/index.js.map +1 -0
  22. package/sdk/dist/kernel.d.ts +47 -0
  23. package/sdk/dist/kernel.d.ts.map +1 -0
  24. package/sdk/dist/kernel.js +76 -0
  25. package/sdk/dist/kernel.js.map +1 -0
  26. package/sdk/dist/namespaces/admin.d.ts +30 -0
  27. package/sdk/dist/namespaces/admin.d.ts.map +1 -0
  28. package/sdk/dist/namespaces/admin.js +36 -0
  29. package/sdk/dist/namespaces/admin.js.map +1 -0
  30. package/sdk/dist/namespaces/ai.d.ts +57 -0
  31. package/sdk/dist/namespaces/ai.d.ts.map +1 -0
  32. package/sdk/dist/namespaces/ai.js +62 -0
  33. package/sdk/dist/namespaces/ai.js.map +1 -0
  34. package/sdk/dist/namespaces/allowance.d.ts +51 -0
  35. package/sdk/dist/namespaces/allowance.d.ts.map +1 -0
  36. package/sdk/dist/namespaces/allowance.js +116 -0
  37. package/sdk/dist/namespaces/allowance.js.map +1 -0
  38. package/sdk/dist/namespaces/apps.d.ts +158 -0
  39. package/sdk/dist/namespaces/apps.d.ts.map +1 -0
  40. package/sdk/dist/namespaces/apps.js +150 -0
  41. package/sdk/dist/namespaces/apps.js.map +1 -0
  42. package/sdk/dist/namespaces/auth.d.ts +53 -0
  43. package/sdk/dist/namespaces/auth.d.ts.map +1 -0
  44. package/sdk/dist/namespaces/auth.js +106 -0
  45. package/sdk/dist/namespaces/auth.js.map +1 -0
  46. package/sdk/dist/namespaces/billing.d.ts +64 -0
  47. package/sdk/dist/namespaces/billing.d.ts.map +1 -0
  48. package/sdk/dist/namespaces/billing.js +105 -0
  49. package/sdk/dist/namespaces/billing.js.map +1 -0
  50. package/sdk/dist/namespaces/blobs.d.ts +41 -0
  51. package/sdk/dist/namespaces/blobs.d.ts.map +1 -0
  52. package/sdk/dist/namespaces/blobs.js +202 -0
  53. package/sdk/dist/namespaces/blobs.js.map +1 -0
  54. package/sdk/dist/namespaces/blobs.types.d.ts +58 -0
  55. package/sdk/dist/namespaces/blobs.types.d.ts.map +1 -0
  56. package/sdk/dist/namespaces/blobs.types.js +9 -0
  57. package/sdk/dist/namespaces/blobs.types.js.map +1 -0
  58. package/sdk/dist/namespaces/contracts.d.ts +65 -0
  59. package/sdk/dist/namespaces/contracts.d.ts.map +1 -0
  60. package/sdk/dist/namespaces/contracts.js +163 -0
  61. package/sdk/dist/namespaces/contracts.js.map +1 -0
  62. package/sdk/dist/namespaces/domains.d.ts +57 -0
  63. package/sdk/dist/namespaces/domains.d.ts.map +1 -0
  64. package/sdk/dist/namespaces/domains.js +60 -0
  65. package/sdk/dist/namespaces/domains.js.map +1 -0
  66. package/sdk/dist/namespaces/email.d.ts +131 -0
  67. package/sdk/dist/namespaces/email.d.ts.map +1 -0
  68. package/sdk/dist/namespaces/email.js +318 -0
  69. package/sdk/dist/namespaces/email.js.map +1 -0
  70. package/sdk/dist/namespaces/functions.d.ts +43 -0
  71. package/sdk/dist/namespaces/functions.d.ts.map +1 -0
  72. package/sdk/dist/namespaces/functions.js +146 -0
  73. package/sdk/dist/namespaces/functions.js.map +1 -0
  74. package/sdk/dist/namespaces/functions.types.d.ts +96 -0
  75. package/sdk/dist/namespaces/functions.types.d.ts.map +1 -0
  76. package/sdk/dist/namespaces/functions.types.js +6 -0
  77. package/sdk/dist/namespaces/functions.types.js.map +1 -0
  78. package/sdk/dist/namespaces/projects.d.ts +97 -0
  79. package/sdk/dist/namespaces/projects.d.ts.map +1 -0
  80. package/sdk/dist/namespaces/projects.js +214 -0
  81. package/sdk/dist/namespaces/projects.js.map +1 -0
  82. package/sdk/dist/namespaces/projects.types.d.ts +112 -0
  83. package/sdk/dist/namespaces/projects.types.d.ts.map +1 -0
  84. package/sdk/dist/namespaces/projects.types.js +9 -0
  85. package/sdk/dist/namespaces/projects.types.js.map +1 -0
  86. package/sdk/dist/namespaces/secrets.d.ts +23 -0
  87. package/sdk/dist/namespaces/secrets.d.ts.map +1 -0
  88. package/sdk/dist/namespaces/secrets.js +45 -0
  89. package/sdk/dist/namespaces/secrets.js.map +1 -0
  90. package/sdk/dist/namespaces/sender-domain.d.ts +40 -0
  91. package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -0
  92. package/sdk/dist/namespaces/sender-domain.js +69 -0
  93. package/sdk/dist/namespaces/sender-domain.js.map +1 -0
  94. package/sdk/dist/namespaces/service.d.ts +51 -0
  95. package/sdk/dist/namespaces/service.d.ts.map +1 -0
  96. package/sdk/dist/namespaces/service.js +25 -0
  97. package/sdk/dist/namespaces/service.js.map +1 -0
  98. package/sdk/dist/namespaces/sites.d.ts +50 -0
  99. package/sdk/dist/namespaces/sites.d.ts.map +1 -0
  100. package/sdk/dist/namespaces/sites.js +38 -0
  101. package/sdk/dist/namespaces/sites.js.map +1 -0
  102. package/sdk/dist/namespaces/subdomains.d.ts +36 -0
  103. package/sdk/dist/namespaces/subdomains.d.ts.map +1 -0
  104. package/sdk/dist/namespaces/subdomains.js +54 -0
  105. package/sdk/dist/namespaces/subdomains.js.map +1 -0
  106. package/sdk/dist/namespaces/tier.d.ts +36 -0
  107. package/sdk/dist/namespaces/tier.d.ts.map +1 -0
  108. package/sdk/dist/namespaces/tier.js +31 -0
  109. package/sdk/dist/namespaces/tier.js.map +1 -0
  110. package/sdk/dist/node/credentials.d.ts +26 -0
  111. package/sdk/dist/node/credentials.d.ts.map +1 -0
  112. package/sdk/dist/node/credentials.js +69 -0
  113. package/sdk/dist/node/credentials.js.map +1 -0
  114. package/sdk/dist/node/index.d.ts +44 -0
  115. package/sdk/dist/node/index.d.ts.map +1 -0
  116. package/sdk/dist/node/index.js +43 -0
  117. package/sdk/dist/node/index.js.map +1 -0
  118. package/sdk/dist/node/paid-fetch.d.ts +22 -0
  119. package/sdk/dist/node/paid-fetch.d.ts.map +1 -0
  120. package/sdk/dist/node/paid-fetch.js +116 -0
  121. package/sdk/dist/node/paid-fetch.js.map +1 -0
package/lib/sdk.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  * RUN402_API_BASE between runs.
8
8
  */
9
9
 
10
- import { run402 } from "../../sdk/dist/node/index.js";
10
+ import { run402 } from "../sdk/dist/node/index.js";
11
11
 
12
12
  export function getSdk() {
13
13
  return run402();
package/package.json CHANGED
@@ -1,18 +1,19 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.40.1",
3
+ "version": "1.40.2",
4
4
  "description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 and MPP micropayments.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "run402": "cli.mjs"
8
8
  },
9
9
  "scripts": {
10
- "prepack": "mkdir -p core-dist && cp ../core/dist/*.js core-dist/"
10
+ "prepack": "mkdir -p core-dist sdk/dist sdk/core-dist && cp ../core/dist/*.js core-dist/ && cp -R ../sdk/dist/. sdk/dist/ && cp ../sdk/core-dist/*.js sdk/core-dist/"
11
11
  },
12
12
  "files": [
13
13
  "cli.mjs",
14
14
  "lib/",
15
- "core-dist/"
15
+ "core-dist/",
16
+ "sdk/"
16
17
  ],
17
18
  "dependencies": {
18
19
  "@noble/curves": "^2.0.1",
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Allowance auth helper — generates SIWX (Sign-In With X / EIP-4361) headers for Run402 API.
3
+ * Uses @noble/curves (lighter than viem) for signing.
4
+ */
5
+ import { randomBytes } from "node:crypto";
6
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
7
+ import { keccak_256 } from "@noble/hashes/sha3.js";
8
+ import { bytesToHex } from "@noble/hashes/utils.js";
9
+ import { readAllowance } from "./allowance.js";
10
+ import { getApiBase } from "./config.js";
11
+ /**
12
+ * EIP-55 mixed-case checksum encoding.
13
+ */
14
+ export function toChecksumAddress(address) {
15
+ const lower = address.toLowerCase().replace("0x", "");
16
+ const hash = bytesToHex(keccak_256(new TextEncoder().encode(lower)));
17
+ let checksummed = "0x";
18
+ for (let i = 0; i < lower.length; i++) {
19
+ checksummed += parseInt(hash[i], 16) >= 8 ? lower[i].toUpperCase() : lower[i];
20
+ }
21
+ return checksummed;
22
+ }
23
+ /**
24
+ * EIP-191 personal_sign: sign a message with the allowance's private key.
25
+ */
26
+ function personalSign(privateKeyHex, address, message) {
27
+ const msgBytes = new TextEncoder().encode(message);
28
+ const prefix = new TextEncoder().encode(`\x19Ethereum Signed Message:\n${msgBytes.length}`);
29
+ const prefixed = new Uint8Array(prefix.length + msgBytes.length);
30
+ prefixed.set(prefix);
31
+ prefixed.set(msgBytes, prefix.length);
32
+ const hash = keccak_256(prefixed);
33
+ const pkHex = privateKeyHex.startsWith("0x")
34
+ ? privateKeyHex.slice(2)
35
+ : privateKeyHex;
36
+ const pkBytes = Uint8Array.from(Buffer.from(pkHex, "hex"));
37
+ const rawSig = secp256k1.sign(hash, pkBytes, { prehash: false });
38
+ const sig = secp256k1.Signature.fromBytes(rawSig);
39
+ // Determine recovery bit by trying both and matching the address
40
+ let recovery = 0;
41
+ for (const v of [0, 1]) {
42
+ try {
43
+ const recovered = sig.addRecoveryBit(v).recoverPublicKey(hash);
44
+ const pubBytes = recovered.toBytes(false).slice(1); // uncompressed, drop 04 prefix
45
+ const addrBytes = keccak_256(pubBytes).slice(-20);
46
+ if ("0x" + bytesToHex(addrBytes) === address.toLowerCase()) {
47
+ recovery = v;
48
+ break;
49
+ }
50
+ }
51
+ catch {
52
+ continue;
53
+ }
54
+ }
55
+ const r = sig.r.toString(16).padStart(64, "0");
56
+ const s = sig.s.toString(16).padStart(64, "0");
57
+ const vHex = (recovery + 27).toString(16).padStart(2, "0");
58
+ return "0x" + r + s + vHex;
59
+ }
60
+ /**
61
+ * Format an EIP-4361 (SIWE) message. Must be byte-for-byte compatible
62
+ * with the `siwe` library's message format used server-side for verification.
63
+ */
64
+ export function formatSIWEMessage(opts, address) {
65
+ const checksummed = toChecksumAddress(address);
66
+ const lines = [
67
+ `${opts.domain} wants you to sign in with your Ethereum account:`,
68
+ checksummed,
69
+ "",
70
+ opts.statement,
71
+ "",
72
+ `URI: ${opts.uri}`,
73
+ `Version: ${opts.version}`,
74
+ `Chain ID: ${opts.chainId}`,
75
+ `Nonce: ${opts.nonce}`,
76
+ `Issued At: ${opts.issuedAt}`,
77
+ ];
78
+ if (opts.expirationTime) {
79
+ lines.push(`Expiration Time: ${opts.expirationTime}`);
80
+ }
81
+ return lines.join("\n");
82
+ }
83
+ /**
84
+ * Get SIWX auth headers for the Run402 API.
85
+ * Returns null if no allowance is configured.
86
+ *
87
+ * @param path - API path (e.g. "/projects/v1") used to build the SIWE uri field.
88
+ */
89
+ export function getAllowanceAuthHeaders(path, allowancePath) {
90
+ const allowance = readAllowance(allowancePath);
91
+ if (!allowance || !allowance.address || !allowance.privateKey)
92
+ return null;
93
+ const apiBase = getApiBase();
94
+ const url = new URL(apiBase);
95
+ const domain = url.hostname;
96
+ const uri = `${apiBase}${path}`;
97
+ const nonce = randomBytes(16).toString("hex");
98
+ const now = new Date();
99
+ const issuedAt = now.toISOString();
100
+ const expirationTime = new Date(now.getTime() + 5 * 60 * 1000).toISOString();
101
+ const message = formatSIWEMessage({
102
+ domain,
103
+ uri,
104
+ statement: "Sign in to Run402",
105
+ version: "1",
106
+ chainId: 84532, // Base Sepolia
107
+ nonce,
108
+ issuedAt,
109
+ expirationTime,
110
+ }, allowance.address);
111
+ const signature = personalSign(allowance.privateKey, allowance.address, message);
112
+ const payload = {
113
+ domain,
114
+ address: toChecksumAddress(allowance.address),
115
+ statement: "Sign in to Run402",
116
+ uri,
117
+ version: "1",
118
+ chainId: "eip155:84532",
119
+ type: "eip191",
120
+ nonce,
121
+ issuedAt,
122
+ expirationTime,
123
+ signature,
124
+ };
125
+ return {
126
+ "SIGN-IN-WITH-X": Buffer.from(JSON.stringify(payload)).toString("base64"),
127
+ };
128
+ }
129
+ //# sourceMappingURL=allowance-auth.js.map
@@ -0,0 +1,25 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, renameSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import { getAllowancePath } from "./config.js";
5
+ export function readAllowance(path) {
6
+ const p = path ?? getAllowancePath();
7
+ if (!existsSync(p))
8
+ return null;
9
+ try {
10
+ return JSON.parse(readFileSync(p, "utf-8"));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function saveAllowance(data, path) {
17
+ const p = path ?? getAllowancePath();
18
+ const dir = dirname(p);
19
+ mkdirSync(dir, { recursive: true });
20
+ const tmp = join(dir, `.allowance.${randomBytes(4).toString("hex")}.tmp`);
21
+ writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });
22
+ renameSync(tmp, p);
23
+ chmodSync(p, 0o600);
24
+ }
25
+ //# sourceMappingURL=allowance.js.map
@@ -0,0 +1,42 @@
1
+ import { getApiBase } from "./config.js";
2
+ export async function apiRequest(path, opts = {}) {
3
+ const { method = "GET", headers = {}, body, rawBody } = opts;
4
+ const url = `${getApiBase()}${path}`;
5
+ const fetchHeaders = { ...headers };
6
+ let fetchBody;
7
+ if (rawBody !== undefined) {
8
+ fetchBody = rawBody;
9
+ }
10
+ else if (body !== undefined) {
11
+ fetchHeaders["Content-Type"] = fetchHeaders["Content-Type"] || "application/json";
12
+ fetchBody = JSON.stringify(body);
13
+ }
14
+ let res;
15
+ try {
16
+ res = await fetch(url, {
17
+ method,
18
+ headers: fetchHeaders,
19
+ body: fetchBody,
20
+ });
21
+ }
22
+ catch (err) {
23
+ return {
24
+ ok: false,
25
+ status: 0,
26
+ body: { error: `Network error: ${err.message}` },
27
+ };
28
+ }
29
+ let resBody;
30
+ const contentType = res.headers.get("content-type") || "";
31
+ if (contentType.includes("application/json")) {
32
+ resBody = await res.json();
33
+ }
34
+ else {
35
+ resBody = await res.text();
36
+ }
37
+ if (res.status === 402) {
38
+ return { ok: false, is402: true, status: 402, body: resBody };
39
+ }
40
+ return { ok: res.ok, status: res.status, body: resBody };
41
+ }
42
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1,26 @@
1
+ import { homedir } from "node:os";
2
+ import { join } from "node:path";
3
+ import { existsSync, renameSync, mkdirSync } from "node:fs";
4
+ export function getApiBase() {
5
+ return process.env.RUN402_API_BASE || "https://api.run402.com";
6
+ }
7
+ export function getConfigDir() {
8
+ return process.env.RUN402_CONFIG_DIR || join(homedir(), ".config", "run402");
9
+ }
10
+ export function getKeystorePath() {
11
+ return join(getConfigDir(), "projects.json");
12
+ }
13
+ export function getAllowancePath() {
14
+ if (process.env.RUN402_ALLOWANCE_PATH)
15
+ return process.env.RUN402_ALLOWANCE_PATH;
16
+ const dir = getConfigDir();
17
+ const newPath = join(dir, "allowance.json");
18
+ const oldPath = join(dir, "wallet.json");
19
+ // Auto-migrate from wallet.json → allowance.json
20
+ if (!existsSync(newPath) && existsSync(oldPath)) {
21
+ mkdirSync(dir, { recursive: true });
22
+ renameSync(oldPath, newPath);
23
+ }
24
+ return newPath;
25
+ }
26
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,97 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, renameSync, chmodSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import { getKeystorePath } from "./config.js";
5
+ /**
6
+ * Load the keystore from disk.
7
+ * Auto-migrates legacy formats:
8
+ * - Array format (CLI legacy): [{project_id, ...}] → {projects: {id: {...}}}
9
+ * - Old field name: expires_at → lease_expires_at
10
+ */
11
+ export function loadKeyStore(path) {
12
+ const p = path ?? getKeystorePath();
13
+ try {
14
+ const data = readFileSync(p, "utf-8");
15
+ const parsed = JSON.parse(data);
16
+ // Auto-migrate array format (CLI legacy) to object format
17
+ if (Array.isArray(parsed)) {
18
+ const projects = {};
19
+ for (const item of parsed) {
20
+ if (item.project_id) {
21
+ projects[item.project_id] = {
22
+ anon_key: item.anon_key,
23
+ service_key: item.service_key,
24
+ ...(item.site_url && { site_url: item.site_url }),
25
+ ...(item.deployed_at && { deployed_at: item.deployed_at }),
26
+ };
27
+ }
28
+ }
29
+ return { projects };
30
+ }
31
+ if (parsed && typeof parsed === "object" && parsed.projects) {
32
+ // Strip legacy fields (tier, lease_expires_at, expires_at) from projects
33
+ for (const proj of Object.values(parsed.projects)) {
34
+ const rec = proj;
35
+ delete rec.tier;
36
+ delete rec.lease_expires_at;
37
+ delete rec.expires_at;
38
+ }
39
+ return {
40
+ ...(parsed.active_project_id && { active_project_id: parsed.active_project_id }),
41
+ projects: parsed.projects,
42
+ };
43
+ }
44
+ return { projects: {} };
45
+ }
46
+ catch {
47
+ return { projects: {} };
48
+ }
49
+ }
50
+ export function saveKeyStore(store, path) {
51
+ const p = path ?? getKeystorePath();
52
+ const dir = dirname(p);
53
+ mkdirSync(dir, { recursive: true });
54
+ const tmp = join(dir, `.projects.${randomBytes(4).toString("hex")}.tmp`);
55
+ writeFileSync(tmp, JSON.stringify(store, null, 2), { mode: 0o600 });
56
+ renameSync(tmp, p);
57
+ chmodSync(p, 0o600);
58
+ }
59
+ export function getProject(projectId, path) {
60
+ const store = loadKeyStore(path);
61
+ return store.projects[projectId];
62
+ }
63
+ export function saveProject(projectId, project, path) {
64
+ const p = path ?? getKeystorePath();
65
+ const store = loadKeyStore(p);
66
+ store.projects[projectId] = project;
67
+ saveKeyStore(store, p);
68
+ }
69
+ export function updateProject(projectId, update, path) {
70
+ const p = path ?? getKeystorePath();
71
+ const store = loadKeyStore(p);
72
+ const existing = store.projects[projectId];
73
+ if (existing) {
74
+ store.projects[projectId] = { ...existing, ...update };
75
+ saveKeyStore(store, p);
76
+ }
77
+ }
78
+ export function removeProject(projectId, path) {
79
+ const p = path ?? getKeystorePath();
80
+ const store = loadKeyStore(p);
81
+ delete store.projects[projectId];
82
+ if (store.active_project_id === projectId) {
83
+ delete store.active_project_id;
84
+ }
85
+ saveKeyStore(store, p);
86
+ }
87
+ export function getActiveProjectId(path) {
88
+ const store = loadKeyStore(path);
89
+ return store.active_project_id;
90
+ }
91
+ export function setActiveProjectId(projectId, path) {
92
+ const p = path ?? getKeystorePath();
93
+ const store = loadKeyStore(p);
94
+ store.active_project_id = projectId;
95
+ saveKeyStore(store, p);
96
+ }
97
+ //# sourceMappingURL=keystore.js.map
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Wallet auth helper — generates EIP-191 signature headers for Run402 API.
3
+ * Uses @noble/curves (lighter than viem) for signing.
4
+ */
5
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
6
+ import { keccak_256 } from "@noble/hashes/sha3.js";
7
+ import { bytesToHex } from "@noble/hashes/utils.js";
8
+ import { readWallet } from "./wallet.js";
9
+ /**
10
+ * EIP-191 personal_sign: sign a message with the wallet's private key.
11
+ */
12
+ function personalSign(privateKeyHex, address, message) {
13
+ const msgBytes = new TextEncoder().encode(message);
14
+ const prefix = new TextEncoder().encode(`\x19Ethereum Signed Message:\n${msgBytes.length}`);
15
+ const prefixed = new Uint8Array(prefix.length + msgBytes.length);
16
+ prefixed.set(prefix);
17
+ prefixed.set(msgBytes, prefix.length);
18
+ const hash = keccak_256(prefixed);
19
+ const pkHex = privateKeyHex.startsWith("0x")
20
+ ? privateKeyHex.slice(2)
21
+ : privateKeyHex;
22
+ const pkBytes = Uint8Array.from(Buffer.from(pkHex, "hex"));
23
+ const rawSig = secp256k1.sign(hash, pkBytes);
24
+ const sig = secp256k1.Signature.fromBytes(rawSig);
25
+ // Determine recovery bit by trying both and matching the address
26
+ let recovery = 0;
27
+ for (const v of [0, 1]) {
28
+ try {
29
+ const recovered = sig.addRecoveryBit(v).recoverPublicKey(hash);
30
+ const pubBytes = recovered.toBytes(false).slice(1); // uncompressed, drop 04 prefix
31
+ const addrBytes = keccak_256(pubBytes).slice(-20);
32
+ if ("0x" + bytesToHex(addrBytes) === address.toLowerCase()) {
33
+ recovery = v;
34
+ break;
35
+ }
36
+ }
37
+ catch {
38
+ continue;
39
+ }
40
+ }
41
+ const r = sig.r.toString(16).padStart(64, "0");
42
+ const s = sig.s.toString(16).padStart(64, "0");
43
+ const vHex = (recovery + 27).toString(16).padStart(2, "0");
44
+ return "0x" + r + s + vHex;
45
+ }
46
+ /**
47
+ * Get wallet auth headers for the Run402 API.
48
+ * Returns null if no wallet is configured.
49
+ */
50
+ export function getWalletAuthHeaders(walletPath) {
51
+ const wallet = readWallet(walletPath);
52
+ if (!wallet || !wallet.address || !wallet.privateKey)
53
+ return null;
54
+ const timestamp = Math.floor(Date.now() / 1000).toString();
55
+ const signature = personalSign(wallet.privateKey, wallet.address, `run402:${timestamp}`);
56
+ return {
57
+ "X-Run402-Wallet": wallet.address,
58
+ "X-Run402-Signature": signature,
59
+ "X-Run402-Timestamp": timestamp,
60
+ };
61
+ }
62
+ //# sourceMappingURL=wallet-auth.js.map
@@ -0,0 +1,25 @@
1
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, renameSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { randomBytes } from "node:crypto";
4
+ import { getWalletPath } from "./config.js";
5
+ export function readWallet(path) {
6
+ const p = path ?? getWalletPath();
7
+ if (!existsSync(p))
8
+ return null;
9
+ try {
10
+ return JSON.parse(readFileSync(p, "utf-8"));
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ }
16
+ export function saveWallet(data, path) {
17
+ const p = path ?? getWalletPath();
18
+ const dir = dirname(p);
19
+ mkdirSync(dir, { recursive: true });
20
+ const tmp = join(dir, `.wallet.${randomBytes(4).toString("hex")}.tmp`);
21
+ writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 0o600 });
22
+ renameSync(tmp, p);
23
+ chmodSync(p, 0o600);
24
+ }
25
+ //# sourceMappingURL=wallet.js.map
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Credential provider interface for the Run402 SDK.
3
+ *
4
+ * The SDK's request kernel calls `getAuth` before each request to obtain
5
+ * signed auth headers, and `getProject` to resolve per-project anon/service
6
+ * keys. All filesystem, environment, and session-state access lives inside
7
+ * provider implementations — never in the kernel.
8
+ *
9
+ * Node consumers use {@link NodeCredentialsProvider} from `@run402/sdk/node`
10
+ * which wraps the local keystore + allowance. Sandbox consumers supply their
11
+ * own implementation bound to a session token issued by the supervisor.
12
+ *
13
+ * The two required methods (`getAuth`, `getProject`) support every API call.
14
+ * The optional methods let providers opt in to local persistence (keystore
15
+ * writes, active-project tracking). Namespace methods that need a missing
16
+ * optional method throw a descriptive error at runtime.
17
+ */
18
+ export interface ProjectKeys {
19
+ anon_key: string;
20
+ service_key: string;
21
+ site_url?: string;
22
+ deployed_at?: string;
23
+ last_deployment_id?: string;
24
+ mailbox_id?: string;
25
+ mailbox_address?: string;
26
+ }
27
+ export interface AllowanceData {
28
+ address: string;
29
+ privateKey: string;
30
+ created?: string;
31
+ funded?: boolean;
32
+ lastFaucet?: string;
33
+ rail?: "x402" | "mpp";
34
+ }
35
+ export interface CredentialsProvider {
36
+ /**
37
+ * Return per-request auth headers for the given API path, or null if none
38
+ * are available. Typical headers: `SIGN-IN-WITH-X` (SIWE), `Authorization:
39
+ * Bearer ...`. The kernel merges these into the request headers without
40
+ * overwriting headers explicitly set on the request.
41
+ */
42
+ getAuth(path: string): Promise<Record<string, string> | null>;
43
+ /**
44
+ * Resolve the anon/service keys for a project. Returns null if the project
45
+ * is not known to this provider — the kernel then throws ProjectNotFound.
46
+ */
47
+ getProject(id: string): Promise<ProjectKeys | null>;
48
+ /**
49
+ * Persist project keys after a successful provision or deploy. Optional:
50
+ * providers without local storage (pure session providers) may omit this.
51
+ */
52
+ saveProject?(id: string, project: ProjectKeys): Promise<void>;
53
+ /** Partially update a project's stored fields (e.g. mailbox_id, last_deployment_id). Optional. */
54
+ updateProject?(id: string, patch: Partial<ProjectKeys>): Promise<void>;
55
+ /** Remove a project from local storage after deletion. Optional. */
56
+ removeProject?(id: string): Promise<void>;
57
+ /** Set the active/default project id in local state. Optional. */
58
+ setActiveProject?(id: string): Promise<void>;
59
+ /** Get the active/default project id from local state. Optional. */
60
+ getActiveProject?(): Promise<string | null>;
61
+ /** Read the local allowance (wallet). Optional — sandbox providers may omit. */
62
+ readAllowance?(): Promise<AllowanceData | null>;
63
+ /** Persist the local allowance. Optional. */
64
+ saveAllowance?(data: AllowanceData): Promise<void>;
65
+ /** Generate a fresh allowance keypair. Optional — Node default uses secp256k1 + keccak for the Ethereum address. */
66
+ createAllowance?(): Promise<AllowanceData>;
67
+ /** Return the absolute path to the local allowance file, for diagnostic output. Optional. */
68
+ getAllowancePath?(): string;
69
+ }
70
+ //# sourceMappingURL=credentials.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;;;OAKG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;IAE9D;;;OAGG;IACH,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAEpD;;;OAGG;IACH,WAAW,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D,kGAAkG;IAClG,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvE,oEAAoE;IACpE,aAAa,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE1C,kEAAkE;IAClE,gBAAgB,CAAC,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C,oEAAoE;IACpE,gBAAgB,CAAC,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5C,gFAAgF;IAChF,aAAa,CAAC,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;IAEhD,6CAA6C;IAC7C,aAAa,CAAC,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD,oHAAoH;IACpH,eAAe,CAAC,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAE3C,6FAA6F;IAC7F,gBAAgB,CAAC,IAAI,MAAM,CAAC;CAC7B"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Credential provider interface for the Run402 SDK.
3
+ *
4
+ * The SDK's request kernel calls `getAuth` before each request to obtain
5
+ * signed auth headers, and `getProject` to resolve per-project anon/service
6
+ * keys. All filesystem, environment, and session-state access lives inside
7
+ * provider implementations — never in the kernel.
8
+ *
9
+ * Node consumers use {@link NodeCredentialsProvider} from `@run402/sdk/node`
10
+ * which wraps the local keystore + allowance. Sandbox consumers supply their
11
+ * own implementation bound to a session token issued by the supervisor.
12
+ *
13
+ * The two required methods (`getAuth`, `getProject`) support every API call.
14
+ * The optional methods let providers opt in to local persistence (keystore
15
+ * writes, active-project tracking). Namespace methods that need a missing
16
+ * optional method throw a descriptive error at runtime.
17
+ */
18
+ export {};
19
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Error hierarchy for the Run402 SDK. Every failure throws a subclass of
3
+ * {@link Run402Error}. Consumers (MCP handlers, CLI commands, user functions)
4
+ * translate these into their native error shapes at the edge.
5
+ */
6
+ export declare abstract class Run402Error extends Error {
7
+ /** HTTP status, or null for local/network failures that produced no response. */
8
+ readonly status: number | null;
9
+ /** Parsed response body, or null when no body was received. */
10
+ readonly body: unknown;
11
+ /** Short verb phrase identifying the attempted operation (e.g. "provisioning project"). */
12
+ readonly context: string;
13
+ constructor(message: string, status: number | null, body: unknown, context: string);
14
+ }
15
+ /** HTTP 402 — the gateway requires payment (lease expired, insufficient balance, or x402 quote). */
16
+ export declare class PaymentRequired extends Run402Error {
17
+ }
18
+ /** Project ID is not present in the credential provider (local miss) or the gateway returned 404. */
19
+ export declare class ProjectNotFound extends Run402Error {
20
+ readonly projectId: string;
21
+ constructor(projectId: string, context: string, status?: number | null, body?: unknown);
22
+ }
23
+ /** HTTP 401 or 403 — authentication missing, invalid, or insufficient for the operation. */
24
+ export declare class Unauthorized extends Run402Error {
25
+ }
26
+ /** Any other non-2xx HTTP response from the gateway. */
27
+ export declare class ApiError extends Run402Error {
28
+ }
29
+ /** The underlying `fetch` threw before producing a response (DNS, connection reset, offline). */
30
+ export declare class NetworkError extends Run402Error {
31
+ readonly cause: unknown;
32
+ constructor(message: string, cause: unknown, context: string);
33
+ }
34
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,8BAAsB,WAAY,SAAQ,KAAK;IAC7C,iFAAiF;IACjF,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,+DAA+D;IAC/D,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,2FAA2F;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;CAOnF;AAED,oGAAoG;AACpG,qBAAa,eAAgB,SAAQ,WAAW;CAAG;AAEnD,qGAAqG;AACrG,qBAAa,eAAgB,SAAQ,WAAW;IAC9C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBACf,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,GAAG,IAAW,EAAE,IAAI,GAAE,OAAc;CAInG;AAED,4FAA4F;AAC5F,qBAAa,YAAa,SAAQ,WAAW;CAAG;AAEhD,wDAAwD;AACxD,qBAAa,QAAS,SAAQ,WAAW;CAAG;AAE5C,iGAAiG;AACjG,qBAAa,YAAa,SAAQ,WAAW;IAC3C,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM;CAI7D"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Error hierarchy for the Run402 SDK. Every failure throws a subclass of
3
+ * {@link Run402Error}. Consumers (MCP handlers, CLI commands, user functions)
4
+ * translate these into their native error shapes at the edge.
5
+ */
6
+ export class Run402Error extends Error {
7
+ /** HTTP status, or null for local/network failures that produced no response. */
8
+ status;
9
+ /** Parsed response body, or null when no body was received. */
10
+ body;
11
+ /** Short verb phrase identifying the attempted operation (e.g. "provisioning project"). */
12
+ context;
13
+ constructor(message, status, body, context) {
14
+ super(message);
15
+ this.name = this.constructor.name;
16
+ this.status = status;
17
+ this.body = body;
18
+ this.context = context;
19
+ }
20
+ }
21
+ /** HTTP 402 — the gateway requires payment (lease expired, insufficient balance, or x402 quote). */
22
+ export class PaymentRequired extends Run402Error {
23
+ }
24
+ /** Project ID is not present in the credential provider (local miss) or the gateway returned 404. */
25
+ export class ProjectNotFound extends Run402Error {
26
+ projectId;
27
+ constructor(projectId, context, status = null, body = null) {
28
+ super(`Project ${projectId} not found`, status, body, context);
29
+ this.projectId = projectId;
30
+ }
31
+ }
32
+ /** HTTP 401 or 403 — authentication missing, invalid, or insufficient for the operation. */
33
+ export class Unauthorized extends Run402Error {
34
+ }
35
+ /** Any other non-2xx HTTP response from the gateway. */
36
+ export class ApiError extends Run402Error {
37
+ }
38
+ /** The underlying `fetch` threw before producing a response (DNS, connection reset, offline). */
39
+ export class NetworkError extends Run402Error {
40
+ cause;
41
+ constructor(message, cause, context) {
42
+ super(message, null, null, context);
43
+ this.cause = cause;
44
+ }
45
+ }
46
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,OAAgB,WAAY,SAAQ,KAAK;IAC7C,iFAAiF;IACxE,MAAM,CAAgB;IAC/B,+DAA+D;IACtD,IAAI,CAAU;IACvB,2FAA2F;IAClF,OAAO,CAAS;IAEzB,YAAY,OAAe,EAAE,MAAqB,EAAE,IAAa,EAAE,OAAe;QAChF,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;QAClC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;CACF;AAED,oGAAoG;AACpG,MAAM,OAAO,eAAgB,SAAQ,WAAW;CAAG;AAEnD,qGAAqG;AACrG,MAAM,OAAO,eAAgB,SAAQ,WAAW;IACrC,SAAS,CAAS;IAC3B,YAAY,SAAiB,EAAE,OAAe,EAAE,SAAwB,IAAI,EAAE,OAAgB,IAAI;QAChG,KAAK,CAAC,WAAW,SAAS,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAC/D,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC7B,CAAC;CACF;AAED,4FAA4F;AAC5F,MAAM,OAAO,YAAa,SAAQ,WAAW;CAAG;AAEhD,wDAAwD;AACxD,MAAM,OAAO,QAAS,SAAQ,WAAW;CAAG;AAE5C,iGAAiG;AACjG,MAAM,OAAO,YAAa,SAAQ,WAAW;IAClC,KAAK,CAAU;IACxB,YAAY,OAAe,EAAE,KAAc,EAAE,OAAe;QAC1D,KAAK,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QACpC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;CACF"}