@wix/himalaya-cli 0.1432.0 → 0.1433.0

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/cli.mjs CHANGED
@@ -842,37 +842,37 @@ var init_file_store = __esm({
842
842
  mkdirSync2(this.appDir(app), { recursive: true });
843
843
  writeFileSync2(join2(this.appDir(app), "release-manifest.json"), JSON.stringify(manifest, null, 2));
844
844
  }
845
- async putScreen(app, screenId, json7) {
845
+ async putScreen(app, screenId, json8) {
846
846
  const dir = join2(this.appDir(app), "screens");
847
847
  mkdirSync2(dir, { recursive: true });
848
- writeFileSync2(join2(dir, `${this.safe(screenId)}.json`), json7);
848
+ writeFileSync2(join2(dir, `${this.safe(screenId)}.json`), json8);
849
849
  }
850
- async putBundle(app, sha2566, bytes2) {
850
+ async putBundle(app, sha2567, bytes2) {
851
851
  const dir = join2(this.appDir(app), "bundles");
852
852
  mkdirSync2(dir, { recursive: true });
853
- writeFileSync2(join2(dir, `${this.safe(sha2566)}.bundle.js`), bytes2);
853
+ writeFileSync2(join2(dir, `${this.safe(sha2567)}.bundle.js`), bytes2);
854
854
  }
855
- async putTokens(app, json7) {
855
+ async putTokens(app, json8) {
856
856
  mkdirSync2(this.appDir(app), { recursive: true });
857
- writeFileSync2(join2(this.appDir(app), "tokens.json"), json7);
857
+ writeFileSync2(join2(this.appDir(app), "tokens.json"), json8);
858
858
  }
859
- async putDoc(app, sha2566, json7) {
859
+ async putDoc(app, sha2567, json8) {
860
860
  const dir = join2(this.appDir(app), "docs");
861
861
  mkdirSync2(dir, { recursive: true });
862
- writeFileSync2(join2(dir, `${this.safe(sha2566)}.json`), json7);
862
+ writeFileSync2(join2(dir, `${this.safe(sha2567)}.json`), json8);
863
863
  }
864
- async putAppConfig(app, json7) {
864
+ async putAppConfig(app, json8) {
865
865
  mkdirSync2(this.appDir(app), { recursive: true });
866
- writeFileSync2(join2(this.appDir(app), "app-config.json"), json7);
866
+ writeFileSync2(join2(this.appDir(app), "app-config.json"), json8);
867
867
  }
868
868
  async putIcon(app, bytes2) {
869
869
  mkdirSync2(this.appDir(app), { recursive: true });
870
870
  writeFileSync2(join2(this.appDir(app), "icon.png"), bytes2);
871
871
  }
872
- async putWidget(app, widgetId, json7) {
872
+ async putWidget(app, widgetId, json8) {
873
873
  const dir = join2(this.appDir(app), "widgets");
874
874
  mkdirSync2(dir, { recursive: true });
875
- writeFileSync2(join2(dir, `${this.safe(widgetId)}.json`), json7);
875
+ writeFileSync2(join2(dir, `${this.safe(widgetId)}.json`), json8);
876
876
  }
877
877
  // Per-app settings (registry visibility) — release-independent, so it lives beside the
878
878
  // manifest, not inside it. NOTE: listApps() keys on release-manifest.json/ledger presence,
@@ -883,16 +883,16 @@ var init_file_store = __esm({
883
883
  }
884
884
  // Template scaffold source archive — a content-addressed JSON doc, so it rides the same
885
885
  // docs/ slot as putDoc (retention via retainedReleaseRefs.docs, which includes sourceHash).
886
- async putSource(app, sha2566, json7) {
887
- return this.putDoc(app, sha2566, json7);
886
+ async putSource(app, sha2567, json8) {
887
+ return this.putDoc(app, sha2567, json8);
888
888
  }
889
889
  // App-owned resources. Stored EXTENSIONLESS under assets/<sha> — the content-type lives
890
890
  // on the manifest record, so the bytes on disk carry no format assumption (unlike
891
891
  // bundles/<sha>.bundle.js, which bakes ".bundle.js" into the filename).
892
- async putAsset(app, sha2566, bytes2) {
892
+ async putAsset(app, sha2567, bytes2) {
893
893
  const dir = join2(this.appDir(app), "assets");
894
894
  mkdirSync2(dir, { recursive: true });
895
- writeFileSync2(join2(dir, this.safe(sha2566)), bytes2);
895
+ writeFileSync2(join2(dir, this.safe(sha2567)), bytes2);
896
896
  }
897
897
  async getManifest(app) {
898
898
  try {
@@ -911,8 +911,8 @@ var init_file_store = __esm({
911
911
  async getScreen(app, screenId) {
912
912
  return this.read(app, "screens", `${this.safe(screenId)}.json`);
913
913
  }
914
- async getBundle(app, sha2566) {
915
- return this.read(app, "bundles", `${this.safe(sha2566)}.bundle.js`);
914
+ async getBundle(app, sha2567) {
915
+ return this.read(app, "bundles", `${this.safe(sha2567)}.bundle.js`);
916
916
  }
917
917
  async getTokens(app) {
918
918
  return this.read(app, "tokens.json");
@@ -923,8 +923,8 @@ var init_file_store = __esm({
923
923
  async getIcon(app) {
924
924
  return this.read(app, "icon.png");
925
925
  }
926
- async getDoc(app, sha2566) {
927
- return this.read(app, "docs", `${this.safe(sha2566)}.json`);
926
+ async getDoc(app, sha2567) {
927
+ return this.read(app, "docs", `${this.safe(sha2567)}.json`);
928
928
  }
929
929
  async getWidget(app, widgetId) {
930
930
  return this.read(app, "widgets", `${this.safe(widgetId)}.json`);
@@ -938,11 +938,11 @@ var init_file_store = __esm({
938
938
  return null;
939
939
  }
940
940
  }
941
- async getSource(app, sha2566) {
942
- return this.getDoc(app, sha2566);
941
+ async getSource(app, sha2567) {
942
+ return this.getDoc(app, sha2567);
943
943
  }
944
- async getAsset(app, sha2566) {
945
- return this.read(app, "assets", this.safe(sha2566));
944
+ async getAsset(app, sha2567) {
945
+ return this.read(app, "assets", this.safe(sha2567));
946
946
  }
947
947
  async listApps() {
948
948
  try {
@@ -1010,11 +1010,11 @@ var init_file_store = __esm({
1010
1010
  writeFileSync2(join2(this.appDir(app), "_wip.json"), JSON.stringify(next, null, 2));
1011
1011
  return true;
1012
1012
  }
1013
- bundleUrl(_app, sha2566) {
1014
- return `/v1/bundles/${sha2566}.bundle.js`;
1013
+ bundleUrl(_app, sha2567) {
1014
+ return `/v1/bundles/${sha2567}.bundle.js`;
1015
1015
  }
1016
- assetUrl(_app, sha2566) {
1017
- return `/v1/asset/${sha2566}`;
1016
+ assetUrl(_app, sha2567) {
1017
+ return `/v1/asset/${sha2567}`;
1018
1018
  }
1019
1019
  async prune(app, keepReleaseIds) {
1020
1020
  const manifest = await this.getManifest(app);
@@ -3135,10 +3135,10 @@ function writeAttestation(input, opts = {}) {
3135
3135
  };
3136
3136
  const attestation = { ...unsigned, selfHash: "" };
3137
3137
  attestation.selfHash = selfHashFor(attestation);
3138
- const json7 = JSON.stringify(attestation, null, 2) + "\n";
3138
+ const json8 = JSON.stringify(attestation, null, 2) + "\n";
3139
3139
  const runFile = `${runId.replace(/:/g, "-")}.json`;
3140
- writeFileSync5(join6(dir, runFile), json7);
3141
- writeFileSync5(join6(dir, "latest.json"), json7);
3140
+ writeFileSync5(join6(dir, runFile), json8);
3141
+ writeFileSync5(join6(dir, "latest.json"), json8);
3142
3142
  const keep = Math.max(1, opts.maxRuns ?? DEFAULT_MAX_RUNS);
3143
3143
  const allPriorRuns = readdirSync3(dir).filter((name) => name.endsWith(".json") && name !== "latest.json").filter((name) => name !== runFile).sort().reverse();
3144
3144
  const priorRuns = allPriorRuns.filter((name) => {
@@ -5893,10 +5893,10 @@ function decodeCatalogEnvelope(value) {
5893
5893
  const envelope = value;
5894
5894
  if (!envelope || envelope.encoding !== "gzip-base64" || typeof envelope.data !== "string" || typeof envelope.sha256 !== "string")
5895
5895
  throw new Error("Unsupported HHP catalog encoding; regenerate the catalog.");
5896
- const json7 = gunzipSync(Buffer.from(envelope.data, "base64"), { maxOutputLength: 2 * 1024 * 1024 }).toString("utf8");
5897
- if (createHash5("sha256").update(json7).digest("hex") !== envelope.sha256)
5896
+ const json8 = gunzipSync(Buffer.from(envelope.data, "base64"), { maxOutputLength: 2 * 1024 * 1024 }).toString("utf8");
5897
+ if (createHash5("sha256").update(json8).digest("hex") !== envelope.sha256)
5898
5898
  throw new Error("HHP catalog integrity check failed; regenerate the catalog.");
5899
- return JSON.parse(json7);
5899
+ return JSON.parse(json8);
5900
5900
  }
5901
5901
  var init_catalog_envelope = __esm({
5902
5902
  "../html-to-t5/src/v2/catalog-envelope.ts"() {
@@ -10359,16 +10359,16 @@ function utf8Length(s2, cap) {
10359
10359
  }
10360
10360
  return n2;
10361
10361
  }
10362
- function repairLoneSurrogateEscapes(json7) {
10363
- if (!json7.includes("\\u")) return json7;
10362
+ function repairLoneSurrogateEscapes(json8) {
10363
+ if (!json8.includes("\\u")) return json8;
10364
10364
  const hexAt = (i2) => {
10365
- const h2 = json7.slice(i2, i2 + 4);
10365
+ const h2 = json8.slice(i2, i2 + 4);
10366
10366
  return /^[0-9A-Fa-f]{4}$/.test(h2) ? parseInt(h2, 16) : -1;
10367
10367
  };
10368
10368
  let out2 = "";
10369
10369
  let inString = false;
10370
- for (let i2 = 0; i2 < json7.length; i2++) {
10371
- const c2 = json7[i2];
10370
+ for (let i2 = 0; i2 < json8.length; i2++) {
10371
+ const c2 = json8[i2];
10372
10372
  if (!inString) {
10373
10373
  if (c2 === '"') inString = true;
10374
10374
  out2 += c2;
@@ -10379,11 +10379,11 @@ function repairLoneSurrogateEscapes(json7) {
10379
10379
  out2 += c2;
10380
10380
  continue;
10381
10381
  }
10382
- if (c2 !== "\\" || i2 + 1 >= json7.length) {
10382
+ if (c2 !== "\\" || i2 + 1 >= json8.length) {
10383
10383
  out2 += c2;
10384
10384
  continue;
10385
10385
  }
10386
- const e2 = json7[i2 + 1];
10386
+ const e2 = json8[i2 + 1];
10387
10387
  const v2 = e2 === "u" ? hexAt(i2 + 2) : -1;
10388
10388
  if (v2 < 0) {
10389
10389
  out2 += c2 + e2;
@@ -10391,9 +10391,9 @@ function repairLoneSurrogateEscapes(json7) {
10391
10391
  continue;
10392
10392
  }
10393
10393
  if (v2 >= 55296 && v2 <= 56319) {
10394
- const low = json7[i2 + 6] === "\\" && json7[i2 + 7] === "u" ? hexAt(i2 + 8) : -1;
10394
+ const low = json8[i2 + 6] === "\\" && json8[i2 + 7] === "u" ? hexAt(i2 + 8) : -1;
10395
10395
  if (low >= 56320 && low <= 57343) {
10396
- out2 += json7.slice(i2, i2 + 12);
10396
+ out2 += json8.slice(i2, i2 + 12);
10397
10397
  i2 += 11;
10398
10398
  continue;
10399
10399
  }
@@ -10406,7 +10406,7 @@ function repairLoneSurrogateEscapes(json7) {
10406
10406
  i2 += 5;
10407
10407
  continue;
10408
10408
  }
10409
- out2 += json7.slice(i2, i2 + 6);
10409
+ out2 += json8.slice(i2, i2 + 6);
10410
10410
  i2 += 5;
10411
10411
  }
10412
10412
  return out2;
@@ -10425,12 +10425,12 @@ function measure(nodes, limits) {
10425
10425
  }
10426
10426
  return count2 > limits.maxNodes ? "too_many_nodes" : null;
10427
10427
  }
10428
- function layoutRichContent(json7, limits = RICH_CONTENT_LIMITS2) {
10429
- if (json7 === "") return { blocks: [] };
10430
- if (utf8Length(json7, limits.maxBytes) > limits.maxBytes) return { rejected: "too_large" };
10428
+ function layoutRichContent(json8, limits = RICH_CONTENT_LIMITS2) {
10429
+ if (json8 === "") return { blocks: [] };
10430
+ if (utf8Length(json8, limits.maxBytes) > limits.maxBytes) return { rejected: "too_large" };
10431
10431
  let parsed;
10432
10432
  try {
10433
- parsed = JSON.parse(repairLoneSurrogateEscapes(json7));
10433
+ parsed = JSON.parse(repairLoneSurrogateEscapes(json8));
10434
10434
  } catch {
10435
10435
  return { rejected: "invalid_json" };
10436
10436
  }
@@ -11051,14 +11051,14 @@ function resolveActionEvaluator(explicit) {
11051
11051
  }
11052
11052
  function responseBytes(value) {
11053
11053
  try {
11054
- const json7 = JSON.stringify(value);
11055
- if (typeof json7 !== "string") return 0;
11054
+ const json8 = JSON.stringify(value);
11055
+ if (typeof json8 !== "string") return 0;
11056
11056
  const envelope = JSON.stringify({
11057
11057
  protocol: "himi.t5.frame/1",
11058
11058
  type: "bridge",
11059
- payload: { op: "wix_result", id: "k000000", ok: true, payload: json7 }
11059
+ payload: { op: "wix_result", id: "k000000", ok: true, payload: json8 }
11060
11060
  });
11061
- return typeof envelope === "string" ? utf8Budget(envelope) : utf8Budget(json7);
11061
+ return typeof envelope === "string" ? utf8Budget(envelope) : utf8Budget(json8);
11062
11062
  } catch {
11063
11063
  return 0;
11064
11064
  }
@@ -16474,8 +16474,8 @@ __export(quickjs_evaluator_exports, {
16474
16474
  uninstallQuickJsBootEvaluator: () => uninstallQuickJsBootEvaluator
16475
16475
  });
16476
16476
  function inputJson(input) {
16477
- const json7 = JSON.stringify(input ?? {});
16478
- return json7 === void 0 ? "{}" : json7;
16477
+ const json8 = JSON.stringify(input ?? {});
16478
+ return json8 === void 0 ? "{}" : json8;
16479
16479
  }
16480
16480
  function timerGlobals(hide, live3) {
16481
16481
  const hidden = new Set(hide ?? []);
@@ -19721,22 +19721,22 @@ import { fileURLToPath as fileURLToPath11 } from "node:url";
19721
19721
  function hasVendoredFloor() {
19722
19722
  return existsSync24(VENDORED_DIR);
19723
19723
  }
19724
- function faceSource(file2, sha2566) {
19724
+ function faceSource(file2, sha2567) {
19725
19725
  const vendored = join24(VENDORED_DIR, basename8(file2));
19726
- if (existsSync24(vendored) && createHash9("sha256").update(readFileSync23(vendored)).digest("hex") === sha2566) {
19726
+ if (existsSync24(vendored) && createHash9("sha256").update(readFileSync23(vendored)).digest("hex") === sha2567) {
19727
19727
  return vendored;
19728
19728
  }
19729
- const cached3 = join24(CACHE_DIR, sha2566);
19729
+ const cached3 = join24(CACHE_DIR, sha2567);
19730
19730
  if (existsSync24(cached3)) return cached3;
19731
19731
  return null;
19732
19732
  }
19733
19733
  function upstreamFaceUrl(ref, dir, file2) {
19734
19734
  return `${RAW}/google/fonts/${ref}/${dir}/${encodeURIComponent(file2)}`;
19735
19735
  }
19736
- function faceUpstream(sha2566, catalog3 = loadCatalog()) {
19736
+ function faceUpstream(sha2567, catalog3 = loadCatalog()) {
19737
19737
  for (const [name, fam] of Object.entries(catalog3.families)) {
19738
19738
  if (!fam.dir || !UPSTREAM_DIR.test(fam.dir)) continue;
19739
- const face = fam.faces.find((f2) => f2.sha256 === sha2566);
19739
+ const face = fam.faces.find((f2) => f2.sha256 === sha2567);
19740
19740
  if (face) return { family: name, url: upstreamFaceUrl(catalog3.source.ref, fam.dir, face.file) };
19741
19741
  }
19742
19742
  return null;
@@ -19783,7 +19783,7 @@ async function ensureFaceBytes(face, opts = {}) {
19783
19783
  });
19784
19784
  return result;
19785
19785
  }
19786
- async function fetchVerifyAndCache(url, sha2566, fetcher) {
19786
+ async function fetchVerifyAndCache(url, sha2567, fetcher) {
19787
19787
  let bytes2;
19788
19788
  try {
19789
19789
  bytes2 = Buffer.from(await fetcher(url));
@@ -19791,10 +19791,10 @@ async function fetchVerifyAndCache(url, sha2566, fetcher) {
19791
19791
  return { ok: false, reason: `fetching ${url} failed: ${err.message}` };
19792
19792
  }
19793
19793
  const got = sha256Hex3(bytes2);
19794
- if (got !== sha2566) {
19794
+ if (got !== sha2567) {
19795
19795
  return {
19796
19796
  ok: false,
19797
- reason: `${url} returned bytes hashing to ${got.slice(0, 12)}\u2026, but the catalog pins ${sha2566.slice(0, 12)}\u2026 \u2014 refused`
19797
+ reason: `${url} returned bytes hashing to ${got.slice(0, 12)}\u2026, but the catalog pins ${sha2567.slice(0, 12)}\u2026 \u2014 refused`
19798
19798
  };
19799
19799
  }
19800
19800
  try {
@@ -19836,10 +19836,10 @@ async function offRepoFontBytes(plan, opts = {}) {
19836
19836
  }
19837
19837
  return out2;
19838
19838
  }
19839
- async function catalogFaceBytes(sha2566, opts = {}) {
19839
+ async function catalogFaceBytes(sha2567, opts = {}) {
19840
19840
  const catalog3 = opts.catalog ?? loadCatalog();
19841
19841
  for (const family of [...Object.values(catalog3.families), ...Object.values(catalog3.bundled)]) {
19842
- const face = family.faces.find((f2) => f2.sha256 === sha2566);
19842
+ const face = family.faces.find((f2) => f2.sha256 === sha2567);
19843
19843
  if (!face) continue;
19844
19844
  if (hasVendoredFloor()) {
19845
19845
  const local = faceSource(face.file, face.sha256);
@@ -25408,14 +25408,14 @@ function isSymlink(file2) {
25408
25408
  function archiveBytes(dir, bytes2) {
25409
25409
  const ext = imageExtension(bytes2);
25410
25410
  if (!ext) return null;
25411
- const sha2566 = createHash12("sha256").update(bytes2).digest("hex");
25412
- const rel = join36(HISTORY_DIR, `${sha2566}.${ext}`);
25411
+ const sha2567 = createHash12("sha256").update(bytes2).digest("hex");
25412
+ const rel = join36(HISTORY_DIR, `${sha2567}.${ext}`);
25413
25413
  const dest = join36(dir, rel);
25414
25414
  if (!realDirIn(dir, HISTORY_DIR)) throw new UnsafeHimiDirError(HISTORY_DIR);
25415
25415
  if (!existsSync35(dest)) {
25416
25416
  writeFileAtomic(dest, bytes2);
25417
25417
  }
25418
- return { sha256: sha2566, ext, file: toPosix(rel) };
25418
+ return { sha256: sha2567, ext, file: toPosix(rel) };
25419
25419
  }
25420
25420
  function appendProgress(dir, input) {
25421
25421
  const message = input.message.trim();
@@ -26241,15 +26241,15 @@ function himiPluginManifest() {
26241
26241
  };
26242
26242
  }
26243
26243
  function himiPluginFiles(skillMd) {
26244
- const json7 = (o2) => `${JSON.stringify(o2, null, 2)}
26244
+ const json8 = (o2) => `${JSON.stringify(o2, null, 2)}
26245
26245
  `;
26246
26246
  return [
26247
- { path: ".claude-plugin/plugin.json", text: json7(himiPluginManifest()) },
26248
- { path: ".mcp.json", text: json7({ mcpServers: { [HIMI_PLUGIN_NAME]: { command: HIMI_MCP_SERVER.command, args: [...HIMI_MCP_SERVER.args] } } }) },
26247
+ { path: ".claude-plugin/plugin.json", text: json8(himiPluginManifest()) },
26248
+ { path: ".mcp.json", text: json8({ mcpServers: { [HIMI_PLUGIN_NAME]: { command: HIMI_MCP_SERVER.command, args: [...HIMI_MCP_SERVER.args] } } }) },
26249
26249
  { path: "skills/himalaya/SKILL.md", text: skillMd }
26250
26250
  ];
26251
26251
  }
26252
- function himiMarketplace(archiveUrl, sha2566) {
26252
+ function himiMarketplace(archiveUrl, sha2567) {
26253
26253
  return {
26254
26254
  name: HIMI_MARKETPLACE_NAME,
26255
26255
  description: "Wix native app building for Claude Code",
@@ -26258,7 +26258,7 @@ function himiMarketplace(archiveUrl, sha2566) {
26258
26258
  {
26259
26259
  name: HIMI_PLUGIN_NAME,
26260
26260
  description: HIMI_PLUGIN_DESCRIPTION,
26261
- source: { source: "archive", url: archiveUrl, ...sha2566 ? { sha256: sha2566 } : {} }
26261
+ source: { source: "archive", url: archiveUrl, ...sha2567 ? { sha256: sha2567 } : {} }
26262
26262
  }
26263
26263
  ]
26264
26264
  };
@@ -26273,19 +26273,19 @@ function connectorPluginManifest() {
26273
26273
  };
26274
26274
  }
26275
26275
  function connectorPluginFiles(mcpUrl, skillMd) {
26276
- const json7 = (o2) => `${JSON.stringify(o2, null, 2)}
26276
+ const json8 = (o2) => `${JSON.stringify(o2, null, 2)}
26277
26277
  `;
26278
26278
  return [
26279
- { path: ".claude-plugin/plugin.json", text: json7(connectorPluginManifest()) },
26280
- { path: ".mcp.json", text: json7({ mcpServers: { [CONNECTOR_PLUGIN_NAME]: { type: "http", url: mcpUrl } } }) },
26279
+ { path: ".claude-plugin/plugin.json", text: json8(connectorPluginManifest()) },
26280
+ { path: ".mcp.json", text: json8({ mcpServers: { [CONNECTOR_PLUGIN_NAME]: { type: "http", url: mcpUrl } } }) },
26281
26281
  { path: `skills/${CONNECTOR_PLUGIN_NAME}/SKILL.md`, text: skillMd }
26282
26282
  ];
26283
26283
  }
26284
- function connectorMarketplaceEntry(archiveUrl, sha2566) {
26284
+ function connectorMarketplaceEntry(archiveUrl, sha2567) {
26285
26285
  return {
26286
26286
  name: CONNECTOR_PLUGIN_NAME,
26287
26287
  description: CONNECTOR_PLUGIN_DESCRIPTION,
26288
- source: { source: "archive", url: archiveUrl, ...sha2566 ? { sha256: sha2566 } : {} }
26288
+ source: { source: "archive", url: archiveUrl, ...sha2567 ? { sha256: sha2567 } : {} }
26289
26289
  };
26290
26290
  }
26291
26291
  function storeZip(files) {
@@ -27173,9 +27173,9 @@ function inferScreenDisplayName(screenId) {
27173
27173
  const words = (opaque ? id.slice(0, opaque.index) : id).replace(/([a-z0-9])([A-Z])/g, "$1 $2").split(/[_\/\s-]+/).filter(Boolean).map((word) => /^(cms|faq|api|ai|id|url)$/i.test(word) ? word.toUpperCase() : word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
27174
27174
  return `${words || screenId}${opaque ? ` (${opaque[1]})` : ""}`;
27175
27175
  }
27176
- function screenDisplayName(screenId, displayName2, title, navigationLabel) {
27176
+ function screenDisplayName(screenId, displayName3, title, navigationLabel) {
27177
27177
  const literal = (value) => typeof value === "string" && !!value.trim() && !value.trim().startsWith("$") && !/^__[A-Z0-9_]+__$/.test(value.trim());
27178
- if (literal(displayName2)) return displayName2.trim();
27178
+ if (literal(displayName3)) return displayName3.trim();
27179
27179
  if (literal(title) && title.trim() !== screenId) return title.trim();
27180
27180
  if (literal(navigationLabel)) return navigationLabel.trim();
27181
27181
  return inferScreenDisplayName(screenId);
@@ -29010,9 +29010,9 @@ function addScreen(dir, id, entry, opts = {}) {
29010
29010
  if (content) {
29011
29011
  if (content.screens !== void 0) {
29012
29012
  const route = findRoute(reparsed, id);
29013
- const displayName2 = screenDisplayName(id, opts.displayName, route.block.title);
29013
+ const displayName3 = screenDisplayName(id, opts.displayName, route.block.title);
29014
29014
  try {
29015
- const metadata2 = parseScreenMetadata({ ...parseScreenMetadata(content.screens), [id]: { displayName: displayName2 } });
29015
+ const metadata2 = parseScreenMetadata({ ...parseScreenMetadata(content.screens), [id]: { displayName: displayName3 } });
29016
29016
  validateScreenMetadata(metadata2);
29017
29017
  content.screens = metadata2;
29018
29018
  } catch (error) {
@@ -29386,8 +29386,8 @@ function lastDistFromJob(job, now = /* @__PURE__ */ new Date()) {
29386
29386
  const capabilities = [...new Set(job.buildCapabilities.filter((x2) => typeof x2 === "string").map(catalogKey))].sort();
29387
29387
  return { app: job.app, jobId: job.jobId, shell: job.shell, capabilities, finishedAt: now.toISOString() };
29388
29388
  }
29389
- function needsBinaryNotice(json7) {
29390
- const nb = json7?.needsBinary;
29389
+ function needsBinaryNotice(json8) {
29390
+ const nb = json8?.needsBinary;
29391
29391
  if (!nb || !Array.isArray(nb.missing)) return null;
29392
29392
  const missing = nb.missing.filter((x2) => typeof x2 === "string");
29393
29393
  if (!missing.length) return null;
@@ -29729,12 +29729,12 @@ function updateSiteViewMetadata(existing, before, after) {
29729
29729
  const bounded2 = suffix2.length > 100 ? `${suffix2.slice(0, 99)})` : suffix2;
29730
29730
  return title.slice(0, 120 - bounded2.length).trimEnd() + bounded2;
29731
29731
  };
29732
- let displayName2 = fit2();
29733
- if (claimed.has(screenDisplayNameKey(displayName2))) displayName2 = fit2(` (${page.id})`);
29732
+ let displayName3 = fit2();
29733
+ if (claimed.has(screenDisplayNameKey(displayName3))) displayName3 = fit2(` (${page.id})`);
29734
29734
  let suffix = 2;
29735
- while (claimed.has(screenDisplayNameKey(displayName2))) displayName2 = fit2(` (${suffix++}, ${page.id})`);
29736
- updated[id] = { displayName: displayName2 };
29737
- claimed.add(screenDisplayNameKey(displayName2));
29735
+ while (claimed.has(screenDisplayNameKey(displayName3))) displayName3 = fit2(` (${suffix++}, ${page.id})`);
29736
+ updated[id] = { displayName: displayName3 };
29737
+ claimed.add(screenDisplayNameKey(displayName3));
29738
29738
  }
29739
29739
  validateScreenMetadata(updated);
29740
29740
  return updated;
@@ -39399,10 +39399,10 @@ function createRicosHostSession(o2) {
39399
39399
  });
39400
39400
  };
39401
39401
  const writes = () => committed || state.dirty;
39402
- const moved = (json7) => {
39403
- if (json7 === lastContent) return false;
39404
- lastContent = json7;
39405
- if (json7 !== baseline) {
39402
+ const moved = (json8) => {
39403
+ if (json8 === lastContent) return false;
39404
+ lastContent = json8;
39405
+ if (json8 !== baseline) {
39406
39406
  state.dirty = true;
39407
39407
  if (dirtyTimer !== null) t2.clearTimeout(dirtyTimer);
39408
39408
  dirtyTimer = t2.setTimeout(() => {
@@ -39626,13 +39626,13 @@ async function runRicosImageUpload(o2) {
39626
39626
  return fail6("put");
39627
39627
  }
39628
39628
  if (!res.ok) return fail6("put");
39629
- let json7;
39629
+ let json8;
39630
39630
  try {
39631
- json7 = await res.json();
39631
+ json8 = await res.json();
39632
39632
  } catch {
39633
39633
  return fail6("bad_descriptor");
39634
39634
  }
39635
- const root = json7 && typeof json7 === "object" ? json7 : {};
39635
+ const root = json8 && typeof json8 === "object" ? json8 : {};
39636
39636
  const d2 = root.file && typeof root.file === "object" ? root.file : root;
39637
39637
  const id = typeof d2.id === "string" ? d2.id : "";
39638
39638
  if (!id) return fail6("bad_descriptor");
@@ -39833,9 +39833,9 @@ function RichContentEditorView({ props, ctx, wrap, nodeId, renderChild, ...dom }
39833
39833
  locale: ctx.localeTag ?? "en",
39834
39834
  rtl: ctx.isRTL ?? document.documentElement.dir === "rtl",
39835
39835
  palette: { text: C2("textPrimary", "#13171E"), background: C2("surfacePrimary", "#FFFFFF"), accent: C2("accentPrimary", "#116DFF") },
39836
- onContent: (json7) => {
39836
+ onContent: (json8) => {
39837
39837
  const b2 = bagRef.current, n2 = nameRef.current;
39838
- if (b2 && n2) b2.set(n2, json7);
39838
+ if (b2 && n2) b2.set(n2, json8);
39839
39839
  },
39840
39840
  onToolbar: setToolbar,
39841
39841
  onFocusChange: setFocused,
@@ -40202,13 +40202,13 @@ async function runMediaFieldUpload(o2) {
40202
40202
  return { ok: false, reason: e2 instanceof Deadline || expired ? "timeout" : "put" };
40203
40203
  }
40204
40204
  if (!res.ok) return { ok: false, reason: "put" };
40205
- let json7;
40205
+ let json8;
40206
40206
  try {
40207
- json7 = await bounded2(res.json());
40207
+ json8 = await bounded2(res.json());
40208
40208
  } catch (e2) {
40209
40209
  return { ok: false, reason: e2 instanceof Deadline || expired ? "timeout" : "bad_descriptor" };
40210
40210
  }
40211
- const root = json7 && typeof json7 === "object" ? json7 : {};
40211
+ const root = json8 && typeof json8 === "object" ? json8 : {};
40212
40212
  const d2 = root.file && typeof root.file === "object" ? root.file : root;
40213
40213
  const mediaId = typeof d2.id === "string" ? d2.id : "";
40214
40214
  if (!mediaId) return { ok: false, reason: "bad_descriptor" };
@@ -45273,7 +45273,7 @@ var init_pdf = __esm({
45273
45273
  var defineProperty = Object.defineProperty;
45274
45274
  var stringSlice = uncurryThis("".slice);
45275
45275
  var replace = uncurryThis("".replace);
45276
- var join91 = uncurryThis([].join);
45276
+ var join92 = uncurryThis([].join);
45277
45277
  var CONFIGURABLE_LENGTH = DESCRIPTORS && !fails(function() {
45278
45278
  return defineProperty(function() {
45279
45279
  }, "length", { value: 8 }).length !== 8;
@@ -45300,7 +45300,7 @@ var init_pdf = __esm({
45300
45300
  }
45301
45301
  var state = enforceInternalState(value);
45302
45302
  if (!hasOwn(state, "source")) {
45303
- state.source = join91(TEMPLATE, typeof name == "string" ? name : "");
45303
+ state.source = join92(TEMPLATE, typeof name == "string" ? name : "");
45304
45304
  }
45305
45305
  return value;
45306
45306
  };
@@ -47327,7 +47327,7 @@ var init_pdf = __esm({
47327
47327
  var currentOrdered;
47328
47328
  var marked = false;
47329
47329
  var root = true;
47330
- var json7 = stringifyWithProperSymbolsConversion(text9, function(key3, value) {
47330
+ var json8 = stringifyWithProperSymbolsConversion(text9, function(key3, value) {
47331
47331
  key3 = $String(key3);
47332
47332
  if (propertyList) {
47333
47333
  if (key3 === END_MARK) {
@@ -47354,16 +47354,16 @@ var init_pdf = __esm({
47354
47354
  }
47355
47355
  return value;
47356
47356
  }, space);
47357
- if (typeof json7 != "string") return json7;
47358
- if (ILL_FORMED_UNICODE) json7 = replace(json7, surrogates, fixIllFormedJSON);
47359
- if (!marked) return json7;
47357
+ if (typeof json8 != "string") return json8;
47358
+ if (ILL_FORMED_UNICODE) json8 = replace(json8, surrogates, fixIllFormedJSON);
47359
+ if (!marked) return json8;
47360
47360
  var result = "";
47361
- var length = json7.length;
47361
+ var length = json8.length;
47362
47362
  for (var i2 = 0; i2 < length; i2++) {
47363
- var chr = charAt(json7, i2);
47363
+ var chr = charAt(json8, i2);
47364
47364
  if (chr === '"') {
47365
- var end = parseJSONString(json7, ++i2).end - 1;
47366
- var string5 = slice(json7, i2, end);
47365
+ var end = parseJSONString(json8, ++i2).end - 1;
47366
+ var string5 = slice(json8, i2, end);
47367
47367
  if (slice(string5, 0, RAW_MARK_LENGTH) === RAW_MARK) result += rawStrings[slice(string5, RAW_MARK_LENGTH)];
47368
47368
  else if (slice(string5, 0, KEY_MARK_LENGTH) === KEY_MARK) result += '"' + slice(string5, KEY_MARK_LENGTH) + '"';
47369
47369
  else result += '"' + string5 + '"';
@@ -47823,7 +47823,7 @@ var init_pdf = __esm({
47823
47823
  var charAt = uncurryThis("".charAt);
47824
47824
  var Uint8Array2 = globalThis2.Uint8Array;
47825
47825
  var $Array = globalThis2.Array;
47826
- var join91 = uncurryThis([].join);
47826
+ var join92 = uncurryThis([].join);
47827
47827
  var INCORRECT_BEHAVIOR_OR_DOESNT_EXISTS = !Uint8Array2 || !Uint8Array2.prototype.toBase64 || !(function() {
47828
47828
  try {
47829
47829
  var target = new Uint8Array2();
@@ -47869,7 +47869,7 @@ var init_pdf = __esm({
47869
47869
  result[written++] = "=";
47870
47870
  }
47871
47871
  }
47872
- return join91(result, "");
47872
+ return join92(result, "");
47873
47873
  }
47874
47874
  });
47875
47875
  },
@@ -47881,7 +47881,7 @@ var init_pdf = __esm({
47881
47881
  var anUint8Array = __webpack_require__3(4154);
47882
47882
  var notDetached = __webpack_require__3(5169);
47883
47883
  var numberToString2 = uncurryThis(1.1.toString);
47884
- var join91 = uncurryThis([].join);
47884
+ var join92 = uncurryThis([].join);
47885
47885
  var $Array = Array;
47886
47886
  var Uint8Array2 = globalThis2.Uint8Array;
47887
47887
  var INCORRECT_BEHAVIOR_OR_DOESNT_EXISTS = !Uint8Array2 || !Uint8Array2.prototype.toHex || !(function() {
@@ -47901,7 +47901,7 @@ var init_pdf = __esm({
47901
47901
  var hex2 = numberToString2(this[i2], 16);
47902
47902
  result[i2] = hex2.length === 1 ? "0" + hex2 : hex2;
47903
47903
  }
47904
- return join91(result, "");
47904
+ return join92(result, "");
47905
47905
  }
47906
47906
  });
47907
47907
  },
@@ -124985,9 +124985,9 @@ function renderInner(node, ctx) {
124985
124985
  }
124986
124986
  const transitionKey = typeof modifiers?.transitionId === "string" ? resolvedComponentId(modifiers.transitionId, ctx) : void 0;
124987
124987
  if (transitionKey && !ctx.reduceMotion()) {
124988
- let ident = transitionKey.replace(/[^a-zA-Z0-9_-]/g, "-");
124989
- if (/^[0-9-]/.test(ident)) ident = `h-${ident}`;
124990
- wrap.viewTransitionName = ident;
124988
+ let ident2 = transitionKey.replace(/[^a-zA-Z0-9_-]/g, "-");
124989
+ if (/^[0-9-]/.test(ident2)) ident2 = `h-${ident2}`;
124990
+ wrap.viewTransitionName = ident2;
124991
124991
  }
124992
124992
  if (modifiers?.revealOnScroll && tr2 && !ctx.reduceMotion()) {
124993
124993
  const spec = enterAnimSpec(tr2, modifiers?.motion ?? ctx.kit?.motion, ctx);
@@ -139729,10 +139729,10 @@ async function bakeWidgets(fetchPath, putWidget, ownBundles) {
139729
139729
  }
139730
139730
  continue;
139731
139731
  }
139732
- const json7 = JSON.stringify(snap);
139733
- await putWidget(widgetId, json7);
139732
+ const json8 = JSON.stringify(snap);
139733
+ await putWidget(widgetId, json8);
139734
139734
  widgets[widgetId] = {
139735
- contentEtag: snap.contentEtag ?? sha2564(json7),
139735
+ contentEtag: snap.contentEtag ?? sha2564(json8),
139736
139736
  ttlSeconds: snap.refresh?.ttlSeconds ?? 0
139737
139737
  };
139738
139738
  }
@@ -139756,10 +139756,10 @@ function tokensSnapshotMessage(app, base, status) {
139756
139756
  const why = status === 400 ? `the dev-server rejected the app id as malformed (400 bad_app). It must be lowercase letters, digits and hyphens, starting with a letter or digit, at most 63 characters, with at most one interior dot \u2014 so a leading underscore or an uppercase letter fails, on every build of that app. A single dot is FINE (an owner-namespaced slug), so do not remove one to get past this` : `${status} from the tokens route`;
139757
139757
  return `[release-build] tokens snapshot FAILED for "${app}" (${why}; ${base}/v1/tokens) \u2014 this release carries no tokensHash and the app will render with NO design tokens. On iOS an undefined colour token is invisible, not merely unstyled.` + (status === 400 ? ` Fix the app id (himalaya.content.json "name", or the package directory basename) to match that grammar \u2014 but do NOT drop an owner prefix's dot: \`himi push\` requires the manifest name to equal the composed <ownerPrefix>.<name>, so removing it trades this warning for a 400 "manifest.app must match app" and the app cannot publish at all.` : "");
139758
139758
  }
139759
- function pinBakedServerBaseUrl(json7, base) {
139759
+ function pinBakedServerBaseUrl(json8, base) {
139760
139760
  const escaped = base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
139761
139761
  const boundary = /:\d+$/.test(base) ? "(?!\\d)" : "(?![\\w.-])(?!:\\d)";
139762
- return json7.replace(new RegExp(`${escaped}${boundary}`, "g"), BAKED_SERVER_BASE_URL);
139762
+ return json8.replace(new RegExp(`${escaped}${boundary}`, "g"), BAKED_SERVER_BASE_URL);
139763
139763
  }
139764
139764
  function stableStringify2(v2) {
139765
139765
  if (v2 === null || typeof v2 !== "object") return JSON.stringify(v2);
@@ -140500,15 +140500,15 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140500
140500
  }
140501
140501
  tierScreens.push(summarize3({ ...descriptor, ...lb ? { logicBundle: lb } : {} }));
140502
140502
  delete descriptor.etag;
140503
- const json7 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
140504
- await store.putScreen(app, screenId, json7);
140505
- screens[screenId] = { descriptorHash: sha2564(json7), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId) };
140506
- rememberInput(bundleName, JSON.parse(json7));
140503
+ const json8 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
140504
+ await store.putScreen(app, screenId, json8);
140505
+ screens[screenId] = { descriptorHash: sha2564(json8), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId) };
140506
+ rememberInput(bundleName, JSON.parse(json8));
140507
140507
  if (collect2?.descriptors) {
140508
140508
  baked.push({
140509
140509
  screenId,
140510
- descriptor: JSON.parse(json7),
140511
- descriptorJson: json7,
140510
+ descriptor: JSON.parse(json8),
140511
+ descriptorJson: json8,
140512
140512
  bundleName,
140513
140513
  bundleJs: bundleName ? readBundle(bundleName) ?? null : null,
140514
140514
  ...bundleMapFor(tier5Dir, bundleName ?? void 0)
@@ -140662,14 +140662,14 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140662
140662
  lint.push({ screenId, ok: false, issues: fresh });
140663
140663
  }
140664
140664
  }
140665
- const json7 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
140666
- await store.putScreen(app, screenId, json7);
140667
- screens[screenId] = { descriptorHash: sha2564(json7), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId, canonical5) };
140668
- rememberInput(bundleName, JSON.parse(json7));
140665
+ const json8 = pinBakedServerBaseUrl(JSON.stringify(descriptor), base);
140666
+ await store.putScreen(app, screenId, json8);
140667
+ screens[screenId] = { descriptorHash: sha2564(json8), bundle: bundleName, ...screenMetadataFor(screenMetadata, screenId, canonical5) };
140668
+ rememberInput(bundleName, JSON.parse(json8));
140669
140669
  if (collect2?.descriptors) {
140670
140670
  baked.push({
140671
140671
  screenId,
140672
- descriptor: JSON.parse(json7),
140672
+ descriptor: JSON.parse(json8),
140673
140673
  // The EXACT baked bytes, exactly as the exact-screen path hands
140674
140674
  // them over. Omitting them did not degrade the crawl quietly — it
140675
140675
  // took out the native-decode oracle for the whole app, which
@@ -140678,7 +140678,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140678
140678
  // bytes") and throws rather than skipping. So one templated route
140679
140679
  // anywhere in an app meant `himi test --native` reported nothing
140680
140680
  // for ANY of its screens.
140681
- descriptorJson: json7,
140681
+ descriptorJson: json8,
140682
140682
  bundleName,
140683
140683
  bundleJs: bundleName ? readBundle(bundleName) ?? null : null,
140684
140684
  ...bundleMapFor(tier5Dir, bundleName ?? void 0)
@@ -140771,7 +140771,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140771
140771
  if (store.putWidget) {
140772
140772
  const bakedWidgets = await bakeWidgets(
140773
140773
  (path2) => devFetch(`${base}${path2}`, { headers: { "X-Himalaya-App": app } }),
140774
- (widgetId, json7) => store.putWidget(app, widgetId, json7),
140774
+ (widgetId, json8) => store.putWidget(app, widgetId, json8),
140775
140775
  // `opts.tier5Dir`, NOT the `tier5Dir` local — that one falls back to the
140776
140776
  // repo-global dir, where this package's widgets cannot be told from anyone
140777
140777
  // else's and the filter would claim every app's rows as ours.
@@ -140790,9 +140790,9 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
140790
140790
  if (!js2) continue;
140791
140791
  try {
140792
140792
  const input = bundleInputs.get(name);
140793
- const json7 = JSON.stringify(extractBundleServices(js2, input ? { input } : {}));
140794
- const hash2 = sha2564(json7);
140795
- await store.putDoc(app, hash2, json7);
140793
+ const json8 = JSON.stringify(extractBundleServices(js2, input ? { input } : {}));
140794
+ const hash2 = sha2564(json8);
140795
+ await store.putDoc(app, hash2, json8);
140796
140796
  meta.servicesHash = hash2;
140797
140797
  } catch {
140798
140798
  }
@@ -153273,7 +153273,7 @@ function recommendTemplates(input) {
153273
153273
  const text9 = input.text ?? "";
153274
153274
  const owner = OWNER_WORDS.test(text9);
153275
153275
  const want = new Set(tokens(text9));
153276
- const byName2 = /* @__PURE__ */ new Map();
153276
+ const byName3 = /* @__PURE__ */ new Map();
153277
153277
  for (const row of input.catalog) {
153278
153278
  const reasons = [];
153279
153279
  let score = 0;
@@ -153294,24 +153294,24 @@ function recommendTemplates(input) {
153294
153294
  score -= 0.3;
153295
153295
  }
153296
153296
  }
153297
- byName2.set(row.name, { name: row.name, score, reasons, ...row.source ? { source: row.source } : {} });
153297
+ byName3.set(row.name, { name: row.name, score, reasons, ...row.source ? { source: row.source } : {} });
153298
153298
  }
153299
153299
  if (input.verticals?.length) {
153300
153300
  for (const c2 of scoreOverlays(input.verticals, "")) {
153301
- const name = owner && byName2.has(`${c2.id}-owner`) ? `${c2.id}-owner` : c2.id;
153302
- const rec = byName2.get(name);
153301
+ const name = owner && byName3.has(`${c2.id}-owner`) ? `${c2.id}-owner` : c2.id;
153302
+ const rec = byName3.get(name);
153303
153303
  if (!rec || c2.score <= 0) continue;
153304
153304
  rec.score += c2.score;
153305
153305
  rec.reasons.push(`site verticals: ${c2.why}`);
153306
153306
  }
153307
153307
  }
153308
153308
  const fallback = owner ? "owner-lite" : "branded-lite";
153309
- const fb = byName2.get(fallback);
153309
+ const fb = byName3.get(fallback);
153310
153310
  if (fb) {
153311
153311
  fb.score += 0.25;
153312
153312
  fb.reasons.push(owner ? "the default start for owner/operations apps" : "the default start for a customer app on a Wix site");
153313
153313
  }
153314
- const ranked = [...byName2.values()].map((r2) => ({ ...r2, score: Math.round(r2.score * 1e3) / 1e3 })).filter((r2) => r2.score > 0).sort((a2, b2) => b2.score - a2.score || a2.name.localeCompare(b2.name));
153314
+ const ranked = [...byName3.values()].map((r2) => ({ ...r2, score: Math.round(r2.score * 1e3) / 1e3 })).filter((r2) => r2.score > 0).sort((a2, b2) => b2.score - a2.score || a2.name.localeCompare(b2.name));
153315
153315
  return { owner, ranked };
153316
153316
  }
153317
153317
  var OWNER_WORDS, STOP, tokens;
@@ -156823,8 +156823,8 @@ function memoryStore(manifest, built) {
156823
156823
  },
156824
156824
  // The two members the cast used to paper over. Serve-relative, as FileReleaseStore's is; and
156825
156825
  // nothing to prune, since this store only ever holds the one release it was built with.
156826
- bundleUrl(_app, sha2566) {
156827
- return `/v1/bundles/${sha2566}.bundle.js`;
156826
+ bundleUrl(_app, sha2567) {
156827
+ return `/v1/bundles/${sha2567}.bundle.js`;
156828
156828
  },
156829
156829
  async prune() {
156830
156830
  return 0;
@@ -158801,9 +158801,9 @@ function verifyReport(dir, app, manifest) {
158801
158801
  function addAssetPayload(manifest, filePath, opts) {
158802
158802
  const bytes2 = readFileSync84(filePath);
158803
158803
  const name = opts.name ?? basename17(filePath);
158804
- const sha2566 = assetSha256(bytes2);
158804
+ const sha2567 = assetSha256(bytes2);
158805
158805
  const record2 = {
158806
- sha256: sha2566,
158806
+ sha256: sha2567,
158807
158807
  sri: assetSri(bytes2),
158808
158808
  bytes: bytes2.length,
158809
158809
  contentType: opts.contentType ?? contentTypeFor(name),
@@ -158816,7 +158816,7 @@ function addAssetPayload(manifest, filePath, opts) {
158816
158816
  const assets = { ...manifest.assets ?? {}, [name]: record2 };
158817
158817
  const next = { ...manifest, assets, releaseId: opts.mintReleaseId({ ...manifest, assets, releaseId: void 0 }) };
158818
158818
  return {
158819
- payload: { app: manifest.app, manifest: next, screens: {}, bundles: {}, assets: { [sha2566]: bytes2.toString("base64") } },
158819
+ payload: { app: manifest.app, manifest: next, screens: {}, bundles: {}, assets: { [sha2567]: bytes2.toString("base64") } },
158820
158820
  record: record2,
158821
158821
  name
158822
158822
  };
@@ -165461,9 +165461,9 @@ var init_store_iap = __esm({
165461
165461
  UsageError = class extends Error {
165462
165462
  };
165463
165463
  CommandOutput = class {
165464
- constructor(io2, json7) {
165464
+ constructor(io2, json8) {
165465
165465
  this.io = io2;
165466
- this.json = json7;
165466
+ this.json = json8;
165467
165467
  }
165468
165468
  secrets = [];
165469
165469
  get interactive() {
@@ -165634,7 +165634,7 @@ async function runStore(pos, flags, io2, deps) {
165634
165634
  const invokedAs = pos[0];
165635
165635
  const sub = invokedAs === "testflight" ? "upload" : pos[1];
165636
165636
  const app = invokedAs === "testflight" ? pos[1] : pos[2];
165637
- const json7 = on3(flags.json);
165637
+ const json8 = on3(flags.json);
165638
165638
  const print = (o2) => io2.log(JSON.stringify(o2, null, 2));
165639
165639
  const fail6 = (error, extra = {}) => {
165640
165640
  io2.error(JSON.stringify({ error, ...extra }));
@@ -165672,7 +165672,7 @@ async function runStore(pos, flags, io2, deps) {
165672
165672
  }
165673
165673
  try {
165674
165674
  const s2 = await deps.ascStatus(app, credsToEnv(creds2));
165675
- if (json7) print({ ok: true, app, team: creds2.team, ...s2 });
165675
+ if (json8) print({ ok: true, app, team: creds2.team, ...s2 });
165676
165676
  else {
165677
165677
  io2.log(` ${pad2("app")}${app} team ${creds2.team} appStoreId ${s2.appStoreId}`);
165678
165678
  for (const b2 of s2.builds.slice(0, 5)) io2.log(` ${pad2("build")}${b2.version} ${b2.processingState}`);
@@ -165692,7 +165692,7 @@ async function runStore(pos, flags, io2, deps) {
165692
165692
  }
165693
165693
  const { errors, warnings } = partitionFindings(findings);
165694
165694
  if (sub === "preflight") {
165695
- if (json7) print({ ok: errors.length === 0, app, findings });
165695
+ if (json8) print({ ok: errors.length === 0, app, findings });
165696
165696
  else {
165697
165697
  for (const f2 of findings) io2.log(` ${f2.level === "error" ? "\u2717" : "!"} ${f2.message}`);
165698
165698
  io2.log(errors.length === 0 ? `
@@ -165730,7 +165730,7 @@ async function runStore(pos, flags, io2, deps) {
165730
165730
  const reuseExisting = on3(flags["skip-build"]);
165731
165731
  const nextBuild = reuseExisting ? version4.build : version4.build + 1;
165732
165732
  const plannedSteps = UPLOAD_STEPS.filter((s2) => !(reuseExisting && BUILD_STEPS.has(s2)));
165733
- if (!json7) {
165733
+ if (!json8) {
165734
165734
  io2.log("");
165735
165735
  for (const line of accountHeader({ app, bundleId, version: version4, nextBuild, creds, track })) io2.log(line);
165736
165736
  io2.log("");
@@ -165758,7 +165758,7 @@ async function runStore(pos, flags, io2, deps) {
165758
165758
  steps: [...plannedSteps],
165759
165759
  warnings: warnings.map((w2) => w2.message)
165760
165760
  };
165761
- if (json7) print(plan);
165761
+ if (json8) print(plan);
165762
165762
  else {
165763
165763
  io2.log(`
165764
165764
  dry run \u2014 nothing was built, uploaded or bumped.`);
@@ -165811,11 +165811,11 @@ async function runStore(pos, flags, io2, deps) {
165811
165811
  stderr: r2.stderr.split("\n").slice(-20).join("\n")
165812
165812
  });
165813
165813
  }
165814
- if (!json7) io2.log(` \u2713 ${pad2(step)}ok`);
165814
+ if (!json8) io2.log(` \u2713 ${pad2(step)}ok`);
165815
165815
  }
165816
165816
  if (on3(flags["no-wait"])) {
165817
165817
  const out2 = { ok: true, app, build: uploaded.build, uploaded: true, processed: null, note: "upload accepted; processing not awaited. `himi store status " + app + "` tells you whether it landed." };
165818
- if (json7) print(out2);
165818
+ if (json8) print(out2);
165819
165819
  else io2.log(`
165820
165820
  \u2713 uploaded build ${uploaded.build} \u2014 processing not awaited.`);
165821
165821
  return 0;
@@ -165836,7 +165836,7 @@ async function runStore(pos, flags, io2, deps) {
165836
165836
  const mine = findBuild(s2.builds, uploaded.build);
165837
165837
  if (mine && mine.processingState === "VALID") {
165838
165838
  const url = `https://appstoreconnect.apple.com/apps/${s2.appStoreId}/testflight/ios`;
165839
- if (json7) print({ ok: true, app, build: uploaded.build, processingState: mine.processingState, url });
165839
+ if (json8) print({ ok: true, app, build: uploaded.build, processingState: mine.processingState, url });
165840
165840
  else io2.log(`
165841
165841
  \u2713 TestFlight ${uploaded.marketing} (${uploaded.build}) \u2014 Processing complete
165842
165842
  ${url}`);
@@ -165856,7 +165856,7 @@ async function runStore(pos, flags, io2, deps) {
165856
165856
  { app, build: uploaded.build, uploaded: true, processed: false }
165857
165857
  );
165858
165858
  }
165859
- if (!json7) io2.log(` \u2026 processing (${Math.round(waited / 1e3)}s)`);
165859
+ if (!json8) io2.log(` \u2026 processing (${Math.round(waited / 1e3)}s)`);
165860
165860
  await deps.sleep(PROCESSING_POLL_MS);
165861
165861
  waited += PROCESSING_POLL_MS;
165862
165862
  }
@@ -167284,7 +167284,7 @@ function parseDataFlag(raw) {
167284
167284
  async function runNotify(pos, flags, io2, deps = {}) {
167285
167285
  const doFetch = deps.fetchImpl ?? fetch;
167286
167286
  const str11 = (v2) => typeof v2 === "string" ? v2 : void 0;
167287
- const json7 = flags.json === true;
167287
+ const json8 = flags.json === true;
167288
167288
  const print = (v2) => io2.log(JSON.stringify(v2, null, 2));
167289
167289
  const sub = pos[1];
167290
167290
  const USAGE2 = "himi notify send --app <app> (--token <t> | --device <id> | --all) --title <s> [--body <s>] [--link <url>]\n [--platform ios|android] [--category <c>] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]\n [--data k=v ...]\nhimi notify tokens --app <app> [--platform ios|android] [--include-disabled] [--json]\nhimi notify doctor [--json]";
@@ -167329,7 +167329,7 @@ async function runNotify(pos, flags, io2, deps = {}) {
167329
167329
  ...session ? {} : { hint: "no admin session \u2014 pass --admin-session or set HIMALAYA_ADMIN_SESSION" },
167330
167330
  ...reachable && authed && !senders ? { hint: "this admin-server predates /v1/admin/push-doctor \u2014 upgrade it to see sender readiness" } : {}
167331
167331
  };
167332
- if (json7) {
167332
+ if (json8) {
167333
167333
  print(report3);
167334
167334
  return report3.ok ? 0 : 1;
167335
167335
  }
@@ -167377,7 +167377,7 @@ async function runNotify(pos, flags, io2, deps = {}) {
167377
167377
  return 1;
167378
167378
  }
167379
167379
  const body = await res2.json();
167380
- if (json7) {
167380
+ if (json8) {
167381
167381
  print({ ok: true, app, count: body.tokens.length, tokens: body.tokens });
167382
167382
  return 0;
167383
167383
  }
@@ -167467,7 +167467,7 @@ async function runNotify(pos, flags, io2, deps = {}) {
167467
167467
  io2.error(JSON.stringify({ error: out2.error ?? `send failed: ${res.status}` }));
167468
167468
  return 1;
167469
167469
  }
167470
- if (json7) {
167470
+ if (json8) {
167471
167471
  print(out2);
167472
167472
  return out2.ok ? 0 : 1;
167473
167473
  }
@@ -167679,7 +167679,7 @@ function adoptFromBook(book, opts = {}) {
167679
167679
  return { entities, functions, skipped };
167680
167680
  }
167681
167681
  function renderAdoptModule(r2, source = ".himi/base44-book/book.json") {
167682
- const json7 = (v2) => JSON.stringify(v2, null, 2);
167682
+ const json8 = (v2) => JSON.stringify(v2, null, 2);
167683
167683
  return [
167684
167684
  `// Generated by \`himi base44 adopt\` from ${source}. Every entry is ADOPTED: the Base44 app's`,
167685
167685
  `// own entity or function, read-only to himi. Review it, then wire it into config.ts:`,
@@ -167687,9 +167687,9 @@ function renderAdoptModule(r2, source = ".himi/base44-book/book.json") {
167687
167687
  `// See docs/base44-entities.md.`,
167688
167688
  `import type { Base44EntitySpec, Base44FunctionSpec } from "@wix/himalaya/core";`,
167689
167689
  ``,
167690
- `export const base44Entities = ${json7(r2.entities)} satisfies Base44EntitySpec[];`,
167690
+ `export const base44Entities = ${json8(r2.entities)} satisfies Base44EntitySpec[];`,
167691
167691
  ``,
167692
- `export const base44Functions = ${json7(r2.functions)} satisfies Base44FunctionSpec[];`,
167692
+ `export const base44Functions = ${json8(r2.functions)} satisfies Base44FunctionSpec[];`,
167693
167693
  ``
167694
167694
  ].join("\n");
167695
167695
  }
@@ -167738,6 +167738,380 @@ var init_base44_adopt = __esm({
167738
167738
  }
167739
167739
  });
167740
167740
 
167741
+ // src/base44-screens/plan.ts
167742
+ var plan_exports = {};
167743
+ __export(plan_exports, {
167744
+ ENTITY_TAB_SLOTS: () => ENTITY_TAB_SLOTS,
167745
+ MORE_SCREEN_ID: () => MORE_SCREEN_ID,
167746
+ PlanError: () => PlanError,
167747
+ RESERVED_SCREEN_IDS: () => RESERVED_SCREEN_IDS,
167748
+ displayName: () => displayName2,
167749
+ gateFor: () => gateFor,
167750
+ humanize: () => humanize,
167751
+ planEntity: () => planEntity,
167752
+ planEntityScreens: () => planEntityScreens,
167753
+ pluralize: () => pluralize,
167754
+ rowTitleField: () => rowTitleField,
167755
+ snake: () => snake
167756
+ });
167757
+ function ownerKeyOf(rule) {
167758
+ if (!rule || typeof rule !== "object" || Array.isArray(rule)) return void 0;
167759
+ const entries = Object.entries(rule);
167760
+ if (entries.length !== 1) return void 0;
167761
+ const [k2, v2] = entries[0];
167762
+ return OWNER_RULES.find(([key3, tpl]) => key3 === k2 && v2 === tpl)?.[0];
167763
+ }
167764
+ function gateFor(rule) {
167765
+ if (rule === void 0 || rule === null || rule === false) return "never";
167766
+ if (rule === true) return "always";
167767
+ return ownerKeyOf(rule) ? "owner" : "signed-in";
167768
+ }
167769
+ function pluralize(word) {
167770
+ if (/[^aeiou]y$/i.test(word)) return `${word.slice(0, -1)}ies`;
167771
+ if (/(s|x|z|ch|sh)$/i.test(word)) return `${word}es`;
167772
+ return `${word}s`;
167773
+ }
167774
+ function snake(name) {
167775
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/[^A-Za-z0-9]+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
167776
+ }
167777
+ function humanize(key3) {
167778
+ const words = snake(key3).replace(/_/g, " ").trim();
167779
+ return words ? words.charAt(0).toUpperCase() + words.slice(1) : key3;
167780
+ }
167781
+ function displayName2(name) {
167782
+ const words = name.match(/[A-Z]+(?![a-z])|[A-Z][a-z0-9]*|[a-z0-9]+/g) ?? [name];
167783
+ return words.map((w2, i2) => w2.length > 1 && w2 === w2.toUpperCase() ? w2 : i2 === 0 ? w2.charAt(0).toUpperCase() + w2.slice(1).toLowerCase() : w2.toLowerCase()).join(" ");
167784
+ }
167785
+ function rowTitleField(spec) {
167786
+ const fields = Array.isArray(spec.fields) ? spec.fields : [];
167787
+ if (spec.displayField) return spec.displayField;
167788
+ const strings = fields.filter((f2) => isPlainString(f2) && !isLongText(f2) && !isImage(f2));
167789
+ const required3 = new Set(spec.required ?? []);
167790
+ const req = strings.filter((f2) => required3.has(f2.key));
167791
+ const preferred = (pool2) => PREFERRED_TITLES.map((k2) => pool2.find((f2) => f2.key === k2)).find(Boolean);
167792
+ return (preferred(req) ?? req[0] ?? preferred(strings) ?? strings[0])?.key ?? "id";
167793
+ }
167794
+ function rowSubtitle(spec, title) {
167795
+ const short = (f2) => f2.key !== title && (isEnum(f2) || isDate(f2) || f2.type === "number" || f2.type === "integer");
167796
+ return (spec.fields ?? []).filter(short).slice(0, 2).map((f2) => f2.key);
167797
+ }
167798
+ function detailShow(f2) {
167799
+ if (f2.type === "object" || f2.type === "array" && f2.items?.type !== "string") return "section";
167800
+ if (f2.type === "array") return "chips";
167801
+ if (f2.type === "boolean") return "badge";
167802
+ if (isEnum(f2)) return "badge";
167803
+ if (isDate(f2)) return "date";
167804
+ if (isImage(f2)) return "image";
167805
+ if (f2.type === "string" && (f2.format === "email" || f2.format === "uri")) return "link";
167806
+ if (isLongText(f2)) return "longText";
167807
+ return "text";
167808
+ }
167809
+ function formControl(f2) {
167810
+ if (f2.type === "object" || f2.type === "array" && f2.items?.type !== "string") return null;
167811
+ if (f2.type === "array") return { control: "ChipsInput" };
167812
+ if (f2.type === "boolean") return { control: "Toggle" };
167813
+ if (isEnum(f2)) {
167814
+ const options = (f2.enum ?? []).map(String);
167815
+ return { control: options.length <= SEGMENTED_MAX ? "SegmentedControl" : "Picker", options, optionLabels: options.map(humanize) };
167816
+ }
167817
+ if (f2.type === "number" || f2.type === "integer") return { control: "NumberField", keyboard: "number" };
167818
+ if (isDate(f2)) return { control: "DatePicker" };
167819
+ if (isImage(f2)) return { control: "TextField", keyboard: "url", forcedReadOnly: true };
167820
+ if (f2.type === "string" && f2.format === "email") return { control: "TextField", keyboard: "email" };
167821
+ if (f2.type === "string" && f2.format === "uri") return { control: "TextField", keyboard: "url" };
167822
+ if (isLongText(f2)) return { control: "MultilineTextField" };
167823
+ return { control: "TextField" };
167824
+ }
167825
+ function fieldReadOnly(f2) {
167826
+ const rls = f2.rls;
167827
+ return !!rls && typeof rls === "object" && "write" in rls && rls.write !== true;
167828
+ }
167829
+ function ruleFor(spec, op) {
167830
+ const rls = spec.rls;
167831
+ if (!rls || typeof rls !== "object") return void 0;
167832
+ return op in rls ? rls[op] : rls.write;
167833
+ }
167834
+ function planEntity(spec, app = "") {
167835
+ const name = String(isAdoptedEntity(spec) ? spec.base44Name : spec.name);
167836
+ const singularSlug = snake(name);
167837
+ const pluralSlug = pluralize(singularSlug);
167838
+ const kebab2 = singularSlug.replace(/_/g, "-");
167839
+ const title = spec.title?.trim() || displayName2(name);
167840
+ const listTitle = pluralize(title);
167841
+ const titleField = rowTitleField(spec);
167842
+ const fields = (spec.fields ?? []).filter((f2) => f2 && typeof f2.key === "string");
167843
+ const titleSpec = fields.find((f2) => f2.key === titleField);
167844
+ const composable = !!titleSpec && isPlainString(titleSpec) && !fieldReadOnly(titleSpec);
167845
+ const gates = { create: composable ? gateFor(ruleFor(spec, "create")) : "never", update: gateFor(ruleFor(spec, "update")), delete: gateFor(ruleFor(spec, "delete")) };
167846
+ const owner = {};
167847
+ for (const op of ["update", "delete"]) {
167848
+ if (gates[op] === "owner") owner[op] = ownerKeyOf(ruleFor(spec, op));
167849
+ }
167850
+ const createDefaults = {};
167851
+ for (const f2 of fields) if (f2.default !== void 0) createDefaults[f2.key] = f2.default;
167852
+ return {
167853
+ entity: physicalEntityName(spec, app),
167854
+ name,
167855
+ service: { list: `b44${pascal(pluralSlug)}`, detail: `b44${pascal(singularSlug)}` },
167856
+ ids: { list: `b44_${pluralSlug}`, detail: `b44_${singularSlug}`, bundleList: `b44-${kebab2}-list`, bundleDetail: `b44-${kebab2}` },
167857
+ title,
167858
+ listTitle,
167859
+ countNoun: [nounOf(title), nounOf(listTitle)],
167860
+ row: { title: titleField, subtitle: rowSubtitle(spec, titleField) },
167861
+ detail: fields.filter((f2) => f2.key !== titleField).map((f2) => ({
167862
+ key: f2.key,
167863
+ label: humanize(f2.key),
167864
+ show: detailShow(f2),
167865
+ type: f2.type,
167866
+ ...f2.format ? { format: f2.format } : {}
167867
+ })),
167868
+ form: fields.flatMap((f2) => {
167869
+ const c2 = formControl(f2);
167870
+ if (!c2) return [];
167871
+ const { forcedReadOnly, ...control } = c2;
167872
+ return [{
167873
+ key: f2.key,
167874
+ label: humanize(f2.key),
167875
+ type: f2.type,
167876
+ ...f2.format ? { format: f2.format } : {},
167877
+ ...control,
167878
+ readOnly: forcedReadOnly === true || fieldReadOnly(f2)
167879
+ }];
167880
+ }),
167881
+ gates,
167882
+ owner,
167883
+ createDefaults
167884
+ };
167885
+ }
167886
+ function planEntityScreens(specs, app = "") {
167887
+ const plans = specs.map((s2) => planEntity(s2, app));
167888
+ const owners = new Map(RESERVED_SCREEN_IDS.map((id) => [`screen id "${id}"`, "the base44-starter template"]));
167889
+ for (const p2 of plans) {
167890
+ const claims = [
167891
+ ...[p2.ids.list, p2.ids.detail].map((id) => [`screen id "${id}"`, id]),
167892
+ ...[p2.ids.bundleList, p2.ids.bundleDetail].map((b2) => [`worker bundle "${b2}"`, b2]),
167893
+ ...[p2.service.list, p2.service.detail].map((svc) => [`service "${svc}"`, svc])
167894
+ ];
167895
+ for (const [what, id] of claims) {
167896
+ const other = owners.get(what);
167897
+ if (other !== void 0) {
167898
+ throw new PlanError("entity_id_collision", `entity "${p2.name}" would generate ${what}, which ${other} already uses \u2014 rename one of them`, { entity: p2.name, screenId: id, with: other });
167899
+ }
167900
+ owners.set(what, `entity "${p2.name}"`);
167901
+ }
167902
+ }
167903
+ const names = plans.map((p2) => p2.name);
167904
+ if (names.length <= ENTITY_TAB_SLOTS) return { plans, tabs: names, more: [] };
167905
+ return { plans, tabs: names.slice(0, ENTITY_TAB_SLOTS - 1), more: names.slice(ENTITY_TAB_SLOTS - 1) };
167906
+ }
167907
+ var PlanError, RESERVED_SCREEN_IDS, MORE_SCREEN_ID, ENTITY_TAB_SLOTS, OWNER_RULES, pascal, PREFERRED_TITLES, LONG_TEXT, IMAGE_KEY, SEGMENTED_MAX, isPlainString, isEnum, isDate, isImage, isLongText, nounOf;
167908
+ var init_plan4 = __esm({
167909
+ "src/base44-screens/plan.ts"() {
167910
+ init_base44_schema();
167911
+ PlanError = class extends Error {
167912
+ constructor(code, message, extra = {}) {
167913
+ super(message);
167914
+ this.code = code;
167915
+ this.extra = extra;
167916
+ this.name = "PlanError";
167917
+ }
167918
+ };
167919
+ RESERVED_SCREEN_IDS = ["b44_functions", "b44_account", "b44_more"];
167920
+ MORE_SCREEN_ID = "b44_more";
167921
+ ENTITY_TAB_SLOTS = 3;
167922
+ OWNER_RULES = [["created_by", "{{user.email}}"], ["created_by_id", "{{user.id}}"]];
167923
+ pascal = (slug) => slug.split("_").filter(Boolean).map((p2) => p2.charAt(0).toUpperCase() + p2.slice(1)).join("");
167924
+ PREFERRED_TITLES = ["title", "name", "label"];
167925
+ LONG_TEXT = /^(description|notes?|body|content|summary|details?|bio|comments?|message|text)$|_(description|notes?|body|summary)$/i;
167926
+ IMAGE_KEY = /^(image|photo|avatar|picture|thumbnail|logo|cover)$|_url$/i;
167927
+ SEGMENTED_MAX = 4;
167928
+ isPlainString = (f2) => f2.type === "string" && !f2.enum && (f2.format === void 0 || f2.format === "richtext");
167929
+ isEnum = (f2) => Array.isArray(f2.enum) && f2.enum.length > 0;
167930
+ isDate = (f2) => f2.type === "string" && (f2.format === "date" || f2.format === "date-time");
167931
+ isImage = (f2) => f2.type === "string" && !isEnum(f2) && (f2.format === "file" || IMAGE_KEY.test(f2.key));
167932
+ isLongText = (f2) => f2.type === "string" && !isEnum(f2) && (f2.format === "richtext" || LONG_TEXT.test(f2.key));
167933
+ nounOf = (display) => display.split(" ").map((w2) => /^[A-Z]{2,}s?$/.test(w2) ? w2 : w2.toLowerCase()).join(" ");
167934
+ }
167935
+ });
167936
+
167937
+ // src/base44-screens/generate.ts
167938
+ var generate_exports = {};
167939
+ __export(generate_exports, {
167940
+ ENGINE_FILES: () => ENGINE_FILES,
167941
+ GENERATED_HEADER: () => GENERATED_HEADER,
167942
+ isEdited: () => isEdited,
167943
+ orphanedFiles: () => orphanedFiles,
167944
+ renderAll: () => renderAll,
167945
+ renderDetailEntry: () => renderDetailEntry,
167946
+ renderDevRegistrations: () => renderDevRegistrations,
167947
+ renderListEntry: () => renderListEntry,
167948
+ renderNav: () => renderNav,
167949
+ renderPlanModule: () => renderPlanModule,
167950
+ withHeader: () => withHeader,
167951
+ writeGenerated: () => writeGenerated
167952
+ });
167953
+ import { createHash as createHash43 } from "node:crypto";
167954
+ import { existsSync as existsSync79, mkdirSync as mkdirSync37, readdirSync as readdirSync35, readFileSync as readFileSync100, writeFileSync as writeFileSync34 } from "node:fs";
167955
+ import { dirname as dirname46, join as join84 } from "node:path";
167956
+ function withHeader(body) {
167957
+ return `${GENERATED_HEADER}${sha2566(body)}
167958
+ ${body}`;
167959
+ }
167960
+ function isEdited(content) {
167961
+ const m2 = HEADER_RE.exec(content);
167962
+ return !m2 || sha2566(content.slice(m2[0].length)) !== m2[1];
167963
+ }
167964
+ function renderPlanModule(p2) {
167965
+ return `// The ${p2.name} screens' plan, derived from config.ts \`base44.entities\` by \`himi base44 screens\`
167966
+ // (himi docs base44-app-to-himi-app): the list (../${p2.ids.bundleList}), the detail
167967
+ // (../${p2.ids.bundleDetail}) and dev/entities.generated.ts all read it. Edit it to customise the
167968
+ // screens \u2014 the generator then leaves this file alone (skipped-edited) unless --force.
167969
+ import type { EntityScreenPlan } from "./entity-plan.js";
167970
+
167971
+ export const PLAN: EntityScreenPlan = ${json7(p2)};
167972
+ `;
167973
+ }
167974
+ function renderListEntry(p2) {
167975
+ return `// The ${p2.name} list (${p2.ids.list}) \u2014 generated by \`himi base44 screens\`. The behaviour lives in the
167976
+ // shared engine (../_shared/entity-list.ts); this file only names the entity's plan.
167977
+ import { defineEntityList } from "../_shared/entity-list.js";
167978
+ import { PLAN } from "../_shared/${planFile(p2)}.js";
167979
+
167980
+ export default defineEntityList(PLAN);
167981
+ `;
167982
+ }
167983
+ function renderDetailEntry(p2) {
167984
+ return `// One ${p2.name} (${p2.ids.detail}), pushed from ${p2.ids.list} with its id \u2014 generated by \`himi base44 screens\`.
167985
+ // The behaviour lives in the shared engine (../_shared/entity-detail.ts); this file only names the
167986
+ // entity's plan.
167987
+ import { defineEntityDetail } from "../_shared/entity-detail.js";
167988
+ import { PLAN } from "../_shared/${planFile(p2)}.js";
167989
+
167990
+ export default defineEntityDetail(PLAN);
167991
+ `;
167992
+ }
167993
+ function renderDevRegistrations(sp) {
167994
+ const plans = byName2(sp);
167995
+ const imports = sp.plans.map((p2) => `import { PLAN as ${ident(p2)} } from "../tier5-src/_shared/${planFile(p2)}.ts";`);
167996
+ const exact = sp.plans.flatMap((p2) => [
167997
+ ` ${p2.ids.list}: (c?: BuildCtx) => entityListScreen(${ident(p2)}, c),`,
167998
+ ` ${p2.ids.detail}: (c?: BuildCtx) => entityDetailScreen(${ident(p2)}, c),`
167999
+ ]);
168000
+ const more = sp.more.map((n2) => ident(plans.get(n2)));
168001
+ if (more.length) exact.push(` ${MORE_SCREEN_ID}: () => entityMoreScreen([${more.join(", ")}]),`);
168002
+ const seed2 = sp.tabs.map((n2) => ` ${plans.get(n2).ids.list}: entityListScreen(${ident(plans.get(n2))}),`);
168003
+ if (more.length) seed2.push(` ${MORE_SCREEN_ID}: entityMoreScreen([${more.join(", ")}]),`);
168004
+ const home = sp.tabs.length ? JSON.stringify(plans.get(sp.tabs[0]).ids.list) : "undefined";
168005
+ return `// The generated entity screens' registrations \u2014 written by \`himi base44 screens\` from config.ts
168006
+ // \`base44.entities\`; dev/index.ts spreads them into screens.exact and seed.screens. The builders
168007
+ // are dev/entity-screens.ts.
168008
+ import type { BuildCtx } from "./dsl.ts";
168009
+ import { ${more.length ? "entityDetailScreen, entityListScreen, entityMoreScreen" : "entityDetailScreen, entityListScreen"} } from "./entity-screens.ts";
168010
+ ${imports.join("\n")}${imports.length ? "\n" : ""}
168011
+ /** Every generated screen, by id. */
168012
+ export const entityScreens = {
168013
+ ${exact.join("\n")}${exact.length ? "\n" : ""}};
168014
+
168015
+ /** The entity tab roots, seeded so the tab bar paints on a cold offline start. */
168016
+ export const entitySeedScreens = () => ({
168017
+ ${seed2.join("\n")}${seed2.length ? "\n" : ""}});
168018
+
168019
+ /** The first entity tab \u2014 the app's home \u2014 or undefined when no entity is declared. */
168020
+ export const entityHomeScreenId: string | undefined = ${home};
168021
+ `;
168022
+ }
168023
+ function renderNav(sp) {
168024
+ const plans = byName2(sp);
168025
+ const tabbed = new Set(sp.tabs);
168026
+ const linkable = [];
168027
+ const tabs = [];
168028
+ for (const p2 of sp.plans) {
168029
+ const tabId = tabbed.has(p2.name) ? p2.ids.list.replace(/^b44_/, "") : "more";
168030
+ linkable.push({ screenId: p2.ids.list, isPublic: true, paramKeys: [], parentScreenId: tabbed.has(p2.name) ? "" : MORE_SCREEN_ID, tabId });
168031
+ linkable.push({ screenId: p2.ids.detail, isPublic: true, paramKeys: ["id"], parentScreenId: p2.ids.list, tabId });
168032
+ }
168033
+ for (const n2 of sp.tabs) {
168034
+ const p2 = plans.get(n2);
168035
+ tabs.push({ id: p2.ids.list.replace(/^b44_/, ""), screenId: p2.ids.list, label: p2.listTitle, icon: "list", selectedIcon: "list" });
168036
+ }
168037
+ if (sp.more.length) {
168038
+ linkable.push({ screenId: MORE_SCREEN_ID, isPublic: true, paramKeys: [], parentScreenId: "", tabId: "more" });
168039
+ tabs.push({ id: "more", screenId: MORE_SCREEN_ID, label: "More", icon: "grid", selectedIcon: "grid" });
168040
+ }
168041
+ const lines = (items) => items.map((i2) => ` ${JSON.stringify(i2)},`).join("\n");
168042
+ return `// The generated entity screens' navigation \u2014 written by \`himi base44 screens\` from config.ts
168043
+ // \`base44.entities\`. config.ts spreads these in front of the Functions and Account entries; tab
168044
+ // labels go through its copyOf("tab.<id>", \u2026) there, so copy overrides keep working.
168045
+ export const entityLinkableScreens = [
168046
+ ${lines(linkable)}${linkable.length ? "\n" : ""}];
168047
+
168048
+ export const entityTabItems = [
168049
+ ${lines(tabs)}${tabs.length ? "\n" : ""}];
168050
+ `;
168051
+ }
168052
+ function renderAll(sp) {
168053
+ const files = [];
168054
+ for (const p2 of sp.plans) {
168055
+ files.push({ path: `tier5-src/_shared/${planFile(p2)}.ts`, body: renderPlanModule(p2) });
168056
+ files.push({ path: `tier5-src/${p2.ids.bundleList}/index.ts`, body: renderListEntry(p2) });
168057
+ files.push({ path: `tier5-src/${p2.ids.bundleDetail}/index.ts`, body: renderDetailEntry(p2) });
168058
+ }
168059
+ files.push({ path: "dev/entities.generated.ts", body: renderDevRegistrations(sp) });
168060
+ files.push({ path: "config.entities.generated.ts", body: renderNav(sp) });
168061
+ return files;
168062
+ }
168063
+ function writeGenerated(dir, files, opts = {}) {
168064
+ const out2 = [];
168065
+ for (const f2 of files) {
168066
+ const abs = join84(dir, f2.path);
168067
+ const next = withHeader(f2.body);
168068
+ let outcome;
168069
+ if (!existsSync79(abs)) outcome = "written";
168070
+ else {
168071
+ const cur = readFileSync100(abs, "utf8");
168072
+ if (cur === next) outcome = "unchanged";
168073
+ else if (isEdited(cur) && !opts.force) outcome = "skipped-edited";
168074
+ else outcome = "written";
168075
+ }
168076
+ if (outcome === "written" && !opts.dryRun) {
168077
+ mkdirSync37(dirname46(abs), { recursive: true });
168078
+ writeFileSync34(abs, next);
168079
+ }
168080
+ out2.push({ path: f2.path, outcome });
168081
+ }
168082
+ return { files: out2 };
168083
+ }
168084
+ function orphanedFiles(dir, current) {
168085
+ const keep = new Set(current.map((f2) => f2.path));
168086
+ const found = [];
168087
+ const t5 = join84(dir, "tier5-src");
168088
+ if (!existsSync79(t5)) return found;
168089
+ const candidates = readdirSync35(t5).filter((d2) => d2.startsWith("b44-")).map((d2) => `tier5-src/${d2}/index.ts`);
168090
+ const shared = join84(t5, "_shared");
168091
+ if (existsSync79(shared)) {
168092
+ for (const f2 of readdirSync35(shared)) if (/^b44-.*-plan\.ts$/.test(f2)) candidates.push(`tier5-src/_shared/${f2}`);
168093
+ }
168094
+ for (const rel of candidates.sort()) {
168095
+ if (keep.has(rel) || !existsSync79(join84(dir, rel))) continue;
168096
+ if (readFileSync100(join84(dir, rel), "utf8").startsWith(GENERATED_HEADER)) found.push(rel);
168097
+ }
168098
+ return found;
168099
+ }
168100
+ var GENERATED_HEADER, HEADER_RE, ENGINE_FILES, sha2566, planFile, ident, json7, byName2;
168101
+ var init_generate = __esm({
168102
+ "src/base44-screens/generate.ts"() {
168103
+ init_plan4();
168104
+ GENERATED_HEADER = "// @generated by himi base44 screens \u2014 sha256:";
168105
+ HEADER_RE = /^\/\/ @generated by himi base44 screens — sha256:([0-9a-f]{64})\n/;
168106
+ ENGINE_FILES = ["tier5-src/_shared/entity-list.ts", "tier5-src/_shared/entity-detail.ts", "tier5-src/_shared/entity-plan.ts", "dev/entity-screens.ts"];
168107
+ sha2566 = (s2) => createHash43("sha256").update(s2).digest("hex");
168108
+ planFile = (p2) => `${p2.ids.bundleDetail}-plan`;
168109
+ ident = (p2) => `plan_${p2.ids.detail.replace(/^b44_/, "")}`;
168110
+ json7 = (v2) => JSON.stringify(v2, null, 2);
168111
+ byName2 = (sp) => new Map(sp.plans.map((p2) => [p2.name, p2]));
168112
+ }
168113
+ });
168114
+
167741
168115
  // src/base44-auth.ts
167742
168116
  var base44_auth_exports = {};
167743
168117
  __export(base44_auth_exports, {
@@ -167755,20 +168129,20 @@ __export(base44_auth_exports, {
167755
168129
  usableBase44Auth: () => usableBase44Auth,
167756
168130
  writeBase44Session: () => writeBase44Session
167757
168131
  });
167758
- import { chmodSync as chmodSync4, existsSync as existsSync79, mkdirSync as mkdirSync37, readFileSync as readFileSync100, rmSync as rmSync20, writeFileSync as writeFileSync34 } from "node:fs";
168132
+ import { chmodSync as chmodSync4, existsSync as existsSync80, mkdirSync as mkdirSync38, readFileSync as readFileSync101, rmSync as rmSync20, writeFileSync as writeFileSync35 } from "node:fs";
167759
168133
  import { homedir as homedir13 } from "node:os";
167760
- import { dirname as dirname46, join as join84 } from "node:path";
168134
+ import { dirname as dirname47, join as join85 } from "node:path";
167761
168135
  function base44ApiBase(env = process.env) {
167762
168136
  return (env.BASE44_API_URL?.trim() || "https://app.base44.com").replace(/\/+$/, "");
167763
168137
  }
167764
168138
  function base44SessionPath(home = homedir13()) {
167765
- return join84(home, ".himi", "base44", "session.json");
168139
+ return join85(home, ".himi", "base44", "session.json");
167766
168140
  }
167767
168141
  function readBase44Session(home) {
167768
168142
  const p2 = base44SessionPath(home);
167769
- if (!existsSync79(p2)) return void 0;
168143
+ if (!existsSync80(p2)) return void 0;
167770
168144
  try {
167771
- const s2 = JSON.parse(readFileSync100(p2, "utf8"));
168145
+ const s2 = JSON.parse(readFileSync101(p2, "utf8"));
167772
168146
  return typeof s2.accessToken === "string" && s2.accessToken ? s2 : void 0;
167773
168147
  } catch {
167774
168148
  return void 0;
@@ -167776,15 +168150,15 @@ function readBase44Session(home) {
167776
168150
  }
167777
168151
  function writeBase44Session(session, home) {
167778
168152
  const p2 = base44SessionPath(home);
167779
- mkdirSync37(dirname46(p2), { recursive: true, mode: 448 });
167780
- writeFileSync34(p2, `${JSON.stringify(session, null, 2)}
168153
+ mkdirSync38(dirname47(p2), { recursive: true, mode: 448 });
168154
+ writeFileSync35(p2, `${JSON.stringify(session, null, 2)}
167781
168155
  `, { mode: 384 });
167782
168156
  chmodSync4(p2, 384);
167783
168157
  return p2;
167784
168158
  }
167785
168159
  function deleteBase44Session(home) {
167786
168160
  const p2 = base44SessionPath(home);
167787
- if (!existsSync79(p2)) return false;
168161
+ if (!existsSync80(p2)) return false;
167788
168162
  rmSync20(p2, { force: true });
167789
168163
  return true;
167790
168164
  }
@@ -167879,9 +168253,9 @@ async function base44DeviceLogin(opts) {
167879
168253
  throw new Error("the Base44 sign-in code expired before it was approved \u2014 run `himi base44 login` again");
167880
168254
  }
167881
168255
  function importBase44CliSession(home = homedir13(), env = process.env) {
167882
- const src = join84(home, ".base44", "auth", "auth.json");
167883
- if (!existsSync79(src)) throw new Error(`no Base44 CLI session at ${src} \u2014 run \`npx base44 login\` first, or use \`himi base44 login\``);
167884
- const a2 = JSON.parse(readFileSync100(src, "utf8"));
168256
+ const src = join85(home, ".base44", "auth", "auth.json");
168257
+ if (!existsSync80(src)) throw new Error(`no Base44 CLI session at ${src} \u2014 run \`npx base44 login\` first, or use \`himi base44 login\``);
168258
+ const a2 = JSON.parse(readFileSync101(src, "utf8"));
167885
168259
  if (typeof a2.accessToken !== "string" || !a2.accessToken) throw new Error(`${src} holds no access token`);
167886
168260
  const session = {
167887
168261
  accessToken: a2.accessToken,
@@ -168671,9 +169045,9 @@ function asObj(x2) {
168671
169045
  return x2 && typeof x2 === "object" && !Array.isArray(x2) ? x2 : void 0;
168672
169046
  }
168673
169047
  function invokeFlag(identities, key3) {
168674
- const ident = asObj(asObj(identities)?.[key3]);
168675
- if (!ident || typeof ident.invoke !== "boolean") return void 0;
168676
- return ident.invoke;
169048
+ const ident2 = asObj(asObj(identities)?.[key3]);
169049
+ if (!ident2 || typeof ident2.invoke !== "boolean") return void 0;
169050
+ return ident2.invoke;
168677
169051
  }
168678
169052
  function methodStar(entry) {
168679
169053
  return asObj(asObj(entry)?.["*"]);
@@ -170174,8 +170548,8 @@ var init_brand_font = __esm({
170174
170548
  });
170175
170549
 
170176
170550
  // src/site-analyze/write-artifacts.ts
170177
- import { mkdirSync as mkdirSync38, rmSync as rmSync21, writeFileSync as writeFileSync35 } from "node:fs";
170178
- import { join as join85 } from "node:path";
170551
+ import { mkdirSync as mkdirSync39, rmSync as rmSync21, writeFileSync as writeFileSync36 } from "node:fs";
170552
+ import { join as join86 } from "node:path";
170179
170553
  function attachVeloSources(book, sources, put) {
170180
170554
  const byPath = new Map((sources ?? []).map((s2) => [s2.path, s2.text]));
170181
170555
  for (const f2 of book.velo.files) {
@@ -170330,13 +170704,13 @@ async function downloadMedia(book, fetchImpl, put) {
170330
170704
  }
170331
170705
  async function writeBusinessBook(result, opts) {
170332
170706
  const out2 = opts.outDir;
170333
- mkdirSync38(join85(out2, "evidence/pages"), { recursive: true });
170334
- mkdirSync38(join85(out2, "media/catalog"), { recursive: true });
170707
+ mkdirSync39(join86(out2, "evidence/pages"), { recursive: true });
170708
+ mkdirSync39(join86(out2, "media/catalog"), { recursive: true });
170335
170709
  const files = [];
170336
170710
  const put = (rel, body) => {
170337
- const p2 = join85(out2, rel);
170338
- mkdirSync38(join85(p2, ".."), { recursive: true });
170339
- writeFileSync35(p2, body);
170711
+ const p2 = join86(out2, rel);
170712
+ mkdirSync39(join86(p2, ".."), { recursive: true });
170713
+ writeFileSync36(p2, body);
170340
170714
  files.push(rel);
170341
170715
  };
170342
170716
  const book = result.book;
@@ -170378,7 +170752,7 @@ async function writeBusinessBook(result, opts) {
170378
170752
  const allGated = shots.length > 0 && gatedPages.length > 0 && usablePages.length === 0;
170379
170753
  put("README.md", folderReadme(shots.length > 0 && !allGated, book.look.unavailable, allGated));
170380
170754
  put("evidence/probes.json", JSON.stringify(book.probes, null, 2) + "\n");
170381
- rmSync21(join85(out2, "evidence/mobile-app"), { recursive: true, force: true });
170755
+ rmSync21(join86(out2, "evidence/mobile-app"), { recursive: true, force: true });
170382
170756
  for (const [name, value] of Object.entries(result.mobileAppEvidence ?? {})) {
170383
170757
  if (/^[a-z-]+\.json$/.test(name)) put(`evidence/mobile-app/${name}`, JSON.stringify(value, null, 2) + "\n");
170384
170758
  }
@@ -170609,7 +170983,7 @@ function readConnectors(files, dirs) {
170609
170983
  return out2;
170610
170984
  }
170611
170985
  function providersOf(cfg) {
170612
- const on4 = (camel2, snake) => cfg[camel2] === true || cfg[snake] === true;
170986
+ const on4 = (camel2, snake2) => cfg[camel2] === true || cfg[snake2] === true;
170613
170987
  const out2 = [];
170614
170988
  if (on4("enableUsernamePassword", "enable_username_password")) out2.push("email-password");
170615
170989
  if (on4("enableGoogleLogin", "enable_google_login")) out2.push("google");
@@ -171243,11 +171617,11 @@ var init_analyze = __esm({
171243
171617
  });
171244
171618
 
171245
171619
  // src/base44-analyze/write-artifacts.ts
171246
- import { existsSync as existsSync80, mkdirSync as mkdirSync39, readFileSync as readFileSync101, rmSync as rmSync22, writeFileSync as writeFileSync36 } from "node:fs";
171247
- import { dirname as dirname47, join as join86, resolve as resolve60, sep as sep19 } from "node:path";
171620
+ import { existsSync as existsSync81, mkdirSync as mkdirSync40, readFileSync as readFileSync102, rmSync as rmSync22, writeFileSync as writeFileSync37 } from "node:fs";
171621
+ import { dirname as dirname48, join as join87, resolve as resolve60, sep as sep19 } from "node:path";
171248
171622
  function isBookFolder(dir) {
171249
171623
  try {
171250
- return JSON.parse(readFileSync101(join86(dir, "book.json"), "utf8")).kind === "base44-app-book";
171624
+ return JSON.parse(readFileSync102(join87(dir, "book.json"), "utf8")).kind === "base44-app-book";
171251
171625
  } catch {
171252
171626
  return false;
171253
171627
  }
@@ -171329,32 +171703,32 @@ function writeBase44Book(result, outDir, opts = {}) {
171329
171703
  const shot = shots.find((s2) => page.screenshotLocal?.endsWith(s2.filename));
171330
171704
  if (shot) page.screenshotLocal = `screenshots/${shot.filename}`;
171331
171705
  }
171332
- const json7 = `${JSON.stringify(book, null, 2)}
171706
+ const json8 = `${JSON.stringify(book, null, 2)}
171333
171707
  `;
171334
171708
  const md = renderBookMd(book);
171335
- assertNoSecrets(json7);
171709
+ assertNoSecrets(json8);
171336
171710
  assertNoSecrets(md);
171337
- const owned = ["book.json", "BOOK.md", "source", "screenshots"].filter((n2) => existsSync80(join86(out2, n2)));
171711
+ const owned = ["book.json", "BOOK.md", "source", "screenshots"].filter((n2) => existsSync81(join87(out2, n2)));
171338
171712
  if (owned.length) {
171339
171713
  if (!isBookFolder(out2)) throw new Error(`refusing to write the app book into ${out2}: it already has ${owned.join(", ")} that ${owned.length === 1 ? "is" : "are"} not from an app book \u2014 pick an empty --out`);
171340
- for (const d2 of ["source", "screenshots"]) rmSync22(join86(out2, d2), { recursive: true, force: true });
171714
+ for (const d2 of ["source", "screenshots"]) rmSync22(join87(out2, d2), { recursive: true, force: true });
171341
171715
  }
171342
- mkdirSync39(out2, { recursive: true });
171343
- writeFileSync36(join86(out2, "book.json"), json7);
171344
- writeFileSync36(join86(out2, "BOOK.md"), md);
171716
+ mkdirSync40(out2, { recursive: true });
171717
+ writeFileSync37(join87(out2, "book.json"), json8);
171718
+ writeFileSync37(join87(out2, "BOOK.md"), md);
171345
171719
  files.push("book.json", "BOOK.md");
171346
171720
  for (const s2 of shots) {
171347
- mkdirSync39(join86(out2, "screenshots"), { recursive: true });
171348
- writeFileSync36(join86(out2, "screenshots", s2.filename), s2.bytes);
171721
+ mkdirSync40(join87(out2, "screenshots"), { recursive: true });
171722
+ writeFileSync37(join87(out2, "screenshots", s2.filename), s2.bytes);
171349
171723
  files.push(`screenshots/${s2.filename}`);
171350
171724
  }
171351
- const sourceRoot = join86(out2, "source");
171725
+ const sourceRoot = join87(out2, "source");
171352
171726
  for (const f2 of result.sourceFiles) {
171353
171727
  if (!safeRel(f2.path)) continue;
171354
- const dest = join86(sourceRoot, ...f2.path.split("/"));
171728
+ const dest = join87(sourceRoot, ...f2.path.split("/"));
171355
171729
  if (!dest.startsWith(sourceRoot + sep19)) continue;
171356
- mkdirSync39(dirname47(dest), { recursive: true });
171357
- writeFileSync36(dest, f2.bytes);
171730
+ mkdirSync40(dirname48(dest), { recursive: true });
171731
+ writeFileSync37(dest, f2.bytes);
171358
171732
  }
171359
171733
  if (result.sourceFiles.length) files.push(`source/ (${result.sourceFiles.length} files)`);
171360
171734
  return { outDir: out2, files };
@@ -174895,9 +175269,9 @@ __export(cli_exports, {
174895
175269
  systemTar: () => systemTar
174896
175270
  });
174897
175271
  import { execFile } from "node:child_process";
174898
- import { mkdirSync as mkdirSync40, mkdtempSync as mkdtempSync5, rmSync as rmSync23, writeFileSync as writeFileSync37 } from "node:fs";
175272
+ import { mkdirSync as mkdirSync41, mkdtempSync as mkdtempSync5, rmSync as rmSync23, writeFileSync as writeFileSync38 } from "node:fs";
174899
175273
  import { tmpdir as tmpdir6 } from "node:os";
174900
- import { join as join87, resolve as resolve61 } from "node:path";
175274
+ import { join as join88, resolve as resolve61 } from "node:path";
174901
175275
  import { promisify } from "node:util";
174902
175276
  function projectDownloader(apiFetch, apiBase, token, extract = systemTar) {
174903
175277
  return async (appId2) => {
@@ -174910,13 +175284,13 @@ function projectDownloader(apiFetch, apiBase, token, extract = systemTar) {
174910
175284
  }
174911
175285
  const bytes2 = await readCapped(res, MAX_TARBALL_BYTES);
174912
175286
  if (!bytes2) throw new Error(`project download is over the ${MAX_TARBALL_BYTES}-byte cap`);
174913
- const root = mkdtempSync5(join87(tmpdir6(), "himi-base44-"));
175287
+ const root = mkdtempSync5(join88(tmpdir6(), "himi-base44-"));
174914
175288
  const cleanup = () => rmSync23(root, { recursive: true, force: true });
174915
175289
  try {
174916
- const archive = join87(root, "project.tar");
174917
- const dir = join87(root, "project");
174918
- writeFileSync37(archive, bytes2);
174919
- mkdirSync40(dir);
175290
+ const archive = join88(root, "project.tar");
175291
+ const dir = join88(root, "project");
175292
+ writeFileSync38(archive, bytes2);
175293
+ mkdirSync41(dir);
174920
175294
  await extract(archive, dir);
174921
175295
  return { dir, cleanup };
174922
175296
  } catch (e2) {
@@ -175135,7 +175509,7 @@ var init_errors2 = __esm({
175135
175509
  });
175136
175510
 
175137
175511
  // src/one-app/collect.ts
175138
- import { createHash as createHash43 } from "node:crypto";
175512
+ import { createHash as createHash44 } from "node:crypto";
175139
175513
  function isObject2(value) {
175140
175514
  return value !== null && typeof value === "object" && !Array.isArray(value);
175141
175515
  }
@@ -175207,7 +175581,7 @@ function semanticFingerprint(structure) {
175207
175581
  }
175208
175582
  return JSON.stringify(value);
175209
175583
  };
175210
- return createHash43("sha256").update(canonical5(structure, true)).digest("hex");
175584
+ return createHash44("sha256").update(canonical5(structure, true)).digest("hex");
175211
175585
  }
175212
175586
  async function collectOneApp(input, deps = {}) {
175213
175587
  if (typeof input.metaSiteId !== "string" || !UUID.test(input.metaSiteId)) {
@@ -175831,9 +176205,9 @@ __export(cli_exports2, {
175831
176205
  ONE_APP_WORKFLOW: () => ONE_APP_WORKFLOW,
175832
176206
  runOneAppCommand: () => runOneAppCommand
175833
176207
  });
175834
- import { closeSync as closeSync7, constants as constants4, lstatSync as lstatSync21, mkdirSync as mkdirSync41, openSync as openSync6, readFileSync as readFileSync102, realpathSync as realpathSync14, writeFileSync as writeFileSync38 } from "node:fs";
175835
- import { createHash as createHash44 } from "node:crypto";
175836
- import { dirname as dirname48, join as join88, parse as parse3, resolve as resolve62 } from "node:path";
176208
+ import { closeSync as closeSync7, constants as constants4, lstatSync as lstatSync21, mkdirSync as mkdirSync42, openSync as openSync6, readFileSync as readFileSync103, realpathSync as realpathSync14, writeFileSync as writeFileSync39 } from "node:fs";
176209
+ import { createHash as createHash45 } from "node:crypto";
176210
+ import { dirname as dirname49, join as join89, parse as parse3, resolve as resolve62 } from "node:path";
175837
176211
  function readOptions(argv, cwd, commandIncluded) {
175838
176212
  let cursor = 0;
175839
176213
  const skipGlobalSwitches = () => {
@@ -175881,7 +176255,7 @@ function readOptions(argv, cwd, commandIncluded) {
175881
176255
  const file2 = resolve62(cwd, flags["prompt-file"]);
175882
176256
  const stat2 = lstatSync21(file2);
175883
176257
  if (!stat2.isFile() || stat2.size > MAX_PROMPT_BYTES) throw new Error("invalid prompt file");
175884
- prompt = readFileSync102(file2, "utf8");
176258
+ prompt = readFileSync103(file2, "utf8");
175885
176259
  } catch {
175886
176260
  throw new OneAppError("invalid_prompt", "Use a readable text file of at most 64 KiB for --prompt-file.");
175887
176261
  }
@@ -175897,7 +176271,7 @@ function readOptions(argv, cwd, commandIncluded) {
175897
176271
  throw new OneAppError("invalid_output_path", "Pass a parent directory with --dir.");
175898
176272
  }
175899
176273
  const parentDir = resolve62(cwd, flags.dir ?? ".");
175900
- const workspace = join88(parentDir, name);
176274
+ const workspace = join89(parentDir, name);
175901
176275
  assertSafeParents(parentDir);
175902
176276
  if (pathExists(workspace)) throw new OneAppError("output_exists", "The destination already exists. Choose a new --name or --dir.");
175903
176277
  return { metaSiteId, appInstance, prompt, name, parentDir, workspace, target, topTabs };
@@ -175926,13 +176300,13 @@ function assertSafeParents(path2) {
175926
176300
  throw new OneAppError("invalid_output_path", "The output parent cannot be inspected.");
175927
176301
  }
175928
176302
  }
175929
- current = dirname48(current);
176303
+ current = dirname49(current);
175930
176304
  }
175931
176305
  }
175932
176306
  function writePrivate(path2, contents, append) {
175933
176307
  const fd = openSync6(path2, constants4.O_WRONLY | constants4.O_CREAT | constants4.O_NOFOLLOW | (append ? constants4.O_APPEND : constants4.O_EXCL), 384);
175934
176308
  try {
175935
- writeFileSync38(fd, contents, { encoding: "utf8" });
176309
+ writeFileSync39(fd, contents, { encoding: "utf8" });
175936
176310
  } finally {
175937
176311
  closeSync7(fd);
175938
176312
  }
@@ -175954,7 +176328,7 @@ Collect the source mobile app and hand it to the current coding agent. The agent
175954
176328
  const hashes = {};
175955
176329
  const diskWrite = deps.writeFile ?? writePrivate;
175956
176330
  const put = (name, content) => {
175957
- const path2 = join88(evidenceDir, name);
176331
+ const path2 = join89(evidenceDir, name);
175958
176332
  try {
175959
176333
  assertSafeParents(evidenceDir);
175960
176334
  diskWrite(path2, content, false);
@@ -175962,7 +176336,7 @@ Collect the source mobile app and hand it to the current coding agent. The agent
175962
176336
  throw new OneAppError("artifact_write_failed", "Could not save the migration evidence. Existing files were kept.", { stage });
175963
176337
  }
175964
176338
  artifacts[name] = path2;
175965
- hashes[name] = createHash44("sha256").update(content).digest("hex");
176339
+ hashes[name] = createHash45("sha256").update(content).digest("hex");
175966
176340
  return path2;
175967
176341
  };
175968
176342
  const progress = (nextStage) => {
@@ -175970,7 +176344,7 @@ Collect the source mobile app and hand it to the current coding agent. The agent
175970
176344
  if (!evidenceDir) return;
175971
176345
  try {
175972
176346
  assertSafeParents(evidenceDir);
175973
- diskWrite(join88(evidenceDir, "progress.jsonl"), JSON.stringify({ stage, at: now() }) + "\n", true);
176347
+ diskWrite(join89(evidenceDir, "progress.jsonl"), JSON.stringify({ stage, at: now() }) + "\n", true);
175974
176348
  } catch {
175975
176349
  throw new OneAppError("artifact_write_failed", "Could not record migration progress. Existing files were kept.", { stage });
175976
176350
  }
@@ -175982,12 +176356,12 @@ Collect the source mobile app and hand it to the current coding agent. The agent
175982
176356
  try {
175983
176357
  options = readOptions(argv, deps.cwd ?? process.cwd(), commandIncluded);
175984
176358
  try {
175985
- mkdirSync41(options.parentDir, { recursive: true, mode: 448 });
176359
+ mkdirSync42(options.parentDir, { recursive: true, mode: 448 });
175986
176360
  assertSafeParents(options.parentDir);
175987
- mkdirSync41(options.workspace, { mode: 448 });
176361
+ mkdirSync42(options.workspace, { mode: 448 });
175988
176362
  if (realpathSync14(options.workspace) !== options.workspace) throw new Error("destination changed");
175989
- evidenceDir = join88(options.workspace, ".himi", "one-app");
175990
- mkdirSync41(evidenceDir, { recursive: true, mode: 448 });
176363
+ evidenceDir = join89(options.workspace, ".himi", "one-app");
176364
+ mkdirSync42(evidenceDir, { recursive: true, mode: 448 });
175991
176365
  } catch (error) {
175992
176366
  if (error.code === "EEXIST") {
175993
176367
  throw new OneAppError("output_exists", "The destination already exists. Choose a new --name or --dir.");
@@ -177715,7 +178089,7 @@ var init_mobile_app = __esm({
177715
178089
  return s2 || void 0;
177716
178090
  };
177717
178091
  strs2 = (v2) => arr(v2).filter((x2) => typeof x2 === "string");
177718
- pick2 = (o2, camel2, snake) => o2[camel2] ?? o2[snake];
178092
+ pick2 = (o2, camel2, snake2) => o2[camel2] ?? o2[snake2];
177719
178093
  outOfScope = (definitionId) => !!definitionId?.startsWith("ntt.");
177720
178094
  empty = () => ({
177721
178095
  tabs: [],
@@ -178123,8 +178497,8 @@ var init_customer_build = __esm({
178123
178497
  });
178124
178498
 
178125
178499
  // src/site-analyze/apply.ts
178126
- import { existsSync as existsSync81, readFileSync as readFileSync103, writeFileSync as writeFileSync39, copyFileSync as copyFileSync2, mkdirSync as mkdirSync42, realpathSync as realpathSync15, statSync as statSync22 } from "node:fs";
178127
- import { dirname as dirname49, isAbsolute as isAbsolute8, join as join89, resolve as resolve63, sep as sep20 } from "node:path";
178500
+ import { existsSync as existsSync82, readFileSync as readFileSync104, writeFileSync as writeFileSync40, copyFileSync as copyFileSync2, mkdirSync as mkdirSync43, realpathSync as realpathSync15, statSync as statSync22 } from "node:fs";
178501
+ import { dirname as dirname50, isAbsolute as isAbsolute8, join as join90, resolve as resolve63, sep as sep20 } from "node:path";
178128
178502
  import { spawnSync as spawnSync4 } from "node:child_process";
178129
178503
  function unpairedRedirectNote(overlay) {
178130
178504
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -178139,8 +178513,8 @@ function committedShellProvisioning(path2) {
178139
178513
  function findRepoRoot(cwd) {
178140
178514
  let dir = cwd;
178141
178515
  for (let i2 = 0; i2 < 8; i2++) {
178142
- if (SHELL_NAMES2.some((name) => existsSync81(join89(dir, shellAppDir(name), "package.json")))) return dir;
178143
- const next = dirname49(dir);
178516
+ if (SHELL_NAMES2.some((name) => existsSync82(join90(dir, shellAppDir(name), "package.json")))) return dir;
178517
+ const next = dirname50(dir);
178144
178518
  if (next === dir) break;
178145
178519
  dir = next;
178146
178520
  }
@@ -178242,11 +178616,11 @@ function sameKnownSite(previous, next) {
178242
178616
  }
178243
178617
  function buildMergedContract(options) {
178244
178618
  const { appDir: appDir3, packageDir, overlay } = options;
178245
- const committedPath = join89(appDir3, "provisioning.json");
178246
- if (!existsSync81(committedPath)) {
178619
+ const committedPath = join90(appDir3, "provisioning.json");
178620
+ if (!existsSync82(committedPath)) {
178247
178621
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
178248
178622
  }
178249
- const committed = JSON.parse(readFileSync103(committedPath, "utf8"));
178623
+ const committed = JSON.parse(readFileSync104(committedPath, "utf8"));
178250
178624
  const merged = mergeCustomerOwned(committed, overlay);
178251
178625
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
178252
178626
  const branding = merged.branding ?? {};
@@ -178258,8 +178632,8 @@ function buildMergedContract(options) {
178258
178632
  };
178259
178633
  const stage = (fromRel, toRel) => {
178260
178634
  const from = contained(packageDir, fromRel);
178261
- const to2 = contained(join89(appDir3, STAGED), toRel.slice(STAGED.length + 1));
178262
- if (!from || !to2 || !existsSync81(from)) return void 0;
178635
+ const to2 = contained(join90(appDir3, STAGED), toRel.slice(STAGED.length + 1));
178636
+ if (!from || !to2 || !existsSync82(from)) return void 0;
178263
178637
  let real;
178264
178638
  let realRoot;
178265
178639
  try {
@@ -178271,7 +178645,7 @@ function buildMergedContract(options) {
178271
178645
  if (!real.startsWith(realRoot + sep20)) return void 0;
178272
178646
  try {
178273
178647
  if (!statSync22(real).isFile()) return void 0;
178274
- mkdirSync42(dirname49(to2), { recursive: true });
178648
+ mkdirSync43(dirname50(to2), { recursive: true });
178275
178649
  copyFileSync2(real, to2);
178276
178650
  } catch {
178277
178651
  return void 0;
@@ -178281,11 +178655,11 @@ function buildMergedContract(options) {
178281
178655
  };
178282
178656
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
178283
178657
  if (icon) branding.appIconPath = icon;
178284
- const facesPath = join89(packageDir, "media/fonts/faces.json");
178285
- if (existsSync81(facesPath)) {
178658
+ const facesPath = join90(packageDir, "media/fonts/faces.json");
178659
+ if (existsSync82(facesPath)) {
178286
178660
  let faces = [];
178287
178661
  try {
178288
- const parsed = JSON.parse(readFileSync103(facesPath, "utf8"));
178662
+ const parsed = JSON.parse(readFileSync104(facesPath, "utf8"));
178289
178663
  faces = Array.isArray(parsed) ? parsed : [];
178290
178664
  } catch {
178291
178665
  faces = [];
@@ -178306,11 +178680,11 @@ function buildMergedContract(options) {
178306
178680
  merged.branding = branding;
178307
178681
  const body = `${JSON.stringify(merged, null, 2)}
178308
178682
  `;
178309
- const path2 = join89(packageDir, "provisioning.contract.json");
178310
- writeFileSync39(path2, body);
178311
- const localPath2 = join89(appDir3, LOCAL_CONTRACT_REL);
178312
- mkdirSync42(dirname49(localPath2), { recursive: true });
178313
- writeFileSync39(localPath2, body);
178683
+ const path2 = join90(packageDir, "provisioning.contract.json");
178684
+ writeFileSync40(path2, body);
178685
+ const localPath2 = join90(appDir3, LOCAL_CONTRACT_REL);
178686
+ mkdirSync43(dirname50(localPath2), { recursive: true });
178687
+ writeFileSync40(localPath2, body);
178314
178688
  staged.push(localPath2);
178315
178689
  return { path: path2, staged };
178316
178690
  }
@@ -178343,10 +178717,10 @@ function catalogTypography(overlay, template) {
178343
178717
  function staticFaqFor(overlay, overlayPath) {
178344
178718
  const own5 = overlay.faq;
178345
178719
  if (own5 !== void 0) return deriveStaticFaq(own5);
178346
- const bookPath = join89(dirname49(resolve63(overlayPath)), "book.json");
178347
- if (!existsSync81(bookPath)) return void 0;
178720
+ const bookPath = join90(dirname50(resolve63(overlayPath)), "book.json");
178721
+ if (!existsSync82(bookPath)) return void 0;
178348
178722
  try {
178349
- const book = JSON.parse(readFileSync103(bookPath, "utf8"));
178723
+ const book = JSON.parse(readFileSync104(bookPath, "utf8"));
178350
178724
  const copy = book?.copy;
178351
178725
  if (!copy || typeof copy !== "object" || copy.unavailable) return void 0;
178352
178726
  const pairs = deriveStaticFaq(copy.faq);
@@ -178360,10 +178734,10 @@ function staticGalleriesFor(overlay, overlayPath) {
178360
178734
  if (overlay.galleries !== void 0) return deriveStaticGalleries(
178361
178735
  overlay.galleries.map((gallery) => ({ role: "gallery", title: gallery.title, galleryImageUrls: gallery.photos }))
178362
178736
  );
178363
- const bookPath = join89(dirname49(resolve63(overlayPath)), "book.json");
178364
- if (!existsSync81(bookPath)) return void 0;
178737
+ const bookPath = join90(dirname50(resolve63(overlayPath)), "book.json");
178738
+ if (!existsSync82(bookPath)) return void 0;
178365
178739
  try {
178366
- const book = JSON.parse(readFileSync103(bookPath, "utf8"));
178740
+ const book = JSON.parse(readFileSync104(bookPath, "utf8"));
178367
178741
  if (!book.look || book.look.unavailable || !Array.isArray(book.look.pages)) return void 0;
178368
178742
  const galleries = deriveStaticGalleries(book.look.pages);
178369
178743
  return galleries.length || book.look.complete === true ? galleries : void 0;
@@ -178375,8 +178749,8 @@ async function applyOverlays(input) {
178375
178749
  const cwd = resolve63(input.cwd);
178376
178750
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
178377
178751
  assertKnownShell(shell);
178378
- const overlay = JSON.parse(readFileSync103(input.overlayPath, "utf8"));
178379
- const tokens2 = JSON.parse(readFileSync103(input.tokensPath, "utf8"));
178752
+ const overlay = JSON.parse(readFileSync104(input.overlayPath, "utf8"));
178753
+ const tokens2 = JSON.parse(readFileSync104(input.tokensPath, "utf8"));
178380
178754
  const faq = staticFaqFor(overlay, input.overlayPath);
178381
178755
  if (faq !== void 0) overlay.faq = faq;
178382
178756
  const galleries = staticGalleriesFor(overlay, input.overlayPath);
@@ -178393,15 +178767,15 @@ async function applyOverlays(input) {
178393
178767
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
178394
178768
  );
178395
178769
  }
178396
- const appDir3 = join89(repoRoot2, shellAppDir(shell));
178397
- const committedPath = join89(appDir3, "provisioning.json");
178398
- const committedSite = existsSync81(committedPath) ? JSON.parse(readFileSync103(committedPath, "utf8")).site : void 0;
178770
+ const appDir3 = join90(repoRoot2, shellAppDir(shell));
178771
+ const committedPath = join90(appDir3, "provisioning.json");
178772
+ const committedSite = existsSync82(committedPath) ? JSON.parse(readFileSync104(committedPath, "utf8")).site : void 0;
178399
178773
  if (committedSite !== void 0 && overlay.site === void 0) {
178400
178774
  throw new Error(
178401
178775
  `shell "${shell}" is a site shell: its contract names the customer's website (a \`site\` block), and this overlay has none. Re-run \`himi site analyze --url <site> --shell ${shell}\` to derive the site's page map (edit \`site.pages\` in provisioning.overlay.json if needed), then --apply.`
178402
178776
  );
178403
178777
  }
178404
- const packageDir = dirname49(resolve63(input.overlayPath));
178778
+ const packageDir = dirname50(resolve63(input.overlayPath));
178405
178779
  const { path: contract, staged } = buildMergedContract({ appDir: appDir3, packageDir, overlay });
178406
178780
  wrote.push(...staged);
178407
178781
  const r2 = spawnSync4("npm", ["run", "prepare:customer", "--", "--contract", contract], {
@@ -178422,9 +178796,9 @@ async function applyOverlays(input) {
178422
178796
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
178423
178797
  };
178424
178798
  }
178425
- const pkgProv = join89(cwd, "provisioning.json");
178799
+ const pkgProv = join90(cwd, "provisioning.json");
178426
178800
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
178427
- const contentPath = join89(cwd, "himalaya.content.json");
178801
+ const contentPath = join90(cwd, "himalaya.content.json");
178428
178802
  const syncViewNames = (before, after) => {
178429
178803
  const content = readContentManifest(cwd);
178430
178804
  if (!content) return;
@@ -178436,26 +178810,26 @@ async function applyOverlays(input) {
178436
178810
  };
178437
178811
  let packageFaq = faq;
178438
178812
  let packageGalleries = galleries;
178439
- if (existsSync81(pkgProv)) {
178440
- const cur = JSON.parse(readFileSync103(pkgProv, "utf8"));
178813
+ if (existsSync82(pkgProv)) {
178814
+ const cur = JSON.parse(readFileSync104(pkgProv, "utf8"));
178441
178815
  if (galleries === void 0 && !sameKnownSite(cur, overlay)) overlay.galleries = [];
178442
178816
  const merged = deepMerge4(cur, overlay);
178443
178817
  packageFaq = merged.faq;
178444
178818
  packageGalleries = merged.galleries;
178445
178819
  syncViewNames(cur.site, merged.site);
178446
- writeFileSync39(pkgProv, JSON.stringify(merged, null, 2) + "\n");
178820
+ writeFileSync40(pkgProv, JSON.stringify(merged, null, 2) + "\n");
178447
178821
  wrote.push(pkgProv);
178448
178822
  } else {
178449
178823
  syncViewNames(void 0, overlay.site);
178450
- writeFileSync39(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
178824
+ writeFileSync40(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
178451
178825
  wrote.push(pkgProv);
178452
178826
  }
178453
178827
  if (Array.isArray(packageGalleries) && packageGalleries.length && !overlay.installedVerticals.includes("galleries")) {
178454
178828
  overlay.installedVerticals.push("galleries");
178455
178829
  }
178456
- const pkgTokens = join89(cwd, "tokens.json");
178457
- if (existsSync81(pkgTokens) && (tokens2.colors || tokens2.typography)) {
178458
- const cur = JSON.parse(readFileSync103(pkgTokens, "utf8"));
178830
+ const pkgTokens = join90(cwd, "tokens.json");
178831
+ if (existsSync82(pkgTokens) && (tokens2.colors || tokens2.typography)) {
178832
+ const cur = JSON.parse(readFileSync104(pkgTokens, "utf8"));
178459
178833
  let tokensIn = tokens2;
178460
178834
  if (tokens2.typography) {
178461
178835
  const fonts = catalogTypography(tokens2.typography, cur.typography);
@@ -178476,13 +178850,13 @@ async function applyOverlays(input) {
178476
178850
  if (primary && glow && typeof glow.color === "string") glow.color = primary;
178477
178851
  stripBrandFromIdiom(merged);
178478
178852
  }
178479
- writeFileSync39(pkgTokens, JSON.stringify(merged, null, 2) + "\n");
178853
+ writeFileSync40(pkgTokens, JSON.stringify(merged, null, 2) + "\n");
178480
178854
  wrote.push(pkgTokens);
178481
178855
  }
178482
178856
  const homeAction = homeRefreshActionId(shell);
178483
- const homeSrc = join89(cwd, "dev/index.ts");
178484
- if (homeAction && existsSync81(homeSrc)) {
178485
- const prev = readFileSync103(homeSrc, "utf8");
178857
+ const homeSrc = join90(cwd, "dev/index.ts");
178858
+ if (homeAction && existsSync82(homeSrc)) {
178859
+ const prev = readFileSync104(homeSrc, "utf8");
178486
178860
  const patched = patchBrandParams(prev, overlay, homeAction);
178487
178861
  let next = patched.src;
178488
178862
  if (!patched.foreign && faq?.length && prev.includes('"tier5.himiBrandedFaq.load"') && !prev.includes('"staticFaq"')) {
@@ -178510,20 +178884,20 @@ async function applyOverlays(input) {
178510
178884
  }
178511
178885
  }
178512
178886
  if (next !== prev) {
178513
- writeFileSync39(homeSrc, next);
178887
+ writeFileSync40(homeSrc, next);
178514
178888
  wrote.push(homeSrc);
178515
178889
  }
178516
178890
  if (patched.unparsed) {
178517
178891
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
178518
178892
  }
178519
178893
  }
178520
- if (!input.iconPath || !existsSync81(input.iconPath)) {
178521
- const book = input.iconPath ? dirname49(dirname49(resolve63(input.iconPath))) : void 0;
178894
+ if (!input.iconPath || !existsSync82(input.iconPath)) {
178895
+ const book = input.iconPath ? dirname50(dirname50(resolve63(input.iconPath))) : void 0;
178522
178896
  notes.push(
178523
178897
  book ? `no icon installed \u2014 ${siteLogoAbsence(book).hint}` : "no icon installed and no business book to say why \u2014 `himi icon generate` draws one from the accent, or `himi icon set <file.png>` installs the customer's own mark"
178524
178898
  );
178525
178899
  }
178526
- if (input.iconPath && existsSync81(input.iconPath)) {
178900
+ if (input.iconPath && existsSync82(input.iconPath)) {
178527
178901
  try {
178528
178902
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
178529
178903
  wrote.push(...installed.wrote);
@@ -178886,13 +179260,13 @@ function parseMethod(raw) {
178886
179260
  return m2;
178887
179261
  }
178888
179262
  function jsonFunctionSource(method, name, body) {
178889
- const json7 = JSON.stringify(body, null, 2).replace(/\n/g, "\n ");
179263
+ const json8 = JSON.stringify(body, null, 2).replace(/\n/g, "\n ");
178890
179264
  return `import { ok } from 'wix-http-functions';
178891
179265
 
178892
179266
  export function ${methodPrefix(method)}_${name}(request) {
178893
179267
  return ok({
178894
179268
  headers: { 'Content-Type': 'application/json' },
178895
- body: ${json7},
179269
+ body: ${json8},
178896
179270
  });
178897
179271
  }
178898
179272
  `;
@@ -179218,12 +179592,12 @@ async function veloCall(fetchImpl, method, url, opts = {}) {
179218
179592
  signal: AbortSignal.timeout(opts.timeoutMs ?? 3e4)
179219
179593
  });
179220
179594
  const text9 = await res.text();
179221
- let json7 = text9;
179595
+ let json8 = text9;
179222
179596
  try {
179223
- json7 = text9 ? JSON.parse(text9) : null;
179597
+ json8 = text9 ? JSON.parse(text9) : null;
179224
179598
  } catch {
179225
179599
  }
179226
- return { status: res.status, json: json7, text: text9 };
179600
+ return { status: res.status, json: json8, text: text9 };
179227
179601
  }
179228
179602
  function snippet2(x2, cap = 180) {
179229
179603
  const s2 = typeof x2 === "string" ? x2 : JSON.stringify(x2) ?? "";
@@ -179274,15 +179648,15 @@ async function patchHttpFunctionsFile(fetchImpl, token, msid, gridAppId, layout,
179274
179648
  else body.filesToUpdate = [{ path: filePath, content }];
179275
179649
  return veloCall(fetchImpl, "PATCH", veloUpdateFilesPath(gridAppId), { token, msid, body });
179276
179650
  }
179277
- function revisionFrom(json7) {
179278
- if (!json7 || typeof json7 !== "object") return void 0;
179279
- const r2 = json7;
179651
+ function revisionFrom(json8) {
179652
+ if (!json8 || typeof json8 !== "object") return void 0;
179653
+ const r2 = json8;
179280
179654
  const v2 = r2.siteRevision?.revision ?? r2.revision;
179281
179655
  return v2 == null ? void 0 : String(v2);
179282
179656
  }
179283
- function codeAppIdFromRevision(json7) {
179284
- if (!json7 || typeof json7 !== "object") return void 0;
179285
- const actions = json7.actions;
179657
+ function codeAppIdFromRevision(json8) {
179658
+ if (!json8 || typeof json8 !== "object") return void 0;
179659
+ const actions = json8.actions;
179286
179660
  if (!Array.isArray(actions)) return void 0;
179287
179661
  for (const a2 of actions) {
179288
179662
  if (!a2 || typeof a2 !== "object") continue;
@@ -179482,7 +179856,7 @@ __export(cli_exports3, {
179482
179856
  runSiteAnalyze: () => runSiteAnalyze,
179483
179857
  runSiteFunctionCli: () => runSiteFunctionCli
179484
179858
  });
179485
- import { existsSync as existsSync82, readFileSync as readFileSync104 } from "node:fs";
179859
+ import { existsSync as existsSync83, readFileSync as readFileSync105 } from "node:fs";
179486
179860
  import { resolve as resolve64 } from "node:path";
179487
179861
  function applyNeedsClient(shell) {
179488
179862
  if (shell === void 0 || !SHELL_NAMES2.includes(shell)) return true;
@@ -179550,9 +179924,9 @@ async function runSiteAnalyze(opts) {
179550
179924
  if (book?.skipped) result.warnings = [...result.warnings ?? [], book.skipped];
179551
179925
  const derived = deriveSiteShell({ siteUrl: result.book.input.siteUrl, look: result.book.look, kind, routes });
179552
179926
  const prevOverlayPath = resolve64(outDir, "provisioning.overlay.json");
179553
- const prevSite = existsSync82(prevOverlayPath) ? (() => {
179927
+ const prevSite = existsSync83(prevOverlayPath) ? (() => {
179554
179928
  try {
179555
- return JSON.parse(readFileSync104(prevOverlayPath, "utf8")).site;
179929
+ return JSON.parse(readFileSync105(prevOverlayPath, "utf8")).site;
179556
179930
  } catch {
179557
179931
  return void 0;
179558
179932
  }
@@ -179571,7 +179945,7 @@ async function runSiteAnalyze(opts) {
179571
179945
  }
179572
179946
  const previousOverlay = resolve64(outDir, "provisioning.overlay.json");
179573
179947
  carryOverGoals(
179574
- existsSync82(previousOverlay) ? readFileSync104(previousOverlay, "utf8") : void 0,
179948
+ existsSync83(previousOverlay) ? readFileSync105(previousOverlay, "utf8") : void 0,
179575
179949
  result.provisioningOverlay
179576
179950
  );
179577
179951
  const ownerCall = ownerAuth ? ownerTransportFrom(fetchImpl, ownerAuth) : void 0;
@@ -179629,7 +180003,7 @@ async function runSiteFunctionCli(opts) {
179629
180003
  let source;
179630
180004
  if (opts.sourcePath) {
179631
180005
  const p2 = resolve64(opts.cwd ?? process.cwd(), opts.sourcePath);
179632
- source = readFileSync104(p2, "utf8");
180006
+ source = readFileSync105(p2, "utf8");
179633
180007
  }
179634
180008
  let body;
179635
180009
  if (opts.bodyRaw != null) {
@@ -179657,9 +180031,9 @@ async function runSiteFunctionCli(opts) {
179657
180031
  });
179658
180032
  }
179659
180033
  function base44BookRoutes(bookPath, siteUrl) {
179660
- if (!existsSync82(bookPath)) return void 0;
180034
+ if (!existsSync83(bookPath)) return void 0;
179661
180035
  try {
179662
- const book = JSON.parse(readFileSync104(bookPath, "utf8"));
180036
+ const book = JSON.parse(readFileSync105(bookPath, "utf8"));
179663
180037
  if (book.kind !== "base44-app-book" || !Array.isArray(book.routes)) return void 0;
179664
180038
  const host = (u2) => {
179665
180039
  try {
@@ -179914,7 +180288,7 @@ __export(site_create_exports, {
179914
180288
  runSiteList: () => runSiteList,
179915
180289
  writeProvisioningBinding: () => writeProvisioningBinding
179916
180290
  });
179917
- import { existsSync as existsSync83, readFileSync as readFileSync105, writeFileSync as writeFileSync40 } from "node:fs";
180291
+ import { existsSync as existsSync84, readFileSync as readFileSync106, writeFileSync as writeFileSync41 } from "node:fs";
179918
180292
  import { resolve as resolvePath2 } from "node:path";
179919
180293
  function oauthClientName(projectName) {
179920
180294
  return `${projectName} client`;
@@ -180063,7 +180437,7 @@ async function runSiteCreate(opts) {
180063
180437
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
180064
180438
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
180065
180439
  }
180066
- function writeProvisioningBinding(target, binding2, fs2 = { existsSync: existsSync83, readFileSync: readFileSync105, writeFileSync: writeFileSync40 }) {
180440
+ function writeProvisioningBinding(target, binding2, fs2 = { existsSync: existsSync84, readFileSync: readFileSync106, writeFileSync: writeFileSync41 }) {
180067
180441
  const msid = binding2.siteId;
180068
180442
  const path2 = resolvePath2(target);
180069
180443
  const committed = committedShellProvisioning(path2);
@@ -180760,12 +181134,12 @@ var init_context = __esm({
180760
181134
  });
180761
181135
 
180762
181136
  // src/cli.ts
180763
- import { mkdtempSync as mkdtempSync6, readFileSync as readFileSync106, mkdirSync as mkdirSync43, writeFileSync as writeFileSync41, existsSync as existsSync84, rmSync as rmSync24, readdirSync as readdirSync35, statSync as statSync23, lstatSync as lstatSync22, accessSync, constants as constants5, renameSync as renameSync13, cpSync as cpSync3 } from "node:fs";
181137
+ import { mkdtempSync as mkdtempSync6, readFileSync as readFileSync107, mkdirSync as mkdirSync44, writeFileSync as writeFileSync42, existsSync as existsSync85, rmSync as rmSync24, readdirSync as readdirSync36, statSync as statSync23, lstatSync as lstatSync22, accessSync, constants as constants5, renameSync as renameSync13, cpSync as cpSync3 } from "node:fs";
180764
181138
  import { execFile as execFile2, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn7 } from "node:child_process";
180765
181139
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
180766
- import { createHash as createHash45, randomUUID as randomUUID11 } from "node:crypto";
181140
+ import { createHash as createHash46, randomUUID as randomUUID11 } from "node:crypto";
180767
181141
  import { tmpdir as tmpdir7, homedir as homedir14, hostname as hostname2 } from "node:os";
180768
- import { join as join90, resolve as resolve65, relative as relative16, basename as basename21, dirname as dirname50 } from "node:path";
181142
+ import { join as join91, resolve as resolve65, relative as relative16, basename as basename21, dirname as dirname51 } from "node:path";
180769
181143
  import { fileURLToPath as fileURLToPath29 } from "node:url";
180770
181144
 
180771
181145
  // ../serve-cli/src/client.ts
@@ -180819,17 +181193,17 @@ var ServeCliClient = class {
180819
181193
  body: opts.rawBody !== void 0 ? new Uint8Array(opts.rawBody) : opts.body !== void 0 ? JSON.stringify(opts.body) : void 0
180820
181194
  });
180821
181195
  const text9 = await res.text();
180822
- let json7 = null;
181196
+ let json8 = null;
180823
181197
  try {
180824
- json7 = text9 ? JSON.parse(text9) : null;
181198
+ json8 = text9 ? JSON.parse(text9) : null;
180825
181199
  } catch {
180826
- json7 = null;
181200
+ json8 = null;
180827
181201
  }
180828
181202
  const h2 = {};
180829
181203
  res.headers.forEach((v2, k2) => {
180830
181204
  h2[k2] = v2;
180831
181205
  });
180832
- return { status: res.status, ok: res.ok, json: json7, text: text9, headers: h2 };
181206
+ return { status: res.status, ok: res.ok, json: json8, text: text9, headers: h2 };
180833
181207
  }
180834
181208
  // ---- serve (device-facing reads) ----
180835
181209
  health(plane) {
@@ -181065,14 +181439,14 @@ var ServeCliClient = class {
181065
181439
  const refusal = res.json;
181066
181440
  const decodedOverPlainCap = res.status === 413 && refusal?.error === "payload_too_large" && refusal.limit === MAX_PUBLISH_BODY_BYTES && bytes2.byteLength <= MAX_PUBLISH_BODY_BYTES;
181067
181441
  if (gzip && (res.status === 400 && refusal?.error === "bad_json" || decodedOverPlainCap)) {
181068
- const json7 = {
181442
+ const json8 = {
181069
181443
  code: "compressed_publish_unsupported",
181070
181444
  error: "compressed_publish_unsupported",
181071
181445
  hint: "this body is over the plain publish cap, and the plane did not accept it compressed: either the plane predates compressed publish bodies (it needs a serve deploy), or this is a self-serve credential, which keeps the plain 8 MB cap",
181072
181446
  sentBytes: bytes2.byteLength,
181073
181447
  ...typeof refusal?.bytes === "number" ? { planeCountedBytes: refusal.bytes } : {}
181074
181448
  };
181075
- return { ...res, json: json7, text: JSON.stringify(json7) };
181449
+ return { ...res, json: json8, text: JSON.stringify(json8) };
181076
181450
  }
181077
181451
  return res;
181078
181452
  }
@@ -181451,8 +181825,8 @@ var BO_WEWIX_COOKIE = "WixBoAuthentication-wewix";
181451
181825
  function dpxCookieHeader(raw) {
181452
181826
  const trimmed = raw.trim();
181453
181827
  try {
181454
- const json7 = Buffer.from(trimmed, "base64url").toString("utf8");
181455
- const obj4 = JSON.parse(json7);
181828
+ const json8 = Buffer.from(trimmed, "base64url").toString("utf8");
181829
+ const obj4 = JSON.parse(json8);
181456
181830
  if (obj4 && typeof obj4 === "object") {
181457
181831
  const pairs = [
181458
181832
  typeof obj4.wewix === "string" ? `${BO_WEWIX_COOKIE}=${obj4.wewix}` : void 0,
@@ -182851,8 +183225,8 @@ var str10 = (v2) => typeof v2 === "string" ? v2 : void 0;
182851
183225
  var num3 = (v2) => typeof v2 === "string" && v2 !== "" && Number.isFinite(Number(v2)) ? Number(v2) : void 0;
182852
183226
  function embeddedTemplateCatalog() {
182853
183227
  return availableTemplates().map((name) => {
182854
- const path2 = join90(templateSourceDir(name), TEMPLATE_META_FILE);
182855
- const meta = JSON.parse(readFileSync106(path2, "utf8"));
183228
+ const path2 = join91(templateSourceDir(name), TEMPLATE_META_FILE);
183229
+ const meta = JSON.parse(readFileSync107(path2, "utf8"));
182856
183230
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
182857
183231
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
182858
183232
  }
@@ -183572,6 +183946,12 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
183572
183946
  write tier5-src/_shared/base44-entities.ts: a row type and
183573
183947
  a typed entities<Row>() handle per declared entity.
183574
183948
  himi validate flags it base44-types-stale when stale.
183949
+ himi base44 screens [--content <dir>] [--force] [--dry-run] [--json]
183950
+ generate one list + detail screen per declared
183951
+ base44.entities entity (base44-starter packages), write
183952
+ controls gated by each entity's rls. Re-running is a
183953
+ no-op; a hand-edited generated file is skipped-edited
183954
+ unless --force. Never calls Base44.
183575
183955
  himi base44 login [--from-base44-cli] | whoami | logout
183576
183956
  the Base44 session, in ~/.himi/base44/ (never the repo).
183577
183957
  BASE44_ACCESS_TOKEN wins, for CI.
@@ -183719,7 +184099,7 @@ var COMMANDS2 = [
183719
184099
  { name: "notify", summary: "Hosted --all-members requires --yes for a live send; --dry-run checks ownership/readiness and counts approved recipients without sending. `setup` optionally configures Branded/Ping from local customer key files; `status --refresh` reads live backend readiness. Setup may be deferred while building. These use serve-control and require owner plus Wix account authentication. `send`, `tokens`, `doctor`: send a REAL push notification to registered devices (RFC 0036 WS5) \u2014 APNs .p8 over HTTP/2 for iOS, FCM v1 for Android. `send` targets one device (--token / --device <registry id>) or fans out to the whole app (--all), reporting a per-device result; a token the provider says is uninstalled (APNs 410 Unregistered / FCM UNREGISTERED) is auto-disabled, while a merely wrong one (BadDeviceToken \u2014 usually a sandbox/production mismatch) is recorded and left enabled. `tokens` lists the registry. `doctor` reports which credentials resolve and which sender each platform picks. Talks to the admin-server via --admin-url/HIMALAYA_ADMIN_URL, authenticated with --admin-session/HIMALAYA_ADMIN_SESSION. `send --provider ping` is the hosted alternative: the serve plane sends to one site member (--member-email) or all approved members (--all-members) through Ping as Himalaya, with owner login instead of provider keys, and reports acceptance, never delivery. NOT `himi push`, which publishes a release.", required: "setup|status [--content <dir>] [--target <t>] [--refresh] [--yes] [--no-push-setup] | send --provider ping [--content <dir>] [--app <a>] [--site <msid>] (--member-email <email> | --all-members (--dry-run | --yes)) --title <s> [--body <s>] --link <url> [--request-id <uuid>] [--dry-run] [--json] | send|tokens|doctor [--app <a>] [--token <t> | --device <id> | --all] [--title <s>] [--body <s>] [--link <url>] [--platform ios|android] [--badge <n>] [--channel <id>] [--silent] [--dry-run] [--json]" },
183720
184100
  { name: "signing", summary: "READ-ONLY App Store Connect inventory for customer BYO TestFlight readiness. `inspect` lists whether the bundle id, distribution certificate, IOS_APP_STORE profile, and ASC app record exist for --bundle-id / --team-id using a local ASC .p8 (flags or ASC_KEY_* env). Never uploads or mutates Apple resources. Optional --cert-file/--profile-file classify on-disk material.", required: "inspect --bundle-id <id> --team-id <id> --asc-key-file <path> --asc-key-id <id> --asc-issuer-id <id> [--cert-file <p12> --cert-password <pw> --profile-file <mobileprovision>] [--json]" },
183721
184101
  { name: "site", summary: "PROVISION a Wix project, then analyze or extend it. `create` is the one door that makes a site instead of adopting one: POST /funnel/projects/v1/create (a WIX site, a HEADLESS backend, a BRANDED_APP, or a VIBE site), and for headless it also mints the OAuth app whose id IS the `WIX_CLIENT_ID` \u2014 with --redirect-uri already registered, because an unregistered redirect makes native signInDirect fail at redirect-session. `--verticals` REQUIRES `--template` and reports only what Wix confirmed: measured, installing onto a project created from the BLANK template returns 200 while the vertical's API answers 428 forever, because there is no site for it to attach to (the install asks for CREATE_SITE). A real template ships its verticals installed and populated, and installing further ones onto it then works. It MUTATES your Wix account and needs --yes; without it you get the payload and nothing is sent. `list` is the read-only site inventory (with each site's premium flag, which is what explains an installed-but-428 vertical). Auth is the account-scoped `wix token` (no --site), falling back to WIX_API_KEY + WIX_ACCOUNT_ID. `client` is the same OAuth step for a site that ALREADY exists \u2014 the one himi adopts rather than makes \u2014 so a clientId no longer means a manual trip to the dashboard: `client ensure` reconciles rather than mints, because a site can already carry a client with NO registered redirect and shipping that one fails signInDirect at runtime; it reuses a client that carries --redirect-uri, PATCHes one that matches --name but lacks it (unioning, so another app's redirect survives), and creates only when there is nothing to reuse (--new forces a fresh one). --name IS the idempotency key. `client` auth is SITE-scoped (`wix token --site <msid>`) because minting is owner-of-that-site, and --yes gates every mutation: a site that already has a usable client resolves WITHOUT it, but --write-provisioning needs --yes even then because it rewrites a manifest on disk (the dry run's planned.provisioning names the file). That query endpoint IGNORES paging (measured: query.paging, cursorPaging and a top-level paging all return the full set at offset 0), so when pagingMetadata reports more clients than came back, `ensure` REFUSES EVERY outcome \u2014 create, patch AND reuse. Reuse is refused too because pickClient breaks ties on the OLDEST createdDate, so an omitted older same-named MOBILE client with the same redirect would win a later complete read and the resolved clientId would MOVE between runs \u2014 and it gets written into provisioning.json; convergence is what makes a stray duplicate untidy rather than harmful. --name cannot narrow the query (no filter is sent; the name is applied to the rows after they arrive), so the recovery is the dashboard; --new skips the query entirely. A `patched` result is verified against what Wix STORED \u2014 the redirect list is read off the PATCH response, and re-read via GET when the response omits it, because falling back to the list himi just sent would make the guard pass vacuously on exactly the silent no-op it exists to catch. `client list` is the read-only inventory of a site's clients: act on its `shippable` column rather than `hasRedirect`, because shippable ALSO requires a MOBILE applicationType and `ensure` refuses a WEB client \u2014 a WEB one carrying your redirect looks right and is not a candidate \u2014 and it sets truncated:true rather than presenting a partial inventory as the whole one. `analyze --read-only` returns API-only generation context as JSON (identity, access probes, CMS permissions and selected redacted Velo source); it needs no browser and never uploads or applies anything. A URL is optional there: `--url` lets it read the published page's Velo code-app ids (credential-free, pinned transport) so the backend source is found on sites where the owner code-app probe 404s. `analyze` reads a site into a business-book folder (REST + Playwright, GET-only); `--url` alone is the visitor-only pass over the published pages, and `--client-id` adds the vertical probes and the app binding `--apply` requires; `site function` list/get/put/publish/invokes that site's Velo HTTP functions and is not `himi functions` (serve-plane jobs). `--apply` bakes into a brandable shell (a site shell needs no `--client-id`: it is visitor-only and binds no client); `--shell` takes the same names `himi dist --shell` does and defaults to branded-lite, and a shell that ships no prepare:customer is refused by name rather than half-built. `--shell brandy` (the site shell, RFC 0062) also derives the site's page map from its published pages and header menu \u2014 Home first, up to four tabs for the pages a visitor returns to, the rest in the drawer \u2014 into the overlay's `site` block, so it can be edited before `--apply`; only a site shell ever receives that block. It never overwrites any shell's committed apps/<shell>/provisioning.json. `member-app set|clear|show <msid>` is the site's member app in Spaces (#4091): the himi app the site's members open in Spaces Lite instead of Branded Lite. `set --app <id>` takes the site's own app or one your account owns (an owned app not yet tied to the site is linked first; another account's app is refused, 403); `clear` returns the site to Branded Lite; `show` prints the pick plus the COMPUTED verdict \u2014 the picked release's requirements judged against the Spaces host manifest (checkHostCompatibility, `--platform` to judge one phone platform) \u2014 and `reason` when it falls back to Branded Lite. Auth is your `himi login` control token plus the account `wix token`, which the plane uses to verify you own the site.", required: "create <name> [--type wix|headless|branded-app|vibe] [--template <guid>] [--verticals stores,bookings,\u2026] [--folder <guid>] [--redirect-uri <uri>] [--no-oauth-app] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | list | client ensure|list --site <msid> [--name <name>] [--redirect-uri <uri>] [--new] [--write-provisioning [path]] [--site-url <url>] [--json] --yes | analyze --read-only [--site <msid>] [--client-id <id>] [--url <https>] [--json] | analyze --url <https> [--client-id <id>] [--site <msid>] [--out <dir>] [--redirect-uri <uri>] [--apply] [--shell " + SHELL_ENUM + "] | function list|get|put|publish|invoke [--site <msid>] [--url <https>] [--method GET|POST|PUT|DELETE] [--source <file>] [--body <json>] [--publish] [--invoke] | member-app set|clear|show <msid> [--app <id>] [--platform ios|android]" },
183722
- { name: "base44", summary: "read a BASE44 app into an app book (book.json + BOOK.md) for the agent building a Himi app from it \u2014 the Base44 counterpart of `site analyze`, READ-ONLY. `analyze --url <published app>` is the VISITOR pass: the app id, name, favicon and colours from the published SPA, its router paths, and screenshots + look tokens from the Playwright crawl. `analyze --app-id <id>` adds the OWNER pass and needs a session: the project source under <out>/source/ (the read half of `base44 eject` \u2014 GET api/apps/<id>/eject \u2014 through the same deny-list and secret scan `himi push` uses, widened by the book's token shapes; `eject` itself is never run because it CREATES a copy of the app), every entity's JSON schema, required fields and RLS rules with up to --sample redacted rows (default 5, 0 = off), backend functions, agents, connectors (names and scopes only, never tokens), secret NAMES (never values), auth visibility + providers, and the route \u2192 component \u2192 entities map from the source router. Every chapter it could not read is listed in `gaps` rather than failing the run. `login` runs Base44's device sign-in and stores the session in ~/.himi/base44/session.json (never the repo; --from-base44-cli imports `npx base44 login`'s session instead); BASE44_ACCESS_TOKEN in the environment wins, for CI. `whoami` prints who is signed in (never the token); `logout` removes the stored session. `auth-check --url <tenant> --ios-app-id <PREFIX.bundle.id>` and/or `--android-package <package> --android-sha256 <fingerprint>` checks public native-login associations and prints missing entries; it never reads an owner token. --json prints the book itself. himi docs base44-app-context. `adopt` turns the book into a reviewable base44-entities.ts of ADOPTED entity + function declarations for config.ts (never edits config.ts, never overwrites); `entities query` is READ-ONLY and reports, per declared entity, ownership, row count and schema drift against the live app, plus foreign entities. `types` writes tier5-src/_shared/base44-entities.ts \u2014 a row type and a typed `entities<Row>()` handle per declared entity, byte-stable \u2014 and `himi validate` reports base44-types-stale when that file falls behind the block (nothing when it is absent). himi docs base44-entities", required: "analyze (--url <https://\u2026> | --app-id <id>) [--out .himi/base44-book] [--sample <n>] [--json] | adopt [--book .himi/base44-book] [--entities A,B] [--write [path]] [--json] | entities query [--content <dir>] [--all] [--json] | types [--content <dir>] [--json] | login [--from-base44-cli] | whoami | logout | auth-check --url <https://tenant> [--ios-app-id <PREFIX.bundle.id>] [--android-package <package> --android-sha256 <fingerprint>]" },
184102
+ { name: "base44", summary: "read a BASE44 app into an app book (book.json + BOOK.md) for the agent building a Himi app from it \u2014 the Base44 counterpart of `site analyze`, READ-ONLY. `analyze --url <published app>` is the VISITOR pass: the app id, name, favicon and colours from the published SPA, its router paths, and screenshots + look tokens from the Playwright crawl. `analyze --app-id <id>` adds the OWNER pass and needs a session: the project source under <out>/source/ (the read half of `base44 eject` \u2014 GET api/apps/<id>/eject \u2014 through the same deny-list and secret scan `himi push` uses, widened by the book's token shapes; `eject` itself is never run because it CREATES a copy of the app), every entity's JSON schema, required fields and RLS rules with up to --sample redacted rows (default 5, 0 = off), backend functions, agents, connectors (names and scopes only, never tokens), secret NAMES (never values), auth visibility + providers, and the route \u2192 component \u2192 entities map from the source router. Every chapter it could not read is listed in `gaps` rather than failing the run. `login` runs Base44's device sign-in and stores the session in ~/.himi/base44/session.json (never the repo; --from-base44-cli imports `npx base44 login`'s session instead); BASE44_ACCESS_TOKEN in the environment wins, for CI. `whoami` prints who is signed in (never the token); `logout` removes the stored session. `auth-check --url <tenant> --ios-app-id <PREFIX.bundle.id>` and/or `--android-package <package> --android-sha256 <fingerprint>` checks public native-login associations and prints missing entries; it never reads an owner token. --json prints the book itself. himi docs base44-app-context. `adopt` turns the book into a reviewable base44-entities.ts of ADOPTED entity + function declarations for config.ts (never edits config.ts, never overwrites); `entities query` is READ-ONLY and reports, per declared entity, ownership, row count and schema drift against the live app, plus foreign entities. `types` writes tier5-src/_shared/base44-entities.ts \u2014 a row type and a typed `entities<Row>()` handle per declared entity, byte-stable \u2014 and `himi validate` reports base44-types-stale when that file falls behind the block (nothing when it is absent). himi docs base44-entities. `screens` (base44-starter packages) writes one generated list + detail screen per declared entity over the template's shared engine, with write controls gated by the entity's rls; an unchanged config reports every file `unchanged`, a hand-edited generated file is `skipped-edited` unless --force; never calls Base44. himi docs base44-app-to-himi-app", required: "analyze (--url <https://\u2026> | --app-id <id>) [--out .himi/base44-book] [--sample <n>] [--json] | adopt [--book .himi/base44-book] [--entities A,B] [--write [path]] [--json] | entities query [--content <dir>] [--all] [--json] | types [--content <dir>] [--json] | screens [--content <dir>] [--force] [--dry-run] [--json] | login [--from-base44-cli] | whoami | logout | auth-check --url <https://tenant> [--ios-app-id <PREFIX.bundle.id>] [--android-package <package> --android-sha256 <fingerprint>]" },
183723
184103
  { name: "doctor", summary: 'off-VPN preflight in one shot: probe the PUBLIC data plane (www.wixapis.com \u2014 reachable off-VPN), the INTERNAL serve/control plane for the selected target (VPN-gated), and report auth state (dpx session on disk + stored control token). --dpx additionally runs a live, EADDRINUSE-safe `dpx whoami`. Machine-readable with --json (an unreachable internal plane carries a stable code:"needs_vpn" so an agent branches on the code, not the prose); human-readable otherwise. Never loads the authoring runtime \u2014 works off-repo.', required: "[--target <t>] [--dpx] [--json] [--data-url <url>] [--timeout <ms>]" },
183724
184104
  { name: "upgrade", summary: "install the newest published CLI (`npm i -g @wix/himalaya-cli@latest`) and report what moved. This is the explicit door: himi also updates itself when the running build is more than a week old \u2014 unattended it just does it and re-runs your command, at a terminal it asks first \u2014 and `HIMI_NO_UPDATE_CHECK=1` turns that off, leaving this command as the way to catch up. Reports `unchanged` rather than claiming success when npm exits 0 without moving the version, which is what a registry mirror serving its own pinned copy looks like.", required: "[--json]" },
183725
184105
  { name: "push", summary: "lint + build + publish a release (default --draft: servable via ?release= but NOT live). REFUSES to publish on any error unless --force; --strict promotes warnings. Prints preview + agent-context links. A successful draft push also rebuilds the making-of (.himi/making-of.html, local and offline) and names it as `reel` \u2014 the file to hand the owner beside the preview link. Its postcard carries the home shot only when that shot is newer than the last source edit; a repeat draft's postcard adds what changed since the last one (\"Since the last draft: the home screen changed.\", from the release manifests' screen hashes, remembered in .himi/last-draft.json); otherwise the postcard says why and names `himi render <home> --png`, and the envelope's `postcardPicture: {skipped, render}` leads next[] with that render (the push never renders it itself). The published slug is NAMESPACED per owner \u2014 `<ownerPrefix>.<name>`, composed offline from the control token's ownerPrefix claim, so the CLI and the server derive the identical string and himalaya.content.json is NOT rewritten (a legacy token with no ownerPrefix publishes the declared name unchanged). An app claimed BEFORE namespacing is stored under its FLAT slug, so the claim route answers `alreadyYours` naming that slug and the push adopts it \u2014 reported as `adoptedSlug`, since it is the one case where the published `app` differs from what the declared name composes to. `--no-claim` skips the route that knows this, so a pre-namespacing app pushed with it still fails 403. --visibility public|unlisted decides whether /v1/apps + the Himi Player picker LIST the app (also declarable as `visibility` in himalaya.content.json; omitted, a brand-new app defaults to unlisted \u2014 share by link \u2014 and an existing setting is kept). --account-id names which account owns the app when your Wix session resolves to several (the auto-claim of a brand-new slug cannot guess \u2014 without it a multi-account caller is told to name one). An unforced push of a release that carries SCREENS also refuses when `.himi/ux-audit.json` holds no mobile-UX drive recorded against that release id, or holds one that FAILED (#2955) \u2014 a published draft IS the owner-private preview a customer is handed; `himi ux record` clears it and `--force` overrides it, which the success envelope then says out loud. `--only <selector>` publishes ONLY the named items as a release derived from the app's live release (or `--from-release <id>`), running the identical build + lint gate and simply publishing less; it requires an explicit `--draft` or `--live` because a partial publish has no safe default, never claims a slug, and carries neither attestation nor a source archive (both named in the output). Selectors are comma-separated in ONE `--only`: `changed` (every item whose bytes differ from the base), `screen:<id|glob>`, `bundle:<name>`, `tokens`, `app-config` \u2014 a selected screen drags its logic bundle and a selected bundle drags the screens that reference it, or the bytes land under a key nothing reads. The plane REFUSES (409 binary_capability_gap) a release declaring a PERMISSION-BEARING capability (iOS usage string or entitlement, Android permission \u2014 camera, location, push, \u2026) that the app's last successful `himi dist` was not built with: an OTA push cannot add an OS permission, and on iOS asking for one whose purpose string is missing kills the app. Run `himi dist` first, or pass `--allow-binary-gap` when every installed binary already carries it \u2014 recorded on the release as `allowedBinaryGap`. A gap in a permission-free capability (share, clipboard, \u2026) stays the `needsBinary` advice.", required: "[--content <dir>] [--draft] [--live] [--force] [--strict] [--label] [--notes] [--visibility public|unlisted] [--allow-binary-gap] [--no-claim] [--account-id <id>] [--only <selector>] [--from-release <id>] [--accept-regressions] [--accept-partial] [--app <slug>] (--control-token)" },
@@ -183761,7 +184141,7 @@ var COMMANDS2 = [
183761
184141
  ];
183762
184142
  function cliVersion() {
183763
184143
  try {
183764
- return JSON.parse(readFileSync106(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
184144
+ return JSON.parse(readFileSync107(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
183765
184145
  } catch {
183766
184146
  return "unknown";
183767
184147
  }
@@ -183852,10 +184232,10 @@ async function fetchPackumentFrom(registry2, fetchImpl, timeoutMs) {
183852
184232
  signal: controller.signal
183853
184233
  });
183854
184234
  if (!response.ok) return void 0;
183855
- const json7 = await response.json();
183856
- const latest = typeof json7?.["dist-tags"]?.latest === "string" ? json7["dist-tags"].latest : void 0;
184235
+ const json8 = await response.json();
184236
+ const latest = typeof json8?.["dist-tags"]?.latest === "string" ? json8["dist-tags"].latest : void 0;
183857
184237
  const time = {};
183858
- for (const [version4, when] of Object.entries(json7?.time ?? {})) if (typeof when === "string") time[version4] = when;
184238
+ for (const [version4, when] of Object.entries(json8?.time ?? {})) if (typeof when === "string") time[version4] = when;
183859
184239
  return latest ? { latest, time, registry: normalizeRegistry(registry2) ?? registry2 } : void 0;
183860
184240
  } catch {
183861
184241
  return void 0;
@@ -183886,7 +184266,7 @@ function ageInDays(publishedAt, now = Date.now()) {
183886
184266
  }
183887
184267
  function packageVersion(path2) {
183888
184268
  try {
183889
- const version4 = JSON.parse(readFileSync106(path2, "utf8")).version;
184269
+ const version4 = JSON.parse(readFileSync107(path2, "utf8")).version;
183890
184270
  return typeof version4 === "string" && version4 ? version4 : void 0;
183891
184271
  } catch {
183892
184272
  return void 0;
@@ -183895,9 +184275,9 @@ function packageVersion(path2) {
183895
184275
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
183896
184276
  let dir = resolve65(fromDir);
183897
184277
  for (; ; ) {
183898
- const version4 = packageVersion(join90(dir, "node_modules", "@wix", "himalaya", "package.json"));
184278
+ const version4 = packageVersion(join91(dir, "node_modules", "@wix", "himalaya", "package.json"));
183899
184279
  if (version4) return { version: version4, source: "local" };
183900
- const parent = dirname50(dir);
184280
+ const parent = dirname51(dir);
183901
184281
  if (parent === dir) break;
183902
184282
  dir = parent;
183903
184283
  }
@@ -183905,7 +184285,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
183905
184285
  if (bundled) return { version: bundled, source: "bundled" };
183906
184286
  try {
183907
184287
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
183908
- const version4 = root ? packageVersion(join90(root, "@wix", "himalaya", "package.json")) : void 0;
184288
+ const version4 = root ? packageVersion(join91(root, "@wix", "himalaya", "package.json")) : void 0;
183909
184289
  return version4 ? { version: version4, source: "global" } : {};
183910
184290
  } catch {
183911
184291
  return {};
@@ -183952,11 +184332,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
183952
184332
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
183953
184333
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
183954
184334
  function updateCachePath(env = process.env) {
183955
- return env.HIMI_UPDATE_CACHE_FILE ?? join90(homedir14(), ".himalaya", "update-check.json");
184335
+ return env.HIMI_UPDATE_CACHE_FILE ?? join91(homedir14(), ".himalaya", "update-check.json");
183956
184336
  }
183957
184337
  function readUpdateCache(path2) {
183958
184338
  try {
183959
- const parsed = JSON.parse(readFileSync106(path2, "utf8"));
184339
+ const parsed = JSON.parse(readFileSync107(path2, "utf8"));
183960
184340
  return parsed && typeof parsed === "object" ? parsed : {};
183961
184341
  } catch {
183962
184342
  return {};
@@ -183964,8 +184344,8 @@ function readUpdateCache(path2) {
183964
184344
  }
183965
184345
  function writeUpdateCache(path2, cache) {
183966
184346
  try {
183967
- mkdirSync43(dirname50(path2), { recursive: true, mode: 448 });
183968
- writeFileSync41(path2, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
184347
+ mkdirSync44(dirname51(path2), { recursive: true, mode: 448 });
184348
+ writeFileSync42(path2, JSON.stringify(cache, null, 2) + "\n", { mode: 384 });
183969
184349
  } catch {
183970
184350
  }
183971
184351
  }
@@ -184025,12 +184405,12 @@ function createUpdateRunner(spawn8 = spawnSync5) {
184025
184405
  },
184026
184406
  installedVersion: () => {
184027
184407
  const root = globalNodeModulesRoot();
184028
- return root ? packageVersion(join90(root, "@wix", "himalaya-cli", "package.json")) : void 0;
184408
+ return root ? packageVersion(join91(root, "@wix", "himalaya-cli", "package.json")) : void 0;
184029
184409
  },
184030
184410
  reexec: (argv) => {
184031
184411
  const root = globalNodeModulesRoot();
184032
- const entry = root ? join90(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
184033
- if (!entry || !existsSync84(entry)) return void 0;
184412
+ const entry = root ? join91(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
184413
+ if (!entry || !existsSync85(entry)) return void 0;
184034
184414
  const result = spawnSync5(process.execPath, [entry, ...argv], {
184035
184415
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
184036
184416
  stdio: "inherit",
@@ -184077,7 +184457,7 @@ function staleUpdateLockFile(cachePath, now) {
184077
184457
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
184078
184458
  let content;
184079
184459
  try {
184080
- content = readFileSync106(path2, "utf8");
184460
+ content = readFileSync107(path2, "utf8");
184081
184461
  } catch {
184082
184462
  continue;
184083
184463
  }
@@ -184100,14 +184480,14 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
184100
184480
  const mine = lockStamp(now);
184101
184481
  const readLock = (path2) => {
184102
184482
  try {
184103
- return readFileSync106(path2, "utf8");
184483
+ return readFileSync107(path2, "utf8");
184104
184484
  } catch {
184105
184485
  return void 0;
184106
184486
  }
184107
184487
  };
184108
184488
  const tryCreate = (path2) => {
184109
184489
  try {
184110
- writeFileSync41(path2, mine, { flag: "wx", mode: 384 });
184490
+ writeFileSync42(path2, mine, { flag: "wx", mode: 384 });
184111
184491
  return true;
184112
184492
  } catch {
184113
184493
  return false;
@@ -184121,7 +184501,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
184121
184501
  };
184122
184502
  const release2 = () => removeIfMine(lockPath);
184123
184503
  try {
184124
- mkdirSync43(dirname50(lockPath), { recursive: true, mode: 448 });
184504
+ mkdirSync44(dirname51(lockPath), { recursive: true, mode: 448 });
184125
184505
  } catch {
184126
184506
  return () => {
184127
184507
  };
@@ -184692,7 +185072,7 @@ function executableOnPath(name, env = process.env) {
184692
185072
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
184693
185073
  for (const dir of entries) {
184694
185074
  for (const suffix of suffixes) {
184695
- const candidate = join90(dir, `${name}${suffix}`);
185075
+ const candidate = join91(dir, `${name}${suffix}`);
184696
185076
  try {
184697
185077
  if (!statSync23(candidate).isFile()) continue;
184698
185078
  if (process.platform !== "win32") accessSync(candidate, constants5.X_OK);
@@ -184801,7 +185181,7 @@ async function nativeConfigAdvisories(app, configSource) {
184801
185181
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
184802
185182
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
184803
185183
  const iosYmlPath = iosProjectYml2(app);
184804
- const iosYml = existsSync84(iosYmlPath) ? readFileSync106(iosYmlPath, "utf8") : "";
185184
+ const iosYml = existsSync85(iosYmlPath) ? readFileSync107(iosYmlPath, "utf8") : "";
184805
185185
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
184806
185186
  if (!optedIn) {
184807
185187
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -184815,14 +185195,14 @@ async function nativeConfigAdvisories(app, configSource) {
184815
185195
  }
184816
185196
  function readConfigSource(dir) {
184817
185197
  try {
184818
- return readFileSync106(join90(dir, "config.ts"), "utf8");
185198
+ return readFileSync107(join91(dir, "config.ts"), "utf8");
184819
185199
  } catch {
184820
185200
  return null;
184821
185201
  }
184822
185202
  }
184823
185203
  function writePngFile(path2, base64) {
184824
- mkdirSync43(dirname50(path2), { recursive: true });
184825
- writeFileSync41(path2, Buffer.from(base64, "base64"));
185204
+ mkdirSync44(dirname51(path2), { recursive: true });
185205
+ writeFileSync42(path2, Buffer.from(base64, "base64"));
184826
185206
  return path2;
184827
185207
  }
184828
185208
  async function seedAgentFilesFor(dir, flags, mode = "init") {
@@ -184869,7 +185249,7 @@ var NoBase44SlotError = class extends Error {
184869
185249
  };
184870
185250
  async function initFromRemoteTemplate(name, parentDir, template, c2, opts = {}) {
184871
185251
  const dir = resolve65(parentDir, name);
184872
- if (existsSync84(join90(dir, "himalaya.content.json"))) {
185252
+ if (existsSync85(join91(dir, "himalaya.content.json"))) {
184873
185253
  throw new Error(`content package already exists at ${dir}`);
184874
185254
  }
184875
185255
  const r2 = await c2.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -184915,11 +185295,11 @@ function symlinkDestinationIssue(dest) {
184915
185295
  function restoreDestinationIssue(dest, force) {
184916
185296
  const link = symlinkDestinationIssue(dest);
184917
185297
  if (link) return link;
184918
- if (!existsSync84(dest)) return null;
185298
+ if (!existsSync85(dest)) return null;
184919
185299
  if (!statSync23(dest).isDirectory()) {
184920
185300
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
184921
185301
  }
184922
- if (readdirSync35(dest).length > 0 && !force) {
185302
+ if (readdirSync36(dest).length > 0 && !force) {
184923
185303
  return {
184924
185304
  error: `${dest} already exists and is not empty`,
184925
185305
  hint: "pass --force to write into it anyway, or --out/--name to land somewhere else"
@@ -184930,8 +185310,8 @@ function restoreDestinationIssue(dest, force) {
184930
185310
  function isHimalayaMonorepoCheckout(start) {
184931
185311
  let dir = resolve65(start);
184932
185312
  for (; ; ) {
184933
- if (existsSync84(join90(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync84(join90(dir, "core", "serve", "src", "server.ts"))) return true;
184934
- const parent = dirname50(dir);
185313
+ if (existsSync85(join91(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync85(join91(dir, "core", "serve", "src", "server.ts"))) return true;
185314
+ const parent = dirname51(dir);
184935
185315
  if (parent === dir) return false;
184936
185316
  dir = parent;
184937
185317
  }
@@ -184982,7 +185362,7 @@ function templateNameError(name, from) {
184982
185362
  const composed2 = TEMPLATE_APP_PREFIX + name;
184983
185363
  return isValidAppId(composed2) ? null : `template name "${name}" (${from}) makes app id "${composed2}", which is over the serve plane's app-id length limit; pass a shorter --name`;
184984
185364
  }
184985
- function materializeForPreview(appDir3, slug, displayName2) {
185365
+ function materializeForPreview(appDir3, slug, displayName3) {
184986
185366
  if (!isTemplateSourceTree(appDir3)) return appDir3;
184987
185367
  const slugError = templateNameError(slug, "preview slug");
184988
185368
  if (slugError) throw Object.assign(new Error(slugError), { exitCode: 2 });
@@ -184991,7 +185371,7 @@ function materializeForPreview(appDir3, slug, displayName2) {
184991
185371
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
184992
185372
  );
184993
185373
  const repoRoot2 = resolve65(fileURLToPath29(new URL("../../..", import.meta.url)));
184994
- const root = join90(repoRoot2, "node_modules", ".cache", "himi-template-preview");
185374
+ const root = join91(repoRoot2, "node_modules", ".cache", "himi-template-preview");
184995
185375
  const stillRunning = (pid) => {
184996
185376
  try {
184997
185377
  process.kill(pid, 0);
@@ -185001,20 +185381,20 @@ function materializeForPreview(appDir3, slug, displayName2) {
185001
185381
  }
185002
185382
  };
185003
185383
  try {
185004
- for (const entry of readdirSync35(root, { withFileTypes: true })) {
185384
+ for (const entry of readdirSync36(root, { withFileTypes: true })) {
185005
185385
  if (!entry.isDirectory()) continue;
185006
185386
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
185007
185387
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
185008
- rmSync24(join90(root, entry.name), { recursive: true, force: true });
185388
+ rmSync24(join91(root, entry.name), { recursive: true, force: true });
185009
185389
  }
185010
185390
  }
185011
185391
  } catch {
185012
185392
  }
185013
- const dest = join90(root, `${slug}-${process.pid}`);
185393
+ const dest = join91(root, `${slug}-${process.pid}`);
185014
185394
  rmSync24(dest, { recursive: true, force: true });
185015
- mkdirSync43(dest, { recursive: true });
185395
+ mkdirSync44(dest, { recursive: true });
185016
185396
  try {
185017
- materializeSource(forBuild, displayName2, dest, slug);
185397
+ materializeSource(forBuild, displayName3, dest, slug);
185018
185398
  } catch (e2) {
185019
185399
  rmSync24(dest, { recursive: true, force: true });
185020
185400
  throw e2;
@@ -185023,8 +185403,8 @@ function materializeForPreview(appDir3, slug, displayName2) {
185023
185403
  }
185024
185404
  async function appId(dir) {
185025
185405
  try {
185026
- const { readFileSync: readFileSync107 } = await import("node:fs");
185027
- const m2 = JSON.parse(readFileSync107(join90(dir, "himalaya.content.json"), "utf8"));
185406
+ const { readFileSync: readFileSync108 } = await import("node:fs");
185407
+ const m2 = JSON.parse(readFileSync108(join91(dir, "himalaya.content.json"), "utf8"));
185028
185408
  if (m2.name) return m2.name;
185029
185409
  } catch {
185030
185410
  }
@@ -185032,8 +185412,8 @@ async function appId(dir) {
185032
185412
  }
185033
185413
  async function contentVisibility(dir) {
185034
185414
  try {
185035
- const { readFileSync: readFileSync107 } = await import("node:fs");
185036
- const m2 = JSON.parse(readFileSync107(join90(dir, "himalaya.content.json"), "utf8"));
185415
+ const { readFileSync: readFileSync108 } = await import("node:fs");
185416
+ const m2 = JSON.parse(readFileSync108(join91(dir, "himalaya.content.json"), "utf8"));
185037
185417
  if (m2.visibility === "public" || m2.visibility === "unlisted") return m2.visibility;
185038
185418
  } catch {
185039
185419
  }
@@ -185325,7 +185705,7 @@ Seed your coding agent with \`himi agent-init\`. Guides: ${idx.portalUrl}`);
185325
185705
  ...res.removed.length ? { removed: res.removed, note: "older copies at the app root, moved to .himi/legacy-agent-files/ (a backup, in case one became app code) \u2014 the current ones live in .agents/skills/himalaya/" } : {},
185326
185706
  // In a git repo the refresh moves the diff identity an earlier `himi test` attested (that
185327
185707
  // hash is shared with the repo gates and deliberately exempts nothing but its own metadata).
185328
- ...existsSync84(join90(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
185708
+ ...existsSync85(join91(dir, ".himi", "attestations", "latest.json")) ? { next: "if `himi push` then refuses the attestation as stale, re-run `himi test` \u2014 in a git checkout this refresh changed the tree it covered" } : {},
185329
185709
  ...res.skipped.length ? { skipped: res.skipped } : {},
185330
185710
  portalUrl: idx.portalUrl
185331
185711
  });
@@ -185348,7 +185728,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
185348
185728
  try {
185349
185729
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
185350
185730
  const bres = await buildContentForValidation2(dir, { resolution });
185351
- out2 = mkdtempSync6(join90(tmpdir7(), "himi-ux-record-"));
185731
+ out2 = mkdtempSync6(join91(tmpdir7(), "himi-ux-record-"));
185352
185732
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
185353
185733
  const outcome = await buildReleaseFromContent2(dir, out2, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
185354
185734
  const releaseId = outcome.manifest.releaseId;
@@ -185516,7 +185896,7 @@ async function producedWidgetFamilies(dir) {
185516
185896
  }
185517
185897
  const outDir = tier5Dir.bundleOutDir(dir);
185518
185898
  try {
185519
- readdirSync35(outDir);
185899
+ readdirSync36(outDir);
185520
185900
  } catch {
185521
185901
  return { families: void 0, source: "unbuilt" };
185522
185902
  }
@@ -185527,7 +185907,7 @@ async function producedWidgetFamilies(dir) {
185527
185907
  let js2;
185528
185908
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
185529
185909
  try {
185530
- js2 = readFileSync106(join90(outDir, candidate), "utf8");
185910
+ js2 = readFileSync107(join91(outDir, candidate), "utf8");
185531
185911
  break;
185532
185912
  } catch {
185533
185913
  }
@@ -185558,7 +185938,7 @@ async function producedWidgetFamilies(dir) {
185558
185938
  }
185559
185939
  return { families, source: "dist/bundles" };
185560
185940
  }
185561
- var boardOpen = (dir) => existsSync84(join90(dir, ".himi", "studio", "index.html"));
185941
+ var boardOpen = (dir) => existsSync85(join91(dir, ".himi", "studio", "index.html"));
185562
185942
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
185563
185943
  async function leaveForBoard(dir, record2, value) {
185564
185944
  if (!boardOpen(dir)) return;
@@ -185582,7 +185962,7 @@ async function liveMapFor(dir) {
185582
185962
  }
185583
185963
  var commandScratch = new AsyncLocalStorage2();
185584
185964
  function commandScratchDir(prefix) {
185585
- const dir = mkdtempSync6(join90(tmpdir7(), prefix));
185965
+ const dir = mkdtempSync6(join91(tmpdir7(), prefix));
185586
185966
  commandScratch.getStore()?.push(dir);
185587
185967
  return dir;
185588
185968
  }
@@ -185936,7 +186316,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
185936
186316
  const owed = openApprovals2(readApprovals2(dir).events).length;
185937
186317
  next.push(`carried the planning journal into the app (${carried.join(", ")}${owed ? `; ${owed} approval${owed === 1 ? "" : "s"} still owed` : ""}) \u2014 \`himi progress --brief\` there shows it`);
185938
186318
  }
185939
- const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f2) => existsSync84(join90(parentDir, f2)));
186319
+ const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f2) => existsSync85(join91(parentDir, f2)));
185940
186320
  if (planInParent.length) {
185941
186321
  next.push(`${planInParent.join(" and ")} in ${parentDir} ${planInParent.length > 1 ? "are" : "is"} not the app's \u2014 if that is the approved plan, move it over the template's boilerplate in ${dir}`);
185942
186322
  }
@@ -186097,13 +186477,13 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186097
186477
  if (adding) {
186098
186478
  const from = str10(flags.from);
186099
186479
  if (!from) throw new TrimRefusal2('--add <id> needs --from <file> holding the whole new SCREENS entry (` "<id>": { "screenId": "<id>", \u2026 },`)');
186100
- print(adopt.addScreen(dir, adding, readFileSync106(from, "utf8"), { parent: str10(flags.parent), tab: str10(flags.tab), displayName: str10(flags["display-name"]) }));
186480
+ print(adopt.addScreen(dir, adding, readFileSync107(from, "utf8"), { parent: str10(flags.parent), tab: str10(flags.tab), displayName: str10(flags["display-name"]) }));
186101
186481
  return 0;
186102
186482
  }
186103
186483
  if (target) {
186104
186484
  const from = str10(flags.from);
186105
186485
  if (!from) throw new TrimRefusal2("--write <id> needs --from <file> holding the whole entry, as --show prints it");
186106
- print(adopt.replaceScreen(dir, target, readFileSync106(from, "utf8")));
186486
+ print(adopt.replaceScreen(dir, target, readFileSync107(from, "utf8")));
186107
186487
  return 0;
186108
186488
  }
186109
186489
  if (show) {
@@ -186158,9 +186538,9 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186158
186538
  try {
186159
186539
  const r2 = await client2().templates();
186160
186540
  const remote = (r2.json?.templates ?? []).filter((t2) => typeof t2.name === "string").map((t2) => ({ ...t2, source: "remote" }));
186161
- const byName2 = new Map(catalog3.map((c2) => [c2.name, c2]));
186162
- for (const t2 of remote) byName2.set(t2.name, t2);
186163
- catalog3 = [...byName2.values()];
186541
+ const byName3 = new Map(catalog3.map((c2) => [c2.name, c2]));
186542
+ for (const t2 of remote) byName3.set(t2.name, t2);
186543
+ catalog3 = [...byName3.values()];
186164
186544
  } catch (e2) {
186165
186545
  note = `remote catalog unreachable (${e2.message}); ranked the embedded templates only`;
186166
186546
  }
@@ -186185,7 +186565,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186185
186565
  const { keepFromSpec: keepFromSpec2 } = await Promise.resolve().then(() => (init_template_adopt(), template_adopt_exports));
186186
186566
  const dir = contentDir(flags);
186187
186567
  try {
186188
- const spec = keepFromSpec2(dir, str10(flags["from-spec"]) ?? join90(dir, "SPEC.md"));
186568
+ const spec = keepFromSpec2(dir, str10(flags["from-spec"]) ?? join91(dir, "SPEC.md"));
186189
186569
  const own5 = typeof keep === "string" ? keep.split(",").map((k2) => k2.trim()).filter(Boolean) : [];
186190
186570
  keep = [.../* @__PURE__ */ new Set([...spec.keep, ...own5])].join(",");
186191
186571
  fromSpec = { fromSpec: {
@@ -186226,7 +186606,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186226
186606
  const result = { ...outcome.result, ...fromSpec };
186227
186607
  const adoptDir = contentDir(flags);
186228
186608
  const kept = flags["dry-run"] !== true && (outcome.code === 0 || outcome.result.forcedKeep === true);
186229
- if (kept && existsSync84(join90(adoptDir, adoptBits.ADOPT_FILE))) {
186609
+ if (kept && existsSync85(join91(adoptDir, adoptBits.ADOPT_FILE))) {
186230
186610
  try {
186231
186611
  const inv = adoptBits.inspectApp(adoptDir);
186232
186612
  adoptBits.writeInventory(adoptDir, inv, { companions: "if-missing" });
@@ -186255,7 +186635,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186255
186635
  const sourceDir = resolve65(str10(flags["source-dir"]) ?? appDir3);
186256
186636
  let meta;
186257
186637
  try {
186258
- meta = JSON.parse(readFileSync106(join90(sourceDir, TEMPLATE_META_FILE), "utf8"));
186638
+ meta = JSON.parse(readFileSync107(join91(sourceDir, TEMPLATE_META_FILE), "utf8"));
186259
186639
  } catch {
186260
186640
  io2.error(JSON.stringify({ error: `no ${TEMPLATE_META_FILE} in ${sourceDir} \u2014 a template must declare { title, description, tags } to be published`, hint: `add ${TEMPLATE_META_FILE} next to himalaya.content.json (or pass --source-dir <dir>)` }));
186261
186641
  return 2;
@@ -186265,7 +186645,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186265
186645
  return 2;
186266
186646
  }
186267
186647
  try {
186268
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync106(join90(sourceDir, "tokens.json"), "utf8")));
186648
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync107(join91(sourceDir, "tokens.json"), "utf8")));
186269
186649
  meta = { ...appearance, ...meta };
186270
186650
  } catch {
186271
186651
  }
@@ -186287,7 +186667,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186287
186667
  const force = flags.force === true;
186288
186668
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
186289
186669
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
186290
- const out2 = mkdtempSync6(join90(tmpdir7(), "himi-template-"));
186670
+ const out2 = mkdtempSync6(join91(tmpdir7(), "himi-template-"));
186291
186671
  publishScratch.push(out2);
186292
186672
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
186293
186673
  const outcome = await buildReleaseFromContent2(buildDir, out2, {
@@ -186349,9 +186729,9 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186349
186729
  let iconHash;
186350
186730
  if (iconPath) {
186351
186731
  try {
186352
- const bytes2 = readFileSync106(iconPath);
186732
+ const bytes2 = readFileSync107(iconPath);
186353
186733
  payload.icon = bytes2.toString("base64");
186354
- iconHash = createHash45("sha256").update(bytes2).digest("hex");
186734
+ iconHash = createHash46("sha256").update(bytes2).digest("hex");
186355
186735
  } catch {
186356
186736
  }
186357
186737
  }
@@ -186561,12 +186941,12 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186561
186941
  print({ ok: false, error: "eject needs a module", hint: `one of: ${EJECTABLE2.join(", ")}` });
186562
186942
  return 2;
186563
186943
  }
186564
- const { readdirSync: readdirSync36, existsSync: fileExists } = await import("node:fs");
186944
+ const { readdirSync: readdirSync37, existsSync: fileExists } = await import("node:fs");
186565
186945
  const { join: pathJoin3 } = await import("node:path");
186566
186946
  const targets = [];
186567
186947
  const t5 = pathJoin3(dir, "tier5-src");
186568
186948
  if (fileExists(t5)) {
186569
- for (const d2 of readdirSync36(t5, { withFileTypes: true })) {
186949
+ for (const d2 of readdirSync37(t5, { withFileTypes: true })) {
186570
186950
  if (d2.isDirectory()) targets.push(pathJoin3(t5, d2.name, "index.ts"));
186571
186951
  }
186572
186952
  }
@@ -186654,7 +187034,7 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186654
187034
  const path2 = dpxSessionTokenPath();
186655
187035
  let raw;
186656
187036
  try {
186657
- raw = readFileSync106(path2, "utf8");
187037
+ raw = readFileSync107(path2, "utf8");
186658
187038
  } catch {
186659
187039
  throw new Error(`no DPX BO session at ${path2}; run dpx auth login, write dpx auth token to that path, then himi login --dpx --target bo`);
186660
187040
  }
@@ -186788,8 +187168,8 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186788
187168
  const dir = resolve65(out2);
186789
187169
  const files = himiPluginFiles2(skills.skillMd);
186790
187170
  for (const f2 of files) {
186791
- mkdirSync43(dirname50(join90(dir, f2.path)), { recursive: true });
186792
- writeFileSync41(join90(dir, f2.path), f2.text);
187171
+ mkdirSync44(dirname51(join91(dir, f2.path)), { recursive: true });
187172
+ writeFileSync42(join91(dir, f2.path), f2.text);
186793
187173
  }
186794
187174
  print({ ok: true, dir, files: files.map((f2) => f2.path), next: [`claude plugin validate ${dir}`, `claude --plugin-dir ${dir}`] });
186795
187175
  return 0;
@@ -186806,16 +187186,16 @@ ${doNow.map((l2) => ` ${l2}`).join("\n")}`);
186806
187186
  case "studio": {
186807
187187
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
186808
187188
  const dir = contentDir(flags);
186809
- if (!existsSync84(join90(dir, "himalaya.content.json"))) {
187189
+ if (!existsSync85(join91(dir, "himalaya.content.json"))) {
186810
187190
  io2.error(JSON.stringify({ ok: false, error: "not_a_content_package", dir, hint: "run from the app directory (or pass --content <dir>) \u2014 the board reads that package's .himi/" }));
186811
187191
  return 2;
186812
187192
  }
186813
187193
  if (pos[1] === "reel") {
186814
187194
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
186815
- const out2 = resolve65(str10(flags.out) ?? join90(dir, ".himi", "making-of.html"));
187195
+ const out2 = resolve65(str10(flags.out) ?? join91(dir, ".himi", "making-of.html"));
186816
187196
  const { html, plan } = buildReel2(dir);
186817
- mkdirSync43(dirname50(out2), { recursive: true });
186818
- writeFileSync41(out2, html);
187197
+ mkdirSync44(dirname51(out2), { recursive: true });
187198
+ writeFileSync42(out2, html);
186819
187199
  const { pathToFileURL: pathToFileURL9 } = await import("node:url");
186820
187200
  const url2 = pathToFileURL9(out2).href;
186821
187201
  const open2 = flags["no-open"] !== true;
@@ -186884,7 +187264,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
186884
187264
  } = await Promise.resolve().then(() => (init_progress(), progress_exports));
186885
187265
  const dir = contentDir(flags);
186886
187266
  const journal2 = journalPath2(dir);
186887
- const isContentPackage = existsSync84(join90(dir, "himalaya.content.json"));
187267
+ const isContentPackage = existsSync85(join91(dir, "himalaya.content.json"));
186888
187268
  const hint = isContentPackage ? {} : { hint: "no himalaya.content.json here \u2014 run from the app directory (or pass --content <dir>) so the build board finds this journal" };
186889
187269
  const message = pos.slice(1).join(" ").trim();
186890
187270
  const approvalFlags = ["approve", "done", "drop"].filter((f2) => flags[f2] !== void 0);
@@ -187036,7 +187416,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
187036
187416
  case "test": {
187037
187417
  const dir = contentDir(flags);
187038
187418
  const structuredOutput = flags.json === true || io2.isTTY !== true;
187039
- if (existsSync84(join90(dir, "functions"))) {
187419
+ if (existsSync85(join91(dir, "functions"))) {
187040
187420
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
187041
187421
  const loaded = await loadFunctionsApp2(dir);
187042
187422
  const results = [];
@@ -187168,7 +187548,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
187168
187548
  io2.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
187169
187549
  return 2;
187170
187550
  }
187171
- const sources = collectWorkerSources2(join90(dir, "tier5-src"), screen.bundleName);
187551
+ const sources = collectWorkerSources2(join91(dir, "tier5-src"), screen.bundleName);
187172
187552
  if (!sources) {
187173
187553
  io2.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
187174
187554
  return 2;
@@ -187348,7 +187728,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
187348
187728
  const sp = await runShotsPass2({
187349
187729
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
187350
187730
  tokens: outcome.tokens,
187351
- outDir: join90(dir, ".himi", "shots"),
187731
+ outDir: join91(dir, ".himi", "shots"),
187352
187732
  width: 390,
187353
187733
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {}, ...base44Data ? { base44Data } : {} },
187354
187734
  ...budgetMs ? { budgetMs } : {},
@@ -187388,7 +187768,7 @@ ${handover.hint[0].toUpperCase()}${handover.hint.slice(1)}.`);
187388
187768
  releaseId: result.releaseId,
187389
187769
  releasePayload: release2,
187390
187770
  descriptors: attestedDescriptors,
187391
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join90(dir, ".himi", "shots", name)])),
187771
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join91(dir, ".himi", "shots", name)])),
187392
187772
  coverage: {
187393
187773
  screens: crawledScreens.size,
187394
187774
  actionsDispatched: result.actions.exercised,
@@ -187513,7 +187893,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
187513
187893
  return 2;
187514
187894
  }
187515
187895
  try {
187516
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync106(stateArg.replace(/^@/, ""), "utf8");
187896
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync107(stateArg.replace(/^@/, ""), "utf8");
187517
187897
  injectedState = JSON.parse(raw);
187518
187898
  } catch (e2) {
187519
187899
  io2.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e2.message}` }));
@@ -187528,7 +187908,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
187528
187908
  return 2;
187529
187909
  }
187530
187910
  try {
187531
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync106(inputArg.replace(/^@/, ""), "utf8");
187911
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync107(inputArg.replace(/^@/, ""), "utf8");
187532
187912
  const parsed = JSON.parse(raw);
187533
187913
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
187534
187914
  injectedInput = parsed;
@@ -187611,7 +187991,7 @@ ${[...afterRun.next, ...testReminder ? [testReminder] : []].join("\n")}`);
187611
187991
  const diagnostics = (outcome.lint ?? []).find((s2) => s2.screenId === screenId)?.issues ?? [];
187612
187992
  let pngFile;
187613
187993
  let attestationPngFile;
187614
- const shotPath = join90(dir, ".himi", "shots", `${screenId}.png`);
187994
+ const shotPath = join91(dir, ".himi", "shots", `${screenId}.png`);
187615
187995
  const pngTarget = typeof flags.png === "string" ? str10(flags.png) : flags.png === true ? shotPath : void 0;
187616
187996
  if (pngTarget && r2.pngBase64) {
187617
187997
  pngFile = writePngFile(pngTarget, r2.pngBase64);
@@ -187970,7 +188350,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
187970
188350
  io2.error(JSON.stringify(linked));
187971
188351
  return 1;
187972
188352
  }
187973
- if (existsSync84(dest)) {
188353
+ if (existsSync85(dest)) {
187974
188354
  if (!statSync23(dest).isDirectory()) {
187975
188355
  io2.error(JSON.stringify({
187976
188356
  error: `${dest} exists and is not a directory`,
@@ -187978,12 +188358,12 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
187978
188358
  }));
187979
188359
  return 1;
187980
188360
  }
187981
- const occupied = readdirSync35(dest).length > 0;
188361
+ const occupied = readdirSync36(dest).length > 0;
187982
188362
  if (occupied && !force) {
187983
188363
  io2.error(JSON.stringify({ error: `${dest} already exists and is not empty`, hint: "pass --force to write into it anyway (merging, and naming what it keeps), --replace to restore exactly what was archived, or --out/--name to land somewhere else" }));
187984
188364
  return 1;
187985
188365
  }
187986
- if (replace && occupied && !existsSync84(join90(dest, "himalaya.content.json"))) {
188366
+ if (replace && occupied && !existsSync85(join91(dest, "himalaya.content.json"))) {
187987
188367
  io2.error(JSON.stringify({
187988
188368
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
187989
188369
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -188219,7 +188599,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
188219
188599
  let localSourceHash;
188220
188600
  try {
188221
188601
  const { collectAppSource: collectAppSource2, zipAppSource: zipAppSource2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
188222
- localSourceHash = createHash45("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
188602
+ localSourceHash = createHash46("sha256").update(zipAppSource2(collectAppSource2(dir).entries).toString("base64")).digest("hex");
188223
188603
  } catch {
188224
188604
  }
188225
188605
  return runReleasePatch({
@@ -188628,9 +189008,9 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
188628
189008
  return 2;
188629
189009
  }
188630
189010
  const { packRelease: packRelease2, packReleaseToDir: packReleaseToDir2 } = await Promise.resolve().then(() => (init_pack(), pack_exports));
188631
- const displayName2 = str10(flags.name);
189011
+ const displayName3 = str10(flags.name);
188632
189012
  const launchMode = flags.snapshot === true ? "snapshot" : "live";
188633
- const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...displayName2 ? { displayName: displayName2 } : {} };
189013
+ const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...displayName3 ? { displayName: displayName3 } : {} };
188634
189014
  const writePack = (root, appName2, out2, source) => format === "dir" ? packReleaseToDir2(root, appName2, out2, packOpts) : packRelease2(root, appName2, out2, { ...packOpts, ...source ? { source } : {} });
188635
189015
  const releaseDir = str10(flags["release-dir"]);
188636
189016
  if (releaseDir) {
@@ -188696,7 +189076,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
188696
189076
  }
188697
189077
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
188698
189078
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
188699
- const built = mkdtempSync6(join90(tmpdir7(), "himi-pack-"));
189079
+ const built = mkdtempSync6(join91(tmpdir7(), "himi-pack-"));
188700
189080
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
188701
189081
  const outcome = await buildReleaseFromContent2(dir, built, {
188702
189082
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -188909,7 +189289,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
188909
189289
  }));
188910
189290
  return 1;
188911
189291
  }
188912
- const archive = readFileSync106(resolve65(file2));
189292
+ const archive = readFileSync107(resolve65(file2));
188913
189293
  const result = await client2().createPackageShare({
188914
189294
  app: opened.header.app,
188915
189295
  releaseId: opened.header.releaseId,
@@ -189006,11 +189386,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189006
189386
  const app = await appId(dir2);
189007
189387
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
189008
189388
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
189009
- temp = mkdtempSync6(join90(tmpdir7(), "himi-run-build-"));
189389
+ temp = mkdtempSync6(join91(tmpdir7(), "himi-run-build-"));
189010
189390
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
189011
189391
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
189012
189392
  servedMissing = initial.missingBundles;
189013
- source = join90(temp, app);
189393
+ source = join91(temp, app);
189014
189394
  }
189015
189395
  const chromeArg = str10(flags.chrome) ?? "preview";
189016
189396
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -189049,7 +189429,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189049
189429
  if (!recordKit) return;
189050
189430
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
189051
189431
  const failure = record2(served2, { ok: true, at: startedAt });
189052
- if (!recorded) io2.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join90(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
189432
+ if (!recorded) io2.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join91(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
189053
189433
  await refreshBoard();
189054
189434
  };
189055
189435
  const retract = async () => {
@@ -189085,7 +189465,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189085
189465
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
189086
189466
  const hhpPreviewRoute = watch2 ? (await Promise.resolve().then(() => (init_hhp_preview(), hhp_preview_exports))).hhpPreviewResponse : null;
189087
189467
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
189088
- if (watch2) served = mkdtempSync6(join90(tmpdir7(), "himi-run-served-"));
189468
+ if (watch2) served = mkdtempSync6(join91(tmpdir7(), "himi-run-served-"));
189089
189469
  const running = await runPackage2(source, {
189090
189470
  port,
189091
189471
  chrome: chromeArg,
@@ -189114,8 +189494,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189114
189494
  await announce(running);
189115
189495
  if (watchKit && bus) {
189116
189496
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
189117
- const live3 = join90(running.releaseRoot, running.app);
189118
- const manifestOf = (tree) => JSON.parse(readFileSync106(join90(tree, "release-manifest.json"), "utf8"));
189497
+ const live3 = join91(running.releaseRoot, running.app);
189498
+ const manifestOf = (tree) => JSON.parse(readFileSync107(join91(tree, "release-manifest.json"), "utf8"));
189119
189499
  let current = manifestOf(live3);
189120
189500
  const failed3 = async (message) => {
189121
189501
  if (stopping) return;
@@ -189126,7 +189506,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189126
189506
  };
189127
189507
  rebuild = watchKit.serialize(async () => {
189128
189508
  const t0 = Date.now();
189129
- const next = mkdtempSync6(join90(tmpdir7(), "himi-run-build-"));
189509
+ const next = mkdtempSync6(join91(tmpdir7(), "himi-run-build-"));
189130
189510
  try {
189131
189511
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
189132
189512
  if (bres.compileErrors.length) {
@@ -189143,16 +189523,16 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189143
189523
  return;
189144
189524
  }
189145
189525
  if (stopping) return;
189146
- const fresh = manifestOf(join90(next, running.app));
189526
+ const fresh = manifestOf(join91(next, running.app));
189147
189527
  const diff = watchKit.diffReleases(current, fresh);
189148
189528
  const { describeChanges: describeChanges2, headline: headline2 } = await Promise.resolve().then(() => (init_build_changes(), build_changes_exports));
189149
- const changes = describeChanges2(live3, join90(next, running.app), diff);
189529
+ const changes = describeChanges2(live3, join91(next, running.app), diff);
189150
189530
  const retired = `${live3}.retired-${Date.now()}`;
189151
189531
  renameSync13(live3, retired);
189152
189532
  try {
189153
- renameSync13(join90(next, running.app), live3);
189533
+ renameSync13(join91(next, running.app), live3);
189154
189534
  } catch {
189155
- cpSync3(join90(next, running.app), live3, { recursive: true });
189535
+ cpSync3(join91(next, running.app), live3, { recursive: true });
189156
189536
  }
189157
189537
  rmSync24(retired, { recursive: true, force: true });
189158
189538
  current = fresh;
@@ -189779,7 +190159,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189779
190159
  io2.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
189780
190160
  return 2;
189781
190161
  }
189782
- if (!existsSync84(file2)) {
190162
+ if (!existsSync85(file2)) {
189783
190163
  io2.error(JSON.stringify({ error: `no such file: ${file2}` }));
189784
190164
  return 2;
189785
190165
  }
@@ -189835,7 +190215,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189835
190215
  lintNote: "`himi patch` sends a raw file. Nothing here built or linted your app \u2014 `himi push --only <selector>` is the same edit with the full lint gate in front of it."
189836
190216
  };
189837
190217
  if (kind === "tokens" || kind === "app-config") {
189838
- const body = readFileSync106(file2, "utf8");
190218
+ const body = readFileSync107(file2, "utf8");
189839
190219
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
189840
190220
  return runReleasePatch({
189841
190221
  ...common,
@@ -189850,7 +190230,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189850
190230
  io2.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
189851
190231
  return 2;
189852
190232
  }
189853
- const descriptor = readFileSync106(file2, "utf8");
190233
+ const descriptor = readFileSync107(file2, "utf8");
189854
190234
  let parsed;
189855
190235
  try {
189856
190236
  parsed = JSON.parse(descriptor);
@@ -189907,8 +190287,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
189907
190287
  extra: { kind, screen: id, file: { path: file2, bytes: Buffer.byteLength(descriptor, "utf8") } }
189908
190288
  });
189909
190289
  }
189910
- const bytes2 = readFileSync106(file2);
189911
- const sha2 = createHash45("sha256").update(bytes2).digest("hex");
190290
+ const bytes2 = readFileSync107(file2);
190291
+ const sha2 = createHash46("sha256").update(bytes2).digest("hex");
189912
190292
  const forScreens = (str10(flags["for-screen"]) ?? "").split(",").map((s2) => s2.trim()).filter(Boolean);
189913
190293
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
189914
190294
  io2.error(JSON.stringify({ ok: false, error: "no_rebind_takes_no_value", message: "--no-rebind is a switch and takes no value; pass it bare.", usage: PATCH_USAGE }));
@@ -190051,7 +190431,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
190051
190431
  }
190052
190432
  let def;
190053
190433
  try {
190054
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync106(arg.slice(1), "utf8") : arg);
190434
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync107(arg.slice(1), "utf8") : arg);
190055
190435
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
190056
190436
  io2.error(JSON.stringify({ error: "invalid_definition", problems: [`--definition must be a JSON object, got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`] }));
190057
190437
  return 2;
@@ -190333,7 +190713,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
190333
190713
  const rememberLastDist = async (jobs2) => {
190334
190714
  try {
190335
190715
  const dir = contentDir(flags);
190336
- if (!existsSync84(join90(dir, "himalaya.content.json"))) return;
190716
+ if (!existsSync85(join91(dir, "himalaya.content.json"))) return;
190337
190717
  const { lastDistFromJob: lastDistFromJob2, writeLastDist: writeLastDist2 } = await Promise.resolve().then(() => (init_capabilities(), capabilities_exports));
190338
190718
  const own5 = await appId(dir);
190339
190719
  for (const job of jobs2) {
@@ -190573,7 +190953,7 @@ ${label3}`);
190573
190953
  const platform = str10(flags.platform)?.split(",").map((p2) => p2.trim()).filter(Boolean);
190574
190954
  {
190575
190955
  const distDir = contentDir(flags);
190576
- if (existsSync84(join90(distDir, "himalaya.content.json"))) {
190956
+ if (existsSync85(join91(distDir, "himalaya.content.json"))) {
190577
190957
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
190578
190958
  const errors = issues.filter((i2) => i2.severity === "error");
190579
190959
  for (const i2 of issues.filter((i3) => i3.severity === "warning")) {
@@ -190913,7 +191293,7 @@ ${label3}`);
190913
191293
  const dpxPath = dpxSessionTokenPath();
190914
191294
  let dpx;
190915
191295
  try {
190916
- dpx = readFileSync106(dpxPath, "utf8").trim();
191296
+ dpx = readFileSync107(dpxPath, "utf8").trim();
190917
191297
  } catch {
190918
191298
  io2.error(JSON.stringify({ error: `no dpx session at ${dpxPath} \u2014 run \`dpx auth login\` then \`dpx auth token > ${dpxPath}\` (dpx 2.x keeps its own state in ~/.dpx/auth.json and does NOT write the legacy session file), or omit --dpx` }));
190919
191299
  return 1;
@@ -191376,7 +191756,7 @@ ${report3.ok ? "OK \u2014 data + serve plane reachable." : typeof report3.hint =
191376
191756
  const path2 = dpxSessionTokenPath();
191377
191757
  let raw;
191378
191758
  try {
191379
- raw = readFileSync106(path2, "utf8");
191759
+ raw = readFileSync107(path2, "utf8");
191380
191760
  } catch {
191381
191761
  io2.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
191382
191762
  return 1;
@@ -191570,7 +191950,7 @@ ${report3.ok ? "OK \u2014 data + serve plane reachable." : typeof report3.hint =
191570
191950
  }
191571
191951
  }
191572
191952
  const sub = pos[1];
191573
- const b44Usage = "himi base44 analyze (--url <https://\u2026> | --app-id <id>) [--out .himi/base44-book] [--sample <n>] [--json] | himi base44 adopt [--book .himi/base44-book] [--entities A,B] [--write [path]] [--json] | himi base44 entities query [--content <dir>] [--all] [--json] | himi base44 types [--content <dir>] [--json] | himi base44 login [--from-base44-cli] | himi base44 whoami | himi base44 logout";
191953
+ const b44Usage = "himi base44 analyze (--url <https://\u2026> | --app-id <id>) [--out .himi/base44-book] [--sample <n>] [--json] | himi base44 adopt [--book .himi/base44-book] [--entities A,B] [--write [path]] [--json] | himi base44 entities query [--content <dir>] [--all] [--json] | himi base44 types [--content <dir>] [--json] | himi base44 screens [--content <dir>] [--force] [--dry-run] [--json] | himi base44 login [--from-base44-cli] | himi base44 whoami | himi base44 logout";
191574
191954
  if (sub === "adopt") {
191575
191955
  const { runBase44Adopt: runBase44Adopt2, AdoptError: AdoptError2 } = await Promise.resolve().then(() => (init_base44_adopt(), base44_adopt_exports));
191576
191956
  const dir = contentDir(flags);
@@ -191617,6 +191997,65 @@ ${report3.ok ? "OK \u2014 data + serve plane reachable." : typeof report3.hint =
191617
191997
  return 1;
191618
191998
  }
191619
191999
  }
192000
+ if (sub === "screens") {
192001
+ const dir = contentDir(flags);
192002
+ const fail6 = (code, error, extra = {}) => {
192003
+ io2.error(JSON.stringify({ ok: false, code, error, ...extra }));
192004
+ return 1;
192005
+ };
192006
+ const gen = await Promise.resolve().then(() => (init_generate(), generate_exports));
192007
+ const missing = gen.ENGINE_FILES.filter((f2) => !existsSync85(join91(dir, f2)));
192008
+ if (missing.length) {
192009
+ return fail6("content_dir_not_base44_starter", `${dir} is not a base44-starter content package: the generated screens import a shared engine it does not have (${missing.join(", ")})`, { missing, hint: "scaffold one with `himi init <name> --template base44-starter --base44-app-id <id>`" });
192010
+ }
192011
+ const app = await appId(dir);
192012
+ let base44;
192013
+ try {
192014
+ const { loadAppConfigModule: loadAppConfigModule2 } = await Promise.resolve().then(() => (init_app_config_loader(), app_config_loader_exports));
192015
+ base44 = (await loadAppConfigModule2(dir))?.base44;
192016
+ } catch (e2) {
192017
+ const finding = (await Promise.resolve().then(() => (init_base44_lint(), base44_lint_exports))).base44DefineAppFinding(e2);
192018
+ if (finding) {
192019
+ io2.error(JSON.stringify({ ok: false, code: "invalid_config", app, issues: [{ severity: "error", ...finding }], hint: '`himi validate` reports these with pass: "base44"' }));
192020
+ return 1;
192021
+ }
192022
+ return fail6("config_unloadable", e2.message, { app, hint: "fix the config so it imports cleanly, then re-run. `himi validate` shows the underlying error." });
192023
+ }
192024
+ const specs = base44 && typeof base44 === "object" && Array.isArray(base44.entities) ? base44.entities : [];
192025
+ if (!specs.length) {
192026
+ return fail6("no_entities_block", "config.ts declares no `base44.entities` \u2014 nothing to generate", { app, hint: "declare the entities first (`himi base44 adopt --write`, himi docs base44-entities), then re-run" });
192027
+ }
192028
+ const { validateBase44Entities: validateBase44Entities2 } = await Promise.resolve().then(() => (init_base44_schema(), base44_schema_exports));
192029
+ const errors = validateBase44Entities2(base44, app).filter((i2) => i2.severity === "error");
192030
+ if (errors.length) {
192031
+ io2.error(JSON.stringify({ ok: false, code: "invalid_config", app, issues: errors, hint: '`himi validate` reports these with pass: "base44"' }));
192032
+ return 1;
192033
+ }
192034
+ const { planEntityScreens: planEntityScreens2, PlanError: PlanError2 } = await Promise.resolve().then(() => (init_plan4(), plan_exports));
192035
+ let sp;
192036
+ try {
192037
+ sp = planEntityScreens2(specs, app);
192038
+ } catch (e2) {
192039
+ if (e2 instanceof PlanError2) return fail6(e2.code, e2.message, { app, ...e2.extra });
192040
+ throw e2;
192041
+ }
192042
+ const rendered = gen.renderAll(sp);
192043
+ const dryRun = flags["dry-run"] === true;
192044
+ const { files } = gen.writeGenerated(dir, rendered, { force: flags.force === true, dryRun });
192045
+ const orphaned = gen.orphanedFiles(dir, rendered);
192046
+ print({
192047
+ ok: true,
192048
+ app,
192049
+ ...dryRun ? { dryRun: true } : {},
192050
+ files,
192051
+ plans: sp.plans.map((p2) => ({ entity: p2.name, physical: p2.entity, list: p2.ids.list, detail: p2.ids.detail, gates: p2.gates })),
192052
+ tabs: sp.tabs,
192053
+ more: sp.more,
192054
+ ...orphaned.length ? { orphaned, note: "generated files for entities no longer declared \u2014 never deleted; remove them by hand if the screens should go" } : {},
192055
+ ...files.some((f2) => f2.outcome === "skipped-edited") ? { hint: "skipped-edited files were changed by hand and are left alone; --force overwrites them" } : {}
192056
+ });
192057
+ return 0;
192058
+ }
191620
192059
  if (sub === "entities") {
191621
192060
  if (pos[2] !== "query") {
191622
192061
  io2.error(JSON.stringify({ ok: false, code: "unknown_subcommand", error: pos[2] ? `unknown base44 entities subcommand "${pos[2]}" \u2014 use query` : "base44 entities needs a subcommand: query", usage: b44Usage }));