@atlasauth/cli 0.7.0 → 0.9.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 (75) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/args.d.ts +155 -0
  3. package/dist/args.js +66 -1
  4. package/dist/args.js.map +1 -1
  5. package/dist/commands/auth.js +5 -1
  6. package/dist/commands/auth.js.map +1 -1
  7. package/dist/commands/config.d.ts +56 -1
  8. package/dist/commands/config.js +188 -29
  9. package/dist/commands/config.js.map +1 -1
  10. package/dist/commands/csp.d.ts +32 -0
  11. package/dist/commands/csp.js +180 -0
  12. package/dist/commands/csp.js.map +1 -0
  13. package/dist/commands/import-better-auth.d.ts +3 -0
  14. package/dist/commands/import-better-auth.js +164 -0
  15. package/dist/commands/import-better-auth.js.map +1 -0
  16. package/dist/commands/import.js +7 -0
  17. package/dist/commands/import.js.map +1 -1
  18. package/dist/commands/info.js +1 -1
  19. package/dist/commands/info.js.map +1 -1
  20. package/dist/commands/init.js +26 -2
  21. package/dist/commands/init.js.map +1 -1
  22. package/dist/commands/integrations.d.ts +8 -0
  23. package/dist/commands/integrations.js +112 -0
  24. package/dist/commands/integrations.js.map +1 -0
  25. package/dist/commands/listen.d.ts +17 -0
  26. package/dist/commands/listen.js +223 -0
  27. package/dist/commands/listen.js.map +1 -0
  28. package/dist/commands/migrate.js +40 -1
  29. package/dist/commands/migrate.js.map +1 -1
  30. package/dist/commands/move-origin.d.ts +11 -0
  31. package/dist/commands/move-origin.js +95 -0
  32. package/dist/commands/move-origin.js.map +1 -0
  33. package/dist/commands/operator.d.ts +26 -0
  34. package/dist/commands/operator.js +198 -0
  35. package/dist/commands/operator.js.map +1 -0
  36. package/dist/commands/project-commands.d.ts +12 -0
  37. package/dist/commands/project-commands.js +130 -30
  38. package/dist/commands/project-commands.js.map +1 -1
  39. package/dist/commands/referrals.d.ts +4 -4
  40. package/dist/commands/referrals.js +203 -33
  41. package/dist/commands/referrals.js.map +1 -1
  42. package/dist/commands/shared.d.ts +6 -0
  43. package/dist/commands/shared.js +10 -1
  44. package/dist/commands/shared.js.map +1 -1
  45. package/dist/commands/status.d.ts +3 -0
  46. package/dist/commands/status.js +77 -0
  47. package/dist/commands/status.js.map +1 -0
  48. package/dist/commands/webhooks.d.ts +29 -2
  49. package/dist/commands/webhooks.js +257 -24
  50. package/dist/commands/webhooks.js.map +1 -1
  51. package/dist/config.d.ts +14 -0
  52. package/dist/config.js.map +1 -1
  53. package/dist/context.d.ts +13 -0
  54. package/dist/context.js.map +1 -1
  55. package/dist/help.d.ts +1 -1
  56. package/dist/help.js +221 -17
  57. package/dist/help.js.map +1 -1
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.js +10 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/migrate/better-auth/integration.d.ts +59 -0
  62. package/dist/migrate/better-auth/integration.js +81 -0
  63. package/dist/migrate/better-auth/integration.js.map +1 -0
  64. package/dist/migrate/better-auth/orchestrator.d.ts +43 -0
  65. package/dist/migrate/better-auth/orchestrator.js +525 -0
  66. package/dist/migrate/better-auth/orchestrator.js.map +1 -0
  67. package/dist/migrate/better-auth/output.d.ts +20 -0
  68. package/dist/migrate/better-auth/output.js +146 -0
  69. package/dist/migrate/better-auth/output.js.map +1 -0
  70. package/dist/migrate/better-auth/translate.d.ts +77 -0
  71. package/dist/migrate/better-auth/translate.js +1989 -0
  72. package/dist/migrate/better-auth/translate.js.map +1 -0
  73. package/dist/run.js +14 -0
  74. package/dist/run.js.map +1 -1
  75. package/package.json +5 -4
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.moveOrigin = moveOrigin;
4
+ const args_1 = require("../args");
5
+ const errors_1 = require("../errors");
6
+ const output_1 = require("../output");
7
+ const project_1 = require("../project");
8
+ const shared_1 = require("./shared");
9
+ const COLUMNS = [
10
+ { header: 'OBJECT', get: (c) => `${c.object} ${c.id}` },
11
+ { header: 'FIELD', get: (c) => c.field },
12
+ { header: 'BEFORE', get: (c) => c.before },
13
+ { header: 'AFTER', get: (c) => c.after },
14
+ ];
15
+ /**
16
+ * `atlas move-origin --from <origin> --to <origin> [--dry-run] [--yes]`: move an
17
+ * app to a new domain in one step. The server rewrites every origin-bearing
18
+ * field (allowed origins, OAuth client redirect/logout URIs, webhook URLs,
19
+ * redirect allowlist, branding links, Sign in with Atlas apps). The CLI always
20
+ * shows the diff first, asks before writing (skip with --yes), and after a real
21
+ * move sends a signed test delivery to each webhook endpoint that moved.
22
+ */
23
+ async function moveOrigin(ctx, args) {
24
+ const v = args.values;
25
+ const from = (0, args_1.str)(v, 'from');
26
+ const to = (0, args_1.str)(v, 'to');
27
+ if (!from || !to)
28
+ throw new errors_1.CliError('move-origin needs --from <origin> and --to <origin>, e.g. --from https://old.example --to https://new.example.');
29
+ const json = (0, shared_1.wantsJson)(v);
30
+ const auth = (0, shared_1.resolveAuth)(ctx, v);
31
+ const http = (0, project_1.httpFor)(ctx, { secretKey: auth.secretKey, apiUrl: auth.apiUrl });
32
+ const path = '/v1/instance/move_origin';
33
+ const plan = await http.post(path, { from, to, dry_run: true });
34
+ if (!json) {
35
+ if (plan.changes.length === 0)
36
+ ctx.io.out(`Nothing on ${plan.from} to move: no field of this instance uses that origin.`);
37
+ else {
38
+ ctx.io.out(`${plan.from} → ${plan.to}: ${plan.changes.length} field(s) would change.`);
39
+ ctx.io.out((0, output_1.renderTable)(COLUMNS, plan.changes));
40
+ }
41
+ for (const p of plan.problems)
42
+ ctx.io.out(`problem: ${p.object} ${p.id} ${p.field}: ${p.message} (${p.url})`);
43
+ }
44
+ if (v['dry-run'] === true) {
45
+ if (json)
46
+ (0, output_1.printJson)(ctx.io, plan);
47
+ else
48
+ ctx.io.out('Dry run: nothing was written.');
49
+ return plan.problems.length > 0 ? 1 : 0;
50
+ }
51
+ if (plan.problems.length > 0) {
52
+ if (json)
53
+ (0, output_1.printJson)(ctx.io, plan);
54
+ throw new errors_1.CliError(`${plan.problems.length} rewritten value(s) would be refused (see above); fix them and run again. Nothing was written.`);
55
+ }
56
+ if (plan.changes.length === 0) {
57
+ if (json)
58
+ (0, output_1.printJson)(ctx.io, plan);
59
+ return 0;
60
+ }
61
+ if (v.yes !== true) {
62
+ if (json)
63
+ throw new errors_1.CliError('Pass --yes to apply a move with --json (the confirmation prompt is interactive).');
64
+ const answer = (await ctx.prompt.text('Apply these changes? [y/N]', { default: 'n' })).trim().toLowerCase();
65
+ if (answer !== 'y' && answer !== 'yes') {
66
+ ctx.io.out('Cancelled: nothing was written.');
67
+ return 1;
68
+ }
69
+ }
70
+ const done = await http.post(path, { from, to });
71
+ const hooks = [...new Set(done.changes.filter((c) => c.object === 'webhook_endpoint').map((c) => c.id))];
72
+ const tests = [];
73
+ for (const id of hooks) {
74
+ try {
75
+ const res = await http.post(`/v1/webhook_endpoints/${encodeURIComponent(id)}/test?wait=10s`, {});
76
+ const status = res.http_status ?? null;
77
+ tests.push({ endpoint: id, ok: status !== null && status >= 200 && status < 300, http_status: status, error: res.error ?? null });
78
+ }
79
+ catch (err) {
80
+ tests.push({ endpoint: id, ok: false, http_status: null, error: err instanceof Error ? err.message : String(err) });
81
+ }
82
+ }
83
+ if (json) {
84
+ (0, output_1.printJson)(ctx.io, { ...done, webhook_tests: tests });
85
+ }
86
+ else {
87
+ ctx.io.out(`Moved ${done.changes.length} field(s) from ${done.from} to ${done.to}.`);
88
+ for (const t of tests) {
89
+ ctx.io.out(`webhook ${t.endpoint}: ${t.ok ? `test delivery answered ${t.http_status}` : `test delivery did not get a 2xx${t.http_status ? ` (${t.http_status})` : ''}${t.error ? `: ${t.error}` : ''}`}`);
90
+ }
91
+ ctx.io.out("Next: update your app's own config (env vars, DNS, deploy) to the new origin, then run 'atlas preflight'.");
92
+ }
93
+ return tests.some((t) => !t.ok) ? 1 : 0;
94
+ }
95
+ //# sourceMappingURL=move-origin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"move-origin.js","sourceRoot":"","sources":["../../src/commands/move-origin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { type ParsedArgs } from '../args';
2
+ import type { RunContext } from '../context';
3
+ /**
4
+ * `atlas login --device` — sign in as an OPERATOR through Atlas's own sign-in
5
+ * (passkey, MFA) with the OAuth device flow, instead of pasting a key. Only
6
+ * short-lived tokens are stored: the operator token (to swap later with
7
+ * `atlas token`) and, with `--resource`, a one-hour token bound to that
8
+ * instance or app, used as the profile credential. No long-lived key is written.
9
+ */
10
+ export declare function deviceLogin(ctx: RunContext, args: ParsedArgs): Promise<number>;
11
+ /**
12
+ * `atlas token --resource <ins_…|app_…|acct_…> --scope "<scopes>"` — swap the
13
+ * stored operator token for a one-hour Atlas token bound to that resource.
14
+ * Prints the token (or JSON with --json); `--profile` stores it as that
15
+ * profile's credential instead.
16
+ */
17
+ export declare function token(ctx: RunContext, args: ParsedArgs): Promise<number>;
18
+ /**
19
+ * `atlas keys whoami|list|create|rotate|revoke|revoke-leaked`.
20
+ *
21
+ * create / rotate ask for the new secret SEALED to a key pair generated here
22
+ * (X-Secret-Recipient), open it locally and write it to a 0600 file
23
+ * (`--out`, default `.atlas/secret-key-<id>.env`), so the plaintext never
24
+ * crosses a log or terminal. `--print-secret` prints it instead.
25
+ */
26
+ export declare function keys(ctx: RunContext, args: ParsedArgs): Promise<number>;
@@ -0,0 +1,198 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deviceLogin = deviceLogin;
4
+ exports.token = token;
5
+ exports.keys = keys;
6
+ const node_fs_1 = require("node:fs");
7
+ const node_path_1 = require("node:path");
8
+ const backend_1 = require("@atlasauth/backend");
9
+ const args_1 = require("../args");
10
+ const errors_1 = require("../errors");
11
+ const output_1 = require("../output");
12
+ const shared_1 = require("./shared");
13
+ /**
14
+ * `atlas login --device` — sign in as an OPERATOR through Atlas's own sign-in
15
+ * (passkey, MFA) with the OAuth device flow, instead of pasting a key. Only
16
+ * short-lived tokens are stored: the operator token (to swap later with
17
+ * `atlas token`) and, with `--resource`, a one-hour token bound to that
18
+ * instance or app, used as the profile credential. No long-lived key is written.
19
+ */
20
+ async function deviceLogin(ctx, args) {
21
+ const { values } = args;
22
+ const profile = (0, args_1.str)(values, 'profile') || 'default';
23
+ const apiUrl = (0, args_1.str)(values, 'url') || (0, shared_1.envApiUrl)(ctx.env) || backend_1.DEFAULT_API_URL;
24
+ const opts = { apiUrl, fetch: ctx.fetch };
25
+ let start;
26
+ try {
27
+ start = await (0, backend_1.startOperatorLogin)(opts);
28
+ }
29
+ catch (err) {
30
+ throw new errors_1.CliError(err instanceof Error ? err.message : String(err));
31
+ }
32
+ ctx.io.out(`Open ${start.verification_uri_complete}`);
33
+ ctx.io.out(`and confirm the code ${start.user_code} (it expires in ${Math.round(start.expires_in / 60)} minutes).`);
34
+ let token;
35
+ try {
36
+ token = await (0, backend_1.pollOperatorLogin)(start, { fetch: ctx.fetch, sleep: ctx.sleep });
37
+ }
38
+ catch (err) {
39
+ if (err instanceof backend_1.OAuthFlowError)
40
+ throw new errors_1.CliError(`Sign-in did not complete: ${err.message}`);
41
+ throw err;
42
+ }
43
+ const now = Date.now();
44
+ const file = ctx.config.load();
45
+ const entry = file.profiles[profile] ?? { api_url: apiUrl, secret_key: '' };
46
+ entry.api_url = apiUrl;
47
+ entry.operator = { access_token: token.access_token, expires_at: now + token.expires_in * 1000, issuer: start.config.issuer };
48
+ const resource = (0, args_1.str)(values, 'resource');
49
+ if (resource) {
50
+ const scope = (0, args_1.str)(values, 'scope') || 'read';
51
+ const bound = await exchangeOrExplain(token.access_token, resource, scope, opts);
52
+ entry.secret_key = bound.access_token;
53
+ entry.expires_at = now + bound.expires_in * 1000;
54
+ entry.resource = resource;
55
+ entry.scope = bound.scope;
56
+ }
57
+ file.profiles[profile] = entry;
58
+ file.active_profile = profile;
59
+ ctx.config.save(file);
60
+ ctx.io.out(`Signed in to ${start.config.issuer} as an operator (profile '${profile}').`);
61
+ if (resource)
62
+ ctx.io.out(`Profile credential: a one-hour token for ${resource} (${entry.scope}).`);
63
+ else
64
+ ctx.io.out("Run 'atlas token --resource <ins_… | app_…> --scope <scopes>' for a token bound to one instance or app.");
65
+ return 0;
66
+ }
67
+ async function exchangeOrExplain(subject, resource, scope, opts) {
68
+ try {
69
+ return await (0, backend_1.exchangeOperatorToken)(subject, { resource, scope }, opts);
70
+ }
71
+ catch (err) {
72
+ if (err instanceof backend_1.OAuthFlowError)
73
+ throw new errors_1.CliError(`Token exchange refused (${err.error}): ${err.message}`);
74
+ throw err;
75
+ }
76
+ }
77
+ /**
78
+ * `atlas token --resource <ins_…|app_…|acct_…> --scope "<scopes>"` — swap the
79
+ * stored operator token for a one-hour Atlas token bound to that resource.
80
+ * Prints the token (or JSON with --json); `--profile` stores it as that
81
+ * profile's credential instead.
82
+ */
83
+ async function token(ctx, args) {
84
+ const { values } = args;
85
+ const file = ctx.config.load();
86
+ const profileName = (0, args_1.str)(values, 'profile') || file.active_profile || 'default';
87
+ const profile = file.profiles[profileName];
88
+ if (!profile?.operator || profile.operator.expires_at <= Date.now()) {
89
+ throw new errors_1.CliError("No current operator sign-in. Run 'atlas login --device' first.");
90
+ }
91
+ const resource = (0, args_1.str)(values, 'resource');
92
+ if (!resource)
93
+ throw new errors_1.CliError('--resource is required (an ins_…, app_… or acct_… id).');
94
+ const scope = (0, args_1.str)(values, 'scope') || 'read';
95
+ const bound = await exchangeOrExplain(profile.operator.access_token, resource, scope, { apiUrl: profile.api_url, fetch: ctx.fetch });
96
+ if ((0, shared_1.wantsJson)(values)) {
97
+ (0, output_1.printJson)(ctx.io, bound);
98
+ return 0;
99
+ }
100
+ ctx.io.out(bound.access_token);
101
+ return 0;
102
+ }
103
+ /** Write a secret to a 0600 file (directories 0700). */
104
+ function writeSecret(path, name, secret) {
105
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(path), { recursive: true, mode: 0o700 });
106
+ (0, node_fs_1.writeFileSync)(path, `${name}=${secret}\n`, { mode: 0o600 });
107
+ (0, node_fs_1.chmodSync)(path, 0o600);
108
+ }
109
+ /**
110
+ * `atlas keys whoami|list|create|rotate|revoke|revoke-leaked`.
111
+ *
112
+ * create / rotate ask for the new secret SEALED to a key pair generated here
113
+ * (X-Secret-Recipient), open it locally and write it to a 0600 file
114
+ * (`--out`, default `.atlas/secret-key-<id>.env`), so the plaintext never
115
+ * crosses a log or terminal. `--print-secret` prints it instead.
116
+ */
117
+ async function keys(ctx, args) {
118
+ const { values, positionals } = args;
119
+ const sub = positionals[1];
120
+ if (sub === 'revoke-leaked') {
121
+ const secret = positionals[2] ?? (await ctx.prompt.secret('Leaked secret'));
122
+ if (!secret)
123
+ throw new errors_1.CliError('Pass the leaked secret (or enter it at the prompt).');
124
+ const apiUrl = (0, args_1.str)(values, 'url') || (0, shared_1.envApiUrl)(ctx.env) || backend_1.DEFAULT_API_URL;
125
+ const res = await (0, backend_1.revokeLeakedKeys)([secret], { apiUrl, fetch: ctx.fetch });
126
+ ctx.io.out(`Result: ${res.results[0]?.outcome ?? 'unknown'}`);
127
+ return 0;
128
+ }
129
+ const auth = (0, shared_1.resolveAuth)(ctx, values);
130
+ const pair = (0, backend_1.generateSealKeyPair)();
131
+ const atlas = (0, backend_1.createAtlasClient)({ secretKey: auth.secretKey, apiUrl: auth.apiUrl, fetch: ctx.fetch, secretRecipient: pair.secretRecipient });
132
+ const emit = (data, line) => ((0, shared_1.wantsJson)(values) ? (0, output_1.printJson)(ctx.io, data) : line());
133
+ const deliver = (id, sealed, plain) => {
134
+ const secret = sealed ? (0, backend_1.openSealedSecret)(sealed, pair) : plain;
135
+ if (!secret)
136
+ throw new errors_1.CliError('The response carried no secret.');
137
+ if (values['print-secret'] === true)
138
+ return ctx.io.out(secret);
139
+ const path = (0, args_1.str)(values, 'out') || `.atlas/secret-key-${id}.env`;
140
+ writeSecret(path, 'ATLAS_SECRET_KEY', secret);
141
+ ctx.io.out(`Secret written to ${path} (mode 0600).`);
142
+ };
143
+ switch (sub) {
144
+ case 'whoami': {
145
+ const me = await atlas.whoami();
146
+ emit(me, () => {
147
+ ctx.io.out(`Key: ${me.key.id} (${me.key.type}, ${me.key.prefix ?? ''}…)`);
148
+ ctx.io.out(`Scopes: ${me.key.scopes.join(' ')}`);
149
+ ctx.io.out(`Instance: ${me.instance?.id ?? '-'} Account: ${me.account.id ?? '-'}`);
150
+ if (me.key.expires_at)
151
+ ctx.io.out(`Expires: ${new Date(me.key.expires_at).toISOString()}`);
152
+ });
153
+ return 0;
154
+ }
155
+ case 'list': {
156
+ const page = await atlas.keys.list();
157
+ emit(page, () => {
158
+ for (const k of page.data) {
159
+ ctx.io.out(`${k.id} ${k.kind.padEnd(14)} ${k.prefix.padEnd(20)} ${k.status.padEnd(9)} owner=${k.owner ?? '-'} last_used=${k.last_used_at ? new Date(k.last_used_at).toISOString() : 'never'}`);
160
+ }
161
+ });
162
+ return 0;
163
+ }
164
+ case 'create': {
165
+ const scopes = ((0, args_1.str)(values, 'scopes') || '').split(/[ ,]+/).filter(Boolean);
166
+ if (scopes.length === 0)
167
+ throw new errors_1.CliError('--scopes is required, e.g. --scopes "instance:read webhooks:read" or --scopes read_only.');
168
+ const created = await atlas.secretKeys.create({
169
+ scopes,
170
+ name: (0, args_1.str)(values, 'name') || undefined,
171
+ owner: (0, args_1.str)(values, 'owner') || undefined,
172
+ expires_at: (0, args_1.str)(values, 'expires-at') || undefined,
173
+ allowed_ips: (0, args_1.str)(values, 'allowed-ips') ? ((0, args_1.str)(values, 'allowed-ips') ?? '').split(',').map((s) => s.trim()) : undefined,
174
+ });
175
+ ctx.io.out(`Created ${created.id} (${created.prefix}…) with ${created.scopes.join(' ')}.`);
176
+ deliver(created.id, created.sealed_secret, created.secret);
177
+ return 0;
178
+ }
179
+ case 'rotate': {
180
+ const id = (0, shared_1.requireArg)(positionals, 2, 'key id');
181
+ const overlap = (0, args_1.str)(values, 'overlap');
182
+ const rotated = await atlas.secretKeys.rotate(id, overlap ? { overlap_seconds: Number(overlap) } : {});
183
+ ctx.io.out(`Rotated ${id} → ${rotated.id}.` +
184
+ (rotated.previous_key_expires_at ? ` The old key works until ${new Date(rotated.previous_key_expires_at).toISOString()}.` : ' The old key was revoked.'));
185
+ deliver(rotated.id, rotated.sealed_secret, rotated.secret);
186
+ return 0;
187
+ }
188
+ case 'revoke': {
189
+ const id = (0, shared_1.requireArg)(positionals, 2, 'key id');
190
+ await atlas.secretKeys.revoke(id);
191
+ ctx.io.out(`Revoked ${id}.`);
192
+ return 0;
193
+ }
194
+ default:
195
+ throw new errors_1.CliError('Usage: atlas keys whoami|list|create|rotate <id>|revoke <id>|revoke-leaked [secret]');
196
+ }
197
+ }
198
+ //# sourceMappingURL=operator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operator.js","sourceRoot":"","sources":["../../src/commands/operator.ts"],"names":[],"mappings":"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"}
@@ -20,7 +20,19 @@ export declare function instanceCredential(ctx: RunContext, args: ParsedArgs): {
20
20
  };
21
21
  /** `atlas preflight` — run the instance's setup checks; exit 1 on errors (or warnings with --fail-on warning). */
22
22
  export declare function preflight(ctx: RunContext, args: ParsedArgs): Promise<number>;
23
+ /** `atlas doctor` — the integration-health report; exit 1 when a check fails. */
24
+ export declare function doctor(ctx: RunContext, args: ParsedArgs): Promise<number>;
23
25
  /** Public values, then each secret as a reference to its file — never the value. */
24
26
  export declare function envCommand(ctx: RunContext, args: ParsedArgs): Promise<number>;
27
+ /**
28
+ * Kubernetes output: a ConfigMap with the public values, `kubectl create secret`
29
+ * commands that read each secret straight from its 0600 file (so a value never
30
+ * passes through this output), and the container `envFrom` / `secretKeyRef`
31
+ * snippet that wires both in.
32
+ */
33
+ export declare function k8sManifest(product: string, environment: string, pub: Record<string, string>, secrets: Array<{
34
+ key: string;
35
+ file: string;
36
+ }>): string;
25
37
  /** `atlas client rotate <id> [--now | --overlap <seconds>]` — the new secret goes straight to its file. */
26
38
  export declare function client(ctx: RunContext, args: ParsedArgs): Promise<number>;
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.instanceCredential = instanceCredential;
4
4
  exports.preflight = preflight;
5
+ exports.doctor = doctor;
5
6
  exports.envCommand = envCommand;
7
+ exports.k8sManifest = k8sManifest;
6
8
  exports.client = client;
7
9
  const backend_1 = require("@atlasauth/backend");
8
10
  const args_1 = require("../args");
@@ -33,7 +35,7 @@ function instanceCredential(ctx, args) {
33
35
  }
34
36
  return {
35
37
  secretKey: (0, project_1.instanceSecretFromFile)(found.instance),
36
- apiUrl: ctx.env.ATLAS_API_URL?.trim() || found.manifest.api_url,
38
+ apiUrl: (0, shared_1.envApiUrl)(ctx.env) || found.manifest.api_url,
37
39
  manifest: found.manifest,
38
40
  instance: found.instance,
39
41
  };
@@ -42,7 +44,7 @@ function instanceCredential(ctx, args) {
42
44
  const { manifest, instance } = projectInstance(ctx, args, product);
43
45
  return {
44
46
  secretKey: (0, project_1.instanceSecretFromFile)(instance),
45
- apiUrl: ctx.env.ATLAS_API_URL?.trim() || manifest.api_url,
47
+ apiUrl: (0, shared_1.envApiUrl)(ctx.env) || manifest.api_url,
46
48
  manifest,
47
49
  instance,
48
50
  };
@@ -108,43 +110,104 @@ async function preflight(ctx, args) {
108
110
  }
109
111
  return 0;
110
112
  }
113
+ const HEALTH_COLUMNS = [
114
+ { header: 'STATE', get: (c) => (c.ok === true ? 'ok' : c.ok === false ? 'FAIL' : '-') },
115
+ { header: 'CHECK', get: (c) => c.id },
116
+ { header: 'HOP', get: (c) => c.hop ?? '' },
117
+ { header: 'DETAIL', get: (c) => c.detail },
118
+ ];
119
+ /** `atlas doctor` — the integration-health report; exit 1 when a check fails. */
120
+ async function doctor(ctx, args) {
121
+ const cred = instanceCredential(ctx, args);
122
+ const q = new URLSearchParams();
123
+ const only = (0, args_1.str)(args.values, 'only');
124
+ if (only)
125
+ q.set('only', only);
126
+ if (args.values['no-probe'] === true)
127
+ q.set('probe', 'false');
128
+ const qs = q.toString();
129
+ const report = await (0, project_1.httpFor)(ctx, cred).get(`/v1/integration-health${qs ? `?${qs}` : ''}`);
130
+ if ((0, shared_1.wantsJson)(args.values)) {
131
+ (0, output_1.printJson)(ctx.io, report);
132
+ }
133
+ else {
134
+ const rows = [...report.checks].sort((a, b) => (a.ok === false ? 0 : a.ok === null ? 2 : 1) - (b.ok === false ? 0 : b.ok === null ? 2 : 1));
135
+ ctx.io.out((0, output_1.renderTable)(HEALTH_COLUMNS, rows));
136
+ const failing = rows.filter((c) => c.ok === false && c.fix);
137
+ if (failing.length) {
138
+ ctx.io.out('');
139
+ for (const c of failing)
140
+ ctx.io.out(`${c.id}: ${c.fix}`);
141
+ }
142
+ ctx.io.out('');
143
+ ctx.io.out(`${report.summary.failing} failing, ${report.summary.passing} passing, ${report.summary.unknown} not known or not configured.`);
144
+ }
145
+ if (!report.ok) {
146
+ ctx.io.err(`atlas: ${report.summary.failing} integration check(s) failing.`);
147
+ return 1;
148
+ }
149
+ return 0;
150
+ }
111
151
  // ---- atlas env ----------------------------------------------------------------
112
- const FORMATS = ['dotenv', 'coolify', 'vercel'];
152
+ const FORMATS = ['dotenv', 'coolify', 'vercel', 'k8s'];
113
153
  /** Public values, then each secret as a reference to its file — never the value. */
114
154
  async function envCommand(ctx, args) {
115
155
  const format = ((0, args_1.str)(args.values, 'format') ?? 'dotenv');
116
156
  if (!FORMATS.includes(format))
117
157
  throw new errors_1.CliError(`--format must be one of: ${FORMATS.join(', ')} (got '${format}').`);
118
158
  const product = (0, args_1.str)(args.values, 'product');
119
- if (!product)
120
- throw new errors_1.CliError('atlas env needs --product <name> (the name you passed to atlas init).');
121
- const { manifest, instance } = projectInstance(ctx, args, product);
122
- const pub = {
123
- ATLAS_PUBLISHABLE_KEY: instance.publishable_key,
124
- ATLAS_API_URL: manifest.api_url,
125
- };
126
- if (instance.frontend_api_host)
127
- pub.ATLAS_FRONTEND_API = `https://${instance.frontend_api_host}`;
128
- if (instance.issuer)
129
- pub.ATLAS_ISSUER = instance.issuer;
130
- if (instance.discovery_url)
131
- pub.ATLAS_DISCOVERY_URL = instance.discovery_url;
132
- if (instance.jwks_uri)
133
- pub.ATLAS_JWKS_URI = instance.jwks_uri;
134
- if (instance.oauth_client)
135
- pub.ATLAS_CLIENT_ID = instance.oauth_client.client_id;
136
- const secrets = [{ key: 'ATLAS_SECRET_KEY', file: instance.secret_file }];
137
- if (instance.oauth_client?.secret_file)
138
- secrets.push({ key: 'ATLAS_CLIENT_SECRET', file: instance.oauth_client.secret_file });
139
- if (instance.webhook?.secret_file)
140
- secrets.push({ key: 'ATLAS_WEBHOOK_SECRET', file: instance.webhook.secret_file });
159
+ const pub = {};
160
+ const secrets = [];
161
+ let environment;
162
+ let label;
163
+ if (product) {
164
+ const { manifest, instance } = projectInstance(ctx, args, product);
165
+ environment = instance.environment;
166
+ label = product;
167
+ pub.ATLAS_PUBLISHABLE_KEY = instance.publishable_key;
168
+ pub.ATLAS_API_BASE = manifest.api_url;
169
+ pub.ATLAS_API_URL = manifest.api_url;
170
+ if (instance.frontend_api_host)
171
+ pub.ATLAS_FRONTEND_API = `https://${instance.frontend_api_host}`;
172
+ if (instance.issuer)
173
+ pub.ATLAS_ISSUER = instance.issuer;
174
+ if (instance.discovery_url)
175
+ pub.ATLAS_DISCOVERY_URL = instance.discovery_url;
176
+ if (instance.jwks_uri)
177
+ pub.ATLAS_JWKS_URI = instance.jwks_uri;
178
+ if (instance.oauth_client)
179
+ pub.ATLAS_CLIENT_ID = instance.oauth_client.client_id;
180
+ secrets.push({ key: 'ATLAS_SECRET_KEY', file: instance.secret_file });
181
+ if (instance.oauth_client?.secret_file)
182
+ secrets.push({ key: 'ATLAS_CLIENT_SECRET', file: instance.oauth_client.secret_file });
183
+ if (instance.webhook?.secret_file)
184
+ secrets.push({ key: 'ATLAS_WEBHOOK_SECRET', file: instance.webhook.secret_file });
185
+ }
186
+ else {
187
+ // No project manifest: read the public values straight from the instance the credential belongs to.
188
+ const cred = instanceCredential(ctx, args);
189
+ const live = await (0, project_1.httpFor)(ctx, cred).get('/v1/instance');
190
+ environment = live.environment;
191
+ label = live.id;
192
+ pub.ATLAS_PUBLISHABLE_KEY = live.publishable_key;
193
+ pub.ATLAS_API_BASE = cred.apiUrl;
194
+ pub.ATLAS_API_URL = cred.apiUrl;
195
+ if (live.frontend_api_host)
196
+ pub.ATLAS_FRONTEND_API = `https://${live.frontend_api_host}`;
197
+ if (live.issuer)
198
+ pub.ATLAS_ISSUER = live.issuer;
199
+ if (live.discovery_url)
200
+ pub.ATLAS_DISCOVERY_URL = live.discovery_url;
201
+ if (live.jwks_uri)
202
+ pub.ATLAS_JWKS_URI = live.jwks_uri;
203
+ }
141
204
  if ((0, shared_1.wantsJson)(args.values)) {
142
- (0, output_1.printJson)(ctx.io, { product, environment: instance.environment, public: pub, secret_files: Object.fromEntries(secrets.map((s) => [s.key, s.file])) });
205
+ (0, output_1.printJson)(ctx.io, { product: product ?? null, environment, public: pub, secret_files: Object.fromEntries(secrets.map((s) => [s.key, s.file])) });
143
206
  return 0;
144
207
  }
145
208
  const out = (l) => ctx.io.out(l);
146
209
  if (format === 'dotenv') {
147
- out(`# ${product} (${instance.environment}): public values`);
210
+ out(`# ${label} (${environment}): public values`);
148
211
  for (const [k, v] of Object.entries(pub))
149
212
  out(`${k}=${v}`);
150
213
  out('# Secrets are not printed. Each *_FILE variable names the 0600 file that holds it.');
@@ -152,16 +215,19 @@ async function envCommand(ctx, args) {
152
215
  out(`${s.key}_FILE=${s.file}`);
153
216
  }
154
217
  else if (format === 'coolify') {
155
- out(`# ${product} (${instance.environment}): paste into Coolify → Environment Variables (Developer view).`);
218
+ out(`# ${label} (${environment}): paste into Coolify → Environment Variables (Developer view).`);
156
219
  for (const [k, v] of Object.entries(pub))
157
220
  out(`${k}=${v}`);
158
221
  out('# Secrets: copy each value from its file (mark them as secret in Coolify):');
159
222
  for (const s of secrets)
160
223
  out(`${s.key}=@${s.file}`);
161
224
  }
225
+ else if (format === 'k8s') {
226
+ out(k8sManifest(label, environment, pub, secrets));
227
+ }
162
228
  else {
163
- out(`# ${product} (${instance.environment}): Vercel CLI commands. Secrets are piped from their files, never echoed.`);
164
- const target = instance.environment === 'production' ? 'production' : 'preview';
229
+ out(`# ${label} (${environment}): Vercel CLI commands. Secrets are piped from their files, never echoed.`);
230
+ const target = environment === 'production' ? 'production' : 'preview';
165
231
  for (const [k, v] of Object.entries(pub))
166
232
  out(`printf '%s' '${v.replace(/'/g, "'\\''")}' | vercel env add ${k} ${target}`);
167
233
  for (const s of secrets)
@@ -169,6 +235,40 @@ async function envCommand(ctx, args) {
169
235
  }
170
236
  return 0;
171
237
  }
238
+ /** DNS-1123 name fragment for a Kubernetes resource. */
239
+ function k8sName(raw) {
240
+ return raw.toLowerCase().replace(/[^a-z0-9-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40) || 'atlas';
241
+ }
242
+ /** YAML double-quoted scalar (JSON strings are valid YAML). */
243
+ const yamlStr = (v) => JSON.stringify(v);
244
+ /**
245
+ * Kubernetes output: a ConfigMap with the public values, `kubectl create secret`
246
+ * commands that read each secret straight from its 0600 file (so a value never
247
+ * passes through this output), and the container `envFrom` / `secretKeyRef`
248
+ * snippet that wires both in.
249
+ */
250
+ function k8sManifest(product, environment, pub, secrets) {
251
+ const base = `${k8sName(product)}-atlas`;
252
+ const lines = [
253
+ `# ${product} (${environment}): Kubernetes. Public values go in a ConfigMap; secrets are created from their 0600 files.`,
254
+ 'apiVersion: v1',
255
+ 'kind: ConfigMap',
256
+ 'metadata:',
257
+ ` name: ${base}`,
258
+ 'data:',
259
+ ...Object.entries(pub).map(([k, v]) => ` ${k}: ${yamlStr(v)}`),
260
+ '---',
261
+ '# Create each secret from its file (nothing is printed or stored in this manifest):',
262
+ ];
263
+ for (const s of secrets) {
264
+ lines.push(`# kubectl create secret generic ${base}-${k8sName(s.key)} --from-env-file='${s.file.replace(/'/g, "'\\''")}'`);
265
+ }
266
+ lines.push('---', '# Container spec fragment:', '# envFrom:', `# - configMapRef: { name: ${base} }`, '# env:');
267
+ for (const s of secrets) {
268
+ lines.push(`# - name: ${s.key}`, `# valueFrom: { secretKeyRef: { name: ${base}-${k8sName(s.key)}, key: ${s.key} } }`);
269
+ }
270
+ return lines.join('\n');
271
+ }
172
272
  // ---- atlas client rotate ----------------------------------------------------------
173
273
  /** `atlas client rotate <id> [--now | --overlap <seconds>]` — the new secret goes straight to its file. */
174
274
  async function client(ctx, args) {