@rosthq/cli 0.7.119 → 0.7.120
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"implementation-credential-store.d.ts","sourceRoot":"","sources":["../src/implementation-credential-store.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,
|
|
1
|
+
{"version":3,"file":"implementation-credential-store.d.ts","sourceRoot":"","sources":["../src/implementation-credential-store.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,EAUL,KAAK,QAAQ,EACd,MAAM,kBAAkB,CAAC;AAO1B,QAAA,MAAM,uCAAuC;;;;;;;kBAOlC,CAAC;AAEZ,MAAM,MAAM,iCAAiC,GAAG,CAAC,CAAC,KAAK,CACrD,OAAO,uCAAuC,CAC/C,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C,IAAI,IAAI,OAAO,CAAC,iCAAiC,GAAG,IAAI,CAAC,CAAC;IAC1D,KAAK,CAAC,UAAU,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,QAAQ,IAAI,MAAM,CAAC;CACpB,CAAC;AAoRF,qBAAa,4BAA6B,YAAW,6BAA6B;IACpE,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,SAAqB;IAEpD,IAAI,IAAI,OAAO,CAAC,iCAAiC,GAAG,IAAI,CAAC;IAezD,KAAK,CAAC,UAAU,EAAE,iCAAiC,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,QAAQ,IAAI,MAAM;YAIJ,cAAc;CAI7B;AAED,MAAM,MAAM,0CAA0C,GAAG;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;CAC5C,CAAC;AAEF,wBAAgB,mCAAmC,CACjD,OAAO,GAAE,0CAA+C,GACvD,6BAA6B,CAmC/B"}
|
package/dist/index.js
CHANGED
|
@@ -49404,7 +49404,12 @@ var sessionSchema = external_exports.object({
|
|
|
49404
49404
|
expiresAt: external_exports.string().datetime({ offset: true }).optional()
|
|
49405
49405
|
}).strict();
|
|
49406
49406
|
var runExecFile = (file2, args, options) => new Promise((resolve2, reject) => {
|
|
49407
|
-
const
|
|
49407
|
+
const execOptions = {};
|
|
49408
|
+
if (options?.timeoutMs !== void 0) {
|
|
49409
|
+
execOptions.timeout = options.timeoutMs;
|
|
49410
|
+
execOptions.killSignal = "SIGKILL";
|
|
49411
|
+
}
|
|
49412
|
+
const child = execFileCallback(file2, [...args], execOptions, (error51, stdout, stderr) => {
|
|
49408
49413
|
if (error51) {
|
|
49409
49414
|
reject(error51);
|
|
49410
49415
|
return;
|
|
@@ -49420,6 +49425,59 @@ var runExecFile = (file2, args, options) => new Promise((resolve2, reject) => {
|
|
|
49420
49425
|
});
|
|
49421
49426
|
var serviceName = `${cliBrand.binName}-cli`;
|
|
49422
49427
|
var accountName = "session";
|
|
49428
|
+
var KEYCHAIN_WRITE_TIMEOUT_MS = 1e4;
|
|
49429
|
+
var KEYCHAIN_CLEAR_TIMEOUT_MS = 1e4;
|
|
49430
|
+
var MAX_INTERACTIVE_COMMAND_BYTES = 2048;
|
|
49431
|
+
function escapeSecurityInteractiveArgument(value) {
|
|
49432
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
49433
|
+
}
|
|
49434
|
+
function buildKeychainInteractiveWriteCommand(service, account, secret) {
|
|
49435
|
+
const command = [
|
|
49436
|
+
"add-generic-password",
|
|
49437
|
+
"-U",
|
|
49438
|
+
"-s",
|
|
49439
|
+
`"${escapeSecurityInteractiveArgument(service)}"`,
|
|
49440
|
+
"-a",
|
|
49441
|
+
`"${escapeSecurityInteractiveArgument(account)}"`,
|
|
49442
|
+
"-w",
|
|
49443
|
+
`"${escapeSecurityInteractiveArgument(secret)}"`
|
|
49444
|
+
].join(" ");
|
|
49445
|
+
return Buffer.byteLength(command, "utf8") > MAX_INTERACTIVE_COMMAND_BYTES ? null : command;
|
|
49446
|
+
}
|
|
49447
|
+
var KeychainInteractiveWriteError = class extends Error {
|
|
49448
|
+
constructor(message) {
|
|
49449
|
+
super(message);
|
|
49450
|
+
this.name = "KeychainInteractiveWriteError";
|
|
49451
|
+
}
|
|
49452
|
+
};
|
|
49453
|
+
function describeExecFailure(error51) {
|
|
49454
|
+
if (error51 && typeof error51 === "object") {
|
|
49455
|
+
const record2 = error51;
|
|
49456
|
+
if (record2.killed === true) {
|
|
49457
|
+
return "timed out";
|
|
49458
|
+
}
|
|
49459
|
+
if (typeof record2.code === "number") {
|
|
49460
|
+
return `exit code ${record2.code}`;
|
|
49461
|
+
}
|
|
49462
|
+
}
|
|
49463
|
+
return "failed";
|
|
49464
|
+
}
|
|
49465
|
+
async function writeKeychainSecretInteractive(run2, service, account, secret, timeoutMs = KEYCHAIN_WRITE_TIMEOUT_MS) {
|
|
49466
|
+
const command = buildKeychainInteractiveWriteCommand(service, account, secret);
|
|
49467
|
+
if (command === null) {
|
|
49468
|
+
throw new KeychainInteractiveWriteError(
|
|
49469
|
+
"the session is too large to write through the keychain's interactive command interpreter"
|
|
49470
|
+
);
|
|
49471
|
+
}
|
|
49472
|
+
try {
|
|
49473
|
+
await run2("security", ["-i"], { input: `${command}
|
|
49474
|
+
`, timeoutMs });
|
|
49475
|
+
} catch (error51) {
|
|
49476
|
+
throw new KeychainInteractiveWriteError(
|
|
49477
|
+
`the macOS Keychain write did not complete (${describeExecFailure(error51)})`
|
|
49478
|
+
);
|
|
49479
|
+
}
|
|
49480
|
+
}
|
|
49423
49481
|
var KeychainTokenStore = class {
|
|
49424
49482
|
constructor(run2 = runExecFile) {
|
|
49425
49483
|
this.run = run2;
|
|
@@ -49442,18 +49500,15 @@ var KeychainTokenStore = class {
|
|
|
49442
49500
|
}
|
|
49443
49501
|
async write(session) {
|
|
49444
49502
|
sessionSchema.parse(session);
|
|
49445
|
-
|
|
49446
|
-
await this.run(
|
|
49447
|
-
"security",
|
|
49448
|
-
["add-generic-password", "-U", "-s", serviceName, "-a", accountName, "-w"],
|
|
49449
|
-
{ input: `${secret}
|
|
49450
|
-
${secret}
|
|
49451
|
-
` }
|
|
49452
|
-
);
|
|
49503
|
+
await writeKeychainSecretInteractive(this.run, serviceName, accountName, JSON.stringify(session));
|
|
49453
49504
|
}
|
|
49454
49505
|
async clear() {
|
|
49455
49506
|
try {
|
|
49456
|
-
await this.run(
|
|
49507
|
+
await this.run(
|
|
49508
|
+
"security",
|
|
49509
|
+
["delete-generic-password", "-s", serviceName, "-a", accountName],
|
|
49510
|
+
{ timeoutMs: KEYCHAIN_CLEAR_TIMEOUT_MS }
|
|
49511
|
+
);
|
|
49457
49512
|
} catch {
|
|
49458
49513
|
return;
|
|
49459
49514
|
}
|
|
@@ -49488,7 +49543,19 @@ var DarwinTokenStore = class extends KeychainTokenStore {
|
|
|
49488
49543
|
await this.file().write(session);
|
|
49489
49544
|
return;
|
|
49490
49545
|
}
|
|
49491
|
-
|
|
49546
|
+
try {
|
|
49547
|
+
await super.write(session);
|
|
49548
|
+
} catch (error51) {
|
|
49549
|
+
if (!(error51 instanceof KeychainInteractiveWriteError)) {
|
|
49550
|
+
throw error51;
|
|
49551
|
+
}
|
|
49552
|
+
this.backend = "file";
|
|
49553
|
+
this.fileBackendReason = "write-failed";
|
|
49554
|
+
await this.file().write(session);
|
|
49555
|
+
await super.clear();
|
|
49556
|
+
this.warnKeychainWriteFailed(error51);
|
|
49557
|
+
return;
|
|
49558
|
+
}
|
|
49492
49559
|
if (await this.keychainReadBackFailed(session)) {
|
|
49493
49560
|
this.backend = "file";
|
|
49494
49561
|
this.fileBackendReason = "unreadable";
|
|
@@ -49505,7 +49572,14 @@ var DarwinTokenStore = class extends KeychainTokenStore {
|
|
|
49505
49572
|
if (this.backend !== "file") {
|
|
49506
49573
|
return super.describe();
|
|
49507
49574
|
}
|
|
49508
|
-
|
|
49575
|
+
switch (this.fileBackendReason) {
|
|
49576
|
+
case "unreadable":
|
|
49577
|
+
return "owner-only file store (macOS keychain not readable back cross-process)";
|
|
49578
|
+
case "write-failed":
|
|
49579
|
+
return "owner-only file store (macOS keychain write did not complete)";
|
|
49580
|
+
default:
|
|
49581
|
+
return "owner-only file store (no usable macOS keychain)";
|
|
49582
|
+
}
|
|
49509
49583
|
}
|
|
49510
49584
|
file() {
|
|
49511
49585
|
return this.fileStore ??= new FileTokenStore(this.filePath);
|
|
@@ -49526,9 +49600,15 @@ var DarwinTokenStore = class extends KeychainTokenStore {
|
|
|
49526
49600
|
}
|
|
49527
49601
|
}
|
|
49528
49602
|
warnKeychainNotReadableBack() {
|
|
49529
|
-
const envKey = brandEnvKey2("CLI_ALLOW_FILE_TOKEN_STORE");
|
|
49530
49603
|
this.stderr.write(
|
|
49531
|
-
`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
|
|
49604
|
+
`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 \u2014 later commands find it there automatically; no configuration is needed.
|
|
49605
|
+
`
|
|
49606
|
+
);
|
|
49607
|
+
}
|
|
49608
|
+
warnKeychainWriteFailed(error51) {
|
|
49609
|
+
const detail = error51 instanceof Error ? error51.message : "the macOS Keychain write failed";
|
|
49610
|
+
this.stderr.write(
|
|
49611
|
+
`NOTICE: ${detail}. Wrote the session to the ${this.file().describe()} instead \u2014 later commands find it there automatically; no configuration is needed.
|
|
49532
49612
|
`
|
|
49533
49613
|
);
|
|
49534
49614
|
}
|
|
@@ -49843,18 +49923,15 @@ var ImplementationKeychainTokenStore = class {
|
|
|
49843
49923
|
}
|
|
49844
49924
|
async write(credential) {
|
|
49845
49925
|
parseCredential(credential);
|
|
49846
|
-
|
|
49847
|
-
await this.run(
|
|
49848
|
-
"security",
|
|
49849
|
-
["add-generic-password", "-U", "-s", serviceName2, "-a", accountName2, "-w"],
|
|
49850
|
-
{ input: `${secret}
|
|
49851
|
-
${secret}
|
|
49852
|
-
` }
|
|
49853
|
-
);
|
|
49926
|
+
await writeKeychainSecretInteractive(this.run, serviceName2, accountName2, JSON.stringify(credential));
|
|
49854
49927
|
}
|
|
49855
49928
|
async clear() {
|
|
49856
49929
|
try {
|
|
49857
|
-
await this.run(
|
|
49930
|
+
await this.run(
|
|
49931
|
+
"security",
|
|
49932
|
+
["delete-generic-password", "-s", serviceName2, "-a", accountName2],
|
|
49933
|
+
{ timeoutMs: KEYCHAIN_CLEAR_TIMEOUT_MS }
|
|
49934
|
+
);
|
|
49858
49935
|
} catch {
|
|
49859
49936
|
return;
|
|
49860
49937
|
}
|
|
@@ -49889,7 +49966,19 @@ var ImplementationDarwinTokenStore = class extends ImplementationKeychainTokenSt
|
|
|
49889
49966
|
await this.file().write(credential);
|
|
49890
49967
|
return;
|
|
49891
49968
|
}
|
|
49892
|
-
|
|
49969
|
+
try {
|
|
49970
|
+
await super.write(credential);
|
|
49971
|
+
} catch (error51) {
|
|
49972
|
+
if (!(error51 instanceof KeychainInteractiveWriteError)) {
|
|
49973
|
+
throw error51;
|
|
49974
|
+
}
|
|
49975
|
+
this.backend = "file";
|
|
49976
|
+
this.fileBackendReason = "write-failed";
|
|
49977
|
+
await this.file().write(credential);
|
|
49978
|
+
await super.clear();
|
|
49979
|
+
this.warnKeychainWriteFailed(error51);
|
|
49980
|
+
return;
|
|
49981
|
+
}
|
|
49893
49982
|
if (await this.keychainReadBackFailed(credential)) {
|
|
49894
49983
|
this.backend = "file";
|
|
49895
49984
|
this.fileBackendReason = "unreadable";
|
|
@@ -49906,7 +49995,14 @@ var ImplementationDarwinTokenStore = class extends ImplementationKeychainTokenSt
|
|
|
49906
49995
|
if (this.backend !== "file") {
|
|
49907
49996
|
return super.describe();
|
|
49908
49997
|
}
|
|
49909
|
-
|
|
49998
|
+
switch (this.fileBackendReason) {
|
|
49999
|
+
case "unreadable":
|
|
50000
|
+
return "owner-only file store (macOS keychain not readable back cross-process)";
|
|
50001
|
+
case "write-failed":
|
|
50002
|
+
return "owner-only file store (macOS keychain write did not complete)";
|
|
50003
|
+
default:
|
|
50004
|
+
return "owner-only file store (no usable macOS keychain)";
|
|
50005
|
+
}
|
|
49910
50006
|
}
|
|
49911
50007
|
file() {
|
|
49912
50008
|
return this.fileStore ??= new ImplementationFileTokenStore(this.filePath);
|
|
@@ -49927,9 +50023,15 @@ var ImplementationDarwinTokenStore = class extends ImplementationKeychainTokenSt
|
|
|
49927
50023
|
}
|
|
49928
50024
|
}
|
|
49929
50025
|
warnKeychainNotReadableBack() {
|
|
49930
|
-
const envKey = brandEnvKey3("CLI_ALLOW_FILE_TOKEN_STORE");
|
|
49931
50026
|
this.stderr.write(
|
|
49932
|
-
`NOTICE: the macOS Keychain accepted the implementation credential but it was not readable back (cross-process keychain read failed). Wrote it to the ${this.file().describe()} instead
|
|
50027
|
+
`NOTICE: the macOS Keychain accepted the implementation credential but it was not readable back (cross-process keychain read failed). Wrote it to the ${this.file().describe()} instead \u2014 later commands find it there automatically; no configuration is needed.
|
|
50028
|
+
`
|
|
50029
|
+
);
|
|
50030
|
+
}
|
|
50031
|
+
warnKeychainWriteFailed(error51) {
|
|
50032
|
+
const detail = error51 instanceof Error ? error51.message : "the macOS Keychain write failed";
|
|
50033
|
+
this.stderr.write(
|
|
50034
|
+
`NOTICE: ${detail}. Wrote it to the ${this.file().describe()} instead \u2014 later commands find it there automatically; no configuration is needed.
|
|
49933
50035
|
`
|
|
49934
50036
|
);
|
|
49935
50037
|
}
|
|
@@ -51483,7 +51585,7 @@ External connectors are being rolled out provider by provider, conservatively (r
|
|
|
51483
51585
|
order: 48,
|
|
51484
51586
|
title: "CLI and MCP installation guide",
|
|
51485
51587
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
51486
|
-
version: "2026-07-27.
|
|
51588
|
+
version: "2026-07-27.2",
|
|
51487
51589
|
public: true,
|
|
51488
51590
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
51489
51591
|
stages: ["company_setup", "staffing"],
|
|
@@ -51775,6 +51877,8 @@ By default the CLI stores the session in the **macOS Keychain** (on darwin). The
|
|
|
51775
51877
|
|
|
51776
51878
|
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.
|
|
51777
51879
|
|
|
51880
|
+
The keychain write itself is also bounded so it can never hang \`login --device\` after a successful approval: it runs through the keychain tool's own batch command interpreter with the session supplied inline (never on the command line, so it still never appears in \`ps\`) and a fixed timeout. If that write times out or otherwise fails, the CLI kills it and automatically falls over to the same owner-only file store \u2014 again, no flag and no manual retry needed.
|
|
51881
|
+
|
|
51778
51882
|
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.
|
|
51779
51883
|
|
|
51780
51884
|
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:
|