@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.
- package/README.md +91 -2
- package/dist/args.d.ts +84 -0
- package/dist/args.js +76 -0
- package/dist/args.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +19 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/actions.d.ts +11 -0
- package/dist/commands/actions.js +120 -0
- package/dist/commands/actions.js.map +1 -0
- package/dist/commands/api-keys.d.ts +4 -0
- package/dist/commands/api-keys.js +81 -0
- package/dist/commands/api-keys.js.map +1 -0
- package/dist/commands/auth.d.ts +25 -0
- package/dist/commands/auth.js +94 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +716 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/orgs.d.ts +4 -0
- package/dist/commands/orgs.js +72 -0
- package/dist/commands/orgs.js.map +1 -0
- package/dist/commands/sessions.d.ts +4 -0
- package/dist/commands/sessions.js +28 -0
- package/dist/commands/sessions.js.map +1 -0
- package/dist/commands/shared.d.ts +32 -0
- package/dist/commands/shared.js +60 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/users.d.ts +4 -0
- package/dist/commands/users.js +87 -0
- package/dist/commands/users.js.map +1 -0
- package/dist/commands/webhooks.d.ts +4 -0
- package/dist/commands/webhooks.js +34 -0
- package/dist/commands/webhooks.js.map +1 -0
- package/dist/config-http.d.ts +27 -0
- package/dist/config-http.js +63 -0
- package/dist/config-http.js.map +1 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.js +69 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +51 -0
- package/dist/context.js +27 -0
- package/dist/context.js.map +1 -0
- package/dist/errors.d.ts +15 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/help.d.ts +8 -0
- package/dist/help.js +114 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +21 -0
- package/dist/io.js +28 -0
- package/dist/io.js.map +1 -0
- package/dist/output.d.ts +41 -0
- package/dist/output.js +72 -0
- package/dist/output.js.map +1 -0
- package/dist/prompt.d.ts +15 -0
- package/dist/prompt.js +46 -0
- package/dist/prompt.js.map +1 -0
- package/dist/run.d.ts +12 -0
- package/dist/run.js +94 -0
- package/dist/run.js.map +1 -0
- package/dist/serialize.d.ts +31 -0
- package/dist/serialize.js +248 -0
- package/dist/serialize.js.map +1 -0
- package/dist/version.d.ts +7 -0
- package/dist/version.js +21 -0
- package/dist/version.js.map +1 -0
- package/package.json +35 -3
package/README.md
CHANGED
|
@@ -1,3 +1,92 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @atlasauth/cli
|
|
2
2
|
|
|
3
|
-
|
|
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
|
package/dist/args.js.map
ADDED
|
@@ -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
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
|
package/dist/cli.js.map
ADDED
|
@@ -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,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
|