run402 1.54.2 → 1.54.4
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/cli.mjs +18 -0
- package/core-dist/allowance-auth.js +5 -0
- package/core-dist/allowance.js +49 -1
- package/core-dist/config.js +35 -2
- package/core-dist/wallet-auth.js +62 -0
- package/core-dist/wallet.js +25 -0
- package/lib/agent.mjs +29 -1
- package/lib/ai.mjs +113 -37
- package/lib/apps.mjs +34 -0
- package/lib/argparse.mjs +128 -0
- package/lib/auth.mjs +15 -2
- package/lib/billing.mjs +35 -0
- package/lib/config.mjs +20 -1
- package/lib/contracts.mjs +41 -0
- package/lib/deploy-v2.mjs +37 -0
- package/lib/deploy.mjs +125 -58
- package/lib/domains.mjs +79 -5
- package/lib/email.mjs +34 -0
- package/lib/functions.mjs +25 -1
- package/lib/image.mjs +33 -1
- package/lib/message.mjs +50 -3
- package/lib/projects.mjs +43 -33
- package/lib/sdk-errors.mjs +2 -1
- package/lib/secrets.mjs +29 -0
- package/lib/sender-domain.mjs +78 -1
- package/lib/service.mjs +30 -1
- package/lib/subdomains.mjs +49 -4
- package/lib/tier.mjs +41 -1
- package/lib/webhooks.mjs +10 -0
- package/package.json +1 -1
- package/sdk/core-dist/allowance-auth.js +5 -0
- package/sdk/core-dist/allowance.js +49 -1
- package/sdk/core-dist/config.js +35 -2
- package/sdk/core-dist/wallet-auth.js +62 -0
- package/sdk/core-dist/wallet.js +25 -0
- package/sdk/dist/node/paid-fetch.d.ts.map +1 -1
- package/sdk/dist/node/paid-fetch.js +12 -1
- package/sdk/dist/node/paid-fetch.js.map +1 -1
package/lib/webhooks.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveProjectId } from "./config.mjs";
|
|
2
2
|
import { getSdk } from "./sdk.mjs";
|
|
3
3
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
4
|
+
import { validateWebhookUrl } from "./argparse.mjs";
|
|
4
5
|
|
|
5
6
|
const HELP = `run402 email webhooks — Manage mailbox webhooks
|
|
6
7
|
|
|
@@ -121,6 +122,11 @@ async function update(args) {
|
|
|
121
122
|
if (!url && !eventsRaw) {
|
|
122
123
|
fail({ code: "BAD_USAGE", message: "Provide at least --url or --events" });
|
|
123
124
|
}
|
|
125
|
+
// GH-192: scheme-only local validation. Server-side SSRF defenses are out
|
|
126
|
+
// of scope for the CLI (private-IP / DNS rebinding / IMDS belongs on the
|
|
127
|
+
// gateway). `validateWebhookUrl` is a no-op when `url` is null/undefined,
|
|
128
|
+
// so partial updates that change only `--events` still work.
|
|
129
|
+
validateWebhookUrl(url, "--url");
|
|
124
130
|
|
|
125
131
|
try {
|
|
126
132
|
const data = await getSdk().email.webhooks.update(projectId, webhookId, {
|
|
@@ -146,6 +152,10 @@ async function register(args) {
|
|
|
146
152
|
hint: "run402 email webhooks register --url <url> --events <e1,e2>",
|
|
147
153
|
});
|
|
148
154
|
}
|
|
155
|
+
// GH-192: validate scheme locally before any network call. Catches
|
|
156
|
+
// javascript:/file:/http:/data: schemes that the gateway would reject
|
|
157
|
+
// anyway, but with a friendlier round-trip-free error.
|
|
158
|
+
validateWebhookUrl(url, "--url");
|
|
149
159
|
if (!eventsRaw) {
|
|
150
160
|
fail({
|
|
151
161
|
code: "BAD_USAGE",
|
package/package.json
CHANGED
|
@@ -87,6 +87,11 @@ export function formatSIWEMessage(opts, address) {
|
|
|
87
87
|
* @param path - API path (e.g. "/projects/v1") used to build the SIWE uri field.
|
|
88
88
|
*/
|
|
89
89
|
export function getAllowanceAuthHeaders(path, allowancePath) {
|
|
90
|
+
// GH-194: readAllowance throws on a malformed-shape allowance file. The
|
|
91
|
+
// CLI's higher-level readAllowance wrapper surfaces this as a structured
|
|
92
|
+
// BAD_ALLOWANCE_FILE envelope; here we preserve the public contract that
|
|
93
|
+
// this helper returns SIWxAuthHeaders | null. Re-throw so callers above
|
|
94
|
+
// the CLI's wrapper (e.g. SDK paid-fetch) can decide whether to swallow it.
|
|
90
95
|
const allowance = readAllowance(allowancePath);
|
|
91
96
|
if (!allowance || !allowance.address || !allowance.privateKey)
|
|
92
97
|
return null;
|
|
@@ -2,16 +2,64 @@ import { readFileSync, writeFileSync, mkdirSync, existsSync, chmodSync, renameSy
|
|
|
2
2
|
import { dirname, join } from "node:path";
|
|
3
3
|
import { randomBytes } from "node:crypto";
|
|
4
4
|
import { getAllowancePath } from "./config.js";
|
|
5
|
+
// 0x-prefixed 40-hex EVM address.
|
|
6
|
+
const ADDRESS_RE = /^0x[a-fA-F0-9]{40}$/;
|
|
7
|
+
// 0x-prefixed 64-hex secp256k1 private key (32 bytes).
|
|
8
|
+
const PRIVATE_KEY_RE = /^0x[a-fA-F0-9]{64}$/;
|
|
9
|
+
/**
|
|
10
|
+
* Load the agent allowance from disk.
|
|
11
|
+
*
|
|
12
|
+
* Returns `null` for the two "no allowance configured" cases:
|
|
13
|
+
* - the file does not exist
|
|
14
|
+
* - the file exists but is not parseable JSON (preserve existing UX —
|
|
15
|
+
* consumers print "no_allowance" and tell the user to run init)
|
|
16
|
+
*
|
|
17
|
+
* Throws a structured `Error` (GH-194) when the file parses as JSON but the
|
|
18
|
+
* shape is wrong (missing/wrong-type/wrong-length fields). Without this guard
|
|
19
|
+
* downstream callers crash with raw stack traces:
|
|
20
|
+
* - `cli/lib/status.mjs` reaches for `allowance.address.toLowerCase()`
|
|
21
|
+
* and crashes with `TypeError: Cannot read properties of undefined`.
|
|
22
|
+
* - `core/src/allowance-auth.ts` passes a malformed `privateKey` to
|
|
23
|
+
* `@noble/curves` which throws "expected 32 bytes, got N".
|
|
24
|
+
*
|
|
25
|
+
* The CLI's `cli/lib/config.mjs:readAllowance()` wrapper and the MCP
|
|
26
|
+
* `src/tools/{status,init}.ts` callers translate the throw into their own
|
|
27
|
+
* structured envelopes (`code: BAD_ALLOWANCE_FILE`).
|
|
28
|
+
*/
|
|
5
29
|
export function readAllowance(path) {
|
|
6
30
|
const p = path ?? getAllowancePath();
|
|
7
31
|
if (!existsSync(p))
|
|
8
32
|
return null;
|
|
33
|
+
let raw;
|
|
9
34
|
try {
|
|
10
|
-
|
|
35
|
+
raw = readFileSync(p, "utf-8");
|
|
11
36
|
}
|
|
12
37
|
catch {
|
|
13
38
|
return null;
|
|
14
39
|
}
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = JSON.parse(raw);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Preserve historical UX — completely unparseable input reads as "no
|
|
46
|
+
// allowance configured" rather than as an error. Consumers already handle
|
|
47
|
+
// null with a friendly "run 'run402 init'" message.
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
51
|
+
throw new Error(`allowance.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.`);
|
|
52
|
+
}
|
|
53
|
+
const data = parsed;
|
|
54
|
+
if (typeof data.address !== "string" || !ADDRESS_RE.test(data.address)) {
|
|
55
|
+
throw new Error("allowance.json missing valid 'address' (expected 0x-prefixed 40-hex string). " +
|
|
56
|
+
"Back up the file and run 'run402 init' to recreate it.");
|
|
57
|
+
}
|
|
58
|
+
if (typeof data.privateKey !== "string" || !PRIVATE_KEY_RE.test(data.privateKey)) {
|
|
59
|
+
throw new Error("allowance.json missing valid 'privateKey' (expected 0x-prefixed 64-hex string). " +
|
|
60
|
+
"Back up the file and run 'run402 init' to recreate it.");
|
|
61
|
+
}
|
|
62
|
+
return data;
|
|
15
63
|
}
|
|
16
64
|
export function saveAllowance(data, path) {
|
|
17
65
|
const p = path ?? getAllowancePath();
|
package/sdk/core-dist/config.js
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
1
|
import { homedir } from "node:os";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { existsSync, renameSync, mkdirSync } from "node:fs";
|
|
4
|
+
const DEFAULT_API_BASE = "https://api.run402.com";
|
|
5
|
+
/**
|
|
6
|
+
* Validate a user-supplied API base URL. Throws a clear error message that
|
|
7
|
+
* names the env var when the URL is malformed or uses a scheme other than
|
|
8
|
+
* http(s). Empty string is treated as "set but empty" (almost always a
|
|
9
|
+
* templating mishap) and emits a stderr warning before falling back to
|
|
10
|
+
* `fallback`.
|
|
11
|
+
*
|
|
12
|
+
* Returns the validated URL string (unchanged) or `null` if the env var was
|
|
13
|
+
* unset.
|
|
14
|
+
*/
|
|
15
|
+
function validateApiBase(envVar, raw, fallback) {
|
|
16
|
+
if (raw == null)
|
|
17
|
+
return null;
|
|
18
|
+
if (raw === "") {
|
|
19
|
+
process.stderr.write(`warning: ${envVar} is set but empty - using default. Unset the env var to suppress this warning.\n`);
|
|
20
|
+
return fallback;
|
|
21
|
+
}
|
|
22
|
+
let u;
|
|
23
|
+
try {
|
|
24
|
+
u = new URL(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error(`${envVar} is not a valid URL: ${JSON.stringify(raw)}. Expected an http(s) URL like https://api.run402.com.`);
|
|
28
|
+
}
|
|
29
|
+
if (u.protocol !== "https:" && u.protocol !== "http:") {
|
|
30
|
+
throw new Error(`${envVar} must use http(s):, got ${u.protocol} (full value: ${JSON.stringify(raw)}).`);
|
|
31
|
+
}
|
|
32
|
+
return raw;
|
|
33
|
+
}
|
|
4
34
|
export function getApiBase() {
|
|
5
|
-
|
|
35
|
+
const validated = validateApiBase("RUN402_API_BASE", process.env.RUN402_API_BASE, DEFAULT_API_BASE);
|
|
36
|
+
return validated ?? DEFAULT_API_BASE;
|
|
6
37
|
}
|
|
7
38
|
/**
|
|
8
39
|
* API base for the deploy-v2 routes. Defaults to the same value as
|
|
@@ -12,7 +43,9 @@ export function getApiBase() {
|
|
|
12
43
|
* should not need this override.
|
|
13
44
|
*/
|
|
14
45
|
export function getDeployApiBase() {
|
|
15
|
-
|
|
46
|
+
const fallback = getApiBase();
|
|
47
|
+
const validated = validateApiBase("RUN402_DEPLOY_API_BASE", process.env.RUN402_DEPLOY_API_BASE, fallback);
|
|
48
|
+
return validated ?? fallback;
|
|
16
49
|
}
|
|
17
50
|
export function getConfigDir() {
|
|
18
51
|
return process.env.RUN402_CONFIG_DIR || join(homedir(), ".config", "run402");
|
|
@@ -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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paid-fetch.d.ts","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,KAAK,OAAO,GAAG,OAAO,UAAU,CAAC,KAAK,CAAC;AAgCvC,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"paid-fetch.d.ts","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,KAAK,OAAO,GAAG,OAAO,UAAU,CAAC,KAAK,CAAC;AAgCvC,wBAAsB,cAAc,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAmF9D;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,IAAI,OAAO,CAW7C"}
|
|
@@ -37,7 +37,18 @@ async function checkBalance(publicClient, tokenAddress, walletAddress) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
export async function setupPaidFetch() {
|
|
40
|
-
|
|
40
|
+
// GH-194: readAllowance throws on a malformed-shape file. The SDK's
|
|
41
|
+
// contract is graceful degradation here (return null and let the caller
|
|
42
|
+
// fall back to unwrapped fetch), so swallow the error — the CLI's own
|
|
43
|
+
// readAllowance wrapper has already surfaced a structured envelope to
|
|
44
|
+
// the user.
|
|
45
|
+
let allowance;
|
|
46
|
+
try {
|
|
47
|
+
allowance = readAllowance();
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
41
52
|
if (!allowance)
|
|
42
53
|
return null;
|
|
43
54
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paid-fetch.js","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAI7D,MAAM,QAAQ,GAAG;IACf;QACE,IAAI,EAAE,WAAW;QACjB,IAAI,EAAE,UAAU;QAChB,eAAe,EAAE,MAAM;QACvB,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;QAC9C,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;KACzC;CACO,CAAC;AACX,MAAM,YAAY,GAAG,4CAA4C,CAAC;AAClE,MAAM,YAAY,GAAG,4CAA4C,CAAC;AAElE,KAAK,UAAU,YAAY,CACzB,YAAkE,EAClE,YAAoB,EACpB,aAAqB;IAErB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC;YAC1C,OAAO,EAAE,YAAY;YACrB,GAAG,EAAE,QAAQ;YACb,YAAY,EAAE,WAAW;YACzB,IAAI,EAAE,CAAC,aAAa,CAAC;SACtB,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,
|
|
1
|
+
{"version":3,"file":"paid-fetch.js","sourceRoot":"","sources":["../../src/node/paid-fetch.ts"],"names":[],"mappings":"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"}
|