@rosthq/cli 0.5.11 → 0.5.12
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 +66 -5
- 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;AAwXD,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.4",
|
|
42309
42346
|
public: true,
|
|
42310
42347
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
42311
42348
|
stages: ["company_setup", "staffing"],
|
|
@@ -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
|
|
|
@@ -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
|