authflow-cli 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +62 -341
  2. package/dist/checks.d.ts +30 -0
  3. package/dist/checks.js +36 -0
  4. package/dist/checks.js.map +1 -0
  5. package/dist/client-conformance.d.ts +5 -1
  6. package/dist/client-conformance.js +9 -1
  7. package/dist/client-conformance.js.map +1 -1
  8. package/dist/commands/account.d.ts +2 -0
  9. package/dist/commands/account.js +105 -0
  10. package/dist/commands/account.js.map +1 -0
  11. package/dist/commands/doctor.js +5 -1
  12. package/dist/commands/doctor.js.map +1 -1
  13. package/dist/commands/mcp.js +11 -6
  14. package/dist/commands/mcp.js.map +1 -1
  15. package/dist/commands/plan.js +7 -7
  16. package/dist/commands/plan.js.map +1 -1
  17. package/dist/commands/register.js +30 -2
  18. package/dist/commands/register.js.map +1 -1
  19. package/dist/commands/set-domain.d.ts +34 -0
  20. package/dist/commands/set-domain.js +162 -0
  21. package/dist/commands/set-domain.js.map +1 -0
  22. package/dist/commands/signup.d.ts +1 -0
  23. package/dist/commands/signup.js +4 -50
  24. package/dist/commands/signup.js.map +1 -1
  25. package/dist/config.d.ts +36 -1
  26. package/dist/config.js +1 -1
  27. package/dist/config.js.map +1 -1
  28. package/dist/credentials.d.ts +22 -0
  29. package/dist/credentials.js +206 -0
  30. package/dist/credentials.js.map +1 -0
  31. package/dist/custom-domain.d.ts +96 -0
  32. package/dist/custom-domain.js +289 -0
  33. package/dist/custom-domain.js.map +1 -0
  34. package/dist/doctor.d.ts +31 -23
  35. package/dist/doctor.js +271 -57
  36. package/dist/doctor.js.map +1 -1
  37. package/dist/google-redirect.d.ts +5 -1
  38. package/dist/google-redirect.js +11 -5
  39. package/dist/google-redirect.js.map +1 -1
  40. package/dist/integration-plans.json +80 -0
  41. package/dist/integration.d.ts +17 -23
  42. package/dist/integration.js +29 -236
  43. package/dist/integration.js.map +1 -1
  44. package/dist/login.d.ts +11 -0
  45. package/dist/login.js +181 -0
  46. package/dist/login.js.map +1 -0
  47. package/dist/management.d.ts +17 -0
  48. package/dist/management.js +67 -0
  49. package/dist/management.js.map +1 -0
  50. package/dist/mcp/management-server.d.ts +4 -0
  51. package/dist/mcp/management-server.js +103 -0
  52. package/dist/mcp/management-server.js.map +1 -0
  53. package/dist/mcp/server.d.ts +8 -1
  54. package/dist/mcp/server.js +137 -7
  55. package/dist/mcp/server.js.map +1 -1
  56. package/dist/program.js +6 -1
  57. package/dist/program.js.map +1 -1
  58. package/dist/rail.d.ts +16 -0
  59. package/dist/rail.js +43 -0
  60. package/dist/rail.js.map +1 -1
  61. package/dist/resource-urls.d.ts +27 -0
  62. package/dist/resource-urls.js +56 -0
  63. package/dist/resource-urls.js.map +1 -0
  64. package/dist/secrets.d.ts +29 -14
  65. package/dist/secrets.js +201 -90
  66. package/dist/secrets.js.map +1 -1
  67. package/dist/status.d.ts +15 -1
  68. package/dist/status.js +68 -6
  69. package/dist/status.js.map +1 -1
  70. package/dist/surface.d.ts +1 -0
  71. package/dist/surface.js +1 -0
  72. package/dist/surface.js.map +1 -1
  73. package/dist/tenant-host.d.ts +49 -0
  74. package/dist/tenant-host.js +367 -0
  75. package/dist/tenant-host.js.map +1 -0
  76. package/dist/version.d.ts +10 -0
  77. package/dist/version.js +27 -0
  78. package/dist/version.js.map +1 -0
  79. package/package.json +6 -5
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;
@@ -28,9 +54,18 @@ export type RegistrationReceipt = {
28
54
  origin_api_key?: string;
29
55
  gateway_egress_cidrs?: string[];
30
56
  status?: string;
57
+ access_mode?: "free" | "paid";
31
58
  metering_mode?: string;
32
- /** What still blocks go-live: origin_base_uri, connected_account_id, stripe_onboarding_complete. */
59
+ metering_default_units?: number;
60
+ tool_units?: Record<string, number>;
61
+ /**
62
+ * What still blocks go-live: origin_base_uri, connected_account_id, stripe_onboarding_complete,
63
+ * and custom_domain_dns while a custom host is set but not yet ready.
64
+ */
33
65
  pending_requirements?: string[];
66
+ allowed_actions?: string[];
67
+ last_probe_at_utc?: string | null;
68
+ last_probe_passed?: boolean | null;
34
69
  };
35
70
  export declare function readReceipt(path: string): Promise<RegistrationReceipt>;
36
71
  export declare function resolveGatewayUrl(opts: {
package/dist/config.js CHANGED
@@ -9,7 +9,7 @@ export function requireIssuer(flag) {
9
9
  export function requireAdminKey(flag) {
10
10
  const key = (flag ?? process.env.AUTHFLOW_ADMIN_API_KEY ?? "").trim();
11
11
  if (!key) {
12
- throw new Error("Set AUTHFLOW_ADMIN_API_KEY or pass --admin-key.");
12
+ throw new Error("This legacy operator command requires AUTHFLOW_ADMIN_API_KEY or --admin-key. For creator onboarding, run authflow login and use authflow resource commands. Claim an existing workspace in the portal with its current tenant key.");
13
13
  }
14
14
  return key;
15
15
  }
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ export type LoginCredential = {
2
+ issuer: string;
3
+ client_id: string;
4
+ access_token: string;
5
+ refresh_token?: string;
6
+ expires_at: number;
7
+ workspace_id: string;
8
+ };
9
+ /** Credentials never enter the checkout, environment files, or tool results. */
10
+ export declare function credentialEntry(issuer: string, suffix?: string): Promise<import("@napi-rs/keyring").Entry>;
11
+ export declare function readCredential(issuer: string): Promise<LoginCredential | undefined>;
12
+ export declare function saveCredential(credential: LoginCredential): Promise<void>;
13
+ export declare function deleteCredential(issuer: string): Promise<void>;
14
+ /** Serialize refresh-token rotation across CLI/MCP processes. The lock contains no secrets. */
15
+ export declare function withCredentialLock<T>(issuer: string, action: () => Promise<T>): Promise<T>;
16
+ /** One storage boundary also lets protocol tests use an isolated in-memory credential store. */
17
+ export declare const credentialStore: {
18
+ read: typeof readCredential;
19
+ save: typeof saveCredential;
20
+ delete: typeof deleteCredential;
21
+ withLock: typeof withCredentialLock;
22
+ };
@@ -0,0 +1,206 @@
1
+ import { createHash, randomBytes } from "node:crypto";
2
+ import { mkdir, readFile, rmdir, stat, unlink, utimes, writeFile } from "node:fs/promises";
3
+ import { homedir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { setTimeout as delay } from "node:timers/promises";
6
+ /** Credentials never enter the checkout, environment files, or tool results. */
7
+ export async function credentialEntry(issuer, suffix = "") {
8
+ try {
9
+ const { Entry } = await import("@napi-rs/keyring");
10
+ return new Entry("authflow-cli", issuer + suffix, { linux: { store: "secret-service" } });
11
+ }
12
+ catch {
13
+ throw new Error("The operating system credential store is unavailable. Enable your desktop keyring and retry; Authflow does not fall back to plaintext credentials.");
14
+ }
15
+ }
16
+ export async function readCredential(issuer) {
17
+ const entry = await credentialEntry(issuer);
18
+ let value;
19
+ try {
20
+ value = entry.getPassword();
21
+ }
22
+ catch {
23
+ throw new Error("Unable to read the OS credential store. Unlock it and retry authflow login.");
24
+ }
25
+ if (!value)
26
+ return undefined;
27
+ const manifest = parseManifest(value);
28
+ const chunks = [];
29
+ for (let i = 0; i < manifest.count; i++) {
30
+ const chunk = (await credentialEntry(issuer, `:${manifest.generation}:${i}`)).getPassword();
31
+ if (chunk === null)
32
+ throw new Error("Stored Authflow login is incomplete; run authflow login again.");
33
+ chunks.push(chunk);
34
+ }
35
+ let parsed;
36
+ try {
37
+ parsed = JSON.parse(chunks.join(""));
38
+ }
39
+ catch {
40
+ throw new Error("Stored Authflow login is invalid; run authflow login again.");
41
+ }
42
+ if (!parsed || typeof parsed !== "object")
43
+ throw new Error("Stored Authflow login is invalid; run authflow login again.");
44
+ const credential = parsed;
45
+ if (credential.issuer !== issuer || typeof credential.client_id !== "string" || !credential.client_id
46
+ || typeof credential.access_token !== "string" || !credential.access_token || typeof credential.workspace_id !== "string" || !credential.workspace_id
47
+ || credential.refresh_token !== undefined && typeof credential.refresh_token !== "string" || !Number.isFinite(credential.expires_at)) {
48
+ throw new Error("Stored Authflow login is invalid; run authflow login again.");
49
+ }
50
+ return credential;
51
+ }
52
+ export async function saveCredential(credential) {
53
+ // Windows limits each credential blob. Publish one small manifest only after every
54
+ // protected chunk is written; interrupted writes leave the previous login readable.
55
+ const entry = await credentialEntry(credential.issuer);
56
+ const previous = entry.getPassword();
57
+ const serialized = JSON.stringify(credential);
58
+ const generation = randomBytes(16).toString("hex");
59
+ const count = Math.ceil(serialized.length / 1000);
60
+ if (count > 64)
61
+ throw new Error("The issuer returned an unexpectedly large login credential.");
62
+ let written = 0;
63
+ try {
64
+ for (; written < count; written++) {
65
+ (await credentialEntry(credential.issuer, `:${generation}:${written}`)).setPassword(serialized.slice(written * 1000, (written + 1) * 1000));
66
+ }
67
+ entry.setPassword(JSON.stringify({ generation, count }));
68
+ }
69
+ catch {
70
+ await removeChunks(credential.issuer, { generation, count: written });
71
+ throw new Error("Unable to save the login in your OS credential store. Unlock it and retry authflow login.");
72
+ }
73
+ if (previous) {
74
+ try {
75
+ await removeChunks(credential.issuer, parseManifest(previous));
76
+ }
77
+ catch { /* Replacing an invalid manifest still successfully repairs the login. */ }
78
+ }
79
+ }
80
+ export async function deleteCredential(issuer) {
81
+ const entry = await credentialEntry(issuer);
82
+ const previous = entry.getPassword();
83
+ entry.deletePassword();
84
+ if (previous) {
85
+ try {
86
+ await removeChunks(issuer, parseManifest(previous));
87
+ }
88
+ catch { /* An invalid old manifest cannot make removal of the login fail. */ }
89
+ }
90
+ }
91
+ function parseManifest(value) {
92
+ let parsed;
93
+ try {
94
+ parsed = JSON.parse(value);
95
+ }
96
+ catch {
97
+ throw new Error("Stored Authflow login is invalid; run authflow login again.");
98
+ }
99
+ if (!parsed || typeof parsed !== "object" || !/^[a-f0-9]{32}$/.test(parsed.generation) || !Number.isInteger(parsed.count) || parsed.count < 1 || parsed.count > 64) {
100
+ throw new Error("Stored Authflow login is invalid; run authflow login again.");
101
+ }
102
+ return parsed;
103
+ }
104
+ async function removeChunks(issuer, manifest) {
105
+ for (let i = 0; i < manifest.count; i++) {
106
+ try {
107
+ (await credentialEntry(issuer, `:${manifest.generation}:${i}`)).deletePassword();
108
+ }
109
+ catch { /* A revoked orphan remains OS-protected. */ }
110
+ }
111
+ }
112
+ /** Serialize refresh-token rotation across CLI/MCP processes. The lock contains no secrets. */
113
+ export async function withCredentialLock(issuer, action) {
114
+ const directory = join(homedir(), ".authflow", "locks");
115
+ await mkdir(directory, { recursive: true, mode: 0o700 });
116
+ const lock = join(directory, createHash("sha256").update(issuer).digest("hex"));
117
+ const ownerPath = join(lock, "owner.json");
118
+ const owner = { pid: process.pid, id: randomBytes(16).toString("hex") };
119
+ const deadline = Date.now() + 45_000;
120
+ while (true) {
121
+ try {
122
+ await mkdir(lock, { mode: 0o700 });
123
+ break;
124
+ }
125
+ catch (error) {
126
+ if (error.code !== "EEXIST")
127
+ throw error;
128
+ const info = await stat(lock).catch(() => undefined);
129
+ // A suspended process can miss its heartbeat while still holding a rotating refresh
130
+ // token. Never steal its lock merely because a timer elapsed.
131
+ if (info && Date.now() - info.mtimeMs > 60_000) {
132
+ await recoverAbandonedLock(lock, ownerPath);
133
+ }
134
+ if (Date.now() >= deadline)
135
+ throw new Error("Another Authflow login is busy. Retry shortly.");
136
+ await delay(200);
137
+ }
138
+ }
139
+ try {
140
+ await writeFile(ownerPath, JSON.stringify(owner), { mode: 0o600 });
141
+ }
142
+ catch (error) {
143
+ await rmdir(lock).catch(() => undefined);
144
+ throw error;
145
+ }
146
+ const heartbeat = setInterval(() => { const now = new Date(); void utimes(lock, now, now).catch(() => undefined); }, 10_000);
147
+ heartbeat.unref();
148
+ try {
149
+ return await action();
150
+ }
151
+ finally {
152
+ clearInterval(heartbeat);
153
+ if ((await lockOwner(ownerPath))?.id === owner.id) {
154
+ await unlink(ownerPath);
155
+ await rmdir(lock);
156
+ }
157
+ }
158
+ }
159
+ async function lockOwner(path) {
160
+ try {
161
+ const owner = JSON.parse(await readFile(path, "utf8"));
162
+ if (owner && typeof owner === "object" && "pid" in owner && "id" in owner
163
+ && Number.isInteger(owner.pid) && Number(owner.pid) > 0 && typeof owner.id === "string")
164
+ return owner;
165
+ }
166
+ catch { /* An interrupted acquisition can leave an empty legacy lock. */ }
167
+ return undefined;
168
+ }
169
+ function processIsAlive(pid) {
170
+ try {
171
+ process.kill(pid, 0);
172
+ return true;
173
+ }
174
+ catch (error) {
175
+ return error.code !== "ESRCH";
176
+ }
177
+ }
178
+ async function recoverAbandonedLock(lock, ownerPath) {
179
+ // Serialize stale cleanup. Recheck the owner after acquiring this marker: another
180
+ // waiter may already have recovered the directory and acquired a fresh login lock.
181
+ const recovery = join(lock, "recovering");
182
+ try {
183
+ await mkdir(recovery, { mode: 0o700 });
184
+ }
185
+ catch {
186
+ return;
187
+ }
188
+ let abandoned = false;
189
+ try {
190
+ const previous = await lockOwner(ownerPath);
191
+ if (previous && processIsAlive(previous.pid))
192
+ return;
193
+ abandoned = true;
194
+ await unlink(ownerPath).catch(() => undefined);
195
+ }
196
+ finally {
197
+ await rmdir(recovery).catch(() => undefined);
198
+ }
199
+ // A new owner writes owner.json before entering its action, so a delayed cleanup
200
+ // cannot delete an acquired lock: rmdir refuses any nonempty directory.
201
+ if (abandoned)
202
+ await rmdir(lock).catch(() => undefined);
203
+ }
204
+ /** One storage boundary also lets protocol tests use an isolated in-memory credential store. */
205
+ export const credentialStore = { read: readCredential, save: saveCredential, delete: deleteCredential, withLock: withCredentialLock };
206
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/credentials.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;