@roarkanalytics/cli 0.28.0 → 0.31.1
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/CHANGELOG.md +28 -0
- package/README.md +34 -0
- package/commands.js +46 -2
- package/completions.js +4 -4
- package/man/roark-customer-flow-edge-case-create.1 +3 -0
- package/man/roark-customer-flow-edge-case-update.1 +1 -1
- package/man/roark-customer-flow-happy-path-update.1 +1 -1
- package/man/roark-project-list.1 +89 -0
- package/man/roark-whoami.1 +89 -0
- package/man/roark.1 +6 -0
- package/npm-shrinkwrap.json +6 -6
- package/package.json +5 -3
- package/runtime/browser-login.d.ts +15 -1
- package/runtime/browser-login.js +2 -1
- package/runtime/commands/api.js +4 -0
- package/runtime/commands/auth.js +99 -17
- package/runtime/commands/config.js +1 -0
- package/runtime/config.d.ts +12 -0
- package/runtime/config.js +1 -0
- package/runtime/program.d.ts +1 -0
- package/runtime/program.js +5 -0
- package/version.d.ts +1 -1
- package/version.js +1 -1
|
@@ -30,6 +30,9 @@ Optional.
|
|
|
30
30
|
\fB\-\-preceded\-by\-customer\-flow\-variant\-id\fR \fI<value>\fR
|
|
31
31
|
Optional.
|
|
32
32
|
.TP
|
|
33
|
+
\fB\-\-additional\-expectations\fR \fI<value>\fR
|
|
34
|
+
The expectations graded on this variant only, on top of the flow's own. Sent as the complete set: it replaces whatever the variant had, so omit it to leave the set alone and send [] to clear it. Improv flows only: a scripted variant's expectations come from the agent turns on its path and are rewritten on every graph edit. Optional.
|
|
35
|
+
.TP
|
|
33
36
|
\fB\-\-persona\-override\-id\fR \fI<value>\fR
|
|
34
37
|
The persona this runs as. Omit to inherit the happy path's. Optional.
|
|
35
38
|
.SH "GLOBAL OPTIONS"
|
|
@@ -37,7 +37,7 @@ Optional.
|
|
|
37
37
|
The persona this runs as. Null on an edge case inherits the happy path's. Optional.
|
|
38
38
|
.TP
|
|
39
39
|
\fB\-\-additional\-expectations\fR \fI<value>\fR
|
|
40
|
-
|
|
40
|
+
The expectations graded on this variant only, on top of the flow's own. Sent as the complete set: it replaces whatever the variant had, so omit it to leave the set alone and send [] to clear it. Improv flows only: a scripted variant's expectations come from the agent turns on its path and are rewritten on every graph edit. Optional.
|
|
41
41
|
.SH "GLOBAL OPTIONS"
|
|
42
42
|
.TP
|
|
43
43
|
\fB\-\-data\fR \fI<json>\fR
|
|
@@ -34,7 +34,7 @@ Optional.
|
|
|
34
34
|
The persona this runs as. Null on an edge case inherits the happy path's. Optional.
|
|
35
35
|
.TP
|
|
36
36
|
\fB\-\-additional\-expectations\fR \fI<value>\fR
|
|
37
|
-
|
|
37
|
+
The expectations graded on this variant only, on top of the flow's own. Sent as the complete set: it replaces whatever the variant had, so omit it to leave the set alone and send [] to clear it. Improv flows only: a scripted variant's expectations come from the agent turns on its path and are rewritten on every graph edit. Optional.
|
|
38
38
|
.SH "GLOBAL OPTIONS"
|
|
39
39
|
.TP
|
|
40
40
|
\fB\-\-data\fR \fI<json>\fR
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.\" File generated from our OpenAPI spec by app-agent-codegen. Do not edit.
|
|
2
|
+
.TH "ROARK\-PROJECT\-LIST" "1" "" "roark-project-list" "Roark Analytics API Manual"
|
|
3
|
+
.SH "NAME"
|
|
4
|
+
roark\-project\-list \- List projects available to this credential
|
|
5
|
+
.SH "SYNOPSIS"
|
|
6
|
+
.B roark project list
|
|
7
|
+
[\fIoptions\fR]
|
|
8
|
+
.SH "DESCRIPTION"
|
|
9
|
+
Returns every project the calling credential may act on, with the permissions it holds in each. A project\-scoped API key returns the single project it is bound to. A user\-scoped credential returns the projects in its organization where the holder is an active member. Use the returned id in the X\-Roark\-Project\-Id header.
|
|
10
|
+
.SH "API"
|
|
11
|
+
GET /v1/projects
|
|
12
|
+
.SH "GLOBAL OPTIONS"
|
|
13
|
+
.TP
|
|
14
|
+
\fB\-\-data\fR \fI<json>\fR
|
|
15
|
+
Request body as JSON, or @file to read one (@\- for stdin).
|
|
16
|
+
.TP
|
|
17
|
+
\fB\-y\fR, \fB\-\-yes\fR
|
|
18
|
+
Skip confirmation prompts.
|
|
19
|
+
.TP
|
|
20
|
+
\fB\-\-no\-input\fR
|
|
21
|
+
Never prompt; fail instead.
|
|
22
|
+
.TP
|
|
23
|
+
\fB\-\-base\-url\fR \fI<url>\fR
|
|
24
|
+
API base URL.
|
|
25
|
+
.TP
|
|
26
|
+
\fB\-\-token\fR \fI<token>\fR
|
|
27
|
+
Bearer token. Prefer ROARK_API_BEARER_TOKEN or the stored credential.
|
|
28
|
+
.TP
|
|
29
|
+
\fB\-\-timeout\fR \fI<ms>\fR
|
|
30
|
+
Request timeout in milliseconds.
|
|
31
|
+
.TP
|
|
32
|
+
\fB\-\-max\-retries\fR \fI<count>\fR
|
|
33
|
+
Retries for retryable failures.
|
|
34
|
+
.TP
|
|
35
|
+
\fB\-\-format\fR \fI<format>\fR
|
|
36
|
+
Output format: auto, json, jsonl, plain.
|
|
37
|
+
.TP
|
|
38
|
+
\fB\-\-json\fR
|
|
39
|
+
Shorthand for \-\-format json.
|
|
40
|
+
.TP
|
|
41
|
+
\fB\-\-no\-color\fR
|
|
42
|
+
Disable colour.
|
|
43
|
+
.TP
|
|
44
|
+
\fB\-q\fR, \fB\-\-quiet\fR
|
|
45
|
+
Suppress non\-essential output.
|
|
46
|
+
.TP
|
|
47
|
+
\fB\-\-allow\-project\-base\-url\fR
|
|
48
|
+
Send the stored credential to a base URL set by a project .roark.json. Refused by default.
|
|
49
|
+
.SH "EXAMPLES"
|
|
50
|
+
.PP
|
|
51
|
+
.RS 4
|
|
52
|
+
.nf
|
|
53
|
+
roark project list
|
|
54
|
+
.fi
|
|
55
|
+
.RE
|
|
56
|
+
.SH "ENVIRONMENT"
|
|
57
|
+
.TP
|
|
58
|
+
\fBROARK_API_BEARER_TOKEN\fR
|
|
59
|
+
Bearer token used when \-\-token is not given.
|
|
60
|
+
.TP
|
|
61
|
+
\fBROARK_BASE_URL\fR
|
|
62
|
+
API base URL.
|
|
63
|
+
.TP
|
|
64
|
+
\fBROARK_ALLOW_PROJECT_BASE_URL\fR
|
|
65
|
+
Trusts a base URL set by a project .roark.json, like \-\-allow\-project\-base\-url.
|
|
66
|
+
.TP
|
|
67
|
+
\fBNO_COLOR\fR
|
|
68
|
+
Disables colour when set to any value.
|
|
69
|
+
.SH "EXIT STATUS"
|
|
70
|
+
.TP
|
|
71
|
+
\fB0\fR
|
|
72
|
+
The command succeeded.
|
|
73
|
+
.TP
|
|
74
|
+
\fB1\fR
|
|
75
|
+
The API rejected the request.
|
|
76
|
+
.TP
|
|
77
|
+
\fB2\fR
|
|
78
|
+
The command line was wrong.
|
|
79
|
+
.TP
|
|
80
|
+
\fB3\fR
|
|
81
|
+
No credential, or the credential was refused.
|
|
82
|
+
.TP
|
|
83
|
+
\fB4\fR
|
|
84
|
+
The addressed resource does not exist.
|
|
85
|
+
.TP
|
|
86
|
+
\fB5\fR
|
|
87
|
+
The request never completed: connection, timeout, or abort.
|
|
88
|
+
.SH "SEE ALSO"
|
|
89
|
+
.BR roark (1)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
.\" File generated from our OpenAPI spec by app-agent-codegen. Do not edit.
|
|
2
|
+
.TH "ROARK\-WHOAMI" "1" "" "roark-whoami" "Roark Analytics API Manual"
|
|
3
|
+
.SH "NAME"
|
|
4
|
+
roark\-whoami \- Describe the current credential
|
|
5
|
+
.SH "SYNOPSIS"
|
|
6
|
+
.B roark whoami
|
|
7
|
+
[\fIoptions\fR]
|
|
8
|
+
.SH "DESCRIPTION"
|
|
9
|
+
Returns the scope of the credential making the request, the organization it acts in, its default project (if any) and the permissions it was granted. Works for both project\-scoped API keys and user\-scoped credentials from the CLI or an MCP connector.
|
|
10
|
+
.SH "API"
|
|
11
|
+
GET /v1/me
|
|
12
|
+
.SH "GLOBAL OPTIONS"
|
|
13
|
+
.TP
|
|
14
|
+
\fB\-\-data\fR \fI<json>\fR
|
|
15
|
+
Request body as JSON, or @file to read one (@\- for stdin).
|
|
16
|
+
.TP
|
|
17
|
+
\fB\-y\fR, \fB\-\-yes\fR
|
|
18
|
+
Skip confirmation prompts.
|
|
19
|
+
.TP
|
|
20
|
+
\fB\-\-no\-input\fR
|
|
21
|
+
Never prompt; fail instead.
|
|
22
|
+
.TP
|
|
23
|
+
\fB\-\-base\-url\fR \fI<url>\fR
|
|
24
|
+
API base URL.
|
|
25
|
+
.TP
|
|
26
|
+
\fB\-\-token\fR \fI<token>\fR
|
|
27
|
+
Bearer token. Prefer ROARK_API_BEARER_TOKEN or the stored credential.
|
|
28
|
+
.TP
|
|
29
|
+
\fB\-\-timeout\fR \fI<ms>\fR
|
|
30
|
+
Request timeout in milliseconds.
|
|
31
|
+
.TP
|
|
32
|
+
\fB\-\-max\-retries\fR \fI<count>\fR
|
|
33
|
+
Retries for retryable failures.
|
|
34
|
+
.TP
|
|
35
|
+
\fB\-\-format\fR \fI<format>\fR
|
|
36
|
+
Output format: auto, json, jsonl, plain.
|
|
37
|
+
.TP
|
|
38
|
+
\fB\-\-json\fR
|
|
39
|
+
Shorthand for \-\-format json.
|
|
40
|
+
.TP
|
|
41
|
+
\fB\-\-no\-color\fR
|
|
42
|
+
Disable colour.
|
|
43
|
+
.TP
|
|
44
|
+
\fB\-q\fR, \fB\-\-quiet\fR
|
|
45
|
+
Suppress non\-essential output.
|
|
46
|
+
.TP
|
|
47
|
+
\fB\-\-allow\-project\-base\-url\fR
|
|
48
|
+
Send the stored credential to a base URL set by a project .roark.json. Refused by default.
|
|
49
|
+
.SH "EXAMPLES"
|
|
50
|
+
.PP
|
|
51
|
+
.RS 4
|
|
52
|
+
.nf
|
|
53
|
+
roark whoami
|
|
54
|
+
.fi
|
|
55
|
+
.RE
|
|
56
|
+
.SH "ENVIRONMENT"
|
|
57
|
+
.TP
|
|
58
|
+
\fBROARK_API_BEARER_TOKEN\fR
|
|
59
|
+
Bearer token used when \-\-token is not given.
|
|
60
|
+
.TP
|
|
61
|
+
\fBROARK_BASE_URL\fR
|
|
62
|
+
API base URL.
|
|
63
|
+
.TP
|
|
64
|
+
\fBROARK_ALLOW_PROJECT_BASE_URL\fR
|
|
65
|
+
Trusts a base URL set by a project .roark.json, like \-\-allow\-project\-base\-url.
|
|
66
|
+
.TP
|
|
67
|
+
\fBNO_COLOR\fR
|
|
68
|
+
Disables colour when set to any value.
|
|
69
|
+
.SH "EXIT STATUS"
|
|
70
|
+
.TP
|
|
71
|
+
\fB0\fR
|
|
72
|
+
The command succeeded.
|
|
73
|
+
.TP
|
|
74
|
+
\fB1\fR
|
|
75
|
+
The API rejected the request.
|
|
76
|
+
.TP
|
|
77
|
+
\fB2\fR
|
|
78
|
+
The command line was wrong.
|
|
79
|
+
.TP
|
|
80
|
+
\fB3\fR
|
|
81
|
+
No credential, or the credential was refused.
|
|
82
|
+
.TP
|
|
83
|
+
\fB4\fR
|
|
84
|
+
The addressed resource does not exist.
|
|
85
|
+
.TP
|
|
86
|
+
\fB5\fR
|
|
87
|
+
The request never completed: connection, timeout, or abort.
|
|
88
|
+
.SH "SEE ALSO"
|
|
89
|
+
.BR roark (1)
|
package/man/roark.1
CHANGED
|
@@ -282,6 +282,9 @@ List a metric’s variants See roark\-metric\-variant\-list(1).
|
|
|
282
282
|
\fBroark metric variant update\fR
|
|
283
283
|
Update a metric variant See roark\-metric\-variant\-update(1).
|
|
284
284
|
.TP
|
|
285
|
+
\fBroark project list\fR
|
|
286
|
+
List projects available to this credential See roark\-project\-list(1).
|
|
287
|
+
.TP
|
|
285
288
|
\fBroark simulation environment create\fR
|
|
286
289
|
Create an environment See roark\-simulation\-environment\-create(1).
|
|
287
290
|
.TP
|
|
@@ -377,6 +380,9 @@ Get webhook by ID See roark\-webhook\-get(1).
|
|
|
377
380
|
.TP
|
|
378
381
|
\fBroark webhook list\fR
|
|
379
382
|
List webhooks See roark\-webhook\-list(1).
|
|
383
|
+
.TP
|
|
384
|
+
\fBroark whoami\fR
|
|
385
|
+
Describe the current credential See roark\-whoami(1).
|
|
380
386
|
.SH "CONFIGURATION"
|
|
381
387
|
Settings are resolved highest precedence first: command line flag, environment variable, project .roark.json found by walking up from the working directory, then the user config file. The stored credential is written with mode 0600.
|
|
382
388
|
.SH "ENVIRONMENT"
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roarkanalytics/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@roarkanalytics/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.31.1",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@roarkanalytics/sdk": "^4.
|
|
12
|
+
"@roarkanalytics/sdk": "^4.15.0",
|
|
13
13
|
"commander": "^14.0.3",
|
|
14
14
|
"yaml": "^2.9.0"
|
|
15
15
|
},
|
|
@@ -21,9 +21,9 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"node_modules/@roarkanalytics/sdk": {
|
|
24
|
-
"version": "4.
|
|
25
|
-
"resolved": "https://registry.npmjs.org/@roarkanalytics/sdk/-/sdk-4.
|
|
26
|
-
"integrity": "sha512-
|
|
24
|
+
"version": "4.15.0",
|
|
25
|
+
"resolved": "https://registry.npmjs.org/@roarkanalytics/sdk/-/sdk-4.15.0.tgz",
|
|
26
|
+
"integrity": "sha512-H5bHFILsMVMUG7+6yi3gO4JrAvvoFPLn82zZp0tS1A6UugH/hzQgwuMFSkjbJCl29p9GcVjAFhLDaZa7845gmA==",
|
|
27
27
|
"license": "Apache-2.0"
|
|
28
28
|
},
|
|
29
29
|
"node_modules/commander": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@roarkanalytics/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.1",
|
|
4
4
|
"description": "The official command line interface for the Roark API",
|
|
5
5
|
"author": "Roark <james@roark.ai>",
|
|
6
6
|
"type": "commonjs",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"fix": "eslint --fix . && prettier --write ."
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@roarkanalytics/sdk": "^4.
|
|
32
|
+
"@roarkanalytics/sdk": "^4.15.0",
|
|
33
33
|
"commander": "^14.0.3",
|
|
34
34
|
"yaml": "^2.9.0"
|
|
35
35
|
},
|
|
@@ -109,6 +109,7 @@
|
|
|
109
109
|
"./man/roark-metric-variant-get.1",
|
|
110
110
|
"./man/roark-metric-variant-list.1",
|
|
111
111
|
"./man/roark-metric-variant-update.1",
|
|
112
|
+
"./man/roark-project-list.1",
|
|
112
113
|
"./man/roark-simulation-environment-create.1",
|
|
113
114
|
"./man/roark-simulation-environment-delete.1",
|
|
114
115
|
"./man/roark-simulation-environment-get.1",
|
|
@@ -140,6 +141,7 @@
|
|
|
140
141
|
"./man/roark-webhook-create.1",
|
|
141
142
|
"./man/roark-webhook-delete.1",
|
|
142
143
|
"./man/roark-webhook-get.1",
|
|
143
|
-
"./man/roark-webhook-list.1"
|
|
144
|
+
"./man/roark-webhook-list.1",
|
|
145
|
+
"./man/roark-whoami.1"
|
|
144
146
|
]
|
|
145
147
|
}
|
|
@@ -23,4 +23,18 @@ export interface BrowserLoginOptions {
|
|
|
23
23
|
onAuthorizeUrl?: (url: string) => void;
|
|
24
24
|
open?: (url: string) => void;
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/** What the token exchange hands back. */
|
|
27
|
+
export interface BrowserLoginResult {
|
|
28
|
+
/** The minted bearer token. */
|
|
29
|
+
token: string;
|
|
30
|
+
/**
|
|
31
|
+
* The project chosen on the consent screen, when the server reports one.
|
|
32
|
+
*
|
|
33
|
+
* A user-scoped credential stores no project of its own, so this is the only moment the choice
|
|
34
|
+
* is communicated: `auth login` writes it to the user config so the next command works without
|
|
35
|
+
* a `--project`. Absent when the server minted a project-scoped key, which needs no project
|
|
36
|
+
* setting at all.
|
|
37
|
+
*/
|
|
38
|
+
project?: string;
|
|
39
|
+
}
|
|
40
|
+
export declare const browserLogin: (options: BrowserLoginOptions) => Promise<BrowserLoginResult>;
|
package/runtime/browser-login.js
CHANGED
|
@@ -127,6 +127,7 @@ const browserLogin = async (options) => {
|
|
|
127
127
|
const token = body.data?.token;
|
|
128
128
|
if (!token)
|
|
129
129
|
throw new Error('token exchange returned no token');
|
|
130
|
-
|
|
130
|
+
const project = body.data?.defaultProjectId;
|
|
131
|
+
return { token, ...(typeof project === 'string' && project.length > 0 ? { project } : {}) };
|
|
131
132
|
};
|
|
132
133
|
exports.browserLogin = browserLogin;
|
package/runtime/commands/api.js
CHANGED
|
@@ -43,6 +43,10 @@ const registerApiCommand = (root, binaryName, clientFor, resolveOutput) => {
|
|
|
43
43
|
.option('--data <json>', 'request body as JSON, or @file to read one (@- for stdin)')
|
|
44
44
|
.option('--query <key=value>', 'query parameter; repeat for more than one', (value, previous) => [...(previous ?? []), value])
|
|
45
45
|
.option('--base-url <url>', 'API base URL')
|
|
46
|
+
// `api` redeclares the connection options rather than inheriting the root ones, so this has to
|
|
47
|
+
// be listed here too. Without it the escape hatch is the one command a user credential cannot
|
|
48
|
+
// use: every project-scoped path 400s with no way to name a project.
|
|
49
|
+
.option('--project <id>', 'project to act on (or ROARK_PROJECT_ID, or `roark config set project`)')
|
|
46
50
|
.option('--token <token>', 'bearer token')
|
|
47
51
|
.option('--timeout <ms>', 'request timeout in milliseconds')
|
|
48
52
|
.option('--max-retries <count>', 'retries for retryable failures')
|
package/runtime/commands/auth.js
CHANGED
|
@@ -24,6 +24,55 @@ const DEFAULT_BASE_URL = 'https://api.roark.ai';
|
|
|
24
24
|
// a 401 while a valid one that merely lacks this endpoint's permission is a 403 — both prove the
|
|
25
25
|
// token authenticates. `/v1/agent` is a stable GET that every project has.
|
|
26
26
|
const AUTH_PROBE_PATH = '/v1/agent';
|
|
27
|
+
// What the credential itself says it is. Added with user-scoped credentials and the only endpoint
|
|
28
|
+
// that answers without naming a project, which is exactly what a liveness check needs. An older API
|
|
29
|
+
// does not have it; callers fall back to AUTH_PROBE_PATH on a 404.
|
|
30
|
+
const AUTH_DESCRIBE_PATH = '/v1/me';
|
|
31
|
+
/**
|
|
32
|
+
* Asks the API whether the effective credential still works.
|
|
33
|
+
*
|
|
34
|
+
* `auth status` used to answer from the config file alone, so it said "Authenticated" for a
|
|
35
|
+
* credential the server had already revoked. That was always wrong and is now easy to hit: a
|
|
36
|
+
* user-scoped credential is revoked from the web UI, in a different place from the machine
|
|
37
|
+
* holding it, so the CLI is routinely the last to know.
|
|
38
|
+
*/
|
|
39
|
+
const describeCredential = async (clientFor) => {
|
|
40
|
+
let client;
|
|
41
|
+
try {
|
|
42
|
+
// Resolved the same way a real command would, so the base-url trust guard still applies and we
|
|
43
|
+
// are checking the credential that would actually be used.
|
|
44
|
+
client = clientFor({});
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return { kind: 'unknown', reason: error.message.split('\n')[0] ?? 'unknown error' };
|
|
48
|
+
}
|
|
49
|
+
const classify = (error) => {
|
|
50
|
+
const status = error.status;
|
|
51
|
+
if (status === 401)
|
|
52
|
+
return { kind: 'rejected' };
|
|
53
|
+
// 403 (authenticated, lacks a permission) and 400 (a user credential that named no project)
|
|
54
|
+
// both prove authentication succeeded.
|
|
55
|
+
if (status !== undefined && status < 500)
|
|
56
|
+
return { kind: 'live' };
|
|
57
|
+
return { kind: 'unknown', reason: error.message };
|
|
58
|
+
};
|
|
59
|
+
try {
|
|
60
|
+
const me = (await client.get(AUTH_DESCRIBE_PATH));
|
|
61
|
+
return { kind: 'live', scope: me.data?.tokenScope, email: me.data?.user?.email ?? null };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (error.status !== 404)
|
|
65
|
+
return classify(error);
|
|
66
|
+
}
|
|
67
|
+
// Older API without /v1/me: any authenticated endpoint still proves the token lives.
|
|
68
|
+
try {
|
|
69
|
+
await client.get(AUTH_PROBE_PATH, { query: { limit: '1' } });
|
|
70
|
+
return { kind: 'live' };
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return classify(error);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
27
76
|
/**
|
|
28
77
|
* Confirm the stored credential actually authenticates, so a mistyped or half-pasted token is caught
|
|
29
78
|
* here instead of on the user's next real command. Best-effort: it never fails the login (the token
|
|
@@ -112,10 +161,21 @@ const readToken = async (binaryName) => {
|
|
|
112
161
|
return token;
|
|
113
162
|
};
|
|
114
163
|
// Save the token and report where it went (masked). Shared by the browser and paste paths.
|
|
115
|
-
|
|
116
|
-
|
|
164
|
+
//
|
|
165
|
+
// `project` is written only when the browser flow reports one: a user credential stores no
|
|
166
|
+
// project of its own, so without this the very next command would need a `--project`. The paste
|
|
167
|
+
// path passes nothing, which leaves any existing setting alone rather than clearing it.
|
|
168
|
+
const persistToken = (token, project) => {
|
|
169
|
+
const path = (0, config_1.writeUserConfig)({
|
|
170
|
+
...(0, config_1.readUserConfig)(),
|
|
171
|
+
bearerToken: token,
|
|
172
|
+
...(project === undefined ? {} : { project }),
|
|
173
|
+
});
|
|
117
174
|
const color = (0, output_1.supportsColor)(process.stderr);
|
|
118
175
|
(0, output_1.write)(`${(0, output_1.paint)('Saved', 'green', color)} ${(0, config_1.maskToken)(token)} to ${path}`, process.stderr);
|
|
176
|
+
if (project !== undefined) {
|
|
177
|
+
(0, output_1.write)(`${(0, output_1.paint)('Project', 'green', color)} ${project} (change it with \`config set project\`)`, process.stderr);
|
|
178
|
+
}
|
|
119
179
|
return { color };
|
|
120
180
|
};
|
|
121
181
|
// A set environment variable outranks the stored token (see the precedence in config.ts), so without
|
|
@@ -151,9 +211,9 @@ const registerAuthCommands = (root, binaryName, clientFor) => {
|
|
|
151
211
|
const color = (0, output_1.supportsColor)(process.stderr);
|
|
152
212
|
const apiBaseUrl = (0, config_1.resolveConfig)({}).config.baseURL ?? DEFAULT_BASE_URL;
|
|
153
213
|
(0, output_1.write)((0, output_1.paint)('Opening your browser to authorize this CLI…', 'dim', color), process.stderr);
|
|
154
|
-
let
|
|
214
|
+
let login;
|
|
155
215
|
try {
|
|
156
|
-
|
|
216
|
+
login = await (0, browser_login_1.browserLogin)({
|
|
157
217
|
apiBaseUrl,
|
|
158
218
|
platformOrigin: (0, browser_login_1.platformOriginFor)(apiBaseUrl),
|
|
159
219
|
clientName: `CLI on ${(0, node_os_1.hostname)()}`,
|
|
@@ -164,8 +224,8 @@ const registerAuthCommands = (root, binaryName, clientFor) => {
|
|
|
164
224
|
throw new errors_1.UsageError(`Browser login failed: ${error.message}\n` +
|
|
165
225
|
`Run \`${binaryName} auth login --paste\` to paste or pipe a token instead.`);
|
|
166
226
|
}
|
|
167
|
-
const { color: savedColor } = persistToken(token);
|
|
168
|
-
warnEnvShadow(token, savedColor);
|
|
227
|
+
const { color: savedColor } = persistToken(login.token, login.project);
|
|
228
|
+
warnEnvShadow(login.token, savedColor);
|
|
169
229
|
return;
|
|
170
230
|
}
|
|
171
231
|
const token = await readToken(binaryName);
|
|
@@ -184,24 +244,46 @@ const registerAuthCommands = (root, binaryName, clientFor) => {
|
|
|
184
244
|
});
|
|
185
245
|
auth
|
|
186
246
|
.command('status')
|
|
187
|
-
.description('Show which credential would be used,
|
|
188
|
-
.action(() => {
|
|
247
|
+
.description('Show which credential would be used, where it came from, and whether it still works')
|
|
248
|
+
.action(async () => {
|
|
189
249
|
const color = (0, output_1.supportsColor)(process.stderr);
|
|
190
250
|
const fromEnv = process.env['ROARK_API_BEARER_TOKEN'];
|
|
191
251
|
const stored = (0, config_1.readUserConfig)().bearerToken;
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
252
|
+
const token = fromEnv ?? stored;
|
|
253
|
+
if (!token) {
|
|
254
|
+
(0, output_1.write)(`${(0, output_1.paint)('Not authenticated.', 'yellow', color)} Run \`${binaryName} auth login\`.`, process.stderr);
|
|
255
|
+
process.exitCode = 3;
|
|
197
256
|
return;
|
|
198
257
|
}
|
|
199
|
-
|
|
200
|
-
|
|
258
|
+
const source = fromEnv ? 'ROARK_API_BEARER_TOKEN' : (0, config_1.userConfigPath)();
|
|
259
|
+
// Ask the API rather than trusting the file. Without this the command reports a revoked
|
|
260
|
+
// credential as authenticated, which is the opposite of what someone runs it to find out.
|
|
261
|
+
const state = clientFor ? await describeCredential(clientFor) : { kind: 'unknown', reason: 'no client' };
|
|
262
|
+
if (state.kind === 'rejected') {
|
|
263
|
+
(0, output_1.write)(`${(0, output_1.paint)('Rejected', 'red', color)} by the API (401): the credential in ${source} (${(0, config_1.maskToken)(token)}) is wrong, expired, or revoked.`, process.stderr);
|
|
264
|
+
(0, output_1.write)((0, output_1.paint)(`Run \`${binaryName} auth login\` to replace it.`, 'dim', color), process.stderr);
|
|
265
|
+
process.exitCode = 3;
|
|
201
266
|
return;
|
|
202
267
|
}
|
|
203
|
-
|
|
204
|
-
|
|
268
|
+
if (state.kind === 'unknown') {
|
|
269
|
+
// Deliberately not "Authenticated": we do not know. Saying so is the whole point.
|
|
270
|
+
(0, output_1.write)(`${(0, output_1.paint)('Unverified', 'yellow', color)} credential in ${source} (${(0, config_1.maskToken)(token)}): could not reach the API (${state.reason}).`, process.stderr);
|
|
271
|
+
}
|
|
272
|
+
else {
|
|
273
|
+
const who = state.email ? ` as ${state.email}` : '';
|
|
274
|
+
(0, output_1.write)(`${(0, output_1.paint)('Authenticated', 'green', color)}${who} via ${source} (${(0, config_1.maskToken)(token)})`, process.stderr);
|
|
275
|
+
if (state.scope === 'USER') {
|
|
276
|
+
// A user credential reaches every project you belong to, so which one it acts on is part
|
|
277
|
+
// of the answer, not a detail.
|
|
278
|
+
const project = (0, config_1.loadConfig)().project;
|
|
279
|
+
(0, output_1.write)((0, output_1.paint)(project ?
|
|
280
|
+
`Acting on project ${project}. Change it with \`${binaryName} config set project <id>\`.`
|
|
281
|
+
: `No project selected: commands will fail until you set one with \`${binaryName} config set project <id>\`.`, 'dim', color), process.stderr);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (fromEnv && stored) {
|
|
285
|
+
(0, output_1.write)((0, output_1.paint)(`A token is also stored in ${(0, config_1.userConfigPath)()}; the environment variable wins.`, 'dim', color), process.stderr);
|
|
286
|
+
}
|
|
205
287
|
});
|
|
206
288
|
root.addCommand(auth);
|
|
207
289
|
};
|
package/runtime/config.d.ts
CHANGED
|
@@ -14,6 +14,18 @@
|
|
|
14
14
|
export interface CliConfig {
|
|
15
15
|
bearerToken?: string;
|
|
16
16
|
baseURL?: string;
|
|
17
|
+
/**
|
|
18
|
+
* The project commands act on, sent as `X-Roark-Project-Id`.
|
|
19
|
+
*
|
|
20
|
+
* Only meaningful for a user credential (what `auth login` now stores): it can reach every
|
|
21
|
+
* project you belong to, so each request has to name one. A project API key already names its
|
|
22
|
+
* own project and ignores this.
|
|
23
|
+
*
|
|
24
|
+
* Unlike `baseURL`, this is safe to take from a project file. It selects which of your own
|
|
25
|
+
* projects you act on; it cannot send your credential anywhere new. See `unsafeBaseUrlRedirect`
|
|
26
|
+
* for the key where that distinction matters.
|
|
27
|
+
*/
|
|
28
|
+
project?: string;
|
|
17
29
|
timeout?: number;
|
|
18
30
|
maxRetries?: number;
|
|
19
31
|
}
|
package/runtime/config.js
CHANGED
|
@@ -62,6 +62,7 @@ const number = (value) => {
|
|
|
62
62
|
const fromEnvironment = () => ({
|
|
63
63
|
...(process.env['ROARK_API_BEARER_TOKEN'] ? { bearerToken: process.env['ROARK_API_BEARER_TOKEN'] } : {}),
|
|
64
64
|
...(process.env['ROARK_BASE_URL'] ? { baseURL: process.env['ROARK_BASE_URL'] } : {}),
|
|
65
|
+
...(process.env['ROARK_PROJECT_ID'] ? { project: process.env['ROARK_PROJECT_ID'] } : {}),
|
|
65
66
|
...(number(process.env['ROARK_TIMEOUT']) !== undefined ?
|
|
66
67
|
{ timeout: number(process.env['ROARK_TIMEOUT']) }
|
|
67
68
|
: {}),
|
package/runtime/program.d.ts
CHANGED
package/runtime/program.js
CHANGED
|
@@ -37,6 +37,7 @@ const isDestructive = (command) => DESTRUCTIVE_VERBS.has(command.commandPath.at(
|
|
|
37
37
|
const addGlobalOptions = (command, extra) => {
|
|
38
38
|
command
|
|
39
39
|
.option('--base-url <url>', 'API base URL')
|
|
40
|
+
.option('--project <id>', 'project to act on (or ROARK_PROJECT_ID, or `roark config set project`)')
|
|
40
41
|
.option('--token <token>', 'bearer token (prefer ROARK_API_BEARER_TOKEN or `auth login`)')
|
|
41
42
|
.option('--timeout <ms>', 'request timeout in milliseconds')
|
|
42
43
|
.option('--max-retries <count>', 'retries for retryable failures')
|
|
@@ -81,6 +82,7 @@ const clientFor = (options, requiresAuth = true) => {
|
|
|
81
82
|
const overrides = {
|
|
82
83
|
...(options.token === undefined ? {} : { bearerToken: options.token }),
|
|
83
84
|
...(options.baseUrl === undefined ? {} : { baseURL: options.baseUrl }),
|
|
85
|
+
...(options.project === undefined ? {} : { project: options.project }),
|
|
84
86
|
...(options.timeout === undefined ? {} : { timeout: Number(options.timeout) }),
|
|
85
87
|
...(options.maxRetries === undefined ? {} : { maxRetries: Number(options.maxRetries) }),
|
|
86
88
|
};
|
|
@@ -94,6 +96,9 @@ const clientFor = (options, requiresAuth = true) => {
|
|
|
94
96
|
// The SDK constructor rejects an absent token, so a public endpoint is given
|
|
95
97
|
// an empty one rather than being made to demand a credential it never sends.
|
|
96
98
|
bearerToken: config.bearerToken ?? '',
|
|
99
|
+
// Sent as `X-Roark-Project-Id`. Absent unless configured, because a project key names its own
|
|
100
|
+
// project and an empty header is a 400 rather than a no-op.
|
|
101
|
+
...(config.project === undefined ? {} : { project: config.project }),
|
|
97
102
|
...(config.baseURL === undefined ? {} : { baseURL: config.baseURL }),
|
|
98
103
|
...(config.timeout === undefined ? {} : { timeout: config.timeout }),
|
|
99
104
|
...(config.maxRetries === undefined ? {} : { maxRetries: config.maxRetries }),
|
package/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "0.
|
|
1
|
+
export declare const version = "0.31.1";
|
package/version.js
CHANGED