@tokenoftrust/cli 1.4.0-rc.2 → 1.4.0-rc.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/package.json +1 -1
- package/src/commands/login.mjs +5 -0
- package/src/commands/start.mjs +24 -1
- package/src/oauth.mjs +14 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tokenoftrust/cli",
|
|
3
|
-
"version": "1.4.0-rc.
|
|
3
|
+
"version": "1.4.0-rc.4",
|
|
4
4
|
"description": "Token of Trust developer CLI — check out a tenant store, run it locally with save→reload, and submit it for preview. Installs the `tot` command.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Token of Trust",
|
package/src/commands/login.mjs
CHANGED
|
@@ -25,6 +25,7 @@ import { openBrowser } from "../open.mjs";
|
|
|
25
25
|
import { fail } from "../errors.mjs";
|
|
26
26
|
import { cockpitRecoveryUrl, normalizeEmailHint, redactEmailForHint, emailFromJwt } from "../auth.mjs";
|
|
27
27
|
import { isInteractive, promptYesNo } from "../prompt.mjs";
|
|
28
|
+
import { versionStamp } from "../mcp.mjs";
|
|
28
29
|
|
|
29
30
|
const DEFAULT_MCP_URL = "https://mcp.tokenoftrust.com";
|
|
30
31
|
|
|
@@ -255,6 +256,10 @@ export async function run(argv, _ctx) {
|
|
|
255
256
|
return 0;
|
|
256
257
|
}
|
|
257
258
|
|
|
259
|
+
// Show which CLI/runtime is actually running BEFORE anything else — the first thing
|
|
260
|
+
// a "why did sign-in behave oddly" investigation needs (e.g. a stale shadowing `tot`).
|
|
261
|
+
console.error(versionStamp());
|
|
262
|
+
|
|
258
263
|
const mcpUrl = args.mcp || env.MCP_BASE_URL || env.TOT_MCP_URL || DEFAULT_MCP_URL;
|
|
259
264
|
|
|
260
265
|
if (args.code) {
|
package/src/commands/start.mjs
CHANGED
|
@@ -57,6 +57,7 @@ import { CliError, fail, formatError, exitCodeFor } from "../errors.mjs";
|
|
|
57
57
|
import { openBrowser, waitForServer, firstFreePort } from "../open.mjs";
|
|
58
58
|
import { startProgress } from "../progress.mjs";
|
|
59
59
|
import { defaultLastTenantPath, readLastTenant, writeLastTenant } from "../last-tenant.mjs";
|
|
60
|
+
import { readCredentials, defaultCredentialsPath } from "../token-store.mjs";
|
|
60
61
|
import { collectChecks } from "./doctor.mjs";
|
|
61
62
|
import { normalizeStores, storeListError, checkoutTenant } from "./checkout.mjs";
|
|
62
63
|
import {
|
|
@@ -161,6 +162,24 @@ export function decideStartMode({ sampleFlag, hasSession }) {
|
|
|
161
162
|
return hasSession ? "authed" : "sample";
|
|
162
163
|
}
|
|
163
164
|
|
|
165
|
+
/**
|
|
166
|
+
* The MCP base URL the cached session was minted on — what `tot login` stored in the
|
|
167
|
+
* credentials file (honoring TOT_PROFILE via defaultCredentialsPath). Lets `tot start`
|
|
168
|
+
* (and callers) FOLLOW wherever the developer signed in rather than defaulting to prod,
|
|
169
|
+
* which would look up a session on the wrong MCP and report "not signed in". Returns
|
|
170
|
+
* null when there's no cached session or it can't be read (falls through to the default).
|
|
171
|
+
* @param {NodeJS.ProcessEnv} env
|
|
172
|
+
* @returns {string|null}
|
|
173
|
+
*/
|
|
174
|
+
export function cachedMcpUrl(env) {
|
|
175
|
+
try {
|
|
176
|
+
const creds = readCredentials(defaultCredentialsPath(env));
|
|
177
|
+
return creds && typeof creds.mcpUrl === "string" && creds.mcpUrl ? creds.mcpUrl : null;
|
|
178
|
+
} catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
164
183
|
/** @param {string[]} argv @param {any} ctx */
|
|
165
184
|
export async function run(argv, ctx) {
|
|
166
185
|
const env = process.env;
|
|
@@ -178,7 +197,11 @@ export async function run(argv, ctx) {
|
|
|
178
197
|
}
|
|
179
198
|
|
|
180
199
|
try {
|
|
181
|
-
|
|
200
|
+
// Resolve the MCP: an explicit flag / env wins, then the MCP the cached session was
|
|
201
|
+
// minted on (what `tot login` stored — so `start` FOLLOWS wherever you signed in
|
|
202
|
+
// instead of defaulting to prod and reporting "not signed in"), then the prod default.
|
|
203
|
+
const baseUrl =
|
|
204
|
+
args.mcp || env.MCP_BASE_URL || env.TOT_MCP_URL || cachedMcpUrl(env) || DEFAULT_MCP_URL;
|
|
182
205
|
const client = createMcpClient(baseUrl);
|
|
183
206
|
|
|
184
207
|
// Resolve a session, tolerating a no-session / no-network condition so we can
|
package/src/oauth.mjs
CHANGED
|
@@ -84,8 +84,15 @@ export async function registerClient(
|
|
|
84
84
|
headers: { "Content-Type": "application/json", Accept: "application/json" },
|
|
85
85
|
body: JSON.stringify({
|
|
86
86
|
client_name: CLIENT_NAME,
|
|
87
|
+
// Include the device-code grant: the browserless rendezvous + B3 device flows
|
|
88
|
+
// redeem their device_code at /oauth/token with this grant, so a client
|
|
89
|
+
// registered WITHOUT it gets "unauthorized_client: grant_type is invalid".
|
|
90
|
+
grant_types: [
|
|
91
|
+
"authorization_code",
|
|
92
|
+
"refresh_token",
|
|
93
|
+
"urn:ietf:params:oauth:grant-type:device_code",
|
|
94
|
+
],
|
|
87
95
|
redirect_uris: [redirectUri],
|
|
88
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
89
96
|
response_types: ["code"],
|
|
90
97
|
token_endpoint_auth_method: "none",
|
|
91
98
|
scope: SCOPE,
|
|
@@ -355,7 +362,6 @@ export async function attachRendezvous(attachEndpoint, { rendezvousCode, clientI
|
|
|
355
362
|
*/
|
|
356
363
|
export async function rendezvousLoginFlow({
|
|
357
364
|
mcpUrl,
|
|
358
|
-
clientId,
|
|
359
365
|
code,
|
|
360
366
|
fetchImpl = fetch,
|
|
361
367
|
log = () => {},
|
|
@@ -363,7 +369,12 @@ export async function rendezvousLoginFlow({
|
|
|
363
369
|
now = () => Date.now(),
|
|
364
370
|
}) {
|
|
365
371
|
const meta = await discoverMetadata(mcpUrl, fetchImpl);
|
|
366
|
-
|
|
372
|
+
// Always register a FRESH client for a rendezvous sign-in — do NOT reuse a cached
|
|
373
|
+
// clientId. A client cached by an older CLI build was registered without the
|
|
374
|
+
// device-code grant, so /oauth/token would reject the device grant with
|
|
375
|
+
// "unauthorized_client". Registration is cheap and a rendezvous has no consent
|
|
376
|
+
// screen to re-trigger (unlike the loopback/device flows, which reuse the cache).
|
|
377
|
+
const resolvedClientId = await registerClient(meta.registration_endpoint, LOOPBACK_REDIRECT, fetchImpl);
|
|
367
378
|
const { verifier, challenge } = generatePkce();
|
|
368
379
|
|
|
369
380
|
const attach = await attachRendezvous(
|