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.
- package/cli.mjs +6 -0
- package/lib/billing.mjs +169 -0
- 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);
|
package/lib/billing.mjs
ADDED
|
@@ -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