@rosthq/cli 0.7.27 → 0.7.28

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/index.js CHANGED
@@ -37346,15 +37346,25 @@ ${secret}
37346
37346
  }
37347
37347
  };
37348
37348
  var DarwinTokenStore = class extends KeychainTokenStore {
37349
- constructor(run2 = runExecFile, filePath) {
37349
+ constructor(run2 = runExecFile, filePath, stderr = process.stderr) {
37350
37350
  super(run2);
37351
37351
  this.filePath = filePath;
37352
+ this.stderr = stderr;
37352
37353
  }
37353
37354
  filePath;
37355
+ stderr;
37354
37356
  backend = null;
37357
+ fileBackendReason = null;
37355
37358
  fileStore = null;
37356
37359
  async read() {
37357
- return await this.selectBackend() === "file" ? this.file().read() : super.read();
37360
+ if (await this.selectBackend() === "file") {
37361
+ return this.file().read();
37362
+ }
37363
+ const keychainSession = await super.read();
37364
+ if (keychainSession) {
37365
+ return keychainSession;
37366
+ }
37367
+ return this.readFileStoreBestEffort();
37358
37368
  }
37359
37369
  async write(session) {
37360
37370
  if (await this.selectBackend() === "file") {
@@ -37362,20 +37372,49 @@ var DarwinTokenStore = class extends KeychainTokenStore {
37362
37372
  return;
37363
37373
  }
37364
37374
  await super.write(session);
37375
+ if (await this.keychainReadBackFailed(session)) {
37376
+ this.backend = "file";
37377
+ this.fileBackendReason = "unreadable";
37378
+ await this.file().write(session);
37379
+ await super.clear();
37380
+ this.warnKeychainNotReadableBack();
37381
+ }
37365
37382
  }
37366
37383
  async clear() {
37367
- if (await this.selectBackend() === "file") {
37368
- await this.file().clear();
37369
- return;
37370
- }
37371
37384
  await super.clear();
37385
+ await this.file().clear();
37372
37386
  }
37373
37387
  describe() {
37374
- return this.backend === "file" ? "owner-only file store (no usable macOS keychain)" : super.describe();
37388
+ if (this.backend !== "file") {
37389
+ return super.describe();
37390
+ }
37391
+ return this.fileBackendReason === "unreadable" ? "owner-only file store (macOS keychain not readable back cross-process)" : "owner-only file store (no usable macOS keychain)";
37375
37392
  }
37376
37393
  file() {
37377
37394
  return this.fileStore ??= new FileTokenStore(this.filePath);
37378
37395
  }
37396
+ async readFileStoreBestEffort() {
37397
+ try {
37398
+ return await this.file().read();
37399
+ } catch {
37400
+ return null;
37401
+ }
37402
+ }
37403
+ async keychainReadBackFailed(session) {
37404
+ try {
37405
+ const readBack = await super.read();
37406
+ return readBack?.accessToken !== session.accessToken;
37407
+ } catch {
37408
+ return true;
37409
+ }
37410
+ }
37411
+ warnKeychainNotReadableBack() {
37412
+ const envKey = brandEnvKey2("CLI_ALLOW_FILE_TOKEN_STORE");
37413
+ this.stderr.write(
37414
+ `NOTICE: the macOS Keychain accepted the session but it was not readable back (cross-process keychain read failed). Wrote the session to the ${this.file().describe()} instead. For headless / non-interactive use, export ${envKey}=1 so later commands read it directly.
37415
+ `
37416
+ );
37417
+ }
37379
37418
  async selectBackend() {
37380
37419
  if (this.backend) {
37381
37420
  return this.backend;
@@ -37384,7 +37423,12 @@ var DarwinTokenStore = class extends KeychainTokenStore {
37384
37423
  await this.run("security", ["find-generic-password", "-s", serviceName, "-a", accountName, "-w"]);
37385
37424
  this.backend = "keychain";
37386
37425
  } catch (error51) {
37387
- this.backend = isKeychainUnusableError(error51) ? "file" : "keychain";
37426
+ if (isKeychainUnusableError(error51)) {
37427
+ this.backend = "file";
37428
+ this.fileBackendReason = "unusable";
37429
+ } else {
37430
+ this.backend = "keychain";
37431
+ }
37388
37432
  }
37389
37433
  return this.backend;
37390
37434
  }
@@ -37513,7 +37557,7 @@ function createTokenStore(options = {}) {
37513
37557
  }
37514
37558
  const platform2 = options.platform ?? process.platform;
37515
37559
  if (platform2 === "darwin") {
37516
- return new DarwinTokenStore(options.execFile, tokenPath);
37560
+ return new DarwinTokenStore(options.execFile, tokenPath, options.stderr ?? process.stderr);
37517
37561
  }
37518
37562
  if (platform2 === "win32") {
37519
37563
  return new WindowsCredentialTokenStore(options.execFile);
@@ -45005,7 +45049,7 @@ External connectors are being rolled out provider by provider, conservatively (r
45005
45049
  order: 48,
45006
45050
  title: "CLI and MCP installation guide",
45007
45051
  summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
45008
- version: "2026-07-04.18",
45052
+ version: "2026-07-04.19",
45009
45053
  public: true,
45010
45054
  audiences: ["human", "cli", "mcp", "in_app_agent"],
45011
45055
  stages: ["company_setup", "staffing"],
@@ -45193,6 +45237,8 @@ Notes:
45193
45237
 
45194
45238
  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.
45195
45239
 
45240
+ On macOS, the CLI now **verifies the keychain write is readable back** right after \`login\`. On some headless/service hosts the keychain accepts the write but a later command's process cannot read it back (an ACL scoped to the writing binary, a re-locked login keychain, or a launchd/SSH session with no keychain), so \`login\` reports success yet the next command prints "Not logged in". When that readback fails, the CLI **automatically** writes the session to the same owner-only file store (directory \`0700\`, file \`0600\`), prints a one-line stderr notice, and later commands read it back transparently \u2014 so a headless macOS agent "just works" with no flag. For a fully unattended macOS host, export \`{{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1\` anyway so every command uses the file store directly.
45241
+
45196
45242
  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.
45197
45243
 
45198
45244
  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: