@tokenoftrust/cli 2.0.17 → 2.0.19

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokenoftrust/cli",
3
- "version": "2.0.17",
3
+ "version": "2.0.19",
4
4
  "description": "Token of Trust developer CLI — clone 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",
@@ -66,7 +66,7 @@ export function parseArgs(argv) {
66
66
  const a = {
67
67
  workspace: null, port: "4321", mcp: null,
68
68
  noLogin: false, noOpen: false, sample: false, help: false,
69
- rendererVersion: null,
69
+ rendererVersion: null, resetCache: false,
70
70
  };
71
71
  for (let i = 0; i < argv.length; i++) {
72
72
  const t = argv[i];
@@ -77,12 +77,34 @@ export function parseArgs(argv) {
77
77
  else if (t === "--no-open") a.noOpen = true;
78
78
  else if (t === "--sample") a.sample = true;
79
79
  else if (t === "--renderer-version") a.rendererVersion = argv[++i];
80
+ else if (t === "--reset-cache") a.resetCache = true;
80
81
  else if (t === "--help" || t === "-h") a.help = true;
81
82
  }
82
83
  return a;
83
84
  }
84
85
 
85
- const USAGE = `tot dev — run your store locally with save→reload
86
+ /**
87
+ * Delete the cached preview engines so the next run downloads and installs fresh.
88
+ *
89
+ * The recovery path this replaces was "hand-delete a directory under ~/.tot/cache" —
90
+ * which agent guidance forbids and no developer should have to know
91
+ * (fb-1789008798249-sc4tgy: the reporter had no way back to a working dev loop
92
+ * without it). A first-class flag makes the supported recovery discoverable in
93
+ * `tot dev --help`.
94
+ *
95
+ * Scoped deliberately to the renderer cache: it is a pure download+install artifact,
96
+ * rebuilt on demand, so removing it can only cost time. It never touches the
97
+ * tenant checkout, the login session, or anything else under ~/.tot.
98
+ * @param {{cacheRoot?: string, rm?: (p: string, o: object) => void}} [deps]
99
+ * @returns {{removed: boolean, path: string}}
100
+ */
101
+ export function resetRendererCache({ cacheRoot = RENDERER_CACHE_ROOT, rm = rmSync } = {}) {
102
+ const existed = existsSync(cacheRoot);
103
+ rm(cacheRoot, { recursive: true, force: true });
104
+ return { removed: existed, path: cacheRoot };
105
+ }
106
+
107
+ export const USAGE = `tot dev — run your store locally with save→reload
86
108
 
87
109
  tot dev <tenant> (in the monorepo) run tenants/<tenant>/
88
110
  tot dev (in a checkout) run this store natively (no Docker)
@@ -91,6 +113,7 @@ const USAGE = `tot dev — run your store locally with save→reload
91
113
  tot dev --workspace <dir> run a specific checkout directory
92
114
  tot dev --port <n> host port (default 4321)
93
115
  tot dev --no-open don't auto-open the browser when the server is up
116
+ tot dev --reset-cache delete the downloaded preview engines and re-fetch
94
117
 
95
118
  Edit content/*.html or the theme + save → the browser reloads. Private local
96
119
  preview — nothing is published. Prerequisites: Node.js and an invite (or just
@@ -104,6 +127,16 @@ export async function run(argv, ctx) {
104
127
  return 0;
105
128
  }
106
129
 
130
+ // Before anything else, so a wedged cache cannot break the very run meant to clear
131
+ // it. Then fall through and start normally: the flag is a modifier, not a separate
132
+ // command, because "clear it and go" is what a stuck developer actually wants.
133
+ if (args.resetCache) {
134
+ const { removed, path } = resetRendererCache();
135
+ console.error(removed
136
+ ? `~ cleared the preview-engine cache (${path}) — this run re-downloads it.`
137
+ : `~ no preview-engine cache to clear (${path}).`);
138
+ }
139
+
107
140
  // Resolve a free port up front so the URL we derive/open/print matches the port
108
141
  // the runner actually binds — Vite's strictPort is off, so a busy default would
109
142
  // silently drift and strand the browser/liveness poll on the wrong port. No-op
@@ -1039,6 +1072,36 @@ function sourceVersionKey(source) {
1039
1072
  * @param {{ log?: (m: string) => void, cacheRoot?: string }} [opts]
1040
1073
  * @returns {Promise<string>} the cached, installed runner tree's root directory.
1041
1074
  */
1075
+ /**
1076
+ * Rename the runner's shipped lockfile back to the name pnpm reads, so a pinned runner
1077
+ * version pins the graph it RUNS rather than just its own source.
1078
+ *
1079
+ * npm strips `pnpm-lock.yaml` from published tarballs unconditionally (the same
1080
+ * always-excluded list as package-lock.json / yarn.lock), so build-runner.mjs emits a
1081
+ * second copy as `pnpm-lock.runner.yaml`, which survives the publish. Verified both
1082
+ * directions: the published 2.0.33 tarball carries pnpm-workspace.yaml and no lock,
1083
+ * and a packed 2.0.34 tree delivers only the alias.
1084
+ *
1085
+ * Why it matters: without the restore, every developer resolves their own graph. Runner
1086
+ * 2.0.33 was built against vite 8.1.0 / rolldown 1.1.3 and installed as 8.2.2 / 1.2.8,
1087
+ * where an .astro parse regression failed the dependency scan — and Vite answers a
1088
+ * failed scan by skipping pre-bundling for the whole app rather than erroring, silently
1089
+ * disabling the SSR prebundle the renderer's own config depends on. Pages still
1090
+ * rendered, so nothing caught it.
1091
+ *
1092
+ * Never clobbers an existing `pnpm-lock.yaml`: the entitled .tar.gz path is a plain
1093
+ * tarball rather than an npm publish, so it already carries the real name.
1094
+ * @param {string} dir the extracted runner tree
1095
+ * @returns {boolean} whether a lock was restored
1096
+ */
1097
+ export function restoreShippedLock(dir) {
1098
+ const alias = join(dir, "pnpm-lock.runner.yaml");
1099
+ const real = join(dir, "pnpm-lock.yaml");
1100
+ if (!existsSync(alias) || existsSync(real)) return false;
1101
+ renameSync(alias, real);
1102
+ return true;
1103
+ }
1104
+
1042
1105
  export async function installRunnerTarball(
1043
1106
  { source, version, isUrl = true, strip = 0, integrity = null },
1044
1107
  { log = (m) => console.error(m), cacheRoot = RENDERER_CACHE_ROOT } = {},
@@ -1137,6 +1200,8 @@ export async function installRunnerTarball(
1137
1200
  /** @type {any} */ (err).permanent = true;
1138
1201
  throw err;
1139
1202
  }
1203
+ // Must run BEFORE the install, or it has no effect at all.
1204
+ restoreShippedLock(stagingDir);
1140
1205
  // Pin the runner install to PUBLIC npm. The moat-free runner has only public
1141
1206
  // deps, but the HOST's global ~/.npmrc may point `registry` at a private
1142
1207
  // mirror (an internal proxy that 502s, or one an invited developer can't