run402 1.13.5 → 1.14.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/cli.mjs +5 -3
- package/lib/allowance.mjs +56 -11
- package/lib/init.mjs +109 -37
- package/lib/paid-fetch.mjs +16 -5
- package/lib/tier.mjs +8 -1
- package/package.json +3 -2
package/cli.mjs
CHANGED
|
@@ -19,7 +19,8 @@ Usage:
|
|
|
19
19
|
run402 <command> [subcommand] [options]
|
|
20
20
|
|
|
21
21
|
Commands:
|
|
22
|
-
init Set up allowance, funding, and check tier status
|
|
22
|
+
init Set up allowance, funding, and check tier status (x402 default)
|
|
23
|
+
init mpp Set up with MPP payment rail (Tempo Moderato testnet)
|
|
23
24
|
status Show full account state (allowance, balance, tier, projects)
|
|
24
25
|
allowance Manage your agent allowance (create, fund, balance, status)
|
|
25
26
|
tier Manage tier subscription (status, set)
|
|
@@ -31,7 +32,7 @@ Commands:
|
|
|
31
32
|
sites Deploy static sites
|
|
32
33
|
subdomains Manage custom subdomains (claim, list, delete)
|
|
33
34
|
apps Browse and manage the app marketplace
|
|
34
|
-
image Generate AI images via x402 micropayments
|
|
35
|
+
image Generate AI images via x402 or MPP micropayments
|
|
35
36
|
message Send messages to Run402 developers
|
|
36
37
|
agent Manage agent identity (contact info)
|
|
37
38
|
|
|
@@ -48,7 +49,8 @@ Examples:
|
|
|
48
49
|
run402 image generate "a startup mascot, pixel art" --output logo.png
|
|
49
50
|
|
|
50
51
|
Getting started:
|
|
51
|
-
run402 init Set up
|
|
52
|
+
run402 init Set up with x402 (Base Sepolia)
|
|
53
|
+
run402 init mpp Set up with MPP (Tempo Moderato)
|
|
52
54
|
run402 tier set prototype Subscribe to a tier
|
|
53
55
|
run402 deploy --manifest app.json
|
|
54
56
|
`;
|
package/lib/allowance.mjs
CHANGED
|
@@ -6,24 +6,25 @@ Usage:
|
|
|
6
6
|
run402 allowance <subcommand>
|
|
7
7
|
|
|
8
8
|
Subcommands:
|
|
9
|
-
status Show allowance address, network, and funding status
|
|
9
|
+
status Show allowance address, network, rail, and funding status
|
|
10
10
|
create Generate a new allowance and save it locally
|
|
11
|
-
fund Request test
|
|
12
|
-
balance Show on-chain
|
|
11
|
+
fund Request test funds from the faucet (Base Sepolia or Tempo)
|
|
12
|
+
balance Show on-chain balances and Run402 billing balance
|
|
13
13
|
export Print the allowance address (useful for scripting)
|
|
14
14
|
checkout Create a billing checkout session (--amount <usd_micros>)
|
|
15
15
|
history View billing transaction history (--limit <n>)
|
|
16
16
|
|
|
17
17
|
Notes:
|
|
18
|
-
- Agent allowance is stored locally at ~/.run402/allowance.json
|
|
19
|
-
- The allowance works on any EVM chain (
|
|
20
|
-
-
|
|
18
|
+
- Agent allowance is stored locally at ~/.config/run402/allowance.json
|
|
19
|
+
- The allowance works on any EVM chain (Base for x402, Tempo for MPP)
|
|
20
|
+
- Use 'run402 init' for x402 or 'run402 init mpp' for MPP rail
|
|
21
21
|
|
|
22
22
|
Examples:
|
|
23
23
|
run402 allowance create
|
|
24
24
|
run402 allowance status
|
|
25
25
|
run402 allowance fund
|
|
26
26
|
run402 allowance export
|
|
27
|
+
run402 allowance balance
|
|
27
28
|
run402 allowance checkout --amount 5000000
|
|
28
29
|
run402 allowance history --limit 10
|
|
29
30
|
`;
|
|
@@ -31,12 +32,20 @@ Examples:
|
|
|
31
32
|
const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
|
|
32
33
|
const USDC_MAINNET = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
33
34
|
const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
35
|
+
const PATH_USD = "0x20c0000000000000000000000000000000000000";
|
|
36
|
+
const TEMPO_RPC = "https://rpc.moderato.tempo.xyz/";
|
|
34
37
|
|
|
35
38
|
async function loadDeps() {
|
|
36
39
|
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
37
|
-
const { createPublicClient, http } = await import("viem");
|
|
40
|
+
const { createPublicClient, http, defineChain } = await import("viem");
|
|
38
41
|
const { base, baseSepolia } = await import("viem/chains");
|
|
39
|
-
|
|
42
|
+
const tempoModerato = defineChain({
|
|
43
|
+
id: 42431,
|
|
44
|
+
name: "Tempo Moderato",
|
|
45
|
+
nativeCurrency: { name: "pathUSD", symbol: "pathUSD", decimals: 6 },
|
|
46
|
+
rpcUrls: { default: { http: [TEMPO_RPC] } },
|
|
47
|
+
});
|
|
48
|
+
return { generatePrivateKey, privateKeyToAccount, createPublicClient, http, base, baseSepolia, tempoModerato };
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
async function status() {
|
|
@@ -45,7 +54,7 @@ async function status() {
|
|
|
45
54
|
console.log(JSON.stringify({ status: "no_wallet", message: "No agent allowance found. Run: run402 allowance create" }));
|
|
46
55
|
return;
|
|
47
56
|
}
|
|
48
|
-
console.log(JSON.stringify({ status: "ok", address: w.address, created: w.created, funded: w.funded || false, path: ALLOWANCE_FILE }));
|
|
57
|
+
console.log(JSON.stringify({ status: "ok", address: w.address, created: w.created, funded: w.funded || false, rail: w.rail || "x402", path: ALLOWANCE_FILE }));
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
async function create() {
|
|
@@ -64,6 +73,37 @@ async function fund() {
|
|
|
64
73
|
const w = readAllowance();
|
|
65
74
|
if (!w) { console.log(JSON.stringify({ status: "error", message: "No agent allowance. Run: run402 allowance create" })); process.exit(1); }
|
|
66
75
|
|
|
76
|
+
if (w.rail === "mpp") {
|
|
77
|
+
// Tempo Moderato faucet — instant, no polling needed
|
|
78
|
+
const { createPublicClient, http, tempoModerato } = await loadDeps();
|
|
79
|
+
const client = createPublicClient({ chain: tempoModerato, transport: http() });
|
|
80
|
+
const before = await readUsdcBalance(client, PATH_USD, w.address).catch(() => 0);
|
|
81
|
+
|
|
82
|
+
const res = await fetch(TEMPO_RPC, {
|
|
83
|
+
method: "POST",
|
|
84
|
+
headers: { "Content-Type": "application/json" },
|
|
85
|
+
body: JSON.stringify({ jsonrpc: "2.0", method: "tempo_fundAddress", params: [w.address], id: 1 }),
|
|
86
|
+
});
|
|
87
|
+
const data = await res.json();
|
|
88
|
+
if (data.error) {
|
|
89
|
+
console.log(JSON.stringify({ status: "error", message: data.error.message || "Tempo faucet failed" }));
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Re-read balance once (instant confirmation)
|
|
94
|
+
const now = await readUsdcBalance(client, PATH_USD, w.address).catch(() => before);
|
|
95
|
+
saveAllowance({ ...w, funded: true, lastFaucet: new Date().toISOString() });
|
|
96
|
+
console.log(JSON.stringify({
|
|
97
|
+
address: w.address,
|
|
98
|
+
rail: "mpp",
|
|
99
|
+
onchain: {
|
|
100
|
+
"tempo-moderato_pathusd_micros": now,
|
|
101
|
+
},
|
|
102
|
+
}, null, 2));
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Default: Base Sepolia faucet (existing behavior)
|
|
67
107
|
const { createPublicClient, http, baseSepolia } = await loadDeps();
|
|
68
108
|
const client = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
69
109
|
const before = await readUsdcBalance(client, USDC_SEPOLIA, w.address).catch(() => 0);
|
|
@@ -83,6 +123,7 @@ async function fund() {
|
|
|
83
123
|
saveAllowance({ ...w, funded: true, lastFaucet: new Date().toISOString() });
|
|
84
124
|
console.log(JSON.stringify({
|
|
85
125
|
address: w.address,
|
|
126
|
+
rail: w.rail || "x402",
|
|
86
127
|
onchain: {
|
|
87
128
|
"base-sepolia_usd_micros": now,
|
|
88
129
|
},
|
|
@@ -104,13 +145,15 @@ async function balance() {
|
|
|
104
145
|
const w = readAllowance();
|
|
105
146
|
if (!w) { console.log(JSON.stringify({ status: "error", message: "No agent allowance. Run: run402 allowance create" })); process.exit(1); }
|
|
106
147
|
|
|
107
|
-
const { createPublicClient, http, base, baseSepolia } = await loadDeps();
|
|
148
|
+
const { createPublicClient, http, base, baseSepolia, tempoModerato } = await loadDeps();
|
|
108
149
|
const mainnetClient = createPublicClient({ chain: base, transport: http() });
|
|
109
150
|
const sepoliaClient = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
151
|
+
const tempoClient = createPublicClient({ chain: tempoModerato, transport: http() });
|
|
110
152
|
|
|
111
|
-
const [mainnetUsdc, sepoliaUsdc, billingRes] = await Promise.all([
|
|
153
|
+
const [mainnetUsdc, sepoliaUsdc, tempoPathUsd, billingRes] = await Promise.all([
|
|
112
154
|
readUsdcBalance(mainnetClient, USDC_MAINNET, w.address).catch(() => null),
|
|
113
155
|
readUsdcBalance(sepoliaClient, USDC_SEPOLIA, w.address).catch(() => null),
|
|
156
|
+
readUsdcBalance(tempoClient, PATH_USD, w.address).catch(() => null),
|
|
114
157
|
fetch(`${API}/billing/v1/accounts/${w.address.toLowerCase()}`),
|
|
115
158
|
]);
|
|
116
159
|
|
|
@@ -118,9 +161,11 @@ async function balance() {
|
|
|
118
161
|
|
|
119
162
|
console.log(JSON.stringify({
|
|
120
163
|
address: w.address,
|
|
164
|
+
rail: w.rail || "x402",
|
|
121
165
|
onchain: {
|
|
122
166
|
"base-mainnet_usd_micros": mainnetUsdc,
|
|
123
167
|
"base-sepolia_usd_micros": sepoliaUsdc,
|
|
168
|
+
"tempo-moderato_pathusd_micros": tempoPathUsd,
|
|
124
169
|
},
|
|
125
170
|
run402: billing ? { balance_usd_micros: billing.available_usd_micros } : "no billing account",
|
|
126
171
|
}, null, 2));
|
package/lib/init.mjs
CHANGED
|
@@ -4,16 +4,19 @@ import { mkdirSync } from "fs";
|
|
|
4
4
|
|
|
5
5
|
const USDC_ABI = [{ name: "balanceOf", type: "function", stateMutability: "view", inputs: [{ name: "account", type: "address" }], outputs: [{ name: "", type: "uint256" }] }];
|
|
6
6
|
const USDC_SEPOLIA = "0x036CbD53842c5426634e7929541eC2318f3dCF7e";
|
|
7
|
+
const PATH_USD = "0x20c0000000000000000000000000000000000000";
|
|
8
|
+
const TEMPO_RPC = "https://rpc.moderato.tempo.xyz/";
|
|
7
9
|
|
|
8
10
|
const HELP = `run402 init — Set up allowance, funding, and check tier status
|
|
9
11
|
|
|
10
12
|
Usage:
|
|
11
|
-
run402 init
|
|
13
|
+
run402 init Set up with x402 (Base Sepolia) — default
|
|
14
|
+
run402 init mpp Set up with MPP (Tempo Moderato)
|
|
12
15
|
|
|
13
16
|
Steps (idempotent — safe to re-run):
|
|
14
17
|
1. Creates config directory (~/.config/run402)
|
|
15
18
|
2. Creates agent allowance if none exists
|
|
16
|
-
3. Checks on-chain
|
|
19
|
+
3. Checks on-chain balance; requests faucet if zero
|
|
17
20
|
4. Shows current tier subscription status
|
|
18
21
|
5. Lists local project count
|
|
19
22
|
6. Suggests next step (tier set or deploy)
|
|
@@ -26,6 +29,7 @@ function line(label, value) { console.log(` ${label.padEnd(10)} ${value}`); }
|
|
|
26
29
|
|
|
27
30
|
export async function run(args = []) {
|
|
28
31
|
if (args.includes("--help") || args.includes("-h")) { console.log(HELP); process.exit(0); }
|
|
32
|
+
const isMpp = args[0] === "mpp";
|
|
29
33
|
console.log();
|
|
30
34
|
|
|
31
35
|
// 1. Config directory
|
|
@@ -34,58 +38,126 @@ export async function run(args = []) {
|
|
|
34
38
|
|
|
35
39
|
// 2. Allowance
|
|
36
40
|
let allowance = readAllowance();
|
|
41
|
+
const previousRail = allowance?.rail;
|
|
37
42
|
if (!allowance) {
|
|
38
43
|
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
39
44
|
const privateKey = generatePrivateKey();
|
|
40
45
|
const account = privateKeyToAccount(privateKey);
|
|
41
|
-
allowance = { address: account.address, privateKey, created: new Date().toISOString(), funded: false };
|
|
46
|
+
allowance = { address: account.address, privateKey, created: new Date().toISOString(), funded: false, rail: isMpp ? "mpp" : "x402" };
|
|
42
47
|
saveAllowance(allowance);
|
|
43
48
|
line("Allowance", `${short(allowance.address)} (created)`);
|
|
44
49
|
} else {
|
|
50
|
+
// Update rail if switching
|
|
51
|
+
if ((isMpp && allowance.rail !== "mpp") || (!isMpp && allowance.rail === "mpp")) {
|
|
52
|
+
allowance = { ...allowance, rail: isMpp ? "mpp" : "x402" };
|
|
53
|
+
saveAllowance(allowance);
|
|
54
|
+
} else if (!allowance.rail) {
|
|
55
|
+
allowance = { ...allowance, rail: isMpp ? "mpp" : "x402" };
|
|
56
|
+
saveAllowance(allowance);
|
|
57
|
+
}
|
|
45
58
|
line("Allowance", short(allowance.address));
|
|
46
59
|
}
|
|
47
60
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
const { baseSepolia } = await import("viem/chains");
|
|
51
|
-
const client = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
61
|
+
line("Network", isMpp ? "Tempo Moderato (testnet)" : "Base Sepolia (testnet)");
|
|
62
|
+
line("Rail", isMpp ? "mpp" : "x402");
|
|
52
63
|
|
|
64
|
+
// 3. Balance — check on-chain, faucet if zero
|
|
53
65
|
let balance = 0;
|
|
54
|
-
try {
|
|
55
|
-
const raw = await client.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] });
|
|
56
|
-
balance = Number(raw);
|
|
57
|
-
} catch {}
|
|
58
66
|
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
if (isMpp) {
|
|
68
|
+
// Tempo Moderato: read pathUSD balance
|
|
69
|
+
const { createPublicClient, http, defineChain } = await import("viem");
|
|
70
|
+
const tempoModerato = defineChain({
|
|
71
|
+
id: 42431,
|
|
72
|
+
name: "Tempo Moderato",
|
|
73
|
+
nativeCurrency: { name: "pathUSD", symbol: "pathUSD", decimals: 6 },
|
|
74
|
+
rpcUrls: { default: { http: [TEMPO_RPC] } },
|
|
65
75
|
});
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
76
|
+
const client = createPublicClient({ chain: tempoModerato, transport: http() });
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
const raw = await client.readContract({ address: PATH_USD, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] });
|
|
80
|
+
balance = Number(raw);
|
|
81
|
+
} catch {}
|
|
82
|
+
|
|
83
|
+
if (balance === 0) {
|
|
84
|
+
line("Balance", "0 pathUSD — requesting Tempo faucet...");
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(TEMPO_RPC, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
headers: { "Content-Type": "application/json" },
|
|
89
|
+
body: JSON.stringify({ jsonrpc: "2.0", method: "tempo_fundAddress", params: [allowance.address], id: 1 }),
|
|
90
|
+
});
|
|
91
|
+
const data = await res.json();
|
|
92
|
+
if (data.result) {
|
|
93
|
+
// Tempo faucet is instant — re-read balance once
|
|
94
|
+
try {
|
|
95
|
+
const raw = await client.readContract({ address: PATH_USD, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] });
|
|
96
|
+
balance = Number(raw);
|
|
97
|
+
} catch {}
|
|
98
|
+
saveAllowance({ ...allowance, funded: true, lastFaucet: new Date().toISOString() });
|
|
99
|
+
if (balance > 0) {
|
|
100
|
+
line("Balance", `${(balance / 1e6).toFixed(2)} pathUSD (funded)`);
|
|
101
|
+
} else {
|
|
102
|
+
line("Balance", "faucet sent — checking balance...");
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
line("Balance", `faucet failed: ${data.error?.message || "unknown error"}`);
|
|
106
|
+
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
line("Balance", `faucet error: ${err.message}`);
|
|
75
109
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
110
|
+
} else {
|
|
111
|
+
line("Balance", `${(balance / 1e6).toFixed(2)} pathUSD`);
|
|
112
|
+
}
|
|
113
|
+
} else {
|
|
114
|
+
// Base Sepolia: read USDC balance (existing behavior)
|
|
115
|
+
const { createPublicClient, http } = await import("viem");
|
|
116
|
+
const { baseSepolia } = await import("viem/chains");
|
|
117
|
+
const client = createPublicClient({ chain: baseSepolia, transport: http() });
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const raw = await client.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] });
|
|
121
|
+
balance = Number(raw);
|
|
122
|
+
} catch {}
|
|
123
|
+
|
|
124
|
+
if (balance === 0) {
|
|
125
|
+
line("Balance", "0 USDC — requesting faucet...");
|
|
126
|
+
const res = await fetch(`${API}/faucet/v1`, {
|
|
127
|
+
method: "POST",
|
|
128
|
+
headers: { "Content-Type": "application/json" },
|
|
129
|
+
body: JSON.stringify({ address: allowance.address }),
|
|
130
|
+
});
|
|
131
|
+
if (res.ok) {
|
|
132
|
+
// Poll for up to 30s
|
|
133
|
+
for (let i = 0; i < 30; i++) {
|
|
134
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
135
|
+
try {
|
|
136
|
+
const raw = await client.readContract({ address: USDC_SEPOLIA, abi: USDC_ABI, functionName: "balanceOf", args: [allowance.address] });
|
|
137
|
+
balance = Number(raw);
|
|
138
|
+
if (balance > 0) break;
|
|
139
|
+
} catch {}
|
|
140
|
+
}
|
|
141
|
+
saveAllowance({ ...allowance, funded: true, lastFaucet: new Date().toISOString() });
|
|
142
|
+
if (balance > 0) {
|
|
143
|
+
line("Balance", `${(balance / 1e6).toFixed(2)} USDC (funded)`);
|
|
144
|
+
} else {
|
|
145
|
+
line("Balance", "faucet sent — not yet confirmed on-chain");
|
|
146
|
+
}
|
|
79
147
|
} else {
|
|
80
|
-
|
|
148
|
+
const data = await res.json().catch(() => ({}));
|
|
149
|
+
const msg = data.error || data.message || `HTTP ${res.status}`;
|
|
150
|
+
line("Balance", `faucet failed: ${msg}`);
|
|
81
151
|
}
|
|
82
152
|
} else {
|
|
83
|
-
|
|
84
|
-
const msg = data.error || data.message || `HTTP ${res.status}`;
|
|
85
|
-
line("Balance", `faucet failed: ${msg}`);
|
|
153
|
+
line("Balance", `${(balance / 1e6).toFixed(2)} USDC`);
|
|
86
154
|
}
|
|
87
|
-
}
|
|
88
|
-
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Show note if switching rails
|
|
158
|
+
if (previousRail && previousRail !== (isMpp ? "mpp" : "x402")) {
|
|
159
|
+
const prev = previousRail === "mpp" ? "Tempo pathUSD" : "Base Sepolia USDC";
|
|
160
|
+
line("Note", `Switched from ${previousRail} — ${prev} balance still available if you switch back`);
|
|
89
161
|
}
|
|
90
162
|
|
|
91
163
|
// 4. Tier status
|
|
@@ -101,7 +173,7 @@ export async function run(args = []) {
|
|
|
101
173
|
}
|
|
102
174
|
} catch {}
|
|
103
175
|
|
|
104
|
-
if (tierInfo && tierInfo.tier && tierInfo.
|
|
176
|
+
if (tierInfo && tierInfo.tier && tierInfo.active) {
|
|
105
177
|
const expiry = tierInfo.lease_expires_at ? tierInfo.lease_expires_at.split("T")[0] : "unknown";
|
|
106
178
|
line("Tier", `${tierInfo.tier} (expires ${expiry})`);
|
|
107
179
|
} else {
|
|
@@ -113,7 +185,7 @@ export async function run(args = []) {
|
|
|
113
185
|
|
|
114
186
|
// 6. Next step
|
|
115
187
|
console.log();
|
|
116
|
-
if (!tierInfo || !tierInfo.tier || tierInfo.
|
|
188
|
+
if (!tierInfo || !tierInfo.tier || !tierInfo.active) {
|
|
117
189
|
console.log(" Next: run402 tier set prototype");
|
|
118
190
|
} else {
|
|
119
191
|
console.log(" Ready to deploy. Run: run402 deploy --manifest app.json");
|
package/lib/paid-fetch.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* Shared payment wrapper for CLI commands that need paid fetch.
|
|
3
|
+
* Branches on allowance rail:
|
|
4
|
+
* - "mpp": uses mppx.fetch (Tempo pathUSD)
|
|
5
|
+
* - "x402" (default): uses @x402/fetch (Base USDC)
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { readAllowance, ALLOWANCE_FILE } from "./config.mjs";
|
|
@@ -15,12 +15,23 @@ export async function setupPaidFetch() {
|
|
|
15
15
|
}
|
|
16
16
|
const allowance = readAllowance();
|
|
17
17
|
const { privateKeyToAccount } = await import("viem/accounts");
|
|
18
|
+
const account = privateKeyToAccount(allowance.privateKey);
|
|
19
|
+
|
|
20
|
+
if (allowance.rail === "mpp") {
|
|
21
|
+
const { Mppx, tempo } = await import("mppx/client");
|
|
22
|
+
const mppx = Mppx.create({
|
|
23
|
+
polyfill: false,
|
|
24
|
+
methods: [tempo({ account })],
|
|
25
|
+
});
|
|
26
|
+
return mppx.fetch;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Default: x402 (existing behavior)
|
|
18
30
|
const { createPublicClient, http } = await import("viem");
|
|
19
31
|
const { base, baseSepolia } = await import("viem/chains");
|
|
20
32
|
const { x402Client, wrapFetchWithPayment } = await import("@x402/fetch");
|
|
21
33
|
const { ExactEvmScheme } = await import("@x402/evm/exact/client");
|
|
22
34
|
const { toClientEvmSigner } = await import("@x402/evm");
|
|
23
|
-
const account = privateKeyToAccount(allowance.privateKey);
|
|
24
35
|
|
|
25
36
|
const mainnetClient = createPublicClient({ chain: base, transport: http() });
|
|
26
37
|
const sepoliaClient = createPublicClient({ chain: baseSepolia, transport: http() });
|
package/lib/tier.mjs
CHANGED
|
@@ -38,7 +38,14 @@ async function set(tierName) {
|
|
|
38
38
|
if (!tierName) { console.error(JSON.stringify({ status: "error", message: "Usage: run402 tier set <prototype|hobby|team>" })); process.exit(1); }
|
|
39
39
|
const fetchPaid = await setupPaidFetch();
|
|
40
40
|
const res = await fetchPaid(`${API}/tiers/v1/${tierName}`, { method: "POST", headers: { "Content-Type": "application/json" } });
|
|
41
|
-
const
|
|
41
|
+
const text = await res.text();
|
|
42
|
+
let data;
|
|
43
|
+
try {
|
|
44
|
+
data = JSON.parse(text);
|
|
45
|
+
} catch {
|
|
46
|
+
console.error(JSON.stringify({ status: "error", http: res.status, message: "Non-JSON response from server", body: text.slice(0, 500) }));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
42
49
|
if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
|
|
43
50
|
console.log(JSON.stringify(data, null, 2));
|
|
44
51
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "run402",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "CLI for Run402 — provision Postgres databases, deploy static sites, generate images, and manage wallets via x402 micropayments.",
|
|
3
|
+
"version": "1.14.0",
|
|
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"
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"@noble/hashes": "^2.0.1",
|
|
20
20
|
"@x402/evm": "^2.6.0",
|
|
21
21
|
"@x402/fetch": "^2.6.0",
|
|
22
|
+
"mppx": "^0.4.7",
|
|
22
23
|
"viem": "^2.47.1"
|
|
23
24
|
},
|
|
24
25
|
"engines": {
|