@wix/himalaya-cli 0.1507.0 → 0.1508.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.
Files changed (2) hide show
  1. package/dist/cli.mjs +169 -121
  2. package/package.json +2 -2
package/dist/cli.mjs CHANGED
@@ -1013,7 +1013,8 @@ var init_compiler_options = __esm({
1013
1013
 
1014
1014
  // src/scaffold.ts
1015
1015
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync, readdirSync, readFileSync as readFileSync2, statSync, lstatSync, copyFileSync } from "node:fs";
1016
- import { resolve, join as join2, dirname as dirname2 } from "node:path";
1016
+ import { spawnSync } from "node:child_process";
1017
+ import { resolve, join as join2, dirname as dirname2, sep as sep2 } from "node:path";
1017
1018
  import { fileURLToPath } from "node:url";
1018
1019
  function keptPlanNote(kept) {
1019
1020
  if (!kept?.length) return void 0;
@@ -1056,6 +1057,20 @@ function defaultWorkerTsconfig() {
1056
1057
  function offRepoImports(body) {
1057
1058
  return body.replace(/(["'])@himalaya\/([^"']+)\1/g, "$1@wix/himalaya/$2$1");
1058
1059
  }
1060
+ function checkoutHead() {
1061
+ const r3 = spawnSync("git", ["rev-parse", "HEAD"], { cwd: dirname2(TEMPLATES_DIR), encoding: "utf8" });
1062
+ return r3.status === 0 ? r3.stdout.trim() : "";
1063
+ }
1064
+ function ensureCheckoutTemplates(run2 = spawnSync, headOf = checkoutHead) {
1065
+ if (process.env.HIMI_TEMPLATES_FRESH || !existsSync(resolve(ENSURE_TEMPLATES, "../gen-templates.mts"))) return;
1066
+ const head = headOf();
1067
+ if (templatesEnsuredAt !== null && templatesEnsuredAt === head) return;
1068
+ const r3 = run2(process.execPath, [ENSURE_TEMPLATES], { stdio: ["ignore", 2, 2] });
1069
+ if (r3.status !== 0) {
1070
+ throw new Error(`could not generate the starter templates (node ${ENSURE_TEMPLATES} exited ${r3.status ?? r3.signal})`);
1071
+ }
1072
+ templatesEnsuredAt = head;
1073
+ }
1059
1074
  function templateTakesBase44AppId(template) {
1060
1075
  if (!availableTemplates().includes(template)) return void 0;
1061
1076
  const config = join2(templateSourceDir(template), "config.ts");
@@ -1106,13 +1121,22 @@ function extractTemplateAppearance(tokens2) {
1106
1121
  const palette2 = [...new Set(slots.map((c2) => c2.toUpperCase()))].map((u2) => slots.find((c2) => c2.toUpperCase() === u2));
1107
1122
  return { ...accent ? { accent } : {}, ...slots.length >= 3 && palette2.length >= 2 ? { palette: palette2 } : {} };
1108
1123
  }
1124
+ function ensureTemplatesFor(...dirs) {
1125
+ if (dirs.some((d2) => resolve(d2) === TEMPLATES_DIR || resolve(d2).startsWith(TEMPLATES_DIR + sep2))) ensureCheckoutTemplates();
1126
+ }
1127
+ function canonicalCatalogFiles(files, separator = sep2) {
1128
+ const posix5 = (rel) => separator === "/" ? rel : rel.split(separator).join("/");
1129
+ return Object.fromEntries(Object.entries(files).map(([rel, body]) => [posix5(rel), body]).sort(([a2], [b2]) => a2 < b2 ? -1 : a2 > b2 ? 1 : 0));
1130
+ }
1109
1131
  function availableTemplates() {
1132
+ ensureCheckoutTemplates();
1110
1133
  const generated = existsSync(TEMPLATES_DIR) ? readdirSync(TEMPLATES_DIR).filter((d2) => !d2.includes(".build-") && statSync(join2(TEMPLATES_DIR, d2)).isDirectory()).filter((d2) => existsSync(join2(TEMPLATES_DIR, d2, "himalaya.content.json"))) : [];
1111
1134
  const showcase = SHOWCASE_TEMPLATE_NAMES.filter((name) => existsSync(join2(SHOWCASE_TEMPLATES_DIR, name, "himalaya.content.json")));
1112
1135
  return [.../* @__PURE__ */ new Set([...generated, ...showcase])].sort();
1113
1136
  }
1114
1137
  function templateSourceDir(template) {
1115
1138
  if (SHOWCASE_TEMPLATE_NAMES.includes(template)) return join2(SHOWCASE_TEMPLATES_DIR, template);
1139
+ ensureCheckoutTemplates();
1116
1140
  return join2(TEMPLATES_DIR, template);
1117
1141
  }
1118
1142
  function idSegOf(name) {
@@ -1154,7 +1178,7 @@ function sentinelizeIdentity(body, identity) {
1154
1178
  }
1155
1179
  if (identity.name) {
1156
1180
  const re2 = new RegExp(`\\b(${NAME_FIELDS.join("|")})(["']?\\s*[:=]\\s*)(["'])${escapeRe(identity.name)}\\3`, "g");
1157
- out2 = out2.replace(re2, (_m, key3, sep21, q2) => `${key3}${sep21}${q2}__HIMI_APP_NAME__${q2}`);
1181
+ out2 = out2.replace(re2, (_m, key3, sep22, q2) => `${key3}${sep22}${q2}__HIMI_APP_NAME__${q2}`);
1158
1182
  }
1159
1183
  return out2;
1160
1184
  }
@@ -1481,7 +1505,7 @@ A per-app \`npm i @wix/himalaya\` is optional and only buys your editor local ty
1481
1505
  );
1482
1506
  return finishScaffold({ dir, files, ...kept.length ? { kept: kept.sort() } : {} });
1483
1507
  }
1484
- var PLAN_FILES, PACKAGE_GITIGNORE, MODULE_SOURCE_FILE, TEMPLATES_DIR, SHOWCASE_TEMPLATES_DIR, SHOWCASE_TEMPLATE_NAMES, BASE44_APP_ID_PLACEHOLDER, BASE44_APP_ID_RE, TEMPLATE_META_FILE, TEMPLATE_ICONS_DIR, HEX_COLOR, PALETTE_KEYS, IDENTITY_SENTINELS, SENTINEL_RE, escapeRe, NAME_FIELDS, IDENTITY_FILES, DOTID_RE, BINARY_EXTENSIONS, extOf;
1508
+ var PLAN_FILES, PACKAGE_GITIGNORE, MODULE_SOURCE_FILE, TEMPLATES_DIR, ENSURE_TEMPLATES, templatesEnsuredAt, SHOWCASE_TEMPLATES_DIR, SHOWCASE_TEMPLATE_NAMES, BASE44_APP_ID_PLACEHOLDER, BASE44_APP_ID_RE, TEMPLATE_META_FILE, TEMPLATE_ICONS_DIR, HEX_COLOR, PALETTE_KEYS, IDENTITY_SENTINELS, SENTINEL_RE, escapeRe, NAME_FIELDS, IDENTITY_FILES, DOTID_RE, BINARY_EXTENSIONS, extOf;
1485
1509
  var init_scaffold = __esm({
1486
1510
  "src/scaffold.ts"() {
1487
1511
  init_source_deny();
@@ -1491,6 +1515,8 @@ var init_scaffold = __esm({
1491
1515
  PACKAGE_GITIGNORE = [".himi/", "dist/", "node_modules/"];
1492
1516
  MODULE_SOURCE_FILE = /\.(?:[cm]?[jt]sx?)$/i;
1493
1517
  TEMPLATES_DIR = resolve(dirname2(fileURLToPath(import.meta.url)), "../templates");
1518
+ ENSURE_TEMPLATES = resolve(TEMPLATES_DIR, "../../authoring-kit/ensure-templates.mjs");
1519
+ templatesEnsuredAt = null;
1494
1520
  SHOWCASE_TEMPLATES_DIR = resolve(dirname2(fileURLToPath(import.meta.url)), "../showcase-apps");
1495
1521
  SHOWCASE_TEMPLATE_NAMES = ["video-courses", "base44-starter", "base44-booking", "base44-inspections", "base44-fitness", "base44-podcast"];
1496
1522
  BASE44_APP_ID_PLACEHOLDER = "ReplaceWithYourBase44AppId";
@@ -5910,7 +5936,7 @@ import { spawn } from "node:child_process";
5910
5936
  import { createHash as createHash4 } from "node:crypto";
5911
5937
  import { randomUUID as randomUUID2 } from "node:crypto";
5912
5938
  import { appendFileSync, closeSync, existsSync as existsSync6, lstatSync as lstatSync4, mkdirSync as mkdirSync5, openSync, readdirSync as readdirSync5, readFileSync as readFileSync7, readSync, renameSync as renameSync3, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
5913
- import { isAbsolute, join as join8, relative, resolve as resolve5, sep as sep2 } from "node:path";
5939
+ import { isAbsolute, join as join8, relative, resolve as resolve5, sep as sep3 } from "node:path";
5914
5940
  function agentCovers(agent, cli) {
5915
5941
  if (isCliEntry(agent)) return false;
5916
5942
  const agentShas = shasOf(agent.images);
@@ -6223,7 +6249,7 @@ function formatBrief(input) {
6223
6249
  const who = `himi \u2014 ${input.appName ?? "this folder"}`;
6224
6250
  const lines = [];
6225
6251
  if (!summary2.count) {
6226
- lines.push(`${who}: no journey journal yet (${PROGRESS_FILE.split(sep2).join("/")}).`);
6252
+ lines.push(`${who}: no journey journal yet (${PROGRESS_FILE.split(sep3).join("/")}).`);
6227
6253
  lines.push(
6228
6254
  'A new app starts with discovery (step 1). An app built before journaling existed continues from its current state instead: confirm with the customer that its SPEC.md was approved rather than re-running discovery. Either way, journal each milestone with `himi progress "<what changed>" --step <n>` so the next session can pick up from it.'
6229
6255
  );
@@ -6338,7 +6364,7 @@ var init_progress = __esm({
6338
6364
  this.code = code;
6339
6365
  }
6340
6366
  };
6341
- toPosix = (p2) => p2.split(sep2).join("/");
6367
+ toPosix = (p2) => p2.split(sep3).join("/");
6342
6368
  UnsafeHimiDirError = class extends Error {
6343
6369
  constructor(rel) {
6344
6370
  super(`${rel} is not a real directory inside the content package (a symlink?) \u2014 refusing to write through it`);
@@ -7245,7 +7271,7 @@ var init_preview = __esm({
7245
7271
  });
7246
7272
 
7247
7273
  // src/mcp/client-configs.ts
7248
- import { dirname as dirname7, relative as relative2, resolve as resolve7, sep as sep3 } from "node:path";
7274
+ import { dirname as dirname7, relative as relative2, resolve as resolve7, sep as sep4 } from "node:path";
7249
7275
  import { fileURLToPath as fileURLToPath4 } from "node:url";
7250
7276
  function sourceCheckout(moduleUrl = import.meta.url) {
7251
7277
  if (!moduleUrl.endsWith(".ts")) return null;
@@ -7343,7 +7369,7 @@ var init_client_configs = __esm({
7343
7369
  HERE = dirname7(fileURLToPath4(import.meta.url));
7344
7370
  inside = (child, parent) => {
7345
7371
  const rel = relative2(parent, child);
7346
- return rel === "" || !rel.startsWith("..") && !rel.startsWith(sep3) && !/^[A-Za-z]:/.test(rel);
7372
+ return rel === "" || !rel.startsWith("..") && !rel.startsWith(sep4) && !/^[A-Za-z]:/.test(rel);
7347
7373
  };
7348
7374
  same = (a2, b2) => JSON.stringify(a2) === JSON.stringify(b2);
7349
7375
  out = (o2) => `${JSON.stringify(o2, null, 2)}
@@ -7721,7 +7747,7 @@ __export(build_lib_exports, {
7721
7747
  umbrellaRewritePlugin: () => umbrellaRewritePlugin
7722
7748
  });
7723
7749
  import { fileURLToPath as fileURLToPath5, pathToFileURL } from "node:url";
7724
- import { basename as basename3, dirname as dirname9, join as join11, resolve as resolve8, sep as sep4 } from "node:path";
7750
+ import { basename as basename3, dirname as dirname9, join as join11, resolve as resolve8, sep as sep5 } from "node:path";
7725
7751
  import { readdirSync as readdirSync6, existsSync as existsSync9, readFileSync as readFileSync10, realpathSync as realpathSync3, statSync as statSync3 } from "node:fs";
7726
7752
  async function loadCompiler() {
7727
7753
  try {
@@ -7769,7 +7795,7 @@ function realpathOrSelf(p2) {
7769
7795
  }
7770
7796
  }
7771
7797
  function isUnder(p2, root) {
7772
- return p2 === root || p2.startsWith(root + sep4);
7798
+ return p2 === root || p2.startsWith(root + sep5);
7773
7799
  }
7774
7800
  function confinementRoots(contentRoot2) {
7775
7801
  const roots2 = /* @__PURE__ */ new Set();
@@ -8059,7 +8085,7 @@ function tsExtResolvePlugin() {
8059
8085
  }
8060
8086
  function importMetaUrlPlugin({ scope: scope2 } = {}) {
8061
8087
  const scopes = scope2 ? [.../* @__PURE__ */ new Set([realpathOrSelf(scope2), resolve8(scope2)])] : [];
8062
- const inScope = (p2) => scopes.some((r3) => p2 === r3 || p2.startsWith(r3 + sep4));
8088
+ const inScope = (p2) => scopes.some((r3) => p2 === r3 || p2.startsWith(r3 + sep5));
8063
8089
  return {
8064
8090
  name: "himalaya-import-meta-url",
8065
8091
  setup(b2) {
@@ -8747,7 +8773,7 @@ var init_tier5_dir = __esm({
8747
8773
 
8748
8774
  // src/tier5-typecheck.ts
8749
8775
  import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
8750
- import { relative as relative3, resolve as resolve10, sep as sep5 } from "node:path";
8776
+ import { relative as relative3, resolve as resolve10, sep as sep6 } from "node:path";
8751
8777
  import { pathToFileURL as pathToFileURL2 } from "node:url";
8752
8778
  async function loadTypeScript() {
8753
8779
  const runtime = process.env.HIMI_AUTHORING_RUNTIME_DIR;
@@ -8786,7 +8812,7 @@ function pathsFor(options) {
8786
8812
  return declarationPaths(options.umbrellaDir);
8787
8813
  }
8788
8814
  function relativeFile(contentDir2, fileName) {
8789
- return relative3(contentDir2, fileName).split(sep5).join("/");
8815
+ return relative3(contentDir2, fileName).split(sep6).join("/");
8790
8816
  }
8791
8817
  function belongsToContentWorkers(contentDir2, diagnostic) {
8792
8818
  if (!diagnostic.file) return true;
@@ -8964,7 +8990,7 @@ var init_provisioning_facts = __esm({
8964
8990
  // ../../core/dev-server/src/app-assets.ts
8965
8991
  import { createHash as createHash6 } from "node:crypto";
8966
8992
  import { existsSync as existsSync11, lstatSync as lstatSync7, readFileSync as readFileSync13, readdirSync as readdirSync8, realpathSync as realpathSync4, statSync as statSync4 } from "node:fs";
8967
- import { join as join12, resolve as resolve12, sep as sep6 } from "node:path";
8993
+ import { join as join12, resolve as resolve12, sep as sep7 } from "node:path";
8968
8994
  function contentTypeFor(name) {
8969
8995
  const ext = name.includes(".") ? name.slice(name.lastIndexOf(".") + 1).toLowerCase() : "";
8970
8996
  return CONTENT_TYPES[ext] ?? "application/octet-stream";
@@ -8985,7 +9011,7 @@ function findAssetsDir(appDir3) {
8985
9011
  const rel = typeof declared === "string" && declared ? declared : "assets";
8986
9012
  const abs = resolve12(appDir3, rel);
8987
9013
  const base = resolve12(appDir3);
8988
- if (abs !== base && !abs.startsWith(base + sep6)) return null;
9014
+ if (abs !== base && !abs.startsWith(base + sep7)) return null;
8989
9015
  return existsSync11(abs) && statSync4(abs).isDirectory() ? abs : null;
8990
9016
  }
8991
9017
  function readOtaOnlyPatterns(appDir3) {
@@ -9043,7 +9069,7 @@ function collectBrandFonts(appDir3) {
9043
9069
  try {
9044
9070
  if (!lstatSync7(path2).isFile()) continue;
9045
9071
  const root = realpathSync4(appDir3);
9046
- if (!realpathSync4(path2).startsWith(root + sep6)) continue;
9072
+ if (!realpathSync4(path2).startsWith(root + sep7)) continue;
9047
9073
  } catch {
9048
9074
  continue;
9049
9075
  }
@@ -12521,8 +12547,8 @@ function textOf(node) {
12521
12547
  function plainText(node) {
12522
12548
  if (typeOf2(node) === "TEXT") return str(at(node, "textData", "text"));
12523
12549
  const children = kids(node).filter(isObj);
12524
- const sep21 = children.every((c2) => typeOf2(c2) === "TEXT") ? "" : "\n";
12525
- return children.map(plainText).join(sep21);
12550
+ const sep22 = children.every((c2) => typeOf2(c2) === "TEXT") ? "" : "\n";
12551
+ return children.map(plainText).join(sep22);
12526
12552
  }
12527
12553
  function alignOf(v2) {
12528
12554
  switch (v2) {
@@ -17908,14 +17934,14 @@ var init_hhp_command = __esm({
17908
17934
 
17909
17935
  // src/hhp.ts
17910
17936
  import { existsSync as existsSync16, readFileSync as readFileSync17, writeFileSync as writeFileSync8, mkdirSync as mkdirSync10, renameSync as renameSync4, rmSync as rmSync5, lstatSync as lstatSync8, readdirSync as readdirSync12 } from "node:fs";
17911
- import { basename as basename6, dirname as dirname13, resolve as resolve18, relative as relative4, sep as sep7, join as join15 } from "node:path";
17937
+ import { basename as basename6, dirname as dirname13, resolve as resolve18, relative as relative4, sep as sep8, join as join15 } from "node:path";
17912
17938
  import { createHash as createHash7, randomUUID as randomUUID3 } from "node:crypto";
17913
17939
  import ts3 from "typescript";
17914
17940
  function localPath(root, path2, from = join15(root, "hhp.json")) {
17915
17941
  if (/^[a-z]+:|^\/\//i.test(path2)) throw new Error("HHP source dependencies must be local files.");
17916
17942
  const target = resolve18(dirname13(from), path2), rel = relative4(resolve18(root), target);
17917
- if (rel.startsWith(`..${sep7}`) || rel === ".." || !rel || target === resolve18(root)) throw new Error("HHP source path escapes the app.");
17918
- const components2 = rel.split(sep7);
17943
+ if (rel.startsWith(`..${sep8}`) || rel === ".." || !rel || target === resolve18(root)) throw new Error("HHP source path escapes the app.");
17944
+ const components2 = rel.split(sep8);
17919
17945
  let current = resolve18(root);
17920
17946
  for (const component of components2) {
17921
17947
  current = join15(current, component);
@@ -18038,13 +18064,13 @@ async function compileContentHhp(root, opts = {}) {
18038
18064
  for (const dependency of result2.dependencies.filter((f2) => /\.tsx?$/.test(f2))) inspect2(dependency);
18039
18065
  result2.manifest.requiredCapabilities = [...requiredCapabilities2].sort();
18040
18066
  outputs.set(`dev/hhp/${id}.descriptor.json`, JSON.stringify(result2.descriptor, null, 2) + "\n");
18041
- outputs.set(`dev/hhp/${id}.map.json`, JSON.stringify({ ...result2.manifest, nodes: Object.fromEntries(Object.entries(result2.sourceMap).map(([key3, node]) => [key3, { ...node, file: relative4(root, node.file).split(sep7).join("/") }])) }, null, 2) + "\n");
18067
+ outputs.set(`dev/hhp/${id}.map.json`, JSON.stringify({ ...result2.manifest, nodes: Object.fromEntries(Object.entries(result2.sourceMap).map(([key3, node]) => [key3, { ...node, file: relative4(root, node.file).split(sep8).join("/") }])) }, null, 2) + "\n");
18042
18068
  if (result2.worker && result2.bundleName) {
18043
18069
  if (results.some((r3) => r3 !== result2 && r3.bundleName === result2.bundleName)) diagnostics.push({ code: "HHP-BUNDLE-ID", severity: "error", file: file2, line: 1, column: 1, message: "Screen ids normalize to the same bundle name." });
18044
18070
  const workerRel = `tier5-src/${result2.bundleName}/index.ts`;
18045
18071
  let worker = result2.worker;
18046
18072
  for (const dependency of result2.dependencies.filter((f2) => /\.(controller|service)\.tsx?$/.test(f2)))
18047
- worker = worker.replace(JSON.stringify(dependency), JSON.stringify(relative4(dirname13(join15(root, workerRel)), dependency).split(sep7).join("/").replace(/\.tsx?$/, ".js")));
18073
+ worker = worker.replace(JSON.stringify(dependency), JSON.stringify(relative4(dirname13(join15(root, workerRel)), dependency).split(sep8).join("/").replace(/\.tsx?$/, ".js")));
18048
18074
  outputs.set(workerRel, worker);
18049
18075
  }
18050
18076
  }
@@ -22150,7 +22176,6 @@ function resolveIdiom(tokens2, ctx = {}) {
22150
22176
  var IDIOM_CONTEXTS, UNSAFE_KEYS;
22151
22177
  var init_idiom = __esm({
22152
22178
  "../../core/ts/src/idiom.ts"() {
22153
- "use strict";
22154
22179
  IDIOM_CONTEXTS = {
22155
22180
  ios: { native: ["ios"] },
22156
22181
  android: { native: ["material"] },
@@ -27549,7 +27574,7 @@ function redactBodyString(body) {
27549
27574
  } catch {
27550
27575
  }
27551
27576
  }
27552
- return body.replace(FORM_ASSIGNMENT_RE, (m2, name) => isSensitiveBodyName(name) ? `${name}=${REDACTED}` : m2).replace(JSON_FIELD_RE, (m2, name, sep21) => isSensitiveBodyName(name) ? `"${name}"${sep21}"${REDACTED}"` : m2).replace(JSON_FIELD_UNTERMINATED_RE, (m2, name, sep21) => isSensitiveBodyName(name) ? `"${name}"${sep21}"${REDACTED}` : m2).replace(BEARER_RE, (_m, s2) => `${s2} ${REDACTED}`);
27577
+ return body.replace(FORM_ASSIGNMENT_RE, (m2, name) => isSensitiveBodyName(name) ? `${name}=${REDACTED}` : m2).replace(JSON_FIELD_RE, (m2, name, sep22) => isSensitiveBodyName(name) ? `"${name}"${sep22}"${REDACTED}"` : m2).replace(JSON_FIELD_UNTERMINATED_RE, (m2, name, sep22) => isSensitiveBodyName(name) ? `"${name}"${sep22}"${REDACTED}` : m2).replace(BEARER_RE, (_m, s2) => `${s2} ${REDACTED}`);
27553
27578
  }
27554
27579
  function createWixGatewayState() {
27555
27580
  return { mode: "record", rules: [], traffic: [], clients: /* @__PURE__ */ new Set(), seq: 0, ruleSeq: 0 };
@@ -32008,10 +32033,10 @@ async function runRicosImageUpload(o2) {
32008
32033
  } catch {
32009
32034
  return fail6("read");
32010
32035
  }
32011
- const sep21 = entry.uploadUrl.includes("?") ? "&" : "?";
32036
+ const sep22 = entry.uploadUrl.includes("?") ? "&" : "?";
32012
32037
  let res;
32013
32038
  try {
32014
- res = await deps.fetch(`${entry.uploadUrl}${sep21}filename=${encodeURIComponent(file2.fileName)}`, { method: "PUT", headers: { "Content-Type": file2.mimeType }, body });
32039
+ res = await deps.fetch(`${entry.uploadUrl}${sep22}filename=${encodeURIComponent(file2.fileName)}`, { method: "PUT", headers: { "Content-Type": file2.mimeType }, body });
32015
32040
  } catch {
32016
32041
  return fail6("put");
32017
32042
  }
@@ -32649,10 +32674,10 @@ async function runMediaFieldUpload(o2) {
32649
32674
  } catch (e2) {
32650
32675
  return { ok: false, reason: e2 instanceof Deadline ? "timeout" : "read" };
32651
32676
  }
32652
- const sep21 = uploadUrl.includes("?") ? "&" : "?";
32677
+ const sep22 = uploadUrl.includes("?") ? "&" : "?";
32653
32678
  let res;
32654
32679
  try {
32655
- res = await bounded2(deps.fetch(`${uploadUrl}${sep21}filename=${encodeURIComponent(file2.fileName)}`, {
32680
+ res = await bounded2(deps.fetch(`${uploadUrl}${sep22}filename=${encodeURIComponent(file2.fileName)}`, {
32656
32681
  method: "PUT",
32657
32682
  headers: { "Content-Type": file2.mimeType },
32658
32683
  body,
@@ -115774,8 +115799,8 @@ function ensureCountUpRuntime() {
115774
115799
  const neg = fixed.startsWith("-");
115775
115800
  const body = neg ? fixed.slice(1) : fixed;
115776
115801
  const [int2, frac] = body.split(".");
115777
- const sep21 = int2.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
115778
- return (neg ? "-" : "") + sep21 + (frac ? "." + frac : "");
115802
+ const sep22 = int2.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
115803
+ return (neg ? "-" : "") + sep22 + (frac ? "." + frac : "");
115779
115804
  };
115780
115805
  const animateEl = (el2) => {
115781
115806
  const node = el2;
@@ -126441,7 +126466,7 @@ var init_widgets = __esm({
126441
126466
  import { createServer as createServer2 } from "node:http";
126442
126467
  import { readFile, stat } from "node:fs/promises";
126443
126468
  import { readFileSync as readFileSync32, existsSync as existsSync28, watch, mkdirSync as mkdirSync15, rmSync as rmSync7 } from "node:fs";
126444
- import { basename as basename8, dirname as dirname20, extname as extname2, join as join24, relative as relative5, resolve as resolve28, normalize as normalize3, sep as sep8 } from "node:path";
126469
+ import { basename as basename8, dirname as dirname20, extname as extname2, join as join24, relative as relative5, resolve as resolve28, normalize as normalize3, sep as sep9 } from "node:path";
126445
126470
  import { createHash as createHash16, randomUUID as randomUUID4 } from "node:crypto";
126446
126471
  import { fileURLToPath as fileURLToPath19, pathToFileURL as pathToFileURL5 } from "node:url";
126447
126472
  function readLogicBundle(read, tier5Dir, name) {
@@ -127310,7 +127335,7 @@ function startBundleSourceWatcher(state) {
127310
127335
  try {
127311
127336
  const watcher2 = watch(TIER5_SRC_ROOT, { persistent: false, recursive: true }, (_event, filename) => {
127312
127337
  if (!filename || !/\.(ts|tsx)$/.test(filename)) return;
127313
- const name = filename.split(sep8)[0];
127338
+ const name = filename.split(sep9)[0];
127314
127339
  if (!name || name.startsWith(".")) return;
127315
127340
  pending.add(name);
127316
127341
  if (timer) clearTimeout(timer);
@@ -127458,12 +127483,12 @@ function servedTokensHash(app) {
127458
127483
  }
127459
127484
  }
127460
127485
  function appDependencyFiles(inputs, appDir3) {
127461
- const inside2 = (root, p2) => p2 === root || p2.startsWith(root + sep8);
127486
+ const inside2 = (root, p2) => p2 === root || p2.startsWith(root + sep9);
127462
127487
  const out2 = /* @__PURE__ */ new Set();
127463
127488
  for (const raw of inputs) {
127464
127489
  const p2 = resolve28(raw);
127465
127490
  if (!inside2(ROOT6, p2) || inside2(resolve28(appDir3), p2) || inside2(HMR_TMP_DIR, p2)) continue;
127466
- if (p2.split(sep8).includes("node_modules")) continue;
127491
+ if (p2.split(sep9).includes("node_modules")) continue;
127467
127492
  out2.add(p2);
127468
127493
  }
127469
127494
  return [...out2].sort();
@@ -127581,7 +127606,7 @@ function startAppSourceWatcher(state) {
127581
127606
  try {
127582
127607
  const watcher2 = watch(appDir3, { persistent: false, recursive: true }, (_event, filename) => {
127583
127608
  if (!filename) return;
127584
- const rel = String(filename).split(sep8).join("/");
127609
+ const rel = String(filename).split(sep9).join("/");
127585
127610
  if (!classifyAppPath(rel)) return;
127586
127611
  pending.add(rel);
127587
127612
  if (timer) clearTimeout(timer);
@@ -129925,7 +129950,7 @@ var init_src5 = __esm({
129925
129950
 
129926
129951
  // ../../core/serve-storage/src/file-store.ts
129927
129952
  import { mkdirSync as mkdirSync16, readFileSync as readFileSync33, rmSync as rmSync8, writeFileSync as writeFileSync10, readdirSync as readdirSync16, existsSync as existsSync29 } from "node:fs";
129928
- import { join as join25, resolve as resolve29, sep as sep9 } from "node:path";
129953
+ import { join as join25, resolve as resolve29, sep as sep10 } from "node:path";
129929
129954
  var FileReleaseStore;
129930
129955
  var init_file_store = __esm({
129931
129956
  "../../core/serve-storage/src/file-store.ts"() {
@@ -129962,7 +129987,7 @@ var init_file_store = __esm({
129962
129987
  */
129963
129988
  appDir(app) {
129964
129989
  const dir = resolve29(this.root, this.safe(app));
129965
- if (!dir.startsWith(this.root + sep9)) {
129990
+ if (!dir.startsWith(this.root + sep10)) {
129966
129991
  throw new Error(`unsafe app id ${JSON.stringify(app)}: ${dir} is not inside the store root`);
129967
129992
  }
129968
129993
  return dir;
@@ -133738,8 +133763,8 @@ async function crawlScreenInner(opts) {
133738
133763
  if (linkable.has(target)) return true;
133739
133764
  for (const base of linkable) {
133740
133765
  if (target.length > base.length + 1 && target.startsWith(base)) {
133741
- const sep21 = target[base.length];
133742
- if (sep21 === "_" || sep21 === "/") return true;
133766
+ const sep22 = target[base.length];
133767
+ if (sep22 === "_" || sep22 === "/") return true;
133743
133768
  }
133744
133769
  }
133745
133770
  return false;
@@ -137604,13 +137629,13 @@ __export(studio_exports, {
137604
137629
  });
137605
137630
  import { createHash as createHash21 } from "node:crypto";
137606
137631
  import { existsSync as existsSync33, readdirSync as readdirSync19, readFileSync as readFileSync37, realpathSync as realpathSync6, statSync as statSync7 } from "node:fs";
137607
- import { basename as basename9, extname as extname3, join as join29, relative as relative6, resolve as resolve31, sep as sep10 } from "node:path";
137632
+ import { basename as basename9, extname as extname3, join as join29, relative as relative6, resolve as resolve31, sep as sep11 } from "node:path";
137608
137633
  import { pathToFileURL as pathToFileURL6 } from "node:url";
137609
137634
  function realInside(dir, abs) {
137610
137635
  try {
137611
137636
  const root = realpathSync6(dir);
137612
137637
  const real = realpathSync6(abs);
137613
- return real.startsWith(root + sep10) ? real : null;
137638
+ return real.startsWith(root + sep11) ? real : null;
137614
137639
  } catch {
137615
137640
  return null;
137616
137641
  }
@@ -141907,21 +141932,21 @@ var init_Matcher = __esm({
141907
141932
  * @returns {string}
141908
141933
  */
141909
141934
  toString(separator, includeNamespace = true) {
141910
- const sep21 = separator || this.separator;
141911
- const isDefault = sep21 === this.separator && includeNamespace === true;
141935
+ const sep22 = separator || this.separator;
141936
+ const isDefault = sep22 === this.separator && includeNamespace === true;
141912
141937
  if (isDefault) {
141913
141938
  if (this._pathStringCache !== null) {
141914
141939
  return this._pathStringCache;
141915
141940
  }
141916
141941
  const result = this.path.map(
141917
141942
  (n2) => n2.namespace ? `${n2.namespace}:${n2.tag}` : n2.tag
141918
- ).join(sep21);
141943
+ ).join(sep22);
141919
141944
  this._pathStringCache = result;
141920
141945
  return result;
141921
141946
  }
141922
141947
  return this.path.map(
141923
141948
  (n2) => includeNamespace && n2.namespace ? `${n2.namespace}:${n2.tag}` : n2.tag
141924
- ).join(sep21);
141949
+ ).join(sep22);
141925
141950
  }
141926
141951
  /**
141927
141952
  * Get path as array of tag names.
@@ -142623,76 +142648,76 @@ var init_redos = __esm({
142623
142648
  });
142624
142649
 
142625
142650
  // ../../node_modules/is-unsafe/src/contexts/nosql.js
142626
- var sep11, NOSQL_PATTERNS, nosql_default;
142651
+ var sep12, NOSQL_PATTERNS, nosql_default;
142627
142652
  var init_nosql = __esm({
142628
142653
  "../../node_modules/is-unsafe/src/contexts/nosql.js"() {
142629
- sep11 = `["'\\s]*:`;
142654
+ sep12 = `["'\\s]*:`;
142630
142655
  NOSQL_PATTERNS = [
142631
142656
  // ─── MongoDB $ operator injection ────────────────────────────────────────
142632
142657
  {
142633
142658
  id: "nosql-where-operator",
142634
142659
  description: "$where \u2014 executes arbitrary JavaScript server-side in MongoDB",
142635
- pattern: new RegExp(`\\$where${sep11}`, "i")
142660
+ pattern: new RegExp(`\\$where${sep12}`, "i")
142636
142661
  },
142637
142662
  {
142638
142663
  id: "nosql-ne-operator",
142639
142664
  description: '$ne \u2014 "not equal" operator used to bypass equality checks',
142640
- pattern: new RegExp(`\\$ne${sep11}`, "i")
142665
+ pattern: new RegExp(`\\$ne${sep12}`, "i")
142641
142666
  },
142642
142667
  {
142643
142668
  id: "nosql-gt-operator",
142644
142669
  description: '$gt \u2014 "greater than" used to bypass password/value checks',
142645
- pattern: new RegExp(`\\$gte?${sep11}`, "i")
142670
+ pattern: new RegExp(`\\$gte?${sep12}`, "i")
142646
142671
  },
142647
142672
  {
142648
142673
  id: "nosql-lt-operator",
142649
142674
  description: '$lt / $lte \u2014 "less than" bypass variants',
142650
- pattern: new RegExp(`\\$lte?${sep11}`, "i")
142675
+ pattern: new RegExp(`\\$lte?${sep12}`, "i")
142651
142676
  },
142652
142677
  {
142653
142678
  id: "nosql-regex-operator",
142654
142679
  description: "$regex \u2014 can be used to extract data character by character (blind injection)",
142655
- pattern: new RegExp(`\\$regex${sep11}`, "i")
142680
+ pattern: new RegExp(`\\$regex${sep12}`, "i")
142656
142681
  },
142657
142682
  {
142658
142683
  id: "nosql-or-operator",
142659
142684
  description: "$or \u2014 logical OR; used to create always-true conditions",
142660
- pattern: new RegExp(`\\$or${sep11}\\s*\\[`, "i")
142685
+ pattern: new RegExp(`\\$or${sep12}\\s*\\[`, "i")
142661
142686
  },
142662
142687
  {
142663
142688
  id: "nosql-and-operator",
142664
142689
  description: "$and \u2014 logical AND operator injection",
142665
- pattern: new RegExp(`\\$and${sep11}\\s*\\[`, "i")
142690
+ pattern: new RegExp(`\\$and${sep12}\\s*\\[`, "i")
142666
142691
  },
142667
142692
  {
142668
142693
  id: "nosql-nor-operator",
142669
142694
  description: "$nor \u2014 logical NOR operator injection",
142670
- pattern: new RegExp(`\\$nor${sep11}\\s*\\[`, "i")
142695
+ pattern: new RegExp(`\\$nor${sep12}\\s*\\[`, "i")
142671
142696
  },
142672
142697
  {
142673
142698
  id: "nosql-exists-operator",
142674
142699
  description: "$exists \u2014 can enumerate fields to determine schema",
142675
- pattern: new RegExp(`\\$exists${sep11}`, "i")
142700
+ pattern: new RegExp(`\\$exists${sep12}`, "i")
142676
142701
  },
142677
142702
  {
142678
142703
  id: "nosql-in-operator",
142679
142704
  description: "$in \u2014 matches any value in a list; can enumerate values",
142680
- pattern: new RegExp(`\\$in${sep11}\\s*\\[`, "i")
142705
+ pattern: new RegExp(`\\$in${sep12}\\s*\\[`, "i")
142681
142706
  },
142682
142707
  {
142683
142708
  id: "nosql-expr-operator",
142684
142709
  description: "$expr \u2014 allows aggregation expressions in queries (MongoDB 3.6+)",
142685
- pattern: new RegExp(`\\$expr${sep11}`, "i")
142710
+ pattern: new RegExp(`\\$expr${sep12}`, "i")
142686
142711
  },
142687
142712
  {
142688
142713
  id: "nosql-function-operator",
142689
142714
  description: "$function \u2014 executes arbitrary JavaScript in MongoDB 4.4+",
142690
- pattern: new RegExp(`\\$function${sep11}`, "i")
142715
+ pattern: new RegExp(`\\$function${sep12}`, "i")
142691
142716
  },
142692
142717
  {
142693
142718
  id: "nosql-accumulator-operator",
142694
142719
  description: "$accumulator \u2014 custom aggregation with arbitrary JS execution",
142695
- pattern: new RegExp(`\\$accumulator${sep11}`, "i")
142720
+ pattern: new RegExp(`\\$accumulator${sep12}`, "i")
142696
142721
  },
142697
142722
  // ─── Prototype pollution ─────────────────────────────────────────────────
142698
142723
  {
@@ -148536,7 +148561,7 @@ __export(app_source_exports, {
148536
148561
  });
148537
148562
  import { createHash as createHash25 } from "node:crypto";
148538
148563
  import { readdirSync as readdirSync24, lstatSync as lstatSync13, readFileSync as readFileSync49, mkdirSync as mkdirSync23, writeFileSync as writeFileSync18, existsSync as existsSync42, rmSync as rmSync13, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
148539
- import { join as join38, resolve as resolve40, sep as sep12 } from "node:path";
148564
+ import { join as join38, resolve as resolve40, sep as sep13 } from "node:path";
148540
148565
  import { createRequire as createRequire3 } from "node:module";
148541
148566
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
148542
148567
  function walkSourceTree(dir, file2, skip, enter = () => {
@@ -148559,7 +148584,7 @@ function walkSourceTree(dir, file2, skip, enter = () => {
148559
148584
  skip(rel);
148560
148585
  continue;
148561
148586
  }
148562
- file2(rel.split(sep12).join("/"), rel);
148587
+ file2(rel.split(sep13).join("/"), rel);
148563
148588
  }
148564
148589
  };
148565
148590
  walk2("");
@@ -148619,8 +148644,8 @@ function renderInputRootDirs(dir) {
148619
148644
  }
148620
148645
  function newestSourceChangeMs(dir) {
148621
148646
  const roots2 = renderInputRootDirs(dir);
148622
- const inRead = (hostRel) => !roots2 || roots2.has(hostRel.split(sep12)[0]);
148623
- const counts = (hostRel) => !hostRel.includes(sep12) || inRead(hostRel);
148647
+ const inRead = (hostRel) => !roots2 || roots2.has(hostRel.split(sep13)[0]);
148648
+ const counts = (hostRel) => !hostRel.includes(sep13) || inRead(hostRel);
148624
148649
  let newest = 0;
148625
148650
  walkSourceTree(
148626
148651
  dir,
@@ -148782,7 +148807,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
148782
148807
  let empty2 = true;
148783
148808
  for (const name of readdirSync24(rel ? join38(root, rel) : root)) {
148784
148809
  const childRel = rel ? join38(rel, name) : name;
148785
- const posix5 = childRel.split(sep12).join("/");
148810
+ const posix5 = childRel.split(sep13).join("/");
148786
148811
  const kind = lstatSync13(join38(root, childRel));
148787
148812
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
148788
148813
  empty2 = false;
@@ -148866,7 +148891,7 @@ __export(run_watch_exports, {
148866
148891
  });
148867
148892
  import { closeSync as closeSync4, constants as constants3, existsSync as existsSync43, fstatSync, lstatSync as lstatSync14, statSync as statSync14, readdirSync as readdirSync25, mkdirSync as mkdirSync24, openSync as openSync4, readFileSync as readFileSync50, realpathSync as realpathSync8, renameSync as renameSync10, rmSync as rmSync14, writeFileSync as writeFileSync19, watch as fsWatch } from "node:fs";
148868
148893
  import { randomUUID as randomUUID7 } from "node:crypto";
148869
- import { extname as extname4, join as join39, relative as relative8, resolve as resolve41, sep as sep13 } from "node:path";
148894
+ import { extname as extname4, join as join39, relative as relative8, resolve as resolve41, sep as sep14 } from "node:path";
148870
148895
  function diffReleases(prev, next) {
148871
148896
  const bundles = Object.keys(next.bundles).filter((b2) => prev.bundles[b2]?.sha256 !== next.bundles[b2].sha256).sort();
148872
148897
  const changedBundles = new Set(bundles);
@@ -148894,7 +148919,7 @@ function reloadEvents(d2) {
148894
148919
  function hhpRasterDir(dir) {
148895
148920
  const assets = findAssetsDir(dir);
148896
148921
  if (!assets) return null;
148897
- const rel = relative8(dir, assets).split(sep13).join("/");
148922
+ const rel = relative8(dir, assets).split(sep14).join("/");
148898
148923
  return rel ? `${rel}/hhp-raster` : "hhp-raster";
148899
148924
  }
148900
148925
  function ignoredChange(relPath, isDirectory = () => false, rasterDir = "assets/hhp-raster") {
@@ -149121,7 +149146,7 @@ function boardFile(dir, pathname) {
149121
149146
  function readConfined(root, rel) {
149122
149147
  try {
149123
149148
  if (!lstatSync14(root).isDirectory()) return null;
149124
- const parts = rel.split(sep13);
149149
+ const parts = rel.split(sep14);
149125
149150
  let at2 = root;
149126
149151
  for (let i2 = 0; i2 < parts.length; i2++) {
149127
149152
  at2 = join39(at2, parts[i2]);
@@ -149129,7 +149154,7 @@ function readConfined(root, rel) {
149129
149154
  if (st2.isSymbolicLink() || !(i2 === parts.length - 1 ? st2.isFile() : st2.isDirectory())) return null;
149130
149155
  }
149131
149156
  const canonicalRoot = realpathSync8(root);
149132
- if (!realpathSync8(at2).startsWith(canonicalRoot + sep13)) return null;
149157
+ if (!realpathSync8(at2).startsWith(canonicalRoot + sep14)) return null;
149133
149158
  const fd = openSync4(at2, constants3.O_RDONLY | (constants3.O_NOFOLLOW ?? 0));
149134
149159
  try {
149135
149160
  if (!fstatSync(fd).isFile()) return null;
@@ -149640,13 +149665,13 @@ import { createHash as createHash28, randomUUID as randomUUID8 } from "node:cryp
149640
149665
  import { spawn as spawn4 } from "node:child_process";
149641
149666
  import { closeSync as closeSync5, existsSync as existsSync44, lstatSync as lstatSync17, readFileSync as readFileSync54, statSync as statSync15 } from "node:fs";
149642
149667
  import { createServer as createServer3 } from "node:net";
149643
- import { basename as basename14, dirname as dirname28, isAbsolute as isAbsolute4, join as join43, relative as relative9, resolve as resolve42, sep as sep14 } from "node:path";
149668
+ import { basename as basename14, dirname as dirname28, isAbsolute as isAbsolute4, join as join43, relative as relative9, resolve as resolve42, sep as sep15 } from "node:path";
149644
149669
  function homeShotPath(dir, home) {
149645
149670
  const root = resolve42(dir);
149646
149671
  const base = resolve42(root, ".himi", "shots");
149647
149672
  const shot = resolve42(base, `${home}.png`);
149648
- if (!shot.startsWith(base + sep14)) return null;
149649
- for (let p2 = shot; p2 !== root && p2.startsWith(root + sep14); p2 = dirname28(p2)) {
149673
+ if (!shot.startsWith(base + sep15)) return null;
149674
+ for (let p2 = shot; p2 !== root && p2.startsWith(root + sep15); p2 = dirname28(p2)) {
149650
149675
  try {
149651
149676
  if (lstatSync17(p2).isSymbolicLink()) return null;
149652
149677
  } catch {
@@ -155069,7 +155094,7 @@ __export(eject_exports, {
155069
155094
  ejectModule: () => ejectModule
155070
155095
  });
155071
155096
  import { existsSync as existsSync58, mkdirSync as mkdirSync27, readFileSync as readFileSync68, writeFileSync as writeFileSync23 } from "node:fs";
155072
- import { dirname as dirname33, join as join58, relative as relative10, resolve as resolve47, sep as sep15 } from "node:path";
155097
+ import { dirname as dirname33, join as join58, relative as relative10, resolve as resolve47, sep as sep16 } from "node:path";
155073
155098
  function locate(repoRootOrCwd, mod) {
155074
155099
  const inRepo = resolve47(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
155075
155100
  if (existsSync58(inRepo)) return { path: inRepo, form: "source" };
@@ -155122,7 +155147,7 @@ function ejectModule(opts) {
155122
155147
  if (!existsSync58(f2)) continue;
155123
155148
  const before = readFileSync68(f2, "utf8");
155124
155149
  let after = before;
155125
- let rel = relative10(dirname33(f2), target).split(sep15).join("/").replace(/\.tsx?$/, ".js");
155150
+ let rel = relative10(dirname33(f2), target).split(sep16).join("/").replace(/\.tsx?$/, ".js");
155126
155151
  if (!rel.startsWith(".")) rel = `./${rel}`;
155127
155152
  for (const spec of specifiersFor(mod)) {
155128
155153
  const q2 = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -155299,7 +155324,7 @@ __export(icon_exports, {
155299
155324
  });
155300
155325
  import { createHash as createHash31 } from "node:crypto";
155301
155326
  import { existsSync as existsSync59, mkdirSync as mkdirSync28, readFileSync as readFileSync69, statSync as statSync18, writeFileSync as writeFileSync24 } from "node:fs";
155302
- import { basename as basename16, dirname as dirname34, join as join59, resolve as resolve48, sep as sep16 } from "node:path";
155327
+ import { basename as basename16, dirname as dirname34, join as join59, resolve as resolve48, sep as sep17 } from "node:path";
155303
155328
  function readManifest(dir) {
155304
155329
  try {
155305
155330
  return JSON.parse(readFileSync69(join59(dir, MANIFEST), "utf8"));
@@ -155316,7 +155341,7 @@ function declaredIconName(dir) {
155316
155341
  function iconDest(dir, name) {
155317
155342
  const base = resolve48(dir);
155318
155343
  const abs = resolve48(base, name);
155319
- if (!abs.startsWith(base + sep16)) {
155344
+ if (!abs.startsWith(base + sep17)) {
155320
155345
  throw new Error(
155321
155346
  `himi icon: himalaya.content.json declares "icon": "${name}", which resolves outside the package (${abs}). Declare a path inside it, e.g. "${DEFAULT_CONTENT_ICON}".`
155322
155347
  );
@@ -155663,8 +155688,8 @@ function baseOf(target, linkable, known) {
155663
155688
  let best = null;
155664
155689
  for (const base of linkable ?? []) {
155665
155690
  if (!known.has(base) || target.length <= base.length + 1 || !target.startsWith(base)) continue;
155666
- const sep21 = target[base.length];
155667
- if ((sep21 === "_" || sep21 === "/") && (!best || base.length > best.length)) best = base;
155691
+ const sep22 = target[base.length];
155692
+ if ((sep22 === "_" || sep22 === "/") && (!best || base.length > best.length)) best = base;
155668
155693
  }
155669
155694
  return best;
155670
155695
  }
@@ -159654,10 +159679,10 @@ var init_cli_bridge = __esm({
159654
159679
  // src/mcp/file-tools.ts
159655
159680
  import { closeSync as closeSync6, constants as constants4, lstatSync as lstatSync21, mkdirSync as mkdirSync33, openSync as openSync5, readdirSync as readdirSync31, readFileSync as readFileSync81, realpathSync as realpathSync10, renameSync as renameSync11, rmSync as rmSync18, statSync as statSync19, writeSync, existsSync as existsSync69 } from "node:fs";
159656
159681
  import { randomBytes as randomBytes3 } from "node:crypto";
159657
- import { dirname as dirname39, isAbsolute as isAbsolute7, join as join72, relative as relative13, resolve as resolve56, sep as sep17 } from "node:path";
159682
+ import { dirname as dirname39, isAbsolute as isAbsolute7, join as join72, relative as relative13, resolve as resolve56, sep as sep18 } from "node:path";
159658
159683
  import * as z2 from "zod";
159659
159684
  function within(root, p2) {
159660
- return p2 === root || p2.startsWith(root.endsWith(sep17) ? root : root + sep17);
159685
+ return p2 === root || p2.startsWith(root.endsWith(sep18) ? root : root + sep18);
159661
159686
  }
159662
159687
  function realish(p2) {
159663
159688
  let cur = p2;
@@ -159665,14 +159690,14 @@ function realish(p2) {
159665
159690
  while (!existsSync69(cur)) {
159666
159691
  const up = dirname39(cur);
159667
159692
  if (up === cur) break;
159668
- tail.unshift(cur.slice(up.length + (up.endsWith(sep17) ? 0 : 1)));
159693
+ tail.unshift(cur.slice(up.length + (up.endsWith(sep18) ? 0 : 1)));
159669
159694
  cur = up;
159670
159695
  }
159671
159696
  return join72(realpathSync10(cur), ...tail);
159672
159697
  }
159673
159698
  function symlinkComponent(base, abs) {
159674
159699
  let cur = base;
159675
- for (const part3 of relative13(base, abs).split(sep17).filter(Boolean)) {
159700
+ for (const part3 of relative13(base, abs).split(sep18).filter(Boolean)) {
159676
159701
  cur = join72(cur, part3);
159677
159702
  try {
159678
159703
  if (lstatSync21(cur).isSymbolicLink()) return relative13(base, cur);
@@ -159711,7 +159736,7 @@ function onDiskSpelling(abs) {
159711
159736
  while (!existsSync69(cur)) {
159712
159737
  const up = dirname39(cur);
159713
159738
  if (up === cur) break;
159714
- tail.unshift(cur.slice(up.length + (up.endsWith(sep17) ? 0 : 1)));
159739
+ tail.unshift(cur.slice(up.length + (up.endsWith(sep18) ? 0 : 1)));
159715
159740
  cur = up;
159716
159741
  }
159717
159742
  return join72(realpathSync10.native(cur), ...tail);
@@ -159771,7 +159796,7 @@ function listFiles(appDir3) {
159771
159796
  const rel = relative13(appDir3, p2);
159772
159797
  if (isDeniedSourcePath(rel) || isDeniedSourcePath(rel.toLowerCase())) continue;
159773
159798
  if (files.length >= MAX_LISTED) return false;
159774
- files.push({ path: rel.split(sep17).join("/"), bytes: statSync19(p2).size });
159799
+ files.push({ path: rel.split(sep18).join("/"), bytes: statSync19(p2).size });
159775
159800
  }
159776
159801
  }
159777
159802
  return true;
@@ -160571,7 +160596,7 @@ __export(native_decode_exports, {
160571
160596
  runAndroidNativeDecode: () => runAndroidNativeDecode,
160572
160597
  runNativeDecode: () => runNativeDecode
160573
160598
  });
160574
- import { execFileSync as execFileSync6, spawnSync as spawnSync3 } from "node:child_process";
160599
+ import { execFileSync as execFileSync6, spawnSync as spawnSync4 } from "node:child_process";
160575
160600
  import { existsSync as existsSync73, mkdirSync as mkdirSync37, mkdtempSync as mkdtempSync5, readFileSync as readFileSync84, rmSync as rmSync20, writeFileSync as writeFileSync31 } from "node:fs";
160576
160601
  import { homedir as homedir10, tmpdir as tmpdir6 } from "node:os";
160577
160602
  import { join as join78 } from "node:path";
@@ -160591,7 +160616,7 @@ function nativeOracleShipped() {
160591
160616
  function detectNativeDecode(options = {}) {
160592
160617
  const hostPlatform = options.hostPlatform ?? process.platform;
160593
160618
  const packageDir = options.packageDir ?? nativeDecodePackageDir();
160594
- const swiftAvailable = options.swiftAvailable ?? spawnSync3("swift", ["--version"], { stdio: "ignore" }).status === 0;
160619
+ const swiftAvailable = options.swiftAvailable ?? spawnSync4("swift", ["--version"], { stdio: "ignore" }).status === 0;
160595
160620
  if (hostPlatform !== "darwin") {
160596
160621
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
160597
160622
  }
@@ -160893,7 +160918,7 @@ __export(browser_authoring_exports, {
160893
160918
  import { createServer as createServer4 } from "node:http";
160894
160919
  import { createHash as createHash38, randomBytes as randomBytes4 } from "node:crypto";
160895
160920
  import { existsSync as existsSync74, readdirSync as readdirSync33, readFileSync as readFileSync85, realpathSync as realpathSync11, statSync as statSync20 } from "node:fs";
160896
- import { join as join79, posix as posix3, relative as relative15, sep as sep18 } from "node:path";
160921
+ import { join as join79, posix as posix3, relative as relative15, sep as sep19 } from "node:path";
160897
160922
  function humanMs(ms2) {
160898
160923
  return ms2 < 1e3 ? `${ms2}ms` : `${Math.round(ms2 / 1e3)}s`;
160899
160924
  }
@@ -160943,7 +160968,7 @@ function collectRelativeSiblings(files, tier5SrcDir) {
160943
160968
  ];
160944
160969
  for (const abs of candidates) {
160945
160970
  if (!existsSync74(abs) || statSync20(abs).isDirectory()) continue;
160946
- const key3 = `/tier5-src/${relative15(tier5SrcDir, abs).split(sep18).join(posix3.sep)}`;
160971
+ const key3 = `/tier5-src/${relative15(tier5SrcDir, abs).split(sep19).join(posix3.sep)}`;
160947
160972
  if (key3 in files) break;
160948
160973
  files[key3] = readFileSync85(abs, "utf8");
160949
160974
  queue.push(key3);
@@ -164280,6 +164305,11 @@ ${releaseId}`;
164280
164305
  ...m2.templateMeta.accent ? { accent: m2.templateMeta.accent } : {},
164281
164306
  ...m2.templateMeta.palette?.length ? { palette: m2.templateMeta.palette } : {},
164282
164307
  releaseId: m2.releaseId,
164308
+ // The content address of the catalog view (`/v1/template-source`), which serve-control
164309
+ // stamps as sha256(JSON.stringify(source)). Public so tools/ci/check-published-templates.mjs
164310
+ // can tell a stale template from a current one without the source or a credential:
164311
+ // `himi template publish --source-hash` computes the same hash from a checkout.
164312
+ ...m2.sourceHash ? { sourceHash: m2.sourceHash } : {},
164283
164313
  minBinaryVersion: m2.minBinaryVersion,
164284
164314
  ...iconUrl ? { iconUrl } : {}
164285
164315
  });
@@ -165387,7 +165417,7 @@ __export(run_browser_exports, {
165387
165417
  });
165388
165418
  import { createServer as createServer6 } from "node:http";
165389
165419
  import { existsSync as existsSync84, readFileSync as readFileSync94 } from "node:fs";
165390
- import { spawnSync as spawnSync4 } from "node:child_process";
165420
+ import { spawnSync as spawnSync5 } from "node:child_process";
165391
165421
  import { dirname as dirname44, resolve as resolve68 } from "node:path";
165392
165422
  import { fileURLToPath as fileURLToPath30 } from "node:url";
165393
165423
  function stagedDir() {
@@ -165398,7 +165428,7 @@ function missingStaged() {
165398
165428
  }
165399
165429
  function ensureBrowserPlaneStaged() {
165400
165430
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
165401
- const r3 = spawnSync4(process.execPath, [resolve68(REPO2, "core/serve/scripts/stage-browser-plane.mjs")], {
165431
+ const r3 = spawnSync5(process.execPath, [resolve68(REPO2, "core/serve/scripts/stage-browser-plane.mjs")], {
165402
165432
  cwd: REPO2,
165403
165433
  encoding: "utf8"
165404
165434
  });
@@ -177494,7 +177524,7 @@ var init_analyze = __esm({
177494
177524
 
177495
177525
  // src/base44-analyze/write-artifacts.ts
177496
177526
  import { existsSync as existsSync101, mkdirSync as mkdirSync45, readFileSync as readFileSync119, rmSync as rmSync29, writeFileSync as writeFileSync44 } from "node:fs";
177497
- import { dirname as dirname52, join as join111, resolve as resolve74, sep as sep19 } from "node:path";
177527
+ import { dirname as dirname52, join as join111, resolve as resolve74, sep as sep20 } from "node:path";
177498
177528
  function isBookFolder(dir) {
177499
177529
  try {
177500
177530
  return JSON.parse(readFileSync119(join111(dir, "book.json"), "utf8")).kind === "base44-app-book";
@@ -177602,7 +177632,7 @@ function writeBase44Book(result, outDir, opts = {}) {
177602
177632
  for (const f2 of result.sourceFiles) {
177603
177633
  if (!safeRel(f2.path)) continue;
177604
177634
  const dest = join111(sourceRoot, ...f2.path.split("/"));
177605
- if (!dest.startsWith(sourceRoot + sep19)) continue;
177635
+ if (!dest.startsWith(sourceRoot + sep20)) continue;
177606
177636
  mkdirSync45(dirname52(dest), { recursive: true });
177607
177637
  writeFileSync44(dest, f2.bytes);
177608
177638
  }
@@ -184117,8 +184147,8 @@ var init_customer_build = __esm({
184117
184147
 
184118
184148
  // src/site-analyze/apply.ts
184119
184149
  import { existsSync as existsSync103, readFileSync as readFileSync121, writeFileSync as writeFileSync47, copyFileSync as copyFileSync2, mkdirSync as mkdirSync48, realpathSync as realpathSync15, statSync as statSync27 } from "node:fs";
184120
- import { dirname as dirname54, isAbsolute as isAbsolute9, join as join115, resolve as resolve78, sep as sep20 } from "node:path";
184121
- import { spawnSync as spawnSync5 } from "node:child_process";
184150
+ import { dirname as dirname54, isAbsolute as isAbsolute9, join as join115, resolve as resolve78, sep as sep21 } from "node:path";
184151
+ import { spawnSync as spawnSync6 } from "node:child_process";
184122
184152
  function unpairedRedirectNote(overlay) {
184123
184153
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
184124
184154
  return `no OAuth redirect paired with client ${overlay.wix.clientId} \u2014 the app (and prepare:customer) will refuse to start until one is set. Register it with \`himi site client ensure --site ${overlay.siteId?.trim() || "<msid>"} --redirect-uri <uri> --yes\`, then re-run analyze with \`--redirect-uri <uri>\` (or export HIMI_BRANDED_LITE_WIX_REDIRECT_URI)`;
@@ -184247,7 +184277,7 @@ function buildMergedContract(options) {
184247
184277
  const contained = (root, rel) => {
184248
184278
  if (isAbsolute9(rel)) return void 0;
184249
184279
  const full = resolve78(root, rel);
184250
- return full.startsWith(resolve78(root) + sep20) ? full : void 0;
184280
+ return full.startsWith(resolve78(root) + sep21) ? full : void 0;
184251
184281
  };
184252
184282
  const stage = (fromRel, toRel) => {
184253
184283
  const from = contained(packageDir, fromRel);
@@ -184261,7 +184291,7 @@ function buildMergedContract(options) {
184261
184291
  } catch {
184262
184292
  return void 0;
184263
184293
  }
184264
- if (!real.startsWith(realRoot + sep20)) return void 0;
184294
+ if (!real.startsWith(realRoot + sep21)) return void 0;
184265
184295
  try {
184266
184296
  if (!statSync27(real).isFile()) return void 0;
184267
184297
  mkdirSync48(dirname54(to2), { recursive: true });
@@ -184397,7 +184427,7 @@ async function applyOverlays(input) {
184397
184427
  const packageDir = dirname54(resolve78(input.overlayPath));
184398
184428
  const { path: contract, staged } = buildMergedContract({ appDir: appDir3, packageDir, overlay });
184399
184429
  wrote.push(...staged);
184400
- const r3 = spawnSync5("npm", ["run", "prepare:customer", "--", "--contract", contract], {
184430
+ const r3 = spawnSync6("npm", ["run", "prepare:customer", "--", "--contract", contract], {
184401
184431
  cwd: appDir3,
184402
184432
  encoding: "utf8"
184403
184433
  });
@@ -187471,7 +187501,8 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
187471
187501
  "refresh-wix",
187472
187502
  "empty-app-ok",
187473
187503
  "aab",
187474
- "managed"
187504
+ "managed",
187505
+ "source-hash"
187475
187506
  ]);
187476
187507
  var SWITCH_FLAGS = new Set([...BOOLEAN_FLAGS].filter((name) => name !== "deep"));
187477
187508
  var SWITCH_CONSEQUENCE = {
@@ -187481,6 +187512,7 @@ var SWITCH_CONSEQUENCE = {
187481
187512
  record: "silently ignored, it would leave you believing the fixture was refreshed",
187482
187513
  "no-oauth-app": "silently ignored, it would mint an OAuth client you asked to skip",
187483
187514
  "dry-run": "silently ignored, it would perform the real writes you asked to rehearse",
187515
+ "source-hash": "silently ignored, it would PUBLISH the template instead of printing its source hash",
187484
187516
  "allow-destructive": "silently ignored, it would refuse the destructive step you opted into",
187485
187517
  static: "silently ignored, it would render HYDRATED when you asked for static",
187486
187518
  // `--wait=false` is the one spelling where "pass it bare" is the WRONG advice: bare `--wait`
@@ -188244,7 +188276,7 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
188244
188276
  else skips it: outside CI it starts, and watch.note says why
188245
188277
  himi template list [--remote] list starter templates + metadata (title/description/tags) + each one's start prompt
188246
188278
  himi template prompt <name> print the prompt that starts an app from that template
188247
- himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted]
188279
+ himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--source-hash]
188248
188280
  publish an app AS a himi init --template on the serve plane
188249
188281
  himi template trim --keep <id,id,\u2026> | --from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force]
188250
188282
  cut a scaffolded template to the screens you keep (the home and
@@ -188992,7 +189024,7 @@ var COMMANDS = [
188992
189024
  { name: "hhp", summary: "author responsive HTML/CSS/TypeScript screens compiled to native SDUI and T5 workers; scaffold, inspect the app profile, lint or compile declared sources", required: "init <screen> | profile | lint | compile [--app <name>|--content <dir>] [--json] [--dry-run]" },
188993
189025
  { name: "captcha", summary: "create and store native CAPTCHA keys in Serve, or inspect setup status; customer setup verifies Wix site ownership; does not enable member CAPTCHA SDKs", required: "setup|status --app <app> [--content <dir>] [--site <msid>] [--ios-bundle-id <id>] [--android-package-name <id>] [--target <t>]" },
188994
189026
  { name: "init", summary: "scaffold a starter content package (OTA-only; no native shells). --template <t> starts from a full reference app (branded-lite = consumer storefront over a Wix site; owner-lite = owner CRUD/management) instead of the minimal counter; --templates lists bundled ones; --remote fetches the template off the serve plane (falls back to the bundled copy offline). The new app also gets its agent files (the same set `himi agent-init` writes: AGENTS.md, CLAUDE.md, the himalaya skill, HIMALAYA.md, a SessionStart hook) so the next session started inside it picks the work up; --no-agent skips them. It also makes the build board (.himi/studio/, exactly as `himi studio --no-open` does \u2014 it never opens a browser), so every later render, test, validate, push and progress entry refreshes it; and, for a template bundled with this CLI, it makes the FIRST PICTURE itself (never for a --remote one: that is its publisher's code, and a render runs it on this machine, so next[] leaves the render to the agent after it has read the code): exactly `himi render <homeScreenId> --png --mocks --device iphone` (offline \u2014 no request; never --live; --mocks loads the app's recorded data when it has any), run as a child it kills after a time budget, writing .himi/shots/<home>.png and journaling it (by: himi) as the STARTING POINT (\"Starting point: the <template> template, before any of your changes\") so the board and the journal hold a picture when init returns. init also records the source it wrote (.himi/init-source.json: a content fingerprint, Markdown left out), and the first home PNG render after the source diverges from it is journaled once as \"First look at your app\" \u2014 reported as `firstLook: {image}` or `{skipped: <reason>}`, never a failed init; `--no-first-look` skips it (for scripts and CI that render themselves). For a bundled template (never a --remote one \u2014 the preview would build and serve its publisher's code) init also STARTS the live preview itself: `himi run --watch --content <dir> --port <p> --no-open --idle-exit 30`, detached in its own process group with its output in .himi/watch.log, never waited for (the url is chosen before it builds), recorded in .himi/watch.json, and reported as `watch: {started, pid, url, board, log}` \u2014 or `{skipped: <reason>}` for a `CI` environment, a test runner, a watch already running, or a start that failed (never a failed init). Nothing else skips it: outside CI it is started, and `watch.note` says an opt-out was ignored. The envelope LEADS with `doNow`: three lines to run now (the consent preflight, the live preview \u2014 its url and board when init started it, else `himi run --watch --content <dir> &` with the board's path \u2014 and the postcard line), and ENDS with the same lines as `doNowAgain`, so an agent that reads only the head or only the tail (`| tail`) gets them; at a terminal they are also printed as plain text on stderr, before the JSON and again after it. Its `next[]` LEADS with the steps for THIS session, in this order for every app \u2014 the offline first render and that picture's `himi progress` postcard (only when init could not make the picture), the skill's consent preflight (authorize or mock any Wix or external startup request; with none, go straight on), and only then the live preview (the running one's url, or `himi run --watch &` when init did not start it) \u2014 ahead of the next-session guidance", required: "<name> [--dir] [--template <t>] [--remote] [--base44-app-id <id>] [--base44-app-url <https://tenant>] [--no-agent] [--no-first-look] | --templates" },
188995
- { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps). Adopting one: `inspect` writes the adoption inventory (.himi/adopt.json: screens, workers, which screens can call Wix/network on appear \u2014 bootsOffline \u2014 placeholder and sample copy, proposed Data access rows, typed-journey stubs); `screens` lists, shows, rewrites or adds ONE dev/index.ts entry; `sweep` replaces sample identity/copy inside string literals; `recommend` ranks the catalog for a business description or a site's verticals; `trim --from-spec` takes the kept screens from SPEC.md's Screen catalogue", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] | trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | inspect [--content <dir>] [--dry-run] | screens [--show <id>] [--write <id> --from <file>] [--add <id> --from <file>] | sweep --set 'Old=>New' | --map <file> [--dry-run] | recommend [--text <t>] [--verticals a,b] [--book <dir>] [--remote]" },
189027
+ { name: "template", summary: "starter-template catalog + publishing. Every template is a SHOWCASE APP \u2014 a real published release you can open running before you start from it. `list` prints available templates with metadata (title/description/tags) plus each one's start prompt \u2014 pass --remote to include the serve-plane catalog so an agent can pick one; `prompt <name>` prints just that sentence for one template (the skill URL is the public door, or the plane named by --target/--serve-url); `publish` publishes an app AS a `himi init --template` (needs a himalaya.template.json; builds a previewable release + attaches the scaffold source; --source-dir overrides which tree becomes the source, --name sets the template name; bakes a launcher icon \u2014 --icon <png>, or the committed template-icons/<name>.png \u2014 and extracts accent/palette gallery meta from the scaffold's tokens.json; --visibility unlisted keeps it out of the /v1/templates catalog \u2014 templates default to public; --source-hash prints the sourceHash and iconHash a publish would be stored under, with no build or network, and exits); `trim --keep <ids>` cuts an app scaffolded from a template down to those screens (the home, and any screen the navigation pins such as a drawer body, always stays) \u2014 it deletes the other SCREENS/TEMPLATED entries whole, prunes navigation (linkableScreens, tabBar, drawer, deep links, surfaces) and drops tier5-src workers no kept screen uses, then runs validate --strict and rolls everything back if it fails (--force keeps it, --dry-run writes nothing). A kept screen that links to a removed one is refused unless --links follow (keep those too) or --links drop (remove the taps). Adopting one: `inspect` writes the adoption inventory (.himi/adopt.json: screens, workers, which screens can call Wix/network on appear \u2014 bootsOffline \u2014 placeholder and sample copy, proposed Data access rows, typed-journey stubs); `screens` lists, shows, rewrites or adds ONE dev/index.ts entry; `sweep` replaces sample identity/copy inside string literals; `recommend` ranks the catalog for a business description or a site's verticals; `trim --from-spec` takes the kept screens from SPEC.md's Screen catalogue", required: "list [--remote] | prompt <name> | publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted] [--target <t>] [--source-hash] | trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | inspect [--content <dir>] [--dry-run] | screens [--show <id>] [--write <id> --from <file>] [--add <id> --from <file>] | sweep --set 'Old=>New' | --map <file> [--dry-run] | recommend [--text <t>] [--verticals a,b] [--book <dir>] [--remote]" },
188996
189028
  { name: "build", summary: "compile tier5-src/<name>/index.ts -> dist/bundles/<name>.bundle.js with esbuild (--binary = resolve published @himalaya/*); semantic TypeScript checking belongs to himi validate", required: "[--content <dir>] [--binary]" },
188997
189029
  { name: "functions", summary: "inspect/invoke server functions and operate durable jobs or schedules", required: "list|describe|openapi|invoke | jobs list|get|cancel|retry|dlq | schedule describe|invoke [--app <a>] [--environment preview|production] [--release <id>]" },
188998
189030
  { name: "eject", summary: "take ownership of a shared flow module (auth|checkout|screens): copies its source into tier5-src/_ejected/ and repoints this package's imports at the copy. The ladder's escape hatch \u2014 for when the factory options cannot express what you need. AFTER EJECTING, CENTRAL FIXES NO LONGER REACH THIS APP, including future corrections to the fail-closed entitlement and confirm-the-session rules. The floor is unaffected: credentials still go only through the native auth actions and money only through the cashier rail.", required: "<auth|checkout|screens> [--content <dir>]" },
@@ -189072,7 +189104,7 @@ function helpJson() {
189072
189104
 
189073
189105
  // src/self-update.ts
189074
189106
  import { readFileSync as readFileSync39, mkdirSync as mkdirSync18, writeFileSync as writeFileSync12, existsSync as existsSync35, rmSync as rmSync10, statSync as statSync8, accessSync, constants } from "node:fs";
189075
- import { execFile, execFileSync as execFileSync3, spawnSync } from "node:child_process";
189107
+ import { execFile, execFileSync as execFileSync3, spawnSync as spawnSync2 } from "node:child_process";
189076
189108
  import { randomUUID as randomUUID5 } from "node:crypto";
189077
189109
  import { homedir as homedir8, hostname } from "node:os";
189078
189110
  import { join as join31, resolve as resolve33, dirname as dirname23 } from "node:path";
@@ -189317,7 +189349,7 @@ function npmInstallArgs(registry2, version5) {
189317
189349
  return ["i", "-g", `${CLI_PACKAGE}@${version5}`, `--registry=${pinned}`, `--@wix:registry=${pinned}`];
189318
189350
  }
189319
189351
  var INSTALLABLE_SPEC = /^(?:latest|\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/;
189320
- function createUpdateRunner(spawn8 = spawnSync) {
189352
+ function createUpdateRunner(spawn8 = spawnSync2) {
189321
189353
  return {
189322
189354
  install: async (registry2, version5) => {
189323
189355
  if (!isTrustedRegistry(registry2)) return { ok: false, detail: `refusing untrusted registry ${registry2}` };
@@ -189341,7 +189373,7 @@ function createUpdateRunner(spawn8 = spawnSync) {
189341
189373
  const root = globalNodeModulesRoot();
189342
189374
  const entry = root ? join31(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
189343
189375
  if (!entry || !existsSync35(entry)) return void 0;
189344
- const result = spawnSync(process.execPath, [entry, ...argv], {
189376
+ const result = spawnSync2(process.execPath, [entry, ...argv], {
189345
189377
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
189346
189378
  stdio: "inherit",
189347
189379
  env: { ...process.env, [UPDATE_REEXEC_ENV]: "1" }
@@ -190345,6 +190377,7 @@ async function templateCommand(ctx) {
190345
190377
  }
190346
190378
  const appDir3 = resolve46(str3(flags.dir) ?? process.cwd());
190347
190379
  const sourceDir = resolve46(str3(flags["source-dir"]) ?? appDir3);
190380
+ ensureTemplatesFor(appDir3, sourceDir);
190348
190381
  let meta;
190349
190382
  try {
190350
190383
  meta = JSON.parse(readFileSync67(join57(sourceDir, TEMPLATE_META_FILE), "utf8"));
@@ -190375,6 +190408,28 @@ async function templateCommand(ctx) {
190375
190408
  const buildDir = materializeForPreview(appDir3, name, meta.title);
190376
190409
  if (buildDir !== appDir3) publishScratch.push(buildDir);
190377
190410
  const app = await appId(buildDir);
190411
+ const catalogView = () => ({ files: canonicalCatalogFiles(sentinelizeArchive(readTemplateFiles(sourceDir), app)), meta });
190412
+ const launcherIcon = () => {
190413
+ const iconPath = str3(flags.icon) ? resolve46(str3(flags.icon)) : templateIconPath(name);
190414
+ if (!iconPath) return void 0;
190415
+ try {
190416
+ const bytes2 = readFileSync67(iconPath);
190417
+ return { bytes: bytes2, hash: createHash30("sha256").update(bytes2).digest("hex") };
190418
+ } catch {
190419
+ return void 0;
190420
+ }
190421
+ };
190422
+ if (flags["source-hash"] === true) {
190423
+ print({
190424
+ ok: true,
190425
+ template: name,
190426
+ app: templateAppId,
190427
+ published: false,
190428
+ sourceHash: createHash30("sha256").update(JSON.stringify(catalogView())).digest("hex"),
190429
+ iconHash: launcherIcon()?.hash ?? null
190430
+ });
190431
+ return 0;
190432
+ }
190378
190433
  const strict = flags.strict === true;
190379
190434
  const force = flags.force === true;
190380
190435
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
@@ -190437,25 +190492,18 @@ async function templateCommand(ctx) {
190437
190492
  }));
190438
190493
  return 1;
190439
190494
  }
190440
- const iconPath = str3(flags.icon) ? resolve46(str3(flags.icon)) : templateIconPath(name);
190441
- let iconHash;
190442
- if (iconPath) {
190443
- try {
190444
- const bytes2 = readFileSync67(iconPath);
190445
- payload.icon = bytes2.toString("base64");
190446
- iconHash = createHash30("sha256").update(bytes2).digest("hex");
190447
- } catch {
190448
- }
190449
- }
190495
+ const icon = launcherIcon();
190496
+ const iconHash = icon?.hash;
190497
+ if (icon) payload.icon = icon.bytes.toString("base64");
190450
190498
  payload.app = templateAppId;
190451
190499
  const { rebindReleaseId: rebindReleaseId2 } = await Promise.resolve().then(() => (init_build3(), build_exports2));
190452
190500
  payload.manifest = rebindReleaseId2({ ...payload.manifest, app: templateAppId, ...iconHash ? { iconHash } : {} });
190453
- const files = sentinelizeArchive(readTemplateFiles(sourceDir), app);
190454
- if (Object.keys(files).length === 0) {
190501
+ const source = catalogView();
190502
+ if (Object.keys(source.files).length === 0) {
190455
190503
  io2.error(JSON.stringify({ error: `no source files collected from ${sourceDir}` }));
190456
190504
  return 1;
190457
190505
  }
190458
- payload.source = { files, meta };
190506
+ payload.source = source;
190459
190507
  const { collectAppSource: collectAppSource2, secretFindings: secretFindings2, zipAppSource: zipAppSource2, largestEntries: largestEntries2, MAX_APP_SOURCE_BYTES: MAX_APP_SOURCE_BYTES2 } = await Promise.resolve().then(() => (init_app_source(), app_source_exports));
190460
190508
  const templateArchive = collectAppSource2(sourceDir);
190461
190509
  const templateSecrets = secretFindings2(templateArchive.entries);
@@ -190611,7 +190659,7 @@ async function templateCommand(ctx) {
190611
190659
  icon: Boolean(iconHash),
190612
190660
  ...meta.accent ? { accent: meta.accent } : {},
190613
190661
  ...meta.palette ? { palette: meta.palette } : {},
190614
- sourceFiles: Object.keys(files).length,
190662
+ sourceFiles: Object.keys(source.files).length,
190615
190663
  ...hasErrors && force ? { forcedPastErrors: true } : {},
190616
190664
  initWith: `himi init <new-app-name> --template ${name} --remote${targetFlag}`
190617
190665
  });
@@ -190626,7 +190674,7 @@ async function templateCommand(ctx) {
190626
190674
  }
190627
190675
  }
190628
190676
  }
190629
- io2.error(JSON.stringify({ error: "template needs a subcommand", usage: "himi template list [--remote] | himi template prompt <name> | himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--target <t>] | himi template trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | himi template inspect | himi template screens [--show <id>] [--write <id> --from <file>] | himi template sweep --set 'Old=>New' | himi template recommend --text <description>" }));
190677
+ io2.error(JSON.stringify({ error: "template needs a subcommand", usage: "himi template list [--remote] | himi template prompt <name> | himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--target <t>] [--source-hash] | himi template trim --keep <id,id,\u2026>|--from-spec [SPEC.md] [--content <dir>] [--links follow|drop] [--dry-run] [--force] | himi template inspect | himi template screens [--show <id>] [--write <id> --from <file>] | himi template sweep --set 'Old=>New' | himi template recommend --text <description>" }));
190630
190678
  return 2;
190631
190679
  }
190632
190680
 
@@ -190844,7 +190892,7 @@ async function validateCommand(ctx) {
190844
190892
  import { readFileSync as readFileSync80 } from "node:fs";
190845
190893
 
190846
190894
  // src/dpx.ts
190847
- import { spawnSync as spawnSync2 } from "node:child_process";
190895
+ import { spawnSync as spawnSync3 } from "node:child_process";
190848
190896
  import { existsSync as existsSync67, readFileSync as readFileSync78 } from "node:fs";
190849
190897
  import { homedir as homedir9 } from "node:os";
190850
190898
  import { join as join70 } from "node:path";
@@ -190889,7 +190937,7 @@ function parseDpxAuthStatusIdentity(stdout) {
190889
190937
  return stdout.split("\n").map((l2) => l2.trim()).find(Boolean) || void 0;
190890
190938
  }
190891
190939
  var defaultRunner2 = (cmd, args) => {
190892
- const r3 = spawnSync2(cmd, args, { encoding: "utf8" });
190940
+ const r3 = spawnSync3(cmd, args, { encoding: "utf8" });
190893
190941
  return { status: r3.status, stdout: r3.stdout ?? "", stderr: r3.stderr ?? "", error: r3.error };
190894
190942
  };
190895
190943
  function dpxAuthStatus(opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1507.0",
3
+ "version": "0.1508.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -71,5 +71,5 @@
71
71
  "artifactId": "himalaya-cli"
72
72
  }
73
73
  },
74
- "falconPackageHash": "9dc5e59ce68dd5d91e8e4618f90faf1cbb7ddb11ef0532104b244413"
74
+ "falconPackageHash": "1dcc0a1ea79af010948564037a6ea00f3df198f1a6cf8c8b37d42e86"
75
75
  }