@nebulr-group/bridge-cli 0.6.0-beta.7 → 0.6.0-beta.9
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 +31 -2
- package/dist/auth/api-client.d.ts +2 -0
- package/dist/auth/api-client.d.ts.map +1 -1
- package/dist/auth/api-client.js.map +1 -1
- package/dist/auth/workspace-client.d.ts +70 -0
- package/dist/auth/workspace-client.d.ts.map +1 -0
- package/dist/auth/workspace-client.js +105 -0
- package/dist/auth/workspace-client.js.map +1 -0
- package/dist/bin.js +4 -1
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -14
- package/dist/cli.js.map +1 -1
- package/dist/commands/app.command.d.ts.map +1 -1
- package/dist/commands/app.command.js +117 -1
- package/dist/commands/app.command.js.map +1 -1
- package/dist/commands/auth/login.command.d.ts +13 -1
- package/dist/commands/auth/login.command.d.ts.map +1 -1
- package/dist/commands/auth/login.command.js +17 -3
- package/dist/commands/auth/login.command.js.map +1 -1
- package/dist/commands/auth/status.command.js +14 -0
- package/dist/commands/auth/status.command.js.map +1 -1
- package/dist/commands/check.command.d.ts +10 -0
- package/dist/commands/check.command.d.ts.map +1 -0
- package/dist/commands/check.command.js +95 -0
- package/dist/commands/check.command.js.map +1 -0
- package/dist/commands/plan.command.d.ts +19 -5
- package/dist/commands/plan.command.d.ts.map +1 -1
- package/dist/commands/plan.command.js +40 -26
- package/dist/commands/plan.command.js.map +1 -1
- package/dist/config.d.ts +46 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +190 -47
- package/dist/config.js.map +1 -1
- package/dist/credentials.d.ts +21 -0
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +21 -0
- package/dist/credentials.js.map +1 -1
- package/dist/gate-rules.d.ts +51 -0
- package/dist/gate-rules.d.ts.map +1 -0
- package/dist/gate-rules.js +171 -0
- package/dist/gate-rules.js.map +1 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +7 -0
- package/dist/output.js.map +1 -1
- package/dist/program.d.ts +17 -0
- package/dist/program.d.ts.map +1 -1
- package/dist/program.js +76 -0
- package/dist/program.js.map +1 -1
- package/dist/prompts/auth-master-integration-prompt.md +5 -2
- package/dist/prompts/billing/master.md +5 -2
- package/dist/prompts/decisions/feature-control.md +6 -1
- package/dist/prompts/decisions/going-live.md +1 -1
- package/dist/prompts/decisions/payments.md +9 -4
- package/dist/prompts/decisions/roles.md +2 -2
- package/dist/prompts/decisions/teams.md +4 -3
- package/dist/prompts/fit-together.md +22 -4
- package/dist/prompts/flags/master.md +3 -0
- package/dist/prompts/mcp-tools.json +7 -2
- package/dist/prompts/mechanisms.md +9 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,30 @@ bridge auth status # show every stored app, and which one is active
|
|
|
31
31
|
bridge auth logout # revoke the active token and remove it locally
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
#### One login, every app of the workspace
|
|
35
|
+
|
|
36
|
+
By default the consent screen preselects **Every app in <workspace>**, so one
|
|
37
|
+
login covers all your apps — the same way the MCP server does. The app you pin
|
|
38
|
+
at login (`--app`) is the *home* app; switch between the others locally:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
bridge auth login # Logged in as you@acme.com. Workspace: every app (home app: Acme)
|
|
42
|
+
bridge app list # every app, marking home and current
|
|
43
|
+
bridge app use "Acme Stage" # the default for every command, no re-login
|
|
44
|
+
bridge app create --name "Acme Prod" --use
|
|
45
|
+
bridge --app 66f0c0ffee0000000000abcd role list # one command on another app
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Precedence: `--app` > `bridge app use` > the home app. `--profile` still picks
|
|
49
|
+
*which stored login*; `--app` picks the app *within* it. Acting on a non-home
|
|
50
|
+
app prints `bridge: Acting on app <name> (<id>)` to stderr. Per-app tokens live
|
|
51
|
+
only in memory for that one command and are never written to disk.
|
|
52
|
+
|
|
53
|
+
`bridge auth login --single-app` asks for a login that covers only the home app
|
|
54
|
+
(the user can still change the choice on the consent screen). On such a login,
|
|
55
|
+
`--app` / `bridge app use` for another app fail and tell you to log in again.
|
|
56
|
+
`BRIDGE_API_KEY` is always bound to its one app.
|
|
57
|
+
|
|
34
58
|
#### Several apps at once
|
|
35
59
|
|
|
36
60
|
The credentials file holds one credential per app, so you can log in to your
|
|
@@ -73,7 +97,8 @@ next time something writes.
|
|
|
73
97
|
|
|
74
98
|
| Flag | Description |
|
|
75
99
|
|------|-------------|
|
|
76
|
-
| `--app <id\|name>` | Pin
|
|
100
|
+
| `--app <id\|name>` | Pin the home app, skipping the picker on the consent screen. |
|
|
101
|
+
| `--single-app` | Preselect "only this app" instead of "every app in the workspace" on the consent screen. |
|
|
77
102
|
| `--label <text>` | Friendly label stored on the token (default: `bridge-cli`). Useful when listing CLI tokens at `app.thebridge.dev/keys`. |
|
|
78
103
|
| `--no-browser` | Print the authorization URL instead of opening a browser. Use this on headless boxes or over SSH. |
|
|
79
104
|
|
|
@@ -191,7 +216,11 @@ bridge auth status # show every stored app, active one marked
|
|
|
191
216
|
bridge auth use <label|app-id> # switch the default app, no browser
|
|
192
217
|
bridge auth logout # revoke + remove the active app
|
|
193
218
|
bridge auth logout --all # ...or every stored app
|
|
194
|
-
bridge --profile <label> <cmd> # target one
|
|
219
|
+
bridge --profile <label> <cmd> # target one stored login for one command
|
|
220
|
+
bridge app list # apps this login covers (home + current marked)
|
|
221
|
+
bridge app use <id|name> # switch app within a workspace login, no browser
|
|
222
|
+
bridge app create --name <n> [--callback-url <url>] [--use]
|
|
223
|
+
bridge --app <id|name> <cmd> # one command on another app of the workspace
|
|
195
224
|
```
|
|
196
225
|
|
|
197
226
|
### Auth Configuration (app-level — separate from `auth login`)
|
|
@@ -21,6 +21,8 @@ export interface CliTokenExchangeResponse {
|
|
|
21
21
|
id: string;
|
|
22
22
|
email: string;
|
|
23
23
|
};
|
|
24
|
+
/** TBP-769 — what the login covers. Absent from servers before TBP-769. */
|
|
25
|
+
app_access?: 'app' | 'workspace';
|
|
24
26
|
}
|
|
25
27
|
export interface CliApiClientOptions {
|
|
26
28
|
/** Base URL of bridge-api, e.g. `https://api.thebridge.dev`. No trailing slash. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAClC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACpC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,mFAAmF;IACnF,OAAO,EAAE,MAAM,CAAC;IAChB,gDAAgD;IAChD,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,kEAAkE;IAClE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,qBAAa,eAAgB,SAAQ,KAAK;IACxC,uFAAuF;IACvF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAM1D;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,mBAAmB;yBAU3B;QAChC,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;KACtB,KAAG,OAAO,CAAC,wBAAwB,CAAC;yBA+BH,MAAM,KAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;EA2BzE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../../src/auth/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the bridge-api `/v1/cli/workspace/*` routes (TBP-769).
|
|
3
|
+
*
|
|
4
|
+
* One `bridge auth login` covers every app of the workspace (unless the user
|
|
5
|
+
* chose "only this app" on the consent screen). These routes are how the CLI
|
|
6
|
+
* lists those apps, creates one, and trades the login token for a short-lived
|
|
7
|
+
* token for ANOTHER app — the same shape the MCP server's `app` argument uses.
|
|
8
|
+
*
|
|
9
|
+
* Every call authenticates with the LOGIN token (`x-api-key`); per-app tokens
|
|
10
|
+
* are never accepted here, and are never written to disk by the CLI.
|
|
11
|
+
*
|
|
12
|
+
* Errors come back as `{ code, message, fix }`. They are surfaced as a
|
|
13
|
+
* `WorkspaceApiError` whose `code` and `hint` (= the server's `fix`) are picked
|
|
14
|
+
* up by `outputError`, so the reader gets the next step, not just the failure.
|
|
15
|
+
*/
|
|
16
|
+
export interface WorkspaceApp {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
home: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface WorkspaceAppsResponse {
|
|
22
|
+
scope: 'workspace' | 'app';
|
|
23
|
+
homeAppId: string;
|
|
24
|
+
apps: WorkspaceApp[];
|
|
25
|
+
}
|
|
26
|
+
export interface WorkspaceAppToken {
|
|
27
|
+
apiKey: string;
|
|
28
|
+
expiresAt: string;
|
|
29
|
+
app: {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export declare class WorkspaceApiError extends Error {
|
|
35
|
+
readonly code: string;
|
|
36
|
+
readonly hint?: string;
|
|
37
|
+
readonly status: number;
|
|
38
|
+
constructor(code: string, message: string, status: number, hint?: string);
|
|
39
|
+
}
|
|
40
|
+
export interface WorkspaceClientOptions {
|
|
41
|
+
baseUrl: string;
|
|
42
|
+
/** The `bridge auth login` token. */
|
|
43
|
+
loginToken: string;
|
|
44
|
+
fetchImpl?: typeof fetch;
|
|
45
|
+
}
|
|
46
|
+
export declare function createWorkspaceClient(options: WorkspaceClientOptions): {
|
|
47
|
+
listApps: () => Promise<WorkspaceAppsResponse>;
|
|
48
|
+
appToken: (appId: string) => Promise<WorkspaceAppToken>;
|
|
49
|
+
createApp: (input: {
|
|
50
|
+
name: string;
|
|
51
|
+
callbackUrl?: string;
|
|
52
|
+
}) => Promise<{
|
|
53
|
+
app: {
|
|
54
|
+
id: string;
|
|
55
|
+
name: string;
|
|
56
|
+
};
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
/** App ids are Mongo ObjectIds. Anything else is treated as an app name. */
|
|
60
|
+
export declare function looksLikeAppId(value: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve `<id|name>` against a list of apps. Id first (exact), then name
|
|
63
|
+
* (case-insensitive). Two apps sharing a name is an ERROR, never a first match
|
|
64
|
+
* — acting on the wrong app is the failure this whole area exists to prevent.
|
|
65
|
+
*/
|
|
66
|
+
export declare function resolveAppSelector<T extends {
|
|
67
|
+
id: string;
|
|
68
|
+
name: string;
|
|
69
|
+
}>(selector: string, apps: T[]): T;
|
|
70
|
+
//# sourceMappingURL=workspace-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-client.d.ts","sourceRoot":"","sources":["../../src/auth/workspace-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,WAAW,GAAG,KAAK,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CACnC;AAED,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;gBACZ,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM;CAOzE;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB;;sBA4B/C,MAAM;uBAEL;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE;aAC3C;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE;;EAE7C;AA2BD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACvE,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,CAAC,EAAE,GACR,CAAC,CAqBH"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thin client for the bridge-api `/v1/cli/workspace/*` routes (TBP-769).
|
|
3
|
+
*
|
|
4
|
+
* One `bridge auth login` covers every app of the workspace (unless the user
|
|
5
|
+
* chose "only this app" on the consent screen). These routes are how the CLI
|
|
6
|
+
* lists those apps, creates one, and trades the login token for a short-lived
|
|
7
|
+
* token for ANOTHER app — the same shape the MCP server's `app` argument uses.
|
|
8
|
+
*
|
|
9
|
+
* Every call authenticates with the LOGIN token (`x-api-key`); per-app tokens
|
|
10
|
+
* are never accepted here, and are never written to disk by the CLI.
|
|
11
|
+
*
|
|
12
|
+
* Errors come back as `{ code, message, fix }`. They are surfaced as a
|
|
13
|
+
* `WorkspaceApiError` whose `code` and `hint` (= the server's `fix`) are picked
|
|
14
|
+
* up by `outputError`, so the reader gets the next step, not just the failure.
|
|
15
|
+
*/
|
|
16
|
+
export class WorkspaceApiError extends Error {
|
|
17
|
+
code;
|
|
18
|
+
hint;
|
|
19
|
+
status;
|
|
20
|
+
constructor(code, message, status, hint) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.name = 'WorkspaceApiError';
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.status = status;
|
|
25
|
+
if (hint)
|
|
26
|
+
this.hint = hint;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function createWorkspaceClient(options) {
|
|
30
|
+
const baseUrl = options.baseUrl.replace(/\/$/, '');
|
|
31
|
+
const fetchImpl = options.fetchImpl ?? globalThis.fetch;
|
|
32
|
+
async function call(method, path, body) {
|
|
33
|
+
let res;
|
|
34
|
+
try {
|
|
35
|
+
res = await fetchImpl(`${baseUrl}/v1/cli/workspace${path}`, {
|
|
36
|
+
method,
|
|
37
|
+
headers: {
|
|
38
|
+
'x-api-key': options.loginToken,
|
|
39
|
+
...(body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
40
|
+
},
|
|
41
|
+
...(body !== undefined ? { body: JSON.stringify(body) } : {}),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
catch (err) {
|
|
45
|
+
throw new WorkspaceApiError('NETWORK_ERROR', `Could not reach ${baseUrl}: ${err instanceof Error ? err.message : String(err)}`, 0);
|
|
46
|
+
}
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
throw await translateError(res);
|
|
49
|
+
return (await res.json());
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
listApps: () => call('GET', '/apps'),
|
|
53
|
+
appToken: (appId) => call('POST', `/apps/${encodeURIComponent(appId)}/token`),
|
|
54
|
+
createApp: (input) => call('POST', '/apps', input),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function translateError(res) {
|
|
58
|
+
let raw;
|
|
59
|
+
try {
|
|
60
|
+
raw = await res.json();
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
raw = undefined;
|
|
64
|
+
}
|
|
65
|
+
const obj = (raw && typeof raw === 'object' ? raw : {});
|
|
66
|
+
const str = (v) => (typeof v === 'string' && v.length > 0 ? v : undefined);
|
|
67
|
+
let code = str(obj.code) ?? `HTTP_${res.status}`;
|
|
68
|
+
let message = str(obj.message) ?? `Bridge API returned HTTP ${res.status}.`;
|
|
69
|
+
let fix = str(obj.fix);
|
|
70
|
+
if (res.status === 401) {
|
|
71
|
+
code = str(obj.code) ?? 'UNAUTHORIZED';
|
|
72
|
+
if (!str(obj.message))
|
|
73
|
+
message = 'Your login is no longer valid.';
|
|
74
|
+
fix = fix ?? 'Run `bridge auth login` again.';
|
|
75
|
+
}
|
|
76
|
+
else if (res.status === 403 && !fix && !str(obj.code)) {
|
|
77
|
+
// A token that is not a `bridge auth login` token (e.g. BRIDGE_API_KEY).
|
|
78
|
+
fix = 'App selection needs a `bridge auth login` credential.';
|
|
79
|
+
}
|
|
80
|
+
return new WorkspaceApiError(code, message, res.status, fix);
|
|
81
|
+
}
|
|
82
|
+
/** App ids are Mongo ObjectIds. Anything else is treated as an app name. */
|
|
83
|
+
export function looksLikeAppId(value) {
|
|
84
|
+
return /^[a-f0-9]{24}$/i.test(value.trim());
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Resolve `<id|name>` against a list of apps. Id first (exact), then name
|
|
88
|
+
* (case-insensitive). Two apps sharing a name is an ERROR, never a first match
|
|
89
|
+
* — acting on the wrong app is the failure this whole area exists to prevent.
|
|
90
|
+
*/
|
|
91
|
+
export function resolveAppSelector(selector, apps) {
|
|
92
|
+
const needle = selector.trim();
|
|
93
|
+
const byId = apps.find((a) => a.id === needle);
|
|
94
|
+
if (byId)
|
|
95
|
+
return byId;
|
|
96
|
+
const lower = needle.toLowerCase();
|
|
97
|
+
const byName = apps.filter((a) => a.name.toLowerCase() === lower);
|
|
98
|
+
if (byName.length === 1)
|
|
99
|
+
return byName[0];
|
|
100
|
+
if (byName.length > 1) {
|
|
101
|
+
throw new WorkspaceApiError('APP_AMBIGUOUS', `"${needle}" matches ${byName.length} apps by name (${byName.map((a) => a.id).join(', ')}).`, 0, 'Select the app by id instead. `bridge app list` shows them.');
|
|
102
|
+
}
|
|
103
|
+
throw new WorkspaceApiError('APP_NOT_FOUND', `No app in this login matches "${needle}". Known: ${apps.map((a) => `${a.name} (${a.id})`).join(', ') || '(none)'}.`, 0, 'Run `bridge app list` to see the apps this login covers.');
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=workspace-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workspace-client.js","sourceRoot":"","sources":["../../src/auth/workspace-client.ts"],"names":[],"mappings":"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"}
|
package/dist/bin.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { program } from './cli.js';
|
|
3
3
|
program.parseAsync(process.argv).catch(() => {
|
|
4
|
-
|
|
4
|
+
// Keep a more specific code already set (e.g. 3 for a config error printed
|
|
5
|
+
// by the preAction hook before it aborted the action).
|
|
6
|
+
if (!process.exitCode)
|
|
7
|
+
process.exitCode = 1;
|
|
5
8
|
});
|
|
6
9
|
//# sourceMappingURL=bin.js.map
|
package/dist/bin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;IAC1C,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAEnC,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;IAC1C,2EAA2E;IAC3E,uDAAuD;IACvD,IAAI,CAAC,OAAO,CAAC,QAAQ;QAAE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AAC9C,CAAC,CAAC,CAAC"}
|
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;AASpC,eAAO,MAAM,OAAO,SAAgB,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -2,8 +2,7 @@ import { readFileSync } from 'node:fs';
|
|
|
2
2
|
import { fileURLToPath } from 'node:url';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
|
-
import {
|
|
6
|
-
import { registerCommands } from './program.js';
|
|
5
|
+
import { registerCommands, registerGlobalOptions } from './program.js';
|
|
7
6
|
// Read version from package.json so `bridge --version` never drifts from the
|
|
8
7
|
// published package. dist/cli.js lives at <pkg>/dist/cli.js, so `../package.json`
|
|
9
8
|
// resolves to the package root in both the published tarball and during dev.
|
|
@@ -13,17 +12,9 @@ export const program = new Command();
|
|
|
13
12
|
program
|
|
14
13
|
.name('bridge')
|
|
15
14
|
.description('Bridge platform CLI — optimized for AI coding agents')
|
|
16
|
-
.version(version)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// something different (which app to authorise), and because `--profile` is
|
|
21
|
-
// what aws/npm users already reach for.
|
|
22
|
-
.option('--profile <label|app-id|app-name>', 'Use a specific stored credential for this command (env: BRIDGE_PROFILE)');
|
|
23
|
-
// Runs before any subcommand action, so `getManagementClient()` sees the
|
|
24
|
-
// override wherever in the tree it is eventually called from.
|
|
25
|
-
program.hook('preAction', (thisCommand) => {
|
|
26
|
-
setProfileOverride(thisCommand.opts().profile);
|
|
27
|
-
});
|
|
15
|
+
.version(version);
|
|
16
|
+
// TBP-628 `--profile` (which stored login) and TBP-769 `--app` (which app
|
|
17
|
+
// within that login), plus the preAction hook that applies them.
|
|
18
|
+
registerGlobalOptions(program);
|
|
28
19
|
registerCommands(program);
|
|
29
20
|
//# sourceMappingURL=cli.js.map
|
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,
|
|
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,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAEvE,6EAA6E;AAC7E,kFAAkF;AAClF,6EAA6E;AAC7E,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC,CAAC;AACpF,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAwB,CAAC;AAErF,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAErC,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,sDAAsD,CAAC;KACnE,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,0EAA0E;AAC1E,iEAAiE;AACjE,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAE/B,gBAAgB,CAAC,OAAO,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.command.d.ts","sourceRoot":"","sources":["../../src/commands/app.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"app.command.d.ts","sourceRoot":"","sources":["../../src/commands/app.command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAmH1D"}
|
|
@@ -1,7 +1,43 @@
|
|
|
1
|
-
import { getManagementClient } from '../config.js';
|
|
1
|
+
import { ConfigError, getManagementClient, resolveSelectedCredential, resetManagementClient } from '../config.js';
|
|
2
2
|
import { outputSuccess, outputError } from '../output.js';
|
|
3
|
+
import { updateCredential } from '../credentials.js';
|
|
4
|
+
import { createWorkspaceClient, resolveAppSelector, } from '../auth/workspace-client.js';
|
|
3
5
|
export function registerAppCommands(program) {
|
|
4
6
|
const app = program.command('app').description('Manage app configuration');
|
|
7
|
+
// TBP-769 — one login, every app of the workspace (MCP parity).
|
|
8
|
+
app.command('list')
|
|
9
|
+
.description('List the apps your login covers, marking the home app and the current one')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
try {
|
|
12
|
+
outputSuccess(await runAppList());
|
|
13
|
+
}
|
|
14
|
+
catch (err) {
|
|
15
|
+
outputError(err);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
app.command('use <id|name>')
|
|
19
|
+
.description('Make another app of your workspace login the default for every command (local, no re-login)')
|
|
20
|
+
.action(async (selector) => {
|
|
21
|
+
try {
|
|
22
|
+
outputSuccess(await runAppUse(selector));
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
outputError(err);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
app.command('create')
|
|
29
|
+
.description('Create a new app in your workspace')
|
|
30
|
+
.requiredOption('--name <name>', 'App name')
|
|
31
|
+
.option('--callback-url <url>', 'OAuth callback URL to register on the new app')
|
|
32
|
+
.option('--use', 'Also make the new app the current one (like `bridge app use`)')
|
|
33
|
+
.action(async (opts) => {
|
|
34
|
+
try {
|
|
35
|
+
outputSuccess(await runAppCreate(opts));
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
outputError(err);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
5
41
|
app.command('get')
|
|
6
42
|
.description('Get current app configuration')
|
|
7
43
|
.action(async () => {
|
|
@@ -99,6 +135,86 @@ export function registerAppCommands(program) {
|
|
|
99
135
|
}
|
|
100
136
|
});
|
|
101
137
|
}
|
|
138
|
+
/** The stored login these commands work with; BRIDGE_API_KEY cannot list apps. */
|
|
139
|
+
function loginContext() {
|
|
140
|
+
const selected = resolveSelectedCredential();
|
|
141
|
+
if (!selected.creds || !selected.key) {
|
|
142
|
+
throw Object.assign(new ConfigError('App selection needs a login from `bridge auth login`; BRIDGE_API_KEY is bound to one app.'), { hint: 'Run `bridge auth login` (it covers every app of the workspace by default).' });
|
|
143
|
+
}
|
|
144
|
+
return { key: selected.key, creds: selected.creds, baseUrl: selected.baseUrl };
|
|
145
|
+
}
|
|
146
|
+
function workspaceClient(ctx) {
|
|
147
|
+
return createWorkspaceClient({ baseUrl: ctx.baseUrl, loginToken: ctx.creds.apiKey });
|
|
148
|
+
}
|
|
149
|
+
/** Current = the locally chosen app of this login, else its home app. */
|
|
150
|
+
function currentAppId(creds) {
|
|
151
|
+
return creds.appAccess === 'workspace' && creds.currentApp ? creds.currentApp.id : creds.app.id;
|
|
152
|
+
}
|
|
153
|
+
/** Learn the login's scope from the server (credentials from before TBP-769 lack it). */
|
|
154
|
+
function rememberScope(key, creds, scope) {
|
|
155
|
+
const appAccess = scope === 'workspace' ? 'workspace' : 'app';
|
|
156
|
+
return creds.appAccess === appAccess ? creds : updateCredential(key, { appAccess });
|
|
157
|
+
}
|
|
158
|
+
async function runAppList() {
|
|
159
|
+
const ctx = loginContext();
|
|
160
|
+
const res = await workspaceClient(ctx).listApps();
|
|
161
|
+
const creds = rememberScope(ctx.key, ctx.creds, res.scope);
|
|
162
|
+
const current = currentAppId(creds);
|
|
163
|
+
return {
|
|
164
|
+
scope: res.scope,
|
|
165
|
+
homeAppId: res.homeAppId,
|
|
166
|
+
currentAppId: current,
|
|
167
|
+
apps: res.apps.map((a) => ({ id: a.id, name: a.name, home: a.home, current: a.id === current })),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
async function runAppUse(selector) {
|
|
171
|
+
const ctx = loginContext();
|
|
172
|
+
const home = ctx.creds.app;
|
|
173
|
+
const lower = selector.trim().toLowerCase();
|
|
174
|
+
let target;
|
|
175
|
+
if (selector.trim() === home.id || lower === home.name.toLowerCase()) {
|
|
176
|
+
target = home;
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
const res = await workspaceClient(ctx).listApps();
|
|
180
|
+
rememberScope(ctx.key, ctx.creds, res.scope);
|
|
181
|
+
if (res.scope !== 'workspace') {
|
|
182
|
+
throw Object.assign(new ConfigError(`This login covers only the app ${home.name} (${home.id}), so it cannot switch to "${selector}".`), {
|
|
183
|
+
code: 'APP_NOT_IN_CONNECTION',
|
|
184
|
+
hint: 'Run `bridge auth login` again and choose "Every app in <workspace>" on the consent ' +
|
|
185
|
+
'screen; one login then covers every app and `bridge app use` switches between them.',
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
target = resolveAppSelector(selector, res.apps);
|
|
189
|
+
}
|
|
190
|
+
const isHome = target.id === home.id;
|
|
191
|
+
updateCredential(ctx.key, { currentApp: isHome ? undefined : { id: target.id, name: target.name } });
|
|
192
|
+
resetManagementClient();
|
|
193
|
+
return {
|
|
194
|
+
currentApp: { id: target.id, name: target.name, home: isHome },
|
|
195
|
+
message: isHome
|
|
196
|
+
? `Now using the home app ${target.name} (${target.id}).`
|
|
197
|
+
: `Now using ${target.name} (${target.id}). Commands act on it until \`bridge app use\` again; \`--app\` overrides it for one command.`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
async function runAppCreate(opts) {
|
|
201
|
+
const ctx = loginContext();
|
|
202
|
+
const res = await workspaceClient(ctx).createApp({
|
|
203
|
+
name: opts.name,
|
|
204
|
+
...(opts.callbackUrl ? { callbackUrl: opts.callbackUrl } : {}),
|
|
205
|
+
});
|
|
206
|
+
let current = false;
|
|
207
|
+
if (opts.use) {
|
|
208
|
+
// A create that succeeded proves the login spans the workspace.
|
|
209
|
+
updateCredential(ctx.key, {
|
|
210
|
+
appAccess: 'workspace',
|
|
211
|
+
currentApp: res.app.id === ctx.creds.app.id ? undefined : { id: res.app.id, name: res.app.name },
|
|
212
|
+
});
|
|
213
|
+
resetManagementClient();
|
|
214
|
+
current = true;
|
|
215
|
+
}
|
|
216
|
+
return { app: res.app, current };
|
|
217
|
+
}
|
|
102
218
|
function validateRedirectUri(value) {
|
|
103
219
|
let parsed;
|
|
104
220
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app.command.js","sourceRoot":"","sources":["../../src/commands/app.command.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"app.command.js","sourceRoot":"","sources":["../../src/commands/app.command.ts"],"names":[],"mappings":"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"}
|
|
@@ -27,8 +27,20 @@ interface LoginOptions {
|
|
|
27
27
|
reauth?: boolean;
|
|
28
28
|
/** TBP-593 — request the elevated `management:admin` scope. */
|
|
29
29
|
admin?: boolean;
|
|
30
|
+
/** TBP-769 — preselect "only this app" instead of "every app" on consent. */
|
|
31
|
+
singleApp?: boolean;
|
|
30
32
|
}
|
|
31
|
-
export { runLogin };
|
|
33
|
+
export { runLogin, buildAuthorizeUrl };
|
|
32
34
|
export declare function registerAuthLoginCommand(auth: Command): void;
|
|
33
35
|
declare function runLogin(opts: LoginOptions): Promise<void>;
|
|
36
|
+
declare function buildAuthorizeUrl(authBaseUrl: string, params: {
|
|
37
|
+
challenge: string;
|
|
38
|
+
redirect: string;
|
|
39
|
+
state: string;
|
|
40
|
+
appId?: string;
|
|
41
|
+
label?: string;
|
|
42
|
+
reauth?: boolean;
|
|
43
|
+
admin?: boolean;
|
|
44
|
+
appAccess?: 'app' | 'workspace';
|
|
45
|
+
}): string;
|
|
34
46
|
//# sourceMappingURL=login.command.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"login.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"login.command.d.ts","sourceRoot":"","sources":["../../../src/commands/auth/login.command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAazC,UAAU,YAAY;IACpB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,6EAA6E;IAC7E,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAID,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;AAEvC,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAqB5D;AAED,iBAAe,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAiHzD;AAED,iBAAS,iBAAiB,CACxB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE;IACN,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,KAAK,GAAG,WAAW,CAAC;CACjC,GACA,MAAM,CAeR"}
|
|
@@ -7,12 +7,13 @@ import { createCliApiClient, CliAuthApiError } from '../../auth/api-client.js';
|
|
|
7
7
|
import { SCOPE_MANAGEMENT, SCOPE_MANAGEMENT_ADMIN } from '../../auth/scopes.js';
|
|
8
8
|
const DEFAULT_API_BASE_URL = 'https://api.thebridge.dev';
|
|
9
9
|
const DEFAULT_AUTH_BASE_URL = 'https://auth.thebridge.dev';
|
|
10
|
-
export { runLogin };
|
|
10
|
+
export { runLogin, buildAuthorizeUrl };
|
|
11
11
|
export function registerAuthLoginCommand(auth) {
|
|
12
12
|
auth
|
|
13
13
|
.command('login')
|
|
14
14
|
.description('Authenticate via browser (loopback PKCE) and save credentials')
|
|
15
|
-
.option('--app <id|name>', 'Pin
|
|
15
|
+
.option('--app <id|name>', 'Pin the home app: an app id skips the picker entirely; an app name is resolved on the consent screen (falls back to the picker if not found)')
|
|
16
|
+
.option('--single-app', 'Ask for a login that covers only the home app. By default the consent screen preselects "Every app in <workspace>", so `bridge app use` / `--app` can switch apps without logging in again')
|
|
16
17
|
.option('--label <text>', 'Friendly label stored on the token (default: "bridge-cli")')
|
|
17
18
|
.option('--no-browser', 'Print the authorization URL instead of opening a browser')
|
|
18
19
|
.option('--reauth', 'Force the consent screen to sign you out and prompt for credentials again')
|
|
@@ -59,6 +60,7 @@ async function runLogin(opts) {
|
|
|
59
60
|
label: opts.label,
|
|
60
61
|
reauth: opts.reauth === true,
|
|
61
62
|
admin: opts.admin === true,
|
|
63
|
+
appAccess: opts.singleApp === true ? 'app' : 'workspace',
|
|
62
64
|
});
|
|
63
65
|
// 3. Open browser (or print URL if --no-browser or open fails).
|
|
64
66
|
const stdout = process.stdout;
|
|
@@ -114,6 +116,9 @@ async function runLogin(opts) {
|
|
|
114
116
|
user: { id: exchanged.user.id, email: exchanged.user.email },
|
|
115
117
|
baseUrl: apiBaseUrl,
|
|
116
118
|
...(opts.label ? { label: opts.label } : {}),
|
|
119
|
+
// TBP-769 — what the user actually chose on the consent screen (the URL
|
|
120
|
+
// only preselected it). An older server omits it: that login is one app.
|
|
121
|
+
appAccess: exchanged.app_access === 'workspace' ? 'workspace' : 'app',
|
|
117
122
|
};
|
|
118
123
|
writeCredentials(stored);
|
|
119
124
|
// Reset the cached BridgeManagement client so the next command picks up the
|
|
@@ -121,7 +126,13 @@ async function runLogin(opts) {
|
|
|
121
126
|
resetManagementClient();
|
|
122
127
|
// 7. Friendly confirmation.
|
|
123
128
|
const expiryHuman = formatExpiryDate(exchanged.expires_at);
|
|
124
|
-
|
|
129
|
+
const scope = stored.appAccess === 'workspace'
|
|
130
|
+
? `Workspace: every app (home app: ${exchanged.app.name})`
|
|
131
|
+
: `App: ${exchanged.app.name}`;
|
|
132
|
+
stdout.write(`Logged in as ${exchanged.user.email}. ${scope}. Token valid until ${expiryHuman}.\n`);
|
|
133
|
+
if (stored.appAccess === 'workspace') {
|
|
134
|
+
stdout.write('Run `bridge app list` to see the apps, `bridge app use <id|name>` to switch.\n');
|
|
135
|
+
}
|
|
125
136
|
}
|
|
126
137
|
function buildAuthorizeUrl(authBaseUrl, params) {
|
|
127
138
|
const url = new URL(`${authBaseUrl}/cli/authorize`);
|
|
@@ -133,6 +144,9 @@ function buildAuthorizeUrl(authBaseUrl, params) {
|
|
|
133
144
|
url.searchParams.set('app_id', params.appId);
|
|
134
145
|
if (params.label)
|
|
135
146
|
url.searchParams.set('label', params.label);
|
|
147
|
+
// TBP-769 — only PRESELECTS the choice on the consent screen; the user decides.
|
|
148
|
+
if (params.appAccess)
|
|
149
|
+
url.searchParams.set('app_access', params.appAccess);
|
|
136
150
|
// OAuth-style `prompt=login` — the consent screen reads this as "clear any
|
|
137
151
|
// existing browser session and re-prompt for credentials" so users who just
|
|
138
152
|
// ran `bridge auth logout` aren't dropped straight into the workspace picker.
|