authflow-cli 0.4.0 → 0.5.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/README.md +157 -5
- package/dist/checks.d.ts +30 -0
- package/dist/checks.js +36 -0
- package/dist/checks.js.map +1 -0
- package/dist/client-conformance.d.ts +5 -1
- package/dist/client-conformance.js +9 -1
- package/dist/client-conformance.js.map +1 -1
- package/dist/commands/doctor.js +5 -1
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/probe.js +1 -1
- package/dist/commands/probe.js.map +1 -1
- package/dist/commands/register.js +30 -2
- package/dist/commands/register.js.map +1 -1
- package/dist/commands/set-domain.d.ts +34 -0
- package/dist/commands/set-domain.js +162 -0
- package/dist/commands/set-domain.js.map +1 -0
- package/dist/config.d.ts +32 -1
- package/dist/config.js.map +1 -1
- package/dist/custom-domain.d.ts +96 -0
- package/dist/custom-domain.js +289 -0
- package/dist/custom-domain.js.map +1 -0
- package/dist/doctor.d.ts +31 -23
- package/dist/doctor.js +271 -57
- package/dist/doctor.js.map +1 -1
- package/dist/google-redirect.d.ts +5 -1
- package/dist/google-redirect.js +11 -5
- package/dist/google-redirect.js.map +1 -1
- package/dist/mcp/server.d.ts +8 -1
- package/dist/mcp/server.js +145 -11
- package/dist/mcp/server.js.map +1 -1
- package/dist/program.js +4 -1
- package/dist/program.js.map +1 -1
- package/dist/rail.d.ts +16 -0
- package/dist/rail.js +39 -0
- package/dist/rail.js.map +1 -1
- package/dist/resource-urls.d.ts +27 -0
- package/dist/resource-urls.js +56 -0
- package/dist/resource-urls.js.map +1 -0
- package/dist/status.d.ts +15 -1
- package/dist/status.js +59 -5
- package/dist/status.js.map +1 -1
- package/dist/surface.d.ts +1 -0
- package/dist/surface.js +1 -0
- package/dist/surface.js.map +1 -1
- package/dist/tenant-host.d.ts +49 -0
- package/dist/tenant-host.js +367 -0
- package/dist/tenant-host.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.js +27 -0
- package/dist/version.js.map +1 -0
- package/package.json +4 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { requireAdminKey, requireIssuer } from "../config.js";
|
|
2
|
+
import { assertCustomDomainReturned, assertCustomDomainSet, customDomainState, defaultPublicPath, defaultWaitTimeoutSeconds, describeCustomDomainState, dnsInstructions, formatDuration, isCustomDomainFailure, parsePublicHost, parsePublicPath, setDomainCommandLine, summarizeCustomDomain, waitForCustomDomain, waitIntervalMs, } from "../custom-domain.js";
|
|
3
|
+
import { describePending, getResource, setCustomDomain, verifyCustomDomain } from "../rail.js";
|
|
4
|
+
import { formatCursorMcpConfig } from "../status.js";
|
|
5
|
+
/**
|
|
6
|
+
* Serve a resource on the tenant's own hostname: the "bring your existing MCP URL" step.
|
|
7
|
+
*
|
|
8
|
+
* Setting the host is the easy half. The rail answers with the DNS records the tenant has to create
|
|
9
|
+
* at a registrar this CLI cannot reach, so the command's job is to put those records on screen
|
|
10
|
+
* exactly as the rail issued them and then, with --wait, to confirm they took.
|
|
11
|
+
*
|
|
12
|
+
* There is no separate verify command, so this is also how a tenant re-checks after adding the
|
|
13
|
+
* records: with no --host it verifies the host already set on the resource instead of setting one.
|
|
14
|
+
*/
|
|
15
|
+
export function setDomainCommand(program, env = {}) {
|
|
16
|
+
program
|
|
17
|
+
.command("set-domain")
|
|
18
|
+
.description("Serve a resource on your own hostname, or re-verify the one already set: print the DNS records to add and, with --wait, poll until the domain is ready")
|
|
19
|
+
.requiredOption("--slug <slug>", "Resource slug")
|
|
20
|
+
.option("--host <host>", "Your hostname, e.g. mcp.example.com (a subdomain; no scheme, path or port). Omit it to re-verify the host already set on the resource")
|
|
21
|
+
.option("--path <path>", "Public MCP path on that host. Sent only when given: the rail uses /mcp the first time and keeps the stored path afterwards. Needs --host")
|
|
22
|
+
.option("--wait", "Poll verification until the domain is ready, fails, or --timeout passes")
|
|
23
|
+
.option("--timeout <seconds>", `With --wait: how long to keep polling before giving up (default ${defaultWaitTimeoutSeconds})`)
|
|
24
|
+
.option("--issuer <url>", "Authflow issuer (default: AUTHFLOW_ISSUER)")
|
|
25
|
+
.option("--admin-key <key>", "Admin API key (default: AUTHFLOW_ADMIN_API_KEY)")
|
|
26
|
+
.action(async (opts) => {
|
|
27
|
+
process.exitCode = await runSetDomain(opts, env);
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Returns the exit code instead of setting it: 0 when the domain is ready (or, without --wait,
|
|
32
|
+
* when it was set or re-checked and is not in a failure state), 1 when it timed out or is blocked,
|
|
33
|
+
* moved, or removed.
|
|
34
|
+
*/
|
|
35
|
+
export async function runSetDomain(opts, env = {}) {
|
|
36
|
+
const log = env.log ?? ((line) => console.log(line));
|
|
37
|
+
const timeoutMs = resolveTimeoutMs(opts);
|
|
38
|
+
// A path belongs to a host being set. Re-verifying sends nothing, so a --path there would be
|
|
39
|
+
// silently ignored, and someone who typed it believes they changed the path.
|
|
40
|
+
if (opts.host === undefined && opts.path !== undefined) {
|
|
41
|
+
throw new Error("--path only applies with --host. Without a host this re-verifies the one already set, and the " +
|
|
42
|
+
"rail keeps its stored path.");
|
|
43
|
+
}
|
|
44
|
+
const host = opts.host === undefined ? undefined : parsePublicHost(opts.host);
|
|
45
|
+
const path = opts.path === undefined ? undefined : parsePublicPath(opts.path);
|
|
46
|
+
const auth = { issuer: requireIssuer(opts.issuer), adminKey: requireAdminKey(opts.adminKey) };
|
|
47
|
+
// Both routes end with a receipt to show and, with --wait, to poll from.
|
|
48
|
+
let receipt;
|
|
49
|
+
let hostName;
|
|
50
|
+
if (host !== undefined) {
|
|
51
|
+
receipt = await setCustomDomain(auth, opts.slug, host, path);
|
|
52
|
+
hostName = host;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Ask the receipt first. Verifying a resource with no host would fail with an error that does
|
|
56
|
+
// not say how to fix it; this says to pass --host.
|
|
57
|
+
hostName = assertCustomDomainSet(await getResource(auth, opts.slug), opts.slug);
|
|
58
|
+
receipt = await verifyCustomDomain(auth, opts.slug);
|
|
59
|
+
}
|
|
60
|
+
assertCustomDomainReturned(receipt, hostName, opts.slug);
|
|
61
|
+
const url = receipt.gateway_url ??
|
|
62
|
+
receipt.resource ??
|
|
63
|
+
`https://${hostName}${receipt.public_path ?? path ?? defaultPublicPath}`;
|
|
64
|
+
log(`${receipt.slug ?? opts.slug} -> ${url}`);
|
|
65
|
+
log(`state: ${summarizeCustomDomain(receipt)}`);
|
|
66
|
+
const instructions = dnsInstructions(receipt);
|
|
67
|
+
if (instructions.length > 0) {
|
|
68
|
+
log("");
|
|
69
|
+
for (const line of instructions) {
|
|
70
|
+
log(line);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const state = customDomainState(receipt);
|
|
74
|
+
if (state === "ready") {
|
|
75
|
+
return reportReady(receipt, opts.slug, log);
|
|
76
|
+
}
|
|
77
|
+
if (isCustomDomainFailure(state)) {
|
|
78
|
+
return reportFailure(receipt, opts.slug, log);
|
|
79
|
+
}
|
|
80
|
+
if (!opts.wait) {
|
|
81
|
+
log("");
|
|
82
|
+
log(host === undefined
|
|
83
|
+
? `Still not ready. To keep checking (now, then every ${formatDuration(waitIntervalMs)} until ready):`
|
|
84
|
+
: `Once the records are in place, verify (re-checks now, then every ${formatDuration(waitIntervalMs)} until ready):`);
|
|
85
|
+
log(` ${setDomainCommandLine(opts.slug)}`);
|
|
86
|
+
return 0;
|
|
87
|
+
}
|
|
88
|
+
log("");
|
|
89
|
+
log("Add the records above. This keeps checking and says when the domain is ready.");
|
|
90
|
+
const result = await waitForCustomDomain(receipt, {
|
|
91
|
+
verify: () => verifyCustomDomain(auth, opts.slug),
|
|
92
|
+
timeoutMs,
|
|
93
|
+
sleep: env.sleep,
|
|
94
|
+
now: env.now,
|
|
95
|
+
onProgress: (line) => log(` ${line}`),
|
|
96
|
+
});
|
|
97
|
+
switch (result.outcome) {
|
|
98
|
+
case "ready":
|
|
99
|
+
return reportReady(result.receipt, opts.slug, log);
|
|
100
|
+
case "failed":
|
|
101
|
+
return reportFailure(result.receipt, opts.slug, log);
|
|
102
|
+
case "timeout":
|
|
103
|
+
return reportTimeout(result.receipt, opts.slug, timeoutMs, log);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function resolveTimeoutMs(opts) {
|
|
107
|
+
if (opts.timeout === undefined) {
|
|
108
|
+
return defaultWaitTimeoutSeconds * 1000;
|
|
109
|
+
}
|
|
110
|
+
if (!opts.wait) {
|
|
111
|
+
throw new Error("--timeout only applies with --wait.");
|
|
112
|
+
}
|
|
113
|
+
const seconds = Number(opts.timeout);
|
|
114
|
+
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
115
|
+
throw new Error(`--timeout must be a positive number of seconds (got '${opts.timeout}').`);
|
|
116
|
+
}
|
|
117
|
+
return seconds * 1000;
|
|
118
|
+
}
|
|
119
|
+
function reportReady(receipt, slug, log) {
|
|
120
|
+
log("");
|
|
121
|
+
log(`Ready. ${receipt.gateway_url ?? receipt.resource ?? "The hostname"} is serving this resource.`);
|
|
122
|
+
const config = formatCursorMcpConfig(receipt);
|
|
123
|
+
if (config.length > 0) {
|
|
124
|
+
log("");
|
|
125
|
+
for (const line of config) {
|
|
126
|
+
log(line);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// A ready hostname is one requirement, not the whole go-live. Say what is left rather than let
|
|
130
|
+
// "Ready" read as "live".
|
|
131
|
+
const pending = receipt.pending_requirements ?? [];
|
|
132
|
+
if (pending.length > 0) {
|
|
133
|
+
log("");
|
|
134
|
+
log("Still pending before go-live:");
|
|
135
|
+
for (const requirement of pending) {
|
|
136
|
+
log(` ${requirement}`);
|
|
137
|
+
log(` ${describePending(requirement, slug)}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
function reportFailure(receipt, slug, log) {
|
|
143
|
+
const state = customDomainState(receipt);
|
|
144
|
+
log("");
|
|
145
|
+
log(`The custom domain is ${state}: ${describeCustomDomainState(state)}.`);
|
|
146
|
+
log(`last error: ${receipt.custom_domain?.last_error?.trim() || "(the rail gave no detail)"}`);
|
|
147
|
+
log("");
|
|
148
|
+
log(`Fix the cause, then re-run: ${setDomainCommandLine(slug)}`);
|
|
149
|
+
return 1;
|
|
150
|
+
}
|
|
151
|
+
function reportTimeout(receipt, slug, timeoutMs, log) {
|
|
152
|
+
log("");
|
|
153
|
+
log(`Still ${customDomainState(receipt) ?? "unknown"} after ${formatDuration(timeoutMs)}.`);
|
|
154
|
+
const error = receipt.custom_domain?.last_error?.trim();
|
|
155
|
+
if (error) {
|
|
156
|
+
log(`last error: ${error}`);
|
|
157
|
+
}
|
|
158
|
+
log("Nothing is lost. Add or fix the records above, then re-run:");
|
|
159
|
+
log(` ${setDomainCommandLine(slug)}`);
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=set-domain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"set-domain.js","sourceRoot":"","sources":["../../src/commands/set-domain.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -8,13 +8,39 @@ export type CliConfig = {
|
|
|
8
8
|
export declare function requireIssuer(flag?: string): string;
|
|
9
9
|
export declare function requireAdminKey(flag?: string): string;
|
|
10
10
|
export declare function requireOriginKey(flag?: string): string;
|
|
11
|
+
/** One DNS record a tenant must create for a custom domain (`purpose`: ownership, traffic, certificate). */
|
|
12
|
+
export type CustomDomainRecord = {
|
|
13
|
+
type: string;
|
|
14
|
+
name: string;
|
|
15
|
+
value: string;
|
|
16
|
+
purpose: string;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* The claim on a tenant-owned hostname. `state` is one of awaiting_ownership, provisioning,
|
|
20
|
+
* awaiting_traffic, ready, moved, blocked, removed; it stays a plain string so a state a newer rail
|
|
21
|
+
* adds is shown rather than rejected.
|
|
22
|
+
*/
|
|
23
|
+
export type CustomDomainStatus = {
|
|
24
|
+
state: string;
|
|
25
|
+
cname_target?: string | null;
|
|
26
|
+
records?: CustomDomainRecord[];
|
|
27
|
+
last_error?: string | null;
|
|
28
|
+
checked_at?: string | null;
|
|
29
|
+
};
|
|
11
30
|
/** Registration receipt fields (snake_case) from RegistryAdminEndpoints. */
|
|
12
31
|
export type RegistrationReceipt = {
|
|
13
32
|
slug?: string;
|
|
14
33
|
display_name?: string;
|
|
34
|
+
/** Canonical resource URI. `https://{public_host}{public_path}` once a custom host is set. */
|
|
15
35
|
resource?: string;
|
|
16
36
|
gateway_url?: string;
|
|
17
37
|
prm_url?: string;
|
|
38
|
+
/** Tenant hostname when the resource is served on its own host (omitted otherwise). */
|
|
39
|
+
public_host?: string;
|
|
40
|
+
/** Public MCP path on that host, `/mcp` unless the tenant chose another. */
|
|
41
|
+
public_path?: string;
|
|
42
|
+
/** DNS records and claim state for the custom host (omitted when there is no host). */
|
|
43
|
+
custom_domain?: CustomDomainStatus;
|
|
18
44
|
issuer?: string;
|
|
19
45
|
keys_url?: string;
|
|
20
46
|
usage_url?: string;
|
|
@@ -29,8 +55,13 @@ export type RegistrationReceipt = {
|
|
|
29
55
|
gateway_egress_cidrs?: string[];
|
|
30
56
|
status?: string;
|
|
31
57
|
metering_mode?: string;
|
|
32
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* What still blocks go-live: origin_base_uri, connected_account_id, stripe_onboarding_complete,
|
|
60
|
+
* and custom_domain_dns while a custom host is set but not yet ready.
|
|
61
|
+
*/
|
|
33
62
|
pending_requirements?: string[];
|
|
63
|
+
last_probe_at_utc?: string | null;
|
|
64
|
+
last_probe_passed?: boolean | null;
|
|
34
65
|
};
|
|
35
66
|
export declare function readReceipt(path: string): Promise<RegistrationReceipt>;
|
|
36
67
|
export declare function resolveGatewayUrl(opts: {
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAU5C,MAAM,UAAU,aAAa,CAAC,IAAa;IACzC,MAAM,MAAM,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtF,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC3D,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAa;IAC3C,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACtE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAa;IAC5C,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IACvE,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { CustomDomainRecord, RegistrationReceipt } from "./config.js";
|
|
2
|
+
/**
|
|
3
|
+
* Everything about a tenant-owned hostname that more than one front end needs: validating what the
|
|
4
|
+
* user typed, showing the DNS records they have to create, and waiting for the rail to confirm them.
|
|
5
|
+
* `set-domain`, the `authflow_set_custom_domain` tool, `status`, and `register` all render the same
|
|
6
|
+
* records, so they render them from here; a table that differs between the CLI and the agent path is
|
|
7
|
+
* how a tenant ends up adding the wrong record.
|
|
8
|
+
*/
|
|
9
|
+
/** The public MCP path when a tenant does not choose one. */
|
|
10
|
+
export declare const defaultPublicPath = "/mcp";
|
|
11
|
+
/** How often verification is re-checked while waiting. Each call makes the rail do real DNS lookups. */
|
|
12
|
+
export declare const waitIntervalMs = 15000;
|
|
13
|
+
/** Default `set-domain --wait` budget: DNS propagation plus certificate issuance. */
|
|
14
|
+
export declare const defaultWaitTimeoutSeconds = 1800;
|
|
15
|
+
/**
|
|
16
|
+
* How long the MCP tool waits. A tool call that outlives the client's request timeout (60 s is the
|
|
17
|
+
* SDK default and common in hosts) is dropped along with its result, so the tool waits less than
|
|
18
|
+
* that, reports where things stand, and tells the agent to call again. The client's clock also
|
|
19
|
+
* covers the request that sets the host and the last verification still in flight when the wait
|
|
20
|
+
* ends, which is why this is well short of 60 s rather than just under it.
|
|
21
|
+
*/
|
|
22
|
+
export declare const mcpWaitTimeoutMs = 45000;
|
|
23
|
+
/**
|
|
24
|
+
* Normalises a hostname the user typed and rejects the things that are not one. The rail validates
|
|
25
|
+
* too; this exists because the likeliest mistake is pasting the MCP URL, and the rail's 400 does not
|
|
26
|
+
* say that.
|
|
27
|
+
*/
|
|
28
|
+
export declare function parsePublicHost(input: string, flag?: string,
|
|
29
|
+
/** Where the path goes, for the error. Null when the caller has no path to offer. */
|
|
30
|
+
pathFlag?: string | null): string;
|
|
31
|
+
export declare function parsePublicPath(input: string, flag?: string): string;
|
|
32
|
+
/**
|
|
33
|
+
* A 200 from the rail that carries no `custom_domain` has nothing to show or wait on. It means the
|
|
34
|
+
* rail predates custom domains or something upstream dropped the object; either way, say so rather
|
|
35
|
+
* than print an empty table and report success.
|
|
36
|
+
*/
|
|
37
|
+
export declare function assertCustomDomainReturned(receipt: RegistrationReceipt, host: string, slug: string): void;
|
|
38
|
+
/**
|
|
39
|
+
* Re-verifying means re-checking a host that is already there. A resource with none has nothing to
|
|
40
|
+
* verify, and the bare error from asking the rail anyway would not say how to fix it, so say that
|
|
41
|
+
* plainly and name the argument that sets one. Returns the host the resource is registered on.
|
|
42
|
+
*/
|
|
43
|
+
export declare function assertCustomDomainSet(receipt: RegistrationReceipt, slug: string, hostFlag?: string): string;
|
|
44
|
+
export declare function customDomainState(receipt: RegistrationReceipt): string | undefined;
|
|
45
|
+
export declare function isCustomDomainFailure(state: string | undefined): boolean;
|
|
46
|
+
/** One line on what a state means, for the person staring at it. */
|
|
47
|
+
export declare function describeCustomDomainState(state: string | undefined): string;
|
|
48
|
+
/** `awaiting_traffic (waiting for the traffic and certificate CNAMEs)`, plus the rail's last error. */
|
|
49
|
+
export declare function summarizeCustomDomain(receipt: RegistrationReceipt): string;
|
|
50
|
+
/** The records as an aligned TYPE / NAME / VALUE / PURPOSE table, in the order the rail listed them. */
|
|
51
|
+
export declare function formatDnsRecords(records: readonly CustomDomainRecord[]): string[];
|
|
52
|
+
/**
|
|
53
|
+
* Registrars disagree about the Name column. GoDaddy and several others append the domain
|
|
54
|
+
* themselves, so a full name there produces `mcp.example.com.example.com` and a record that
|
|
55
|
+
* silently never matches.
|
|
56
|
+
*/
|
|
57
|
+
export declare function registrarNameHint(host: string): string[];
|
|
58
|
+
/** The "add these records" block shared by every front end. Empty when the rail listed none. */
|
|
59
|
+
export declare function dnsInstructions(receipt: RegistrationReceipt): string[];
|
|
60
|
+
/**
|
|
61
|
+
* The command that re-checks the host already set on a resource. It names no host and no path: the
|
|
62
|
+
* rail keeps both, so there is nothing to retype and nothing to get wrong on a re-run.
|
|
63
|
+
*/
|
|
64
|
+
export declare function setDomainCommandLine(slug: string): string;
|
|
65
|
+
export type WaitOutcome = "ready" | "failed" | "timeout";
|
|
66
|
+
export type WaitResult = {
|
|
67
|
+
outcome: WaitOutcome;
|
|
68
|
+
/** The last receipt seen: the one that settled it, or the last one before giving up. */
|
|
69
|
+
receipt: RegistrationReceipt;
|
|
70
|
+
/** Verification calls that returned a receipt. */
|
|
71
|
+
polls: number;
|
|
72
|
+
elapsedMs: number;
|
|
73
|
+
/** The lines that were reported, in order, for callers that cannot stream them. */
|
|
74
|
+
progress: string[];
|
|
75
|
+
};
|
|
76
|
+
export type WaitOptions = {
|
|
77
|
+
/** Re-checks the domain now and returns the fresh receipt. */
|
|
78
|
+
verify: () => Promise<RegistrationReceipt>;
|
|
79
|
+
timeoutMs: number;
|
|
80
|
+
intervalMs?: number;
|
|
81
|
+
/** Injectable so tests do not wait in real time. */
|
|
82
|
+
sleep?: (ms: number) => Promise<void>;
|
|
83
|
+
now?: () => number;
|
|
84
|
+
onProgress?: (line: string) => void;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Polls verification until the domain is ready, fails, or the budget runs out. Verifies straight
|
|
88
|
+
* away and then every interval: a tenant who re-runs after adding their records should not wait a
|
|
89
|
+
* full interval to hear it worked.
|
|
90
|
+
*
|
|
91
|
+
* `ready` and the failure states settle it. Anything else, including a state this CLI has never
|
|
92
|
+
* heard of, keeps waiting, so a rail that adds a state does not break an old CLI.
|
|
93
|
+
*/
|
|
94
|
+
export declare function waitForCustomDomain(initial: RegistrationReceipt, options: WaitOptions): Promise<WaitResult>;
|
|
95
|
+
/** `15s`, `50s`, `30m`. */
|
|
96
|
+
export declare function formatDuration(ms: number): string;
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Everything about a tenant-owned hostname that more than one front end needs: validating what the
|
|
3
|
+
* user typed, showing the DNS records they have to create, and waiting for the rail to confirm them.
|
|
4
|
+
* `set-domain`, the `authflow_set_custom_domain` tool, `status`, and `register` all render the same
|
|
5
|
+
* records, so they render them from here; a table that differs between the CLI and the agent path is
|
|
6
|
+
* how a tenant ends up adding the wrong record.
|
|
7
|
+
*/
|
|
8
|
+
/** The public MCP path when a tenant does not choose one. */
|
|
9
|
+
export const defaultPublicPath = "/mcp";
|
|
10
|
+
/** How often verification is re-checked while waiting. Each call makes the rail do real DNS lookups. */
|
|
11
|
+
export const waitIntervalMs = 15_000;
|
|
12
|
+
/** Default `set-domain --wait` budget: DNS propagation plus certificate issuance. */
|
|
13
|
+
export const defaultWaitTimeoutSeconds = 1800;
|
|
14
|
+
/**
|
|
15
|
+
* How long the MCP tool waits. A tool call that outlives the client's request timeout (60 s is the
|
|
16
|
+
* SDK default and common in hosts) is dropped along with its result, so the tool waits less than
|
|
17
|
+
* that, reports where things stand, and tells the agent to call again. The client's clock also
|
|
18
|
+
* covers the request that sets the host and the last verification still in flight when the wait
|
|
19
|
+
* ends, which is why this is well short of 60 s rather than just under it.
|
|
20
|
+
*/
|
|
21
|
+
export const mcpWaitTimeoutMs = 45_000;
|
|
22
|
+
const failureStates = new Set(["moved", "blocked", "removed"]);
|
|
23
|
+
/** Consecutive verification errors tolerated while waiting before the error is surfaced. */
|
|
24
|
+
const maxConsecutiveFailures = 3;
|
|
25
|
+
/** While nothing changes, say so about once a minute at the default interval. */
|
|
26
|
+
const heartbeatEveryPolls = 4;
|
|
27
|
+
/**
|
|
28
|
+
* Normalises a hostname the user typed and rejects the things that are not one. The rail validates
|
|
29
|
+
* too; this exists because the likeliest mistake is pasting the MCP URL, and the rail's 400 does not
|
|
30
|
+
* say that.
|
|
31
|
+
*/
|
|
32
|
+
export function parsePublicHost(input, flag = "--host",
|
|
33
|
+
/** Where the path goes, for the error. Null when the caller has no path to offer. */
|
|
34
|
+
pathFlag = "--path") {
|
|
35
|
+
const host = input.trim().toLowerCase().replace(/\.$/, "");
|
|
36
|
+
if (host.length === 0 || /[/:?#@*\s]/.test(host)) {
|
|
37
|
+
throw new Error(`${flag} must be a bare hostname such as mcp.example.com, with no scheme, path, port or ` +
|
|
38
|
+
`wildcard (got '${input.trim()}').${pathFlag ? ` The path goes in ${pathFlag}.` : ""}`);
|
|
39
|
+
}
|
|
40
|
+
if (!/^[a-z0-9_]([a-z0-9_.-]*[a-z0-9_])?$/.test(host)) {
|
|
41
|
+
throw new Error(`${flag} '${input.trim()}' is not a plain ASCII hostname. Use the punycode (xn--) form for a ` +
|
|
42
|
+
"non-ASCII name.");
|
|
43
|
+
}
|
|
44
|
+
if (!host.includes(".")) {
|
|
45
|
+
throw new Error(`${flag} must be the full hostname, such as mcp.example.com, not just '${host}'. ` +
|
|
46
|
+
"(The short form belongs in your registrar's Name column, not here.)");
|
|
47
|
+
}
|
|
48
|
+
return host;
|
|
49
|
+
}
|
|
50
|
+
export function parsePublicPath(input, flag = "--path") {
|
|
51
|
+
const path = input.trim();
|
|
52
|
+
if (!path.startsWith("/")) {
|
|
53
|
+
throw new Error(`${flag} must start with '/', for example ${defaultPublicPath} (got '${path}').`);
|
|
54
|
+
}
|
|
55
|
+
return path;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A 200 from the rail that carries no `custom_domain` has nothing to show or wait on. It means the
|
|
59
|
+
* rail predates custom domains or something upstream dropped the object; either way, say so rather
|
|
60
|
+
* than print an empty table and report success.
|
|
61
|
+
*/
|
|
62
|
+
export function assertCustomDomainReturned(receipt, host, slug) {
|
|
63
|
+
if (!receipt.custom_domain) {
|
|
64
|
+
throw new Error(`The rail returned no custom_domain for ${host} on ${slug}, so there are no records to show. ` +
|
|
65
|
+
"Is the rail new enough to support custom domains?");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Re-verifying means re-checking a host that is already there. A resource with none has nothing to
|
|
70
|
+
* verify, and the bare error from asking the rail anyway would not say how to fix it, so say that
|
|
71
|
+
* plainly and name the argument that sets one. Returns the host the resource is registered on.
|
|
72
|
+
*/
|
|
73
|
+
export function assertCustomDomainSet(receipt, slug, hostFlag = "--host") {
|
|
74
|
+
const host = receipt.public_host?.trim();
|
|
75
|
+
if (!host) {
|
|
76
|
+
throw new Error(`no custom domain set on ${slug}; pass ${hostFlag}`);
|
|
77
|
+
}
|
|
78
|
+
return host;
|
|
79
|
+
}
|
|
80
|
+
export function customDomainState(receipt) {
|
|
81
|
+
return receipt.custom_domain?.state?.trim().toLowerCase() || undefined;
|
|
82
|
+
}
|
|
83
|
+
export function isCustomDomainFailure(state) {
|
|
84
|
+
return state !== undefined && failureStates.has(state);
|
|
85
|
+
}
|
|
86
|
+
/** One line on what a state means, for the person staring at it. */
|
|
87
|
+
export function describeCustomDomainState(state) {
|
|
88
|
+
switch (state) {
|
|
89
|
+
case "awaiting_ownership":
|
|
90
|
+
return "waiting for the ownership TXT record";
|
|
91
|
+
case "provisioning":
|
|
92
|
+
return "ownership verified; provisioning the hostname and certificate";
|
|
93
|
+
case "awaiting_traffic":
|
|
94
|
+
return "waiting for the traffic and certificate CNAMEs";
|
|
95
|
+
case "ready":
|
|
96
|
+
return "the hostname is serving traffic";
|
|
97
|
+
case "moved":
|
|
98
|
+
return "DNS no longer points at Authflow";
|
|
99
|
+
case "blocked":
|
|
100
|
+
return "the hostname is blocked";
|
|
101
|
+
case "removed":
|
|
102
|
+
return "the hostname was removed";
|
|
103
|
+
case undefined:
|
|
104
|
+
return "the rail reported no custom-domain state";
|
|
105
|
+
default:
|
|
106
|
+
return "a state this CLI does not know; see last_error";
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** `awaiting_traffic (waiting for the traffic and certificate CNAMEs)`, plus the rail's last error. */
|
|
110
|
+
export function summarizeCustomDomain(receipt) {
|
|
111
|
+
const state = customDomainState(receipt);
|
|
112
|
+
const error = receipt.custom_domain?.last_error?.trim();
|
|
113
|
+
return `${state ?? "unknown"} (${describeCustomDomainState(state)})${error ? ` - ${error}` : ""}`;
|
|
114
|
+
}
|
|
115
|
+
/** The records as an aligned TYPE / NAME / VALUE / PURPOSE table, in the order the rail listed them. */
|
|
116
|
+
export function formatDnsRecords(records) {
|
|
117
|
+
const rows = [
|
|
118
|
+
["TYPE", "NAME", "VALUE", "PURPOSE"],
|
|
119
|
+
...records.map((record) => [
|
|
120
|
+
String(record.type ?? ""),
|
|
121
|
+
String(record.name ?? ""),
|
|
122
|
+
String(record.value ?? ""),
|
|
123
|
+
String(record.purpose ?? ""),
|
|
124
|
+
]),
|
|
125
|
+
];
|
|
126
|
+
const widths = [0, 1, 2].map((column) => Math.max(...rows.map((row) => row[column].length)));
|
|
127
|
+
return rows.map((row) => `${row[0].padEnd(widths[0])} ${row[1].padEnd(widths[1])} ${row[2].padEnd(widths[2])} ${row[3]}`.trimEnd());
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Registrars disagree about the Name column. GoDaddy and several others append the domain
|
|
131
|
+
* themselves, so a full name there produces `mcp.example.com.example.com` and a record that
|
|
132
|
+
* silently never matches.
|
|
133
|
+
*/
|
|
134
|
+
export function registrarNameHint(host) {
|
|
135
|
+
const labels = host.split(".");
|
|
136
|
+
const intro = "Registrars such as GoDaddy usually want only the host part of the name, not the full name";
|
|
137
|
+
if (labels.length < 3) {
|
|
138
|
+
return [`${intro}: leave your domain off.`];
|
|
139
|
+
}
|
|
140
|
+
const relative = labels.slice(0, -2).join(".");
|
|
141
|
+
return [
|
|
142
|
+
`${intro}:`,
|
|
143
|
+
`for ${host} enter "${relative}", and for _authflow-verify.${host} enter "_authflow-verify.${relative}".`,
|
|
144
|
+
];
|
|
145
|
+
}
|
|
146
|
+
/** The "add these records" block shared by every front end. Empty when the rail listed none. */
|
|
147
|
+
export function dnsInstructions(receipt) {
|
|
148
|
+
const records = receipt.custom_domain?.records ?? [];
|
|
149
|
+
if (records.length === 0) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const lines = [
|
|
153
|
+
"Add these DNS records at your registrar:",
|
|
154
|
+
"",
|
|
155
|
+
...formatDnsRecords(records).map((line) => ` ${line}`),
|
|
156
|
+
"",
|
|
157
|
+
];
|
|
158
|
+
const host = receipt.public_host?.trim();
|
|
159
|
+
if (host) {
|
|
160
|
+
lines.push(...registrarNameHint(host));
|
|
161
|
+
}
|
|
162
|
+
// The certificate record only exists once provisioning has begun, which is after ownership is
|
|
163
|
+
// verified. Without saying so, a tenant who sees two records adds two and wonders about the third.
|
|
164
|
+
const state = customDomainState(receipt);
|
|
165
|
+
if (!records.some((record) => record.purpose === "certificate") &&
|
|
166
|
+
state !== "ready" &&
|
|
167
|
+
!isCustomDomainFailure(state)) {
|
|
168
|
+
lines.push("", "The certificate record (_acme-challenge) is listed once provisioning begins, after ownership", "is verified. Check again once the ownership TXT record is in place.");
|
|
169
|
+
}
|
|
170
|
+
return lines;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* The command that re-checks the host already set on a resource. It names no host and no path: the
|
|
174
|
+
* rail keeps both, so there is nothing to retype and nothing to get wrong on a re-run.
|
|
175
|
+
*/
|
|
176
|
+
export function setDomainCommandLine(slug) {
|
|
177
|
+
return `authflow set-domain --slug ${slug} --wait`;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Polls verification until the domain is ready, fails, or the budget runs out. Verifies straight
|
|
181
|
+
* away and then every interval: a tenant who re-runs after adding their records should not wait a
|
|
182
|
+
* full interval to hear it worked.
|
|
183
|
+
*
|
|
184
|
+
* `ready` and the failure states settle it. Anything else, including a state this CLI has never
|
|
185
|
+
* heard of, keeps waiting, so a rail that adds a state does not break an old CLI.
|
|
186
|
+
*/
|
|
187
|
+
export async function waitForCustomDomain(initial, options) {
|
|
188
|
+
const intervalMs = options.intervalMs ?? waitIntervalMs;
|
|
189
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
190
|
+
const now = options.now ?? Date.now;
|
|
191
|
+
const started = now();
|
|
192
|
+
const deadline = started + options.timeoutMs;
|
|
193
|
+
const progress = [];
|
|
194
|
+
const emit = (line) => {
|
|
195
|
+
progress.push(line);
|
|
196
|
+
options.onProgress?.(line);
|
|
197
|
+
};
|
|
198
|
+
const stamp = () => `[${formatClock(now() - started)}]`;
|
|
199
|
+
const finish = (outcome, receipt, polls) => ({
|
|
200
|
+
outcome,
|
|
201
|
+
receipt,
|
|
202
|
+
polls,
|
|
203
|
+
elapsedMs: now() - started,
|
|
204
|
+
progress,
|
|
205
|
+
});
|
|
206
|
+
let receipt = initial;
|
|
207
|
+
const already = verdict(receipt);
|
|
208
|
+
if (already) {
|
|
209
|
+
return finish(already, receipt, 0);
|
|
210
|
+
}
|
|
211
|
+
emit(`Checking every ${formatDuration(intervalMs)}, giving up after ${formatDuration(options.timeoutMs)}.`);
|
|
212
|
+
const seen = new Set((receipt.custom_domain?.records ?? []).map(recordKey));
|
|
213
|
+
let lastSummary = summarizeCustomDomain(receipt);
|
|
214
|
+
let polls = 0;
|
|
215
|
+
let failures = 0;
|
|
216
|
+
for (;;) {
|
|
217
|
+
let fresh;
|
|
218
|
+
try {
|
|
219
|
+
fresh = await options.verify();
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
// A 30-minute wait should not die on one dropped connection, but a persistent error (a bad
|
|
223
|
+
// key, a rail that is down) must not be retried until the deadline either.
|
|
224
|
+
failures += 1;
|
|
225
|
+
if (failures >= maxConsecutiveFailures) {
|
|
226
|
+
throw err;
|
|
227
|
+
}
|
|
228
|
+
emit(`${stamp()} verification failed (${errorMessage(err)}); will retry`);
|
|
229
|
+
}
|
|
230
|
+
if (fresh) {
|
|
231
|
+
failures = 0;
|
|
232
|
+
polls += 1;
|
|
233
|
+
receipt = fresh;
|
|
234
|
+
const summary = summarizeCustomDomain(receipt);
|
|
235
|
+
if (summary !== lastSummary || polls === 1 || polls % heartbeatEveryPolls === 0) {
|
|
236
|
+
emit(`${stamp()} ${summary}`);
|
|
237
|
+
lastSummary = summary;
|
|
238
|
+
}
|
|
239
|
+
// The certificate record appears mid-wait, after ownership verifies. Saying so here is the
|
|
240
|
+
// difference between "waiting on DNS" and "waiting on a record nobody knows to add".
|
|
241
|
+
const appeared = (receipt.custom_domain?.records ?? []).filter((record) => !seen.has(recordKey(record)));
|
|
242
|
+
if (appeared.length > 0) {
|
|
243
|
+
for (const record of appeared) {
|
|
244
|
+
seen.add(recordKey(record));
|
|
245
|
+
}
|
|
246
|
+
emit(`${stamp()} new DNS record${appeared.length === 1 ? "" : "s"} to add:`);
|
|
247
|
+
for (const line of formatDnsRecords(appeared)) {
|
|
248
|
+
emit(` ${line}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
const outcome = verdict(receipt);
|
|
252
|
+
if (outcome) {
|
|
253
|
+
return finish(outcome, receipt, polls);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const remaining = deadline - now();
|
|
257
|
+
if (remaining <= 0) {
|
|
258
|
+
return finish("timeout", receipt, polls);
|
|
259
|
+
}
|
|
260
|
+
await sleep(Math.min(intervalMs, remaining));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
function verdict(receipt) {
|
|
264
|
+
const state = customDomainState(receipt);
|
|
265
|
+
if (state === "ready") {
|
|
266
|
+
return "ready";
|
|
267
|
+
}
|
|
268
|
+
return isCustomDomainFailure(state) ? "failed" : undefined;
|
|
269
|
+
}
|
|
270
|
+
function recordKey(record) {
|
|
271
|
+
return `${record.type}|${record.name}|${record.value}`;
|
|
272
|
+
}
|
|
273
|
+
function errorMessage(err) {
|
|
274
|
+
return err instanceof Error ? err.message : String(err);
|
|
275
|
+
}
|
|
276
|
+
function defaultSleep(ms) {
|
|
277
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
278
|
+
}
|
|
279
|
+
/** `1:05` for 65 seconds. */
|
|
280
|
+
function formatClock(ms) {
|
|
281
|
+
const total = Math.max(0, Math.floor(ms / 1000));
|
|
282
|
+
return `${Math.floor(total / 60)}:${String(total % 60).padStart(2, "0")}`;
|
|
283
|
+
}
|
|
284
|
+
/** `15s`, `50s`, `30m`. */
|
|
285
|
+
export function formatDuration(ms) {
|
|
286
|
+
const seconds = Math.round(ms / 1000);
|
|
287
|
+
return seconds >= 120 && seconds % 60 === 0 ? `${seconds / 60}m` : `${seconds}s`;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=custom-domain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-domain.js","sourceRoot":"","sources":["../src/custom-domain.ts"],"names":[],"mappings":"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"}
|