@tokenoftrust/cli 1.4.0-rc.1 → 1.4.0-rc.11
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/README.md +12 -9
- package/bin/tot.mjs +51 -11
- package/package.json +2 -2
- package/src/candidate-state.mjs +137 -0
- package/src/commands/{checkout.mjs → clone.mjs} +129 -28
- package/src/commands/dev.mjs +110 -15
- package/src/commands/doctor.mjs +4 -3
- package/src/commands/grants.mjs +8 -3
- package/src/commands/link.mjs +225 -0
- package/src/commands/login.mjs +19 -12
- package/src/commands/pr.mjs +214 -0
- package/src/commands/preview.mjs +71 -0
- package/src/commands/ship.mjs +667 -0
- package/src/commands/start.mjs +34 -14
- package/src/commands/submit.mjs +458 -41
- package/src/commands/validate.mjs +2 -2
- package/src/commands/whoami.mjs +6 -2
- package/src/context.mjs +2 -2
- package/src/oauth.mjs +92 -42
- package/src/obstacle-beacon.cjs +1 -1
package/src/commands/start.mjs
CHANGED
|
@@ -57,8 +57,9 @@ 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
|
-
import { normalizeStores, storeListError, checkoutTenant } from "./
|
|
62
|
+
import { normalizeStores, storeListError, checkoutTenant, noStoresGuidance } from "./clone.mjs";
|
|
62
63
|
import {
|
|
63
64
|
buildContainerPlan, spawnDevContainer, dockerAvailable, tryStartDocker,
|
|
64
65
|
resolveDevImage, isPrivateRegistryImage, ensureRegistryLogin,
|
|
@@ -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
|
|
@@ -223,6 +246,7 @@ export async function run(argv, ctx) {
|
|
|
223
246
|
tenant = await resolveTenant(stores, args, env, baseUrl, {
|
|
224
247
|
session,
|
|
225
248
|
listErr: storeListError(listResp),
|
|
249
|
+
list: listResp,
|
|
226
250
|
});
|
|
227
251
|
} catch (e) {
|
|
228
252
|
stopEarlyHeartbeat();
|
|
@@ -537,7 +561,7 @@ function mcpOrigin(baseUrl) {
|
|
|
537
561
|
* (surface the reason — likely an auth/entitlement problem) from a genuinely empty
|
|
538
562
|
* result (invite may still be propagating, or you need one). Points at `tot whoami`.
|
|
539
563
|
*/
|
|
540
|
-
function noStoresError({ session, baseUrl, listErr }) {
|
|
564
|
+
function noStoresError({ session, baseUrl, listErr, list = null }) {
|
|
541
565
|
const who = describeIdentity(session);
|
|
542
566
|
const origin = mcpOrigin(baseUrl);
|
|
543
567
|
if (listErr) {
|
|
@@ -546,14 +570,10 @@ function noStoresError({ session, baseUrl, listErr }) {
|
|
|
546
570
|
{ next: "run `tot whoami` to check your session, or `tot login` again — then re-run `tot start`" },
|
|
547
571
|
);
|
|
548
572
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
"if you were just invited, it may still be propagating — try again in a minute; " +
|
|
554
|
-
"otherwise ask your Token of Trust contact for a store invite (see `tot whoami`)",
|
|
555
|
-
},
|
|
556
|
-
);
|
|
573
|
+
// Status-aware (card c2): an UNLINKED identity is pointed at `tot link`, not the
|
|
574
|
+
// misleading "ask for a store invite" copy; the genuine zero-grants case keeps it.
|
|
575
|
+
const g = noStoresGuidance(list);
|
|
576
|
+
return new CliError(`signed in as ${who} via ${origin}, but ${g.headline}`, { next: g.next });
|
|
557
577
|
}
|
|
558
578
|
|
|
559
579
|
/**
|
|
@@ -561,7 +581,7 @@ function noStoresError({ session, baseUrl, listErr }) {
|
|
|
561
581
|
* the remembered last tenant (A4) — then remember whatever was decided so the
|
|
562
582
|
* next bare `tot start` doesn't have to ask again.
|
|
563
583
|
*/
|
|
564
|
-
async function resolveTenant(stores, args, env, baseUrl, { session = null, listErr = null } = {}) {
|
|
584
|
+
async function resolveTenant(stores, args, env, baseUrl, { session = null, listErr = null, list = null } = {}) {
|
|
565
585
|
const lastTenantPath = defaultLastTenantPath(env);
|
|
566
586
|
const pick = pickTenant(stores, {
|
|
567
587
|
explicit: args.tenant || null,
|
|
@@ -570,7 +590,7 @@ async function resolveTenant(stores, args, env, baseUrl, { session = null, listE
|
|
|
570
590
|
|
|
571
591
|
let tenant;
|
|
572
592
|
if (pick.kind === "none") {
|
|
573
|
-
throw noStoresError({ session, baseUrl, listErr });
|
|
593
|
+
throw noStoresError({ session, baseUrl, listErr, list });
|
|
574
594
|
} else if (pick.kind === "explicit") {
|
|
575
595
|
tenant = pick.tenant;
|
|
576
596
|
console.log(` → your store: ${tenant} (--tenant)`);
|
|
@@ -613,7 +633,7 @@ async function ensureCheckout(client, tenant, dir, env) {
|
|
|
613
633
|
console.log(` ✓ reusing existing checkout ./${tenant}`);
|
|
614
634
|
return;
|
|
615
635
|
}
|
|
616
|
-
throw new CliError(`./${tenant} already exists and isn't a
|
|
636
|
+
throw new CliError(`./${tenant} already exists and isn't a store checkout`, {
|
|
617
637
|
next: `remove it (or run \`tot start\` from an empty directory)`,
|
|
618
638
|
exitCode: 2,
|
|
619
639
|
});
|