seamless-cli 0.5.2 → 0.7.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 (59) hide show
  1. package/README.md +171 -0
  2. package/dist/commands/adminShared.js +23 -0
  3. package/dist/commands/adminShared.js.map +1 -0
  4. package/dist/commands/config.js +187 -0
  5. package/dist/commands/config.js.map +1 -0
  6. package/dist/commands/help.js +98 -0
  7. package/dist/commands/help.js.map +1 -1
  8. package/dist/commands/init.js +182 -26
  9. package/dist/commands/init.js.map +1 -1
  10. package/dist/commands/login.js +86 -0
  11. package/dist/commands/login.js.map +1 -0
  12. package/dist/commands/logout.js +43 -0
  13. package/dist/commands/logout.js.map +1 -0
  14. package/dist/commands/org.js +249 -0
  15. package/dist/commands/org.js.map +1 -0
  16. package/dist/commands/profile.js +150 -0
  17. package/dist/commands/profile.js.map +1 -0
  18. package/dist/commands/sessions.js +112 -0
  19. package/dist/commands/sessions.js.map +1 -0
  20. package/dist/commands/users.js +147 -0
  21. package/dist/commands/users.js.map +1 -0
  22. package/dist/commands/verify.js +133 -9
  23. package/dist/commands/verify.js.map +1 -1
  24. package/dist/commands/whoami.js +29 -0
  25. package/dist/commands/whoami.js.map +1 -0
  26. package/dist/core/admin.js +142 -0
  27. package/dist/core/admin.js.map +1 -0
  28. package/dist/core/args.js +19 -0
  29. package/dist/core/args.js.map +1 -0
  30. package/dist/core/authClient.js +113 -0
  31. package/dist/core/authClient.js.map +1 -0
  32. package/dist/core/config.js +146 -0
  33. package/dist/core/config.js.map +1 -0
  34. package/dist/core/http.js +34 -0
  35. package/dist/core/http.js.map +1 -0
  36. package/dist/core/images.js +1 -1
  37. package/dist/core/keychain.js +112 -0
  38. package/dist/core/keychain.js.map +1 -0
  39. package/dist/core/loginFlow.js +143 -0
  40. package/dist/core/loginFlow.js.map +1 -0
  41. package/dist/core/output.js +46 -0
  42. package/dist/core/output.js.map +1 -1
  43. package/dist/core/portal.js +74 -0
  44. package/dist/core/portal.js.map +1 -0
  45. package/dist/core/session.js +30 -0
  46. package/dist/core/session.js.map +1 -0
  47. package/dist/core/sessions.js +41 -0
  48. package/dist/core/sessions.js.map +1 -0
  49. package/dist/core/systemConfig.js +125 -0
  50. package/dist/core/systemConfig.js.map +1 -0
  51. package/dist/generators/config/config.js +30 -15
  52. package/dist/generators/config/config.js.map +1 -1
  53. package/dist/index.js +51 -3
  54. package/dist/index.js.map +1 -1
  55. package/dist/prompts/appSelect.js +41 -0
  56. package/dist/prompts/appSelect.js.map +1 -0
  57. package/dist/prompts/projectSetup.js +26 -0
  58. package/dist/prompts/projectSetup.js.map +1 -1
  59. package/package.json +13 -2
package/README.md CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  [![License: AGPL-3.0-only](https://img.shields.io/badge/License-AGPL3-yellow.svg)](LICENSE)
4
4
  [![npm version](https://img.shields.io/npm/v/seamless-cli.svg?style=flat)](https://www.npmjs.com/package/seamless-cli)
5
+ ![coverage](resources/coverage-badge.svg)
5
6
 
6
7
  Seamless CLI is a command-line tool for bootstrapping applications with Seamless Auth, an open source, passwordless authentication system.
7
8
 
@@ -32,6 +33,43 @@ You’ll be guided through a short setup process where you can choose:
32
33
 
33
34
  ---
34
35
 
36
+ ## Connecting to a managed instance
37
+
38
+ If you are logged in to a managed Seamless Auth account (see
39
+ [Authenticating against an instance](#authenticating-against-an-instance)), `init` connects the new
40
+ project to your managed instance instead of scaffolding a local auth server. This is the default
41
+ whenever a profile has an active session.
42
+
43
+ ```bash
44
+ seamless login # once, against your managed profile
45
+ seamless init my-app # scaffolds web + api wired to the managed instance
46
+ ```
47
+
48
+ What happens:
49
+
50
+ - The CLI lists your applications from the control plane and, when you have more than one, asks
51
+ which to connect. Skip the prompt with `--app <id>` (an application id or infra id).
52
+ - It issues the application's service token from the control plane (the real credential, not a
53
+ locally generated secret) and writes it, the managed auth server URL, and the JWKS key id into
54
+ `api/.env`. The frontend is pointed at the same auth server URL.
55
+ - No local auth server, Docker Compose, or admin dashboard is generated. Auth, users, and OAuth
56
+ providers are managed from the dashboard.
57
+
58
+ Because the service token is shown only once at issue time, `init` confirms before issuing a new
59
+ one for an application that already has a token (issuing a new token invalidates the old one).
60
+
61
+ Run inside an existing project (a non-empty directory) to wire it up in place: `init` updates
62
+ `api/.env` when an `api` directory is present, otherwise it prints the values to set by hand. Your
63
+ own source is never overwritten.
64
+
65
+ Escape hatches:
66
+
67
+ - `seamless init --local` forces the self-hosted flow below, even when logged in.
68
+ - With no active session, `init` uses the self-hosted flow automatically.
69
+ - `SEAMLESS_PORTAL_API_URL` overrides the control-plane host (defaults to the managed service).
70
+
71
+ ---
72
+
35
73
  ## What gets created
36
74
 
37
75
  Depending on your selections, the CLI generates a project like this:
@@ -120,6 +158,139 @@ npm run dev
120
158
 
121
159
  ---
122
160
 
161
+ ## Authenticating against an instance
162
+
163
+ Beyond scaffolding, the CLI can log in to a Seamless Auth instance (self-hosted, a managed
164
+ tenant, or local dev) and call its authenticated and admin endpoints from the terminal. It talks
165
+ to the instance directly over Bearer and JSON, so no server or contract changes are required.
166
+
167
+ ### Profiles
168
+
169
+ The CLI targets instances through named profiles stored at `~/.config/seamless/config.json`
170
+ (respecting `XDG_CONFIG_HOME`). Profiles hold no secrets; tokens live in the OS keychain (see
171
+ below). Pick the active profile per command with `--profile <name>` or the `SEAMLESS_PROFILE`
172
+ environment variable, otherwise the `default` profile is used.
173
+
174
+ ```bash
175
+ # Add a profile (prompts if you omit the flags)
176
+ seamless profile add prod --instance-url https://auth.example.com
177
+ seamless profile add local --instance-url http://localhost:5312
178
+
179
+ seamless profile list # active profile is marked with *
180
+ seamless profile use local # switch the active profile
181
+ seamless profile remove local # delete a profile (also clears its keychain tokens)
182
+ ```
183
+
184
+ `instanceUrl` is normalized: the trailing slash is stripped and `https` is required for any host
185
+ other than `localhost`, `127.0.0.1`, or `::1`.
186
+
187
+ ### Logging in
188
+
189
+ Login uses email OTP: you paste the code from your inbox, so nothing needs to be delivered to the
190
+ CLI and no service token is required.
191
+
192
+ ```bash
193
+ seamless login # prompts for the identifier, then the code
194
+ seamless login you@example.com # identifier as an argument
195
+ seamless login --identifier you@example.com --profile prod
196
+ ```
197
+
198
+ The command honors the instance's advertised login methods, caps local retries so it does not trip
199
+ the OTP rate limiter, and refreshes the code automatically if the five minute window lapses.
200
+
201
+ ### Identity and sessions
202
+
203
+ ```bash
204
+ seamless whoami # sub, email, roles, active profile, and instance URL
205
+ seamless logout # end the current session and clear local tokens
206
+ seamless logout --all # revoke every session for the user, then clear local tokens
207
+
208
+ seamless sessions # list active sessions (current one marked)
209
+ seamless sessions revoke <id> # revoke one session (confirms if it is the current one)
210
+ seamless sessions revoke --all # revoke every session (confirms, then clears local tokens)
211
+ ```
212
+
213
+ ### Configuration as code
214
+
215
+ Read and write the instance system configuration (requires an admin role). This turns the
216
+ dashboard's config panel into something you can version, diff, and apply in CI.
217
+
218
+ ```bash
219
+ seamless config get # print the whole config
220
+ seamless config get access_token_ttl # print a single key
221
+ seamless config get --json > config.json # capture as JSON
222
+
223
+ # Values are parsed as JSON, falling back to a string, so every shape works:
224
+ seamless config set access_token_ttl 15m
225
+ seamless config set rate_limit 250
226
+ seamless config set passkey_login_fallback_enabled false
227
+ seamless config set login_methods '["email_otp","passkey"]'
228
+
229
+ seamless config roles # list the instance's roles
230
+
231
+ seamless config diff config.json # show how a local file differs from the instance
232
+ seamless config apply config.json --dry-run # preview the delta
233
+ seamless config apply config.json # apply after a confirmation prompt
234
+ ```
235
+
236
+ `apply` sends only the changed keys and ignores read-only or unknown keys, so a full config
237
+ captured with `config get --json` can be edited and applied directly. Token TTLs, origins, login
238
+ methods, and the WebAuthn RP id are all readable and writable.
239
+
240
+ ### Admin: users and organizations
241
+
242
+ Manage users and organizations from the terminal (requires an admin role).
243
+
244
+ ```bash
245
+ seamless users list --limit 50 --offset 0
246
+ seamless users delete <id> # confirms first
247
+ seamless users credentials <id> # registered credentials for a user
248
+ seamless users prepare-device-replacement <id> # admin-assisted recovery
249
+
250
+ seamless org list
251
+ seamless org create "Acme Inc" --slug acme
252
+ seamless org get <id>
253
+ seamless org update <id> --name "Acme" --slug acme
254
+
255
+ seamless org members list <orgId>
256
+ seamless org members add <orgId> --email person@example.com --roles member,billing
257
+ seamless org members update <orgId> <userId> --roles admin
258
+ seamless org members remove <orgId> <userId>
259
+ ```
260
+
261
+ A non-admin user receives a clear permission error rather than a stack trace.
262
+
263
+ ### Token storage and the keychain
264
+
265
+ The refresh token is the durable secret, so sessions are stored in the operating system keychain,
266
+ never in a plaintext file:
267
+
268
+ - macOS: Keychain
269
+ - Windows: Credential Manager
270
+ - Linux: Secret Service (libsecret, for example GNOME Keyring or KWallet)
271
+
272
+ Tokens are keyed per profile (by name and instance URL) so multiple instances never collide, and
273
+ they are removed when you log out or remove the profile. Access tokens are refreshed transparently:
274
+ on a `401` the CLI rotates the pair via `/refresh`, stores the new tokens, and retries once. A
275
+ rotated or reused refresh token clears the local session and prompts a fresh login.
276
+
277
+ ### Headless and CI
278
+
279
+ When no OS keychain is available (for example a headless CI runner), the CLI does not fall back to
280
+ a plaintext file. Instead, set `SEAMLESS_REFRESH_TOKEN` to a valid refresh token and the CLI will
281
+ use it to obtain an access token for the run:
282
+
283
+ ```bash
284
+ export SEAMLESS_PROFILE=prod
285
+ export SEAMLESS_REFRESH_TOKEN=<refresh-token>
286
+ seamless whoami
287
+ ```
288
+
289
+ Because `/refresh` rotates the refresh token on every call, this path is best for a single
290
+ invocation; the rotated token is held only in memory for that process.
291
+
292
+ ---
293
+
123
294
  ## What is configured for you
124
295
 
125
296
  Seamless CLI handles the parts that are usually difficult to get right:
@@ -0,0 +1,23 @@
1
+ import kleur from "kleur";
2
+ import { ReauthRequiredError } from "../core/authClient.js";
3
+ import { AdminApiError, PermissionError } from "../core/admin.js";
4
+ export function reportAdminError(err) {
5
+ if (err instanceof ReauthRequiredError) {
6
+ console.log(kleur.yellow(err.message));
7
+ process.exit(1);
8
+ }
9
+ if (err instanceof PermissionError || err instanceof AdminApiError) {
10
+ console.error(kleur.red(err.message));
11
+ process.exit(1);
12
+ }
13
+ throw err;
14
+ }
15
+ export function parseList(value) {
16
+ if (value === undefined)
17
+ return undefined;
18
+ return value
19
+ .split(",")
20
+ .map((item) => item.trim())
21
+ .filter((item) => item.length > 0);
22
+ }
23
+ //# sourceMappingURL=adminShared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminShared.js","sourceRoot":"","sources":["../../src/commands/adminShared.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAElE,MAAM,UAAU,gBAAgB,CAAC,GAAY;IAC3C,IAAI,GAAG,YAAY,mBAAmB,EAAE,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,IAAI,GAAG,YAAY,eAAe,IAAI,GAAG,YAAY,aAAa,EAAE,CAAC;QACnE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;QACtC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,GAAG,CAAC;AACZ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAc;IACtC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,OAAO,KAAK;SACT,KAAK,CAAC,GAAG,CAAC;SACV,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,187 @@
1
+ import fs from "fs";
2
+ import { confirm, isCancel } from "@clack/prompts";
3
+ import kleur from "kleur";
4
+ import { extractFlag } from "../core/args.js";
5
+ import { createAuthClient, ReauthRequiredError } from "../core/authClient.js";
6
+ import { ConfigApiError, diffConfig, filterWritable, getRoles, getSystemConfig, parseValue, patchSystemConfig, PermissionError, } from "../core/systemConfig.js";
7
+ export async function runConfig(args) {
8
+ const sub = args[0];
9
+ const { value: profileFlag, rest } = extractFlag(args.slice(1), "profile");
10
+ try {
11
+ const client = await createAuthClient({ profileFlag });
12
+ switch (sub) {
13
+ case "get":
14
+ await configGet(client, rest);
15
+ return;
16
+ case "set":
17
+ await configSet(client, rest);
18
+ return;
19
+ case "roles":
20
+ await configRoles(client, rest);
21
+ return;
22
+ case "diff":
23
+ await configDiff(client, rest);
24
+ return;
25
+ case "apply":
26
+ await configApply(client, rest);
27
+ return;
28
+ default:
29
+ console.error(kleur.red(`Unknown config subcommand: ${sub ?? "(none)"}`));
30
+ console.log("Usage: seamless config <get|set|roles|diff|apply>");
31
+ process.exit(1);
32
+ }
33
+ }
34
+ catch (err) {
35
+ if (err instanceof ReauthRequiredError) {
36
+ console.log(kleur.yellow(err.message));
37
+ process.exit(1);
38
+ }
39
+ if (err instanceof PermissionError || err instanceof ConfigApiError) {
40
+ console.error(kleur.red(err.message));
41
+ process.exit(1);
42
+ }
43
+ throw err;
44
+ }
45
+ }
46
+ async function configGet(client, rest) {
47
+ const json = rest.includes("--json");
48
+ const key = rest.find((arg) => !arg.startsWith("--"));
49
+ const config = await getSystemConfig(client);
50
+ if (key) {
51
+ if (!(key in config)) {
52
+ console.error(kleur.red(`No such config key: ${key}`));
53
+ process.exit(1);
54
+ }
55
+ const value = config[key];
56
+ console.log(json
57
+ ? JSON.stringify(value, null, 2)
58
+ : typeof value === "string"
59
+ ? value
60
+ : JSON.stringify(value, null, 2));
61
+ return;
62
+ }
63
+ if (json) {
64
+ console.log(JSON.stringify(config, null, 2));
65
+ return;
66
+ }
67
+ printConfig(config);
68
+ }
69
+ async function configSet(client, rest) {
70
+ const positional = rest.filter((arg) => !arg.startsWith("--"));
71
+ const [key, ...valueParts] = positional;
72
+ if (!key || valueParts.length === 0) {
73
+ console.error(kleur.red("Usage: seamless config set <key> <value>"));
74
+ process.exit(1);
75
+ }
76
+ const value = parseValue(valueParts.join(" "));
77
+ const result = await patchSystemConfig(client, { [key]: value });
78
+ if (result.updatedKeys.length) {
79
+ console.log(kleur.green(`Updated: ${result.updatedKeys.join(", ")}`));
80
+ }
81
+ else {
82
+ console.log(kleur.dim("No changes."));
83
+ }
84
+ }
85
+ async function configRoles(client, rest) {
86
+ const json = rest.includes("--json");
87
+ const roles = await getRoles(client);
88
+ if (json) {
89
+ console.log(JSON.stringify(roles, null, 2));
90
+ return;
91
+ }
92
+ if (roles.length === 0) {
93
+ console.log(kleur.dim("No roles."));
94
+ return;
95
+ }
96
+ for (const role of roles) {
97
+ console.log(" " + role);
98
+ }
99
+ }
100
+ async function configDiff(client, rest) {
101
+ const file = rest.find((arg) => !arg.startsWith("--"));
102
+ if (!file) {
103
+ console.error(kleur.red("Usage: seamless config diff <file>"));
104
+ process.exit(1);
105
+ }
106
+ const local = readConfigFile(file);
107
+ const remote = await getSystemConfig(client);
108
+ const changes = diffConfig(local, remote);
109
+ if (changes.length === 0) {
110
+ console.log(kleur.green("In sync. No differences."));
111
+ return;
112
+ }
113
+ printChanges(changes);
114
+ }
115
+ async function configApply(client, rest) {
116
+ const dryRun = rest.includes("--dry-run");
117
+ const file = rest.find((arg) => !arg.startsWith("--"));
118
+ if (!file) {
119
+ console.error(kleur.red("Usage: seamless config apply <file> [--dry-run]"));
120
+ process.exit(1);
121
+ }
122
+ const local = readConfigFile(file);
123
+ const { patch, dropped } = filterWritable(local);
124
+ if (dropped.length) {
125
+ console.log(kleur.dim(`Ignoring read-only or unknown keys: ${dropped.join(", ")}`));
126
+ }
127
+ const remote = await getSystemConfig(client);
128
+ const changes = diffConfig(patch, remote);
129
+ if (changes.length === 0) {
130
+ console.log(kleur.green("Already in sync. Nothing to apply."));
131
+ return;
132
+ }
133
+ printChanges(changes);
134
+ if (dryRun) {
135
+ console.log(kleur.dim("Dry run: no changes applied."));
136
+ return;
137
+ }
138
+ const proceed = await confirm({
139
+ message: `Apply ${changes.length} change${changes.length === 1 ? "" : "s"} to ${client.profile.instanceUrl}?`,
140
+ initialValue: false,
141
+ });
142
+ if (isCancel(proceed) || !proceed) {
143
+ console.log("Cancelled.");
144
+ return;
145
+ }
146
+ const body = Object.fromEntries(changes.map((change) => [change.key, change.to]));
147
+ const result = await patchSystemConfig(client, body);
148
+ console.log(kleur.green(`Applied. Updated: ${result.updatedKeys.join(", ") || "(none reported)"}`));
149
+ }
150
+ function readConfigFile(file) {
151
+ let raw;
152
+ try {
153
+ raw = fs.readFileSync(file, "utf-8");
154
+ }
155
+ catch {
156
+ throw new ConfigApiError(`Could not read file: ${file}`);
157
+ }
158
+ let parsed;
159
+ try {
160
+ parsed = JSON.parse(raw);
161
+ }
162
+ catch {
163
+ throw new ConfigApiError(`${file} is not valid JSON.`);
164
+ }
165
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
166
+ throw new ConfigApiError(`${file} must contain a JSON object.`);
167
+ }
168
+ return parsed;
169
+ }
170
+ function inline(value) {
171
+ return value === undefined ? "(unset)" : JSON.stringify(value);
172
+ }
173
+ function printConfig(config) {
174
+ const keys = Object.keys(config).sort();
175
+ const width = keys.reduce((max, key) => Math.max(max, key.length), 0);
176
+ for (const key of keys) {
177
+ console.log(kleur.dim(key.padEnd(width) + " ") + inline(config[key]));
178
+ }
179
+ }
180
+ function printChanges(changes) {
181
+ for (const change of changes) {
182
+ console.log(kleur.bold(change.key));
183
+ console.log(" " + kleur.red(`- ${inline(change.from)}`));
184
+ console.log(" " + kleur.green(`+ ${inline(change.to)}`));
185
+ }
186
+ }
187
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -13,6 +13,16 @@ USAGE
13
13
  seamless check
14
14
  seamless bootstrap-admin [email]
15
15
  seamless verify [--api-only] [--filter=<flow>] [--keep-up]
16
+ seamless profile <list|add|use|remove>
17
+ seamless login [identifier] [--identifier <email>] [--profile <name>]
18
+ seamless whoami [--profile <name>]
19
+ seamless logout [--all] [--profile <name>]
20
+ seamless sessions [list]
21
+ seamless sessions revoke <id | --all>
22
+ seamless config <get|set|roles|diff|apply>
23
+ seamless users <list|delete|credentials|prepare-device-replacement>
24
+ seamless org <list|create|get|update>
25
+ seamless org members <list|add|update|remove>
16
26
  seamless --help
17
27
  seamless --version
18
28
 
@@ -35,6 +45,94 @@ COMMANDS
35
45
  GitLab) and wires the ones you configure into the auth server
36
46
  • Run an unknown flag to see the available examples
37
47
 
48
+ profile <list|add|use|remove>
49
+ Manage the Seamless Auth instances the CLI targets, stored as named
50
+ profiles in ~/.config/seamless/config.json (respects XDG_CONFIG_HOME).
51
+
52
+ profile list
53
+ • Show configured profiles; the active one is marked with *
54
+
55
+ profile add <name> --instance-url <url> [--identifier-type email|phone]
56
+ • Create or update a profile (prompts interactively if flags are omitted)
57
+
58
+ profile use <name>
59
+ • Switch the active profile for subsequent commands
60
+
61
+ profile remove <name>
62
+ • Delete a profile
63
+
64
+ The active profile can also be chosen per command with --profile <name> or
65
+ the SEAMLESS_PROFILE environment variable.
66
+
67
+ login [identifier]
68
+ Log in to the active profile's Seamless Auth instance using email OTP.
69
+ Prompts for the identifier (or pass it positionally or with --identifier)
70
+ and the code sent to your inbox, then stores the session in the OS keychain.
71
+
72
+ --profile <name>
73
+ • Log in against a specific profile instead of the active one
74
+ • Also selectable with the SEAMLESS_PROFILE environment variable
75
+
76
+ whoami
77
+ Show the identity behind the active profile's session (sub, email, roles),
78
+ alongside the profile name and instance URL. Fails cleanly if not logged in.
79
+
80
+ logout [--all]
81
+ End the current session on the instance and clear the local keychain tokens.
82
+ --all revokes every session for the user before clearing local tokens.
83
+
84
+ sessions [list]
85
+ List the active sessions for the logged-in user, with the current session
86
+ marked. Shows the session id, device or user agent, IP, and last-used time.
87
+
88
+ sessions revoke <id | --all>
89
+ Revoke one session by id, or every session with --all. Revoking the current
90
+ session (or --all) prompts for confirmation and then clears local tokens.
91
+
92
+ config <get|set|roles|diff|apply>
93
+ Read and write the instance system configuration (requires an admin role).
94
+
95
+ config get [key] [--json]
96
+ • Print the whole config or a single key
97
+
98
+ config set <key> <value>
99
+ • Update one key; the value is parsed as JSON, falling back to a string
100
+ (for example: config set access_token_ttl 15m,
101
+ config set login_methods '["email_otp","passkey"]')
102
+
103
+ config roles [--json]
104
+ • List the instance's available roles
105
+
106
+ config diff <file>
107
+ • Show how a local JSON config file differs from the instance
108
+
109
+ config apply <file> [--dry-run]
110
+ • Apply a local JSON config file after a confirmation prompt
111
+
112
+ users <list|delete|credentials|prepare-device-replacement>
113
+ Admin user management (requires an admin role).
114
+
115
+ users list [--limit <n>] [--offset <n>] [--json]
116
+ • List users
117
+ users delete <id>
118
+ • Delete a user (asks for confirmation)
119
+ users credentials <id> [--json]
120
+ • Show a user's registered credentials
121
+ users prepare-device-replacement <id> [--keep-sessions] [--keep-passkeys] [--keep-totp]
122
+ • Admin-assisted account recovery (needs an elevated session)
123
+
124
+ org <list|create|get|update>, org members <list|add|update|remove>
125
+ Admin organization management (requires an admin role).
126
+
127
+ org list [--json]
128
+ org create <name> [--slug <slug>]
129
+ org get <id> [--json]
130
+ org update <id> [--name <name>] [--slug <slug>]
131
+ org members list <orgId> [--json]
132
+ org members add <orgId> (--user <id> | --email <email>) [--roles a,b] [--scopes a,b]
133
+ org members update <orgId> <userId> [--roles a,b] [--scopes a,b]
134
+ org members remove <orgId> <userId>
135
+
38
136
  check
39
137
  Validate project setup, Docker, and running services
40
138
 
@@ -1 +1 @@
1
- {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,SAAS;IACvB,OAAO,CAAC,GAAG,CAAC;YACF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoHlB,CAAC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/commands/help.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,UAAU,SAAS;IACvB,OAAO,CAAC,GAAG,CAAC;YACF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsNlB,CAAC,CAAC;AACH,CAAC"}