@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 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.
@@ -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;AAQnD,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA0C7E;AASD;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAqBlE"}
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;AAgCD,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,SAAS,EACjB,EAAE,EAAE,KAAK,EACT,IAAI,GAAE,eAAoB,GACzB,OAAO,CAAC,UAAU,CAAC,CAuFrB"}
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;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAMA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,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;CAC1C,CAAC;AAEF,wBAAsB,IAAI,CAAC,IAAI,WAAwB,EAAE,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAyKnG;AAwXD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAUxF"}
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 the CLI session token, never tenant secrets, API keys, or vault refs."
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 the CLI session token \u2014 never tenant secrets, API keys, or vault refs."
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.3",
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 open a confirmation that a human approves (an agent never approves its own request \u2014 see the confirmations-guide). The CLI surfaces the \`confirmation_id\` and an \`approveVia\` URL for the human. (\`{{cli}} mcp install\` is a partial exception: for an **interactive** operator it completes its own token-mint approval inline; in a **headless/agent** session it surfaces the confirmation and stops unless \`--skip-permissions\` / \`{{envPrefix}}_CLI_SKIP_PERMISSIONS=1\` is set \u2014 see Approving the token mint below.)
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 the CLI session 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.
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). Note that the CLI session is a **short-lived** auth session (it carries an expiry), not a durable credential \u2014 so storing the session file as a long-lived CI secret will stop working once it expires, and re-approving a device code unattended is not possible. 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.
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-18.1",
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;