@rosthq/cli 0.5.11 → 0.5.13
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/dist/auth.d.ts +2 -0
- package/dist/auth.d.ts.map +1 -1
- package/dist/device-auth.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +98 -8
- package/dist/index.js.map +2 -2
- package/dist/token-store.d.ts +1 -0
- package/dist/token-store.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/auth.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { CliConfig } from "./config.js";
|
|
2
2
|
import type { CliSession } from "./token-store.js";
|
|
3
3
|
export declare function loginWithBrowser(config: CliConfig): Promise<CliSession>;
|
|
4
|
+
export declare function shouldRefreshSession(session: CliSession, now?: number): boolean;
|
|
5
|
+
export declare function refreshCliSessionIfNeeded(config: CliConfig, session: CliSession, now?: number): Promise<CliSession>;
|
|
4
6
|
/**
|
|
5
7
|
* Best-effort attempt to open a URL in the default browser. Returns true when a
|
|
6
8
|
* launcher was invoked, false when no launcher is available for the platform.
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAUnD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA2C7E;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,UAAU,EAAE,GAAG,SAAa,GAAG,OAAO,CAKnF;AAED,wBAAsB,yBAAyB,CAC7C,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,UAAU,EACnB,GAAG,SAAa,GACf,OAAO,CAAC,UAAU,CAAC,CA4BrB;AASD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBlE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../src/device-auth.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC3C,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAGhD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnG,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAwBF,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;
|
|
1
|
+
{"version":3,"file":"device-auth.d.ts","sourceRoot":"","sources":["../src/device-auth.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAQnD,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC3C,CAAC;AAGF,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAGhD,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IACnG,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB,CAAC;AAwBF,qBAAa,gBAAiB,SAAQ,KAAK;gBAC7B,OAAO,EAAE,MAAM;CAI5B;AAiCD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,UAAU,CAAC,CAuFrB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { loginWithBrowser } from "./auth.js";
|
|
2
|
+
import { loginWithBrowser, refreshCliSessionIfNeeded } from "./auth.js";
|
|
3
3
|
import { loginWithDeviceCode } from "./device-auth.js";
|
|
4
4
|
import { type TokenStore } from "./token-store.js";
|
|
5
5
|
type CliIo = {
|
|
@@ -12,6 +12,7 @@ type MainOptions = {
|
|
|
12
12
|
io?: CliIo;
|
|
13
13
|
login?: typeof loginWithBrowser;
|
|
14
14
|
deviceLogin?: typeof loginWithDeviceCode;
|
|
15
|
+
refreshSession?: typeof refreshCliSessionIfNeeded;
|
|
15
16
|
};
|
|
16
17
|
export declare function main(argv?: string[], options?: MainOptions): Promise<number>;
|
|
17
18
|
export declare function isCliEntrypoint(moduleUrl: string, argvPath: string | undefined): boolean;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,gBAAgB,EAAE,yBAAyB,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAoB,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAoB,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAYrE,KAAK,KAAK,GAAG;IACX,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC1C,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAK1C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,KAAK,WAAW,GAAG;IACjB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,EAAE,CAAC,EAAE,KAAK,CAAC;IACX,KAAK,CAAC,EAAE,OAAO,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,mBAAmB,CAAC;IACzC,cAAc,CAAC,EAAE,OAAO,yBAAyB,CAAC;CACnD,CAAC;AAEF,wBAAsB,IAAI,CAAC,IAAI,WAAwB,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CA+LnG;AAoZD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUxF"}
|
package/dist/index.js
CHANGED
|
@@ -36984,6 +36984,7 @@ var run = promisify(execFile);
|
|
|
36984
36984
|
var oauthCallbackSchema = external_exports.object({
|
|
36985
36985
|
code: external_exports.string().min(1)
|
|
36986
36986
|
}).strict();
|
|
36987
|
+
var REFRESH_SKEW_MS = 5 * 60 * 1e3;
|
|
36987
36988
|
async function loginWithBrowser(config2) {
|
|
36988
36989
|
const callback = await waitForOAuthCallback();
|
|
36989
36990
|
const supabase = createClient(config2.supabaseUrl, config2.supabaseAnonKey, {
|
|
@@ -37021,9 +37022,43 @@ async function loginWithBrowser(config2) {
|
|
|
37021
37022
|
}
|
|
37022
37023
|
return {
|
|
37023
37024
|
accessToken,
|
|
37025
|
+
...sessionData.session.refresh_token ? { refreshToken: sessionData.session.refresh_token } : {},
|
|
37024
37026
|
...sessionData.session.expires_at ? { expiresAt: new Date(sessionData.session.expires_at * 1e3).toISOString() } : {}
|
|
37025
37027
|
};
|
|
37026
37028
|
}
|
|
37029
|
+
function shouldRefreshSession(session, now = Date.now()) {
|
|
37030
|
+
if (!session.refreshToken || !session.expiresAt) {
|
|
37031
|
+
return false;
|
|
37032
|
+
}
|
|
37033
|
+
return new Date(session.expiresAt).getTime() <= now + REFRESH_SKEW_MS;
|
|
37034
|
+
}
|
|
37035
|
+
async function refreshCliSessionIfNeeded(config2, session, now = Date.now()) {
|
|
37036
|
+
const refreshToken = session.refreshToken;
|
|
37037
|
+
if (!refreshToken || !shouldRefreshSession(session, now)) {
|
|
37038
|
+
return session;
|
|
37039
|
+
}
|
|
37040
|
+
const supabase = createClient(config2.supabaseUrl, config2.supabaseAnonKey, {
|
|
37041
|
+
auth: {
|
|
37042
|
+
autoRefreshToken: false,
|
|
37043
|
+
detectSessionInUrl: false,
|
|
37044
|
+
persistSession: false
|
|
37045
|
+
}
|
|
37046
|
+
});
|
|
37047
|
+
const { data, error: error51 } = await supabase.auth.refreshSession({ refresh_token: refreshToken });
|
|
37048
|
+
if (error51) {
|
|
37049
|
+
throw error51;
|
|
37050
|
+
}
|
|
37051
|
+
const refreshed = data.session;
|
|
37052
|
+
const accessToken = refreshed?.access_token;
|
|
37053
|
+
if (!accessToken) {
|
|
37054
|
+
throw new Error("Supabase did not return a refreshed access token.");
|
|
37055
|
+
}
|
|
37056
|
+
return {
|
|
37057
|
+
accessToken,
|
|
37058
|
+
refreshToken: refreshed.refresh_token ?? refreshToken,
|
|
37059
|
+
...refreshed.expires_at ? { expiresAt: new Date(refreshed.expires_at * 1e3).toISOString() } : {}
|
|
37060
|
+
};
|
|
37061
|
+
}
|
|
37027
37062
|
async function openBrowser(url2) {
|
|
37028
37063
|
const opened = await tryOpenBrowser(url2);
|
|
37029
37064
|
if (!opened) {
|
|
@@ -37131,6 +37166,7 @@ async function defaultVerifyOtp(config2, input) {
|
|
|
37131
37166
|
}
|
|
37132
37167
|
return {
|
|
37133
37168
|
accessToken,
|
|
37169
|
+
...data.session?.refresh_token ? { refreshToken: data.session.refresh_token } : {},
|
|
37134
37170
|
...data.session?.expires_at ? { expiresAt: new Date(data.session.expires_at * 1e3).toISOString() } : {}
|
|
37135
37171
|
};
|
|
37136
37172
|
}
|
|
@@ -37241,6 +37277,7 @@ import { promisify as promisify2 } from "node:util";
|
|
|
37241
37277
|
var execFile2 = promisify2(execFileCallback);
|
|
37242
37278
|
var sessionSchema = external_exports.object({
|
|
37243
37279
|
accessToken: external_exports.string().min(1),
|
|
37280
|
+
refreshToken: external_exports.string().min(1).optional(),
|
|
37244
37281
|
expiresAt: external_exports.string().datetime({ offset: true }).optional()
|
|
37245
37282
|
}).strict();
|
|
37246
37283
|
var serviceName = `${cliBrand.binName}-cli`;
|
|
@@ -37332,7 +37369,7 @@ function createTokenStore(options = {}) {
|
|
|
37332
37369
|
if (env[allowFileStoreKey] === "1") {
|
|
37333
37370
|
const warning = [
|
|
37334
37371
|
"WARNING: using the development-only plain-file CLI token store.",
|
|
37335
|
-
"It stores only
|
|
37372
|
+
"It stores only CLI session tokens, never tenant secrets, API keys, or vault refs."
|
|
37336
37373
|
].join(" ");
|
|
37337
37374
|
(options.stderr ?? process.stderr).write(`${warning}
|
|
37338
37375
|
`);
|
|
@@ -37346,7 +37383,7 @@ function createTokenStore(options = {}) {
|
|
|
37346
37383
|
[
|
|
37347
37384
|
"No OS credential store is available (the CLI uses the macOS Keychain by default; there is no Windows or Linux keychain integration).",
|
|
37348
37385
|
`For headless Linux, CI runners, or any no-keychain host, export ${allowFileStoreKey}=1 (set it for every invocation) to opt into the development-only plain-file token store.`,
|
|
37349
|
-
"It is not encrypted: it writes an owner-only file (directory 0700, file 0600) holding only
|
|
37386
|
+
"It is not encrypted: it writes an owner-only file (directory 0700, file 0600) holding only CLI session tokens \u2014 never tenant secrets, API keys, or vault refs."
|
|
37350
37387
|
].join(" ")
|
|
37351
37388
|
);
|
|
37352
37389
|
}
|
|
@@ -42305,7 +42342,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
42305
42342
|
order: 48,
|
|
42306
42343
|
title: "CLI and MCP installation guide",
|
|
42307
42344
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
42308
|
-
version: "2026-06-20.
|
|
42345
|
+
version: "2026-06-20.5",
|
|
42309
42346
|
public: true,
|
|
42310
42347
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
42311
42348
|
stages: ["company_setup", "staffing"],
|
|
@@ -42374,7 +42411,7 @@ The MCP server is remote and token-backed. There is no local MCP daemon to insta
|
|
|
42374
42411
|
A headless agent cannot complete setup unattended. Two things always require a human, so plan to announce the handoff rather than stall silently:
|
|
42375
42412
|
|
|
42376
42413
|
- **Device-code approval at first login.** \`{{cli}} login --device\` prints a code; a human must open the workspace URL in a browser, sign in with Google, match the code, and approve.
|
|
42377
|
-
- **Durable gated commands.** Setting or approving a Charter, staffing a seat, and taking an agent live each
|
|
42414
|
+
- **Durable gated commands.** Setting or approving a Charter, staffing a seat, and taking an agent live each require a human approval boundary (an agent never approves its own request \u2014 see the confirmations-guide). In a **headless/agent** session, or over MCP, the command surfaces the \`confirmation_id\` and an \`approveVia\` URL for a human and stops. When a logged-in human runs \`{{cli}} command <id>\` from an **interactive TTY**, the official CLI completes that returned confirmation inline by calling \`confirmation.approve\` and printing the approved command output. (\`{{cli}} mcp install\` follows the same interactive rule for token minting, with \`--skip-permissions\` / \`{{envPrefix}}_CLI_SKIP_PERMISSIONS=1\` as an explicit headless loosening \u2014 see Approving the token mint below.)
|
|
42378
42415
|
|
|
42379
42416
|
Treat these as blocking prerequisites: the agent prepares the request and waits for the human approver.
|
|
42380
42417
|
|
|
@@ -42453,7 +42490,7 @@ Notes:
|
|
|
42453
42490
|
|
|
42454
42491
|
By default the CLI stores the session in the **macOS Keychain** (on darwin). There is no built-in Windows or Linux keychain integration today \u2014 on any non-macOS host, or any machine without a keychain, the CLI errors with "No OS credential store is available" unless you opt into the file store.
|
|
42455
42492
|
|
|
42456
|
-
For headless Linux, CI runners, or any no-keychain environment, set \`{{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1\` to opt into the **development-only plain-file token store**. It is **not encrypted** \u2014 it writes the file with owner-only permissions (directory \`0700\`, file \`0600\`) and prints a stderr warning. It stores **only
|
|
42493
|
+
For headless Linux, CI runners, or any no-keychain environment, set \`{{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1\` to opt into the **development-only plain-file token store**. It is **not encrypted** \u2014 it writes the file with owner-only permissions (directory \`0700\`, file \`0600\`) and prints a stderr warning. It stores **only CLI session tokens** (access + refresh token), never tenant secrets, API keys, or vault refs. Treat that file as a credential: do not bake it into a shared image and do not commit it.
|
|
42457
42494
|
|
|
42458
42495
|
The CLI checks this flag on **every** invocation, not just \`login\`. Export it for the whole shell or CI job (so \`{{cli}} whoami\`, \`{{cli}} onboard status\`, and the rest also find the file store), not only on the login line:
|
|
42459
42496
|
|
|
@@ -42462,7 +42499,7 @@ export {{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1
|
|
|
42462
42499
|
{{cli}} login --device
|
|
42463
42500
|
\`\`\`
|
|
42464
42501
|
|
|
42465
|
-
For CI, do not run an interactive device login on every job (a device code still needs a human approver, which CI does not have).
|
|
42502
|
+
For CI, do not run an interactive device login on every job (a device code still needs a human approver, which CI does not have). The CLI stores the Supabase refresh token from \`login\` / \`login --device\` and refreshes the access token before command execution when it is close to expiry, so normal local agent sessions survive the short access-token window without repeated browser approvals. The refresh token is still a user credential; if refresh fails or is revoked, run \`{{cli}} login --device\` again. For ongoing unattended automation, prefer a **seat-scoped MCP token** used directly by your MCP client, and treat that client config as a secret. Such a token now defaults to a **90-day** expiry, so for automation that must outlive that window, mint it with an explicit \`--expires-in <days>\` (up to 365) or, accepting the long-lived-credential tradeoff, \`--no-expiry\` \u2014 e.g. \`mcp install --scope seat --seat-id <id> --expires-in 365\`. Rotate it before it lapses (\`--rotate <old-token-id>\`), and revoke and re-mint if a runner image is rebuilt or shared.
|
|
42466
42503
|
|
|
42467
42504
|
## First-run path
|
|
42468
42505
|
|
|
@@ -43323,7 +43360,7 @@ Keep agent scope tight at first. Approve more autonomy only after evidence. Use
|
|
|
43323
43360
|
order: 71,
|
|
43324
43361
|
title: "Confirmations and human gates guide",
|
|
43325
43362
|
summary: "How {{brand}} routes authority-changing work through human confirmation, and why agents never approve their own requests.",
|
|
43326
|
-
version: "2026-06-
|
|
43363
|
+
version: "2026-06-20.1",
|
|
43327
43364
|
public: true,
|
|
43328
43365
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
43329
43366
|
stages: ["graph_design", "charter_design", "staffing", "operating_rhythm"],
|
|
@@ -43356,7 +43393,7 @@ The \`dangerous\` confirmation **level** is rare and is not the same as the **ri
|
|
|
43356
43393
|
|
|
43357
43394
|
## What a gated command returns over MCP
|
|
43358
43395
|
|
|
43359
|
-
A gated command does not mutate when an agent calls it. It returns a pending confirmation with the command id, a risk level, and an \`approveVia\` block containing a web URL and the exact \`{{cli}} command confirmation.approve --json ...\` line. The human approves through the web link or the CLI; the agent surfaces the link and stops.
|
|
43396
|
+
A gated command does not mutate when an agent calls it. It returns a pending confirmation with the command id, a risk level, and an \`approveVia\` block containing a web URL and the exact \`{{cli}} command confirmation.approve --json ...\` line. The human approves through the web link or the CLI; the agent surfaces the link and stops. A logged-in human at an interactive CLI can run their own gated \`{{cli}} command <id>\` without copy-pasting the approval command because the CLI completes the returned confirmation inline.
|
|
43360
43397
|
|
|
43361
43398
|
\`\`\`bash
|
|
43362
43399
|
# Human approves a pending confirmation
|
|
@@ -46181,6 +46218,7 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
46181
46218
|
};
|
|
46182
46219
|
const login = options.login ?? loginWithBrowser;
|
|
46183
46220
|
const deviceLogin = options.deviceLogin ?? loginWithDeviceCode;
|
|
46221
|
+
const refreshSession = options.refreshSession ?? refreshCliSessionIfNeeded;
|
|
46184
46222
|
const [rawCommand, ...args] = argv;
|
|
46185
46223
|
const command = rawCommand === "--help" || rawCommand === "-h" ? "help" : rawCommand;
|
|
46186
46224
|
if (!isCommand(command) && !isOperationGroup(command)) {
|
|
@@ -46281,6 +46319,29 @@ async function main(argv = process.argv.slice(2), options = {}) {
|
|
|
46281
46319
|
return 1;
|
|
46282
46320
|
}
|
|
46283
46321
|
const config2 = loadConfig();
|
|
46322
|
+
let refreshed;
|
|
46323
|
+
try {
|
|
46324
|
+
refreshed = await refreshSession(config2, session);
|
|
46325
|
+
} catch (error51) {
|
|
46326
|
+
io.stderr.write(
|
|
46327
|
+
`${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
46328
|
+
Session refresh failed. Run ${cliBrand.binName} login --device again.
|
|
46329
|
+
`
|
|
46330
|
+
);
|
|
46331
|
+
return 1;
|
|
46332
|
+
}
|
|
46333
|
+
if (refreshed !== session) {
|
|
46334
|
+
session = refreshed;
|
|
46335
|
+
try {
|
|
46336
|
+
await store.write(session);
|
|
46337
|
+
} catch (error51) {
|
|
46338
|
+
io.stderr.write(
|
|
46339
|
+
`Session refreshed, but storing it failed: ${redactForLog(error51 instanceof Error ? error51.message : String(error51))}
|
|
46340
|
+
Run ${cliBrand.binName} login --device again if the next command asks you to authenticate.
|
|
46341
|
+
`
|
|
46342
|
+
);
|
|
46343
|
+
}
|
|
46344
|
+
}
|
|
46284
46345
|
const client = new CommandClient({
|
|
46285
46346
|
appUrl: config2.appUrl,
|
|
46286
46347
|
token: session.accessToken
|
|
@@ -46555,9 +46616,38 @@ async function printCommandOutput(io, client, commandId, body = {}, format, opti
|
|
|
46555
46616
|
`);
|
|
46556
46617
|
return 0;
|
|
46557
46618
|
} catch (error51) {
|
|
46619
|
+
if (io.interactive === true) {
|
|
46620
|
+
const pending = pendingConfirmationFromError(error51);
|
|
46621
|
+
if (pending && pending.commandId === commandId) {
|
|
46622
|
+
io.stderr.write(`Approving ${pending.commandId} confirmation ${pending.confirmationId} as the interactive CLI user.
|
|
46623
|
+
`);
|
|
46624
|
+
try {
|
|
46625
|
+
const approved = await client.execute("confirmation.approve", { confirmation_id: pending.confirmationId });
|
|
46626
|
+
const approvedOutput = asRecord2(approved.output).output ?? approved.output;
|
|
46627
|
+
const rendered = format ? format(approvedOutput) : JSON.stringify(approvedOutput, null, 2);
|
|
46628
|
+
io.stdout.write(`${rendered}
|
|
46629
|
+
`);
|
|
46630
|
+
return 0;
|
|
46631
|
+
} catch (approvalError) {
|
|
46632
|
+
return printCommandError(io, approvalError);
|
|
46633
|
+
}
|
|
46634
|
+
}
|
|
46635
|
+
}
|
|
46558
46636
|
return printCommandError(io, error51);
|
|
46559
46637
|
}
|
|
46560
46638
|
}
|
|
46639
|
+
function pendingConfirmationFromError(error51) {
|
|
46640
|
+
if (!(error51 instanceof CommandClientError) || !error51.response || error51.response.ok || error51.response.error.code !== "COMMAND_CONFIRMATION_REQUIRED") {
|
|
46641
|
+
return null;
|
|
46642
|
+
}
|
|
46643
|
+
const details = asRecord2(error51.response.error.details);
|
|
46644
|
+
const confirmationId = details.confirmationId;
|
|
46645
|
+
const commandId = details.commandId;
|
|
46646
|
+
if (typeof confirmationId !== "string" || confirmationId.length === 0 || typeof commandId !== "string" || commandId.length === 0) {
|
|
46647
|
+
return null;
|
|
46648
|
+
}
|
|
46649
|
+
return { confirmationId, commandId };
|
|
46650
|
+
}
|
|
46561
46651
|
function printCommandError(io, error51) {
|
|
46562
46652
|
if (error51 instanceof CommandClientError && error51.response && !error51.response.ok) {
|
|
46563
46653
|
const help = error51.response.error.help;
|