@nebulr-group/bridge-cli 0.4.4 → 0.5.1-beta.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 +67 -6
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -1
- package/dist/cli.js.map +1 -1
- package/dist/commands/auth/logout.command.d.ts +10 -3
- package/dist/commands/auth/logout.command.d.ts.map +1 -1
- package/dist/commands/auth/logout.command.js +58 -18
- package/dist/commands/auth/logout.command.js.map +1 -1
- package/dist/commands/auth/status.command.d.ts +4 -0
- package/dist/commands/auth/status.command.d.ts.map +1 -1
- package/dist/commands/auth/status.command.js +50 -19
- package/dist/commands/auth/status.command.js.map +1 -1
- package/dist/commands/auth/use.command.d.ts +17 -0
- package/dist/commands/auth/use.command.d.ts.map +1 -0
- package/dist/commands/auth/use.command.js +47 -0
- package/dist/commands/auth/use.command.js.map +1 -0
- package/dist/commands/auth.command.d.ts +1 -0
- package/dist/commands/auth.command.d.ts.map +1 -1
- package/dist/commands/auth.command.js +3 -0
- package/dist/commands/auth.command.js.map +1 -1
- package/dist/commands/setup.command.d.ts +60 -0
- package/dist/commands/setup.command.d.ts.map +1 -1
- package/dist/commands/setup.command.js +129 -28
- package/dist/commands/setup.command.js.map +1 -1
- package/dist/commands/stripe.command.d.ts +36 -0
- package/dist/commands/stripe.command.d.ts.map +1 -1
- package/dist/commands/stripe.command.js +70 -14
- package/dist/commands/stripe.command.js.map +1 -1
- package/dist/config.d.ts +20 -13
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +120 -32
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +68 -5
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +202 -11
- package/dist/credentials.js.map +1 -1
- package/dist/prompts/billing/master.md +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,10 +27,48 @@ bridge auth login
|
|
|
27
27
|
Opens your default browser, runs through a PKCE-secured loopback flow (RFC 8252), and stores a 10-day token at `~/.config/bridge/credentials.json` (mode `0600`). After that, every subsequent `bridge` command picks the token up automatically — no env vars needed.
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
bridge auth status # show
|
|
31
|
-
bridge auth logout # revoke the token and
|
|
30
|
+
bridge auth status # show every stored app, and which one is active
|
|
31
|
+
bridge auth logout # revoke the active token and remove it locally
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
#### Several apps at once
|
|
35
|
+
|
|
36
|
+
The credentials file holds one credential per app, so you can log in to your
|
|
37
|
+
local, stage and prod apps and move between them without a browser round-trip —
|
|
38
|
+
the tokens are already on disk with days left on them.
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bridge auth login --label northwhistle-local # once per app
|
|
42
|
+
bridge auth login --label northwhistle-prod
|
|
43
|
+
|
|
44
|
+
bridge auth status # lists both, marks the active one
|
|
45
|
+
bridge auth use northwhistle-local # switch the default, offline
|
|
46
|
+
bridge --profile northwhistle-prod app get # target ONE command, default untouched
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
A profile is selected by its `--label`, its app id, or its app name, in that
|
|
50
|
+
order. An ambiguous name is an error rather than a guess. `BRIDGE_PROFILE` does
|
|
51
|
+
the same thing as `--profile` for a whole shell session.
|
|
52
|
+
|
|
53
|
+
Prefer `--profile` over `bridge auth use` in scripts and long-running agent
|
|
54
|
+
sessions: `use` mutates state another process may be reading, `--profile` does
|
|
55
|
+
not.
|
|
56
|
+
|
|
57
|
+
Every command prints one line to **stderr** naming the app that answered:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
bridge: northwhistle-prod (606b4416fabdc800087d09ec) · https://api.thebridge.dev · via saved default
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
stdout stays pure JSON, so this does not disturb anything parsing it. Silence it
|
|
64
|
+
with `BRIDGE_NO_BANNER=true` if you are already certain which app you are on.
|
|
65
|
+
|
|
66
|
+
`bridge auth logout` removes the active app only. Use `--profile <name>` for a
|
|
67
|
+
specific one, or `--all` to clear the file.
|
|
68
|
+
|
|
69
|
+
Existing single-app credentials files keep working and are upgraded in place the
|
|
70
|
+
next time something writes.
|
|
71
|
+
|
|
34
72
|
#### `bridge auth login` flags
|
|
35
73
|
|
|
36
74
|
| Flag | Description |
|
|
@@ -56,7 +94,17 @@ For non-interactive contexts (CI/CD pipelines, Docker images, headless agents) s
|
|
|
56
94
|
export BRIDGE_API_KEY=<your-api-token>
|
|
57
95
|
```
|
|
58
96
|
|
|
59
|
-
If you have logged in via `bridge auth login`, the credentials file always wins — a fresh login takes effect immediately, even when `BRIDGE_API_KEY` is still exported in your shell. `BRIDGE_API_KEY` is only used when no credentials file is present (the typical CI runner shape). To switch back to env-var auth on a developer machine, run `bridge auth logout` first.
|
|
97
|
+
If you have logged in via `bridge auth login`, the credentials file always wins — a fresh login takes effect immediately, even when `BRIDGE_API_KEY` is still exported in your shell. `BRIDGE_API_KEY` is only used when no credentials file is present (the typical CI runner shape). To switch back to env-var auth on a developer machine, run `bridge auth logout --all` first.
|
|
98
|
+
|
|
99
|
+
Because that trips people up, the CLI now says so at the moment it happens: with
|
|
100
|
+
both present you get a line on stderr telling you `BRIDGE_API_KEY` is set and
|
|
101
|
+
being ignored, and what to do instead. To point the CLI at a different app, use
|
|
102
|
+
`--profile`, not the env vars.
|
|
103
|
+
|
|
104
|
+
**`BRIDGE_APP_ID` has no effect on any path.** The app is carried by the
|
|
105
|
+
credential itself — a login token and an API key are both already scoped to one
|
|
106
|
+
app, so there is nothing for it to change. It is called out on stderr whenever it
|
|
107
|
+
is set, rather than being quietly ignored.
|
|
60
108
|
|
|
61
109
|
Optional configuration (applies to both auth paths):
|
|
62
110
|
|
|
@@ -139,8 +187,11 @@ bridge auth login # default: open browser, complete PKCE flow
|
|
|
139
187
|
bridge auth login --app acme # pin to a specific app
|
|
140
188
|
bridge auth login --label "work laptop"
|
|
141
189
|
bridge auth login --no-browser # print the URL (headless / SSH)
|
|
142
|
-
bridge auth status # show
|
|
143
|
-
bridge auth
|
|
190
|
+
bridge auth status # show every stored app, active one marked
|
|
191
|
+
bridge auth use <label|app-id> # switch the default app, no browser
|
|
192
|
+
bridge auth logout # revoke + remove the active app
|
|
193
|
+
bridge auth logout --all # ...or every stored app
|
|
194
|
+
bridge --profile <label> <cmd> # target one app for one command
|
|
144
195
|
```
|
|
145
196
|
|
|
146
197
|
### Auth Configuration (app-level — separate from `auth login`)
|
|
@@ -268,10 +319,20 @@ bridge event list --type USER_CREATED --since 24h --limit 50
|
|
|
268
319
|
|
|
269
320
|
```bash
|
|
270
321
|
bridge setup sso --provider google --client-id <id> --client-secret <secret>
|
|
322
|
+
bridge setup sso --provider azure --client-id <id> --client-secret <secret> --tenant-id <entra-directory-id>
|
|
271
323
|
bridge setup payments --stripe-key sk_test_xxx
|
|
272
|
-
bridge setup communication --
|
|
324
|
+
bridge setup communication --from-address noreply@acme.com --from-name "Acme"
|
|
273
325
|
```
|
|
274
326
|
|
|
327
|
+
`setup sso` supports google, github, linkedin, facebook and azure (azure also
|
|
328
|
+
needs the Entra ID Directory (tenant) ID). SAML and OIDC are not supported by
|
|
329
|
+
the Bridge API; set them up in the Bridge admin UI. The command prints the
|
|
330
|
+
callback URL to register in the provider's console.
|
|
331
|
+
|
|
332
|
+
`setup communication` sets only the sender name and address. Bridge sends all
|
|
333
|
+
email through its own provider, so there is no provider or API key to
|
|
334
|
+
configure. A new sender address is verified by email before Bridge uses it.
|
|
335
|
+
|
|
275
336
|
### Info (context for AI agents)
|
|
276
337
|
|
|
277
338
|
```bash
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAyBpC,eAAO,MAAM,OAAO,SAAgB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { registerInfoCommands } from './commands/info.command.js';
|
|
|
18
18
|
import { registerGuideCommands } from './commands/guide.command.js';
|
|
19
19
|
import { registerOpsCommands } from './commands/ops.command.js';
|
|
20
20
|
import { registerStripeCommands } from './commands/stripe.command.js';
|
|
21
|
+
import { setProfileOverride } from './config.js';
|
|
21
22
|
// Read version from package.json so `bridge --version` never drifts from the
|
|
22
23
|
// published package. dist/cli.js lives at <pkg>/dist/cli.js, so `../package.json`
|
|
23
24
|
// resolves to the package root in both the published tarball and during dev.
|
|
@@ -27,7 +28,18 @@ export const program = new Command();
|
|
|
27
28
|
program
|
|
28
29
|
.name('bridge')
|
|
29
30
|
.description('Bridge platform CLI — optimized for AI coding agents')
|
|
30
|
-
.version(version)
|
|
31
|
+
.version(version)
|
|
32
|
+
// TBP-628 — target another stored app for ONE command, without touching the
|
|
33
|
+
// persisted default another process may be reading. Named `--profile` rather
|
|
34
|
+
// than `--app` because `bridge auth login --app` already exists and means
|
|
35
|
+
// something different (which app to authorise), and because `--profile` is
|
|
36
|
+
// what aws/npm users already reach for.
|
|
37
|
+
.option('--profile <label|app-id|app-name>', 'Use a specific stored credential for this command (env: BRIDGE_PROFILE)');
|
|
38
|
+
// Runs before any subcommand action, so `getManagementClient()` sees the
|
|
39
|
+
// override wherever in the tree it is eventually called from.
|
|
40
|
+
program.hook('preAction', (thisCommand) => {
|
|
41
|
+
setProfileOverride(thisCommand.opts().profile);
|
|
42
|
+
});
|
|
31
43
|
registerAppCommands(program);
|
|
32
44
|
registerTenantCommands(program);
|
|
33
45
|
registerUserCommands(program);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,iCAAiC,CAAC;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,11 +3,18 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Behavior:
|
|
5
5
|
* - No credentials file → "Not logged in." (exit 0).
|
|
6
|
-
* -
|
|
6
|
+
* - Otherwise → call `POST /v1/auth/cli/revoke` with `x-api-key`,
|
|
7
7
|
* treat 401 as success (token already gone), then
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* remove it locally. Network errors → warn but still
|
|
9
|
+
* remove (the local file is the source of truth for
|
|
10
10
|
* "logged in" on this machine).
|
|
11
|
+
*
|
|
12
|
+
* TBP-628 — the file can hold several apps, so logout now has a scope.
|
|
13
|
+
* Bare `logout` removes the ACTIVE credential and leaves the others; the
|
|
14
|
+
* alternative, wiping every app because you finished with one of them, throws
|
|
15
|
+
* away valid tokens and forces browser round-trips to get them back, which is
|
|
16
|
+
* the friction this ticket exists to remove. `--profile` names one, `--all`
|
|
17
|
+
* asks for the old whole-file behaviour explicitly.
|
|
11
18
|
*/
|
|
12
19
|
import type { Command } from 'commander';
|
|
13
20
|
export declare function registerAuthLogoutCommand(auth: Command): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.command.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"logout.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/logout.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAczC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAgB7D"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { credentialsPath,
|
|
1
|
+
import { credentialsPath, CredentialSelectorError, deleteAllCredentials, findCredential, listCredentials, readCredentials, removeCredential, } from '../../credentials.js';
|
|
2
2
|
import { resetManagementClient } from '../../config.js';
|
|
3
3
|
import { createCliApiClient, CliAuthApiError } from '../../auth/api-client.js';
|
|
4
4
|
export function registerAuthLogoutCommand(auth) {
|
|
5
5
|
auth
|
|
6
6
|
.command('logout')
|
|
7
|
-
.description('Revoke the
|
|
8
|
-
.
|
|
7
|
+
.description('Revoke the active CLI token and remove it locally')
|
|
8
|
+
.option('--profile <label|app-id|app-name>', 'Log out of one specific stored app')
|
|
9
|
+
.option('--all', 'Log out of every stored app and delete the credentials file')
|
|
10
|
+
.action(async (opts) => {
|
|
9
11
|
try {
|
|
10
|
-
await runLogout();
|
|
12
|
+
await runLogout(opts);
|
|
11
13
|
process.exitCode = 0;
|
|
12
14
|
}
|
|
13
15
|
catch (err) {
|
|
@@ -17,41 +19,79 @@ export function registerAuthLogoutCommand(auth) {
|
|
|
17
19
|
}
|
|
18
20
|
});
|
|
19
21
|
}
|
|
20
|
-
async function runLogout() {
|
|
22
|
+
async function runLogout(opts = {}) {
|
|
21
23
|
const stdout = process.stdout;
|
|
22
24
|
const stderr = process.stderr;
|
|
23
|
-
let
|
|
25
|
+
let entries;
|
|
24
26
|
try {
|
|
25
|
-
|
|
27
|
+
entries = listCredentials();
|
|
26
28
|
}
|
|
27
29
|
catch (err) {
|
|
28
|
-
// Malformed credentials file — still
|
|
30
|
+
// Malformed credentials file — still delete it so the user gets unstuck.
|
|
29
31
|
stderr.write(`Warning: existing credentials file at ${credentialsPath()} could not be parsed (${err instanceof Error ? err.message : String(err)}). Removing it.\n`);
|
|
30
|
-
|
|
32
|
+
deleteAllCredentials();
|
|
31
33
|
stdout.write('Logged out (credentials file removed).\n');
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
34
|
-
if (
|
|
36
|
+
if (entries.length === 0) {
|
|
35
37
|
stdout.write('Not logged in.\n');
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
40
|
+
if (opts.all) {
|
|
41
|
+
for (const entry of entries)
|
|
42
|
+
await revoke(entry.creds, stderr);
|
|
43
|
+
deleteAllCredentials();
|
|
44
|
+
resetManagementClient();
|
|
45
|
+
stdout.write(`Logged out of ${entries.length} app(s).\n`);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let target;
|
|
49
|
+
if (opts.profile) {
|
|
50
|
+
try {
|
|
51
|
+
target = findCredential(opts.profile);
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
if (err instanceof CredentialSelectorError)
|
|
55
|
+
throw new Error(err.message);
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
const active = readCredentials();
|
|
61
|
+
if (!active) {
|
|
62
|
+
// Several stored, none active. Removing one by guess is exactly the
|
|
63
|
+
// silent choice this ticket forbids.
|
|
64
|
+
throw new Error('No active credential. Name one with `--profile <label|app id>`, or use `--all`. ' +
|
|
65
|
+
`Stored: ${entries.map((e) => e.creds.label ?? e.creds.app.name).join(', ')}.`);
|
|
66
|
+
}
|
|
67
|
+
target = entries.find((e) => e.key === active.app.id);
|
|
68
|
+
}
|
|
69
|
+
await revoke(target.creds, stderr);
|
|
70
|
+
const remaining = removeCredential(target.key);
|
|
71
|
+
resetManagementClient();
|
|
72
|
+
const name = target.creds.label ?? target.creds.app.name;
|
|
73
|
+
stdout.write(`Logged out ${target.creds.user.email} from ${name}.\n`);
|
|
74
|
+
if (remaining > 0) {
|
|
75
|
+
const rest = listCredentials();
|
|
76
|
+
const nowActive = rest.find((e) => e.isActive);
|
|
77
|
+
stdout.write(` ${remaining} credential(s) still stored` +
|
|
78
|
+
(nowActive ? `; now using ${nowActive.creds.label ?? nowActive.creds.app.name}` : '') +
|
|
79
|
+
'.\n');
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/** Best-effort server-side revoke. Never fatal — local removal is the point. */
|
|
83
|
+
async function revoke(creds, stderr) {
|
|
38
84
|
const api = createCliApiClient({ baseUrl: creds.baseUrl });
|
|
39
85
|
try {
|
|
40
86
|
await api.revokeToken(creds.apiKey);
|
|
41
87
|
}
|
|
42
88
|
catch (err) {
|
|
43
|
-
// Network failure — warn but still proceed with local deletion.
|
|
44
89
|
if (err instanceof CliAuthApiError && err.code === 'network_error') {
|
|
45
|
-
stderr.write(`Warning: could not reach bridge-api to revoke server-side (${err.message}). Removing local credentials anyway.\n`);
|
|
90
|
+
stderr.write(`Warning: could not reach bridge-api to revoke ${creds.app.name} server-side (${err.message}). Removing local credentials anyway.\n`);
|
|
46
91
|
}
|
|
47
92
|
else {
|
|
48
|
-
|
|
49
|
-
// surface it so the user knows the server-side revoke didn't happen.
|
|
50
|
-
stderr.write(`Warning: server-side revoke returned an error (${err instanceof Error ? err.message : String(err)}). Removing local credentials anyway.\n`);
|
|
93
|
+
stderr.write(`Warning: server-side revoke for ${creds.app.name} returned an error (${err instanceof Error ? err.message : String(err)}). Removing local credentials anyway.\n`);
|
|
51
94
|
}
|
|
52
95
|
}
|
|
53
|
-
deleteCredentials();
|
|
54
|
-
resetManagementClient();
|
|
55
|
-
stdout.write(`Logged out ${creds.user.email}.\n`);
|
|
56
96
|
}
|
|
57
97
|
//# sourceMappingURL=logout.command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logout.command.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"logout.command.js","sourceRoot":"","sources":["../../../src/commands/auth/logout.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
* optimize for readability over machine-parseability.
|
|
9
9
|
*
|
|
10
10
|
* If the user wants JSON, they can run `cat ~/.config/bridge/credentials.json`.
|
|
11
|
+
*
|
|
12
|
+
* TBP-628 — the file can hold several apps, so this lists ALL of them and marks
|
|
13
|
+
* the active one. Knowing which app is in force is the whole reason somebody
|
|
14
|
+
* runs this command, and it was already the only place that told them.
|
|
11
15
|
*/
|
|
12
16
|
import type { Command } from 'commander';
|
|
13
17
|
export declare function registerAuthStatusCommand(auth: Command): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.command.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"status.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/status.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAc7D"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { credentialsPath, isExpired,
|
|
1
|
+
import { credentialsPath, isExpired, listCredentials, readCredentialsFile, } from '../../credentials.js';
|
|
2
2
|
import { formatRelativeTime } from '../../auth/relative-time.js';
|
|
3
3
|
export function registerAuthStatusCommand(auth) {
|
|
4
4
|
auth
|
|
@@ -18,18 +18,20 @@ export function registerAuthStatusCommand(auth) {
|
|
|
18
18
|
}
|
|
19
19
|
function runStatus() {
|
|
20
20
|
const stdout = process.stdout;
|
|
21
|
-
//
|
|
22
|
-
//
|
|
21
|
+
// Report on `BRIDGE_API_KEY` separately if set, so a CI user setting the env
|
|
22
|
+
// var sees something useful.
|
|
23
23
|
const envApiKey = process.env.BRIDGE_API_KEY?.trim();
|
|
24
|
-
|
|
24
|
+
const envProfile = process.env.BRIDGE_PROFILE?.trim();
|
|
25
|
+
let entries;
|
|
25
26
|
try {
|
|
26
|
-
|
|
27
|
+
readCredentialsFile();
|
|
28
|
+
entries = listCredentials();
|
|
27
29
|
}
|
|
28
30
|
catch (err) {
|
|
29
31
|
stdout.write(`Credentials file at ${credentialsPath()} is unreadable: ${err instanceof Error ? err.message : String(err)}\n` + 'Run `bridge auth login` to refresh, or delete the file manually.\n');
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
|
-
if (
|
|
34
|
+
if (entries.length === 0) {
|
|
33
35
|
if (envApiKey && envApiKey.length > 0) {
|
|
34
36
|
stdout.write('Using BRIDGE_API_KEY from environment (service-account / CI path).\n' +
|
|
35
37
|
'Run `bridge auth login` to switch to interactive credentials.\n');
|
|
@@ -38,22 +40,51 @@ function runStatus() {
|
|
|
38
40
|
stdout.write('Not logged in. Run `bridge auth login`.\n');
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
43
|
+
const active = entries.find((e) => e.isActive);
|
|
44
|
+
if (active) {
|
|
45
|
+
stdout.write(`Logged in as ${active.creds.user.email}\n`);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
// A pointer at a credential that is gone. Say so rather than picking one:
|
|
49
|
+
// an inferred default is exactly what this ticket is about.
|
|
50
|
+
stdout.write('No active credential selected — run `bridge auth use <profile>` to pick one.\n');
|
|
51
|
+
}
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
writeEntry(entry, entries.length > 1);
|
|
54
|
+
}
|
|
51
55
|
stdout.write(` credentials=${credentialsPath()}\n`);
|
|
56
|
+
if (entries.length > 1) {
|
|
57
|
+
stdout.write('\nSwitch the default with `bridge auth use <label|app id>`, ' +
|
|
58
|
+
'or target one command with `--profile <label|app id>`.\n');
|
|
59
|
+
}
|
|
60
|
+
if (envProfile) {
|
|
61
|
+
stdout.write(`\nBRIDGE_PROFILE=${envProfile} is set — it overrides the active credential above.\n`);
|
|
62
|
+
}
|
|
52
63
|
if (envApiKey && envApiKey.length > 0) {
|
|
53
|
-
|
|
64
|
+
// This used to claim the opposite. The credentials file has always won;
|
|
65
|
+
// saying otherwise is how somebody comes to believe they retargeted the
|
|
66
|
+
// CLI when they did not (TBP-628).
|
|
67
|
+
stdout.write('\nNote: BRIDGE_API_KEY is set in your environment but is IGNORED while a ' +
|
|
68
|
+
'credentials file exists.\nTo use it, run `bridge auth logout --all`. ' +
|
|
69
|
+
'To target another stored app, use `--profile`.\n');
|
|
54
70
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
71
|
+
if (process.env.BRIDGE_APP_ID?.trim()) {
|
|
72
|
+
stdout.write('\nNote: BRIDGE_APP_ID has no effect. The app is carried by the credential ' +
|
|
73
|
+
'itself; use `--profile <label|app id>` to target another one.\n');
|
|
57
74
|
}
|
|
58
75
|
}
|
|
76
|
+
function writeEntry(entry, showMarker) {
|
|
77
|
+
const stdout = process.stdout;
|
|
78
|
+
const { creds } = entry;
|
|
79
|
+
const expiryDelta = new Date(creds.expiresAt).getTime() - Date.now();
|
|
80
|
+
const expiryRel = formatRelativeTime(expiryDelta);
|
|
81
|
+
const expiredLabel = isExpired(creds) ? ' (EXPIRED)' : '';
|
|
82
|
+
const marker = showMarker ? (entry.isActive ? '* ' : ' ') : '';
|
|
83
|
+
stdout.write(` ${marker}app=${creds.app.name} (${creds.app.id})\n`);
|
|
84
|
+
if (creds.label)
|
|
85
|
+
stdout.write(` ${marker} label=${creds.label}\n`);
|
|
86
|
+
stdout.write(` ${marker} user=${creds.user.email}\n`);
|
|
87
|
+
stdout.write(` ${marker} expires=${expiryRel} — ${creds.expiresAt}${expiredLabel}\n`);
|
|
88
|
+
stdout.write(` ${marker} baseUrl=${creds.baseUrl}\n`);
|
|
89
|
+
}
|
|
59
90
|
//# sourceMappingURL=status.command.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.command.js","sourceRoot":"","sources":["../../../src/commands/auth/status.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"status.command.js","sourceRoot":"","sources":["../../../src/commands/auth/status.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `bridge auth use <profile>` — move the persisted default to another stored
|
|
3
|
+
* credential, without a browser round-trip (TBP-628).
|
|
4
|
+
*
|
|
5
|
+
* The credential is already on disk with days left on it; switching is a
|
|
6
|
+
* pointer move. Before this existed the only way to change apps was
|
|
7
|
+
* `auth logout` + `auth login`, which discarded a perfectly valid token and
|
|
8
|
+
* cost a browser trip, so people did not switch — they ran commands against
|
|
9
|
+
* whichever app happened to be active and read the answers as if they came
|
|
10
|
+
* from the one they meant.
|
|
11
|
+
*
|
|
12
|
+
* For a single command, prefer `--profile` / `BRIDGE_PROFILE`: this command
|
|
13
|
+
* mutates state that other processes read.
|
|
14
|
+
*/
|
|
15
|
+
import type { Command } from 'commander';
|
|
16
|
+
export declare function registerAuthUseCommand(auth: Command): void;
|
|
17
|
+
//# sourceMappingURL=use.command.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/use.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAc1D"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { CredentialSelectorError, findCredential, isExpired, listCredentials, setActiveCredential, } from '../../credentials.js';
|
|
2
|
+
import { resetManagementClient } from '../../config.js';
|
|
3
|
+
export function registerAuthUseCommand(auth) {
|
|
4
|
+
auth
|
|
5
|
+
.command('use <profile>')
|
|
6
|
+
.description('Switch the default app to another stored credential (no re-login)')
|
|
7
|
+
.action((profile) => {
|
|
8
|
+
try {
|
|
9
|
+
runUse(profile);
|
|
10
|
+
process.exitCode = 0;
|
|
11
|
+
}
|
|
12
|
+
catch (err) {
|
|
13
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
14
|
+
process.stderr.write(`bridge auth use: ${msg}\n`);
|
|
15
|
+
process.exitCode = 1;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function runUse(profile) {
|
|
20
|
+
const stdout = process.stdout;
|
|
21
|
+
let entry;
|
|
22
|
+
try {
|
|
23
|
+
entry = findCredential(profile);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
if (err instanceof CredentialSelectorError)
|
|
27
|
+
throw new Error(err.message);
|
|
28
|
+
throw err;
|
|
29
|
+
}
|
|
30
|
+
setActiveCredential(entry.key);
|
|
31
|
+
resetManagementClient();
|
|
32
|
+
const name = entry.creds.label ?? entry.creds.app.name;
|
|
33
|
+
stdout.write(`Now using ${name} — ${entry.creds.app.name} (${entry.creds.app.id})\n`);
|
|
34
|
+
stdout.write(` baseUrl=${entry.creds.baseUrl}\n`);
|
|
35
|
+
// Switching TO an expired credential is allowed — it is the user's stated
|
|
36
|
+
// intent, and refusing would leave them unable to make it the default before
|
|
37
|
+
// re-authenticating it. But it must not be a surprise at the next command.
|
|
38
|
+
if (isExpired(entry.creds)) {
|
|
39
|
+
stdout.write(` WARNING: this credential expired ${entry.creds.expiresAt}. ` +
|
|
40
|
+
'Run `bridge auth login` to refresh it.\n');
|
|
41
|
+
}
|
|
42
|
+
const others = listCredentials().filter((e) => e.key !== entry.key);
|
|
43
|
+
if (others.length > 0) {
|
|
44
|
+
stdout.write(` also stored: ${others.map((e) => e.creds.label ?? e.creds.app.name).join(', ')}\n`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=use.command.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use.command.js","sourceRoot":"","sources":["../../../src/commands/auth/use.command.ts"],"names":[],"mappings":"AAeA,OAAO,EACL,uBAAuB,EACvB,cAAc,EACd,SAAS,EACT,eAAe,EACf,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAExD,MAAM,UAAU,sBAAsB,CAAC,IAAa;IAClD,IAAI;SACD,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,mEAAmE,CAAC;SAChF,MAAM,CAAC,CAAC,OAAe,EAAE,EAAE;QAC1B,IAAI,CAAC;YACH,MAAM,CAAC,OAAO,CAAC,CAAC;YAChB,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAC7D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,oBAAoB,GAAG,IAAI,CAAC,CAAC;YAClD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,MAAM,CAAC,OAAe;IAC7B,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAE9B,IAAI,KAAK,CAAC;IACV,IAAI,CAAC;QACH,KAAK,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,uBAAuB;YAAE,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACzE,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,mBAAmB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/B,qBAAqB,EAAE,CAAC;IAExB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;IACvD,MAAM,CAAC,KAAK,CAAC,aAAa,IAAI,MAAM,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IACtF,MAAM,CAAC,KAAK,CAAC,aAAa,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC;IAEnD,0EAA0E;IAC1E,6EAA6E;IAC7E,2EAA2E;IAC3E,IAAI,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,CAAC,KAAK,CACV,sCAAsC,KAAK,CAAC,KAAK,CAAC,SAAS,IAAI;YAC7D,0CAA0C,CAC7C,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC;IACpE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,CAAC,KAAK,CACV,kBAAkB,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CACtF,CAAC;IACJ,CAAC;AACH,CAAC"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* - `login` (TBP-113): browser-based PKCE auth.
|
|
6
6
|
* - `logout` (TBP-113): revoke + delete credentials.
|
|
7
7
|
* - `status` (TBP-113): show current login state.
|
|
8
|
+
* - `use` (TBP-628): switch the default app, offline.
|
|
8
9
|
* - `config`/`mfa`/`password-policy`: pre-existing app auth-config commands.
|
|
9
10
|
*/
|
|
10
11
|
import type { Command } from 'commander';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.command.d.ts","sourceRoot":"","sources":["../../src/commands/auth.command.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"auth.command.d.ts","sourceRoot":"","sources":["../../src/commands/auth.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOzC,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAe3D"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { registerAuthLoginCommand } from './auth/login.command.js';
|
|
2
2
|
import { registerAuthLogoutCommand } from './auth/logout.command.js';
|
|
3
3
|
import { registerAuthStatusCommand } from './auth/status.command.js';
|
|
4
|
+
import { registerAuthUseCommand } from './auth/use.command.js';
|
|
4
5
|
import { registerAuthConfigCommands } from './auth/config.command.js';
|
|
5
6
|
export function registerAuthCommands(program) {
|
|
6
7
|
const auth = program
|
|
@@ -10,6 +11,8 @@ export function registerAuthCommands(program) {
|
|
|
10
11
|
registerAuthLoginCommand(auth);
|
|
11
12
|
registerAuthLogoutCommand(auth);
|
|
12
13
|
registerAuthStatusCommand(auth);
|
|
14
|
+
// TBP-628 — several apps side by side; switch without a browser round-trip.
|
|
15
|
+
registerAuthUseCommand(auth);
|
|
13
16
|
// Pre-existing — app-level auth configuration.
|
|
14
17
|
registerAuthConfigCommands(auth);
|
|
15
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.command.js","sourceRoot":"","sources":["../../src/commands/auth.command.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.command.js","sourceRoot":"","sources":["../../src/commands/auth.command.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAEtE,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,MAAM,IAAI,GAAG,OAAO;SACjB,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,4DAA4D,CAAC,CAAC;IAE7E,qCAAqC;IACrC,wBAAwB,CAAC,IAAI,CAAC,CAAC;IAC/B,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAChC,yBAAyB,CAAC,IAAI,CAAC,CAAC;IAEhC,4EAA4E;IAC5E,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAE7B,+CAA+C;IAC/C,0BAA0B,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -1,3 +1,63 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
export declare const SSO_PROVIDERS: {
|
|
3
|
+
readonly google: {
|
|
4
|
+
readonly clientId: "googleClientId";
|
|
5
|
+
readonly clientSecret: "googleClientSecret";
|
|
6
|
+
readonly enable: "googleSsoEnabled";
|
|
7
|
+
};
|
|
8
|
+
readonly github: {
|
|
9
|
+
readonly clientId: "githubClientId";
|
|
10
|
+
readonly clientSecret: "githubClientSecret";
|
|
11
|
+
readonly enable: "githubSsoEnabled";
|
|
12
|
+
};
|
|
13
|
+
readonly linkedin: {
|
|
14
|
+
readonly clientId: "linkedinClientId";
|
|
15
|
+
readonly clientSecret: "linkedinClientSecret";
|
|
16
|
+
readonly enable: "linkedinSsoEnabled";
|
|
17
|
+
};
|
|
18
|
+
readonly facebook: {
|
|
19
|
+
readonly clientId: "facebookClientId";
|
|
20
|
+
readonly clientSecret: "facebookClientSecret";
|
|
21
|
+
readonly enable: "facebookSsoEnabled";
|
|
22
|
+
};
|
|
23
|
+
readonly azure: {
|
|
24
|
+
readonly clientId: "microsoftAzureADClientId";
|
|
25
|
+
readonly clientSecret: "microsoftAzureADClientSecret";
|
|
26
|
+
readonly tenantId: "microsoftAzureADTenantId";
|
|
27
|
+
readonly enable: "azureAdSsoEnabled";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type SupportedSsoProvider = keyof typeof SSO_PROVIDERS;
|
|
31
|
+
/** Carries a machine-readable `code`, which `outputError` reports. */
|
|
32
|
+
export declare class SetupError extends Error {
|
|
33
|
+
readonly code: string;
|
|
34
|
+
constructor(message: string, code: string);
|
|
35
|
+
}
|
|
36
|
+
export interface SetupSsoOptions {
|
|
37
|
+
provider: string;
|
|
38
|
+
clientId?: string;
|
|
39
|
+
clientSecret?: string;
|
|
40
|
+
tenantId?: string;
|
|
41
|
+
metadataUrl?: string;
|
|
42
|
+
discoveryUrl?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare function setupSso(opts: SetupSsoOptions): Promise<{
|
|
45
|
+
provider: "google" | "github" | "linkedin" | "facebook" | "azure";
|
|
46
|
+
enabled: boolean;
|
|
47
|
+
callbackUrl: string;
|
|
48
|
+
app: import("@nebulr-group/bridge-auth-core").AppResponse;
|
|
49
|
+
}>;
|
|
50
|
+
export interface SetupCommunicationOptions {
|
|
51
|
+
fromAddress?: string;
|
|
52
|
+
fromName?: string;
|
|
53
|
+
provider?: string;
|
|
54
|
+
apiKey?: string;
|
|
55
|
+
}
|
|
56
|
+
export declare function setupCommunication(opts: SetupCommunicationOptions): Promise<{
|
|
57
|
+
configured: boolean;
|
|
58
|
+
emailSenderEmail: string | null;
|
|
59
|
+
emailSenderName: string | null;
|
|
60
|
+
app: import("@nebulr-group/bridge-auth-core").AppResponse;
|
|
61
|
+
}>;
|
|
2
62
|
export declare function registerSetupCommands(program: Command): void;
|
|
3
63
|
//# sourceMappingURL=setup.command.d.ts.map
|