@tokenoftrust/cli 1.4.0-rc.20 → 1.4.0-rc.21

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.
@@ -63,7 +63,9 @@ const USAGE = `tot rollback [<versionId>] — instant re-point to a prior live v
63
63
 
64
64
  /** Parse `tot rollback` argv. Pure. Deliberately NO --yes/--force (see the header). */
65
65
  export function parseRollbackArgs(argv) {
66
+ /** @type {{ versionId: string|null, target: string|null, mcp: string|null, identity: string|null, help: boolean }} */
66
67
  const a = { versionId: null, target: null, mcp: null, identity: null, help: false };
68
+ /** @type {string[]} */
67
69
  const positional = [];
68
70
  for (let i = 0; i < argv.length; i++) {
69
71
  const t = argv[i];
@@ -179,8 +181,8 @@ export function rollbackCandidates(history, currentVersionId) {
179
181
  /** Render the `tot rollback` (no args) history listing — the rollback-target
180
182
  * picker (unit u2), each entry annotated with its immutable `/rev/<sha>` link
181
183
  * (unit u5) when a storefront origin is available. Pure — unit-tested.
182
- * @param {{ tenant:string, target:string, current:Record<string,unknown>|null,
183
- * candidates:Record<string,unknown>[], revisionBase?:string|null }} input
184
+ * @param {{ tenant:string, target:string, current:any,
185
+ * candidates:any[], revisionBase?:string|null }} input
184
186
  * @returns {string[]}
185
187
  */
186
188
  export function renderHistory({ tenant, target, current, candidates, revisionBase }) {
@@ -209,7 +211,7 @@ export function renderHistory({ tenant, target, current, candidates, revisionBas
209
211
  */
210
212
  export function renderRollbackPreview({ tenant, target, toVersionId, preview }) {
211
213
  const lines = ["", ` This rollback will change the LIVE site for ${tenant}:`];
212
- const current = preview.pointer?.current;
214
+ const current = /** @type {any} */ (preview.pointer?.current);
213
215
  if (current) lines.push(` ${target}: ${current.versionId} → ${toVersionId}`);
214
216
  else lines.push(` ${target}: → ${toVersionId}`);
215
217
  return lines;
@@ -258,7 +260,7 @@ export async function runRollback(client, { tenant, target, toVersionId, revisio
258
260
  );
259
261
  return 1;
260
262
  }
261
- const candidates = rollbackCandidates(status.history, status.current?.versionId ?? null);
263
+ const candidates = rollbackCandidates(status.history, /** @type {any} */ (status).current?.versionId ?? null);
262
264
  for (const line of renderHistory({ tenant, target, current: status.current, candidates, revisionBase })) {
263
265
  console.log(line);
264
266
  }
@@ -67,7 +67,7 @@ import {
67
67
  activityBridgeEnv, NativeArtifactUnavailableError,
68
68
  } from "./dev.mjs";
69
69
  import { scaffoldSample, isSampleCheckout, sampleConfig, SAMPLE_DIR_NAME } from "../sample.mjs";
70
- import { startHeartbeatFromEnv } from "../dev-heartbeat.mjs";
70
+ import { startHeartbeatFromEnv, resolveEditor } from "../dev-heartbeat.mjs";
71
71
  import { streamDevLogs } from "../dev-logs.mjs";
72
72
  import { milestoneBanner, cockpitUrlFrom, DEVELOPER_COCKPIT } from "../banner.mjs";
73
73
  import { IDEAS } from "./ideas.mjs";
@@ -262,11 +262,11 @@ export async function run(argv, ctx) {
262
262
  const dir = resolve(process.cwd(), tenant);
263
263
  // Resolve a free port so the URL we open/poll/print matches what the runner
264
264
  // binds (Vite strictPort is off → a busy port would drift). No-op if free.
265
- const devArgs = {
265
+ const devArgs = /** @type {any} */ ({
266
266
  image: null, port: String(await firstFreePort(Number(args.port || 4321))), mcp: args.mcp,
267
267
  noLogin: false, noOpen: args.noOpen, docker: args.docker,
268
- };
269
- const runtime = { useDocker: args.docker, runnerDir: null };
268
+ });
269
+ const runtime = /** @type {{ useDocker: boolean, runnerDir: any }} */ ({ useDocker: args.docker, runnerDir: null });
270
270
  await Promise.all([
271
271
  ensureCheckout(client, tenant, dir, env),
272
272
  prefetchRuntime(client, devArgs, env, runtime, ctx),
@@ -318,7 +318,7 @@ export async function run(argv, ctx) {
318
318
  // (A3) so the "instant" claim is measured. The "Connect Claude" step is
319
319
  // intentionally removed for now — a blocking prompt here meant Ctrl-C'ing it
320
320
  // tore down the dev server; revisit AI-connect as a non-blocking step later.
321
- printLiveEnding(tenant, url, formatElapsed(Date.now() - startedAt), cockpitUrl);
321
+ printLiveEnding(tenant, url, formatElapsed(Date.now() - startedAt), cockpitUrl, dir);
322
322
 
323
323
  // 7. hand the terminal to the running dev server until Ctrl-C.
324
324
  console.log("\n Watching your store — edit content/home.html + save. Ctrl-C to stop.\n");
@@ -581,6 +581,10 @@ function noStoresError({ session, baseUrl, listErr, list = null }) {
581
581
  * the remembered last tenant (A4) — then remember whatever was decided so the
582
582
  * next bare `tot start` doesn't have to ask again.
583
583
  */
584
+ /**
585
+ * @param {any} stores @param {any} args @param {any} env @param {string} baseUrl
586
+ * @param {{ session?: any, listErr?: string|null, list?: any }} [opts]
587
+ */
584
588
  async function resolveTenant(stores, args, env, baseUrl, { session = null, listErr = null, list = null } = {}) {
585
589
  const lastTenantPath = defaultLastTenantPath(env);
586
590
  const pick = pickTenant(stores, {
@@ -603,9 +607,10 @@ async function resolveTenant(stores, args, env, baseUrl, { session = null, listE
603
607
  } else {
604
608
  // Many stores, nothing remembered — choose. Non-interactive (no TTY /
605
609
  // --yes without a name) can't guess.
610
+ const many = /** @type {any} */ (pick);
606
611
  if (!isInteractive() || args.yes) {
607
612
  throw new CliError("you can build on several stores — pick one", {
608
- next: `tot start --tenant <tenant> (one of: ${pick.stores.map((s) => s.id).join(", ")})`,
613
+ next: `tot start --tenant <tenant> (one of: ${many.stores.map((s) => s.id).join(", ")})`,
609
614
  });
610
615
  }
611
616
  // Loud + unmissable: a >1-store dev is often looking at their browser
@@ -614,11 +619,11 @@ async function resolveTenant(stores, args, env, baseUrl, { session = null, listE
614
619
  console.log("");
615
620
  console.log(" ⚑ ACTION NEEDED IN YOUR TERMINAL — you can build on several stores.");
616
621
  console.log(" Pick one here to light up your cockpit:\n");
617
- pick.stores.forEach((s, i) => console.log(` ${i + 1}. ${s.id}${s.name ? ` — ${s.name}` : ""}`));
622
+ many.stores.forEach((s, i) => console.log(` ${i + 1}. ${s.id}${s.name ? ` — ${s.name}` : ""}`));
618
623
  console.log("");
619
- const idx = await promptChoice(pick.stores.length);
624
+ const idx = await promptChoice(many.stores.length);
620
625
  console.log("");
621
- tenant = pick.stores[idx].id;
626
+ tenant = many.stores[idx].id;
622
627
  }
623
628
 
624
629
  writeLastTenant(lastTenantPath, { mcpUrl: baseUrl, tenant });
@@ -685,6 +690,7 @@ export function formatElapsed(ms) {
685
690
  * Developer Cockpit URL, the one next step is framed as "edit a line → see it in
686
691
  * your cockpit"; otherwise it's the same edit-to-see-it-reload action.
687
692
  */
693
+ /** @param {string} dir @param {string|null} [cockpitUrl] */
688
694
  function printCheckoutLanding(dir, cockpitUrl = null) {
689
695
  console.log("");
690
696
  console.log(` 📁 Your store code is at: ${dir}`);
@@ -693,10 +699,38 @@ function printCheckoutLanding(dir, cockpitUrl = null) {
693
699
  console.log(` content/home.html — you'll see it reflected in ${where}.`);
694
700
  }
695
701
 
702
+ /**
703
+ * OS/editor-aware suggestions for opening the checkout, shown once at the
704
+ * "you're live" moment. $VISUAL/$EDITOR (same signal the cockpit's "open this
705
+ * file" hint uses — see dev-heartbeat.mjs#resolveEditor) wins when set;
706
+ * otherwise a short per-platform shortlist of common editor launch commands.
707
+ * Deliberately NOT probed against PATH — a spawnSync per candidate would add
708
+ * real latency to the crafted "aha" ending for the common case (nothing set),
709
+ * so this is a labeled suggestion list, not a detection result.
710
+ */
711
+ function editorOpenLines(dir, { platform = process.platform, env = process.env } = {}) {
712
+ const configured = resolveEditor(env);
713
+ if (configured) return [`${configured} ${dir}`];
714
+ if (platform === "darwin") {
715
+ return [`code ${dir} (VS Code, if installed)`, `cursor ${dir} (Cursor, if installed)`, `open ${dir} (Finder)`];
716
+ }
717
+ if (platform === "win32") {
718
+ return [`code ${dir} (VS Code, if installed)`, `explorer ${dir} (File Explorer)`];
719
+ }
720
+ return [`code ${dir} (VS Code, if installed)`, `cursor ${dir} (Cursor, if installed)`, `xdg-open ${dir} (file manager)`];
721
+ }
722
+
696
723
  /** The crafted "you're live" ending — a PROMINENT milestone banner (u2) that,
697
724
  * when we hold a Developer Cockpit URL, explicitly sends the developer BACK to
698
- * their cockpit as the next place to look. */
699
- function printLiveEnding(tenant, url, elapsed, cockpitUrl = null) {
725
+ * their cockpit as the next place to look. Then: an OS-suited "open this in
726
+ * an editor" suggestion, and the four-pane layout tip (this terminal, cockpit,
727
+ * local preview, editor) so a build problem, a reload, and the code are never
728
+ * more than a glance apart. */
729
+ /**
730
+ * @param {string} tenant @param {string} url @param {string|null} elapsed
731
+ * @param {string|null} [cockpitUrl] @param {string|null} [dir]
732
+ */
733
+ function printLiveEnding(tenant, url, elapsed, cockpitUrl = null, dir = null) {
700
734
  const lines = [
701
735
  `✨ You're live.${elapsed ? ` (${elapsed})` : ""}`,
702
736
  ` ${url}`,
@@ -710,6 +744,20 @@ function printLiveEnding(tenant, url, elapsed, cockpitUrl = null) {
710
744
  console.log(milestoneBanner(lines));
711
745
  console.log(" " + versionStamp("native"));
712
746
  console.log("");
747
+ if (dir) {
748
+ console.log(" Open your project in an editor (an AI coding agent like Claude works too —");
749
+ console.log(" same idea, different hands on the keyboard):");
750
+ for (const line of editorOpenLines(dir)) console.log(` ${line}`);
751
+ console.log("");
752
+ }
753
+ console.log(" Keep THIS terminal open — it's your live build/status feed, the fastest way to");
754
+ console.log(" see a problem the moment it happens. Need the command line for something else?");
755
+ console.log(" Open a NEW terminal window rather than closing this one.");
756
+ console.log("");
757
+ console.log(" Best view: arrange this terminal, your Developer Cockpit, the local preview,");
758
+ console.log(" and your editor so you can see all four at once — site, cockpit, build status,");
759
+ console.log(" and code, together.");
760
+ console.log("");
713
761
  }
714
762
 
715
763
  /**