run402 3.8.2 → 4.0.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 +2 -2
- package/lib/domains.mjs +314 -144
- package/lib/sender-domain.mjs +26 -204
- package/package.json +1 -1
- package/sdk/dist/namespaces/domains.d.ts +148 -55
- package/sdk/dist/namespaces/domains.d.ts.map +1 -1
- package/sdk/dist/namespaces/domains.js +114 -69
- package/sdk/dist/namespaces/domains.js.map +1 -1
- package/sdk/dist/namespaces/sender-domain.d.ts +12 -10
- package/sdk/dist/namespaces/sender-domain.d.ts.map +1 -1
- package/sdk/dist/namespaces/sender-domain.js +25 -43
- package/sdk/dist/namespaces/sender-domain.js.map +1 -1
- package/sdk/dist/scoped.d.ts +22 -7
- package/sdk/dist/scoped.d.ts.map +1 -1
- package/sdk/dist/scoped.js +33 -4
- package/sdk/dist/scoped.js.map +1 -1
package/cli.mjs
CHANGED
|
@@ -44,14 +44,14 @@ Commands:
|
|
|
44
44
|
sites Deploy static sites
|
|
45
45
|
cdn CloudFront CDN diagnostics (wait-fresh) for public asset URLs
|
|
46
46
|
subdomains Manage custom subdomains (claim, list, delete)
|
|
47
|
-
domains Manage
|
|
47
|
+
domains Manage ProjectDomain lifecycle (connect, check, repair, wait)
|
|
48
48
|
apps Browse and manage the app marketplace
|
|
49
49
|
ai AI translation and moderation tools
|
|
50
50
|
image Generate AI images via x402 or MPP micropayments
|
|
51
51
|
email Send template-based emails from your project
|
|
52
52
|
message Send messages to Run402 developers
|
|
53
53
|
auth Manage project user authentication (magic link, passwords, settings)
|
|
54
|
-
sender-domain
|
|
54
|
+
sender-domain Removed; use domains connect/status/repair
|
|
55
55
|
billing Email organizations, Stripe tier checkout, email packs
|
|
56
56
|
contracts KMS signers ($0.04/day rental + $0.000005/sign)
|
|
57
57
|
agent Manage agent identity (contact info)
|
package/lib/domains.mjs
CHANGED
|
@@ -1,232 +1,402 @@
|
|
|
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 { assertKnownFlags, flagValue, normalizeArgv, positionalArgs } from "./argparse.mjs";
|
|
4
|
+
import { assertKnownFlags, flagValue, normalizeArgv, positionalArgs, parseIntegerFlag } from "./argparse.mjs";
|
|
5
5
|
|
|
6
|
-
const HELP = `run402 domains — Manage
|
|
6
|
+
const HELP = `run402 domains — Manage ProjectDomain lifecycle
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
run402 domains <subcommand> [args...]
|
|
10
10
|
|
|
11
11
|
Subcommands:
|
|
12
|
-
|
|
13
|
-
list
|
|
14
|
-
status
|
|
15
|
-
|
|
12
|
+
connect <domain> [--project <id>] [--web] [--email-send] [--email-receive] [...]
|
|
13
|
+
list [--project <id>] [--include-managed]
|
|
14
|
+
status <domain> [--project <id>]
|
|
15
|
+
dns <domain> [--project <id>] [--format json|bind]
|
|
16
|
+
check <domain> [--project <id>]
|
|
17
|
+
apply <domain> [--project <id>] [--authority auto|provider-connect|delegated-subdomain|hosted-zone]
|
|
18
|
+
repair <domain> [--project <id>]
|
|
19
|
+
test-receive <domain> --to <local-part|address> [--project <id>]
|
|
20
|
+
wait <domain> [--project <id>] [--until active|safe|receive-active] [--timeout-ms <n>] [--interval-ms <n>]
|
|
21
|
+
activate <domain> [--project <id>]
|
|
22
|
+
disconnect <domain> --confirm [--project <id>]
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
run402 domains add example.com myapp --project prj_123
|
|
20
|
-
run402 domains list --project prj_123 --auth service-key
|
|
21
|
-
run402 domains list
|
|
22
|
-
run402 domains status example.com
|
|
23
|
-
run402 domains delete example.com --confirm
|
|
24
|
-
|
|
25
|
-
Notes:
|
|
26
|
-
- After adding a domain, configure DNS as shown in the response
|
|
27
|
-
- Poll 'status' until the domain is active (DNS propagation ~60s)
|
|
28
|
-
- The domain must CNAME to domains.run402.com (or ALIAS for apex domains)
|
|
24
|
+
Removed:
|
|
25
|
+
add, delete. Use connect/disconnect.
|
|
29
26
|
`;
|
|
30
27
|
|
|
31
28
|
const SUB_HELP = {
|
|
32
|
-
|
|
29
|
+
connect: `run402 domains connect — connect a ProjectDomain capability
|
|
33
30
|
|
|
34
31
|
Usage:
|
|
35
|
-
run402 domains
|
|
36
|
-
|
|
37
|
-
Arguments:
|
|
38
|
-
<domain> Custom domain (e.g. example.com)
|
|
39
|
-
<subdomain_name> Existing subdomain to map the custom domain to
|
|
40
|
-
|
|
41
|
-
Options:
|
|
42
|
-
--project <id> Project ID (defaults to the active project)
|
|
43
|
-
--auth <mode> principal (default, server-authoritative) or service-key
|
|
44
|
-
(uses local project-key cache)
|
|
45
|
-
|
|
46
|
-
Notes:
|
|
47
|
-
- After adding, configure DNS as shown in the response
|
|
48
|
-
- Poll 'run402 domains status <domain>' until active
|
|
49
|
-
- The domain must CNAME to domains.run402.com (or ALIAS for apex domains)
|
|
32
|
+
run402 domains connect <domain> --project <id> [--web] [--email-send] [--email-receive] [options]
|
|
50
33
|
|
|
51
34
|
Examples:
|
|
52
|
-
run402 domains
|
|
53
|
-
run402 domains
|
|
35
|
+
run402 domains connect kysigned.com --project prj_123 --email-send --email-receive --mailbox-addresses primary --addresses info
|
|
36
|
+
run402 domains connect example.com --project prj_123 --web --web-target production
|
|
54
37
|
`,
|
|
55
|
-
list: `run402 domains list —
|
|
38
|
+
list: `run402 domains list — list project domains
|
|
56
39
|
|
|
57
40
|
Usage:
|
|
58
|
-
run402 domains list
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
<id> Project ID (defaults to the active project)
|
|
62
|
-
|
|
63
|
-
Options:
|
|
64
|
-
--auth <mode> principal (default, server-authoritative) or service-key
|
|
65
|
-
(uses local project-key cache)
|
|
41
|
+
run402 domains list --project <id>
|
|
42
|
+
`,
|
|
43
|
+
status: `run402 domains status — show one ProjectDomain aggregate
|
|
66
44
|
|
|
67
|
-
|
|
68
|
-
run402 domains
|
|
69
|
-
run402 domains list --project prj_abc123
|
|
45
|
+
Usage:
|
|
46
|
+
run402 domains status <domain> --project <id>
|
|
70
47
|
`,
|
|
71
|
-
|
|
48
|
+
dns: `run402 domains dns — print required DNS records
|
|
72
49
|
|
|
73
50
|
Usage:
|
|
74
|
-
run402 domains
|
|
51
|
+
run402 domains dns <domain> --project <id> [--format json|bind]
|
|
52
|
+
`,
|
|
53
|
+
check: `run402 domains check — refresh ProjectDomain observations and checks
|
|
75
54
|
|
|
76
|
-
|
|
77
|
-
<domain>
|
|
55
|
+
Usage:
|
|
56
|
+
run402 domains check <domain> --project <id>
|
|
57
|
+
`,
|
|
58
|
+
apply: `run402 domains apply — apply safe provider-managed changes
|
|
78
59
|
|
|
79
|
-
|
|
80
|
-
--project <id>
|
|
81
|
-
|
|
82
|
-
|
|
60
|
+
Usage:
|
|
61
|
+
run402 domains apply <domain> --project <id> [--authority auto|provider-connect|delegated-subdomain|hosted-zone]
|
|
62
|
+
`,
|
|
63
|
+
repair: `run402 domains repair — repair Run402-owned domain infrastructure
|
|
83
64
|
|
|
84
|
-
|
|
85
|
-
run402 domains
|
|
86
|
-
run402 domains status example.com --project prj_abc123
|
|
65
|
+
Usage:
|
|
66
|
+
run402 domains repair <domain> --project <id>
|
|
87
67
|
`,
|
|
88
|
-
|
|
68
|
+
"test-receive": `run402 domains test-receive — create an inbound receive test token
|
|
89
69
|
|
|
90
70
|
Usage:
|
|
91
|
-
run402 domains
|
|
71
|
+
run402 domains test-receive <domain> --project <id> --to <local-part|address>
|
|
72
|
+
`,
|
|
73
|
+
wait: `run402 domains wait — poll until a ProjectDomain is ready
|
|
92
74
|
|
|
93
|
-
|
|
94
|
-
<domain>
|
|
75
|
+
Usage:
|
|
76
|
+
run402 domains wait <domain> --project <id> [--until active|safe|receive-active] [--timeout-ms <n>] [--interval-ms <n>]
|
|
77
|
+
`,
|
|
78
|
+
activate: `run402 domains activate — activate custom mailbox addresses
|
|
95
79
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
--project <id> Project ID (defaults to the active project)
|
|
101
|
-
--auth <mode> principal (default, server-authoritative) or service-key
|
|
102
|
-
(uses local project-key cache)
|
|
80
|
+
Usage:
|
|
81
|
+
run402 domains activate <domain> --project <id>
|
|
82
|
+
`,
|
|
83
|
+
disconnect: `run402 domains disconnect — disconnect a ProjectDomain
|
|
103
84
|
|
|
104
|
-
|
|
105
|
-
run402 domains
|
|
85
|
+
Usage:
|
|
86
|
+
run402 domains disconnect <domain> --project <id> --confirm
|
|
106
87
|
`,
|
|
107
88
|
};
|
|
108
89
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
90
|
+
const CONNECT_VALUE_FLAGS = [
|
|
91
|
+
"--project",
|
|
92
|
+
"--web-target",
|
|
93
|
+
"--receive-strategy",
|
|
94
|
+
"--mail-subdomain",
|
|
95
|
+
"--mailbox-addresses",
|
|
96
|
+
"--addresses",
|
|
97
|
+
"--activation",
|
|
98
|
+
"--authority",
|
|
99
|
+
"--confirm-mx-takeover",
|
|
100
|
+
];
|
|
101
|
+
const COMMON_VALUE_FLAGS = ["--project"];
|
|
102
|
+
|
|
103
|
+
function print(data) {
|
|
104
|
+
console.log(JSON.stringify(data, null, 2));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function removed(command, replacement) {
|
|
108
|
+
fail({
|
|
109
|
+
code: "COMMAND_REMOVED",
|
|
110
|
+
message: `${command} has been removed. Use ${replacement}.`,
|
|
111
|
+
details: { command, replacement },
|
|
112
|
+
next_actions: [{ type: "use_replacement_command", command: replacement }],
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function parseCommon(args, extraKnown = [], valueFlags = COMMON_VALUE_FLAGS) {
|
|
117
|
+
const parsed = normalizeArgv(args);
|
|
118
|
+
assertKnownFlags(parsed, [...valueFlags, ...extraKnown, "--help", "-h"], valueFlags);
|
|
119
|
+
return {
|
|
120
|
+
parsed,
|
|
121
|
+
projectId: resolveProjectId(flagValue(parsed, "--project")),
|
|
122
|
+
rest: positionalArgs(parsed, valueFlags),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function normalizeReceiveStrategy(value) {
|
|
127
|
+
if (!value) return "auto";
|
|
128
|
+
const map = {
|
|
129
|
+
auto: "auto",
|
|
130
|
+
"outbound-only": "outbound_only",
|
|
131
|
+
outbound_only: "outbound_only",
|
|
132
|
+
forwarding: "forwarding_mode",
|
|
133
|
+
"forwarding-mode": "forwarding_mode",
|
|
134
|
+
forwarding_mode: "forwarding_mode",
|
|
135
|
+
subdomain: "subdomain_mode",
|
|
136
|
+
"subdomain-mode": "subdomain_mode",
|
|
137
|
+
subdomain_mode: "subdomain_mode",
|
|
138
|
+
"full-mx": "full_receive_takeover",
|
|
139
|
+
"full-receive-takeover": "full_receive_takeover",
|
|
140
|
+
full_receive_takeover: "full_receive_takeover",
|
|
141
|
+
};
|
|
142
|
+
const normalized = map[value];
|
|
143
|
+
if (!normalized) {
|
|
115
144
|
fail({
|
|
116
145
|
code: "BAD_FLAG",
|
|
117
|
-
message: "--
|
|
118
|
-
details: { flag: "--
|
|
146
|
+
message: "--receive-strategy must be one of: auto, outbound-only, forwarding, subdomain, full-mx.",
|
|
147
|
+
details: { flag: "--receive-strategy", value },
|
|
119
148
|
});
|
|
120
149
|
}
|
|
121
|
-
return
|
|
122
|
-
project: flagValue(parsedArgs, "--project"),
|
|
123
|
-
authMode: auth === "service-key" ? "service_key" : "principal",
|
|
124
|
-
rest: positionalArgs(parsedArgs, valueFlags),
|
|
125
|
-
args: parsedArgs,
|
|
126
|
-
};
|
|
150
|
+
return normalized;
|
|
127
151
|
}
|
|
128
152
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const
|
|
132
|
-
|
|
133
|
-
|
|
153
|
+
function parseMailboxAddressMode(value) {
|
|
154
|
+
if (!value) return null;
|
|
155
|
+
const allowed = ["primary", "alias", "managed", "none"];
|
|
156
|
+
if (value.includes(",") || !allowed.includes(value)) {
|
|
157
|
+
fail({
|
|
158
|
+
code: "BAD_FLAG",
|
|
159
|
+
message: "--mailbox-addresses must be one of: primary, alias, managed, none.",
|
|
160
|
+
hint: "Use `--mailbox-addresses primary --addresses info,legal`, not `--mailbox-addresses info,legal`.",
|
|
161
|
+
details: { flag: "--mailbox-addresses", value, replacement_flag: "--addresses" },
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function parseAddresses(value) {
|
|
168
|
+
if (!value) return [];
|
|
169
|
+
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function desiredFromConnectFlags(domain, parsed) {
|
|
173
|
+
const desired = {};
|
|
174
|
+
const web = parsed.includes("--web") || flagValue(parsed, "--web-target") || parsed.includes("--primary-web") || parsed.includes("--web-alias");
|
|
175
|
+
if (web) {
|
|
176
|
+
desired.web = {
|
|
177
|
+
enabled: true,
|
|
178
|
+
target: flagValue(parsed, "--web-target") ?? "production",
|
|
179
|
+
role: parsed.includes("--web-alias") ? "alias" : "primary",
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const emailSend = parsed.includes("--email-send");
|
|
184
|
+
const emailReceive = parsed.includes("--email-receive");
|
|
185
|
+
const mailboxMode = parseMailboxAddressMode(flagValue(parsed, "--mailbox-addresses"));
|
|
186
|
+
if (emailSend || emailReceive || mailboxMode) {
|
|
187
|
+
const email = {};
|
|
188
|
+
if (emailSend) email.send = { enabled: true };
|
|
189
|
+
if (emailReceive) {
|
|
190
|
+
const receive = {
|
|
191
|
+
enabled: true,
|
|
192
|
+
strategy: normalizeReceiveStrategy(flagValue(parsed, "--receive-strategy")),
|
|
193
|
+
};
|
|
194
|
+
const mailSubdomain = flagValue(parsed, "--mail-subdomain");
|
|
195
|
+
if (mailSubdomain) receive.mail_subdomain = mailSubdomain;
|
|
196
|
+
const mxFingerprint = flagValue(parsed, "--confirm-mx-takeover");
|
|
197
|
+
if (mxFingerprint) receive.observed_mx_fingerprint = mxFingerprint;
|
|
198
|
+
email.receive = receive;
|
|
199
|
+
}
|
|
200
|
+
if (mailboxMode) {
|
|
201
|
+
const locals = parseAddresses(flagValue(parsed, "--addresses"));
|
|
202
|
+
if ((mailboxMode === "primary" || mailboxMode === "alias") && locals.length === 0) {
|
|
203
|
+
fail({
|
|
204
|
+
code: "BAD_FLAG",
|
|
205
|
+
message: "--addresses is required when --mailbox-addresses is primary or alias.",
|
|
206
|
+
details: { flag: "--addresses", domain },
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
const create = parsed.includes("--create-mailboxes")
|
|
210
|
+
? true
|
|
211
|
+
: parsed.includes("--no-create-mailboxes") ? false : false;
|
|
212
|
+
email.mailbox_addresses = {
|
|
213
|
+
mode: mailboxMode,
|
|
214
|
+
addresses: locals.map((local) => ({
|
|
215
|
+
local_part: local,
|
|
216
|
+
mailbox_slug: local,
|
|
217
|
+
create_mailbox: create,
|
|
218
|
+
})),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
const activation = flagValue(parsed, "--activation");
|
|
222
|
+
if (activation) {
|
|
223
|
+
if (!["automatic_when_ready", "manual"].includes(activation)) {
|
|
224
|
+
fail({
|
|
225
|
+
code: "BAD_FLAG",
|
|
226
|
+
message: "--activation must be automatic_when_ready or manual.",
|
|
227
|
+
details: { flag: "--activation", value: activation },
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
email.activation = activation;
|
|
231
|
+
}
|
|
232
|
+
desired.email = email;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (!desired.web && !desired.email) {
|
|
134
236
|
fail({
|
|
135
237
|
code: "BAD_USAGE",
|
|
136
|
-
message: "
|
|
137
|
-
hint: "
|
|
238
|
+
message: "No desired domain capability selected.",
|
|
239
|
+
hint: "Use --web, --email-send, --email-receive, and/or --mailbox-addresses.",
|
|
138
240
|
});
|
|
139
241
|
}
|
|
140
|
-
|
|
141
|
-
|
|
242
|
+
return desired;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function connect(args) {
|
|
246
|
+
const parsed = normalizeArgv(args);
|
|
247
|
+
assertKnownFlags(parsed, [
|
|
248
|
+
...CONNECT_VALUE_FLAGS,
|
|
249
|
+
"--web",
|
|
250
|
+
"--primary-web",
|
|
251
|
+
"--web-alias",
|
|
252
|
+
"--email-send",
|
|
253
|
+
"--email-receive",
|
|
254
|
+
"--create-mailboxes",
|
|
255
|
+
"--no-create-mailboxes",
|
|
256
|
+
"--help",
|
|
257
|
+
"-h",
|
|
258
|
+
], CONNECT_VALUE_FLAGS);
|
|
259
|
+
const rest = positionalArgs(parsed, CONNECT_VALUE_FLAGS);
|
|
260
|
+
const domain = rest[0];
|
|
261
|
+
if (!domain) fail({ code: "BAD_USAGE", message: "Missing <domain>.", hint: "run402 domains connect <domain> --project <id> --web" });
|
|
262
|
+
if (rest.length > 1) fail({ code: "BAD_USAGE", message: `Unexpected argument for domains connect: ${rest[1]}` });
|
|
263
|
+
if (parsed.includes("--create-mailboxes") && parsed.includes("--no-create-mailboxes")) {
|
|
264
|
+
fail({ code: "BAD_FLAG", message: "Choose only one of --create-mailboxes or --no-create-mailboxes." });
|
|
265
|
+
}
|
|
266
|
+
const authority = flagValue(parsed, "--authority");
|
|
267
|
+
if (authority && !["auto", "manual-dns", "provider-connect", "delegated-subdomain", "hosted-zone"].includes(authority)) {
|
|
268
|
+
fail({ code: "BAD_FLAG", message: "--authority must be one of: auto, manual-dns, provider-connect, delegated-subdomain, hosted-zone.", details: { flag: "--authority", value: authority } });
|
|
142
269
|
}
|
|
143
|
-
const projectId = resolveProjectId(project);
|
|
270
|
+
const projectId = resolveProjectId(flagValue(parsed, "--project"));
|
|
144
271
|
try {
|
|
145
|
-
|
|
146
|
-
console.log(JSON.stringify(data, null, 2));
|
|
272
|
+
print(await getSdk().domains.ensure(projectId, domain, { desired: desiredFromConnectFlags(domain, parsed) }));
|
|
147
273
|
} catch (err) {
|
|
148
274
|
reportSdkError(err);
|
|
149
275
|
}
|
|
150
276
|
}
|
|
151
277
|
|
|
152
278
|
async function list(args) {
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
if (rest.length > 0) {
|
|
156
|
-
fail({
|
|
157
|
-
code: "BAD_USAGE",
|
|
158
|
-
message: `Unexpected argument for domains list: ${rest[0]}`,
|
|
159
|
-
hint: "Use `run402 domains list --project <id>`.",
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
const projectId = resolveProjectId(project);
|
|
279
|
+
const { projectId, rest } = parseCommon(args, ["--include-managed"]);
|
|
280
|
+
if (rest.length > 0) fail({ code: "BAD_USAGE", message: `Unexpected argument for domains list: ${rest[0]}` });
|
|
163
281
|
try {
|
|
164
|
-
|
|
165
|
-
console.log(JSON.stringify(data, null, 2));
|
|
282
|
+
print(await getSdk().domains.list(projectId));
|
|
166
283
|
} catch (err) {
|
|
167
284
|
reportSdkError(err);
|
|
168
285
|
}
|
|
169
286
|
}
|
|
170
287
|
|
|
171
288
|
async function status(args) {
|
|
172
|
-
const {
|
|
289
|
+
const { projectId, rest } = parseCommon(args);
|
|
173
290
|
const domain = rest[0];
|
|
174
|
-
if (!domain) {
|
|
175
|
-
|
|
176
|
-
code: "BAD_USAGE",
|
|
177
|
-
message: "Missing <domain>.",
|
|
178
|
-
hint: "run402 domains status <domain> [--project <id>]",
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (rest.length > 1) {
|
|
182
|
-
fail({ code: "BAD_USAGE", message: `Unexpected argument for domains status: ${rest[1]}` });
|
|
183
|
-
}
|
|
184
|
-
const projectId = resolveProjectId(project);
|
|
291
|
+
if (!domain) fail({ code: "BAD_USAGE", message: "Missing <domain>.", hint: "run402 domains status <domain> [--project <id>]" });
|
|
292
|
+
if (rest.length > 1) fail({ code: "BAD_USAGE", message: `Unexpected argument for domains status: ${rest[1]}` });
|
|
185
293
|
try {
|
|
186
|
-
|
|
187
|
-
console.log(JSON.stringify(data, null, 2));
|
|
294
|
+
print(await getSdk().domains.get(projectId, domain));
|
|
188
295
|
} catch (err) {
|
|
189
296
|
reportSdkError(err);
|
|
190
297
|
}
|
|
191
298
|
}
|
|
192
299
|
|
|
193
|
-
async function
|
|
194
|
-
const
|
|
300
|
+
async function dns(args) {
|
|
301
|
+
const parsed = normalizeArgv(args);
|
|
302
|
+
const valueFlags = ["--project", "--format"];
|
|
303
|
+
assertKnownFlags(parsed, [...valueFlags, "--help", "-h"], valueFlags);
|
|
304
|
+
const rest = positionalArgs(parsed, valueFlags);
|
|
195
305
|
const domain = rest[0];
|
|
196
|
-
if (!domain) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
306
|
+
if (!domain) fail({ code: "BAD_USAGE", message: "Missing <domain>.", hint: "run402 domains dns <domain> [--format json|bind]" });
|
|
307
|
+
const format = flagValue(parsed, "--format") ?? "json";
|
|
308
|
+
if (!["json", "bind"].includes(format)) fail({ code: "BAD_FLAG", message: "--format must be json or bind.", details: { flag: "--format", value: format } });
|
|
309
|
+
const projectId = resolveProjectId(flagValue(parsed, "--project"));
|
|
310
|
+
try {
|
|
311
|
+
const data = await getSdk().domains.get(projectId, domain);
|
|
312
|
+
if (format === "bind") {
|
|
313
|
+
console.log(data.dns_records.map((record) => record.bind).filter(Boolean).join("\n"));
|
|
314
|
+
} else {
|
|
315
|
+
print({ domain: data.domain, dns_records: data.dns_records, checks: data.checks, next_action: data.next_action });
|
|
316
|
+
}
|
|
317
|
+
} catch (err) {
|
|
318
|
+
reportSdkError(err);
|
|
202
319
|
}
|
|
203
|
-
|
|
204
|
-
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async function action(name, args) {
|
|
323
|
+
const extraValue = name === "test-receive" ? ["--to"] : name === "wait" ? ["--until", "--timeout-ms", "--interval-ms"] : name === "apply" ? ["--authority"] : [];
|
|
324
|
+
const parsed = normalizeArgv(args);
|
|
325
|
+
const valueFlags = ["--project", ...extraValue];
|
|
326
|
+
assertKnownFlags(parsed, [...valueFlags, "--help", "-h"], valueFlags);
|
|
327
|
+
const rest = positionalArgs(parsed, valueFlags);
|
|
328
|
+
const domain = rest[0];
|
|
329
|
+
if (!domain) fail({ code: "BAD_USAGE", message: `Missing <domain>.`, hint: `run402 domains ${name} <domain> [--project <id>]` });
|
|
330
|
+
if (rest.length > 1) fail({ code: "BAD_USAGE", message: `Unexpected argument for domains ${name}: ${rest[1]}` });
|
|
331
|
+
const projectId = resolveProjectId(flagValue(parsed, "--project"));
|
|
332
|
+
try {
|
|
333
|
+
const sdk = getSdk().domains;
|
|
334
|
+
if (name === "check") return print(await sdk.check(projectId, domain));
|
|
335
|
+
if (name === "apply") return print(await sdk.apply(projectId, domain));
|
|
336
|
+
if (name === "repair") return print(await sdk.repair(projectId, domain));
|
|
337
|
+
if (name === "activate") return print(await sdk.activate(projectId, domain));
|
|
338
|
+
if (name === "test-receive") {
|
|
339
|
+
const to = flagValue(parsed, "--to");
|
|
340
|
+
if (!to) fail({ code: "BAD_FLAG", message: "--to is required for domains test-receive.", details: { flag: "--to" } });
|
|
341
|
+
return print(await sdk.testReceive(projectId, domain, to));
|
|
342
|
+
}
|
|
343
|
+
if (name === "wait") {
|
|
344
|
+
const until = flagValue(parsed, "--until") ?? "active";
|
|
345
|
+
if (!["active", "safe", "receive-active"].includes(until)) fail({ code: "BAD_FLAG", message: "--until must be active, safe, or receive-active.", details: { flag: "--until", value: until } });
|
|
346
|
+
const timeoutMs = parseIntegerFlag("--timeout-ms", flagValue(parsed, "--timeout-ms"), { min: 1, def: 120000 });
|
|
347
|
+
const intervalMs = parseIntegerFlag("--interval-ms", flagValue(parsed, "--interval-ms"), { min: 1, def: 5000 });
|
|
348
|
+
return print(await sdk.wait(projectId, domain, { until, timeoutMs, intervalMs }));
|
|
349
|
+
}
|
|
350
|
+
} catch (err) {
|
|
351
|
+
reportSdkError(err);
|
|
205
352
|
}
|
|
206
|
-
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async function disconnect(args) {
|
|
356
|
+
const parsed = normalizeArgv(args);
|
|
357
|
+
const valueFlags = ["--project"];
|
|
358
|
+
assertKnownFlags(parsed, [...valueFlags, "--confirm", "--help", "-h"], valueFlags);
|
|
359
|
+
const rest = positionalArgs(parsed, valueFlags);
|
|
360
|
+
const domain = rest[0];
|
|
361
|
+
if (!domain) fail({ code: "BAD_USAGE", message: "Missing <domain>.", hint: "run402 domains disconnect <domain> --confirm [--project <id>]" });
|
|
362
|
+
if (!parsed.includes("--confirm")) {
|
|
207
363
|
fail({
|
|
208
364
|
code: "CONFIRMATION_REQUIRED",
|
|
209
|
-
message: `
|
|
365
|
+
message: `Disconnecting ${domain} removes the desired custom binding. Re-run with --confirm to proceed.`,
|
|
210
366
|
details: { domain },
|
|
211
367
|
});
|
|
212
368
|
}
|
|
213
|
-
const projectId = resolveProjectId(project);
|
|
369
|
+
const projectId = resolveProjectId(flagValue(parsed, "--project"));
|
|
214
370
|
try {
|
|
215
|
-
await getSdk().domains.
|
|
216
|
-
console.log(JSON.stringify({ domain, project_id: projectId, released: true }));
|
|
371
|
+
print(await getSdk().domains.disconnect(projectId, domain));
|
|
217
372
|
} catch (err) {
|
|
218
373
|
reportSdkError(err);
|
|
219
374
|
}
|
|
220
375
|
}
|
|
221
376
|
|
|
222
377
|
export async function run(sub, args) {
|
|
223
|
-
if (!sub || sub ===
|
|
224
|
-
if (
|
|
378
|
+
if (!sub || sub === "--help" || sub === "-h") { console.log(HELP); process.exit(0); }
|
|
379
|
+
if ((args ?? []).includes("--help") || (args ?? []).includes("-h")) {
|
|
380
|
+
const help = SUB_HELP[sub];
|
|
381
|
+
if (help) {
|
|
382
|
+
console.log(help);
|
|
383
|
+
process.exit(0);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
225
386
|
switch (sub) {
|
|
226
|
-
case "
|
|
227
|
-
case "list":
|
|
387
|
+
case "connect": await connect(args); break;
|
|
388
|
+
case "list": await list(args); break;
|
|
228
389
|
case "status": await status(args); break;
|
|
229
|
-
case "
|
|
390
|
+
case "dns": await dns(args); break;
|
|
391
|
+
case "check": await action("check", args); break;
|
|
392
|
+
case "apply": await action("apply", args); break;
|
|
393
|
+
case "repair": await action("repair", args); break;
|
|
394
|
+
case "test-receive": await action("test-receive", args); break;
|
|
395
|
+
case "wait": await action("wait", args); break;
|
|
396
|
+
case "activate": await action("activate", args); break;
|
|
397
|
+
case "disconnect": await disconnect(args); break;
|
|
398
|
+
case "add": removed("run402 domains add", "run402 domains connect <domain> --project <id> --web"); break;
|
|
399
|
+
case "delete": removed("run402 domains delete", "run402 domains disconnect <domain> --project <id> --confirm"); break;
|
|
230
400
|
default:
|
|
231
401
|
fail({ code: "UNKNOWN_SUBCOMMAND", message: `Unknown domains subcommand: ${sub}`, hint: "Run `run402 domains --help` for usage.", details: { command: "domains", subcommand: sub } });
|
|
232
402
|
}
|