run402 1.27.0 → 1.28.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 (3) hide show
  1. package/cli.mjs +6 -0
  2. package/lib/billing.mjs +169 -0
  3. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -39,6 +39,7 @@ Commands:
39
39
  message Send messages to Run402 developers
40
40
  auth Manage project user authentication (magic link, passwords, settings)
41
41
  sender-domain Manage custom email sender domain (register, status, remove)
42
+ billing Email billing accounts, Stripe tier checkout, email packs
42
43
  agent Manage agent identity (contact info)
43
44
 
44
45
  Run 'run402 <command> --help' for detailed usage of each command.
@@ -171,6 +172,11 @@ switch (cmd) {
171
172
  await run(sub, rest);
172
173
  break;
173
174
  }
175
+ case "billing": {
176
+ const { run } = await import("./lib/billing.mjs");
177
+ await run(sub, rest);
178
+ break;
179
+ }
174
180
  default:
175
181
  console.error(`Unknown command: ${cmd}\n`);
176
182
  console.log(HELP);
@@ -0,0 +1,169 @@
1
+ import { API } from "./config.mjs";
2
+
3
+ const HELP = `run402 billing — Email billing accounts, Stripe tier checkout, email packs
4
+
5
+ Usage:
6
+ run402 billing <subcommand> [args...]
7
+
8
+ Subcommands:
9
+ create-email <email> Create an email billing account
10
+ link-wallet <account_id> <wallet> Link a wallet to an email account
11
+ tier-checkout <tier> [--email <e> | --wallet <w>] Stripe tier checkout
12
+ buy-pack [--email <e> | --wallet <w>] Buy \$5 email pack (10,000 emails)
13
+ auto-recharge <account_id> <on|off> [--threshold <n>]
14
+ balance <identifier> Balance by email or wallet (0x...)
15
+ history <identifier> [--limit <n>] Ledger history by email or wallet
16
+
17
+ Examples:
18
+ run402 billing create-email user@example.com
19
+ run402 billing tier-checkout hobby --email user@example.com
20
+ run402 billing buy-pack --wallet 0x1234...
21
+ run402 billing auto-recharge acct_abc on --threshold 2000
22
+ run402 billing balance user@example.com
23
+ `;
24
+
25
+ function parseFlag(args, flag) {
26
+ for (let i = 0; i < args.length; i++) {
27
+ if (args[i] === flag && args[i + 1]) return args[i + 1];
28
+ }
29
+ return null;
30
+ }
31
+
32
+ async function createEmail(args) {
33
+ const email = args[0];
34
+ if (!email) {
35
+ console.error(JSON.stringify({ status: "error", message: "Missing email. Usage: run402 billing create-email <email>" }));
36
+ process.exit(1);
37
+ }
38
+ const res = await fetch(`${API}/billing/v1/accounts`, {
39
+ method: "POST",
40
+ headers: { "Content-Type": "application/json" },
41
+ body: JSON.stringify({ email }),
42
+ });
43
+ const data = await res.json();
44
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
45
+ console.log(JSON.stringify(data, null, 2));
46
+ }
47
+
48
+ async function linkWallet(args) {
49
+ const accountId = args[0];
50
+ const wallet = args[1];
51
+ if (!accountId || !wallet) {
52
+ console.error(JSON.stringify({ status: "error", message: "Usage: run402 billing link-wallet <account_id> <wallet>" }));
53
+ process.exit(1);
54
+ }
55
+ const res = await fetch(`${API}/billing/v1/accounts/${encodeURIComponent(accountId)}/link-wallet`, {
56
+ method: "POST",
57
+ headers: { "Content-Type": "application/json" },
58
+ body: JSON.stringify({ wallet }),
59
+ });
60
+ const data = await res.json();
61
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
62
+ console.log(JSON.stringify(data, null, 2));
63
+ }
64
+
65
+ async function tierCheckout(args) {
66
+ const tier = args[0];
67
+ if (!tier) {
68
+ console.error(JSON.stringify({ status: "error", message: "Usage: run402 billing tier-checkout <tier> [--email <e> | --wallet <w>]" }));
69
+ process.exit(1);
70
+ }
71
+ const email = parseFlag(args, "--email");
72
+ const wallet = parseFlag(args, "--wallet");
73
+ if (!email && !wallet) {
74
+ console.error(JSON.stringify({ status: "error", message: "Must provide --email or --wallet" }));
75
+ process.exit(1);
76
+ }
77
+ const body = email ? { email } : { wallet };
78
+ const res = await fetch(`${API}/billing/v1/tiers/${encodeURIComponent(tier)}/checkout`, {
79
+ method: "POST",
80
+ headers: { "Content-Type": "application/json" },
81
+ body: JSON.stringify(body),
82
+ });
83
+ const data = await res.json();
84
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
85
+ console.log(JSON.stringify(data, null, 2));
86
+ }
87
+
88
+ async function buyPack(args) {
89
+ const email = parseFlag(args, "--email");
90
+ const wallet = parseFlag(args, "--wallet");
91
+ if (!email && !wallet) {
92
+ console.error(JSON.stringify({ status: "error", message: "Must provide --email or --wallet" }));
93
+ process.exit(1);
94
+ }
95
+ const body = email ? { email } : { wallet };
96
+ const res = await fetch(`${API}/billing/v1/email-packs/checkout`, {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify(body),
100
+ });
101
+ const data = await res.json();
102
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
103
+ console.log(JSON.stringify(data, null, 2));
104
+ }
105
+
106
+ async function autoRecharge(args) {
107
+ const accountId = args[0];
108
+ const state = args[1];
109
+ if (!accountId || !state || !["on", "off"].includes(state)) {
110
+ console.error(JSON.stringify({ status: "error", message: "Usage: run402 billing auto-recharge <account_id> <on|off> [--threshold <n>]" }));
111
+ process.exit(1);
112
+ }
113
+ const thresholdStr = parseFlag(args, "--threshold");
114
+ const body = {
115
+ billing_account_id: accountId,
116
+ enabled: state === "on",
117
+ };
118
+ if (thresholdStr) body.threshold = Number(thresholdStr);
119
+ const res = await fetch(`${API}/billing/v1/email-packs/auto-recharge`, {
120
+ method: "POST",
121
+ headers: { "Content-Type": "application/json" },
122
+ body: JSON.stringify(body),
123
+ });
124
+ const data = await res.json();
125
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
126
+ console.log(JSON.stringify(data, null, 2));
127
+ }
128
+
129
+ async function balance(args) {
130
+ const id = args[0];
131
+ if (!id) {
132
+ console.error(JSON.stringify({ status: "error", message: "Usage: run402 billing balance <email-or-wallet>" }));
133
+ process.exit(1);
134
+ }
135
+ const res = await fetch(`${API}/billing/v1/accounts/${encodeURIComponent(id)}`);
136
+ const data = await res.json();
137
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
138
+ console.log(JSON.stringify(data, null, 2));
139
+ }
140
+
141
+ async function history(args) {
142
+ const id = args[0];
143
+ if (!id) {
144
+ console.error(JSON.stringify({ status: "error", message: "Usage: run402 billing history <email-or-wallet> [--limit <n>]" }));
145
+ process.exit(1);
146
+ }
147
+ const limit = parseFlag(args, "--limit") || "50";
148
+ const res = await fetch(`${API}/billing/v1/accounts/${encodeURIComponent(id)}/history?limit=${encodeURIComponent(limit)}`);
149
+ const data = await res.json();
150
+ if (!res.ok) { console.error(JSON.stringify({ status: "error", http: res.status, ...data })); process.exit(1); }
151
+ console.log(JSON.stringify(data, null, 2));
152
+ }
153
+
154
+ export async function run(sub, args) {
155
+ if (!sub || sub === "--help" || sub === "-h") { console.log(HELP); process.exit(0); }
156
+ switch (sub) {
157
+ case "create-email": await createEmail(args); break;
158
+ case "link-wallet": await linkWallet(args); break;
159
+ case "tier-checkout": await tierCheckout(args); break;
160
+ case "buy-pack": await buyPack(args); break;
161
+ case "auto-recharge": await autoRecharge(args); break;
162
+ case "balance": await balance(args); break;
163
+ case "history": await history(args); break;
164
+ default:
165
+ console.error(`Unknown subcommand: ${sub}\n`);
166
+ console.log(HELP);
167
+ process.exit(1);
168
+ }
169
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "run402",
3
- "version": "1.27.0",
3
+ "version": "1.28.0",
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": {