run402 2.42.0 → 2.44.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 +1 -1
- package/lib/admin.mjs +22 -22
- package/lib/allowance.mjs +1 -1
- package/lib/billing.mjs +48 -48
- package/lib/operator.mjs +3 -3
- package/lib/projects.mjs +137 -9
- package/lib/sdk-errors.mjs +1 -1
- package/lib/service.mjs +1 -1
- package/lib/status.mjs +10 -7
- package/lib/tier.mjs +4 -4
- package/lib/transfer.mjs +5 -5
- package/package.json +1 -1
- package/sdk/dist/errors.d.ts +8 -8
- package/sdk/dist/errors.d.ts.map +1 -1
- package/sdk/dist/errors.js +6 -6
- package/sdk/dist/errors.js.map +1 -1
- package/sdk/dist/namespaces/admin.d.ts +15 -16
- package/sdk/dist/namespaces/admin.d.ts.map +1 -1
- package/sdk/dist/namespaces/admin.js +14 -15
- package/sdk/dist/namespaces/admin.js.map +1 -1
- package/sdk/dist/namespaces/billing.d.ts +47 -53
- package/sdk/dist/namespaces/billing.d.ts.map +1 -1
- package/sdk/dist/namespaces/billing.js +59 -59
- package/sdk/dist/namespaces/billing.js.map +1 -1
- package/sdk/dist/namespaces/operator.d.ts +3 -3
- package/sdk/dist/namespaces/operator.d.ts.map +1 -1
- package/sdk/dist/namespaces/operator.js +1 -1
- package/sdk/dist/namespaces/org.types.d.ts +2 -2
- package/sdk/dist/namespaces/org.types.js +1 -1
- package/sdk/dist/namespaces/projects.d.ts +45 -17
- package/sdk/dist/namespaces/projects.d.ts.map +1 -1
- package/sdk/dist/namespaces/projects.js +71 -29
- package/sdk/dist/namespaces/projects.js.map +1 -1
- package/sdk/dist/namespaces/projects.types.d.ts +100 -20
- package/sdk/dist/namespaces/projects.types.d.ts.map +1 -1
- package/sdk/dist/namespaces/tier.d.ts +11 -11
- package/sdk/dist/namespaces/tier.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.d.ts +8 -8
- package/sdk/dist/namespaces/transfers.d.ts.map +1 -1
- package/sdk/dist/namespaces/transfers.js +3 -3
- package/sdk/dist/namespaces/transfers.js.map +1 -1
- package/sdk/dist/scoped.d.ts +3 -2
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +5 -2
- package/sdk/dist/scoped.js.map +1 -1
package/cli.mjs
CHANGED
|
@@ -47,7 +47,7 @@ Commands:
|
|
|
47
47
|
message Send messages to Run402 developers
|
|
48
48
|
auth Manage project user authentication (magic link, passwords, settings)
|
|
49
49
|
sender-domain Manage custom email sender domain (register, status, remove)
|
|
50
|
-
billing Email
|
|
50
|
+
billing Email organizations, Stripe tier checkout, email packs
|
|
51
51
|
contracts KMS contract wallets ($0.04/day rental + $0.000005/sign)
|
|
52
52
|
agent Manage agent identity (contact info)
|
|
53
53
|
operator Operator (human/email) session — login, then overview across your wallets
|
package/lib/admin.mjs
CHANGED
|
@@ -8,9 +8,9 @@ Usage:
|
|
|
8
8
|
run402 admin <subcommand> [args...]
|
|
9
9
|
|
|
10
10
|
Subcommands:
|
|
11
|
-
lease-perpetual <
|
|
12
|
-
Toggle the
|
|
13
|
-
When enabled, the
|
|
11
|
+
lease-perpetual <organization_id> (--enable | --disable)
|
|
12
|
+
Toggle the organization-level escape hatch.
|
|
13
|
+
When enabled, the organization never advances
|
|
14
14
|
past 'active' regardless of lease expiry.
|
|
15
15
|
If the account is currently in a grace
|
|
16
16
|
state, enabling reactivates it inline
|
|
@@ -19,12 +19,12 @@ Subcommands:
|
|
|
19
19
|
|
|
20
20
|
archive <project_id> [--reason "..."] Moderate-archive a single project. Sets
|
|
21
21
|
projects.archived_at = NOW(). Independent
|
|
22
|
-
of
|
|
23
|
-
|
|
22
|
+
of organization lifecycle; the rest of the
|
|
23
|
+
organization's projects keep serving.
|
|
24
24
|
|
|
25
25
|
reactivate <project_id> Un-archive a project (flips archived_at
|
|
26
26
|
back to NULL). In v1.57 this no longer
|
|
27
|
-
touches
|
|
27
|
+
touches organization-level lifecycle — to
|
|
28
28
|
reactivate a grace-state account, use
|
|
29
29
|
\`tier set <tier>\` or enable
|
|
30
30
|
lease-perpetual above.
|
|
@@ -36,17 +36,17 @@ Notes:
|
|
|
36
36
|
- Output is JSON.
|
|
37
37
|
|
|
38
38
|
Examples:
|
|
39
|
-
run402 admin lease-perpetual
|
|
40
|
-
run402 admin lease-perpetual
|
|
39
|
+
run402 admin lease-perpetual org_abc123 --enable
|
|
40
|
+
run402 admin lease-perpetual org_abc123 --disable
|
|
41
41
|
run402 admin archive prj_abuse --reason "ToS violation"
|
|
42
42
|
run402 admin reactivate prj_abuse
|
|
43
43
|
`;
|
|
44
44
|
|
|
45
45
|
const SUB_HELP = {
|
|
46
|
-
"lease-perpetual": `run402 admin lease-perpetual — Toggle the
|
|
46
|
+
"lease-perpetual": `run402 admin lease-perpetual — Toggle the organization-level escape hatch
|
|
47
47
|
|
|
48
48
|
Usage:
|
|
49
|
-
run402 admin lease-perpetual <
|
|
49
|
+
run402 admin lease-perpetual <organization_id> (--enable | --disable)
|
|
50
50
|
|
|
51
51
|
Options:
|
|
52
52
|
--enable Set lease_perpetual = true (pins every project on the account)
|
|
@@ -59,8 +59,8 @@ Notes:
|
|
|
59
59
|
the gateway reactivates inline and reports \`reactivated: true\`.
|
|
60
60
|
|
|
61
61
|
Examples:
|
|
62
|
-
run402 admin lease-perpetual
|
|
63
|
-
run402 admin lease-perpetual
|
|
62
|
+
run402 admin lease-perpetual org_abc123 --enable
|
|
63
|
+
run402 admin lease-perpetual org_abc123 --disable
|
|
64
64
|
`,
|
|
65
65
|
archive: `run402 admin archive — Moderate-archive a single project
|
|
66
66
|
|
|
@@ -71,8 +71,8 @@ Options:
|
|
|
71
71
|
--reason <text> Free-text moderation reason recorded in the audit log.
|
|
72
72
|
|
|
73
73
|
Notes:
|
|
74
|
-
- Sets projects.archived_at = NOW(). Independent of
|
|
75
|
-
only this project goes dark; siblings on the same
|
|
74
|
+
- Sets projects.archived_at = NOW(). Independent of organization-level lifecycle —
|
|
75
|
+
only this project goes dark; siblings on the same organization keep
|
|
76
76
|
serving.
|
|
77
77
|
- No-op when the project is already archived (returns \`note: "already
|
|
78
78
|
archived"\` without changing archived_at).
|
|
@@ -88,9 +88,9 @@ Usage:
|
|
|
88
88
|
|
|
89
89
|
Notes:
|
|
90
90
|
- Flips projects.archived_at back to NULL. In v1.57 this was narrowed:
|
|
91
|
-
|
|
91
|
+
organization-level lifecycle reactivation is NOT triggered. Use
|
|
92
92
|
\`run402 tier set <tier>\` (the tier flow runs the lifecycle advance) or
|
|
93
|
-
\`run402 admin lease-perpetual <
|
|
93
|
+
\`run402 admin lease-perpetual <org_id> --enable\` for that.
|
|
94
94
|
- No-op when the project is not archived (returns \`note: "not archived"\`).
|
|
95
95
|
|
|
96
96
|
Examples:
|
|
@@ -110,25 +110,25 @@ function validateFlags(sub, args) {
|
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
async function leasePerpetual(args) {
|
|
113
|
-
const
|
|
113
|
+
const organizationId = args.find((a) => typeof a === "string" && !a.startsWith("--"));
|
|
114
114
|
const enable = args.includes("--enable");
|
|
115
115
|
const disable = args.includes("--disable");
|
|
116
|
-
if (!
|
|
116
|
+
if (!organizationId) {
|
|
117
117
|
fail({
|
|
118
118
|
code: "BAD_USAGE",
|
|
119
|
-
message: "Missing <
|
|
120
|
-
hint: "run402 admin lease-perpetual <
|
|
119
|
+
message: "Missing <organization_id>.",
|
|
120
|
+
hint: "run402 admin lease-perpetual <organization_id> --enable | --disable",
|
|
121
121
|
});
|
|
122
122
|
}
|
|
123
123
|
if (enable === disable) {
|
|
124
124
|
fail({
|
|
125
125
|
code: "BAD_USAGE",
|
|
126
126
|
message: "Pass exactly one of --enable / --disable.",
|
|
127
|
-
hint: "run402 admin lease-perpetual <
|
|
127
|
+
hint: "run402 admin lease-perpetual <organization_id> --enable | --disable",
|
|
128
128
|
});
|
|
129
129
|
}
|
|
130
130
|
try {
|
|
131
|
-
const data = await getSdk().admin.setLeasePerpetual(
|
|
131
|
+
const data = await getSdk().admin.setLeasePerpetual(organizationId, enable);
|
|
132
132
|
console.log(JSON.stringify(data, null, 2));
|
|
133
133
|
} catch (err) {
|
|
134
134
|
reportSdkError(err);
|
package/lib/allowance.mjs
CHANGED
|
@@ -238,7 +238,7 @@ async function balance() {
|
|
|
238
238
|
"base-sepolia_usd_micros": sepoliaUsdc,
|
|
239
239
|
"tempo-moderato_pathusd_micros": tempoPathUsd,
|
|
240
240
|
},
|
|
241
|
-
run402: billingRes ? { balance_usd_micros: billingRes.available_usd_micros } : "no
|
|
241
|
+
run402: billingRes ? { balance_usd_micros: billingRes.available_usd_micros } : "no organization",
|
|
242
242
|
}, null, 2));
|
|
243
243
|
}
|
|
244
244
|
|
package/lib/billing.mjs
CHANGED
|
@@ -2,25 +2,25 @@ import { getSdk } from "./sdk.mjs";
|
|
|
2
2
|
import { reportSdkError, fail } from "./sdk-errors.mjs";
|
|
3
3
|
import { assertKnownFlags, flagValue, normalizeArgv, parseIntegerFlag, positionalArgs } from "./argparse.mjs";
|
|
4
4
|
|
|
5
|
-
const HELP = `run402 billing — Email
|
|
5
|
+
const HELP = `run402 billing — Email organizations, Stripe tier checkout, email packs
|
|
6
6
|
|
|
7
7
|
Usage:
|
|
8
8
|
run402 billing <subcommand> [args...]
|
|
9
9
|
|
|
10
10
|
Subcommands:
|
|
11
|
-
create-email <email> Create an email
|
|
12
|
-
link-wallet <
|
|
11
|
+
create-email <email> Create an email organization
|
|
12
|
+
link-wallet <org_id> <wallet> Link a wallet to an email organization
|
|
13
13
|
tier-checkout <tier> [--email <e> | --wallet <w>] Stripe tier checkout
|
|
14
14
|
buy-email-pack [--email <e> | --wallet <w>] Buy \$5 email pack (10,000 emails)
|
|
15
|
-
auto-recharge <
|
|
16
|
-
balance <identifier> Balance by
|
|
17
|
-
history <identifier> [--limit <n>] Ledger history by
|
|
15
|
+
auto-recharge <org_id> <on|off> [--threshold <n>]
|
|
16
|
+
balance <identifier> Balance by organization id (UUID), wallet (0x...), or email
|
|
17
|
+
history <identifier> [--limit <n>] Ledger history by organization id (UUID), wallet, or email
|
|
18
18
|
|
|
19
19
|
Examples:
|
|
20
20
|
run402 billing create-email user@example.com
|
|
21
21
|
run402 billing tier-checkout hobby --email user@example.com
|
|
22
22
|
run402 billing buy-email-pack --wallet 0x1234...
|
|
23
|
-
run402 billing auto-recharge
|
|
23
|
+
run402 billing auto-recharge org_abc on --threshold 2000
|
|
24
24
|
run402 billing balance user@example.com
|
|
25
25
|
`;
|
|
26
26
|
|
|
@@ -34,7 +34,7 @@ Arguments:
|
|
|
34
34
|
<tier> Tier name (e.g. hobby, pro)
|
|
35
35
|
|
|
36
36
|
Options:
|
|
37
|
-
--email <e> Email
|
|
37
|
+
--email <e> Email organization to charge
|
|
38
38
|
--wallet <w> Wallet address (0x...) to associate with the checkout
|
|
39
39
|
|
|
40
40
|
Examples:
|
|
@@ -47,7 +47,7 @@ Usage:
|
|
|
47
47
|
run402 billing buy-email-pack [--email <e> | --wallet <w>]
|
|
48
48
|
|
|
49
49
|
Options:
|
|
50
|
-
--email <e> Email
|
|
50
|
+
--email <e> Email organization to charge
|
|
51
51
|
--wallet <w> Wallet address (0x...) to associate with the purchase
|
|
52
52
|
|
|
53
53
|
Examples:
|
|
@@ -57,33 +57,33 @@ Examples:
|
|
|
57
57
|
"auto-recharge": `run402 billing auto-recharge — Toggle email-pack auto-recharge
|
|
58
58
|
|
|
59
59
|
Usage:
|
|
60
|
-
run402 billing auto-recharge <
|
|
60
|
+
run402 billing auto-recharge <org_id> <on|off> [--threshold <n>]
|
|
61
61
|
|
|
62
62
|
Arguments:
|
|
63
|
-
<
|
|
63
|
+
<org_id> Organization ID
|
|
64
64
|
<on|off> Enable or disable auto-recharge
|
|
65
65
|
|
|
66
66
|
Options:
|
|
67
67
|
--threshold <n> Remaining-email threshold that triggers auto-recharge
|
|
68
68
|
|
|
69
69
|
Examples:
|
|
70
|
-
run402 billing auto-recharge
|
|
71
|
-
run402 billing auto-recharge
|
|
70
|
+
run402 billing auto-recharge org_abc on --threshold 2000
|
|
71
|
+
run402 billing auto-recharge org_abc off
|
|
72
72
|
`,
|
|
73
|
-
history: `run402 billing history — Show ledger history for a
|
|
73
|
+
history: `run402 billing history — Show ledger history for a organization
|
|
74
74
|
|
|
75
75
|
Usage:
|
|
76
76
|
run402 billing history <identifier> [--limit <n>]
|
|
77
77
|
|
|
78
78
|
Arguments:
|
|
79
|
-
<identifier>
|
|
80
|
-
Wallet/email are resolved to the
|
|
79
|
+
<identifier> Organization id (UUID), wallet (0x...), or email.
|
|
80
|
+
Wallet/email are resolved to the organization id first.
|
|
81
81
|
|
|
82
82
|
Options:
|
|
83
83
|
--limit <n> Max entries to return (default: 50)
|
|
84
84
|
|
|
85
85
|
Auth:
|
|
86
|
-
Requires SIWX from a wallet linked to the
|
|
86
|
+
Requires SIWX from a wallet linked to the organization (signed automatically from
|
|
87
87
|
the local allowance), or an admin key. Email lookups require an admin key.
|
|
88
88
|
|
|
89
89
|
Examples:
|
|
@@ -91,17 +91,17 @@ Examples:
|
|
|
91
91
|
run402 billing history 0x1234... --limit 100
|
|
92
92
|
run402 billing history 00000000-0000-4000-8000-000000000001
|
|
93
93
|
`,
|
|
94
|
-
balance: `run402 billing balance — Show balance for a
|
|
94
|
+
balance: `run402 billing balance — Show balance for a organization
|
|
95
95
|
|
|
96
96
|
Usage:
|
|
97
97
|
run402 billing balance <identifier>
|
|
98
98
|
|
|
99
99
|
Arguments:
|
|
100
|
-
<identifier>
|
|
101
|
-
Wallet/email are resolved via the
|
|
100
|
+
<identifier> Organization id (UUID), wallet (0x...), or email.
|
|
101
|
+
Wallet/email are resolved via the organization lookup.
|
|
102
102
|
|
|
103
103
|
Auth:
|
|
104
|
-
Requires SIWX from a wallet linked to the
|
|
104
|
+
Requires SIWX from a wallet linked to the organization (signed automatically from
|
|
105
105
|
the local allowance), or an admin key. Email lookups require an admin key.
|
|
106
106
|
|
|
107
107
|
Examples:
|
|
@@ -109,38 +109,38 @@ Examples:
|
|
|
109
109
|
run402 billing balance 0x1234abcd...
|
|
110
110
|
run402 billing balance 00000000-0000-4000-8000-000000000001
|
|
111
111
|
`,
|
|
112
|
-
"create-email": `run402 billing create-email — Create an email
|
|
112
|
+
"create-email": `run402 billing create-email — Create an email organization
|
|
113
113
|
|
|
114
114
|
Usage:
|
|
115
115
|
run402 billing create-email <email>
|
|
116
116
|
|
|
117
117
|
Arguments:
|
|
118
|
-
<email> Email address to register as a
|
|
118
|
+
<email> Email address to register as a organization
|
|
119
119
|
|
|
120
120
|
Examples:
|
|
121
121
|
run402 billing create-email user@example.com
|
|
122
122
|
`,
|
|
123
|
-
"link-wallet": `run402 billing link-wallet — Link a wallet to an email
|
|
123
|
+
"link-wallet": `run402 billing link-wallet — Link a wallet to an email organization
|
|
124
124
|
|
|
125
125
|
Usage:
|
|
126
|
-
run402 billing link-wallet <
|
|
126
|
+
run402 billing link-wallet <org_id> <wallet>
|
|
127
127
|
|
|
128
128
|
Arguments:
|
|
129
|
-
<
|
|
129
|
+
<org_id> Organization ID (e.g. org_abc123)
|
|
130
130
|
<wallet> Wallet address (0x...) to link
|
|
131
131
|
|
|
132
132
|
Notes:
|
|
133
|
-
- Tier and quotas are per-
|
|
134
|
-
spend into the
|
|
135
|
-
on this
|
|
133
|
+
- Tier and quotas are per-organization. Linking a wallet merges its
|
|
134
|
+
spend into the organization-wide pool that already includes every project
|
|
135
|
+
on this organization.
|
|
136
136
|
- The response includes a 'pool_implications' block on v1.46+ gateways:
|
|
137
|
-
tier, projects_in_pool_count,
|
|
138
|
-
|
|
137
|
+
tier, projects_in_pool_count, organization_api_calls_current,
|
|
138
|
+
organization_storage_bytes_current, tier_limits, over_limit. Inspect
|
|
139
139
|
'over_limit' before linking a wallet whose existing usage might push
|
|
140
140
|
the merged pool past the tier cap.
|
|
141
141
|
|
|
142
142
|
Examples:
|
|
143
|
-
run402 billing link-wallet
|
|
143
|
+
run402 billing link-wallet org_abc123 0x1234abcd...
|
|
144
144
|
`,
|
|
145
145
|
};
|
|
146
146
|
|
|
@@ -177,7 +177,7 @@ async function createEmail(args) {
|
|
|
177
177
|
});
|
|
178
178
|
}
|
|
179
179
|
try {
|
|
180
|
-
const data = await getSdk().billing.
|
|
180
|
+
const data = await getSdk().billing.createEmailOrganization(email);
|
|
181
181
|
console.log(JSON.stringify(data, null, 2));
|
|
182
182
|
} catch (err) {
|
|
183
183
|
reportSdkError(err);
|
|
@@ -188,23 +188,23 @@ async function linkWallet(args) {
|
|
|
188
188
|
const parsedArgs = normalizeArgv(args);
|
|
189
189
|
assertKnownFlags(parsedArgs, ["--help", "-h"]);
|
|
190
190
|
const positionals = positionalArgs(parsedArgs);
|
|
191
|
-
const
|
|
191
|
+
const organizationId = positionals[0];
|
|
192
192
|
const wallet = positionals[1];
|
|
193
193
|
if (positionals.length > 2) {
|
|
194
194
|
fail({ code: "BAD_USAGE", message: `Unexpected argument for billing link-wallet: ${positionals[2]}` });
|
|
195
195
|
}
|
|
196
|
-
if (!
|
|
196
|
+
if (!organizationId || !wallet) {
|
|
197
197
|
fail({
|
|
198
198
|
code: "BAD_USAGE",
|
|
199
|
-
message: "Missing <
|
|
200
|
-
hint: "run402 billing link-wallet <
|
|
199
|
+
message: "Missing <org_id> and/or <wallet>.",
|
|
200
|
+
hint: "run402 billing link-wallet <org_id> <wallet>",
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
try {
|
|
204
|
-
const data = await getSdk().billing.linkWallet(
|
|
204
|
+
const data = await getSdk().billing.linkWallet(organizationId, wallet);
|
|
205
205
|
const output = {
|
|
206
206
|
status: data?.status ?? "ok",
|
|
207
|
-
|
|
207
|
+
organization_id: data?.organization_id ?? organizationId,
|
|
208
208
|
wallet: data?.wallet ?? wallet.toLowerCase(),
|
|
209
209
|
...(data?.pool_implications ? { pool_implications: data.pool_implications } : {}),
|
|
210
210
|
};
|
|
@@ -265,16 +265,16 @@ async function autoRecharge(args) {
|
|
|
265
265
|
const valueFlags = ["--threshold"];
|
|
266
266
|
assertKnownFlags(parsedArgs, [...valueFlags, "--help", "-h"], valueFlags);
|
|
267
267
|
const positionals = positionalArgs(parsedArgs, valueFlags);
|
|
268
|
-
const
|
|
268
|
+
const organizationId = positionals[0];
|
|
269
269
|
const state = positionals[1];
|
|
270
270
|
if (positionals.length > 2) {
|
|
271
271
|
fail({ code: "BAD_USAGE", message: `Unexpected argument for billing auto-recharge: ${positionals[2]}` });
|
|
272
272
|
}
|
|
273
|
-
if (!
|
|
273
|
+
if (!organizationId || !state || !["on", "off"].includes(state)) {
|
|
274
274
|
fail({
|
|
275
275
|
code: "BAD_USAGE",
|
|
276
|
-
message: "Missing <
|
|
277
|
-
hint: "run402 billing auto-recharge <
|
|
276
|
+
message: "Missing <org_id> and/or <on|off>.",
|
|
277
|
+
hint: "run402 billing auto-recharge <org_id> <on|off> [--threshold <n>]",
|
|
278
278
|
});
|
|
279
279
|
}
|
|
280
280
|
const thresholdStr = flagValue(parsedArgs, "--threshold");
|
|
@@ -283,11 +283,11 @@ async function autoRecharge(args) {
|
|
|
283
283
|
: undefined;
|
|
284
284
|
try {
|
|
285
285
|
await getSdk().billing.setAutoRecharge({
|
|
286
|
-
|
|
286
|
+
organizationId: organizationId,
|
|
287
287
|
enabled: state === "on",
|
|
288
288
|
threshold,
|
|
289
289
|
});
|
|
290
|
-
console.log(JSON.stringify({
|
|
290
|
+
console.log(JSON.stringify({ organization_id: organizationId, enabled: state === "on", updated: true }));
|
|
291
291
|
} catch (err) {
|
|
292
292
|
reportSdkError(err);
|
|
293
293
|
}
|
|
@@ -305,11 +305,11 @@ async function balance(args) {
|
|
|
305
305
|
fail({
|
|
306
306
|
code: "BAD_USAGE",
|
|
307
307
|
message: "Missing <identifier>.",
|
|
308
|
-
hint: "run402 billing balance <
|
|
308
|
+
hint: "run402 billing balance <org-id | wallet | email>",
|
|
309
309
|
});
|
|
310
310
|
}
|
|
311
311
|
try {
|
|
312
|
-
const data = await getSdk().billing.
|
|
312
|
+
const data = await getSdk().billing.getOrganization(id);
|
|
313
313
|
console.log(JSON.stringify(data, null, 2));
|
|
314
314
|
} catch (err) {
|
|
315
315
|
reportSdkError(err);
|
|
@@ -329,7 +329,7 @@ async function history(args) {
|
|
|
329
329
|
fail({
|
|
330
330
|
code: "BAD_USAGE",
|
|
331
331
|
message: "Missing <identifier>.",
|
|
332
|
-
hint: "run402 billing history <
|
|
332
|
+
hint: "run402 billing history <org-id | wallet | email> [--limit <n>]",
|
|
333
333
|
});
|
|
334
334
|
}
|
|
335
335
|
const limit = parsedArgs.includes("--limit")
|
package/lib/operator.mjs
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* The operator is YOU, the human, identified by email — distinct from the
|
|
5
5
|
* AGENT (your wallet / SIWX identity). One browser login spans every wallet
|
|
6
6
|
* that verified your email, so `operator overview` returns the cross-wallet
|
|
7
|
-
* union. For a single wallet's
|
|
7
|
+
* union. For a single wallet's organization state, use `run402 status`.
|
|
8
8
|
*
|
|
9
9
|
* Auth: browser-delegated device-authorization grant (RFC 8628, the
|
|
10
10
|
* `aws sso login` model). The CLI never performs WebAuthn — the browser does,
|
|
@@ -47,7 +47,7 @@ const HELP = `run402 operator — operator (human / email) session
|
|
|
47
47
|
|
|
48
48
|
The operator is YOU, the human, identified by email — distinct from the agent
|
|
49
49
|
(your wallet). One browser login spans every wallet that verified your email.
|
|
50
|
-
For a single wallet's
|
|
50
|
+
For a single wallet's organization state, use 'run402 status'.
|
|
51
51
|
|
|
52
52
|
Usage:
|
|
53
53
|
run402 operator login [--no-open] (read session, device-flow)
|
|
@@ -289,7 +289,7 @@ async function loopbackLogin(args, { stepUp }) {
|
|
|
289
289
|
if (memberships.length) {
|
|
290
290
|
process.stderr.write(
|
|
291
291
|
`Member of ${memberships.length} org(s):\n` +
|
|
292
|
-
memberships.map((m) => ` - ${m.
|
|
292
|
+
memberships.map((m) => ` - ${m.display_name || m.org_id || m.organization_id || "unknown"} (${m.role}, ${m.status})`).join("\n") +
|
|
293
293
|
"\n",
|
|
294
294
|
);
|
|
295
295
|
}
|
package/lib/projects.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from "fs";
|
|
2
2
|
import { loadKeyStore, API, allowanceAuthHeaders, resolveProjectId, getActiveProjectId } from "./config.mjs";
|
|
3
|
+
import { loadLiveOperatorSession } from "../core-dist/operator-session.js";
|
|
3
4
|
import { getSdk } from "./sdk.mjs";
|
|
4
5
|
import { reportSdkError, fail, parseFlagJson } from "./sdk-errors.mjs";
|
|
5
6
|
import { assertKnownFlags, failBadProjectId, flagValue, hasHelp, normalizeArgv, positionalArgs, resolvePositionalProject, validateRegularFile } from "./argparse.mjs";
|
|
@@ -13,7 +14,8 @@ Subcommands:
|
|
|
13
14
|
quote Show pricing tiers
|
|
14
15
|
provision [--tier <tier>] [--name <n>] [--org <id>] Provision a new Postgres project (pays via x402)
|
|
15
16
|
use <id> Set the active project (used as default for other commands)
|
|
16
|
-
list
|
|
17
|
+
list [--org <id>] [--all] List your projects from the server (name, site_url, custom domains, org_id, active marker)
|
|
18
|
+
rename <id> --name <label> Rename a project (fix an auto-generated name)
|
|
17
19
|
info [id] Show project details: REST URL, keys
|
|
18
20
|
keys [id] Print anon_key and service_key as JSON
|
|
19
21
|
sql [id] "<query>" [--file <path>] [--params '<json>'] Run a SQL query (supports parameterized queries)
|
|
@@ -36,6 +38,9 @@ Examples:
|
|
|
36
38
|
run402 projects provision --tier hobby --name my-app
|
|
37
39
|
run402 projects use prj_abc123
|
|
38
40
|
run402 projects list
|
|
41
|
+
run402 projects list --org 11111111-2222-3333-4444-555555555555
|
|
42
|
+
run402 projects list --all
|
|
43
|
+
run402 projects rename prj_abc123 --name "My Site"
|
|
39
44
|
run402 projects info prj_abc123
|
|
40
45
|
run402 projects sql prj_abc123 "SELECT * FROM users LIMIT 5"
|
|
41
46
|
run402 projects sql prj_abc123 "SELECT * FROM users WHERE id = $1" --params '[42]'
|
|
@@ -50,11 +55,25 @@ Examples:
|
|
|
50
55
|
run402 projects keys prj_abc123
|
|
51
56
|
run402 projects delete prj_abc123 --confirm
|
|
52
57
|
|
|
58
|
+
Global options (any command):
|
|
59
|
+
--wallet <name> Use a named wallet (profile) for this command. Precedence:
|
|
60
|
+
--wallet > RUN402_WALLET env > nearest .run402.json binding >
|
|
61
|
+
'run402 wallets use' default > 'default'. See 'run402 wallets'.
|
|
62
|
+
|
|
53
63
|
Notes:
|
|
54
64
|
- <id> is the project_id shown in 'run402 projects list' (prefix: 'prj_')
|
|
55
65
|
- Most commands that take <id> default to the active project when omitted
|
|
56
66
|
(set it with 'run402 projects use <id>'). Project IDs start with 'prj_';
|
|
57
67
|
any first positional that doesn't is treated as the next argument instead.
|
|
68
|
+
- 'list' is a SERVER read, not the local keystore: it shows every project the
|
|
69
|
+
active wallet can reach (membership-scoped), with name, site_url, custom
|
|
70
|
+
domains, and org_id. '--org <id>' filters to one org; '--all' reads the
|
|
71
|
+
cross-wallet inventory for every wallet controlling your operator email
|
|
72
|
+
(run 'run402 operator login' first for the union, else it falls back to the
|
|
73
|
+
current wallet's slice). The 'active' marker still comes from local state.
|
|
74
|
+
- 'rename' fixes a project's display name. You must be an org admin (or hold a
|
|
75
|
+
project:write grant) on the owning org; it works even if the project was
|
|
76
|
+
never provisioned from this machine.
|
|
58
77
|
- 'rest' uses PostgREST query syntax (table name + optional query string)
|
|
59
78
|
- 'provision' requires a funded allowance — payment is automatic via x402
|
|
60
79
|
- 'apply-expose' declares the full authorization surface (tables, views, RPCs)
|
|
@@ -76,6 +95,53 @@ Notes:
|
|
|
76
95
|
`;
|
|
77
96
|
|
|
78
97
|
const SUB_HELP = {
|
|
98
|
+
list: `run402 projects list — List your projects from the server
|
|
99
|
+
|
|
100
|
+
Usage:
|
|
101
|
+
run402 projects list [--org <id>] [--all]
|
|
102
|
+
|
|
103
|
+
Options:
|
|
104
|
+
--org <id> Filter to projects owned by one org (organization).
|
|
105
|
+
Authorize-before-reveal: a non-member or guessed id is a
|
|
106
|
+
403; a non-UUID id is a 400.
|
|
107
|
+
--all Read the cross-wallet inventory across every wallet
|
|
108
|
+
controlling your operator email. Run 'run402 operator
|
|
109
|
+
login' first for the union; without a session it falls
|
|
110
|
+
back to the current wallet's slice. Mutually exclusive
|
|
111
|
+
with --org.
|
|
112
|
+
|
|
113
|
+
Notes:
|
|
114
|
+
- This is a SERVER read (membership-scoped), not the local keystore. Each row
|
|
115
|
+
has project_id, name, site_url, custom_domains, org_id, status, and an
|
|
116
|
+
'active' marker derived from local state.
|
|
117
|
+
- Tier and lifecycle live on the organization, not each project — use
|
|
118
|
+
'run402 status' or 'run402 tier status' for the account view.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
run402 projects list
|
|
122
|
+
run402 projects list --org 11111111-2222-3333-4444-555555555555
|
|
123
|
+
run402 projects list --all
|
|
124
|
+
run402 projects list --wallet work
|
|
125
|
+
`,
|
|
126
|
+
rename: `run402 projects rename — Rename a project
|
|
127
|
+
|
|
128
|
+
Usage:
|
|
129
|
+
run402 projects rename <id> --name <label>
|
|
130
|
+
|
|
131
|
+
Arguments:
|
|
132
|
+
<id> Project ID (prefix: 'prj_'). Required.
|
|
133
|
+
|
|
134
|
+
Options:
|
|
135
|
+
--name <label> New display name (1-200 chars, no control characters).
|
|
136
|
+
|
|
137
|
+
Notes:
|
|
138
|
+
- You must be an org admin (or hold a project:write grant) on the owning org.
|
|
139
|
+
Authorize-before-reveal: an unauthorized or guessed id returns 403, never a
|
|
140
|
+
not-found oracle. Works even if the project isn't in the local keystore.
|
|
141
|
+
|
|
142
|
+
Examples:
|
|
143
|
+
run402 projects rename prj_abc123 --name "My Site"
|
|
144
|
+
`,
|
|
79
145
|
provision: `run402 projects provision — Provision a new Postgres project
|
|
80
146
|
|
|
81
147
|
Usage:
|
|
@@ -375,12 +441,71 @@ async function getExpose(projectId) {
|
|
|
375
441
|
}
|
|
376
442
|
}
|
|
377
443
|
|
|
378
|
-
async function list() {
|
|
379
|
-
const
|
|
380
|
-
const
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
444
|
+
async function list(args = []) {
|
|
445
|
+
const org = flagValue(args, "--org");
|
|
446
|
+
const all = Array.isArray(args) && args.includes("--all");
|
|
447
|
+
if (all && org) {
|
|
448
|
+
fail({
|
|
449
|
+
code: "BAD_USAGE",
|
|
450
|
+
message: "--all and --org are mutually exclusive.",
|
|
451
|
+
hint: "--all reads the cross-wallet operator inventory; --org filters the membership-scoped list to one org.",
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// `--all` reads the operator email-union inventory across every wallet
|
|
456
|
+
// controlling your verified email. Pass the cached operator-session token
|
|
457
|
+
// when present (cross-wallet union); otherwise the SDK falls back to SIWX
|
|
458
|
+
// wallet auth and the gateway returns just this wallet's slice.
|
|
459
|
+
const opts = {};
|
|
460
|
+
if (all) {
|
|
461
|
+
opts.all = true;
|
|
462
|
+
const session = loadLiveOperatorSession();
|
|
463
|
+
if (session) opts.token = session.operator_session_token;
|
|
464
|
+
} else if (org) {
|
|
465
|
+
opts.org = org;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// Active marker comes from local state; the inventory itself is the server
|
|
469
|
+
// read (NOT the keystore), so it surfaces every project the wallet/email can
|
|
470
|
+
// reach — including ones never provisioned from this machine.
|
|
471
|
+
const activeId = getActiveProjectId();
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
const data = await getSdk().projects.list(opts);
|
|
475
|
+
const rows = (data.projects || []).map((p) => ({
|
|
476
|
+
project_id: p.id,
|
|
477
|
+
name: p.name ?? null,
|
|
478
|
+
active: p.id === activeId,
|
|
479
|
+
site_url: p.site_url ?? null,
|
|
480
|
+
custom_domains: p.custom_domains ?? [],
|
|
481
|
+
org_id: p.organization_id ?? null,
|
|
482
|
+
status: p.status ?? p.effective_status ?? null,
|
|
483
|
+
}));
|
|
484
|
+
const out = { projects: rows };
|
|
485
|
+
if (data.scope !== undefined) out.scope = data.scope;
|
|
486
|
+
if (data.has_more !== undefined) out.has_more = data.has_more;
|
|
487
|
+
if (data.next_cursor !== undefined && data.next_cursor !== null) out.next_cursor = data.next_cursor;
|
|
488
|
+
console.log(JSON.stringify(out, null, 2));
|
|
489
|
+
} catch (err) {
|
|
490
|
+
reportSdkError(err);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function rename(projectId, args = []) {
|
|
495
|
+
const name = flagValue(args, "--name");
|
|
496
|
+
if (!name) {
|
|
497
|
+
fail({
|
|
498
|
+
code: "BAD_USAGE",
|
|
499
|
+
message: "Missing --name.",
|
|
500
|
+
hint: "run402 projects rename <id> --name \"My Site\"",
|
|
501
|
+
});
|
|
502
|
+
}
|
|
503
|
+
try {
|
|
504
|
+
const data = await getSdk().projects.rename(projectId, name);
|
|
505
|
+
console.log(JSON.stringify({ project_id: data.project_id, name: data.name, renamed: true }, null, 2));
|
|
506
|
+
} catch (err) {
|
|
507
|
+
reportSdkError(err);
|
|
508
|
+
}
|
|
384
509
|
}
|
|
385
510
|
|
|
386
511
|
async function info(projectId) {
|
|
@@ -571,6 +696,8 @@ async function deleteProject(projectId, args = []) {
|
|
|
571
696
|
|
|
572
697
|
const FLAGS_BY_SUB = {
|
|
573
698
|
provision: { known: ["--tier", "--name", "--org"], values: ["--tier", "--name", "--org"] },
|
|
699
|
+
list: { known: ["--org", "--all"], values: ["--org"] },
|
|
700
|
+
rename: { known: ["--name"], values: ["--name"] },
|
|
574
701
|
sql: { known: ["--file", "--params"], values: ["--file", "--params"] },
|
|
575
702
|
costs: { known: ["--window"], values: ["--window"] },
|
|
576
703
|
"apply-expose": { known: ["--file"], values: ["--file"] },
|
|
@@ -597,7 +724,7 @@ export async function run(sub, args) {
|
|
|
597
724
|
fail({
|
|
598
725
|
code: "REMOVED_COMMAND",
|
|
599
726
|
message: "`run402 projects pin` was removed in v1.57.",
|
|
600
|
-
hint: "Per-project pin is superseded by the
|
|
727
|
+
hint: "Per-project pin is superseded by the organization-level escape hatch. Use `run402 admin lease-perpetual <organization_id> --enable` (platform-admin only).",
|
|
601
728
|
});
|
|
602
729
|
}
|
|
603
730
|
args = normalizeArgv(args);
|
|
@@ -610,7 +737,8 @@ export async function run(sub, args) {
|
|
|
610
737
|
case "quote": await quote(); break;
|
|
611
738
|
case "provision": await provision(args); break;
|
|
612
739
|
case "use": await use(args[0]); break;
|
|
613
|
-
case "list": await list(); break;
|
|
740
|
+
case "list": await list(args); break;
|
|
741
|
+
case "rename": { const { projectId, rest } = resolvePositionalProject(args, { rejectBareFirst: true, valueFlags: FLAGS_BY_SUB.rename.values }); await rename(projectId, rest); break; }
|
|
614
742
|
case "info": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await info(projectId); break; }
|
|
615
743
|
case "keys": { const { projectId } = resolvePositionalProject(args, { rejectBareFirst: true }); await keys(projectId); break; }
|
|
616
744
|
case "sql": { const { projectId, rest } = resolvePositionalProject(args, { maxBarePositionals: 1, valueFlags: FLAGS_BY_SUB.sql.values, rejectBareFirstWhenFlagPresent: ["--file"] }); await sqlCmd(projectId, rest); break; }
|
package/lib/sdk-errors.mjs
CHANGED
|
@@ -111,7 +111,7 @@ export function reportSdkError(err) {
|
|
|
111
111
|
if (d?.required_capability) need.push(`capability \`${d.required_capability}\``);
|
|
112
112
|
const needStr = need.length > 0 ? ` (needs ${need.join(" or ")})` : "";
|
|
113
113
|
payload.hint =
|
|
114
|
-
`Authorization denied${needStr}: a wallet authenticates, but the owning org (
|
|
114
|
+
`Authorization denied${needStr}: a wallet authenticates, but the owning org (organization) ` +
|
|
115
115
|
"decides access via membership role (owner > admin > developer > billing > viewer) or a per-project grant. " +
|
|
116
116
|
"High-stakes actions (delete, transfer, membership change) require an active `owner` membership. " +
|
|
117
117
|
"Returned as 403 even when the project does not exist, so verify the project id too.";
|
package/lib/service.mjs
CHANGED
|
@@ -22,7 +22,7 @@ Usage:
|
|
|
22
22
|
Notes:
|
|
23
23
|
- Unauthenticated and free; no allowance required
|
|
24
24
|
- Returns uptime, supported capabilities, operator, and deployment info
|
|
25
|
-
- For
|
|
25
|
+
- For organization state (allowance, balance, tier, projects), use
|
|
26
26
|
'run402 status' instead
|
|
27
27
|
|
|
28
28
|
Examples:
|