@rosthq/cli 0.5.3 → 0.5.4
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 +204 -33
- package/dist/index.js.map +2 -2
- package/dist/mcp-install.d.ts +10 -0
- package/dist/mcp-install.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -42241,7 +42241,7 @@ Agents should explain the job, the required tool category, the minimum permissio
|
|
|
42241
42241
|
order: 48,
|
|
42242
42242
|
title: "CLI and MCP installation guide",
|
|
42243
42243
|
summary: "Install the public CLI, register remote token-backed MCP clients, and find the full command and tool catalog.",
|
|
42244
|
-
version: "2026-06-18.
|
|
42244
|
+
version: "2026-06-18.4",
|
|
42245
42245
|
public: true,
|
|
42246
42246
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
42247
42247
|
stages: ["company_setup", "staffing"],
|
|
@@ -42394,7 +42394,7 @@ export {{envPrefix}}_CLI_ALLOW_FILE_TOKEN_STORE=1
|
|
|
42394
42394
|
{{cli}} login --device
|
|
42395
42395
|
\`\`\`
|
|
42396
42396
|
|
|
42397
|
-
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**
|
|
42397
|
+
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.
|
|
42398
42398
|
|
|
42399
42399
|
## First-run path
|
|
42400
42400
|
|
|
@@ -42503,7 +42503,22 @@ After registering, confirm the client can actually reach the server with the new
|
|
|
42503
42503
|
|
|
42504
42504
|
### Token lifetime and rotation
|
|
42505
42505
|
|
|
42506
|
-
Tokens minted by \`{{cli}} mcp install\`
|
|
42506
|
+
Every MCP token now carries an expiry. Tokens minted by \`{{cli}} mcp install\` default to a **90-day** TTL (for both tenant-admin and seat-scoped tokens); after that the token stops authenticating and you re-mint. Override the lifetime at mint time:
|
|
42507
|
+
|
|
42508
|
+
- \`--expires-in <days>\` \u2014 set an explicit TTL, between **1 and 365** days. Example: \`{{cli}} mcp install --client codex --expires-in 30\`.
|
|
42509
|
+
- \`--no-expiry\` \u2014 mint a token with **no practical expiry** (a ~100-year TTL). This is a deliberate loosening for long-lived automation; the CLI prints a warning. Prefer a finite \`--expires-in\` and rotate instead.
|
|
42510
|
+
|
|
42511
|
+
\`mcp install\` echoes the chosen expiry, and \`{{cli}} command mcp_token.list\` reports \`expires_in_days\` for every token so you can see what is about to lapse.
|
|
42512
|
+
|
|
42513
|
+
**One-step rotation.** Replace a live token in a single command \u2014 it mints the replacement, prints the new registration block, then revokes the old token:
|
|
42514
|
+
|
|
42515
|
+
\`\`\`bash
|
|
42516
|
+
{{cli}} mcp install --client <client> --rotate <old-token-id>
|
|
42517
|
+
\`\`\`
|
|
42518
|
+
|
|
42519
|
+
Rotation **preserves the original token's scope and seat**: rotating a seat-scoped token mints a new seat-scoped token for the same seat, and rotating a tenant-admin token mints a tenant-admin token \u2014 you do **not** pass \`--scope\`/\`--seat-id\` (and if you do, they must match the old token, or the command errors without minting). The CLI looks the old token up first, so an unknown \`--rotate\` id fails cleanly with nothing minted. If the mint succeeds but revoking the old token fails (or the id isn't found at revoke time), the CLI says so on stderr and prints the manual \`mcp_token.revoke\` command \u2014 the new token is already live, so revoke the old one by hand. Find the \`<old-token-id>\` with \`{{cli}} command mcp_token.list\`.
|
|
42520
|
+
|
|
42521
|
+
You can also rotate the long way (mint then revoke separately):
|
|
42507
42522
|
|
|
42508
42523
|
1. Re-run \`{{cli}} mcp install --client <client>\` (add \`--scope seat --seat-id <seat-id>\` for a seat token) to mint and register a fresh token.
|
|
42509
42524
|
2. Revoke the old one (below). Rotate on a periodic cadence, and whenever the scope or seat changes.
|
|
@@ -42637,7 +42652,7 @@ These ergonomic wrappers (including the \`{{cli}} agent\` group) require **{{cli
|
|
|
42637
42652
|
|
|
42638
42653
|
| Command | Purpose | Scope | Safe example |
|
|
42639
42654
|
|---|---|---|---|
|
|
42640
|
-
| \`{{cli}} mcp install --client claude-code|codex|cursor\` | Help syntax for supported MCP clients. | Tenant | \`{{cli}} mcp install --client codex\` |
|
|
42655
|
+
| \`{{cli}} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]\` | Help syntax for supported MCP clients. \`--expires-in <days>\` (1..365) or \`--no-expiry\` sets the token TTL (default 90 days); \`--rotate <token-id>\` mints a replacement then revokes the old token. | Tenant | \`{{cli}} mcp install --client codex --expires-in 30\` |
|
|
42641
42656
|
| \`{{cli}} mcp install --client claude-code\` | Mint a tenant-admin MCP token and print a Claude Code registration command. | Tenant | \`{{cli}} mcp install --client claude-code\` |
|
|
42642
42657
|
| \`{{cli}} mcp install --client codex\` | Mint a tenant-admin MCP token and print Codex TOML. | Tenant | \`{{cli}} mcp install --client codex\` |
|
|
42643
42658
|
| \`{{cli}} mcp install --client cursor\` | Mint a tenant-admin MCP token and print Cursor JSON. | Tenant | \`{{cli}} mcp install --client cursor\` |
|
|
@@ -42822,7 +42837,7 @@ These rows are quick, at-a-glance triage. For deeper auth, tenant, scope, confir
|
|
|
42822
42837
|
- Node present but too old (npx launches but the CLI rejects it): run \`node --version\`; if it is below v22, upgrade Node.
|
|
42823
42838
|
- Stale npx cache: rerun with \`npx {{cliPackage}}@latest --help\` or clear the npm cache.
|
|
42824
42839
|
- MCP connection not working after registering: call \`rost_list_commands\` with \`{}\` (any token); with a tenant-admin token read \`rost://tenant/status\`, with a seat-scoped token call \`rost_get_context\` with \`{}\` (a seat token cannot read \`rost://tenant/status\`). A 401 / not-authorized shape means the token did not register \u2014 re-run \`mcp install\`.
|
|
42825
|
-
- Revoked or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one.
|
|
42840
|
+
- Revoked, **expired**, or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one (or rotate with \`--rotate <old-token-id>\`). Tokens minted by \`mcp install\` default to a 90-day expiry \u2014 check \`expires_in_days\` in \`{{cli}} command mcp_token.list\`; mint with \`--expires-in <days>\` or \`--no-expiry\` to change it.
|
|
42826
42841
|
- Confirmation required: a human approves from the \`approveVia\` web link or runs the \`{{cli}} command confirmation.approve --json ...\` command shown in the CLI error output (an agent never approves its own request \u2014 see the confirmations-guide).
|
|
42827
42842
|
- Command denied by scope or manifest: switch to a tenant-admin token for setup, or ask a human Steward to update the seat Charter and permission manifest.
|
|
42828
42843
|
- Need command guidance: run \`{{cli}} docs\`, \`{{cli}} reference search "onboarding"\`, or \`{{cli}} reference get agent-reference-map\`.
|
|
@@ -43527,7 +43542,7 @@ Templates may draft. Humans approve. A stock agent should not go live until a hu
|
|
|
43527
43542
|
order: 77,
|
|
43528
43543
|
title: "Troubleshooting guide",
|
|
43529
43544
|
summary: "How users and agents should diagnose common setup, tool, Signal, Friction, and MCP problems.",
|
|
43530
|
-
version: "2026-06-18.
|
|
43545
|
+
version: "2026-06-18.2",
|
|
43531
43546
|
public: true,
|
|
43532
43547
|
audiences: ["human", "cli", "mcp", "in_app_agent"],
|
|
43533
43548
|
stages: ["company_setup", "staffing", "operating_rhythm"],
|
|
@@ -43571,7 +43586,7 @@ Before calling a command that changes state, discover its exact shape so you do
|
|
|
43571
43586
|
- "Wrong tenant": \`{{cli}} tenants\` then \`{{cli}} use <tenant-slug-or-id>\`.
|
|
43572
43587
|
- "Command denied by scope or manifest": a seat token cannot run tenant-admin setup. Switch to a tenant-admin token, or ask a human Steward to update the seat Charter and permission manifest.
|
|
43573
43588
|
- "Confirmation required": the command is gated. The CLI prints \`{{cli}} command confirmation.approve --json ...\` or a web link. A human approves; an agent does not approve its own request.
|
|
43574
|
-
- Revoked or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one. Tokens minted by \`mcp install\`
|
|
43589
|
+
- Revoked, **expired**, or invalid MCP token: run \`{{cli}} mcp install --client <client>\` again to mint and register a fresh one (or rotate with \`--rotate <old-token-id>\`). Tokens minted by \`mcp install\` default to a 90-day expiry \u2014 check \`expires_in_days\` in \`{{cli}} command mcp_token.list\`.
|
|
43575
43590
|
|
|
43576
43591
|
## Agent-creation failures
|
|
43577
43592
|
|
|
@@ -44224,12 +44239,29 @@ function renderReferenceCorpusIndex() {
|
|
|
44224
44239
|
}
|
|
44225
44240
|
|
|
44226
44241
|
// src/mcp-install.ts
|
|
44242
|
+
var MCP_TOKEN_DEFAULT_EXPIRY_DAYS = 90;
|
|
44243
|
+
var MCP_TOKEN_MIN_EXPIRY_DAYS = 1;
|
|
44244
|
+
var MCP_TOKEN_MAX_EXPIRY_DAYS = 365;
|
|
44245
|
+
var MCP_TOKEN_NO_EXPIRY_SENTINEL_DAYS = 36525;
|
|
44246
|
+
var mcpTokenScopeSchema = external_exports.enum(["tenant_admin", "seat"]);
|
|
44227
44247
|
var mcpTokenOutputSchema = external_exports.object({
|
|
44228
44248
|
token_id: external_exports.string().min(1),
|
|
44229
44249
|
token: external_exports.string().min(1),
|
|
44230
|
-
scope:
|
|
44250
|
+
scope: mcpTokenScopeSchema,
|
|
44231
44251
|
seat_id: external_exports.string().min(1).nullable()
|
|
44232
44252
|
}).strict();
|
|
44253
|
+
var mcpTokenRevokeOutputSchema = external_exports.object({
|
|
44254
|
+
token_id: external_exports.string().min(1),
|
|
44255
|
+
revoked: external_exports.boolean()
|
|
44256
|
+
}).strict();
|
|
44257
|
+
var mcpTokenListEntrySchema = external_exports.object({
|
|
44258
|
+
token_id: external_exports.string().min(1),
|
|
44259
|
+
scope: mcpTokenScopeSchema,
|
|
44260
|
+
seat_id: external_exports.string().min(1).nullable()
|
|
44261
|
+
}).passthrough();
|
|
44262
|
+
var mcpTokenListOutputSchema = external_exports.object({
|
|
44263
|
+
tokens: external_exports.array(mcpTokenListEntrySchema)
|
|
44264
|
+
}).passthrough();
|
|
44233
44265
|
var pendingConfirmationSchema = external_exports.object({
|
|
44234
44266
|
confirmationId: external_exports.string().min(1),
|
|
44235
44267
|
commandId: external_exports.string().min(1)
|
|
@@ -44245,6 +44277,9 @@ function parseMcpInstallOptions(args) {
|
|
|
44245
44277
|
let scope = "tenant_admin";
|
|
44246
44278
|
let seatId;
|
|
44247
44279
|
let json2 = false;
|
|
44280
|
+
let expiresInDays;
|
|
44281
|
+
let noExpiry = false;
|
|
44282
|
+
let rotateTokenId;
|
|
44248
44283
|
for (let index = 0; index < args.length; index += 1) {
|
|
44249
44284
|
const arg = args[index];
|
|
44250
44285
|
if (arg === "--client") {
|
|
@@ -44257,6 +44292,14 @@ function parseMcpInstallOptions(args) {
|
|
|
44257
44292
|
seatId = requireValue(args, index, "--seat-id");
|
|
44258
44293
|
scope = "seat";
|
|
44259
44294
|
index += 1;
|
|
44295
|
+
} else if (arg === "--expires-in") {
|
|
44296
|
+
expiresInDays = parseExpiresIn(requireValue(args, index, "--expires-in"));
|
|
44297
|
+
index += 1;
|
|
44298
|
+
} else if (arg === "--no-expiry") {
|
|
44299
|
+
noExpiry = true;
|
|
44300
|
+
} else if (arg === "--rotate") {
|
|
44301
|
+
rotateTokenId = requireValue(args, index, "--rotate");
|
|
44302
|
+
index += 1;
|
|
44260
44303
|
} else if (arg === "--json") {
|
|
44261
44304
|
json2 = true;
|
|
44262
44305
|
} else {
|
|
@@ -44269,22 +44312,53 @@ function parseMcpInstallOptions(args) {
|
|
|
44269
44312
|
if (scope === "seat" && !seatId) {
|
|
44270
44313
|
throw new Error("Seat-scoped MCP install requires --seat-id.");
|
|
44271
44314
|
}
|
|
44315
|
+
if (noExpiry && expiresInDays !== void 0) {
|
|
44316
|
+
throw new Error("Use either --expires-in or --no-expiry, not both.");
|
|
44317
|
+
}
|
|
44318
|
+
const expiry = noExpiry ? { kind: "none" } : expiresInDays === void 0 ? { kind: "default" } : { kind: "days", days: expiresInDays };
|
|
44272
44319
|
return {
|
|
44273
44320
|
client,
|
|
44274
44321
|
scope,
|
|
44275
44322
|
...seatId === void 0 ? {} : { seatId },
|
|
44276
|
-
json: json2
|
|
44323
|
+
json: json2,
|
|
44324
|
+
expiry,
|
|
44325
|
+
...rotateTokenId === void 0 ? {} : { rotateTokenId }
|
|
44277
44326
|
};
|
|
44278
44327
|
}
|
|
44279
44328
|
var MCP_TOKEN_CAUTION = "Security: this minted MCP token is a live credential that lands in the client's plaintext config \u2014 prefer the narrowest scope (--scope seat --seat-id <id>) and revoke it immediately if it leaks.";
|
|
44329
|
+
var NO_EXPIRY_WARNING = "Warning: --no-expiry mints a token with no practical expiry (a ~100-year TTL). This is a long-lived credential by choice \u2014 rotate it deliberately and revoke immediately if it leaks.";
|
|
44280
44330
|
async function renderMcpInstall(input) {
|
|
44281
|
-
const
|
|
44282
|
-
|
|
44283
|
-
|
|
44284
|
-
|
|
44331
|
+
const stderrLines = [];
|
|
44332
|
+
const mintScope = input.options.rotateTokenId === void 0 ? { scope: input.options.scope, ...input.options.seatId === void 0 ? {} : { seatId: input.options.seatId } } : await resolveRotationScope(input.client, input.options);
|
|
44333
|
+
const token = await createMcpToken(input.client, mcpTokenCreateBody(mintScope, input.options.expiry));
|
|
44334
|
+
let rotationOldRevoked = null;
|
|
44335
|
+
if (input.options.rotateTokenId !== void 0) {
|
|
44336
|
+
const oldTokenId = input.options.rotateTokenId;
|
|
44337
|
+
try {
|
|
44338
|
+
const output = await executeWithConfirmation(input.client, "mcp_token.revoke", { token_id: oldTokenId });
|
|
44339
|
+
const revoke = mcpTokenRevokeOutputSchema.safeParse(output);
|
|
44340
|
+
rotationOldRevoked = revoke.success && revoke.data.revoked === true;
|
|
44341
|
+
if (!rotationOldRevoked) {
|
|
44342
|
+
const reason = revoke.success ? "the token id was not found for your tenant (it was not revoked \u2014 check the id)" : "the revoke response could not be parsed";
|
|
44343
|
+
stderrLines.push(
|
|
44344
|
+
`Warning: the new token (${token.token_id}) was minted, but the old token (${oldTokenId}) was NOT revoked \u2014 ${reason}. It is still live. Revoke it by hand: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${oldTokenId}"}'`
|
|
44345
|
+
);
|
|
44346
|
+
}
|
|
44347
|
+
} catch (error51) {
|
|
44348
|
+
rotationOldRevoked = false;
|
|
44349
|
+
stderrLines.push(
|
|
44350
|
+
`Warning: the new token (${token.token_id}) was minted, but revoking the old token (${oldTokenId}) failed: ${revokeFailureReason(error51)}. It is still live. Revoke it by hand: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${oldTokenId}"}'`
|
|
44351
|
+
);
|
|
44352
|
+
}
|
|
44353
|
+
}
|
|
44285
44354
|
const mcpUrl = `${input.appUrl.replace(/\/+$/, "")}/mcp`;
|
|
44286
44355
|
const registration = registrationFor(input.options.client, mcpUrl, token.token);
|
|
44356
|
+
const expiryLine = expiryEcho(input.options.expiry);
|
|
44357
|
+
if (input.options.expiry.kind === "none") {
|
|
44358
|
+
stderrLines.push(NO_EXPIRY_WARNING);
|
|
44359
|
+
}
|
|
44287
44360
|
if (input.options.json) {
|
|
44361
|
+
stderrLines.unshift(MCP_TOKEN_CAUTION);
|
|
44288
44362
|
return {
|
|
44289
44363
|
stdout: `${JSON.stringify({
|
|
44290
44364
|
client: input.options.client,
|
|
@@ -44292,32 +44366,119 @@ async function renderMcpInstall(input) {
|
|
|
44292
44366
|
scope: token.scope,
|
|
44293
44367
|
seat_id: token.seat_id,
|
|
44294
44368
|
mcp_url: mcpUrl,
|
|
44369
|
+
expiry: expiryDescriptor(input.options.expiry),
|
|
44370
|
+
...input.options.rotateTokenId === void 0 ? {} : { rotated_from: input.options.rotateTokenId, rotated_old_revoked: rotationOldRevoked },
|
|
44295
44371
|
registration
|
|
44296
44372
|
}, null, 2)}
|
|
44297
44373
|
`,
|
|
44298
|
-
stderr: `${
|
|
44299
|
-
`
|
|
44374
|
+
stderr: stderrLines.length > 0 ? `${stderrLines.join("\n")}
|
|
44375
|
+
` : ""
|
|
44300
44376
|
};
|
|
44301
44377
|
}
|
|
44378
|
+
const stdoutLines = [
|
|
44379
|
+
input.options.rotateTokenId === void 0 ? "MCP token minted. It is shown once in the registration block below." : "MCP token rotated. The replacement is shown once in the registration block below.",
|
|
44380
|
+
expiryLine
|
|
44381
|
+
];
|
|
44382
|
+
if (input.options.rotateTokenId !== void 0 && rotationOldRevoked === true) {
|
|
44383
|
+
stdoutLines.push(`Old token revoked: ${input.options.rotateTokenId}`);
|
|
44384
|
+
}
|
|
44385
|
+
stdoutLines.push(
|
|
44386
|
+
"",
|
|
44387
|
+
registration,
|
|
44388
|
+
"",
|
|
44389
|
+
MCP_TOKEN_CAUTION,
|
|
44390
|
+
`Revoke later with: ${cliBrand.binName} command mcp_token.revoke --json '{"token_id":"${token.token_id}"}'`,
|
|
44391
|
+
""
|
|
44392
|
+
);
|
|
44302
44393
|
return {
|
|
44303
|
-
stdout:
|
|
44304
|
-
|
|
44305
|
-
|
|
44306
|
-
|
|
44307
|
-
|
|
44308
|
-
|
|
44309
|
-
|
|
44310
|
-
|
|
44311
|
-
|
|
44312
|
-
stderr: ""
|
|
44394
|
+
stdout: stdoutLines.join("\n"),
|
|
44395
|
+
stderr: stderrLines.length > 0 ? `${stderrLines.join("\n")}
|
|
44396
|
+
` : ""
|
|
44397
|
+
};
|
|
44398
|
+
}
|
|
44399
|
+
function mcpTokenCreateBody(mint, expiry) {
|
|
44400
|
+
const base = {
|
|
44401
|
+
scope: mint.scope,
|
|
44402
|
+
...mint.seatId === void 0 ? {} : { seat_id: mint.seatId }
|
|
44313
44403
|
};
|
|
44404
|
+
if (expiry.kind === "days") {
|
|
44405
|
+
return { ...base, expires_in_days: expiry.days };
|
|
44406
|
+
}
|
|
44407
|
+
if (expiry.kind === "none") {
|
|
44408
|
+
const sentinel = new Date(Date.now() + MCP_TOKEN_NO_EXPIRY_SENTINEL_DAYS * 24 * 60 * 60 * 1e3);
|
|
44409
|
+
return { ...base, expires_at: sentinel.toISOString() };
|
|
44410
|
+
}
|
|
44411
|
+
return base;
|
|
44412
|
+
}
|
|
44413
|
+
async function resolveRotationScope(client, options) {
|
|
44414
|
+
const oldTokenId = options.rotateTokenId;
|
|
44415
|
+
const listOutput = await executeWithConfirmation(client, "mcp_token.list", { include_revoked: true });
|
|
44416
|
+
const parsed = mcpTokenListOutputSchema.safeParse(listOutput);
|
|
44417
|
+
if (!parsed.success) {
|
|
44418
|
+
throw new Error("Could not read the token list to rotate \u2014 mcp_token.list returned an unexpected shape.");
|
|
44419
|
+
}
|
|
44420
|
+
const old = parsed.data.tokens.find((entry) => entry.token_id === oldTokenId);
|
|
44421
|
+
if (!old) {
|
|
44422
|
+
throw new Error(
|
|
44423
|
+
`--rotate token id "${oldTokenId}" was not found in this tenant's tokens (nothing was minted). List ids with: ${cliBrand.binName} command mcp_token.list --json '{"include_revoked":true}'`
|
|
44424
|
+
);
|
|
44425
|
+
}
|
|
44426
|
+
const userSpecifiedScope = options.seatId !== void 0 || options.scope === "seat";
|
|
44427
|
+
if (userSpecifiedScope && options.scope !== old.scope) {
|
|
44428
|
+
throw new Error(
|
|
44429
|
+
`--rotate preserves the old token's scope (${old.scope}); the requested scope (${options.scope}) does not match. Omit --scope/--seat-id to rotate in place, or revoke and mint a new token with the different scope.`
|
|
44430
|
+
);
|
|
44431
|
+
}
|
|
44432
|
+
if (old.scope === "seat" && options.seatId !== void 0 && options.seatId !== old.seat_id) {
|
|
44433
|
+
throw new Error(
|
|
44434
|
+
`--rotate preserves the old token's seat (${old.seat_id ?? "unknown"}); the requested --seat-id (${options.seatId}) does not match.`
|
|
44435
|
+
);
|
|
44436
|
+
}
|
|
44437
|
+
if (old.scope === "seat") {
|
|
44438
|
+
if (old.seat_id === null) {
|
|
44439
|
+
throw new Error(`--rotate token "${oldTokenId}" is seat-scoped but has no seat id; cannot rotate it in place.`);
|
|
44440
|
+
}
|
|
44441
|
+
return { scope: "seat", seatId: old.seat_id };
|
|
44442
|
+
}
|
|
44443
|
+
return { scope: "tenant_admin" };
|
|
44444
|
+
}
|
|
44445
|
+
function expiryEcho(expiry) {
|
|
44446
|
+
if (expiry.kind === "days") {
|
|
44447
|
+
return `Expiry: ${expiry.days} day${expiry.days === 1 ? "" : "s"} from now.`;
|
|
44448
|
+
}
|
|
44449
|
+
if (expiry.kind === "none") {
|
|
44450
|
+
return "Expiry: none (a ~100-year TTL \u2014 see the warning on stderr).";
|
|
44451
|
+
}
|
|
44452
|
+
return `Expiry: ${MCP_TOKEN_DEFAULT_EXPIRY_DAYS} days from now (default).`;
|
|
44453
|
+
}
|
|
44454
|
+
function expiryDescriptor(expiry) {
|
|
44455
|
+
if (expiry.kind === "days") {
|
|
44456
|
+
return { mode: "days", days: expiry.days };
|
|
44457
|
+
}
|
|
44458
|
+
if (expiry.kind === "none") {
|
|
44459
|
+
return { mode: "none", days: null };
|
|
44460
|
+
}
|
|
44461
|
+
return { mode: "default", days: MCP_TOKEN_DEFAULT_EXPIRY_DAYS };
|
|
44462
|
+
}
|
|
44463
|
+
function revokeFailureReason(error51) {
|
|
44464
|
+
if (error51 instanceof CommandClientError) {
|
|
44465
|
+
return error51.message;
|
|
44466
|
+
}
|
|
44467
|
+
if (error51 instanceof Error) {
|
|
44468
|
+
return error51.message;
|
|
44469
|
+
}
|
|
44470
|
+
return "unknown error";
|
|
44314
44471
|
}
|
|
44315
44472
|
async function createMcpToken(client, body) {
|
|
44473
|
+
const output = await executeWithConfirmation(client, "mcp_token.create", body);
|
|
44474
|
+
return mcpTokenOutputSchema.parse(output);
|
|
44475
|
+
}
|
|
44476
|
+
async function executeWithConfirmation(client, commandId, body) {
|
|
44316
44477
|
try {
|
|
44317
|
-
const result = await client.execute(
|
|
44318
|
-
return
|
|
44478
|
+
const result = await client.execute(commandId, body);
|
|
44479
|
+
return result.output;
|
|
44319
44480
|
} catch (error51) {
|
|
44320
|
-
const pending =
|
|
44481
|
+
const pending = pendingCommandConfirmation(error51, commandId);
|
|
44321
44482
|
if (!pending) {
|
|
44322
44483
|
throw error51;
|
|
44323
44484
|
}
|
|
@@ -44325,10 +44486,10 @@ async function createMcpToken(client, body) {
|
|
|
44325
44486
|
confirmation_id: pending.confirmationId
|
|
44326
44487
|
});
|
|
44327
44488
|
const approvalOutput = confirmationApprovalOutputSchema.parse(approved.output);
|
|
44328
|
-
return
|
|
44489
|
+
return approvalOutput.output;
|
|
44329
44490
|
}
|
|
44330
44491
|
}
|
|
44331
|
-
function
|
|
44492
|
+
function pendingCommandConfirmation(error51, commandId) {
|
|
44332
44493
|
if (!(error51 instanceof CommandClientError) || error51.status !== 409 || !error51.response || error51.response.ok) {
|
|
44333
44494
|
return null;
|
|
44334
44495
|
}
|
|
@@ -44336,7 +44497,7 @@ function pendingMcpTokenConfirmation(error51) {
|
|
|
44336
44497
|
return null;
|
|
44337
44498
|
}
|
|
44338
44499
|
const pending = pendingConfirmationSchema.safeParse(error51.response.error.details);
|
|
44339
|
-
if (!pending.success || pending.data.commandId !==
|
|
44500
|
+
if (!pending.success || pending.data.commandId !== commandId) {
|
|
44340
44501
|
return null;
|
|
44341
44502
|
}
|
|
44342
44503
|
return pending.data;
|
|
@@ -44379,6 +44540,16 @@ function parseScope(value) {
|
|
|
44379
44540
|
}
|
|
44380
44541
|
throw new Error("MCP token scope must be tenant-admin or seat.");
|
|
44381
44542
|
}
|
|
44543
|
+
function parseExpiresIn(value) {
|
|
44544
|
+
if (!/^\d+$/.test(value)) {
|
|
44545
|
+
throw new Error(`--expires-in must be a whole number of days (${MCP_TOKEN_MIN_EXPIRY_DAYS}..${MCP_TOKEN_MAX_EXPIRY_DAYS}).`);
|
|
44546
|
+
}
|
|
44547
|
+
const days = Number.parseInt(value, 10);
|
|
44548
|
+
if (days < MCP_TOKEN_MIN_EXPIRY_DAYS || days > MCP_TOKEN_MAX_EXPIRY_DAYS) {
|
|
44549
|
+
throw new Error(`--expires-in must be between ${MCP_TOKEN_MIN_EXPIRY_DAYS} and ${MCP_TOKEN_MAX_EXPIRY_DAYS} days.`);
|
|
44550
|
+
}
|
|
44551
|
+
return days;
|
|
44552
|
+
}
|
|
44382
44553
|
function requireValue(args, index, flag) {
|
|
44383
44554
|
const value = args[index + 1];
|
|
44384
44555
|
if (!value) {
|
|
@@ -45964,7 +46135,7 @@ async function executeOnboard(io, client, args) {
|
|
|
45964
46135
|
}
|
|
45965
46136
|
async function executeMcp(io, client, appUrl2, args) {
|
|
45966
46137
|
if (args[0] !== "install") {
|
|
45967
|
-
io.stderr.write(`Usage: ${cliBrand.binName} mcp install --client claude-code|codex|cursor
|
|
46138
|
+
io.stderr.write(`Usage: ${cliBrand.binName} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]
|
|
45968
46139
|
`);
|
|
45969
46140
|
return 1;
|
|
45970
46141
|
}
|
|
@@ -46085,7 +46256,7 @@ function printUsage(io) {
|
|
|
46085
46256
|
...operationUsageLines(cliBrand.binName),
|
|
46086
46257
|
`${cliBrand.binName} onboard status`,
|
|
46087
46258
|
`${cliBrand.binName} onboard run`,
|
|
46088
|
-
`${cliBrand.binName} mcp install --client claude-code|codex|cursor`,
|
|
46259
|
+
`${cliBrand.binName} mcp install --client claude-code|codex|cursor [--scope seat --seat-id <id>] [--expires-in <days>|--no-expiry] [--rotate <token-id>]`,
|
|
46089
46260
|
`${cliBrand.binName} init [--client claude-code|codex|cursor]`,
|
|
46090
46261
|
`${cliBrand.binName} docs`,
|
|
46091
46262
|
`${cliBrand.binName} reference <list|search|get> [options]`,
|