@atlasauth/cli 0.0.0-stage → 0.2.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 (71) hide show
  1. package/README.md +91 -2
  2. package/dist/args.d.ts +84 -0
  3. package/dist/args.js +76 -0
  4. package/dist/args.js.map +1 -0
  5. package/dist/cli.d.ts +2 -0
  6. package/dist/cli.js +19 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/commands/actions.d.ts +11 -0
  9. package/dist/commands/actions.js +120 -0
  10. package/dist/commands/actions.js.map +1 -0
  11. package/dist/commands/api-keys.d.ts +4 -0
  12. package/dist/commands/api-keys.js +81 -0
  13. package/dist/commands/api-keys.js.map +1 -0
  14. package/dist/commands/auth.d.ts +25 -0
  15. package/dist/commands/auth.js +94 -0
  16. package/dist/commands/auth.js.map +1 -0
  17. package/dist/commands/config.d.ts +4 -0
  18. package/dist/commands/config.js +716 -0
  19. package/dist/commands/config.js.map +1 -0
  20. package/dist/commands/orgs.d.ts +4 -0
  21. package/dist/commands/orgs.js +72 -0
  22. package/dist/commands/orgs.js.map +1 -0
  23. package/dist/commands/sessions.d.ts +4 -0
  24. package/dist/commands/sessions.js +28 -0
  25. package/dist/commands/sessions.js.map +1 -0
  26. package/dist/commands/shared.d.ts +32 -0
  27. package/dist/commands/shared.js +60 -0
  28. package/dist/commands/shared.js.map +1 -0
  29. package/dist/commands/users.d.ts +4 -0
  30. package/dist/commands/users.js +87 -0
  31. package/dist/commands/users.js.map +1 -0
  32. package/dist/commands/webhooks.d.ts +4 -0
  33. package/dist/commands/webhooks.js +34 -0
  34. package/dist/commands/webhooks.js.map +1 -0
  35. package/dist/config-http.d.ts +27 -0
  36. package/dist/config-http.js +63 -0
  37. package/dist/config-http.js.map +1 -0
  38. package/dist/config.d.ts +48 -0
  39. package/dist/config.js +69 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/context.d.ts +51 -0
  42. package/dist/context.js +27 -0
  43. package/dist/context.js.map +1 -0
  44. package/dist/errors.d.ts +15 -0
  45. package/dist/errors.js +25 -0
  46. package/dist/errors.js.map +1 -0
  47. package/dist/help.d.ts +8 -0
  48. package/dist/help.js +114 -0
  49. package/dist/help.js.map +1 -0
  50. package/dist/index.d.ts +22 -0
  51. package/dist/index.js +49 -0
  52. package/dist/index.js.map +1 -0
  53. package/dist/io.d.ts +21 -0
  54. package/dist/io.js +28 -0
  55. package/dist/io.js.map +1 -0
  56. package/dist/output.d.ts +41 -0
  57. package/dist/output.js +72 -0
  58. package/dist/output.js.map +1 -0
  59. package/dist/prompt.d.ts +15 -0
  60. package/dist/prompt.js +46 -0
  61. package/dist/prompt.js.map +1 -0
  62. package/dist/run.d.ts +12 -0
  63. package/dist/run.js +94 -0
  64. package/dist/run.js.map +1 -0
  65. package/dist/serialize.d.ts +31 -0
  66. package/dist/serialize.js +248 -0
  67. package/dist/serialize.js.map +1 -0
  68. package/dist/version.d.ts +7 -0
  69. package/dist/version.js +21 -0
  70. package/dist/version.js.map +1 -0
  71. package/package.json +35 -3
package/README.md CHANGED
@@ -1,3 +1,92 @@
1
- # Temporary Holding Version
1
+ # @atlasauth/cli
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ `atlas` — manage an Atlas instance from the terminal, over the secret-key
4
+ Backend API. The peer of `auth0-cli` and Clerk's `cli`, built directly on
5
+ [`@atlasauth/backend`](../backend).
6
+
7
+ ## Install
8
+
9
+ ```sh
10
+ npm i -g @atlasauth/cli
11
+ # or run without installing:
12
+ npx @atlasauth/cli --help
13
+ ```
14
+
15
+ ## Authentication
16
+
17
+ The CLI is config-file + secret-key based, like `auth0-cli`'s token model — no
18
+ dashboard OAuth app to register against.
19
+
20
+ ```sh
21
+ atlas login # prompts for an API URL + an sk_ key, validates it
22
+ atlas login --profile prod # store a second, named credential
23
+ atlas whoami # show the active profile + instance (key masked)
24
+ atlas logout # forget a stored credential
25
+ ```
26
+
27
+ `login` validates the key with one cheap Backend call before saving it to
28
+ `~/.config/atlasauth/config.json` (mode `0600`). `--url` / `--secret-key` skip
29
+ the prompts for CI.
30
+
31
+ Env overrides (win over the stored profile, like most cloud CLIs):
32
+
33
+ ```sh
34
+ ATLAS_SECRET_KEY=sk_live_... ATLAS_API_URL=https://api.atlasauth.net atlas users list
35
+ ```
36
+
37
+ ## Commands
38
+
39
+ ```
40
+ atlas users list | get <id> | create --email <e> | delete <id>
41
+ atlas orgs list | get <id> | members <id>
42
+ atlas api-keys list | create --subject-type <t> --subject-id <id> | revoke <id>
43
+ atlas webhooks list
44
+ atlas sessions revoke <id>
45
+ atlas config export [--dir <path>] [--format json|yaml]
46
+ atlas config import [--dir <path>] [--format json|yaml] [--dry-run] [--prune]
47
+ ```
48
+
49
+ Every command takes `--json` for raw JSON (otherwise a human table). Paginated
50
+ lists (`users list`, `orgs list`) take `--limit <n>` and `--starting-after
51
+ <cursor>`; the table footer prints the exact cursor flag for the next page.
52
+
53
+ ```sh
54
+ atlas users list --limit 20
55
+ atlas users list --json | jq '.data[].id'
56
+ atlas api-keys create --subject-type user --subject-id user_123 --name ci
57
+ ```
58
+
59
+ Run `atlas --help` or `atlas <command> --help` for details, `atlas --version`
60
+ for the version.
61
+
62
+ ## Config-as-code (`atlas config`)
63
+
64
+ The Auth0 `deploy-cli` equivalent: round-trip an instance's configuration to a
65
+ deterministic directory tree you keep in Git. `export` writes one file per
66
+ resource type with sorted keys and **secrets omitted**; `import` diffs the
67
+ directory against the live instance and applies creates/updates idempotently.
68
+
69
+ ```sh
70
+ atlas config export --dir ./atlas-config # snapshot the instance
71
+ atlas config import --dir ./atlas-config --dry-run # preview the plan
72
+ atlas config import --dir ./atlas-config # apply creates/updates
73
+ atlas config import --dir ./atlas-config --prune # also delete removed resources
74
+ ```
75
+
76
+ Managed resource types (the same set the Terraform provider manages):
77
+ `instance` (auth config + allowed origins), `oauth_clients`, `oauth_providers`,
78
+ `sso_connections`, `resource_servers`, `jwt_templates`, `roles`,
79
+ `webhook_endpoints`, `billing_plans`, `redirect_urls`, `org_settings_schema`,
80
+ `notification_templates`, `email_templates`, `organization_policies`.
81
+
82
+ Rules that make it safe and repeatable:
83
+
84
+ - **Deterministic.** Keys are sorted, so re-exporting an unchanged instance is a
85
+ byte-for-byte no-op and a round-trip (`export` then `import`) is an empty diff.
86
+ - **No secrets on disk.** The management API never returns a secret on a read,
87
+ so none is ever written. To (re)write a credential at import, put a
88
+ `${ENV_VAR}` placeholder in the file; it is resolved from the environment.
89
+ - **Non-destructive.** `import` never deletes without `--prune`, and a resource
90
+ type with no file is left untouched (only committed types are managed).
91
+
92
+ Ship it on push with the [`atlas-deploy` GitHub Action](../../actions/atlas-deploy).
package/dist/args.d.ts ADDED
@@ -0,0 +1,84 @@
1
+ /**
2
+ * The single option schema for the whole CLI. parseArgs in `strict` mode rejects
3
+ * an unknown flag for us (turned into a clean {@link CliError} below), so every
4
+ * flag any command accepts must appear here once. Per-command validation of
5
+ * which flags actually apply happens in the handler.
6
+ */
7
+ declare const OPTIONS: {
8
+ readonly json: {
9
+ readonly type: "boolean";
10
+ };
11
+ readonly profile: {
12
+ readonly type: "string";
13
+ };
14
+ readonly limit: {
15
+ readonly type: "string";
16
+ };
17
+ readonly 'starting-after': {
18
+ readonly type: "string";
19
+ };
20
+ readonly help: {
21
+ readonly type: "boolean";
22
+ readonly short: "h";
23
+ };
24
+ readonly version: {
25
+ readonly type: "boolean";
26
+ readonly short: "v";
27
+ };
28
+ readonly url: {
29
+ readonly type: "string";
30
+ };
31
+ readonly 'secret-key': {
32
+ readonly type: "string";
33
+ };
34
+ readonly email: {
35
+ readonly type: "string";
36
+ };
37
+ readonly 'first-name': {
38
+ readonly type: "string";
39
+ };
40
+ readonly 'last-name': {
41
+ readonly type: "string";
42
+ };
43
+ readonly password: {
44
+ readonly type: "string";
45
+ };
46
+ readonly 'subject-type': {
47
+ readonly type: "string";
48
+ };
49
+ readonly 'subject-id': {
50
+ readonly type: "string";
51
+ };
52
+ readonly name: {
53
+ readonly type: "string";
54
+ };
55
+ readonly dir: {
56
+ readonly type: "string";
57
+ };
58
+ readonly format: {
59
+ readonly type: "string";
60
+ };
61
+ readonly 'dry-run': {
62
+ readonly type: "boolean";
63
+ };
64
+ readonly prune: {
65
+ readonly type: "boolean";
66
+ };
67
+ readonly trigger: {
68
+ readonly type: "string";
69
+ };
70
+ };
71
+ export type Values = {
72
+ [K in keyof typeof OPTIONS]?: (typeof OPTIONS)[K]['type'] extends 'boolean' ? boolean : string;
73
+ };
74
+ export interface ParsedArgs {
75
+ positionals: string[];
76
+ values: Values;
77
+ }
78
+ /** Parse argv, turning parseArgs' own errors (unknown/invalid flag) into clean CliErrors. */
79
+ export declare function parse(argv: string[]): ParsedArgs;
80
+ /** A string flag's value, or undefined. */
81
+ export declare function str(values: Values, key: keyof Values): string | undefined;
82
+ /** `--limit` as a positive integer, or undefined. Rejects garbage with a clean error. */
83
+ export declare function limit(values: Values): number | undefined;
84
+ export {};
package/dist/args.js ADDED
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parse = parse;
4
+ exports.str = str;
5
+ exports.limit = limit;
6
+ const node_util_1 = require("node:util");
7
+ const errors_1 = require("./errors");
8
+ /**
9
+ * The single option schema for the whole CLI. parseArgs in `strict` mode rejects
10
+ * an unknown flag for us (turned into a clean {@link CliError} below), so every
11
+ * flag any command accepts must appear here once. Per-command validation of
12
+ * which flags actually apply happens in the handler.
13
+ */
14
+ const OPTIONS = {
15
+ // Global.
16
+ json: { type: 'boolean' },
17
+ profile: { type: 'string' },
18
+ limit: { type: 'string' },
19
+ 'starting-after': { type: 'string' },
20
+ help: { type: 'boolean', short: 'h' },
21
+ version: { type: 'boolean', short: 'v' },
22
+ // login (non-interactive overrides for the prompts).
23
+ url: { type: 'string' },
24
+ 'secret-key': { type: 'string' },
25
+ // users create.
26
+ email: { type: 'string' },
27
+ 'first-name': { type: 'string' },
28
+ 'last-name': { type: 'string' },
29
+ password: { type: 'string' },
30
+ // api-keys.
31
+ 'subject-type': { type: 'string' },
32
+ 'subject-id': { type: 'string' },
33
+ name: { type: 'string' },
34
+ // config export / import.
35
+ dir: { type: 'string' },
36
+ format: { type: 'string' },
37
+ 'dry-run': { type: 'boolean' },
38
+ prune: { type: 'boolean' },
39
+ // actions.
40
+ trigger: { type: 'string' },
41
+ };
42
+ /** Parse argv, turning parseArgs' own errors (unknown/invalid flag) into clean CliErrors. */
43
+ function parse(argv) {
44
+ try {
45
+ const { values, positionals } = (0, node_util_1.parseArgs)({
46
+ args: argv,
47
+ options: OPTIONS,
48
+ allowPositionals: true,
49
+ strict: true,
50
+ });
51
+ return { values: values, positionals };
52
+ }
53
+ catch (err) {
54
+ // parseArgs throws a TypeError with an ERR_PARSE_ARGS_* code; surface its
55
+ // message (e.g. "Unknown option '--frob'") without the stack.
56
+ const message = err instanceof Error ? err.message : String(err);
57
+ throw new errors_1.CliError(message);
58
+ }
59
+ }
60
+ /** A string flag's value, or undefined. */
61
+ function str(values, key) {
62
+ const v = values[key];
63
+ return typeof v === 'string' ? v : undefined;
64
+ }
65
+ /** `--limit` as a positive integer, or undefined. Rejects garbage with a clean error. */
66
+ function limit(values) {
67
+ const raw = str(values, 'limit');
68
+ if (raw === undefined)
69
+ return undefined;
70
+ const n = Number(raw);
71
+ if (!Number.isInteger(n) || n < 1) {
72
+ throw new errors_1.CliError(`--limit must be a positive integer, got '${raw}'.`);
73
+ }
74
+ return n;
75
+ }
76
+ //# sourceMappingURL=args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../src/args.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const context_1 = require("./context");
5
+ const run_1 = require("./run");
6
+ /**
7
+ * The bin entry — the ONLY place that touches `process` for lifecycle. It wires
8
+ * the production context, runs, and exits with the returned code. `run` is
9
+ * designed to catch its own errors, so the `.catch` here is a last-resort guard
10
+ * against a genuine bug; even then it prints a line, never a raw stack.
11
+ */
12
+ (0, run_1.run)(process.argv.slice(2), (0, context_1.defaultContext)())
13
+ .then((code) => process.exit(code))
14
+ .catch((err) => {
15
+ const message = err instanceof Error ? err.message : String(err);
16
+ process.stderr.write(`atlas: unexpected error: ${message}\n`);
17
+ process.exit(1);
18
+ });
19
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;AACA,uCAA2C;AAC3C,+BAA4B;AAE5B;;;;;GAKG;AACH,IAAA,SAAG,EAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,IAAA,wBAAc,GAAE,CAAC;KACzC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAClC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtB,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACjE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4BAA4B,OAAO,IAAI,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { type ParsedArgs } from '../args';
2
+ import type { RunContext } from '../context';
3
+ /**
4
+ * `atlas actions` — manage tenant Actions (code run in the auth pipeline).
5
+ *
6
+ * list the instance's actions
7
+ * deploy <file> --trigger <t> upload a file's code as a new action
8
+ * test <file> [--trigger <t>] dry-run the file OFFLINE through @atlasauth/actions
9
+ * logs <id> where to find an action's execution history
10
+ */
11
+ export declare function actions(ctx: RunContext, args: ParsedArgs): Promise<number>;
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.actions = actions;
4
+ const node_path_1 = require("node:path");
5
+ const actions_1 = require("@atlasauth/actions");
6
+ const args_1 = require("../args");
7
+ const errors_1 = require("../errors");
8
+ const output_1 = require("../output");
9
+ const shared_1 = require("./shared");
10
+ const COLUMNS = [
11
+ { header: 'ID', get: (a) => a.id },
12
+ { header: 'NAME', get: (a) => a.name },
13
+ { header: 'TRIGGER', get: (a) => a.trigger },
14
+ { header: 'ENABLED', get: (a) => a.enabled },
15
+ { header: 'SECRETS', get: (a) => a.secret_names },
16
+ { header: 'UPDATED', get: (a) => (0, output_1.fmtTime)(a.updated_at) },
17
+ ];
18
+ /** Validate a `--trigger` value against the known set, with a clean error. */
19
+ function parseTrigger(raw) {
20
+ if (!raw) {
21
+ throw new errors_1.CliError(`--trigger is required (one of: ${actions_1.ACTION_TRIGGERS.join(', ')}).`);
22
+ }
23
+ if (!actions_1.ACTION_TRIGGERS.includes(raw)) {
24
+ throw new errors_1.CliError(`Unknown trigger '${raw}'. Expected one of: ${actions_1.ACTION_TRIGGERS.join(', ')}.`);
25
+ }
26
+ return raw;
27
+ }
28
+ /**
29
+ * `atlas actions` — manage tenant Actions (code run in the auth pipeline).
30
+ *
31
+ * list the instance's actions
32
+ * deploy <file> --trigger <t> upload a file's code as a new action
33
+ * test <file> [--trigger <t>] dry-run the file OFFLINE through @atlasauth/actions
34
+ * logs <id> where to find an action's execution history
35
+ */
36
+ async function actions(ctx, args) {
37
+ const sub = args.positionals[1];
38
+ const json = (0, shared_1.wantsJson)(args.values);
39
+ switch (sub) {
40
+ case 'list': {
41
+ const { client } = (0, shared_1.requireClient)(ctx, args.values);
42
+ const page = await client.actions.list();
43
+ if (json)
44
+ (0, output_1.printJson)(ctx.io, page);
45
+ else
46
+ ctx.io.out((0, output_1.renderTable)(COLUMNS, page.data));
47
+ return 0;
48
+ }
49
+ case 'deploy': {
50
+ const file = (0, shared_1.requireArg)(args.positionals, 2, 'file');
51
+ const trigger = parseTrigger((0, args_1.str)(args.values, 'trigger'));
52
+ const code = ctx.readFile(file);
53
+ if (!code.trim())
54
+ throw new errors_1.CliError(`'${file}' is empty — nothing to deploy.`);
55
+ const name = (0, args_1.str)(args.values, 'name') || (0, node_path_1.basename)(file).replace(/\.[^.]+$/, '');
56
+ const { client } = (0, shared_1.requireClient)(ctx, args.values);
57
+ const action = await client.actions.create({ name, trigger, code });
58
+ if (json)
59
+ (0, output_1.printJson)(ctx.io, action);
60
+ else {
61
+ ctx.io.out(`Deployed action ${action.id} (${action.name}) for ${action.trigger}.`);
62
+ ctx.io.err(`It is created but NOT yet bound to the ${action.trigger} flow — bind it to run it.`);
63
+ }
64
+ return 0;
65
+ }
66
+ case 'test': {
67
+ const file = (0, shared_1.requireArg)(args.positionals, 2, 'file');
68
+ // Default to post_login's sample event; --trigger picks another shape.
69
+ const trigger = (0, args_1.str)(args.values, 'trigger')
70
+ ? parseTrigger((0, args_1.str)(args.values, 'trigger'))
71
+ : 'post_login';
72
+ const code = ctx.readFile(file);
73
+ if (!code.trim())
74
+ throw new errors_1.CliError(`'${file}' is empty — nothing to test.`);
75
+ // OFFLINE: no network, no client — the local harness mirrors the isolate.
76
+ const result = await (0, actions_1.runScript)(code, (0, actions_1.createTestEvent)(trigger));
77
+ if (json) {
78
+ (0, output_1.printJson)(ctx.io, result);
79
+ }
80
+ else {
81
+ ctx.io.out(`Trigger: ${trigger}`);
82
+ ctx.io.out(`Denied: ${result.denied}${result.denyReason ? ` (${result.denyReason})` : ''}`);
83
+ ctx.io.out(`ID claims: ${JSON.stringify(result.idTokenClaims)}`);
84
+ ctx.io.out(`AT claims: ${JSON.stringify(result.accessTokenClaims)}`);
85
+ ctx.io.out(`Metadata: ${JSON.stringify(result.appMetadata)}`);
86
+ if (result.logs.length)
87
+ ctx.io.out(`Logs:\n ${result.logs.join('\n ')}`);
88
+ if (result.error)
89
+ ctx.io.err(`Error: ${result.error}`);
90
+ }
91
+ // A thrown error in the action is a failed dry-run → non-zero so CI catches it.
92
+ return result.error ? 1 : 0;
93
+ }
94
+ case 'logs': {
95
+ const id = (0, shared_1.requireArg)(args.positionals, 2, 'action-id');
96
+ // The Backend API does not expose a per-action execution-log endpoint:
97
+ // each run is written as an audit event (`action.<trigger>`) carrying the
98
+ // outcome, duration and captured logs. Point the user at where to read it
99
+ // rather than pretend an endpoint exists.
100
+ const lines = [
101
+ `Atlas does not expose a live log stream for action ${id}.`,
102
+ 'Each execution is recorded as an audit event named `action.<trigger>`',
103
+ '(outcome, duration, deny reason / error, and the action\'s console logs).',
104
+ 'Read them in the dashboard audit log, or stream them via a configured',
105
+ 'log stream. To see what an action WOULD do right now, dry-run it:',
106
+ ` atlas actions test <file> --trigger <trigger>`,
107
+ ];
108
+ if (json)
109
+ (0, output_1.printJson)(ctx.io, { object: 'action_logs_info', action_id: id, message: lines.join(' ') });
110
+ else
111
+ ctx.io.out(lines.join('\n'));
112
+ return 0;
113
+ }
114
+ default:
115
+ throw new errors_1.CliError(sub
116
+ ? `Unknown subcommand 'actions ${sub}'. Try: list, deploy, test, logs.`
117
+ : 'actions needs a subcommand: list, deploy, test, logs.');
118
+ }
119
+ }
120
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/commands/actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { type ParsedArgs } from '../args';
2
+ import type { RunContext } from '../context';
3
+ /** `atlas api-keys <list|create|revoke>` — maps onto `client.apiKeys.*`. */
4
+ export declare function apiKeys(ctx: RunContext, args: ParsedArgs): Promise<number>;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apiKeys = apiKeys;
4
+ const args_1 = require("../args");
5
+ const errors_1 = require("../errors");
6
+ const output_1 = require("../output");
7
+ const shared_1 = require("./shared");
8
+ const COLUMNS = [
9
+ { header: 'ID', get: (k) => k.id },
10
+ { header: 'SUBJECT_TYPE', get: (k) => k.subject_type },
11
+ { header: 'SUBJECT_ID', get: (k) => k.subject_id },
12
+ { header: 'NAME', get: (k) => k.name },
13
+ { header: 'PREFIX', get: (k) => k.prefix },
14
+ { header: 'CREATED', get: (k) => (0, output_1.fmtTime)(k.created_at) },
15
+ ];
16
+ const SUBJECT_TYPES = ['user', 'organization'];
17
+ function parseSubjectType(value, flag) {
18
+ if (!value)
19
+ throw new errors_1.CliError(`api-keys create requires ${flag} <user|organization>.`);
20
+ if (!SUBJECT_TYPES.includes(value)) {
21
+ throw new errors_1.CliError(`${flag} must be one of: ${SUBJECT_TYPES.join(', ')} (got '${value}').`);
22
+ }
23
+ return value;
24
+ }
25
+ /** `atlas api-keys <list|create|revoke>` — maps onto `client.apiKeys.*`. */
26
+ async function apiKeys(ctx, args) {
27
+ const sub = args.positionals[1];
28
+ const json = (0, shared_1.wantsJson)(args.values);
29
+ switch (sub) {
30
+ case 'list': {
31
+ const { client } = (0, shared_1.requireClient)(ctx, args.values);
32
+ // Optional narrowing to one subject; the route itself is not cursor-paged.
33
+ const subjectType = (0, args_1.str)(args.values, 'subject-type');
34
+ const page = await client.apiKeys.list({
35
+ subject_type: subjectType ? parseSubjectType(subjectType, '--subject-type') : undefined,
36
+ subject_id: (0, args_1.str)(args.values, 'subject-id'),
37
+ });
38
+ if (json)
39
+ (0, output_1.printJson)(ctx.io, page);
40
+ else
41
+ ctx.io.out((0, output_1.renderTable)(COLUMNS, page.data));
42
+ return 0;
43
+ }
44
+ case 'create': {
45
+ const subjectType = parseSubjectType((0, args_1.str)(args.values, 'subject-type'), '--subject-type');
46
+ const subjectId = (0, args_1.str)(args.values, 'subject-id');
47
+ if (!subjectId)
48
+ throw new errors_1.CliError('api-keys create requires --subject-id <id>.');
49
+ const { client } = (0, shared_1.requireClient)(ctx, args.values);
50
+ const key = await client.apiKeys.create({
51
+ subject_type: subjectType,
52
+ subject_id: subjectId,
53
+ name: (0, args_1.str)(args.values, 'name'),
54
+ });
55
+ if (json) {
56
+ (0, output_1.printJson)(ctx.io, key);
57
+ }
58
+ else {
59
+ // The secret is revealed exactly once — make that unmissable.
60
+ ctx.io.out(`Created API key ${key.id}.`);
61
+ ctx.io.out(`Secret (shown once): ${key.secret}`);
62
+ }
63
+ return 0;
64
+ }
65
+ case 'revoke': {
66
+ const id = (0, shared_1.requireArg)(args.positionals, 2, 'api-key-id');
67
+ const { client } = (0, shared_1.requireClient)(ctx, args.values);
68
+ const result = await client.apiKeys.delete(id);
69
+ if (json)
70
+ (0, output_1.printJson)(ctx.io, result);
71
+ else
72
+ ctx.io.out(`Revoked API key ${result.id}.`);
73
+ return 0;
74
+ }
75
+ default:
76
+ throw new errors_1.CliError(sub
77
+ ? `Unknown subcommand 'api-keys ${sub}'. Try: list, create, revoke.`
78
+ : 'api-keys needs a subcommand: list, create, revoke.');
79
+ }
80
+ }
81
+ //# sourceMappingURL=api-keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-keys.js","sourceRoot":"","sources":["../../src/commands/api-keys.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import { type ParsedArgs } from '../args';
2
+ import type { RunContext } from '../context';
3
+ /**
4
+ * `atlas login` — prompt for an API URL + a secret key, PROVE the key works
5
+ * with one cheap Backend call, then store it under a named profile (0600).
6
+ *
7
+ * Validating before saving is the point: a login that silently stored a typo'd
8
+ * key would surface only later as a baffling 401 on an unrelated command. We
9
+ * spend one `users.list({ limit: 1 })` round-trip here so every later command
10
+ * can trust the stored credential. `--url` / `--secret-key` skip the prompts
11
+ * for non-interactive setup (CI), mirroring the `ATLAS_*` env overrides.
12
+ */
13
+ export declare function login(ctx: RunContext, args: ParsedArgs): Promise<number>;
14
+ /**
15
+ * `atlas logout` — forget one profile's stored credential. If it was the active
16
+ * profile, the active pointer falls back to any remaining profile so the CLI
17
+ * isn't left pointing at nothing.
18
+ */
19
+ export declare function logout(ctx: RunContext, args: ParsedArgs): Promise<number>;
20
+ /**
21
+ * `atlas whoami` — report the active profile and instance without any network
22
+ * call. The secret is always masked; the per-field source tells a user whether
23
+ * an `ATLAS_*` env var is shadowing the stored profile.
24
+ */
25
+ export declare function whoami(ctx: RunContext, args: ParsedArgs): Promise<number>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.login = login;
4
+ exports.logout = logout;
5
+ exports.whoami = whoami;
6
+ const backend_1 = require("@atlasauth/backend");
7
+ const args_1 = require("../args");
8
+ const errors_1 = require("../errors");
9
+ const output_1 = require("../output");
10
+ const shared_1 = require("./shared");
11
+ /**
12
+ * `atlas login` — prompt for an API URL + a secret key, PROVE the key works
13
+ * with one cheap Backend call, then store it under a named profile (0600).
14
+ *
15
+ * Validating before saving is the point: a login that silently stored a typo'd
16
+ * key would surface only later as a baffling 401 on an unrelated command. We
17
+ * spend one `users.list({ limit: 1 })` round-trip here so every later command
18
+ * can trust the stored credential. `--url` / `--secret-key` skip the prompts
19
+ * for non-interactive setup (CI), mirroring the `ATLAS_*` env overrides.
20
+ */
21
+ async function login(ctx, args) {
22
+ const { values } = args;
23
+ const profile = (0, args_1.str)(values, 'profile') || 'default';
24
+ const apiUrl = (0, args_1.str)(values, 'url') ||
25
+ (await ctx.prompt.text('Atlas API URL', { default: backend_1.DEFAULT_API_URL })) ||
26
+ backend_1.DEFAULT_API_URL;
27
+ const secretKey = (0, args_1.str)(values, 'secret-key') || (await ctx.prompt.secret('Atlas secret key (sk_...)'));
28
+ if (!secretKey)
29
+ throw new errors_1.CliError('A secret key is required to log in.');
30
+ // Prove the credential before persisting it.
31
+ const client = ctx.makeClient({ secretKey, apiUrl });
32
+ try {
33
+ await client.users.list({ limit: 1 });
34
+ }
35
+ catch (err) {
36
+ if (err instanceof backend_1.AtlasApiError) {
37
+ throw new errors_1.CliError(`Secret key rejected by ${apiUrl} (HTTP ${err.status}${err.code ? `, ${err.code}` : ''}). Nothing was saved.`);
38
+ }
39
+ // A network/DNS failure, not a bad key — say so distinctly.
40
+ const detail = err instanceof Error ? err.message : String(err);
41
+ throw new errors_1.CliError(`Could not reach ${apiUrl}: ${detail}. Nothing was saved.`);
42
+ }
43
+ const file = ctx.config.load();
44
+ file.profiles[profile] = { api_url: apiUrl, secret_key: secretKey };
45
+ file.active_profile = profile;
46
+ ctx.config.save(file);
47
+ ctx.io.out(`Logged in to ${apiUrl} as profile '${profile}'.`);
48
+ ctx.io.out(`Credential stored at ${ctx.config.path} (mode 0600).`);
49
+ return 0;
50
+ }
51
+ /**
52
+ * `atlas logout` — forget one profile's stored credential. If it was the active
53
+ * profile, the active pointer falls back to any remaining profile so the CLI
54
+ * isn't left pointing at nothing.
55
+ */
56
+ async function logout(ctx, args) {
57
+ const { values } = args;
58
+ const file = ctx.config.load();
59
+ const profile = (0, args_1.str)(values, 'profile') || file.active_profile || 'default';
60
+ if (!file.profiles[profile]) {
61
+ ctx.io.out(`No stored credential for profile '${profile}'.`);
62
+ return 0;
63
+ }
64
+ delete file.profiles[profile];
65
+ if (file.active_profile === profile) {
66
+ file.active_profile = Object.keys(file.profiles)[0];
67
+ }
68
+ ctx.config.save(file);
69
+ ctx.io.out(`Logged out of profile '${profile}'.`);
70
+ return 0;
71
+ }
72
+ /**
73
+ * `atlas whoami` — report the active profile and instance without any network
74
+ * call. The secret is always masked; the per-field source tells a user whether
75
+ * an `ATLAS_*` env var is shadowing the stored profile.
76
+ */
77
+ async function whoami(ctx, args) {
78
+ const auth = (0, shared_1.resolveAuth)(ctx, args.values);
79
+ if ((0, shared_1.wantsJson)(args.values)) {
80
+ (0, output_1.printJson)(ctx.io, {
81
+ profile: auth.profile,
82
+ api_url: auth.apiUrl,
83
+ secret_key: (0, shared_1.maskSecret)(auth.secretKey),
84
+ key_source: auth.keySource,
85
+ url_source: auth.urlSource,
86
+ });
87
+ return 0;
88
+ }
89
+ ctx.io.out(`Profile: ${auth.profile}`);
90
+ ctx.io.out(`API URL: ${auth.apiUrl} (${auth.urlSource})`);
91
+ ctx.io.out(`Secret key: ${(0, shared_1.maskSecret)(auth.secretKey)} (${auth.keySource})`);
92
+ return 0;
93
+ }
94
+ //# sourceMappingURL=auth.js.map