@wix/himalaya-cli 0.1069.0 → 0.1070.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
@@ -3957,8 +3957,8 @@ function committedProvisioningPath(name) {
3957
3957
  function committedShellFor(path2) {
3958
3958
  const normalized = path2.replace(/\\/g, "/");
3959
3959
  return SHELL_NAMES2.find((name) => {
3960
- const relative12 = committedProvisioningPath(name);
3961
- return normalized === relative12 || normalized.endsWith(`/${relative12}`);
3960
+ const relative13 = committedProvisioningPath(name);
3961
+ return normalized === relative13 || normalized.endsWith(`/${relative13}`);
3962
3962
  });
3963
3963
  }
3964
3964
  function isBrandable(repoRoot2, name) {
@@ -17420,15 +17420,20 @@ var init_gen_native_config = __esm({
17420
17420
  // src/progress.ts
17421
17421
  var progress_exports = {};
17422
17422
  __export(progress_exports, {
17423
+ AGENT_WINS_WITHIN_MS: () => AGENT_WINS_WITHIN_MS,
17423
17424
  APPROVALS_FILE: () => APPROVALS_FILE,
17424
17425
  APPROVAL_GATES: () => APPROVAL_GATES,
17426
+ AUTO_MILESTONES: () => AUTO_MILESTONES,
17427
+ CLI_AUTHOR: () => CLI_AUTHOR,
17425
17428
  HISTORY_DIR: () => HISTORY_DIR,
17426
17429
  JOURNEY_STEPS: () => JOURNEY_STEPS,
17427
17430
  MAX_IMAGE_BYTES: () => MAX_IMAGE_BYTES,
17428
17431
  MAX_MESSAGE_CHARS: () => MAX_MESSAGE_CHARS,
17432
+ MILESTONE_STEP: () => MILESTONE_STEP,
17429
17433
  PROGRESS_FILE: () => PROGRESS_FILE,
17430
17434
  ProgressError: () => ProgressError,
17431
17435
  UnsafeHimiDirError: () => UnsafeHimiDirError,
17436
+ agentCovers: () => agentCovers,
17432
17437
  appLabel: () => appLabel,
17433
17438
  appendApproval: () => appendApproval,
17434
17439
  appendProgress: () => appendProgress,
@@ -17438,7 +17443,10 @@ __export(progress_exports, {
17438
17443
  carryPlanningJournal: () => carryPlanningJournal,
17439
17444
  formatBrief: () => formatBrief,
17440
17445
  imageExtension: () => imageExtension,
17446
+ isCliEntry: () => isCliEntry,
17447
+ journalMilestone: () => journalMilestone,
17441
17448
  journalPath: () => journalPath,
17449
+ looksSecret: () => looksSecret,
17442
17450
  notifyDesktop: () => notifyDesktop,
17443
17451
  openApprovals: () => openApprovals,
17444
17452
  parseApprovalRef: () => parseApprovalRef,
@@ -17448,13 +17456,26 @@ __export(progress_exports, {
17448
17456
  readProgress: () => readProgress,
17449
17457
  realDirIn: () => realDirIn,
17450
17458
  summarizeProgress: () => summarizeProgress,
17459
+ visibleEntries: () => visibleEntries,
17451
17460
  writeFileAtomic: () => writeFileAtomic
17452
17461
  });
17453
17462
  import { spawn as spawn3 } from "node:child_process";
17454
17463
  import { createHash as createHash8 } from "node:crypto";
17455
17464
  import { randomUUID } from "node:crypto";
17456
17465
  import { appendFileSync, closeSync, existsSync as existsSync27, lstatSync as lstatSync5, mkdirSync as mkdirSync13, openSync, readdirSync as readdirSync15, readFileSync as readFileSync27, readSync, renameSync as renameSync3, rmSync as rmSync5, statSync as statSync5, writeFileSync as writeFileSync12 } from "node:fs";
17457
- import { join as join30, relative as relative5, resolve as resolve19, sep as sep6 } from "node:path";
17466
+ import { isAbsolute as isAbsolute3, join as join30, relative as relative5, resolve as resolve19, sep as sep6 } from "node:path";
17467
+ function agentCovers(agent, cli) {
17468
+ if (isCliEntry(agent)) return false;
17469
+ const agentShas = shasOf(agent.images);
17470
+ if (!agentShas.size) return false;
17471
+ const gap = Math.abs(Date.parse(agent.ts) - Date.parse(cli.ts));
17472
+ if (!(gap <= AGENT_WINS_WITHIN_MS)) return false;
17473
+ if (cli.step && agent.step === cli.step) return true;
17474
+ return [...shasOf(cli.images)].some((sha) => agentShas.has(sha));
17475
+ }
17476
+ function visibleEntries(entries) {
17477
+ return entries.filter((e) => !isCliEntry(e) || !entries.some((a) => agentCovers(a, e)));
17478
+ }
17458
17479
  function parseStep(raw) {
17459
17480
  const v = raw.trim().toLowerCase();
17460
17481
  if (/^[1-8]$/.test(v)) return JOURNEY_STEPS[Number(v) - 1];
@@ -17543,12 +17564,13 @@ function appendProgress(dir, input) {
17543
17564
  `a postcard is a line or two (${message.length} chars; the cap is ${MAX_MESSAGE_CHARS}) \u2014 put the detail in your reply and journal the headline`
17544
17565
  );
17545
17566
  }
17546
- const images = (input.images ?? []).map((src) => archiveImage(dir, src));
17567
+ const images = (input.images ?? []).map((src) => archiveImage(dir, src)).map((img) => input.auto ? { ...img, source: packageRelative(dir, img.source) } : img);
17547
17568
  const entry = {
17548
17569
  ts: (input.now ?? /* @__PURE__ */ new Date()).toISOString(),
17549
17570
  message,
17550
17571
  ...input.step ? { step: input.step, stepNumber: JOURNEY_STEPS.indexOf(input.step) + 1 } : {},
17551
- ...images.length ? { images } : {}
17572
+ ...images.length ? { images } : {},
17573
+ ...input.auto ? { by: CLI_AUTHOR, milestone: input.auto.milestone, ...input.auto.ref ? { ref: input.auto.ref } : {} } : {}
17552
17574
  };
17553
17575
  if (!realDirIn(dir, ".himi")) throw new ProgressError("unsafe_himi_dir", new UnsafeHimiDirError(".himi").message);
17554
17576
  const file = join30(dir, PROGRESS_FILE);
@@ -17557,6 +17579,10 @@ function appendProgress(dir, input) {
17557
17579
  `);
17558
17580
  return entry;
17559
17581
  }
17582
+ function packageRelative(dir, p) {
17583
+ const rel = relative5(dir, resolve19(p));
17584
+ return rel && !rel.startsWith("..") && !isAbsolute3(rel) ? toPosix(rel) : p;
17585
+ }
17560
17586
  function endsWithNewline(file) {
17561
17587
  if (!existsSync27(file)) return true;
17562
17588
  const size = statSync5(file).size;
@@ -17575,7 +17601,7 @@ function isEntry(v) {
17575
17601
  const e = v;
17576
17602
  return typeof e.ts === "string" && typeof e.message === "string";
17577
17603
  }
17578
- function readProgress(dir) {
17604
+ function readProgress(dir, opts = {}) {
17579
17605
  const file = join30(dir, PROGRESS_FILE);
17580
17606
  if (!existsSync27(file)) return { entries: [], skipped: 0 };
17581
17607
  const entries = [];
@@ -17590,7 +17616,34 @@ function readProgress(dir) {
17590
17616
  skipped++;
17591
17617
  }
17592
17618
  }
17593
- return { entries, skipped };
17619
+ return { entries: opts.all ? entries : visibleEntries(entries), skipped };
17620
+ }
17621
+ function journalMilestone(dir, input) {
17622
+ try {
17623
+ if (looksSecret(input.message) || input.ref !== void 0 && (!REF_SHAPE.test(input.ref) || looksSecret(input.ref))) {
17624
+ return { journaled: false, reason: "unsafe" };
17625
+ }
17626
+ const { entries } = readProgress(dir, { all: true });
17627
+ const ref = input.ref ?? null;
17628
+ if (entries.some((e) => isCliEntry(e) && e.milestone === input.milestone && (e.ref ?? null) === ref)) {
17629
+ return { journaled: false, reason: "already-journaled" };
17630
+ }
17631
+ const now = input.now ?? /* @__PURE__ */ new Date();
17632
+ const step = MILESTONE_STEP[input.milestone];
17633
+ const abs = input.image ? locateImage(dir, input.image) : null;
17634
+ const images = abs ? [{ sha256: createHash8("sha256").update(readFileSync27(abs)).digest("hex") }] : [];
17635
+ if (entries.some((e) => agentCovers(e, { ts: now.toISOString(), step, images }))) return { journaled: false, reason: "agent-journaled" };
17636
+ const entry = appendProgress(dir, {
17637
+ message: input.message,
17638
+ step,
17639
+ ...abs ? { images: [abs] } : {},
17640
+ now,
17641
+ auto: { milestone: input.milestone, ...input.ref ? { ref: input.ref } : {} }
17642
+ });
17643
+ return { journaled: true, entry };
17644
+ } catch {
17645
+ return { journaled: false, reason: "failed" };
17646
+ }
17594
17647
  }
17595
17648
  function parseGate(raw) {
17596
17649
  const v = raw.trim().toLowerCase();
@@ -17721,7 +17774,7 @@ function formatBrief(input) {
17721
17774
  if (summary.last) lines.push(`Last postcard (${minute(summary.last.ts)}): ${summary.last.message}`);
17722
17775
  const pastPlan = (summary.current?.stepNumber ?? 0) > JOURNEY_STEPS.indexOf("plan") + 1;
17723
17776
  lines.push(
17724
- !summary.current ? "Resume from the journal: `himi progress` prints every postcard." : pastPlan ? `Resume at step ${summary.current.stepNumber}. Do not restart discovery: the approved plan is SPEC.md + MOBILE-UX.md.` : `Resume at step ${summary.current.stepNumber}. Implementation has not started: treat SPEC.md + MOBILE-UX.md as a draft unless the journal records the customer approving them.`
17777
+ !summary.current ? "No journey step recorded yet: a new app starts with discovery (step 1); one whose SPEC.md the customer approved continues from its current state. `himi progress` prints every postcard." : pastPlan ? `Resume at step ${summary.current.stepNumber}. Do not restart discovery: the approved plan is SPEC.md + MOBILE-UX.md.` : `Resume at step ${summary.current.stepNumber}. Implementation has not started: treat SPEC.md + MOBILE-UX.md as a draft unless the journal records the customer approving them.`
17725
17778
  );
17726
17779
  }
17727
17780
  if (open.length) {
@@ -17735,7 +17788,7 @@ function formatBrief(input) {
17735
17788
  }
17736
17789
  function summarizeProgress(entries) {
17737
17790
  if (!entries.length) return { count: 0 };
17738
- const stepped = [...entries].reverse().find((e) => e.step && JOURNEY_STEPS.includes(e.step));
17791
+ const stepped = [...entries].reverse().find((e) => !isCliEntry(e) && e.step && JOURNEY_STEPS.includes(e.step));
17739
17792
  const first = entries[0];
17740
17793
  const last = entries[entries.length - 1];
17741
17794
  const elapsed = Date.parse(last.ts) - Date.parse(first.ts);
@@ -17796,10 +17849,16 @@ function journalPath(dir) {
17796
17849
  const rel = relative5(process.cwd(), abs);
17797
17850
  return rel && !rel.startsWith("..") ? rel : abs;
17798
17851
  }
17799
- var JOURNEY_STEPS, PROGRESS_FILE, HISTORY_DIR, APPROVALS_FILE, APPROVAL_GATES, MAX_MESSAGE_CHARS, MAX_IMAGE_BYTES, ProgressError, toPosix, UnsafeHimiDirError, gateLabel, minute, NOTIFY_TIMEOUT_MS;
17852
+ var JOURNEY_STEPS, CLI_AUTHOR, AUTO_MILESTONES, MILESTONE_STEP, AGENT_WINS_WITHIN_MS, isCliEntry, shasOf, PROGRESS_FILE, HISTORY_DIR, APPROVALS_FILE, APPROVAL_GATES, MAX_MESSAGE_CHARS, MAX_IMAGE_BYTES, ProgressError, toPosix, UnsafeHimiDirError, SECRET_SHAPES, looksSecret, REF_SHAPE, gateLabel, minute, NOTIFY_TIMEOUT_MS;
17800
17853
  var init_progress = __esm({
17801
17854
  "src/progress.ts"() {
17802
17855
  JOURNEY_STEPS = ["discover", "classify", "plan", "implement", "brand", "validate", "review", "download"];
17856
+ CLI_AUTHOR = "himi";
17857
+ AUTO_MILESTONES = ["first-look", "test", "draft"];
17858
+ MILESTONE_STEP = { "first-look": "plan", test: "validate", draft: "review" };
17859
+ AGENT_WINS_WITHIN_MS = 5 * 60 * 1e3;
17860
+ isCliEntry = (e) => e.by === CLI_AUTHOR;
17861
+ shasOf = (images) => new Set(Array.isArray(images) ? images.flatMap((i) => i && typeof i === "object" && typeof i.sha256 === "string" ? [i.sha256] : []) : []);
17803
17862
  PROGRESS_FILE = join30(".himi", "progress.jsonl");
17804
17863
  HISTORY_DIR = join30(".himi", "history");
17805
17864
  APPROVALS_FILE = join30(".himi", "approvals.jsonl");
@@ -17826,6 +17885,17 @@ var init_progress = __esm({
17826
17885
  super(`${rel} is not a real directory inside the content package (a symlink?) \u2014 refusing to write through it`);
17827
17886
  }
17828
17887
  };
17888
+ SECRET_SHAPES = [
17889
+ /:\/\//,
17890
+ // any URL: a preview link carries its bearer in the query
17891
+ /[?&#;](?:pvt|h|token|code|handover|key|secret|access_token)=/i,
17892
+ // `?h=` is the handover code
17893
+ /\bpvt[_=-]/i,
17894
+ /\beyJ[A-Za-z0-9_-]{6,}/
17895
+ // a JWT (a control token), whose header always starts `{"`
17896
+ ];
17897
+ looksSecret = (s) => SECRET_SHAPES.some((re) => re.test(s));
17898
+ REF_SHAPE = /^[A-Za-z0-9._:-]{1,128}$/;
17829
17899
  gateLabel = (gate) => APPROVAL_GATES.find((g) => g.id === gate)?.label ?? gate;
17830
17900
  minute = (iso) => `${iso.slice(0, 16).replace("T", " ")} UTC`;
17831
17901
  NOTIFY_TIMEOUT_MS = 3e3;
@@ -17894,9 +17964,9 @@ ${after}`;
17894
17964
  function blockVersion(text5) {
17895
17965
  return STAMP_RE.exec(text5)?.[1] ?? null;
17896
17966
  }
17897
- function agentsMdBody(appName) {
17967
+ function agentsMdBody(appName2) {
17898
17968
  const gates = APPROVAL_GATES.map((g) => `- \`${g.id}\` \u2014 ${g.label}`).join("\n");
17899
- return `# ${appName ? `${appName} \u2014 a Himalaya app` : "A Himalaya app"}
17969
+ return `# ${appName2 ? `${appName2} \u2014 a Himalaya app` : "A Himalaya app"}
17900
17970
 
17901
17971
  This folder is a Himalaya content package: a native iOS / Android / web app whose screens are data
17902
17972
  (JSON descriptors) plus small JavaScript workers, built and shipped with the \`himi\` CLI
@@ -18957,9 +19027,9 @@ var init_store2 = __esm({
18957
19027
  Store = class {
18958
19028
  _db;
18959
19029
  dbPath;
18960
- constructor(appName) {
19030
+ constructor(appName2) {
18961
19031
  const dataRoot = process.env.HIMALAYA_DEV_STORE_DIR ? resolve23(process.env.HIMALAYA_DEV_STORE_DIR) : resolve23(ROOT3, "core/dev-server/data");
18962
- this.dbPath = resolve23(dataRoot, `${appName}.sqlite`);
19032
+ this.dbPath = resolve23(dataRoot, `${appName2}.sqlite`);
18963
19033
  }
18964
19034
  /** The node:sqlite handle — opened, bootstrapped, and seeded on first access. */
18965
19035
  get db() {
@@ -21684,8 +21754,8 @@ var init_net_mocks_rollover = __esm({
21684
21754
  });
21685
21755
 
21686
21756
  // ../../core/dev-server/src/player-proxy.ts
21687
- function isPlayerHost(appName) {
21688
- return appName === PLAYER_APP_ID || process.env.HIMALAYA_DEV_MULTI_APP === "1";
21757
+ function isPlayerHost(appName2) {
21758
+ return appName2 === PLAYER_APP_ID || process.env.HIMALAYA_DEV_MULTI_APP === "1";
21689
21759
  }
21690
21760
  function resolveServeBase() {
21691
21761
  const override = process.env.HIMALAYA_PLAYER_SERVE_URL;
@@ -22933,6 +23003,7 @@ function shouldRevealError(args) {
22933
23003
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
22934
23004
  var init_validation = __esm({
22935
23005
  "../../core/ts/src/validation.ts"() {
23006
+ "use strict";
22936
23007
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
22937
23008
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
22938
23009
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -26533,6 +26604,7 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
26533
26604
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
26534
26605
  var init_worker_subtree = __esm({
26535
26606
  "../../core/ts/src/worker-subtree.ts"() {
26607
+ "use strict";
26536
26608
  init_worker_subtree_safe_types_generated();
26537
26609
  WORKER_SUBTREE_CEILINGS = Object.freeze({
26538
26610
  maxEncodedBytes: 65536,
@@ -33194,8 +33266,8 @@ function clampRule(rule, denyPrefixes, maxTTL) {
33194
33266
  ttl: Math.min(rule.ttl, maxTTL)
33195
33267
  };
33196
33268
  }
33197
- function resolveWixCachePolicy(appName) {
33198
- const appRules = CACHE_OVERRIDES[appName] ?? [];
33269
+ function resolveWixCachePolicy(appName2) {
33270
+ const appRules = CACHE_OVERRIDES[appName2] ?? [];
33199
33271
  const rules = appRules.map((r) => clampRule(r, PLATFORM_DENY_PREFIXES, PLATFORM_MAX_TTL));
33200
33272
  return { rules, defaultCacheable: false };
33201
33273
  }
@@ -33313,7 +33385,7 @@ function expandNavBundle(ref, ctx) {
33313
33385
  };
33314
33386
  }
33315
33387
  function deriveNavigationConfig(app, bundleCtx) {
33316
- const appName = app.name;
33388
+ const appName2 = app.name;
33317
33389
  const concrete = app.screenIds.map((screenId) => ({
33318
33390
  screenId,
33319
33391
  isPublic: true,
@@ -33326,7 +33398,7 @@ function deriveNavigationConfig(app, bundleCtx) {
33326
33398
  const keys = (pat.match(/\{([^}]+)\}/g) ?? []).map((m) => m.slice(1, -1));
33327
33399
  return { screenId: base, isPublic: true, paramKeys: keys, parentScreenId: "", tabId: "" };
33328
33400
  });
33329
- const ov = NAV_OVERRIDES[appName];
33401
+ const ov = NAV_OVERRIDES[appName2];
33330
33402
  if (ov?.parents) {
33331
33403
  for (const s of [...concrete, ...templated]) {
33332
33404
  if (ov.parents[s.screenId]) s.parentScreenId = ov.parents[s.screenId];
@@ -33337,7 +33409,7 @@ function deriveNavigationConfig(app, bundleCtx) {
33337
33409
  scheme: app.urlScheme,
33338
33410
  deepLinkPatterns: app.deepLinkPatterns ?? ov?.deepLinkPatterns ?? [],
33339
33411
  linkableScreens,
33340
- wixCachePolicy: resolveWixCachePolicy(appName),
33412
+ wixCachePolicy: resolveWixCachePolicy(appName2),
33341
33413
  // Fail-open baseline; tightened OTA via the admin app-config override. The
33342
33414
  // DESTINATION(s) are derived generically from the app's config (no app-name
33343
33415
  // special-casing) so the app chooses where telemetry goes via OTA:
@@ -33415,12 +33487,12 @@ function buildAppConfigDocument(req, meta, override, bundleCtx) {
33415
33487
  return { config, body, etag, locale };
33416
33488
  }
33417
33489
  function handleAppConfigRequest(req, res, meta, override, bundleCtx) {
33418
- const appName = decodeURIComponent(
33490
+ const appName2 = decodeURIComponent(
33419
33491
  (req.url ?? "").replace(/^\/v1\/app-config\//, "").split("?")[0]
33420
33492
  );
33421
- if (appName !== meta.name) {
33493
+ if (appName2 !== meta.name) {
33422
33494
  res.writeHead(404, { "Content-Type": "application/json" });
33423
- res.end(JSON.stringify({ error: "unknown_app", appName }));
33495
+ res.end(JSON.stringify({ error: "unknown_app", appName: appName2 }));
33424
33496
  return;
33425
33497
  }
33426
33498
  const { body, etag, locale } = buildAppConfigDocument(req, meta, override, bundleCtx);
@@ -36377,7 +36449,7 @@ async function handleToggleEval(req, res, state) {
36377
36449
  for (const id of requested) out[id] = state.toggles[id] ?? false;
36378
36450
  sendJson(res, 200, out);
36379
36451
  }
36380
- function evalContextFromReq(req, appName) {
36452
+ function evalContextFromReq(req, appName2) {
36381
36453
  const h = (k) => {
36382
36454
  const v = req.headers[k];
36383
36455
  return typeof v === "string" ? v : void 0;
@@ -36385,7 +36457,7 @@ function evalContextFromReq(req, appName) {
36385
36457
  return {
36386
36458
  sessionId: h("x-himalaya-session") ?? "dev-session",
36387
36459
  userId: h("x-himalaya-user"),
36388
- appId: appName,
36460
+ appId: appName2,
36389
36461
  platform: h("x-himalaya-platform"),
36390
36462
  appVersion: h("x-himalaya-app-version"),
36391
36463
  locale: typeof req.headers["accept-language"] === "string" ? req.headers["accept-language"].split(",")[0] : void 0
@@ -37533,8 +37605,8 @@ async function handleActionHandlerBundleGet(res, app) {
37533
37605
  }
37534
37606
  async function serveStatic(req, res, root, prefix) {
37535
37607
  const requested = decodeURIComponent((req.url ?? "").split("?")[0]);
37536
- const relative12 = requested.replace(prefix, "").replace(/^\//, "");
37537
- const safe = normalize3(relative12).replace(/^(\.\.[\/\\])+/, "");
37608
+ const relative13 = requested.replace(prefix, "").replace(/^\//, "");
37609
+ const safe = normalize3(relative13).replace(/^(\.\.[\/\\])+/, "");
37538
37610
  const fullPath = resolve31(root, safe);
37539
37611
  if (!fullPath.startsWith(root)) {
37540
37612
  return send(res, 403, "forbidden");
@@ -37698,19 +37770,19 @@ function handleAssetLinks(res, state) {
37698
37770
  });
37699
37771
  }
37700
37772
  function createDevServer(opts = {}) {
37701
- const appName = opts.app ?? "sample";
37773
+ const appName2 = opts.app ?? "sample";
37702
37774
  if (!opts.preloaded) {
37703
- throw new Error(`createDevServer requires a preloaded module for "${appName}". Preload via loadApp("${appName}").`);
37775
+ throw new Error(`createDevServer requires a preloaded module for "${appName2}". Preload via loadApp("${appName2}").`);
37704
37776
  }
37705
37777
  checkRegistry(opts.preloaded.module.actions);
37706
37778
  const port = opts.port ?? Number(process.env.HIMALAYA_DEV_PORT ?? 8080);
37707
37779
  const hotReloadEnabled = opts.enableHotReload ?? process.env.HIMALAYA_DEV_HOT_RELOAD === "1";
37708
37780
  const state = {
37709
- appName,
37781
+ appName: appName2,
37710
37782
  // Store construction is cheap now; its node:sqlite DB is opened LAZILY on first query
37711
37783
  // (store.ts), so the serve authoring plane can boot createDevServer on a Whirlwind guest
37712
37784
  // that has no node:sqlite — the stateless build/render path never queries the demo store.
37713
- store: new Store(appName),
37785
+ store: new Store(appName2),
37714
37786
  toggles: {
37715
37787
  pinned_notes_v2: false,
37716
37788
  weekly_review_v1: true,
@@ -37731,7 +37803,7 @@ function createDevServer(opts = {}) {
37731
37803
  localOverrides: /* @__PURE__ */ new Map(),
37732
37804
  localOverrideHistory: /* @__PURE__ */ new Map(),
37733
37805
  app: opts.preloaded,
37734
- moduleStore: opts.preloaded ? opts.preloaded.module.createStore(appName) : void 0,
37806
+ moduleStore: opts.preloaded ? opts.preloaded.module.createStore(appName2) : void 0,
37735
37807
  telemetryEvents: [],
37736
37808
  adminUrl: process.env.HIMALAYA_ADMIN_URL,
37737
37809
  wix: createWixGatewayState(),
@@ -42298,7 +42370,7 @@ async function buildFromLoaded(preloaded, dest, opts = {}) {
42298
42370
  ...collect2?.lint ? { lint, workers } : {},
42299
42371
  ...collect2?.render ? { renders } : {},
42300
42372
  ...collect2?.descriptors ? { descriptors: baked, linkableScreenIds: [...linkable] } : {},
42301
- ...collect2?.descriptors && typeof preloaded.meta.homeScreenId === "string" && preloaded.meta.homeScreenId ? { homeScreenId: preloaded.meta.homeScreenId } : {},
42373
+ ...typeof preloaded.meta.homeScreenId === "string" && preloaded.meta.homeScreenId ? { homeScreenId: preloaded.meta.homeScreenId } : {},
42302
42374
  ...renderTokens !== void 0 ? { tokens: renderTokens } : {},
42303
42375
  ...preloaded.meta.surfaces ? { surfaceNav: preloaded.meta.surfaces } : {},
42304
42376
  auth: (() => {
@@ -42595,7 +42667,7 @@ h2{font-size:12px;font-weight:600;color:var(--muted);text-transform:uppercase;le
42595
42667
  .sw{display:flex;gap:6px}.sw span{width:22px;height:22px;border-radius:50%;border:1px solid var(--line)}
42596
42668
  .nav{display:flex;flex-wrap:wrap;gap:6px}.nav span{font-size:12px;padding:3px 8px;border-radius:6px;background:var(--bg);border:1px solid var(--line)}
42597
42669
  .log{list-style:none;margin:0;padding:0}.log li{display:flex;gap:10px;padding:10px 0;border-top:1px solid var(--line)}.log li:first-child{border-top:0}
42598
- .log .t{color:var(--faint);font-size:12px;min-width:64px;font-variant-numeric:tabular-nums}.log .s{font-size:11px;color:var(--muted);text-transform:capitalize}.log .imgs{display:flex;gap:6px;margin-top:6px}.log .imgs img{height:72px;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
42670
+ .log .t{color:var(--faint);font-size:12px;min-width:64px;font-variant-numeric:tabular-nums}.log .s{font-size:11px;color:var(--muted);text-transform:capitalize}.log .by{font-size:11px;color:var(--faint)}.log .imgs{display:flex;gap:6px;margin-top:6px}.log .imgs img{height:72px;border-radius:8px;border:1px solid var(--line);cursor:zoom-in}
42599
42671
  .empty{color:var(--faint);font-size:13px;padding:8px 0}
42600
42672
  #lb{position:fixed;inset:0;background:rgba(0,0,0,.72);display:none;align-items:center;justify-content:center;z-index:9;cursor:zoom-out}#lb img{max-height:92vh;max-width:92vw;border-radius:14px;background:#fff}
42601
42673
  .enter{animation:enter .7s cubic-bezier(.2,.8,.2,1) both}@keyframes enter{from{opacity:0;transform:translateY(10px) scale(.94);filter:grayscale(1)}to{opacity:1;transform:none;filter:none}}
@@ -42724,7 +42796,7 @@ function render(s){
42724
42796
  s.steps.forEach(function(name,i){var d=el("div","step"+(i+1<n?" done":i+1===n?" cur":""));d.appendChild(el("div","bar"));d.appendChild(el("div","lbl",(i+1)+". "+name));steps.appendChild(d)});
42725
42797
  var last=s.journal[0];
42726
42798
  $("nowmsg").textContent=last?last.message:"Waiting for the first postcard.";
42727
- $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+ago(last.ts)):"Now";
42799
+ $("nowmeta").textContent=last?("Now \xB7 "+(last.step?("step "+last.stepNumber+" of 8, "+last.step+" \xB7 "):"")+(last.by?"noted by "+last.by+" \xB7 ":"")+ago(last.ts)):"Now";
42728
42800
  var film=$("film");clear(film);
42729
42801
  if(!rewinding){var n0=s.timelineFrames||0;$("scrub").hidden=n0<2;$("slider").max=String(Math.max(1,n0-1));$("slider").value=$("slider").max}
42730
42802
  if(rewinding){paintStill(s)}else{$("still").hidden=true;$("focus").hidden=false;var focus=$("focus");
@@ -42756,7 +42828,7 @@ function render(s){
42756
42828
  if(s.app.palette.length){var sw=el("div","sw");s.app.palette.forEach(function(c){var d=el("span");d.style.background=c.hex;d.title=c.name+" "+c.hex;sw.appendChild(d)});brand.appendChild(sw)}
42757
42829
  var nav=$("nav");clear(nav);$("navcard").style.display=s.nav.length&&!s.map?"":"none";drawMap(s.map);s.nav.forEach(function(e){nav.appendChild(el("span",null,e.from+" \u2192 "+e.to))});
42758
42830
  var log=$("log");clear(log);if(!s.journal.length)log.appendChild(el("li","empty","Postcards the agent journals appear here."));
42759
- s.journal.forEach(function(j){var li=el("li");li.appendChild(el("div","t",hhmm(j.ts)));var body=el("div");body.appendChild(el("div",null,j.message));if(j.step)body.appendChild(el("div","s","step "+j.stepNumber+" \xB7 "+j.step));
42831
+ s.journal.forEach(function(j){var li=el("li");li.appendChild(el("div","t",hhmm(j.ts)));var body=el("div");body.appendChild(el("div",null,j.message));if(j.step)body.appendChild(el("div","s","step "+j.stepNumber+" \xB7 "+j.step));if(j.by)body.appendChild(el("div","by","noted by "+j.by));
42760
42832
  if(j.images.length){var row=el("div","imgs");j.images.forEach(function(u){row.appendChild(img(u))});body.appendChild(row)}li.appendChild(body);log.appendChild(li)});
42761
42833
  paintPop(s);
42762
42834
  }
@@ -43529,9 +43601,10 @@ function buildView(dir, memory, now) {
43529
43601
  journal: entries.slice(-MAX_JOURNAL).reverse().map((e) => ({
43530
43602
  ts: e.ts,
43531
43603
  message: e.message,
43532
- step: e.step ?? null,
43533
- stepNumber: e.stepNumber ?? null,
43534
- images: (e.images ?? []).map((i) => historyUrl(basename10(i.file)))
43604
+ step: isCliEntry(e) ? null : e.step ?? null,
43605
+ stepNumber: isCliEntry(e) ? null : e.stepNumber ?? null,
43606
+ images: (e.images ?? []).map((i) => historyUrl(basename10(i.file))),
43607
+ by: isCliEntry(e) ? CLI_AUTHOR : null
43535
43608
  })),
43536
43609
  screens,
43537
43610
  focus,
@@ -44779,6 +44852,109 @@ var init_notify_setup = __esm({
44779
44852
  }
44780
44853
  });
44781
44854
 
44855
+ // src/milestones.ts
44856
+ var milestones_exports = {};
44857
+ __export(milestones_exports, {
44858
+ afterDraftPush: () => afterDraftPush,
44859
+ afterRenderPng: () => afterRenderPng,
44860
+ afterTestRun: () => afterTestRun,
44861
+ displayPath: () => displayPath,
44862
+ homeScreenIdOf: () => homeScreenIdOf,
44863
+ openBoardForSession: () => openBoardForSession,
44864
+ progressCommand: () => progressCommand,
44865
+ sessionSteps: () => sessionSteps,
44866
+ testPostcardMessage: () => testPostcardMessage
44867
+ });
44868
+ import { existsSync as existsSync44, readFileSync as readFileSync48 } from "node:fs";
44869
+ import { basename as basename12, isAbsolute as isAbsolute4, join as join42, relative as relative8 } from "node:path";
44870
+ function homeScreenIdOf(dir) {
44871
+ let src;
44872
+ try {
44873
+ src = readFileSync48(join42(dir, "dev", "index.ts"), "utf8");
44874
+ } catch {
44875
+ return null;
44876
+ }
44877
+ const m = /^[ \t]*homeScreenId[ \t]*:[ \t]*(?:"((?:[^"\\\n]|\\.)*)"|'((?:[^'\\\n]|\\.)*)')/m.exec(src);
44878
+ const id = m ? m[1] ?? m[2] : void 0;
44879
+ return id && /^[A-Za-z0-9_.\-/{}]+$/.test(id) ? id : null;
44880
+ }
44881
+ function displayPath(p, cwd = process.cwd()) {
44882
+ const rel = relative8(cwd, p);
44883
+ return rel && !rel.startsWith("..") && !isAbsolute4(rel) ? rel : p;
44884
+ }
44885
+ function progressCommand(opts) {
44886
+ return `himi progress "<${opts.what}>"${opts.step ? ` --step ${opts.step}` : ""}${opts.image ? ` --image ${shellQuote(opts.image)}` : ""}${contentArg(opts.content)}`;
44887
+ }
44888
+ function sessionSteps(opts) {
44889
+ const content = displayPath(opts.dir, opts.cwd);
44890
+ const home = opts.home ?? "<homeScreenId>";
44891
+ const shot = `.himi/shots/${home}.png`;
44892
+ return [
44893
+ `himi run --watch${contentArg(content)} & \u2014 now, in this session: keep it running in the background. The live preview repaints on every save and serves the build board at <its url>/__himi/studio/`,
44894
+ `himi render ${shellQuote(home)} --png --device iphone${contentArg(content)} \u2014 the first picture, written to ${shot}${opts.home ? "" : " (the id is the dev module's homeScreenId)"}; the CLI journals it as the first look`,
44895
+ `${progressCommand({ what: "where the build starts", step: "plan", image: shot, content })} \u2014 your postcard for the owner, with that picture`
44896
+ ];
44897
+ }
44898
+ async function openBoardForSession(dir, cwd = process.cwd()) {
44899
+ try {
44900
+ const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
44901
+ const { page, url } = openStudio2(dir);
44902
+ const rel = relative8(cwd, page);
44903
+ return { page: rel && !rel.startsWith("..") && !isAbsolute4(rel) ? rel : page, url, opened: false };
44904
+ } catch (e) {
44905
+ return { opened: false, error: `the build board was not created: ${e.message} \u2014 \`himi studio --no-open\` in the app retries it` };
44906
+ }
44907
+ }
44908
+ function journal(dir, milestone, message, extra) {
44909
+ const r = journalMilestone(dir, { milestone, message, ...extra });
44910
+ return r.journaled ? { journaled: true, milestone, message } : void 0;
44911
+ }
44912
+ function afterRenderPng(dir, input) {
44913
+ const postcard = input.renderable && input.homeScreenId !== null && input.screenId === input.homeScreenId ? journal(dir, "first-look", `First look at ${appName(dir)}: the home screen, where the build starts.`, { image: input.pngFile, ...input.now ? { now: input.now } : {} }) : void 0;
44914
+ return {
44915
+ ...postcard ? { postcard } : {},
44916
+ next: input.renderable ? [progressCommand({ what: "what changed", image: displayPath(input.pngFile), content: input.content })] : []
44917
+ };
44918
+ }
44919
+ function testPostcardMessage(s) {
44920
+ const links = s.broken === void 0 ? "" : `, ${plural(s.broken, "broken link")}`;
44921
+ const unreached = s.unreached ? `, ${plural(s.unreached, "screen")} nothing links to` : "";
44922
+ return `Test crawl: ${plural(s.screens, "screen")} and ${plural(s.actions, "action")} exercised${links}${unreached} \u2014 ${s.errors ? `${plural(s.errors, "error")} to fix` : "no errors"}.`;
44923
+ }
44924
+ function afterTestRun(dir, input) {
44925
+ const postcard = journal(dir, "test", testPostcardMessage(input), {
44926
+ ref: input.runId,
44927
+ ...input.homeShot && existsSync44(input.homeShot) ? { image: input.homeShot } : {},
44928
+ ...input.now ? { now: input.now } : {}
44929
+ });
44930
+ return {
44931
+ ...postcard ? { postcard } : {},
44932
+ next: [
44933
+ input.boardOpen ? "the build board's App map now shows every screen this crawl reached, how they link, and any broken link \u2014 .himi/studio/index.html, or /__himi/studio/ under `himi run --watch`" : "`himi studio` opens the build board, whose App map draws every screen this crawl reached, how they link, and any broken link"
44934
+ ]
44935
+ };
44936
+ }
44937
+ function afterDraftPush(dir, input) {
44938
+ const postcard = journal(dir, "draft", `Draft pushed: release ${input.releaseId.slice(0, 12)} of ${input.app} \u2014 owner-private, not live.`, {
44939
+ ref: input.releaseId,
44940
+ ...input.now ? { now: input.now } : {}
44941
+ });
44942
+ return {
44943
+ ...postcard ? { postcard } : {},
44944
+ next: `${progressCommand({ what: "what the owner can review in this draft", step: "review", content: input.content })} \u2014 your postcard for the draft; share the preview link in chat, never in the journal`
44945
+ };
44946
+ }
44947
+ var contentArg, plural, appName;
44948
+ var init_milestones = __esm({
44949
+ "src/milestones.ts"() {
44950
+ init_progress();
44951
+ init_shell_quote();
44952
+ contentArg = (content) => content ? ` --content ${shellQuote(content)}` : "";
44953
+ plural = (n, one, many = `${one}s`) => `${n} ${n === 1 ? one : many}`;
44954
+ appName = (dir) => appLabel(dir) ?? basename12(dir);
44955
+ }
44956
+ });
44957
+
44782
44958
  // src/site-analyze/types.ts
44783
44959
  function isRedirectUri(value) {
44784
44960
  return /^[a-z][a-z0-9+.-]*:\/\/\S*$/i.test(value);
@@ -45216,8 +45392,8 @@ __export(template_trim_exports, {
45216
45392
  routeMatcher: () => routeMatcher,
45217
45393
  runTemplateTrim: () => runTemplateTrim
45218
45394
  });
45219
- import { cpSync, existsSync as existsSync44, mkdirSync as mkdirSync20, readdirSync as readdirSync24, readFileSync as readFileSync48, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync16 } from "node:fs";
45220
- import { dirname as dirname25, join as join42 } from "node:path";
45395
+ import { cpSync, existsSync as existsSync45, mkdirSync as mkdirSync20, readdirSync as readdirSync24, readFileSync as readFileSync49, renameSync as renameSync5, rmSync as rmSync8, statSync as statSync10, writeFileSync as writeFileSync16 } from "node:fs";
45396
+ import { dirname as dirname25, join as join43 } from "node:path";
45221
45397
  function routeMatcher(pattern) {
45222
45398
  const detail = /^\^([A-Za-z0-9_]+)\(\?:\[_\/\]\(\.\+\)\)\?\$$/.exec(pattern);
45223
45399
  if (detail) {
@@ -45911,20 +46087,20 @@ function homeModulesPass(app, kept, edited) {
45911
46087
  return { text: patched.src, dropped: patched.droppedModules };
45912
46088
  }
45913
46089
  function listWorkers(dir) {
45914
- const root = join42(dir, "tier5-src");
45915
- if (!existsSync44(root)) return { dirs: [], files: {} };
45916
- const dirs = readdirSync24(root).filter((n) => statSync10(join42(root, n)).isDirectory()).sort();
46090
+ const root = join43(dir, "tier5-src");
46091
+ if (!existsSync45(root)) return { dirs: [], files: {} };
46092
+ const dirs = readdirSync24(root).filter((n) => statSync10(join43(root, n)).isDirectory()).sort();
45917
46093
  const files = {};
45918
46094
  const walk2 = (abs, rel) => {
45919
46095
  for (const name of readdirSync24(abs)) {
45920
46096
  if (name === "node_modules") continue;
45921
- const p = join42(abs, name);
46097
+ const p = join43(abs, name);
45922
46098
  const r = `${rel}/${name}`;
45923
46099
  if (statSync10(p).isDirectory()) walk2(p, r);
45924
- else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync48(p, "utf8");
46100
+ else if (/\.(?:[cm]?[jt]sx?|json)$/.test(name)) files[r] = readFileSync49(p, "utf8");
45925
46101
  }
45926
46102
  };
45927
- for (const d of dirs) walk2(join42(root, d), `tier5-src/${d}`);
46103
+ for (const d of dirs) walk2(join43(root, d), `tier5-src/${d}`);
45928
46104
  return { dirs, files };
45929
46105
  }
45930
46106
  function csv(v) {
@@ -45947,16 +46123,16 @@ async function runTemplateTrim(req, deps) {
45947
46123
  links = req.links;
45948
46124
  }
45949
46125
  const dir = req.dir;
45950
- const devPath = join42(dir, "dev/index.ts");
45951
- const configPath = join42(dir, "config.ts");
45952
- if (!existsSync44(devPath) || !existsSync44(configPath)) {
46126
+ const devPath = join43(dir, "dev/index.ts");
46127
+ const configPath = join43(dir, "config.ts");
46128
+ if (!existsSync45(devPath) || !existsSync45(configPath)) {
45953
46129
  return refuse3(new TrimRefusal(`${dir} has no dev/index.ts + config.ts \u2014 point --content at an app scaffolded with \`himi init --template\``));
45954
46130
  }
45955
46131
  if (isTemplateSourceTree(dir)) {
45956
46132
  return refuse3(new TrimRefusal(`${dir} is a template source tree (its identity is still __HIMI_APP_*__ sentinels) \u2014 trim the app \`himi init --template\` scaffolds from it, not the template itself`));
45957
46133
  }
45958
- const dev = readFileSync48(devPath, "utf8");
45959
- const config = readFileSync48(configPath, "utf8");
46134
+ const dev = readFileSync49(devPath, "utf8");
46135
+ const config = readFileSync49(configPath, "utf8");
45960
46136
  const workers = listWorkers(dir);
45961
46137
  let plan;
45962
46138
  try {
@@ -45966,23 +46142,23 @@ async function runTemplateTrim(req, deps) {
45966
46142
  throw e;
45967
46143
  }
45968
46144
  if (req.dryRun) return { code: 0, result: { ok: true, dryRun: true, ...plan.report, validate: null } };
45969
- const hadHimi = existsSync44(join42(dir, ".himi"));
45970
- const backupRel = join42(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
45971
- const backup = join42(dir, backupRel);
46145
+ const hadHimi = existsSync45(join43(dir, ".himi"));
46146
+ const backupRel = join43(".himi", "trim-backup", (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
46147
+ const backup = join43(dir, backupRel);
45972
46148
  const originals = [[devPath, "dev/index.ts"], [configPath, "config.ts"]];
45973
46149
  const moved = [];
45974
46150
  const restore = () => {
45975
46151
  for (const [path2, rel] of originals) {
45976
- const original = readFileSync48(join42(backup, rel));
45977
- if (!existsSync44(path2) || !readFileSync48(path2).equals(original)) writeFileSync16(path2, original);
46152
+ const original = readFileSync49(join43(backup, rel));
46153
+ if (!existsSync45(path2) || !readFileSync49(path2).equals(original)) writeFileSync16(path2, original);
45978
46154
  }
45979
- for (const name of [...moved].reverse()) renameSync5(join42(backup, "tier5-src", name), join42(dir, "tier5-src", name));
46155
+ for (const name of [...moved].reverse()) renameSync5(join43(backup, "tier5-src", name), join43(dir, "tier5-src", name));
45980
46156
  moved.length = 0;
45981
46157
  };
45982
46158
  const apply = () => {
45983
46159
  const moveDir = deps.moveDir ?? renameSync5;
45984
46160
  for (const name of plan.removedWorkers) {
45985
- moveDir(join42(dir, "tier5-src", name), join42(backup, "tier5-src", name));
46161
+ moveDir(join43(dir, "tier5-src", name), join43(backup, "tier5-src", name));
45986
46162
  moved.push(name);
45987
46163
  }
45988
46164
  if (plan.dev !== dev) writeFileSync16(devPath, plan.dev);
@@ -45990,8 +46166,8 @@ async function runTemplateTrim(req, deps) {
45990
46166
  };
45991
46167
  const dropBackup = () => {
45992
46168
  rmSync8(backup, { recursive: true, force: true });
45993
- for (const d of [dirname25(backup), join42(dir, ".himi")]) {
45994
- if (d === join42(dir, ".himi") && hadHimi) break;
46169
+ for (const d of [dirname25(backup), join43(dir, ".himi")]) {
46170
+ if (d === join43(dir, ".himi") && hadHimi) break;
45995
46171
  try {
45996
46172
  if (!readdirSync24(d).length) rmSync8(d, { recursive: true });
45997
46173
  } catch {
@@ -46017,9 +46193,9 @@ async function runTemplateTrim(req, deps) {
46017
46193
  return { code: 1, result: { ok: false, error: `${error} (${message}); every file was restored`, rolledBack: true, ...base(validate4) } };
46018
46194
  };
46019
46195
  try {
46020
- mkdirSync20(join42(backup, "dev"), { recursive: true });
46021
- for (const [path2, rel] of originals) cpSync(path2, join42(backup, rel));
46022
- if (plan.removedWorkers.length) mkdirSync20(join42(backup, "tier5-src"), { recursive: true });
46196
+ mkdirSync20(join43(backup, "dev"), { recursive: true });
46197
+ for (const [path2, rel] of originals) cpSync(path2, join43(backup, rel));
46198
+ if (plan.removedWorkers.length) mkdirSync20(join43(backup, "tier5-src"), { recursive: true });
46023
46199
  } catch (e) {
46024
46200
  dropBackup();
46025
46201
  return { code: 1, result: { ok: false, error: `could not back up the app before trimming it (${e.message}); nothing was changed`, rolledBack: true, ...base() } };
@@ -46032,7 +46208,7 @@ async function runTemplateTrim(req, deps) {
46032
46208
  let failure;
46033
46209
  let validate3;
46034
46210
  try {
46035
- const written = parseApp(readFileSync48(devPath, "utf8"), readFileSync48(configPath, "utf8"));
46211
+ const written = parseApp(readFileSync49(devPath, "utf8"), readFileSync49(configPath, "utf8"));
46036
46212
  const leftovers = leftoverNavRefs(written.nav.value, plan.removedIds);
46037
46213
  if (leftovers.length) failure = { error: "navigation still names screens this trim removed", leftovers };
46038
46214
  else {
@@ -46069,7 +46245,7 @@ async function runTemplateTrim(req, deps) {
46069
46245
  else if (compared) {
46070
46246
  failure = {
46071
46247
  ...failure,
46072
- error: `himi validate --strict failed on the trimmed app \u2014 the trim introduced ${plural(compared.introduced.length, "error")} the untrimmed app does not have: ${summarize3(compared.introduced)}`,
46248
+ error: `himi validate --strict failed on the trimmed app \u2014 the trim introduced ${plural2(compared.introduced.length, "error")} the untrimmed app does not have: ${summarize3(compared.introduced)}`,
46073
46249
  introducedErrors: compared.introduced,
46074
46250
  preExistingErrors: compared.preExisting
46075
46251
  };
@@ -46085,7 +46261,7 @@ async function runTemplateTrim(req, deps) {
46085
46261
  ...base(validate3),
46086
46262
  ...preExisting.length ? {
46087
46263
  preExistingErrors: preExisting,
46088
- note: `trimmed; ${plural(preExisting.length, "error")} ${preExisting.length === 1 ? "was" : "were"} already present before trim: ${summarize3(preExisting)}`
46264
+ note: `trimmed; ${plural2(preExisting.length, "error")} ${preExisting.length === 1 ? "was" : "were"} already present before trim: ${summarize3(preExisting)}`
46089
46265
  } : {}
46090
46266
  }
46091
46267
  };
@@ -46136,7 +46312,7 @@ function summarize3(errors) {
46136
46312
  const shown2 = errors.slice(0, 5).map(one).join("; ");
46137
46313
  return errors.length > 5 ? `${shown2}; and ${errors.length - 5} more` : shown2;
46138
46314
  }
46139
- var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf2, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3, plural;
46315
+ var TrimRefusal, LIT, ATOM, QUANT, CAP, SAFE_ROUTE_PATTERN, lineOf2, isStatic, entryLabel, isNode3, asItems, NAV_SCREEN_KEYS, sorted, refuse3, plural2;
46140
46316
  var init_template_trim = __esm({
46141
46317
  "src/template-trim.ts"() {
46142
46318
  init_validate_descriptor();
@@ -46170,7 +46346,7 @@ var init_template_trim = __esm({
46170
46346
  NAV_SCREEN_KEYS = /* @__PURE__ */ new Set(["screenId", "parentScreenId", "contentScreenId", "badgeScreenId"]);
46171
46347
  sorted = (xs) => [...new Set(xs)].sort();
46172
46348
  refuse3 = (e) => ({ code: 2, result: { ok: false, error: e.message, ...e.detail } });
46173
- plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
46349
+ plural2 = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
46174
46350
  }
46175
46351
  });
46176
46352
 
@@ -46800,8 +46976,8 @@ __export(pack_exports, {
46800
46976
  });
46801
46977
  import { createHash as createHash22 } from "node:crypto";
46802
46978
  import { deflateRawSync, inflateRawSync } from "node:zlib";
46803
- import { existsSync as existsSync45, mkdirSync as mkdirSync21, readFileSync as readFileSync49, readdirSync as readdirSync25, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
46804
- import { dirname as dirname26, join as join43, resolve as resolve37 } from "node:path";
46979
+ import { existsSync as existsSync46, mkdirSync as mkdirSync21, readFileSync as readFileSync50, readdirSync as readdirSync25, rmSync as rmSync9, statSync as statSync11, writeFileSync as writeFileSync17 } from "node:fs";
46980
+ import { dirname as dirname26, join as join44, resolve as resolve37 } from "node:path";
46805
46981
  function writeZip(entries) {
46806
46982
  const sorted2 = entries.slice().sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
46807
46983
  const total = sorted2.reduce((n, e) => n + e.bytes.length, 0);
@@ -46895,13 +47071,13 @@ function inflateEntry(buf, entry) {
46895
47071
  return bytes2;
46896
47072
  }
46897
47073
  function readIf(path2) {
46898
- return existsSync45(path2) && statSync11(path2).isFile() ? readFileSync49(path2) : null;
47074
+ return existsSync46(path2) && statSync11(path2).isFile() ? readFileSync50(path2) : null;
46899
47075
  }
46900
47076
  function listDir(dir) {
46901
- return existsSync45(dir) ? readdirSync25(dir).filter((f) => statSync11(join43(dir, f)).isFile()) : [];
47077
+ return existsSync46(dir) ? readdirSync25(dir).filter((f) => statSync11(join44(dir, f)).isFile()) : [];
46902
47078
  }
46903
47079
  function gatherReleaseEntries(appDir2) {
46904
- const manifestPath = join43(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
47080
+ const manifestPath = join44(appDir2, HIMI_PACKAGE_MANIFEST_PATH);
46905
47081
  const manifestBytes = readIf(manifestPath);
46906
47082
  if (!manifestBytes) throw new Error(`no ${HIMI_PACKAGE_MANIFEST_PATH} in ${appDir2} \u2014 build a release there first`);
46907
47083
  const manifest = JSON.parse(manifestBytes.toString("utf8"));
@@ -46925,7 +47101,7 @@ function gatherReleaseEntries(appDir2) {
46925
47101
  }
46926
47102
  const missing = [];
46927
47103
  for (const rel of wanted) {
46928
- const bytes2 = readIf(join43(appDir2, rel));
47104
+ const bytes2 = readIf(join44(appDir2, rel));
46929
47105
  if (bytes2) entries.push({ path: rel, bytes: bytes2 });
46930
47106
  else missing.push(rel);
46931
47107
  }
@@ -46936,12 +47112,12 @@ function gatherReleaseEntries(appDir2) {
46936
47112
  );
46937
47113
  }
46938
47114
  for (const name of SINGLETONS) {
46939
- const bytes2 = readIf(join43(appDir2, name));
47115
+ const bytes2 = readIf(join44(appDir2, name));
46940
47116
  if (bytes2) entries.push({ path: name, bytes: bytes2 });
46941
47117
  }
46942
47118
  const skipped = [];
46943
47119
  for (const sub of ["screens", "bundles", "assets", "widgets"]) {
46944
- for (const f of listDir(join43(appDir2, sub))) {
47120
+ for (const f of listDir(join44(appDir2, sub))) {
46945
47121
  if (!wanted.has(`${sub}/${f}`)) skipped.push(`${sub}/${f}`);
46946
47122
  }
46947
47123
  }
@@ -46974,18 +47150,18 @@ function buildEntries(appDir2, app, opts) {
46974
47150
  return { all, header, manifest, skipped };
46975
47151
  }
46976
47152
  function packRelease(releaseRoot, app, out, opts = {}) {
46977
- const appDir2 = join43(resolve37(releaseRoot), app);
47153
+ const appDir2 = join44(resolve37(releaseRoot), app);
46978
47154
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
46979
47155
  const buf = writeZip(all);
46980
- const isDir2 = existsSync45(out) && statSync11(out).isDirectory();
46981
- const outFile = isDir2 ? join43(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
47156
+ const isDir2 = existsSync46(out) && statSync11(out).isDirectory();
47157
+ const outFile = isDir2 ? join44(resolve37(out), packageFileName(app, header.releaseId)) : resolve37(out);
46982
47158
  mkdirSync21(dirname26(outFile), { recursive: true });
46983
47159
  writeFileSync17(outFile, buf);
46984
47160
  return { app, releaseId: header.releaseId, header, out: outFile, bytes: buf.length, entryCount: all.length, skipped };
46985
47161
  }
46986
47162
  function resolvePackageDirDest(outDir, app, releaseId) {
46987
47163
  const packagedApp = (dir) => {
46988
- const bytes2 = readIf(join43(dir, HIMI_PACKAGE_HEADER_PATH));
47164
+ const bytes2 = readIf(join44(dir, HIMI_PACKAGE_HEADER_PATH));
46989
47165
  if (!bytes2) return void 0;
46990
47166
  try {
46991
47167
  return JSON.parse(bytes2.toString("utf8")).app;
@@ -46994,15 +47170,15 @@ function resolvePackageDirDest(outDir, app, releaseId) {
46994
47170
  }
46995
47171
  };
46996
47172
  const replaceable = (dir) => {
46997
- if (!existsSync45(dir)) return true;
47173
+ if (!existsSync46(dir)) return true;
46998
47174
  if (!statSync11(dir).isDirectory()) return false;
46999
47175
  return readdirSync25(dir).length === 0 || packagedApp(dir) === app;
47000
47176
  };
47001
- if (existsSync45(outDir) && !statSync11(outDir).isDirectory()) {
47177
+ if (existsSync46(outDir) && !statSync11(outDir).isDirectory()) {
47002
47178
  throw new Error(`refusing to write a package tree over ${outDir} \u2014 it is a file, not a directory`);
47003
47179
  }
47004
47180
  if (replaceable(outDir)) return outDir;
47005
- const child = join43(outDir, packageDirName(app, releaseId));
47181
+ const child = join44(outDir, packageDirName(app, releaseId));
47006
47182
  if (!replaceable(child)) {
47007
47183
  throw new Error(
47008
47184
  `refusing to replace ${child}: it is not empty and is not a ${app} package (no ${HIMI_PACKAGE_HEADER_PATH}). Pass --out to a new or empty directory.`
@@ -47016,13 +47192,13 @@ function packReleaseToDir(releaseRoot, app, outDir, opts = {}) {
47016
47192
  "a package carrying the app's source has no --format dir form: readers that predate the source entry drop it before hashing a directory and refuse the package as tampered. Pack it as a .himi archive."
47017
47193
  );
47018
47194
  }
47019
- const appDir2 = join43(resolve37(releaseRoot), app);
47195
+ const appDir2 = join44(resolve37(releaseRoot), app);
47020
47196
  const { all, header, skipped } = buildEntries(appDir2, app, opts);
47021
47197
  const dest = resolvePackageDirDest(resolve37(outDir), app, header.releaseId);
47022
47198
  rmSync9(dest, { recursive: true, force: true });
47023
47199
  let bytes2 = 0;
47024
47200
  for (const entry of all) {
47025
- const target = join43(dest, entry.path);
47201
+ const target = join44(dest, entry.path);
47026
47202
  mkdirSync21(dirname26(target), { recursive: true });
47027
47203
  writeFileSync17(target, entry.bytes);
47028
47204
  bytes2 += entry.bytes.length;
@@ -47045,21 +47221,21 @@ function openPackageBuffer(buf) {
47045
47221
  return openFromEntries(readZip(buf), { requireHeader: true });
47046
47222
  }
47047
47223
  function openPackageFile(file) {
47048
- return openPackageBuffer(readFileSync49(file));
47224
+ return openPackageBuffer(readFileSync50(file));
47049
47225
  }
47050
47226
  function openPackageDir(dir) {
47051
47227
  const root = resolve37(dir);
47052
47228
  const entries = /* @__PURE__ */ new Map();
47053
47229
  const walk2 = (rel) => {
47054
- const abs = rel ? join43(root, rel) : root;
47230
+ const abs = rel ? join44(root, rel) : root;
47055
47231
  for (const name of readdirSync25(abs)) {
47056
47232
  const childRel = rel ? `${rel}/${name}` : name;
47057
- const childAbs = join43(abs, name);
47233
+ const childAbs = join44(abs, name);
47058
47234
  if (statSync11(childAbs).isDirectory()) walk2(childRel);
47059
- else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync49(childAbs));
47235
+ else if (isSafeEntryName(childRel)) entries.set(childRel, readFileSync50(childAbs));
47060
47236
  }
47061
47237
  };
47062
- if (!existsSync45(root)) throw new Error(`no such package directory: ${root}`);
47238
+ if (!existsSync46(root)) throw new Error(`no such package directory: ${root}`);
47063
47239
  walk2("");
47064
47240
  const manifestBytes = entries.get(HIMI_PACKAGE_MANIFEST_PATH);
47065
47241
  if (manifestBytes) {
@@ -47101,10 +47277,10 @@ function unpackTo(buf, dest, appFallback) {
47101
47277
  if (!app) throw new Error("package declares no app id (no himi-package.json and no manifest.app)");
47102
47278
  if (!isSafeAppId(app)) throw new Error(`package declares an unsafe app id ${JSON.stringify(app)} \u2014 refusing to unpack it`);
47103
47279
  const root = resolve37(dest);
47104
- const appDir2 = join43(root, app);
47280
+ const appDir2 = join44(root, app);
47105
47281
  rmSync9(appDir2, { recursive: true, force: true });
47106
47282
  for (const [rel, bytes2] of entries) {
47107
- const target = join43(appDir2, rel);
47283
+ const target = join44(appDir2, rel);
47108
47284
  mkdirSync21(dirname26(target), { recursive: true });
47109
47285
  writeFileSync17(target, bytes2);
47110
47286
  }
@@ -47140,20 +47316,20 @@ __export(app_source_exports, {
47140
47316
  writeConflicts: () => writeConflicts,
47141
47317
  zipAppSource: () => zipAppSource
47142
47318
  });
47143
- import { readdirSync as readdirSync26, lstatSync as lstatSync8, readFileSync as readFileSync50, mkdirSync as mkdirSync22, writeFileSync as writeFileSync18, existsSync as existsSync46, rmSync as rmSync10, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
47144
- import { join as join44, resolve as resolve38, sep as sep10 } from "node:path";
47319
+ import { readdirSync as readdirSync26, lstatSync as lstatSync8, readFileSync as readFileSync51, mkdirSync as mkdirSync22, writeFileSync as writeFileSync18, existsSync as existsSync47, rmSync as rmSync10, rmdirSync as rmdirSync2, unlinkSync as unlinkSync3 } from "node:fs";
47320
+ import { join as join45, resolve as resolve38, sep as sep10 } from "node:path";
47145
47321
  import { deflateRawSync as deflateRawSync2 } from "node:zlib";
47146
47322
  function walkSourceTree(dir, file, skip) {
47147
47323
  const walk2 = (relDir) => {
47148
- for (const name of readdirSync26(join44(dir, relDir))) {
47149
- const rel = relDir ? join44(relDir, name) : name;
47150
- const st = lstatSync8(join44(dir, rel));
47324
+ for (const name of readdirSync26(join45(dir, relDir))) {
47325
+ const rel = relDir ? join45(relDir, name) : name;
47326
+ const st = lstatSync8(join45(dir, rel));
47151
47327
  if (st.isSymbolicLink()) {
47152
47328
  skip(rel);
47153
47329
  continue;
47154
47330
  }
47155
47331
  if (st.isDirectory()) {
47156
- if (isDeniedSourceDir(name, relDir) || existsSync46(join44(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
47332
+ if (isDeniedSourceDir(name, relDir) || existsSync47(join45(dir, rel, PACKAGE_DIR_MARKER))) skip(rel);
47157
47333
  else walk2(rel);
47158
47334
  continue;
47159
47335
  }
@@ -47171,7 +47347,7 @@ function collectAppSource(dir) {
47171
47347
  const skipped = [];
47172
47348
  walkSourceTree(
47173
47349
  dir,
47174
- (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync50(join44(dir, hostRel)) }),
47350
+ (archivePath, hostRel) => entries.push({ path: archivePath, bytes: readFileSync51(join45(dir, hostRel)) }),
47175
47351
  (hostRel) => skipped.push(hostRel)
47176
47352
  );
47177
47353
  entries.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
@@ -47225,13 +47401,13 @@ function writeAppSource(entries, destDir) {
47225
47401
  const segments = entry.path.split("/");
47226
47402
  let dir = root;
47227
47403
  for (const segment of segments.slice(0, -1)) {
47228
- dir = join44(dir, segment);
47404
+ dir = join45(dir, segment);
47229
47405
  const kind = pathKind(dir);
47230
47406
  if (kind?.isSymbolicLink()) unlinkSync3(dir);
47231
47407
  else if (kind) continue;
47232
47408
  mkdirSync22(dir);
47233
47409
  }
47234
- const target = join44(root, ...segments);
47410
+ const target = join45(root, ...segments);
47235
47411
  if (pathKind(target)?.isSymbolicLink()) unlinkSync3(target);
47236
47412
  writeFileSync18(target, entry.bytes);
47237
47413
  written.push(entry.path);
@@ -47239,7 +47415,7 @@ function writeAppSource(entries, destDir) {
47239
47415
  return written;
47240
47416
  }
47241
47417
  function staleSourcePaths(entries, destDir) {
47242
- if (!existsSync46(destDir)) return [];
47418
+ if (!existsSync47(destDir)) return [];
47243
47419
  const archived = new Set(entries.map((e) => e.path));
47244
47420
  const stale = [];
47245
47421
  walkSourceTree(destDir, (archivePath) => {
@@ -47266,7 +47442,7 @@ function writeConflicts(entries, destDir) {
47266
47442
  let dir = root;
47267
47443
  let stop = false;
47268
47444
  for (const [i, segment] of segments.slice(0, -1).entries()) {
47269
- dir = join44(dir, segment);
47445
+ dir = join45(dir, segment);
47270
47446
  const kind2 = pathKind(dir);
47271
47447
  if (!kind2 || kind2.isSymbolicLink()) {
47272
47448
  stop = true;
@@ -47278,7 +47454,7 @@ function writeConflicts(entries, destDir) {
47278
47454
  break;
47279
47455
  }
47280
47456
  if (stop) continue;
47281
- const kind = pathKind(join44(root, ...segments));
47457
+ const kind = pathKind(join45(root, ...segments));
47282
47458
  if (kind && !kind.isSymbolicLink() && kind.isDirectory()) {
47283
47459
  say(entry.path, "is a directory here, but the release has a file at that path");
47284
47460
  }
@@ -47288,22 +47464,22 @@ function writeConflicts(entries, destDir) {
47288
47464
  function removeStaleSource(entries, destDir) {
47289
47465
  const root = resolve38(destDir);
47290
47466
  const removed = staleSourcePaths(entries, root);
47291
- for (const rel of removed) rmSync10(join44(root, ...rel.split("/")), { force: true });
47467
+ for (const rel of removed) rmSync10(join45(root, ...rel.split("/")), { force: true });
47292
47468
  const needed = /* @__PURE__ */ new Set();
47293
47469
  for (const entry of entries) {
47294
47470
  if (isDeniedSourcePath(entry.path)) continue;
47295
47471
  const segments = entry.path.split("/");
47296
47472
  for (let i = 1; i < segments.length; i++) needed.add(segments.slice(0, i).join("/"));
47297
47473
  }
47298
- if (existsSync46(root)) pruneEmptyDirs(root, "", needed, removed);
47474
+ if (existsSync47(root)) pruneEmptyDirs(root, "", needed, removed);
47299
47475
  return removed.sort();
47300
47476
  }
47301
47477
  function pruneEmptyDirs(root, rel, needed, removed) {
47302
47478
  let empty = true;
47303
- for (const name of readdirSync26(rel ? join44(root, rel) : root)) {
47304
- const childRel = rel ? join44(rel, name) : name;
47479
+ for (const name of readdirSync26(rel ? join45(root, rel) : root)) {
47480
+ const childRel = rel ? join45(rel, name) : name;
47305
47481
  const posix4 = childRel.split(sep10).join("/");
47306
- const kind = lstatSync8(join44(root, childRel));
47482
+ const kind = lstatSync8(join45(root, childRel));
47307
47483
  if (kind.isSymbolicLink() || !kind.isDirectory() || isDeniedSourceDir(name, rel)) {
47308
47484
  empty = false;
47309
47485
  continue;
@@ -47313,7 +47489,7 @@ function pruneEmptyDirs(root, rel, needed, removed) {
47313
47489
  empty = false;
47314
47490
  continue;
47315
47491
  }
47316
- rmdirSync2(join44(root, childRel));
47492
+ rmdirSync2(join45(root, childRel));
47317
47493
  removed.push(`${posix4}/`);
47318
47494
  }
47319
47495
  return empty;
@@ -47368,13 +47544,13 @@ __export(eject_exports, {
47368
47544
  EJECTABLE: () => EJECTABLE,
47369
47545
  ejectModule: () => ejectModule
47370
47546
  });
47371
- import { existsSync as existsSync47, mkdirSync as mkdirSync23, readFileSync as readFileSync51, writeFileSync as writeFileSync19 } from "node:fs";
47372
- import { dirname as dirname27, join as join45, relative as relative8, resolve as resolve39, sep as sep11 } from "node:path";
47547
+ import { existsSync as existsSync48, mkdirSync as mkdirSync23, readFileSync as readFileSync52, writeFileSync as writeFileSync19 } from "node:fs";
47548
+ import { dirname as dirname27, join as join46, relative as relative9, resolve as resolve39, sep as sep11 } from "node:path";
47373
47549
  function locate(repoRootOrCwd, mod) {
47374
47550
  const inRepo = resolve39(repoRootOrCwd, `stdlib/flows/${mod}/src/index.ts`);
47375
- if (existsSync47(inRepo)) return { path: inRepo, form: "source" };
47551
+ if (existsSync48(inRepo)) return { path: inRepo, form: "source" };
47376
47552
  const dist = resolve39(repoRootOrCwd, `node_modules/@wix/himalaya/dist/${mod}.mjs`);
47377
- if (existsSync47(dist)) return { path: dist, form: "bundle" };
47553
+ if (existsSync48(dist)) return { path: dist, form: "bundle" };
47378
47554
  return null;
47379
47555
  }
47380
47556
  function specifiersFor(mod) {
@@ -47398,7 +47574,7 @@ function ejectModule(opts) {
47398
47574
  };
47399
47575
  }
47400
47576
  const ext = found.form === "source" ? "ts" : "mjs";
47401
- const target = join45(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
47577
+ const target = join46(contentDir2, "tier5-src", "_ejected", `${mod}.${ext}`);
47402
47578
  mkdirSync23(dirname27(target), { recursive: true });
47403
47579
  const banner = `// EJECTED from @wix/himalaya/${mod}. This app owns this file now.
47404
47580
  //
@@ -47412,17 +47588,17 @@ function ejectModule(opts) {
47412
47588
  // gate still applies. Ejecting buys layout and flow, never the floor.
47413
47589
 
47414
47590
  `;
47415
- const normalized = readFileSync51(found.path, "utf8").replace(
47591
+ const normalized = readFileSync52(found.path, "utf8").replace(
47416
47592
  /(["'])@himalaya\/([a-z0-9-]+(?:\/[a-z0-9-]+)?)\1/g,
47417
47593
  (_m, q, sub) => `${q}@wix/himalaya/${sub}${q}`
47418
47594
  );
47419
47595
  writeFileSync19(target, banner + normalized);
47420
47596
  const rewired = [];
47421
47597
  for (const f of files) {
47422
- if (!existsSync47(f)) continue;
47423
- const before = readFileSync51(f, "utf8");
47598
+ if (!existsSync48(f)) continue;
47599
+ const before = readFileSync52(f, "utf8");
47424
47600
  let after = before;
47425
- let rel = relative8(dirname27(f), target).split(sep11).join("/").replace(/\.tsx?$/, ".js");
47601
+ let rel = relative9(dirname27(f), target).split(sep11).join("/").replace(/\.tsx?$/, ".js");
47426
47602
  if (!rel.startsWith(".")) rel = `./${rel}`;
47427
47603
  for (const spec of specifiersFor(mod)) {
47428
47604
  const q = spec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -47540,8 +47716,8 @@ var init_app_map = __esm({
47540
47716
  });
47541
47717
 
47542
47718
  // src/wix-writes.ts
47543
- import { existsSync as existsSync48, readFileSync as readFileSync52 } from "node:fs";
47544
- import { join as join46 } from "node:path";
47719
+ import { existsSync as existsSync49, readFileSync as readFileSync53 } from "node:fs";
47720
+ import { join as join47 } from "node:path";
47545
47721
  function isProvenRead(method, path2) {
47546
47722
  if (/[#\x00-\x1F\x7F]/.test(path2)) return false;
47547
47723
  const verb = method.toUpperCase();
@@ -47622,11 +47798,11 @@ function parseDataAccess(text5, from) {
47622
47798
  }
47623
47799
  function readDataAccess(dir) {
47624
47800
  for (const name of ["SPEC.md", "MOBILE-UX.md"]) {
47625
- const p = join46(dir, name);
47626
- if (!existsSync48(p)) continue;
47801
+ const p = join47(dir, name);
47802
+ if (!existsSync49(p)) continue;
47627
47803
  let text5;
47628
47804
  try {
47629
- text5 = readFileSync52(p, "utf8");
47805
+ text5 = readFileSync53(p, "utf8");
47630
47806
  } catch {
47631
47807
  continue;
47632
47808
  }
@@ -47733,14 +47909,14 @@ __export(test_exports, {
47733
47909
  scopeDescriptors: () => scopeDescriptors,
47734
47910
  writeRecordedMocks: () => writeRecordedMocks
47735
47911
  });
47736
- import { existsSync as existsSync49, mkdirSync as mkdirSync24, readFileSync as readFileSync53, writeFileSync as writeFileSync20 } from "node:fs";
47737
- import { join as join47, resolve as resolve40 } from "node:path";
47912
+ import { existsSync as existsSync50, mkdirSync as mkdirSync24, readFileSync as readFileSync54, writeFileSync as writeFileSync20 } from "node:fs";
47913
+ import { join as join48, resolve as resolve40 } from "node:path";
47738
47914
  function readTestConfig(dir) {
47739
47915
  const p = resolve40(dir, TEST_CONFIG_FILE);
47740
- if (!existsSync49(p)) return {};
47916
+ if (!existsSync50(p)) return {};
47741
47917
  let parsed;
47742
47918
  try {
47743
- parsed = JSON.parse(readFileSync53(p, "utf8"));
47919
+ parsed = JSON.parse(readFileSync54(p, "utf8"));
47744
47920
  } catch (e) {
47745
47921
  throw new Error(`${TEST_CONFIG_FILE} is not valid JSON \u2014 ${e.message}`);
47746
47922
  }
@@ -47787,7 +47963,7 @@ function readTestConfig(dir) {
47787
47963
  function readNetRules(path2) {
47788
47964
  let parsed;
47789
47965
  try {
47790
- parsed = JSON.parse(readFileSync53(path2, "utf8"));
47966
+ parsed = JSON.parse(readFileSync54(path2, "utf8"));
47791
47967
  } catch (e) {
47792
47968
  throw new Error(`--net ${path2} could not be read as JSON \u2014 ${e.message}`);
47793
47969
  }
@@ -47808,7 +47984,7 @@ function readNetRules(path2) {
47808
47984
  function defaultNetRules(dir) {
47809
47985
  for (const rel of ["dev/net-mocks.json", "tests/net-mocks.json"]) {
47810
47986
  const p = resolve40(dir, rel);
47811
- if (existsSync49(p)) {
47987
+ if (existsSync50(p)) {
47812
47988
  const rules = readNetRules(p);
47813
47989
  if (rules.length) return { rules, from: rel };
47814
47990
  }
@@ -47824,7 +48000,7 @@ function readCannedResponses() {
47824
48000
  ];
47825
48001
  for (const url of candidates) {
47826
48002
  try {
47827
- const parsed = JSON.parse(readFileSync53(url, "utf8"));
48003
+ const parsed = JSON.parse(readFileSync54(url, "utf8"));
47828
48004
  const byKey = {};
47829
48005
  for (const [k, v] of Object.entries(parsed)) {
47830
48006
  if (!k.startsWith("//")) byKey[k] = v;
@@ -47914,8 +48090,8 @@ function writeRecordedMocks(opts) {
47914
48090
  mkdir: (p) => mkdirSync24(p, { recursive: true }),
47915
48091
  write: (p, s) => writeFileSync20(p, s)
47916
48092
  };
47917
- fs.mkdir(join47(opts.dir, "dev"));
47918
- fs.write(join47(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
48093
+ fs.mkdir(join48(opts.dir, "dev"));
48094
+ fs.write(join48(opts.dir, "dev", "net-mocks.json"), JSON.stringify(opts.mocks, null, 2) + "\n");
47919
48095
  return { ok: true, rules, to: "dev/net-mocks.json" };
47920
48096
  }
47921
48097
  function scopeDescriptors(descriptors, requestedIds, invalidScreens) {
@@ -48418,14 +48594,14 @@ var mobile_ux_lint_exports = {};
48418
48594
  __export(mobile_ux_lint_exports, {
48419
48595
  mobileUxIssues: () => mobileUxIssues
48420
48596
  });
48421
- import { existsSync as existsSync50, readFileSync as readFileSync54 } from "node:fs";
48422
- import { join as join48 } from "node:path";
48597
+ import { existsSync as existsSync51, readFileSync as readFileSync55 } from "node:fs";
48598
+ import { join as join49 } from "node:path";
48423
48599
  function mobileUxIssues(dir, strict) {
48424
- const file = join48(dir, "MOBILE-UX.md");
48425
- if (!existsSync50(file)) return [];
48600
+ const file = join49(dir, "MOBILE-UX.md");
48601
+ if (!existsSync51(file)) return [];
48426
48602
  let text5;
48427
48603
  try {
48428
- text5 = readFileSync54(file, "utf8");
48604
+ text5 = readFileSync55(file, "utf8");
48429
48605
  } catch {
48430
48606
  return [];
48431
48607
  }
@@ -50100,8 +50276,8 @@ __export(functions_exports, {
50100
50276
  resolveFunctionsTarget: () => resolveFunctionsTarget,
50101
50277
  runRemoteFunctionOperation: () => runRemoteFunctionOperation
50102
50278
  });
50103
- import { readFileSync as readFileSync55, existsSync as existsSync51 } from "node:fs";
50104
- import { join as join49, resolve as resolve41 } from "node:path";
50279
+ import { readFileSync as readFileSync56, existsSync as existsSync52 } from "node:fs";
50280
+ import { join as join50, resolve as resolve41 } from "node:path";
50105
50281
  function resolveFunctionsTarget(input) {
50106
50282
  const explicitUrl = input.functionsUrl?.trim();
50107
50283
  if (explicitUrl) return { mode: "remote", baseUrl: explicitUrl };
@@ -50120,9 +50296,9 @@ async function loadFunctionsApp(dir) {
50120
50296
  }
50121
50297
  const sources = {};
50122
50298
  for (const definition of config.functions.functions) {
50123
- const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join49(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync51);
50299
+ const source = [".ts", ".js", ".mts", ".mjs"].map((extension) => join50(loaded.dir, "functions", `${definition.name}${extension}`)).find(existsSync52);
50124
50300
  if (!source) throw new Error(`missing source for function ${definition.name}; expected functions/${definition.name}.ts`);
50125
- sources[definition.name] = readFileSync55(source, "utf8");
50301
+ sources[definition.name] = readFileSync56(source, "utf8");
50126
50302
  }
50127
50303
  const data = config.cms || config.data ? mergeDataResources(config) : void 0;
50128
50304
  const dataErrors = validateDataResources(data, loaded.name).filter((issue2) => issue2.severity === "error");
@@ -50282,10 +50458,10 @@ __export(studio_reel_exports, {
50282
50458
  utcDay: () => utcDay
50283
50459
  });
50284
50460
  import { createHash as createHash25 } from "node:crypto";
50285
- import { lstatSync as lstatSync9, readFileSync as readFileSync56 } from "node:fs";
50286
- import { basename as basename12, join as join50 } from "node:path";
50461
+ import { lstatSync as lstatSync9, readFileSync as readFileSync57 } from "node:fs";
50462
+ import { basename as basename13, join as join51 } from "node:path";
50287
50463
  function loadMemory2(dir) {
50288
- const m = readJson5(join50(dir, ".himi", "studio", "memory.json"));
50464
+ const m = readJson5(join51(dir, ".himi", "studio", "memory.json"));
50289
50465
  if (!isObj2(m) || m.version !== 1 || !isObj2(m.shots) || !Array.isArray(m.timeline)) return null;
50290
50466
  const shots = [];
50291
50467
  for (const [id, s] of Object.entries(m.shots)) {
@@ -50322,8 +50498,8 @@ function planReel(dir) {
50322
50498
  const memory = loadMemory2(dir);
50323
50499
  const { entries } = readProgress(dir);
50324
50500
  const summary = summarizeProgress(entries);
50325
- const manifest = readJson5(join50(dir, "himalaya.content.json"));
50326
- const tokens = readJson5(join50(dir, "tokens.json"));
50501
+ const manifest = readJson5(join51(dir, "himalaya.content.json"));
50502
+ const tokens = readJson5(join51(dir, "tokens.json"));
50327
50503
  const accentToken = tokens?.colors?.accentPrimary;
50328
50504
  const accentRaw = typeof accentToken === "string" ? accentToken : accentToken?.light;
50329
50505
  const accent = typeof accentRaw === "string" && ACCENT.test(accentRaw) ? accentRaw : DEFAULT_ACCENT;
@@ -50337,14 +50513,15 @@ function planReel(dir) {
50337
50513
  const postcards = entries.map((e) => ({
50338
50514
  ts: e.ts,
50339
50515
  message: e.message,
50340
- step: str(e.step),
50341
- files: (Array.isArray(e.images) ? e.images : []).map((i) => isObj2(i) && typeof i.file === "string" ? basename12(i.file) : "").filter((f) => HISTORY_NAME.test(f))
50516
+ step: isCliEntry(e) ? null : str(e.step),
50517
+ by: isCliEntry(e) ? CLI_AUTHOR : null,
50518
+ files: (Array.isArray(e.images) ? e.images : []).map((i) => isObj2(i) && typeof i.file === "string" ? basename13(i.file) : "").filter((f) => HISTORY_NAME.test(f))
50342
50519
  }));
50343
50520
  const times = [summary.startedAt, ...timeline.map((t) => t.ts), memory?.createdAt].filter((x) => !!x).sort();
50344
50521
  const startedAt = times[0] ?? null;
50345
50522
  const endedAt = [summary.lastAt, ...timeline.map((t) => t.ts)].filter((x) => !!x).sort().pop() ?? null;
50346
50523
  return {
50347
- app: typeof manifest?.name === "string" && manifest.name ? manifest.name : basename12(dir),
50524
+ app: typeof manifest?.name === "string" && manifest.name ? manifest.name : basename13(dir),
50348
50525
  accent,
50349
50526
  startedAt,
50350
50527
  endedAt,
@@ -50365,10 +50542,10 @@ function inlineImages(dir, plan, budget = REEL_BUDGET_BYTES) {
50365
50542
  const verified = (f) => {
50366
50543
  if (!HISTORY_NAME.test(f)) return null;
50367
50544
  try {
50368
- const path2 = join50(dir, HISTORY_DIR, f);
50545
+ const path2 = join51(dir, HISTORY_DIR, f);
50369
50546
  const st = lstatSync9(path2);
50370
50547
  if (!st.isFile() || st.nlink !== 1) return null;
50371
- const bytes2 = readFileSync56(path2);
50548
+ const bytes2 = readFileSync57(path2);
50372
50549
  return createHash25("sha256").update(bytes2).digest("hex") === f.slice(0, 64) && imageExtension(bytes2) === f.slice(65) ? bytes2 : null;
50373
50550
  } catch {
50374
50551
  return null;
@@ -50462,7 +50639,7 @@ function renderReel(plan, images) {
50462
50639
  .flipcap{flex:1;min-width:220px}.flipcap .t{font-size:40px;font-weight:700;font-variant-numeric:tabular-nums;color:var(--accent)}.bar{height:4px;background:var(--line);border-radius:2px;margin-top:16px}.bar div{height:4px;background:var(--accent);border-radius:2px;width:0}
50463
50640
  .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:18px}.grid figure{margin:0;text-align:center}.grid img{width:100%;aspect-ratio:390/844;object-fit:cover;object-position:top;border:6px solid #111;border-radius:22px;background:#fff}
50464
50641
  figcaption{font-size:12px;color:var(--muted);margin-top:6px;word-break:break-all}
50465
- .log{list-style:none;padding:0;margin:0}.log li{display:flex;gap:14px;padding:12px 0;border-top:1px solid var(--line)}.log .t{min-width:52px;color:var(--muted);font-variant-numeric:tabular-nums}.log .s{font-size:12px;color:var(--muted);text-transform:capitalize}
50642
+ .log{list-style:none;padding:0;margin:0}.log li{display:flex;gap:14px;padding:12px 0;border-top:1px solid var(--line)}.log .t{min-width:52px;color:var(--muted);font-variant-numeric:tabular-nums}.log .s{font-size:12px;color:var(--muted);text-transform:capitalize}.log .by{font-size:12px;color:var(--muted)}
50466
50643
  .log .imgs{display:flex;gap:8px;margin-top:8px;flex-wrap:wrap}.log .imgs img{height:120px;border-radius:10px;border:1px solid var(--line)}
50467
50644
  footer{margin-top:48px;font-size:12px;color:var(--muted)}
50468
50645
  </style></head><body><div class="wrap">
@@ -50473,7 +50650,7 @@ ${flip}
50473
50650
  <h2>Where it ended up</h2>
50474
50651
  <div class="grid">${plan.finals.map((f) => `<figure>${img(f.file, f.id)}<figcaption>${escapeHtml(f.id)}</figcaption></figure>`).join("")}</div>
50475
50652
  <h2>Postcards</h2>
50476
- <ul class="log">${plan.postcards.map((c) => `<li><div class="t">${clock(c.ts)}</div><div><div>${escapeHtml(c.message)}</div>${c.step ? `<div class="s">step ${JOURNEY_STEPS.indexOf(c.step) + 1} \xB7 ${escapeHtml(c.step)}</div>` : ""}${c.files.length ? `<div class="imgs">${c.files.map((f) => img(f, c.message)).join("")}</div>` : ""}</div></li>`).join("") || `<li class="muted">No postcards were journaled.</li>`}</ul>
50653
+ <ul class="log">${plan.postcards.map((c) => `<li><div class="t">${clock(c.ts)}</div><div><div>${escapeHtml(c.message)}</div>${c.step ? `<div class="s">step ${JOURNEY_STEPS.indexOf(c.step) + 1} \xB7 ${escapeHtml(c.step)}</div>` : ""}${c.by ? `<div class="by">noted by ${escapeHtml(c.by)}</div>` : ""}${c.files.length ? `<div class="imgs">${c.files.map((f) => img(f, c.message)).join("")}</div>` : ""}</div></li>`).join("") || `<li class="muted">No postcards were journaled.</li>`}</ul>
50477
50654
  <footer>Built with himi.${plan.dropped ? ` ${count2(plan.dropped)} pictures were left out to keep this file small.` : ""}</footer>
50478
50655
  </div>
50479
50656
  <script>
@@ -50502,7 +50679,7 @@ var init_studio_reel = __esm({
50502
50679
  mimeOf = (file) => MIME2[file.slice(65)] ?? "image/png";
50503
50680
  readJson5 = (file) => {
50504
50681
  try {
50505
- return JSON.parse(readFileSync56(file, "utf8"));
50682
+ return JSON.parse(readFileSync57(file, "utf8"));
50506
50683
  } catch {
50507
50684
  return null;
50508
50685
  }
@@ -50533,9 +50710,9 @@ __export(native_decode_exports, {
50533
50710
  runNativeDecode: () => runNativeDecode
50534
50711
  });
50535
50712
  import { execFileSync as execFileSync5, spawnSync as spawnSync2 } from "node:child_process";
50536
- import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, readFileSync as readFileSync57, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
50713
+ import { existsSync as existsSync53, mkdtempSync as mkdtempSync2, readFileSync as readFileSync58, rmSync as rmSync11, writeFileSync as writeFileSync21 } from "node:fs";
50537
50714
  import { tmpdir as tmpdir3 } from "node:os";
50538
- import { join as join51 } from "node:path";
50715
+ import { join as join52 } from "node:path";
50539
50716
  import { fileURLToPath as fileURLToPath20 } from "node:url";
50540
50717
  function nativeDecodePackageDir() {
50541
50718
  return fileURLToPath20(new URL("../../../core/runtime/ios/HimalayaCore/", import.meta.url));
@@ -50545,8 +50722,8 @@ function nativeDecodeAndroidHarnessDir() {
50545
50722
  }
50546
50723
  function nativeOracleShipped() {
50547
50724
  return {
50548
- ios: existsSync52(join51(nativeDecodePackageDir(), "Package.swift")),
50549
- android: existsSync52(join51(nativeDecodeAndroidHarnessDir(), "gradlew"))
50725
+ ios: existsSync53(join52(nativeDecodePackageDir(), "Package.swift")),
50726
+ android: existsSync53(join52(nativeDecodeAndroidHarnessDir(), "gradlew"))
50550
50727
  };
50551
50728
  }
50552
50729
  function detectNativeDecode(options = {}) {
@@ -50557,7 +50734,7 @@ function detectNativeDecode(options = {}) {
50557
50734
  return { platform: "ios", available: false, reason: `the iOS decoder oracle requires macOS (darwin); this host is ${hostPlatform}` };
50558
50735
  }
50559
50736
  if (!swiftAvailable) return { platform: "ios", available: false, reason: "swift is not on PATH" };
50560
- const packageAvailable = options.packageAvailable ?? existsSync52(join51(packageDir, "Package.swift"));
50737
+ const packageAvailable = options.packageAvailable ?? existsSync53(join52(packageDir, "Package.swift"));
50561
50738
  if (!packageAvailable) {
50562
50739
  return {
50563
50740
  platform: "ios",
@@ -50569,7 +50746,7 @@ function detectNativeDecode(options = {}) {
50569
50746
  }
50570
50747
  function localPropertiesSdk(harnessDir) {
50571
50748
  try {
50572
- const match = readFileSync57(join51(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
50749
+ const match = readFileSync58(join52(harnessDir, "local.properties"), "utf8").match(/^sdk\.dir\s*=\s*(.+)$/m);
50573
50750
  return match?.[1]?.trim().replace(/\\([ :\\])/g, "$1") ?? null;
50574
50751
  } catch {
50575
50752
  return null;
@@ -50577,8 +50754,8 @@ function localPropertiesSdk(harnessDir) {
50577
50754
  }
50578
50755
  function detectAndroidNativeDecode(options = {}) {
50579
50756
  const harnessDir = options.harnessDir ?? nativeDecodeAndroidHarnessDir();
50580
- const gradlew = join51(harnessDir, "gradlew");
50581
- const gradlewAvailable = options.gradlewAvailable ?? existsSync52(gradlew);
50757
+ const gradlew = join52(harnessDir, "gradlew");
50758
+ const gradlewAvailable = options.gradlewAvailable ?? existsSync53(gradlew);
50582
50759
  if (!gradlewAvailable) {
50583
50760
  return {
50584
50761
  platform: "android",
@@ -50588,7 +50765,7 @@ function detectAndroidNativeDecode(options = {}) {
50588
50765
  }
50589
50766
  const androidHome = options.androidHome ?? process.env.ANDROID_HOME ?? "";
50590
50767
  const sdk = androidHome.trim() || (options.localPropertiesSdk !== void 0 ? options.localPropertiesSdk : localPropertiesSdk(harnessDir));
50591
- const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync52(sdk));
50768
+ const sdkAvailable = options.sdkAvailable ?? (typeof sdk === "string" && sdk.length > 0 && existsSync53(sdk));
50592
50769
  if (!sdkAvailable) {
50593
50770
  return {
50594
50771
  platform: "android",
@@ -50660,14 +50837,14 @@ function parseNativeDecodeOutput(stdout, expectedScreenIds, platform = "ios") {
50660
50837
  return verdicts;
50661
50838
  }
50662
50839
  function runNativeDecode(options) {
50663
- const descriptorDir = mkdtempSync2(join51(tmpdir3(), "himi-native-decode-"));
50840
+ const descriptorDir = mkdtempSync2(join52(tmpdir3(), "himi-native-decode-"));
50664
50841
  const startedAt = Date.now();
50665
50842
  try {
50666
50843
  for (const descriptor of options.descriptors) {
50667
50844
  if (typeof descriptor.descriptorJson !== "string") {
50668
50845
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
50669
50846
  }
50670
- writeFileSync21(join51(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50847
+ writeFileSync21(join52(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50671
50848
  }
50672
50849
  options.progress?.("building cached Swift oracle");
50673
50850
  execFileSync5("swift", ["build", "--package-path", options.packageDir, "--product", "himi-decode-oracle"], {
@@ -50679,7 +50856,7 @@ function runNativeDecode(options) {
50679
50856
  encoding: "utf8"
50680
50857
  }).trim();
50681
50858
  options.progress?.(`running real Swift decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
50682
- const stdout = execFileSync5(join51(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
50859
+ const stdout = execFileSync5(join52(binPath, "himi-decode-oracle"), ["--dir", descriptorDir], {
50683
50860
  stdio: ["ignore", "pipe", "pipe"],
50684
50861
  encoding: "utf8"
50685
50862
  });
@@ -50697,15 +50874,15 @@ function runNativeDecode(options) {
50697
50874
  }
50698
50875
  }
50699
50876
  function runAndroidNativeDecode(options) {
50700
- const descriptorDir = mkdtempSync2(join51(tmpdir3(), "himi-native-decode-android-"));
50701
- const outputPath = join51(descriptorDir, "verdicts.ndjson");
50877
+ const descriptorDir = mkdtempSync2(join52(tmpdir3(), "himi-native-decode-android-"));
50878
+ const outputPath = join52(descriptorDir, "verdicts.ndjson");
50702
50879
  const startedAt = Date.now();
50703
50880
  try {
50704
50881
  for (const descriptor of options.descriptors) {
50705
50882
  if (typeof descriptor.descriptorJson !== "string") {
50706
50883
  throw new Error(`native decode cannot judge ${descriptor.screenId}: the release builder did not return its exact baked JSON bytes`);
50707
50884
  }
50708
- writeFileSync21(join51(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50885
+ writeFileSync21(join52(descriptorDir, `${encodeURIComponent(descriptor.screenId)}.json`), descriptor.descriptorJson);
50709
50886
  }
50710
50887
  options.progress?.(`running real Kotlin decoder over ${options.descriptors.length} screen${options.descriptors.length === 1 ? "" : "s"}`);
50711
50888
  execFileSync5(options.gradlew, [
@@ -50720,7 +50897,7 @@ function runAndroidNativeDecode(options) {
50720
50897
  stdio: ["ignore", "ignore", "pipe"],
50721
50898
  encoding: "utf8"
50722
50899
  });
50723
- const stdout = readFileSync57(outputPath, "utf8");
50900
+ const stdout = readFileSync58(outputPath, "utf8");
50724
50901
  return {
50725
50902
  platform: "android",
50726
50903
  verdicts: parseNativeDecodeOutput(stdout, options.descriptors.map((descriptor) => descriptor.screenId), "android"),
@@ -50790,13 +50967,13 @@ __export(browser_authoring_exports, {
50790
50967
  });
50791
50968
  import { createServer as createServer3 } from "node:http";
50792
50969
  import { createHash as createHash26, randomBytes as randomBytes4 } from "node:crypto";
50793
- import { existsSync as existsSync53, readdirSync as readdirSync27, readFileSync as readFileSync58, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
50794
- import { join as join52, posix as posix2, relative as relative9, sep as sep12 } from "node:path";
50970
+ import { existsSync as existsSync54, readdirSync as readdirSync27, readFileSync as readFileSync59, realpathSync as realpathSync6, statSync as statSync12 } from "node:fs";
50971
+ import { join as join53, posix as posix2, relative as relative10, sep as sep12 } from "node:path";
50795
50972
  function humanMs(ms) {
50796
50973
  return ms < 1e3 ? `${ms}ms` : `${Math.round(ms / 1e3)}s`;
50797
50974
  }
50798
50975
  function collectWorkerSources(tier5SrcDir, bundleName) {
50799
- const root = join52(tier5SrcDir, bundleName);
50976
+ const root = join53(tier5SrcDir, bundleName);
50800
50977
  const files = {};
50801
50978
  const visitedDirs = /* @__PURE__ */ new Set();
50802
50979
  const walk2 = (abs, rel) => {
@@ -50804,16 +50981,16 @@ function collectWorkerSources(tier5SrcDir, bundleName) {
50804
50981
  if (visitedDirs.has(real)) return;
50805
50982
  visitedDirs.add(real);
50806
50983
  for (const name of readdirSync27(abs).sort()) {
50807
- const childAbs = join52(abs, name);
50984
+ const childAbs = join53(abs, name);
50808
50985
  const childRel = posix2.join(rel, name);
50809
50986
  if (statSync12(childAbs).isDirectory()) {
50810
50987
  walk2(childAbs, childRel);
50811
50988
  } else if (/\.(ts|tsx|js|mjs|json)$/.test(name)) {
50812
- files[childRel] = readFileSync58(childAbs, "utf8");
50989
+ files[childRel] = readFileSync59(childAbs, "utf8");
50813
50990
  }
50814
50991
  }
50815
50992
  };
50816
- if (!existsSync53(root)) return null;
50993
+ if (!existsSync54(root)) return null;
50817
50994
  walk2(root, `/tier5-src/${bundleName}`);
50818
50995
  const entry = [`/tier5-src/${bundleName}/index.ts`, `/tier5-src/${bundleName}/index.js`].find((p) => p in files);
50819
50996
  if (!entry) return null;
@@ -50830,20 +51007,20 @@ function collectRelativeSiblings(files, tier5SrcDir) {
50830
51007
  const spec = m[1];
50831
51008
  const virtual = posix2.normalize(posix2.join(dir, spec));
50832
51009
  if (!virtual.startsWith("/tier5-src/")) continue;
50833
- const onDisk = join52(tier5SrcDir, virtual.slice("/tier5-src/".length));
51010
+ const onDisk = join53(tier5SrcDir, virtual.slice("/tier5-src/".length));
50834
51011
  const candidates = [
50835
51012
  onDisk,
50836
51013
  `${onDisk}.ts`,
50837
51014
  `${onDisk}.js`,
50838
51015
  onDisk.replace(/\.js$/, ".ts"),
50839
- join52(onDisk, "index.ts"),
50840
- join52(onDisk, "index.js")
51016
+ join53(onDisk, "index.ts"),
51017
+ join53(onDisk, "index.js")
50841
51018
  ];
50842
51019
  for (const abs of candidates) {
50843
- if (!existsSync53(abs) || statSync12(abs).isDirectory()) continue;
50844
- const key2 = `/tier5-src/${relative9(tier5SrcDir, abs).split(sep12).join(posix2.sep)}`;
51020
+ if (!existsSync54(abs) || statSync12(abs).isDirectory()) continue;
51021
+ const key2 = `/tier5-src/${relative10(tier5SrcDir, abs).split(sep12).join(posix2.sep)}`;
50845
51022
  if (key2 in files) break;
50846
- files[key2] = readFileSync58(abs, "utf8");
51023
+ files[key2] = readFileSync59(abs, "utf8");
50847
51024
  queue.push(key2);
50848
51025
  break;
50849
51026
  }
@@ -50878,7 +51055,7 @@ function collectSdkModules(files, resolveSubpath) {
50878
51055
  function sdkSubpathReader(resolve48) {
50879
51056
  return (sub) => {
50880
51057
  try {
50881
- return readFileSync58(resolve48(`@wix/himalaya/${sub}`), "utf8");
51058
+ return readFileSync59(resolve48(`@wix/himalaya/${sub}`), "utf8");
50882
51059
  } catch {
50883
51060
  return null;
50884
51061
  }
@@ -51050,8 +51227,8 @@ __export(coverage_exports, {
51050
51227
  suppressedNativeKeys: () => suppressedNativeKeys,
51051
51228
  trendAndStore: () => trendAndStore
51052
51229
  });
51053
- import { existsSync as existsSync54, mkdirSync as mkdirSync25, readFileSync as readFileSync59, writeFileSync as writeFileSync22 } from "node:fs";
51054
- import { dirname as dirname28, join as join53 } from "node:path";
51230
+ import { existsSync as existsSync55, mkdirSync as mkdirSync25, readFileSync as readFileSync60, writeFileSync as writeFileSync22 } from "node:fs";
51231
+ import { dirname as dirname28, join as join54 } from "node:path";
51055
51232
  function suppressedNativeKeys(oracle) {
51056
51233
  return [...oracle.ios ? [] : ["native-decode|ios"], ...oracle.android ? [] : ["native-decode|android"]];
51057
51234
  }
@@ -51231,11 +51408,11 @@ function assembleLedger(r, options = {}) {
51231
51408
  return out;
51232
51409
  }
51233
51410
  function trendAndStore(dir, entries, suppressed) {
51234
- const p = join53(dir, LEDGER_FILE);
51411
+ const p = join54(dir, LEDGER_FILE);
51235
51412
  let previous = null;
51236
51413
  try {
51237
- if (existsSync54(p)) {
51238
- const parsed = JSON.parse(readFileSync59(p, "utf8"));
51414
+ if (existsSync55(p)) {
51415
+ const parsed = JSON.parse(readFileSync60(p, "utf8"));
51239
51416
  if (Array.isArray(parsed.keys)) previous = parsed.keys.filter((k) => typeof k === "string");
51240
51417
  }
51241
51418
  } catch {
@@ -51274,11 +51451,11 @@ function formatLedger(entries, trend) {
51274
51451
  return out.join("\n");
51275
51452
  }
51276
51453
  function diffAndStoreObservations(dir, current) {
51277
- const p = join53(dir, OBS_FILE);
51454
+ const p = join54(dir, OBS_FILE);
51278
51455
  let previous = null;
51279
51456
  try {
51280
- if (existsSync54(p)) {
51281
- const parsed = JSON.parse(readFileSync59(p, "utf8"));
51457
+ if (existsSync55(p)) {
51458
+ const parsed = JSON.parse(readFileSync60(p, "utf8"));
51282
51459
  if (parsed.screens && typeof parsed.screens === "object" && !Array.isArray(parsed.screens) && Object.values(parsed.screens).every(
51283
51460
  (v) => Array.isArray(v) && v.every((l) => typeof l === "string")
51284
51461
  )) {
@@ -51327,8 +51504,8 @@ var LEDGER_FILE, OBS_FILE;
51327
51504
  var init_coverage = __esm({
51328
51505
  "src/coverage.ts"() {
51329
51506
  init_native_decode();
51330
- LEDGER_FILE = join53(".himi", "test-ledger.json");
51331
- OBS_FILE = join53(".himi", "test-observations.json");
51507
+ LEDGER_FILE = join54(".himi", "test-ledger.json");
51508
+ OBS_FILE = join54(".himi", "test-observations.json");
51332
51509
  }
51333
51510
  });
51334
51511
 
@@ -51339,16 +51516,16 @@ __export(design_kits_exports, {
51339
51516
  kitsDir: () => kitsDir,
51340
51517
  listKits: () => listKits2
51341
51518
  });
51342
- import { existsSync as existsSync55, readFileSync as readFileSync60, readdirSync as readdirSync28 } from "node:fs";
51343
- import { join as join54, basename as basename13 } from "node:path";
51519
+ import { existsSync as existsSync56, readFileSync as readFileSync61, readdirSync as readdirSync28 } from "node:fs";
51520
+ import { join as join55, basename as basename14 } from "node:path";
51344
51521
  import { fileURLToPath as fileURLToPath21 } from "node:url";
51345
51522
  function kitsDir() {
51346
- return KIT_DIR_CANDIDATES.find((p) => existsSync55(p));
51523
+ return KIT_DIR_CANDIDATES.find((p) => existsSync56(p));
51347
51524
  }
51348
51525
  function listKits2() {
51349
51526
  const dir = kitsDir();
51350
51527
  if (!dir) return [];
51351
- return readdirSync28(dir).filter((f) => f.endsWith(".json")).map((f) => basename13(f, ".json")).sort();
51528
+ return readdirSync28(dir).filter((f) => f.endsWith(".json")).map((f) => basename14(f, ".json")).sort();
51352
51529
  }
51353
51530
  function kitReport(name) {
51354
51531
  const dir = kitsDir();
@@ -51367,7 +51544,7 @@ In a published @wix/himalaya-cli this is a packaging bug \u2014 the kits should
51367
51544
  if (!kits.includes(name)) {
51368
51545
  return { ok: false, kit: name, kits, problems: [`no design kit named "${name}". Available: ${kits.join(", ")}`] };
51369
51546
  }
51370
- const raw = readFileSync60(join54(dir, `${name}.json`), "utf8");
51547
+ const raw = readFileSync61(join55(dir, `${name}.json`), "utf8");
51371
51548
  let parsed;
51372
51549
  try {
51373
51550
  parsed = JSON.parse(raw);
@@ -51391,9 +51568,9 @@ var init_design_kits = __esm({
51391
51568
  "src/design-kits.ts"() {
51392
51569
  KIT_DIR_CANDIDATES = [
51393
51570
  // in-repo: tools/himi-cli/src/design-kits.ts → <repo>/stdlib/design-kits/
51394
- join54(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
51571
+ join55(fileURLToPath21(new URL("../../..", import.meta.url)), "stdlib", "design-kits"),
51395
51572
  // published CLI: <pkg>/dist/cli.mjs → <pkg>/stdlib/design-kits/
51396
- join54(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
51573
+ join55(fileURLToPath21(new URL("..", import.meta.url)), "stdlib", "design-kits")
51397
51574
  ];
51398
51575
  }
51399
51576
  });
@@ -51407,11 +51584,11 @@ __export(assets_exports, {
51407
51584
  removeAssetPayload: () => removeAssetPayload,
51408
51585
  verifyReport: () => verifyReport
51409
51586
  });
51410
- import { readFileSync as readFileSync61 } from "node:fs";
51411
- import { basename as basename14 } from "node:path";
51587
+ import { readFileSync as readFileSync62 } from "node:fs";
51588
+ import { basename as basename15 } from "node:path";
51412
51589
  function declaredAssets(dir) {
51413
51590
  return collectAssets(dir).map((a) => {
51414
- const bytes2 = readFileSync61(a.path);
51591
+ const bytes2 = readFileSync62(a.path);
51415
51592
  return {
51416
51593
  name: a.name,
51417
51594
  sha256: assetSha256(bytes2),
@@ -51488,8 +51665,8 @@ function verifyReport(dir, app, manifest) {
51488
51665
  };
51489
51666
  }
51490
51667
  function addAssetPayload(manifest, filePath, opts) {
51491
- const bytes2 = readFileSync61(filePath);
51492
- const name = opts.name ?? basename14(filePath);
51668
+ const bytes2 = readFileSync62(filePath);
51669
+ const name = opts.name ?? basename15(filePath);
51493
51670
  const sha2565 = assetSha256(bytes2);
51494
51671
  const record2 = {
51495
51672
  sha256: sha2565,
@@ -53163,9 +53340,9 @@ __export(run_exports, {
53163
53340
  runPackage: () => runPackage,
53164
53341
  withoutProdServe: () => withoutProdServe
53165
53342
  });
53166
- import { cpSync as cpSync2, existsSync as existsSync56, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, readFileSync as readFileSync62, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
53343
+ import { cpSync as cpSync2, existsSync as existsSync57, mkdirSync as mkdirSync26, mkdtempSync as mkdtempSync3, readFileSync as readFileSync63, rmSync as rmSync12, statSync as statSync13 } from "node:fs";
53167
53344
  import { networkInterfaces as osNetworkInterfaces, tmpdir as tmpdir4 } from "node:os";
53168
- import { dirname as dirname29, join as join55 } from "node:path";
53345
+ import { dirname as dirname29, join as join56 } from "node:path";
53169
53346
  import { fileURLToPath as fileURLToPath22 } from "node:url";
53170
53347
  function makePackageRunHandler(opts) {
53171
53348
  const store = new FileReleaseStore(opts.releaseRoot);
@@ -53193,9 +53370,9 @@ async function fetchBytes(url) {
53193
53370
  }
53194
53371
  async function resolvePackage(source, workDir) {
53195
53372
  const isUrl2 = /^https?:\/\//i.test(source);
53196
- if (!isUrl2 && existsSync56(source) && statSync13(source).isDirectory()) {
53197
- const appDirManifest = join55(source, "release-manifest.json");
53198
- if (existsSync56(appDirManifest)) {
53373
+ if (!isUrl2 && existsSync57(source) && statSync13(source).isDirectory()) {
53374
+ const appDirManifest = join56(source, "release-manifest.json");
53375
+ if (existsSync57(appDirManifest)) {
53199
53376
  const opened2 = openPackageDir(source);
53200
53377
  if (!opened2.validation.ok) {
53201
53378
  throw new Error(`package is not valid:
@@ -53204,9 +53381,9 @@ async function resolvePackage(source, workDir) {
53204
53381
  const app2 = opened2.header?.app ?? opened2.manifest?.app;
53205
53382
  if (!app2) throw new Error(`${source} has no app id (no himi-package.json and no manifest.app)`);
53206
53383
  if (!isSafeAppId(app2)) throw new Error(`${source} declares an unsafe app id ${JSON.stringify(app2)} \u2014 refusing to open it`);
53207
- const root2 = workDir ?? mkdtempSync3(join55(tmpdir4(), "himi-run-"));
53208
- const target = join55(root2, app2);
53209
- if (join55(source) !== target) {
53384
+ const root2 = workDir ?? mkdtempSync3(join56(tmpdir4(), "himi-run-"));
53385
+ const target = join56(root2, app2);
53386
+ if (join56(source) !== target) {
53210
53387
  mkdirSync26(root2, { recursive: true });
53211
53388
  rmSync12(target, { recursive: true, force: true });
53212
53389
  cpSync2(source, target, { recursive: true });
@@ -53223,8 +53400,8 @@ async function resolvePackage(source, workDir) {
53223
53400
  }
53224
53401
  throw new Error(`${source} is a directory but has no release-manifest.json \u2014 is it a package?`);
53225
53402
  }
53226
- const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync62(source);
53227
- const root = workDir ?? mkdtempSync3(join55(tmpdir4(), "himi-run-"));
53403
+ const bytes2 = isUrl2 ? await fetchBytes(source) : readFileSync63(source);
53404
+ const root = workDir ?? mkdtempSync3(join56(tmpdir4(), "himi-run-"));
53228
53405
  const { app, appDir: appDir2 } = unpackTo(bytes2, root);
53229
53406
  const opened = openPackageDir(appDir2);
53230
53407
  if (!opened.validation.ok) {
@@ -53247,14 +53424,14 @@ async function loadPreviewAssets() {
53247
53424
  const mod = await Promise.resolve().then(() => (init_preview_assets_generated(), preview_assets_generated_exports));
53248
53425
  const here = dirname29(fileURLToPath22(import.meta.url));
53249
53426
  const candidates = [
53250
- join55(here, "preview-assets.generated.bin"),
53427
+ join56(here, "preview-assets.generated.bin"),
53251
53428
  // published: copied next to the bundled CLI
53252
- join55(here, "../../../packages/serve/src/preview-assets.generated.bin")
53429
+ join56(here, "../../../packages/serve/src/preview-assets.generated.bin")
53253
53430
  // in-repo checkout
53254
53431
  ];
53255
53432
  for (const path2 of candidates) {
53256
- if (!existsSync56(path2)) continue;
53257
- const blob = readFileSync62(path2);
53433
+ if (!existsSync57(path2)) continue;
53434
+ const blob = readFileSync63(path2);
53258
53435
  return mod.assemblePreviewAssetsFromBlob(new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength));
53259
53436
  }
53260
53437
  return /* @__PURE__ */ new Map();
@@ -53401,9 +53578,9 @@ __export(run_watch_exports, {
53401
53578
  watchedContentDir: () => watchedContentDir,
53402
53579
  writeRunRecord: () => writeRunRecord
53403
53580
  });
53404
- import { closeSync as closeSync2, constants, existsSync as existsSync57, fstatSync, lstatSync as lstatSync10, statSync as statSync14, mkdirSync as mkdirSync27, openSync as openSync2, readFileSync as readFileSync63, realpathSync as realpathSync7, renameSync as renameSync6, rmSync as rmSync13, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
53581
+ import { closeSync as closeSync2, constants, existsSync as existsSync58, fstatSync, lstatSync as lstatSync10, statSync as statSync14, mkdirSync as mkdirSync27, openSync as openSync2, readFileSync as readFileSync64, realpathSync as realpathSync7, renameSync as renameSync6, rmSync as rmSync13, writeFileSync as writeFileSync23, watch as fsWatch } from "node:fs";
53405
53582
  import { randomUUID as randomUUID3 } from "node:crypto";
53406
- import { extname as extname4, join as join56, resolve as resolve42, sep as sep13 } from "node:path";
53583
+ import { extname as extname4, join as join57, resolve as resolve42, sep as sep13 } from "node:path";
53407
53584
  function diffReleases(prev, next) {
53408
53585
  const bundles = Object.keys(next.bundles).filter((b) => prev.bundles[b]?.sha256 !== next.bundles[b].sha256).sort();
53409
53586
  const changedBundles = new Set(bundles);
@@ -53444,7 +53621,7 @@ function watchContent(dir, onChange, opts = {}) {
53444
53621
  let timer = null;
53445
53622
  const pending = /* @__PURE__ */ new Set();
53446
53623
  const watcher2 = watch2(dir, { recursive: true }, (_event, file) => {
53447
- if (!file || ignoredChange(file, (rel) => isDir(join56(dir, rel)))) return;
53624
+ if (!file || ignoredChange(file, (rel) => isDir(join57(dir, rel)))) return;
53448
53625
  pending.add(file);
53449
53626
  if (timer) clearTimeout(timer);
53450
53627
  timer = setTimeout(() => {
@@ -53465,7 +53642,7 @@ function watchedContentDir(source) {
53465
53642
  if (!source || /^https?:\/\//i.test(source)) return null;
53466
53643
  const dir = resolve42(source);
53467
53644
  try {
53468
- return statSync14(dir).isDirectory() && existsSync57(join56(dir, "himalaya.content.json")) ? dir : null;
53645
+ return statSync14(dir).isDirectory() && existsSync58(join57(dir, "himalaya.content.json")) ? dir : null;
53469
53646
  } catch {
53470
53647
  return null;
53471
53648
  }
@@ -53522,7 +53699,7 @@ function serialize(task) {
53522
53699
  });
53523
53700
  }
53524
53701
  function himiDir(dir) {
53525
- const root = join56(dir, ".himi");
53702
+ const root = join57(dir, ".himi");
53526
53703
  try {
53527
53704
  return lstatSync10(root).isDirectory() ? root : null;
53528
53705
  } catch (e) {
@@ -53534,11 +53711,11 @@ function himiDir(dir) {
53534
53711
  function writeRunRecord(dir, record2) {
53535
53712
  const root = himiDir(dir);
53536
53713
  if (!root) return false;
53537
- const tmp = join56(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
53714
+ const tmp = join57(root, `.run.json.${process.pid}.${randomUUID3()}.tmp`);
53538
53715
  try {
53539
53716
  writeFileSync23(tmp, `${JSON.stringify(record2, null, 2)}
53540
53717
  `, { flag: "wx" });
53541
- renameSync6(tmp, join56(root, "run.json"));
53718
+ renameSync6(tmp, join57(root, "run.json"));
53542
53719
  } catch (e) {
53543
53720
  rmSync13(tmp, { force: true });
53544
53721
  throw e;
@@ -53546,7 +53723,7 @@ function writeRunRecord(dir, record2) {
53546
53723
  return true;
53547
53724
  }
53548
53725
  function removeRunRecord(dir, owner) {
53549
- const root = join56(dir, ".himi");
53726
+ const root = join57(dir, ".himi");
53550
53727
  try {
53551
53728
  if (!lstatSync10(root).isDirectory()) return;
53552
53729
  } catch {
@@ -53561,16 +53738,16 @@ function removeRunRecord(dir, owner) {
53561
53738
  }
53562
53739
  if (typeof pid === "number" && pid !== owner) return;
53563
53740
  }
53564
- rmSync13(join56(root, "run.json"), { force: true });
53741
+ rmSync13(join57(root, "run.json"), { force: true });
53565
53742
  }
53566
53743
  function boardFile(dir, pathname) {
53567
53744
  let rel = null;
53568
- if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join56("studio", "index.html");
53569
- else if (pathname === "/__himi/studio/state.js") rel = join56("studio", "state.js");
53570
- else if (pathname === "/__himi/studio/timeline.js") rel = join56("studio", "timeline.js");
53745
+ if (pathname === "/__himi/studio/" || pathname === "/__himi/studio/index.html") rel = join57("studio", "index.html");
53746
+ else if (pathname === "/__himi/studio/state.js") rel = join57("studio", "state.js");
53747
+ else if (pathname === "/__himi/studio/timeline.js") rel = join57("studio", "timeline.js");
53571
53748
  else {
53572
53749
  const m = /^\/__himi\/history\/([0-9a-f]{64}\.(png|jpg|gif|webp))$/.exec(pathname);
53573
- if (m) rel = join56("history", m[1]);
53750
+ if (m) rel = join57("history", m[1]);
53574
53751
  }
53575
53752
  if (!rel) return null;
53576
53753
  const bytes2 = readConfined(resolve42(dir, ".himi"), rel);
@@ -53586,7 +53763,7 @@ function readConfined(root, rel) {
53586
53763
  const parts = rel.split(sep13);
53587
53764
  let at = root;
53588
53765
  for (let i = 0; i < parts.length; i++) {
53589
- at = join56(at, parts[i]);
53766
+ at = join57(at, parts[i]);
53590
53767
  const st = lstatSync10(at);
53591
53768
  if (st.isSymbolicLink() || !(i === parts.length - 1 ? st.isFile() : st.isDirectory())) return null;
53592
53769
  }
@@ -53595,7 +53772,7 @@ function readConfined(root, rel) {
53595
53772
  const fd = openSync2(at, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
53596
53773
  try {
53597
53774
  if (!fstatSync(fd).isFile()) return null;
53598
- return readFileSync63(fd);
53775
+ return readFileSync64(fd);
53599
53776
  } finally {
53600
53777
  closeSync2(fd);
53601
53778
  }
@@ -53647,7 +53824,7 @@ __export(run_browser_exports, {
53647
53824
  runInBrowser: () => runInBrowser
53648
53825
  });
53649
53826
  import { createServer as createServer5 } from "node:http";
53650
- import { existsSync as existsSync58, readFileSync as readFileSync64 } from "node:fs";
53827
+ import { existsSync as existsSync59, readFileSync as readFileSync65 } from "node:fs";
53651
53828
  import { spawnSync as spawnSync3 } from "node:child_process";
53652
53829
  import { dirname as dirname30, resolve as resolve43 } from "node:path";
53653
53830
  import { fileURLToPath as fileURLToPath23 } from "node:url";
@@ -53655,7 +53832,7 @@ function stagedDir() {
53655
53832
  return process.env.HIMI_BROWSER_PLANE_OUT ? resolve43(process.env.HIMI_BROWSER_PLANE_OUT) : resolve43(REPO, "packages", "serve", "dist", "browser-plane");
53656
53833
  }
53657
53834
  function missingStaged() {
53658
- return [...SERVED].filter((f) => !existsSync58(resolve43(stagedDir(), f))).sort();
53835
+ return [...SERVED].filter((f) => !existsSync59(resolve43(stagedDir(), f))).sort();
53659
53836
  }
53660
53837
  function ensureBrowserPlaneStaged() {
53661
53838
  if (missingStaged().length === 0) return { ok: true, detail: "already staged" };
@@ -53740,7 +53917,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
53740
53917
  }
53741
53918
  const name = path2 === "/" || path2 === "/index.html" ? "host.html" : path2.slice(1);
53742
53919
  if (SERVED.has(name)) {
53743
- if (!existsSync58(resolve43(stagedDir(), name))) {
53920
+ if (!existsSync59(resolve43(stagedDir(), name))) {
53744
53921
  const restaged = ensureBrowserPlaneStaged();
53745
53922
  if (!restaged.ok) {
53746
53923
  res.writeHead(503, { "content-type": "application/json" });
@@ -53750,7 +53927,7 @@ async function runInBrowser(releaseRoot, app, header, opts = {}) {
53750
53927
  }
53751
53928
  let bytes2;
53752
53929
  try {
53753
- bytes2 = readFileSync64(resolve43(stagedDir(), name));
53930
+ bytes2 = readFileSync65(resolve43(stagedDir(), name));
53754
53931
  } catch (e) {
53755
53932
  res.writeHead(503, { "content-type": "application/json" });
53756
53933
  res.end(JSON.stringify({ error: "browser_plane_unstaged", file: name, detail: String(e) }));
@@ -54211,11 +54388,11 @@ __export(preflight_exports, {
54211
54388
  parseStoreConfig: () => parseStoreConfig,
54212
54389
  requireEnv: () => requireEnv
54213
54390
  });
54214
- import { existsSync as existsSync59, readFileSync as readFileSync65 } from "node:fs";
54215
- import { isAbsolute as isAbsolute3, relative as relative10, resolve as resolve44 } from "node:path";
54391
+ import { existsSync as existsSync60, readFileSync as readFileSync66 } from "node:fs";
54392
+ import { isAbsolute as isAbsolute5, relative as relative11, resolve as resolve44 } from "node:path";
54216
54393
  function isInsideRepo(p) {
54217
- const rel = relative10(REPO_ROOT3, resolve44(p));
54218
- return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
54394
+ const rel = relative11(REPO_ROOT3, resolve44(p));
54395
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute5(rel);
54219
54396
  }
54220
54397
  function assertCredentialOutsideRepo(label2, p) {
54221
54398
  if (isInsideRepo(p)) {
@@ -54268,10 +54445,10 @@ function parseStoreConfig(text5) {
54268
54445
  }
54269
54446
  function loadStoreConfig(app) {
54270
54447
  const p = storeConfigPath(app);
54271
- if (!existsSync59(p)) {
54448
+ if (!existsSync60(p)) {
54272
54449
  throw new Error(`No store config at ${p}. Create apps/${app}/store/store.config.yaml first (see /setup-store-signing).`);
54273
54450
  }
54274
- return parseStoreConfig(readFileSync65(p, "utf8"));
54451
+ return parseStoreConfig(readFileSync66(p, "utf8"));
54275
54452
  }
54276
54453
  var init_preflight = __esm({
54277
54454
  "../store-release/preflight.ts"() {
@@ -54290,19 +54467,19 @@ __export(credential_profile_exports, {
54290
54467
  profilePath: () => profilePath,
54291
54468
  resolveStoreCredentials: () => resolveStoreCredentials
54292
54469
  });
54293
- import { existsSync as existsSync60, readFileSync as readFileSync66 } from "node:fs";
54470
+ import { existsSync as existsSync61, readFileSync as readFileSync67 } from "node:fs";
54294
54471
  import { homedir as homedir5 } from "node:os";
54295
- import { join as join57 } from "node:path";
54472
+ import { join as join58 } from "node:path";
54296
54473
  function credentialsDir(home = homedir5()) {
54297
- return join57(home, ".himalaya", "store-credentials");
54474
+ return join58(home, ".himalaya", "store-credentials");
54298
54475
  }
54299
54476
  function profilePath(name, home = homedir5()) {
54300
54477
  if (!PROFILE_NAME.test(name)) {
54301
54478
  throw new Error(
54302
- `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join57(credentialsDir(home), "<name>.json")}; it is never a path.`
54479
+ `credential profile ${JSON.stringify(name)} is not a profile name \u2014 use letters, digits, ".", "_" or "-" starting with a letter or digit. It names ${join58(credentialsDir(home), "<name>.json")}; it is never a path.`
54303
54480
  );
54304
54481
  }
54305
- return join57(credentialsDir(home), `${name}.json`);
54482
+ return join58(credentialsDir(home), `${name}.json`);
54306
54483
  }
54307
54484
  function parseProfile(text5, label2) {
54308
54485
  let doc;
@@ -54333,8 +54510,8 @@ function resolveStoreCredentials(opts) {
54333
54510
  const { app, cfg } = opts;
54334
54511
  const env = opts.env ?? process.env;
54335
54512
  const home = opts.home ?? homedir5();
54336
- const read = opts.readProfile ?? ((p) => readFileSync66(p, "utf8"));
54337
- const exists = opts.profileExists ?? existsSync60;
54513
+ const read = opts.readProfile ?? ((p) => readFileSync67(p, "utf8"));
54514
+ const exists = opts.profileExists ?? existsSync61;
54338
54515
  const ios = cfg.ios ?? {};
54339
54516
  const flagName = opts.profileFlag?.trim();
54340
54517
  const named = namedProfile(cfg, opts.profileFlag);
@@ -54414,9 +54591,9 @@ __export(preflight_app_exports, {
54414
54591
  preflightApp: () => preflightApp,
54415
54592
  profileAccount: () => profileAccount
54416
54593
  });
54417
- import { readFileSync as readFileSync67, existsSync as existsSync61, readdirSync as readdirSync29, statSync as statSync15 } from "node:fs";
54594
+ import { readFileSync as readFileSync68, existsSync as existsSync62, readdirSync as readdirSync29, statSync as statSync15 } from "node:fs";
54418
54595
  import { createHash as createHash27 } from "node:crypto";
54419
- import { join as join58 } from "node:path";
54596
+ import { join as join59 } from "node:path";
54420
54597
  function analyze(projectYml, storeConfig, icon, account) {
54421
54598
  const f = [];
54422
54599
  const has = (re, s) => re.test(s);
@@ -54480,13 +54657,13 @@ function profileAccount(app, storeConfig, opts = {}) {
54480
54657
  }
54481
54658
  }
54482
54659
  async function preflightApp(app, opts = {}) {
54483
- const projectYml = existsSync61(iosProjectYml(app)) ? readFileSync67(iosProjectYml(app), "utf8") : "";
54484
- const storeConfig = existsSync61(storeConfigPath(app)) ? readFileSync67(storeConfigPath(app), "utf8") : "";
54660
+ const projectYml = existsSync62(iosProjectYml(app)) ? readFileSync68(iosProjectYml(app), "utf8") : "";
54661
+ const storeConfig = existsSync62(storeConfigPath(app)) ? readFileSync68(storeConfigPath(app), "utf8") : "";
54485
54662
  if (!storeConfig) return [{ level: "error", message: `no apps/${app}/store/store.config.yaml \u2014 run /setup-store-signing first.` }];
54486
54663
  const findings = analyze(
54487
54664
  projectYml,
54488
54665
  storeConfig,
54489
- await appIconState(join58(appDir(app), "ios")),
54666
+ await appIconState(join59(appDir(app), "ios")),
54490
54667
  profileAccount(app, storeConfig, opts)
54491
54668
  );
54492
54669
  findings.push(...analyzeListing(metadataFilesPresent(app), listScreenshots(app)));
@@ -54515,17 +54692,17 @@ function analyzeSubmissionRecord(appLevel, review, opts) {
54515
54692
  return f;
54516
54693
  }
54517
54694
  function appLevelMetadataFiles(app, locale = "en-US") {
54518
- const root = join58(storeDir(app), "metadata");
54695
+ const root = join59(storeDir(app), "metadata");
54519
54696
  const out = /* @__PURE__ */ new Set();
54520
- if (existsSync61(root)) {
54697
+ if (existsSync62(root)) {
54521
54698
  for (const n of readdirSync29(root)) if (n.endsWith(".txt")) out.add(n);
54522
54699
  }
54523
- if (existsSync61(join58(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
54700
+ if (existsSync62(join59(root, locale, "privacy_url.txt"))) out.add("privacy_url.txt");
54524
54701
  return out;
54525
54702
  }
54526
54703
  function reviewInfoFiles(app) {
54527
- const dir = join58(storeDir(app), "review_information");
54528
- if (!existsSync61(dir)) return /* @__PURE__ */ new Set();
54704
+ const dir = join59(storeDir(app), "review_information");
54705
+ if (!existsSync62(dir)) return /* @__PURE__ */ new Set();
54529
54706
  return new Set(readdirSync29(dir).filter((n) => n.endsWith(".txt")));
54530
54707
  }
54531
54708
  function isValidAppStoreSize(w, h) {
@@ -54635,12 +54812,12 @@ function analyzePlayListing(assets) {
54635
54812
  return f;
54636
54813
  }
54637
54814
  function playListingAssets(app, locale = "en-US") {
54638
- const dir = join58(storeDir(app), "play", locale, "images");
54815
+ const dir = join59(storeDir(app), "play", locale, "images");
54639
54816
  const read = (names) => {
54640
54817
  for (const name of names) {
54641
- const p = join58(dir, name);
54818
+ const p = join59(dir, name);
54642
54819
  try {
54643
- if (statSync15(p).isFile()) return { name, bytes: readFileSync67(p) };
54820
+ if (statSync15(p).isFile()) return { name, bytes: readFileSync68(p) };
54644
54821
  } catch {
54645
54822
  }
54646
54823
  }
@@ -54671,26 +54848,26 @@ function analyzeListing(metaFiles, shots) {
54671
54848
  return f;
54672
54849
  }
54673
54850
  function metadataFilesPresent(app, locale = "en-US") {
54674
- const dir = join58(storeDir(app), "metadata", locale);
54675
- if (!existsSync61(dir)) return /* @__PURE__ */ new Set();
54851
+ const dir = join59(storeDir(app), "metadata", locale);
54852
+ if (!existsSync62(dir)) return /* @__PURE__ */ new Set();
54676
54853
  return new Set(readdirSync29(dir).filter((n) => n.endsWith(".txt")));
54677
54854
  }
54678
54855
  function listScreenshots(app, locale = "en-US") {
54679
54856
  const out = [];
54680
- const flat = join58(storeDir(app), "screenshots", locale);
54681
- if (existsSync61(flat)) {
54857
+ const flat = join59(storeDir(app), "screenshots", locale);
54858
+ if (existsSync62(flat)) {
54682
54859
  for (const png of readdirSync29(flat).filter((n) => n.endsWith(".png"))) {
54683
- const sz = pngSize(readFileSync67(join58(flat, png)));
54860
+ const sz = pngSize(readFileSync68(join59(flat, png)));
54684
54861
  if (sz) out.push({ name: png, w: sz.w, h: sz.h });
54685
54862
  }
54686
54863
  }
54687
- const legacy = join58(storeDir(app), "metadata", locale, "screenshots");
54688
- if (existsSync61(legacy)) {
54864
+ const legacy = join59(storeDir(app), "metadata", locale, "screenshots");
54865
+ if (existsSync62(legacy)) {
54689
54866
  for (const deviceDir of readdirSync29(legacy)) {
54690
- const d = join58(legacy, deviceDir);
54867
+ const d = join59(legacy, deviceDir);
54691
54868
  if (!statSync15(d).isDirectory()) continue;
54692
54869
  for (const png of readdirSync29(d).filter((n) => n.endsWith(".png"))) {
54693
- const sz = pngSize(readFileSync67(join58(d, png)));
54870
+ const sz = pngSize(readFileSync68(join59(d, png)));
54694
54871
  if (sz) out.push({ name: `${deviceDir}/${png}`, w: sz.w, h: sz.h });
54695
54872
  }
54696
54873
  }
@@ -54698,13 +54875,13 @@ function listScreenshots(app, locale = "en-US") {
54698
54875
  return out;
54699
54876
  }
54700
54877
  function placeholderHashes2() {
54701
- const templateIos = join58(appDir("_template"), "ios");
54702
- if (!existsSync61(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
54878
+ const templateIos = join59(appDir("_template"), "ios");
54879
+ if (!existsSync62(templateIos)) return [LEGACY_PLACEHOLDER_SHA2562];
54703
54880
  for (const target of readdirSync29(templateIos)) {
54704
- const p = join58(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54881
+ const p = join59(templateIos, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54705
54882
  try {
54706
54883
  if (!statSync15(p).isFile()) continue;
54707
- return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync67(p)).digest("hex")];
54884
+ return [LEGACY_PLACEHOLDER_SHA2562, createHash27("sha256").update(readFileSync68(p)).digest("hex")];
54708
54885
  } catch {
54709
54886
  }
54710
54887
  }
@@ -54735,12 +54912,12 @@ async function pixelIssues(bytes2) {
54735
54912
  }
54736
54913
  }
54737
54914
  async function appIconState(iosDir) {
54738
- if (!existsSync61(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
54915
+ if (!existsSync62(iosDir)) return { exists: false, isPlaceholder: false, storeIssues: [] };
54739
54916
  for (const target of readdirSync29(iosDir)) {
54740
- const p = join58(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54917
+ const p = join59(iosDir, target, "Assets.xcassets", "AppIcon.appiconset", "icon_1024.png");
54741
54918
  try {
54742
54919
  if (!statSync15(p).isFile()) continue;
54743
- const bytes2 = readFileSync67(p);
54920
+ const bytes2 = readFileSync68(p);
54744
54921
  const hash = createHash27("sha256").update(bytes2).digest("hex");
54745
54922
  const pixels = await pixelIssues(bytes2);
54746
54923
  return {
@@ -54812,7 +54989,7 @@ __export(version_exports, {
54812
54989
  setAndroidVersion: () => setAndroidVersion,
54813
54990
  setIosVersion: () => setIosVersion
54814
54991
  });
54815
- import { readFileSync as readFileSync68, writeFileSync as writeFileSync24 } from "node:fs";
54992
+ import { readFileSync as readFileSync69, writeFileSync as writeFileSync24 } from "node:fs";
54816
54993
  function readIosVersion(projectYml) {
54817
54994
  const marketing = matchScalar(projectYml, "CFBundleShortVersionString");
54818
54995
  const build2 = matchScalar(projectYml, "CFBundleVersion");
@@ -54870,8 +55047,8 @@ function setAndroidVersion(gradle, v) {
54870
55047
  function bumpVersion(app, opts) {
54871
55048
  const iosPath = iosProjectYml(app);
54872
55049
  const gradlePath = androidGradle(app);
54873
- const iosYml = readFileSync68(iosPath, "utf8");
54874
- const gradle = readFileSync68(gradlePath, "utf8");
55050
+ const iosYml = readFileSync69(iosPath, "utf8");
55051
+ const gradle = readFileSync69(gradlePath, "utf8");
54875
55052
  const current = readIosVersion(iosYml);
54876
55053
  const next = {
54877
55054
  marketing: opts.marketing ?? current.marketing,
@@ -54888,7 +55065,7 @@ var init_version = __esm({
54888
55065
  });
54889
55066
 
54890
55067
  // ../store-release/register-bundle.ts
54891
- import { readFileSync as readFileSync69 } from "node:fs";
55068
+ import { readFileSync as readFileSync70 } from "node:fs";
54892
55069
  import { createPrivateKey as createPrivateKey2, sign } from "node:crypto";
54893
55070
  function b64url2(b) {
54894
55071
  return Buffer.from(b).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
@@ -54906,7 +55083,7 @@ function bundleIdBody(identifier, name, platform = "IOS") {
54906
55083
  async function registerBundleId(opts) {
54907
55084
  const env = requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
54908
55085
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
54909
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync69(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
55086
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync70(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54910
55087
  const res = await fetch(`${ASC_API}/v1/bundleIds`, {
54911
55088
  method: "POST",
54912
55089
  headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
@@ -54960,7 +55137,7 @@ __export(asc_status_exports, {
54960
55137
  ascStatus: () => ascStatus,
54961
55138
  buildVerdict: () => buildVerdict
54962
55139
  });
54963
- import { readFileSync as readFileSync70 } from "node:fs";
55140
+ import { readFileSync as readFileSync71 } from "node:fs";
54964
55141
  function buildVerdict(builds) {
54965
55142
  if (!builds.length) {
54966
55143
  return "NO builds registered \u2014 if you uploaded recently and it's been >15 min, the binary was REJECTED IN PROCESSING (reason is email-only; run validate-binary.ts / scan-purpose-strings.ts to reproduce it).";
@@ -54974,7 +55151,7 @@ function buildVerdict(builds) {
54974
55151
  function client(creds) {
54975
55152
  const env = creds ?? requireEnv(["ASC_KEY_ID", "ASC_ISSUER_ID", "ASC_KEY_PATH"]);
54976
55153
  assertCredentialOutsideRepo("ASC_KEY_PATH", env.ASC_KEY_PATH);
54977
- const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync70(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
55154
+ const jwt = ascJwt(env.ASC_KEY_ID, env.ASC_ISSUER_ID, readFileSync71(env.ASC_KEY_PATH, "utf8"), Math.floor(Date.now() / 1e3));
54978
55155
  return async (path2) => {
54979
55156
  const r = await fetch(`${ASC}${path2}`, { headers: { Authorization: `Bearer ${jwt}` } });
54980
55157
  return { status: r.status, json: await r.json().catch(() => ({})) };
@@ -55134,7 +55311,7 @@ __export(secrets_exports, {
55134
55311
  runRemoteSecretsCommand: () => runRemoteSecretsCommand,
55135
55312
  runSecretsCommand: () => runSecretsCommand
55136
55313
  });
55137
- import { readFileSync as readFileSync71 } from "node:fs";
55314
+ import { readFileSync as readFileSync72 } from "node:fs";
55138
55315
  function environment(value) {
55139
55316
  if (value === "preview" || value === "production") return value;
55140
55317
  throw new Error("--environment must be preview or production");
@@ -55165,7 +55342,7 @@ async function runSecretsCommand(operation, flags, io, options = {}) {
55165
55342
  io.log(JSON.stringify(await vault.list({ app, environment: env })));
55166
55343
  return 0;
55167
55344
  }
55168
- const value = options.readValue ? options.readValue() : readFileSync71(0, "utf8").replace(/\r?\n$/, "");
55345
+ const value = options.readValue ? options.readValue() : readFileSync72(0, "utf8").replace(/\r?\n$/, "");
55169
55346
  if (operation === "set") await vault.set({ app, environment: env, name, value });
55170
55347
  else if (operation === "rotate") await vault.rotate({ app, environment: env, name, value });
55171
55348
  else throw new Error(`unknown secrets operation ${operation}`);
@@ -55194,7 +55371,7 @@ async function runRemoteSecretsCommand(operation, flags, io, options) {
55194
55371
  }
55195
55372
  let body;
55196
55373
  if (operation !== "list") {
55197
- const value = options.readValue ? options.readValue() : readFileSync71(0, "utf8").replace(/\r?\n$/, "");
55374
+ const value = options.readValue ? options.readValue() : readFileSync72(0, "utf8").replace(/\r?\n$/, "");
55198
55375
  body = JSON.stringify({ value });
55199
55376
  }
55200
55377
  const response = await (options.fetchImpl ?? fetch)(url, { method: operation === "list" ? "GET" : operation === "set" ? "PUT" : "POST", headers: headers2, body });
@@ -55230,15 +55407,15 @@ __export(notify_exports, {
55230
55407
  runNotify: () => runNotify,
55231
55408
  saveAdminSession: () => saveAdminSession
55232
55409
  });
55233
- import { chmodSync as chmodSync3, mkdirSync as mkdirSync28, readFileSync as readFileSync72, writeFileSync as writeFileSync25 } from "node:fs";
55410
+ import { chmodSync as chmodSync3, mkdirSync as mkdirSync28, readFileSync as readFileSync73, writeFileSync as writeFileSync25 } from "node:fs";
55234
55411
  import { homedir as homedir6 } from "node:os";
55235
- import { dirname as dirname32, join as join59 } from "node:path";
55412
+ import { dirname as dirname32, join as join60 } from "node:path";
55236
55413
  function defaultSessionsPath() {
55237
- return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join59(homedir6(), ".himalaya", "admin-sessions.json");
55414
+ return process.env.HIMI_ADMIN_SESSIONS_FILE ?? join60(homedir6(), ".himalaya", "admin-sessions.json");
55238
55415
  }
55239
55416
  function loadSessionsFile(path2 = defaultSessionsPath()) {
55240
55417
  try {
55241
- return JSON.parse(readFileSync72(path2, "utf8"));
55418
+ return JSON.parse(readFileSync73(path2, "utf8"));
55242
55419
  } catch {
55243
55420
  return {};
55244
55421
  }
@@ -56368,7 +56545,7 @@ function deriveAdoptedCmsCollections(cms) {
56368
56545
  return { collections, dropped };
56369
56546
  }
56370
56547
  function deriveTailoring(ctx, overrides = {}, revision = "site-derived") {
56371
- const appName = deriveAppName(ctx, overrides.appName);
56548
+ const appName2 = deriveAppName(ctx, overrides.appName);
56372
56549
  const supportUrl = clean(overrides.supportUrl) || clean(ctx.url.primary);
56373
56550
  const privacyUrl = clean(overrides.privacyUrl);
56374
56551
  const termsUrl = clean(overrides.termsUrl);
@@ -56388,8 +56565,8 @@ function deriveTailoring(ctx, overrides = {}, revision = "site-derived") {
56388
56565
  // empty; either way the key exists and the shell's list never survives the merge.
56389
56566
  cmsCollections: [],
56390
56567
  identity: {
56391
- appName,
56392
- businessName: clean(ctx.identity.businessName) || appName,
56568
+ appName: appName2,
56569
+ businessName: clean(ctx.identity.businessName) || appName2,
56393
56570
  tagline: clean(overrides.tagline) || clean(ctx.identity.description),
56394
56571
  // "" means UNKNOWN here, with `missing` above as the signal — the published overlay's
56395
56572
  // documented shape. Keeping an unknown url out of a MERGE is `apply.ts`'s job, at the
@@ -56399,7 +56576,7 @@ function deriveTailoring(ctx, overrides = {}, revision = "site-derived") {
56399
56576
  termsUrl
56400
56577
  }
56401
56578
  };
56402
- return { appName, iconSource: deriveIconSource(ctx), provisioning, missing };
56579
+ return { appName: appName2, iconSource: deriveIconSource(ctx), provisioning, missing };
56403
56580
  }
56404
56581
  function bounded(s, max) {
56405
56582
  if (s.length <= max) return s;
@@ -58495,11 +58672,11 @@ function crawledPageName(look, page) {
58495
58672
  const hit = (page?.pageId ? sitePages.find((p) => p.pageId === page.pageId) : void 0) ?? (look.mainPageId ? sitePages.find((p) => p.pageId === look.mainPageId) : void 0) ?? sitePages.find((p) => p.slug === "home");
58496
58673
  return hit?.title.trim() ?? "";
58497
58674
  }
58498
- function guessedNameWarning(appName, crawledTitle, ownerLookupError) {
58499
- const from = crawledTitle && crawledTitle !== appName ? ` off the crawled page title "${crawledTitle}"` : " from the site crawl";
58675
+ function guessedNameWarning(appName2, crawledTitle, ownerLookupError) {
58676
+ const from = crawledTitle && crawledTitle !== appName2 ? ` off the crawled page title "${crawledTitle}"` : " from the site crawl";
58500
58677
  const why = ownerLookupError ? `the owner site-properties read FAILED (${ownerLookupError}), so the owner-entered name was never read` : "no owner token was available, so site-properties was never read";
58501
58678
  const remedy = ownerLookupError ? "resolve that failure and re-run the same owner-authenticated command" : "re-run with owner auth (`himi site analyze --site <msid>`)";
58502
- return `identity.appName "${appName}" is a GUESS, read${from} rather than from the owner-entered business name \u2014 ${why}. --apply writes it into provisioning.json as-is and it reaches both app stores verbatim, so check it: ${remedy}, or set identity.appName in provisioning.json yourself.`;
58679
+ return `identity.appName "${appName2}" is a GUESS, read${from} rather than from the owner-entered business name \u2014 ${why}. --apply writes it into provisioning.json as-is and it reaches both app stores verbatim, so check it: ${remedy}, or set identity.appName in provisioning.json yourself.`;
58503
58680
  }
58504
58681
  function mergeLookFromPublished(look, published, siteUrl) {
58505
58682
  const sitePages = withPageUrls(
@@ -59007,7 +59184,7 @@ var init_brand_font = __esm({
59007
59184
 
59008
59185
  // src/site-analyze/write-artifacts.ts
59009
59186
  import { mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
59010
- import { join as join60 } from "node:path";
59187
+ import { join as join61 } from "node:path";
59011
59188
  function attachVeloSources(book, sources, put) {
59012
59189
  const byPath = new Map((sources ?? []).map((s) => [s.path, s.text]));
59013
59190
  for (const f of book.velo.files) {
@@ -59162,12 +59339,12 @@ async function downloadMedia(book, fetchImpl, put) {
59162
59339
  }
59163
59340
  async function writeBusinessBook(result, opts) {
59164
59341
  const out = opts.outDir;
59165
- mkdirSync29(join60(out, "evidence/pages"), { recursive: true });
59166
- mkdirSync29(join60(out, "media/catalog"), { recursive: true });
59342
+ mkdirSync29(join61(out, "evidence/pages"), { recursive: true });
59343
+ mkdirSync29(join61(out, "media/catalog"), { recursive: true });
59167
59344
  const files = [];
59168
59345
  const put = (rel, body) => {
59169
- const p = join60(out, rel);
59170
- mkdirSync29(join60(p, ".."), { recursive: true });
59346
+ const p = join61(out, rel);
59347
+ mkdirSync29(join61(p, ".."), { recursive: true });
59171
59348
  writeFileSync26(p, body);
59172
59349
  files.push(rel);
59173
59350
  };
@@ -59261,8 +59438,8 @@ var init_customer_build = __esm({
59261
59438
  });
59262
59439
 
59263
59440
  // src/site-analyze/apply.ts
59264
- import { existsSync as existsSync62, readFileSync as readFileSync73, writeFileSync as writeFileSync27, copyFileSync as copyFileSync3, mkdirSync as mkdirSync30, realpathSync as realpathSync8, statSync as statSync16 } from "node:fs";
59265
- import { dirname as dirname33, isAbsolute as isAbsolute4, join as join61, resolve as resolve45, sep as sep14 } from "node:path";
59441
+ import { existsSync as existsSync63, readFileSync as readFileSync74, writeFileSync as writeFileSync27, copyFileSync as copyFileSync3, mkdirSync as mkdirSync30, realpathSync as realpathSync8, statSync as statSync16 } from "node:fs";
59442
+ import { dirname as dirname33, isAbsolute as isAbsolute6, join as join62, resolve as resolve45, sep as sep14 } from "node:path";
59266
59443
  import { spawnSync as spawnSync4 } from "node:child_process";
59267
59444
  function unpairedRedirectNote(overlay) {
59268
59445
  if (!overlay.wix?.clientId || overlay.wix.redirectUri) return void 0;
@@ -59277,7 +59454,7 @@ function committedShellProvisioning(path2) {
59277
59454
  function findRepoRoot(cwd) {
59278
59455
  let dir = cwd;
59279
59456
  for (let i = 0; i < 8; i++) {
59280
- if (SHELL_NAMES2.some((name) => existsSync62(join61(dir, shellAppDir(name), "package.json")))) return dir;
59457
+ if (SHELL_NAMES2.some((name) => existsSync63(join62(dir, shellAppDir(name), "package.json")))) return dir;
59281
59458
  const next = dirname33(dir);
59282
59459
  if (next === dir) break;
59283
59460
  dir = next;
@@ -59346,24 +59523,24 @@ function mergeCustomerOwned(committed, overlay) {
59346
59523
  }
59347
59524
  function buildMergedContract(options) {
59348
59525
  const { appDir: appDir2, packageDir, overlay } = options;
59349
- const committedPath = join61(appDir2, "provisioning.json");
59350
- if (!existsSync62(committedPath)) {
59526
+ const committedPath = join62(appDir2, "provisioning.json");
59527
+ if (!existsSync63(committedPath)) {
59351
59528
  throw new Error(`shell has no committed provisioning.json at ${committedPath} to build a contract from`);
59352
59529
  }
59353
- const committed = JSON.parse(readFileSync73(committedPath, "utf8"));
59530
+ const committed = JSON.parse(readFileSync74(committedPath, "utf8"));
59354
59531
  const merged = mergeCustomerOwned(committed, overlay);
59355
59532
  const overlayFontFamily = (overlay.branding ?? {}).fontFamily;
59356
59533
  const branding = merged.branding ?? {};
59357
59534
  const staged = [];
59358
59535
  const contained = (root, rel) => {
59359
- if (isAbsolute4(rel)) return void 0;
59536
+ if (isAbsolute6(rel)) return void 0;
59360
59537
  const full = resolve45(root, rel);
59361
59538
  return full.startsWith(resolve45(root) + sep14) ? full : void 0;
59362
59539
  };
59363
59540
  const stage = (fromRel, toRel) => {
59364
59541
  const from = contained(packageDir, fromRel);
59365
- const to = contained(join61(appDir2, STAGED), toRel.slice(STAGED.length + 1));
59366
- if (!from || !to || !existsSync62(from)) return void 0;
59542
+ const to = contained(join62(appDir2, STAGED), toRel.slice(STAGED.length + 1));
59543
+ if (!from || !to || !existsSync63(from)) return void 0;
59367
59544
  let real;
59368
59545
  let realRoot;
59369
59546
  try {
@@ -59385,11 +59562,11 @@ function buildMergedContract(options) {
59385
59562
  };
59386
59563
  const icon = stage("media/icon.png", `${STAGED}/icon.png`);
59387
59564
  if (icon) branding.appIconPath = icon;
59388
- const facesPath = join61(packageDir, "media/fonts/faces.json");
59389
- if (existsSync62(facesPath)) {
59565
+ const facesPath = join62(packageDir, "media/fonts/faces.json");
59566
+ if (existsSync63(facesPath)) {
59390
59567
  let faces = [];
59391
59568
  try {
59392
- const parsed = JSON.parse(readFileSync73(facesPath, "utf8"));
59569
+ const parsed = JSON.parse(readFileSync74(facesPath, "utf8"));
59393
59570
  faces = Array.isArray(parsed) ? parsed : [];
59394
59571
  } catch {
59395
59572
  faces = [];
@@ -59410,9 +59587,9 @@ function buildMergedContract(options) {
59410
59587
  merged.branding = branding;
59411
59588
  const body = `${JSON.stringify(merged, null, 2)}
59412
59589
  `;
59413
- const path2 = join61(packageDir, "provisioning.contract.json");
59590
+ const path2 = join62(packageDir, "provisioning.contract.json");
59414
59591
  writeFileSync27(path2, body);
59415
- const localPath = join61(appDir2, LOCAL_CONTRACT_REL);
59592
+ const localPath = join62(appDir2, LOCAL_CONTRACT_REL);
59416
59593
  mkdirSync30(dirname33(localPath), { recursive: true });
59417
59594
  writeFileSync27(localPath, body);
59418
59595
  staged.push(localPath);
@@ -59447,10 +59624,10 @@ function catalogTypography(overlay, template) {
59447
59624
  function staticFaqFor(overlay, overlayPath) {
59448
59625
  const own3 = overlay.faq;
59449
59626
  if (own3 !== void 0) return deriveStaticFaq(own3);
59450
- const bookPath = join61(dirname33(resolve45(overlayPath)), "book.json");
59451
- if (!existsSync62(bookPath)) return void 0;
59627
+ const bookPath = join62(dirname33(resolve45(overlayPath)), "book.json");
59628
+ if (!existsSync63(bookPath)) return void 0;
59452
59629
  try {
59453
- const book = JSON.parse(readFileSync73(bookPath, "utf8"));
59630
+ const book = JSON.parse(readFileSync74(bookPath, "utf8"));
59454
59631
  const copy = book?.copy;
59455
59632
  if (!copy || typeof copy !== "object" || copy.unavailable) return void 0;
59456
59633
  const pairs = deriveStaticFaq(copy.faq);
@@ -59464,8 +59641,8 @@ async function applyOverlays(input) {
59464
59641
  const cwd = resolve45(input.cwd);
59465
59642
  const shell = input.shell ?? DEFAULT_BRANDABLE_SHELL;
59466
59643
  assertKnownShell(shell);
59467
- const overlay = JSON.parse(readFileSync73(input.overlayPath, "utf8"));
59468
- const tokens = JSON.parse(readFileSync73(input.tokensPath, "utf8"));
59644
+ const overlay = JSON.parse(readFileSync74(input.overlayPath, "utf8"));
59645
+ const tokens = JSON.parse(readFileSync74(input.tokensPath, "utf8"));
59469
59646
  const faq = staticFaqFor(overlay, input.overlayPath);
59470
59647
  if (faq !== void 0) overlay.faq = faq;
59471
59648
  const wrote = [];
@@ -59477,9 +59654,9 @@ async function applyOverlays(input) {
59477
59654
  `shell "${shell}" cannot bake a customer brand \u2014 it ships no prepare:customer script`
59478
59655
  );
59479
59656
  }
59480
- const appDir2 = join61(repoRoot2, shellAppDir(shell));
59481
- const committedPath = join61(appDir2, "provisioning.json");
59482
- const committedSite = existsSync62(committedPath) ? JSON.parse(readFileSync73(committedPath, "utf8")).site : void 0;
59657
+ const appDir2 = join62(repoRoot2, shellAppDir(shell));
59658
+ const committedPath = join62(appDir2, "provisioning.json");
59659
+ const committedSite = existsSync63(committedPath) ? JSON.parse(readFileSync74(committedPath, "utf8")).site : void 0;
59483
59660
  if (committedSite !== void 0 && overlay.site === void 0) {
59484
59661
  throw new Error(
59485
59662
  `shell "${shell}" is a site shell: its contract names the customer's website (a \`site\` block), which this analysis does not produce yet. Write the site block into a contract and run \`npm run prepare:customer -- --contract <file>\` in ${shellAppDir(shell)} (or \`himi dist --shell ${shell}\`).`
@@ -59506,11 +59683,11 @@ async function applyOverlays(input) {
59506
59683
  note: `prepare:customer --contract with a merged contract (committed provisioning.json untouched)${unpairedInRepo ? ` \xB7 ${unpairedInRepo}` : ""}`
59507
59684
  };
59508
59685
  }
59509
- const pkgProv = join61(cwd, "provisioning.json");
59686
+ const pkgProv = join62(cwd, "provisioning.json");
59510
59687
  assertNotCommitted(pkgProv, input.forbiddenProvisioning);
59511
59688
  let packageFaq = faq;
59512
- if (existsSync62(pkgProv)) {
59513
- const cur = JSON.parse(readFileSync73(pkgProv, "utf8"));
59689
+ if (existsSync63(pkgProv)) {
59690
+ const cur = JSON.parse(readFileSync74(pkgProv, "utf8"));
59514
59691
  const merged = deepMerge4(cur, overlay);
59515
59692
  packageFaq = merged.faq;
59516
59693
  writeFileSync27(pkgProv, JSON.stringify(merged, null, 2) + "\n");
@@ -59519,9 +59696,9 @@ async function applyOverlays(input) {
59519
59696
  writeFileSync27(pkgProv, JSON.stringify(overlay, null, 2) + "\n");
59520
59697
  wrote.push(pkgProv);
59521
59698
  }
59522
- const pkgTokens = join61(cwd, "tokens.json");
59523
- if (existsSync62(pkgTokens) && (tokens.colors || tokens.typography)) {
59524
- const cur = JSON.parse(readFileSync73(pkgTokens, "utf8"));
59699
+ const pkgTokens = join62(cwd, "tokens.json");
59700
+ if (existsSync63(pkgTokens) && (tokens.colors || tokens.typography)) {
59701
+ const cur = JSON.parse(readFileSync74(pkgTokens, "utf8"));
59525
59702
  let tokensIn = tokens;
59526
59703
  if (tokens.typography) {
59527
59704
  const fonts = catalogTypography(tokens.typography, cur.typography);
@@ -59546,9 +59723,9 @@ async function applyOverlays(input) {
59546
59723
  wrote.push(pkgTokens);
59547
59724
  }
59548
59725
  const homeAction = homeRefreshActionId(shell);
59549
- const homeSrc = join61(cwd, "dev/index.ts");
59550
- if (homeAction && existsSync62(homeSrc)) {
59551
- const prev = readFileSync73(homeSrc, "utf8");
59726
+ const homeSrc = join62(cwd, "dev/index.ts");
59727
+ if (homeAction && existsSync63(homeSrc)) {
59728
+ const prev = readFileSync74(homeSrc, "utf8");
59552
59729
  const patched = patchBrandParams(prev, overlay, homeAction);
59553
59730
  let next = patched.src;
59554
59731
  if (!patched.foreign && faq?.length && prev.includes('"tier5.himiBrandedFaq.load"') && !prev.includes('"staticFaq"')) {
@@ -59579,13 +59756,13 @@ async function applyOverlays(input) {
59579
59756
  notes.push(`${patched.unparsed} onAppear params block(s) in ${homeSrc} could not be read \u2014 any brand values inside them were not applied`);
59580
59757
  }
59581
59758
  }
59582
- if (!input.iconPath || !existsSync62(input.iconPath)) {
59759
+ if (!input.iconPath || !existsSync63(input.iconPath)) {
59583
59760
  const book = input.iconPath ? dirname33(dirname33(resolve45(input.iconPath))) : void 0;
59584
59761
  notes.push(
59585
59762
  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"
59586
59763
  );
59587
59764
  }
59588
- if (input.iconPath && existsSync62(input.iconPath)) {
59765
+ if (input.iconPath && existsSync63(input.iconPath)) {
59589
59766
  try {
59590
59767
  const installed = await installIcon(cwd, input.iconPath, { derivePlay: true });
59591
59768
  wrote.push(...installed.wrote);
@@ -63772,7 +63949,7 @@ __export(cli_exports, {
63772
63949
  runSiteAnalyze: () => runSiteAnalyze,
63773
63950
  runSiteFunctionCli: () => runSiteFunctionCli
63774
63951
  });
63775
- import { readFileSync as readFileSync74 } from "node:fs";
63952
+ import { readFileSync as readFileSync75 } from "node:fs";
63776
63953
  import { resolve as resolve46 } from "node:path";
63777
63954
  function assetFetchFor(_opts) {
63778
63955
  return pinnedFetch2;
@@ -63871,7 +64048,7 @@ async function runSiteFunctionCli(opts) {
63871
64048
  let source;
63872
64049
  if (opts.sourcePath) {
63873
64050
  const p = resolve46(opts.cwd ?? process.cwd(), opts.sourcePath);
63874
- source = readFileSync74(p, "utf8");
64051
+ source = readFileSync75(p, "utf8");
63875
64052
  }
63876
64053
  let body;
63877
64054
  if (opts.bodyRaw != null) {
@@ -64130,7 +64307,7 @@ __export(site_create_exports, {
64130
64307
  runSiteList: () => runSiteList,
64131
64308
  writeProvisioningBinding: () => writeProvisioningBinding
64132
64309
  });
64133
- import { existsSync as existsSync63, readFileSync as readFileSync75, writeFileSync as writeFileSync28 } from "node:fs";
64310
+ import { existsSync as existsSync64, readFileSync as readFileSync76, writeFileSync as writeFileSync28 } from "node:fs";
64134
64311
  import { resolve as resolvePath2 } from "node:path";
64135
64312
  function oauthClientName(projectName) {
64136
64313
  return `${projectName} client`;
@@ -64279,7 +64456,7 @@ async function runSiteCreate(opts) {
64279
64456
  if (!wantOauthApp) return { ...base, nextSteps: nextSteps(metaSiteId) };
64280
64457
  return { ...base, clientId, redirectUri, nextSteps: nextSteps(metaSiteId, clientId) };
64281
64458
  }
64282
- function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync63, readFileSync: readFileSync75, writeFileSync: writeFileSync28 }) {
64459
+ function writeProvisioningBinding(target, binding2, fs = { existsSync: existsSync64, readFileSync: readFileSync76, writeFileSync: writeFileSync28 }) {
64283
64460
  const msid = binding2.siteId;
64284
64461
  const path2 = resolvePath2(target);
64285
64462
  const committed = committedShellProvisioning(path2);
@@ -64952,11 +65129,11 @@ var init_context = __esm({
64952
65129
  });
64953
65130
 
64954
65131
  // src/cli.ts
64955
- import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync76, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync64, rmSync as rmSync14, readdirSync as readdirSync30, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
65132
+ import { mkdtempSync as mkdtempSync4, readFileSync as readFileSync77, mkdirSync as mkdirSync31, writeFileSync as writeFileSync29, existsSync as existsSync65, rmSync as rmSync14, readdirSync as readdirSync30, statSync as statSync17, lstatSync as lstatSync11, accessSync, constants as constants2, renameSync as renameSync7, cpSync as cpSync3 } from "node:fs";
64956
65133
  import { execFile, execFileSync as execFileSync7, spawnSync as spawnSync5, spawn as spawn6 } from "node:child_process";
64957
65134
  import { createHash as createHash28, randomUUID as randomUUID5 } from "node:crypto";
64958
65135
  import { tmpdir as tmpdir5, homedir as homedir7, hostname } from "node:os";
64959
- import { join as join62, resolve as resolve47, relative as relative11, basename as basename15, dirname as dirname34 } from "node:path";
65136
+ import { join as join63, resolve as resolve47, relative as relative12, basename as basename16, dirname as dirname34 } from "node:path";
64960
65137
  import { fileURLToPath as fileURLToPath24 } from "node:url";
64961
65138
 
64962
65139
  // ../serve-cli/src/client.ts
@@ -67072,8 +67249,8 @@ var str6 = (v) => typeof v === "string" ? v : void 0;
67072
67249
  var num2 = (v) => typeof v === "string" && v !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
67073
67250
  function embeddedTemplateCatalog() {
67074
67251
  return availableTemplates().map((name) => {
67075
- const path2 = join62(templateSourceDir(name), TEMPLATE_META_FILE);
67076
- const meta = JSON.parse(readFileSync76(path2, "utf8"));
67252
+ const path2 = join63(templateSourceDir(name), TEMPLATE_META_FILE);
67253
+ const meta = JSON.parse(readFileSync77(path2, "utf8"));
67077
67254
  if (!meta || typeof meta.title !== "string" || typeof meta.description !== "string" || !Array.isArray(meta.tags)) {
67078
67255
  throw new Error(`${path2} must be { title: string, description: string, tags: string[], category?: string }`);
67079
67256
  }
@@ -67103,7 +67280,9 @@ var USAGE = `himi \u2014 the Himalaya authoring CLI (remote dev loop)
67103
67280
  scaffold a starter content package (--template <t> starts
67104
67281
  from a reference app; --remote fetches it off the serve plane),
67105
67282
  with the agent files inside it (AGENTS.md, CLAUDE.md, the himalaya
67106
- skill, a session-start hook); --no-agent skips them
67283
+ skill, a session-start hook); --no-agent skips them. Also makes
67284
+ the build board (no browser is opened), and its next[] leads with
67285
+ THIS session's steps: the live preview, the first picture, a postcard
67107
67286
  himi template list [--remote] list starter templates + metadata (title/description/tags) + each one's start prompt
67108
67287
  himi template prompt <name> print the prompt that starts an app from that template
67109
67288
  himi template publish [--dir <app>] [--source-dir <dir>] [--name <n>] [--icon <png>] [--visibility public|unlisted]
@@ -67694,7 +67873,7 @@ WHAT A PATCH DOES NOT CARRY \u2014 all reported in the output, never silent
67694
67873
  himi docs releasing-with-himi-serve`;
67695
67874
  var COMMANDS = [
67696
67875
  { 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>]" },
67697
- { 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", required: "<name> [--dir] [--template <t>] [--remote] [--no-agent] | --templates" },
67876
+ { 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 its `next[]` LEADS with the steps for THIS session \u2014 `himi run --watch &`, the first `himi render <homeScreenId> --png --device iphone`, and that picture's `himi progress` postcard \u2014 ahead of the next-session guidance", required: "<name> [--dir] [--template <t>] [--remote] [--no-agent] | --templates" },
67698
67877
  { 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)", 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> [--content <dir>] [--links follow|drop] [--dry-run] [--force]" },
67699
67878
  { 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]" },
67700
67879
  { 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>]" },
@@ -67702,8 +67881,8 @@ var COMMANDS = [
67702
67881
  { name: "validate", summary: "lint the content package (no push): fixed-contract TypeScript checking for tier5-src workers + schema/proto + token refs + component allowlist + $service bindings + strict-enum foot-guns + capability decls + worker boot dry-run + build/compile failures. Type issues are advisory warnings normally; --strict promotes them and hygiene warnings to errors. Exit 1 on any error. Off-repo, the CLI supplies its compiler and SDK declarations.", required: "[--content <dir>] [--strict]" },
67703
67882
  { name: "test", summary: "DERIVED behavioral crawl \u2014 no test files to write. Boots each screen's real worker, replays its onAppear, then dispatches every action site the descriptor already declares (each from a fresh boot) and checks a narrow set of unambiguous invariants: no uncaught throw, never stuck on `loading`, no `$service:` token or NaN/undefined/[object Object] reaching visible text, no binding aimed at state the worker never defines, no nav param silently dropped, no dead nav target. Catches what validate/render cannot \u2014 a WRONG REDUCER passes both with ok:true. Runs the worker in a DEVICE-PARITY sandbox (no setTimeout/setInterval, as on JSC/WebView) so a bundle that would die on device fails here. Crawls on HERMETIC DATA by default \u2014 zero network: the app's own dev/net-mocks.json first (--net <file> overrides), then the config's CMS `seed:` rows, then a canned fixture of frozen real vertical responses shipped with the CLI (exercises code paths, not API-contract truth) \u2014 so the success/mapping path runs, not just the failure UI. Two boot-only HOSTILE passes (every list empty; every request 500) then check the failure/empty day \u2014 corpus-proven, so their findings are errors. NAV EDGES are followed too: every himi.open site ($item params resolved against row 0 of its list, $service against worker state) and worker _nav directive is harvested, and each baked target is booted WITH the params the source sends \u2014 detail screens finally run their content arm; edge findings are errors tagged edge:<source>. --native runs both real native decoders over the exact baked JSON bytes; --native ios or --native android selects one. It is explicit, runs only from a Himalaya SOURCE CHECKOUT (the decoder oracles are that tree's own HimalayaCore Swift package and Gradle harness, which a published install does not ship), and needs the selected local toolchain. From a source checkout, default runs name missing native verdicts in the coverage ledger; elsewhere the ledger stays silent about them rather than offering a remedy you cannot run. --offline strips all data layers (the old reject-all axis); --no-hostile skips the hostile passes. --record (EXPLICIT, never automatic) refreshes dev/net-mocks.json from live reads first: it boots each screen (load + root onAppear ONLY \u2014 nothing the app's own launch would not fire; the tap/action crawl never sees the live transport, and only proven reads \u2014 GET, or read-shaped POST (/query, /search, /list*, \u2026) \u2014 ever reach the wire), freezes the round-trips, then crawls hermetically on the fresh fixture; record against a QA site, not production. Nodes it cannot judge headless are named, never silently passed. Exit 1 on any error. The CLI carries the authoring runtime off-repo; no per-app SDK install is required. --browser runs the whole loop (build \u2192 extract \u2192 validate \u2192 render \u2192 test) in YOUR browser instead of here, so the source never reaches a pod: it opens one screen's worker + descriptor in a tab, blocks on a loopback callback, and prints the report it posts back. One screen per tab \u2014 name it. --no-open prints the URL rather than launching a browser (SSH); --timeout <ms> bounds the wait, which matters because the page has no execution deadline of its own and an infinite loop in the worker hangs the tab.", required: "[<screenId>\u2026] [--content <dir>] [--net <file>] [--budget <ms>] [--deep [n]] [--record] [--native [ios|android]] [--no-shots] [--offline] [--no-hostile] [--all-screens] [--browser] [--no-open] [--timeout <ms>] [--json]" },
67704
67883
  { name: "ux", summary: "audit or record mobile UX evidence. `audit` checks the public-safe contract, seeded blank acceptance rows, placeholder source copy, app-specific mocks, and iOS/Android audit records. It warns while existing templates migrate; `--strict` promotes missing evidence. `record` stores checks performed on an installed iOS/Android build: `--checks` is required for passed|failed and empty for unavailable, which drove nothing and needs a `--reason` instead. `record` also STAMPS the release it audited (it resolves the id from a build, the same one a push would publish), because an unforced `himi push` of a release with screens refuses when no drive is recorded for THAT release, or when the one that is recorded says `failed` \u2014 the artifact stopped being advice nobody reads (#2955). `unavailable` still satisfies it; only `failed` refuses.", required: "audit [--content <dir>] [--strict] [--json] | record --content <dir> --platform ios|android --status passed|failed|unavailable [--checks back,keyboard,...] [--reason <text>]" },
67705
- { name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. APPROVALS: `--approve <gate> "<what>"` records one the customer just gave (gates: site-create, connect, private-data, draft, promote, push-signing, qa-build) in .himi/approvals.jsonl, and `--done <gate>` / `--drop <gate>` closes it \u2014 an approval is owed until then, and the chat that held it does not survive a new session. `--brief` prints plain text for a session start: the current step, the last postcard, every open approval, and whether this app\'s agent files are stale \u2014 the SessionStart hook `himi agent-init` installs runs exactly this. Local only: nothing is sent anywhere.', required: '["<message>" [--step <1-8|name>] [--image <png>]\u2026 [--notify]] [--content <dir>] | --approve|--done|--drop <gate> ["<note>"] | --brief' },
67706
- { name: "studio", summary: "open the BUILD BOARD \u2014 one local page the owner can leave open while an agent builds their app, which fills itself in: every screen as it renders (NEW/CHANGED badges, and each earlier version kept for a filmstrip), the journey stepper and the latest postcard from `himi progress`, the validate/test/UX/draft checks as they run, the site next to the app's home screen with its palette, and the navigation the test crawl followed. Writes .himi/studio/index.html and opens it; the page re-reads its state every two seconds, so nothing needs to be served. OPT-IN BY EXISTENCE: until this command has run, no other command touches .himi/studio/ \u2014 after it, render/test/validate/push/icon/site/ux/progress each refresh the board as they finish (best-effort; never fails a command). Every image is a content-addressed copy in .himi/history/, never a live path into .himi/shots/. Local only, no secrets: it never reads a preview token, handover code or control token. --no-open prints the path instead (SSH, CI). `himi studio reel` writes the MAKING-OF: one self-contained HTML file (every image inlined, nothing fetched) with a flipbook of the home screen version by version, captioned by the postcard in effect at the time, the final screens, and every postcard \u2014 sized to stay attachable, thinning frames first. Defaults to .himi/making-of.html (never archived by push); --out to put it elsewhere.", required: "[--content <dir>] [--no-open] | reel [--out <file>] [--no-open]" },
67884
+ { name: "progress", summary: 'the build JOURNAL an agent keeps for the owner while it works. `himi progress "<what changed>" --step <1-8|name> --image <png>` appends one postcard to .himi/progress.jsonl \u2014 the step is one of the customer journey\'s eight (discover, classify, plan, implement, brand, validate, review, download) \u2014 and COPIES each image, content-addressed, into .himi/history/, because .himi/shots/ is overwritten by every render and cleared by every full `himi test`, so a path into it would later show a different picture than the one the owner was sent. --image repeats. --notify also raises a best-effort desktop notification for an owner who stepped away. A bare `himi progress` prints the journal with the current step and elapsed time \u2014 also how an agent recovers its place after its context was compacted. APPROVALS: `--approve <gate> "<what>"` records one the customer just gave (gates: site-create, connect, private-data, draft, promote, push-signing, qa-build) in .himi/approvals.jsonl, and `--done <gate>` / `--drop <gate>` closes it \u2014 an approval is owed until then, and the chat that held it does not survive a new session. `--brief` prints plain text for a session start: the current step, the last postcard, every open approval, and whether this app\'s agent files are stale \u2014 the SessionStart hook `himi agent-init` installs runs exactly this. The CLI also journals three milestones by itself \u2014 the first PNG render of the home screen (once per app), every completed `himi test` crawl, every draft push \u2014 marked `"by": "himi"`; they never move the journey step, carry no link or credential, and step aside (folded out of every reader) when the agent journals the same step or picture within five minutes. Local only: nothing is sent anywhere.', required: '["<message>" [--step <1-8|name>] [--image <png>]\u2026 [--notify]] [--content <dir>] | --approve|--done|--drop <gate> ["<note>"] | --brief' },
67885
+ { name: "studio", summary: "open the BUILD BOARD \u2014 one local page the owner can leave open while an agent builds their app, which fills itself in: every screen as it renders (NEW/CHANGED badges, and each earlier version kept for a filmstrip), the journey stepper and the latest postcard from `himi progress`, the validate/test/UX/draft checks as they run, the site next to the app's home screen with its palette, and the navigation the test crawl followed. Writes .himi/studio/index.html and opens it; the page re-reads its state every two seconds, so nothing needs to be served. OPT-IN BY EXISTENCE: until this command (or `himi init`, which makes the board for every app it creates, without opening a browser) has run, no other command touches .himi/studio/ \u2014 after it, render/test/validate/push/icon/site/ux/progress each refresh the board as they finish (best-effort; never fails a command). Every image is a content-addressed copy in .himi/history/, never a live path into .himi/shots/. Local only, no secrets: it never reads a preview token, handover code or control token. --no-open prints the path instead (SSH, CI). `himi studio reel` writes the MAKING-OF: one self-contained HTML file (every image inlined, nothing fetched) with a flipbook of the home screen version by version, captioned by the postcard in effect at the time, the final screens, and every postcard \u2014 sized to stay attachable, thinning frames first. Defaults to .himi/making-of.html (never archived by push); --out to put it elsewhere.", required: "[--content <dir>] [--no-open] | reel [--out <file>] [--no-open]" },
67707
67886
  { name: "render", summary: "headless-render a screen with the web renderer: render-tree + diagnostics, optional --png [<file>] (bare: .himi/shots/<id>.png, what the build board shows), --scheme dark, --tree. Local by default (content dir). Pass --remote --app --release to hit GET /v1/preview/\u2026/render on the serve plane after a push. Boots the worker so $service bindings resolve; --static raw bindings; --live real Wix fetches; --state injects worker state; --input supplies pre-boot navigation params; --width/--device for PNG viewport; --rtl for dir=rtl + $env:isRTL.", required: "<screenId> [--content <dir>] [--scheme dark] [--png [<file>]] [--tree] [--static] [--live] [--state <json|file>] [--input <json|file>] [--width <px> | --device <name>] [--rtl] [--remote --app --release]" },
67708
67887
  { name: "cms", summary: "inspect, provision, and seed the app's CMS (Wix Data) collections, declared as a `cms` block in config.ts. `query` is READ-ONLY \u2014 by default it fetches ONLY the physical ids `config.ts` declares (item counts + schema drift) and reports `siteScanned: false`; it never lists the site, so an UNDECLARED `himi_<app>_` collection this app owns is not reached without `--all`. `--all` SCANS the site \u2014 every collection it lists is labeled `owned`/`adopted`/`foreign`, and it describes every foreign one \u2014 id, non-system field keys + types, and the site's read role \u2014 so an app with no `cms` block at all can DISCOVER a collection to adopt (`--all` is the one case where a missing `cms` block is not 'nothing to do'). Foreign collections are described up to a cap and are never item-counted (`itemCount: null`), because a row count is one query each; `foreign.count` is how many the scan LISTED \u2014 it follows the list endpoint's pages, and `listingIncomplete: true` says when the walk stopped short of the end, with `unlistedCollections` counting what it left out when Wix gave a paging total \u2014 and `foreign.truncated` says how many of those went undescribed, each still NAMED in `foreign.others` (id + displayName), so a collection the site already has is never hidden behind the cap. The scan is OWNER-scoped \u2014 it needs --site <msid> (or WIX_SITE_ID) plus owner auth via `wix token`; a visitor token can read a public collection's ITEMS but can never enumerate collections, so there is no tokenless discovery path. `create` reconciles declared collections additively and idempotently, and REFUSES any id outside the app's `himi_<app>_` namespace, so it is safe against a production site. It reports declared-vs-site `permissionDrift` but never writes permissions unless asked with --apply-permissions, so a field reconcile can't silently revert access control changed in the dashboard. `seed` inserts the git-tracked `seed:` rows into EMPTY owned collections (--force to add to a non-empty one; --ai writes placeholder rows plus a refinement prompt for you to review). Adopted collections (declared by `wixId`) are read-only and never created, updated, or seeded. Site from --site <msid> or WIX_SITE_ID; owner auth via `wix token`.", required: "query|create|seed [--site <msid>] [--content <dir>] [--all] [--dry-run] [--force] [--allow-destructive] [--apply-permissions] [--only <id>] [--ai] [--count <n>]" },
67709
67888
  { name: "data", summary: "read-only plan or explicitly provision/seed declared KV and Wix Data resources. Preview and production are separate scopes. KV administration uses the operator data plane from --data-url/HIMI_DATA_ADMIN_URL and HIMI_DATA_ADMIN_TOKEN; draft push never calls it.", required: "plan|create|seed [--site <msid>] [--environment preview|production] [--content <dir>] [--data-url <url>] [--dry-run] [--force]" },
@@ -67752,7 +67931,7 @@ var COMMANDS = [
67752
67931
  ];
67753
67932
  function cliVersion() {
67754
67933
  try {
67755
- return JSON.parse(readFileSync76(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
67934
+ return JSON.parse(readFileSync77(new URL("../package.json", import.meta.url), "utf8")).version ?? "unknown";
67756
67935
  } catch {
67757
67936
  return "unknown";
67758
67937
  }
@@ -67870,7 +68049,7 @@ function ageInDays(publishedAt, now = Date.now()) {
67870
68049
  }
67871
68050
  function packageVersion(path2) {
67872
68051
  try {
67873
- const version2 = JSON.parse(readFileSync76(path2, "utf8")).version;
68052
+ const version2 = JSON.parse(readFileSync77(path2, "utf8")).version;
67874
68053
  return typeof version2 === "string" && version2 ? version2 : void 0;
67875
68054
  } catch {
67876
68055
  return void 0;
@@ -67879,7 +68058,7 @@ function packageVersion(path2) {
67879
68058
  function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("./authoring-runtime/node_modules/@wix/himalaya/package.json", import.meta.url)) {
67880
68059
  let dir = resolve47(fromDir);
67881
68060
  for (; ; ) {
67882
- const version2 = packageVersion(join62(dir, "node_modules", "@wix", "himalaya", "package.json"));
68061
+ const version2 = packageVersion(join63(dir, "node_modules", "@wix", "himalaya", "package.json"));
67883
68062
  if (version2) return { version: version2, source: "local" };
67884
68063
  const parent = dirname34(dir);
67885
68064
  if (parent === dir) break;
@@ -67889,7 +68068,7 @@ function resolvedSdkVersion(fromDir = process.cwd(), bundledManifest = new URL("
67889
68068
  if (bundled) return { version: bundled, source: "bundled" };
67890
68069
  try {
67891
68070
  const root = execFileSync7("npm", ["root", "-g"], { stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
67892
- const version2 = root ? packageVersion(join62(root, "@wix", "himalaya", "package.json")) : void 0;
68071
+ const version2 = root ? packageVersion(join63(root, "@wix", "himalaya", "package.json")) : void 0;
67893
68072
  return version2 ? { version: version2, source: "global" } : {};
67894
68073
  } catch {
67895
68074
  return {};
@@ -67936,11 +68115,11 @@ var UPDATE_RETRY_AFTER_FAILURE_MS = 60 * 60 * 1e3;
67936
68115
  var UPDATE_SNOOZE_MS = 24 * 60 * 60 * 1e3;
67937
68116
  var UPDATE_REEXEC_ENV = "HIMI_UPDATED_ONCE";
67938
68117
  function updateCachePath(env = process.env) {
67939
- return env.HIMI_UPDATE_CACHE_FILE ?? join62(homedir7(), ".himalaya", "update-check.json");
68118
+ return env.HIMI_UPDATE_CACHE_FILE ?? join63(homedir7(), ".himalaya", "update-check.json");
67940
68119
  }
67941
68120
  function readUpdateCache(path2) {
67942
68121
  try {
67943
- const parsed = JSON.parse(readFileSync76(path2, "utf8"));
68122
+ const parsed = JSON.parse(readFileSync77(path2, "utf8"));
67944
68123
  return parsed && typeof parsed === "object" ? parsed : {};
67945
68124
  } catch {
67946
68125
  return {};
@@ -68008,12 +68187,12 @@ function createUpdateRunner(spawn7 = spawnSync5) {
68008
68187
  },
68009
68188
  installedVersion: () => {
68010
68189
  const root = globalNodeModulesRoot();
68011
- return root ? packageVersion(join62(root, "@wix", "himalaya-cli", "package.json")) : void 0;
68190
+ return root ? packageVersion(join63(root, "@wix", "himalaya-cli", "package.json")) : void 0;
68012
68191
  },
68013
68192
  reexec: (argv) => {
68014
68193
  const root = globalNodeModulesRoot();
68015
- const entry = root ? join62(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
68016
- if (!entry || !existsSync64(entry)) return void 0;
68194
+ const entry = root ? join63(root, "@wix", "himalaya-cli", "dist", "cli.mjs") : void 0;
68195
+ if (!entry || !existsSync65(entry)) return void 0;
68017
68196
  const result = spawnSync5(process.execPath, [entry, ...argv], {
68018
68197
  // inherit keeps PIPED STDIN working — commands that read stdin must survive a re-exec.
68019
68198
  stdio: "inherit",
@@ -68060,7 +68239,7 @@ function staleUpdateLockFile(cachePath, now) {
68060
68239
  for (const path2 of [`${cachePath}.lock.break`, `${cachePath}.lock`]) {
68061
68240
  let content;
68062
68241
  try {
68063
- content = readFileSync76(path2, "utf8");
68242
+ content = readFileSync77(path2, "utf8");
68064
68243
  } catch {
68065
68244
  continue;
68066
68245
  }
@@ -68083,7 +68262,7 @@ function acquireUpdateLock(cachePath, now, hooks = {}) {
68083
68262
  const mine = lockStamp(now);
68084
68263
  const readLock = (path2) => {
68085
68264
  try {
68086
- return readFileSync76(path2, "utf8");
68265
+ return readFileSync77(path2, "utf8");
68087
68266
  } catch {
68088
68267
  return void 0;
68089
68268
  }
@@ -68667,7 +68846,7 @@ function executableOnPath(name, env = process.env) {
68667
68846
  const suffixes = process.platform === "win32" ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
68668
68847
  for (const dir of entries) {
68669
68848
  for (const suffix of suffixes) {
68670
- const candidate = join62(dir, `${name}${suffix}`);
68849
+ const candidate = join63(dir, `${name}${suffix}`);
68671
68850
  try {
68672
68851
  if (!statSync17(candidate).isFile()) continue;
68673
68852
  if (process.platform !== "win32") accessSync(candidate, constants2.X_OK);
@@ -68776,7 +68955,7 @@ async function nativeConfigAdvisories(app, configSource) {
68776
68955
  const { iosProjectYml: iosProjectYml2 } = await Promise.resolve().then(() => (init_paths2(), paths_exports));
68777
68956
  const { androidOptedIn: androidOptedIn3, LABEL_IOS_SPLASH: LABEL_IOS_SPLASH2 } = await Promise.resolve().then(() => (init_splash(), splash_exports));
68778
68957
  const iosYmlPath = iosProjectYml2(app);
68779
- const iosYml = existsSync64(iosYmlPath) ? readFileSync76(iosYmlPath, "utf8") : "";
68958
+ const iosYml = existsSync65(iosYmlPath) ? readFileSync77(iosYmlPath, "utf8") : "";
68780
68959
  let optedIn = iosYml.includes(LABEL_IOS_SPLASH2);
68781
68960
  if (!optedIn) {
68782
68961
  const { androidManifestPath: androidManifestPath3 } = await Promise.resolve().then(() => (init_gen_native_config(), gen_native_config_exports));
@@ -68790,7 +68969,7 @@ async function nativeConfigAdvisories(app, configSource) {
68790
68969
  }
68791
68970
  function readConfigSource(dir) {
68792
68971
  try {
68793
- return readFileSync76(join62(dir, "config.ts"), "utf8");
68972
+ return readFileSync77(join63(dir, "config.ts"), "utf8");
68794
68973
  } catch {
68795
68974
  return null;
68796
68975
  }
@@ -68826,7 +69005,7 @@ function keptPlan(kept) {
68826
69005
  }
68827
69006
  async function initFromRemoteTemplate(name, parentDir, template, c) {
68828
69007
  const dir = resolve47(parentDir, name);
68829
- if (existsSync64(join62(dir, "himalaya.content.json"))) {
69008
+ if (existsSync65(join63(dir, "himalaya.content.json"))) {
68830
69009
  throw new Error(`content package already exists at ${dir}`);
68831
69010
  }
68832
69011
  const r = await c.fetchTemplateSource(TEMPLATE_APP_PREFIX + template);
@@ -68869,7 +69048,7 @@ function symlinkDestinationIssue(dest) {
68869
69048
  function restoreDestinationIssue(dest, force) {
68870
69049
  const link = symlinkDestinationIssue(dest);
68871
69050
  if (link) return link;
68872
- if (!existsSync64(dest)) return null;
69051
+ if (!existsSync65(dest)) return null;
68873
69052
  if (!statSync17(dest).isDirectory()) {
68874
69053
  return { error: `${dest} exists and is not a directory`, hint: "pass --out/--name to land somewhere else, or remove the file" };
68875
69054
  }
@@ -68884,7 +69063,7 @@ function restoreDestinationIssue(dest, force) {
68884
69063
  function isHimalayaMonorepoCheckout(start) {
68885
69064
  let dir = resolve47(start);
68886
69065
  for (; ; ) {
68887
- if (existsSync64(join62(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync64(join62(dir, "core", "serve", "src", "server.ts"))) return true;
69066
+ if (existsSync65(join63(dir, "tools", "himi-cli", "src", "cli.ts")) && existsSync65(join63(dir, "core", "serve", "src", "server.ts"))) return true;
68888
69067
  const parent = dirname34(dir);
68889
69068
  if (parent === dir) return false;
68890
69069
  dir = parent;
@@ -68922,7 +69101,7 @@ function shellShaDivergenceWarnings(jobs) {
68922
69101
  }
68923
69102
  async function templatePublishName(sourceDir) {
68924
69103
  const id = await appId(sourceDir);
68925
- return IDENTITY_SENTINELS.some((s) => id.includes(s)) ? basename15(sourceDir) : id;
69104
+ return IDENTITY_SENTINELS.some((s) => id.includes(s)) ? basename16(sourceDir) : id;
68926
69105
  }
68927
69106
  function templateNameError(name, from) {
68928
69107
  if (name.startsWith(TEMPLATE_APP_PREFIX)) {
@@ -68945,7 +69124,7 @@ function materializeForPreview(appDir2, slug, displayName) {
68945
69124
  Object.entries(files).map(([rel, body]) => [rel, rel === "himalaya.content.json" ? substituteIdentity(body, slug) : body])
68946
69125
  );
68947
69126
  const repoRoot2 = resolve47(fileURLToPath24(new URL("../../..", import.meta.url)));
68948
- const root = join62(repoRoot2, "node_modules", ".cache", "himi-template-preview");
69127
+ const root = join63(repoRoot2, "node_modules", ".cache", "himi-template-preview");
68949
69128
  const stillRunning = (pid) => {
68950
69129
  try {
68951
69130
  process.kill(pid, 0);
@@ -68959,12 +69138,12 @@ function materializeForPreview(appDir2, slug, displayName) {
68959
69138
  if (!entry.isDirectory()) continue;
68960
69139
  const pid = Number(entry.name.startsWith(`${slug}-`) ? entry.name.slice(slug.length + 1) : NaN);
68961
69140
  if (Number.isInteger(pid) && pid > 0 && pid !== process.pid && !stillRunning(pid)) {
68962
- rmSync14(join62(root, entry.name), { recursive: true, force: true });
69141
+ rmSync14(join63(root, entry.name), { recursive: true, force: true });
68963
69142
  }
68964
69143
  }
68965
69144
  } catch {
68966
69145
  }
68967
- const dest = join62(root, `${slug}-${process.pid}`);
69146
+ const dest = join63(root, `${slug}-${process.pid}`);
68968
69147
  rmSync14(dest, { recursive: true, force: true });
68969
69148
  mkdirSync31(dest, { recursive: true });
68970
69149
  try {
@@ -68977,17 +69156,17 @@ function materializeForPreview(appDir2, slug, displayName) {
68977
69156
  }
68978
69157
  async function appId(dir) {
68979
69158
  try {
68980
- const { readFileSync: readFileSync77 } = await import("node:fs");
68981
- const m = JSON.parse(readFileSync77(join62(dir, "himalaya.content.json"), "utf8"));
69159
+ const { readFileSync: readFileSync78 } = await import("node:fs");
69160
+ const m = JSON.parse(readFileSync78(join63(dir, "himalaya.content.json"), "utf8"));
68982
69161
  if (m.name) return m.name;
68983
69162
  } catch {
68984
69163
  }
68985
- return basename15(dir);
69164
+ return basename16(dir);
68986
69165
  }
68987
69166
  async function contentVisibility(dir) {
68988
69167
  try {
68989
- const { readFileSync: readFileSync77 } = await import("node:fs");
68990
- const m = JSON.parse(readFileSync77(join62(dir, "himalaya.content.json"), "utf8"));
69168
+ const { readFileSync: readFileSync78 } = await import("node:fs");
69169
+ const m = JSON.parse(readFileSync78(join63(dir, "himalaya.content.json"), "utf8"));
68991
69170
  if (m.visibility === "public" || m.visibility === "unlisted") return m.visibility;
68992
69171
  } catch {
68993
69172
  }
@@ -69211,7 +69390,7 @@ Seed your coding agent with \`himi agent-init\`. Guides: ${idx.portalUrl}`);
69211
69390
  ...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/" } : {},
69212
69391
  // In a git repo the refresh moves the diff identity an earlier `himi test` attested (that
69213
69392
  // hash is shared with the repo gates and deliberately exempts nothing but its own metadata).
69214
- ...existsSync64(join62(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" } : {},
69393
+ ...existsSync65(join63(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" } : {},
69215
69394
  ...res.skipped.length ? { skipped: res.skipped } : {},
69216
69395
  portalUrl: idx.portalUrl
69217
69396
  });
@@ -69234,7 +69413,7 @@ async function recordedReleaseForContentQuietly(dir, resolution, onDrift) {
69234
69413
  try {
69235
69414
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69236
69415
  const bres = await buildContentForValidation2(dir, { resolution });
69237
- out = mkdtempSync4(join62(tmpdir5(), "himi-ux-record-"));
69416
+ out = mkdtempSync4(join63(tmpdir5(), "himi-ux-record-"));
69238
69417
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69239
69418
  const outcome = await buildReleaseFromContent2(dir, out, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {}, collect: { lint: true } });
69240
69419
  const releaseId = outcome.manifest.releaseId;
@@ -69284,8 +69463,8 @@ function uxRestoreCommands(ctx) {
69284
69463
  };
69285
69464
  }
69286
69465
  function uxHandoverHint(uxEvidence, ctx) {
69287
- const { releaseId, contentArg, door, app } = ctx;
69288
- const uxRemedy = uxRecordCommands(contentArg);
69466
+ const { releaseId, contentArg: contentArg2, door, app } = ctx;
69467
+ const uxRemedy = uxRecordCommands(contentArg2);
69289
69468
  if (uxEvidence.state === "inherited-screens") {
69290
69469
  const restore = uxRestoreCommands(ctx);
69291
69470
  const alsoOwed = uxEvidence.supersededState === "failed" ? ` This package's own drive FAILED on ${uxEvidence.failedPlatforms.join(", ")}, so you owe more than a record: fix what that drive found, then record it against the restored tree \u2014 re-recording a failure without changing the app writes the same failure down again.` : uxEvidence.supersededState ? ` This package also has no drive this gate accepts (${uxEvidence.supersededState}), so the record below is a step you take AFTER the restore, not instead of it.` : "";
@@ -69412,7 +69591,7 @@ async function producedWidgetFamilies(dir) {
69412
69591
  let js;
69413
69592
  for (const candidate of [`${name}.bundle.js`, `${name}.js`, name]) {
69414
69593
  try {
69415
- js = readFileSync76(join62(outDir, candidate), "utf8");
69594
+ js = readFileSync77(join63(outDir, candidate), "utf8");
69416
69595
  break;
69417
69596
  } catch {
69418
69597
  }
@@ -69443,7 +69622,7 @@ async function producedWidgetFamilies(dir) {
69443
69622
  }
69444
69623
  return { families, source: "dist/bundles" };
69445
69624
  }
69446
- var boardOpen = (dir) => existsSync64(join62(dir, ".himi", "studio", "index.html"));
69625
+ var boardOpen = (dir) => existsSync65(join63(dir, ".himi", "studio", "index.html"));
69447
69626
  var BOARD_REFRESHERS = /* @__PURE__ */ new Set(["render", "test", "validate", "build", "push", "icon", "site", "ux", "progress"]);
69448
69627
  async function leaveForBoard(dir, record2, value) {
69449
69628
  if (!boardOpen(dir)) return;
@@ -69738,12 +69917,18 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69738
69917
  const owed = openApprovals2(readApprovals2(dir).events).length;
69739
69918
  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`);
69740
69919
  }
69741
- const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f) => existsSync64(join62(parentDir, f)));
69920
+ const planInParent = ["SPEC.md", "MOBILE-UX.md"].filter((f) => existsSync65(join63(parentDir, f)));
69742
69921
  if (planInParent.length) {
69743
69922
  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}`);
69744
69923
  }
69745
69924
  return { ...seeded, next };
69746
69925
  };
69926
+ const startSession = async (dir, agent2) => {
69927
+ const { openBoardForSession: openBoardForSession2, sessionSteps: sessionSteps2, homeScreenIdOf: homeScreenIdOf2 } = await Promise.resolve().then(() => (init_milestones(), milestones_exports));
69928
+ const board = await openBoardForSession2(dir);
69929
+ const { next: later2, ...rest } = agent2;
69930
+ return { ...rest, board, next: [...sessionSteps2({ dir, home: homeScreenIdOf2(dir) }), ...later2 ?? []] };
69931
+ };
69747
69932
  if (template && flags.remote === true) {
69748
69933
  let fetched;
69749
69934
  try {
@@ -69756,14 +69941,14 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69756
69941
  io.error(JSON.stringify({ warning: `remote template fetch failed (${e.message}); falling back to the bundled "${template}"` }));
69757
69942
  }
69758
69943
  if (fetched) {
69759
- const agent2 = await withAgentFiles(fetched.dir);
69944
+ const agent2 = await startSession(fetched.dir, await withAgentFiles(fetched.dir));
69760
69945
  const pushSetup2 = await offerPushSetup2({ contentDir: fetched.dir, flags, io });
69761
69946
  print({ ok: true, app: name, dir: fetched.dir, template, source: "remote", files: fetched.files, ...keptPlan(fetched.kept), ...agent2, pushSetup: pushSetup2 });
69762
69947
  return 0;
69763
69948
  }
69764
69949
  }
69765
69950
  const res = scaffoldContentPackage(name, parentDir, template);
69766
- const agent = await withAgentFiles(res.dir);
69951
+ const agent = await startSession(res.dir, await withAgentFiles(res.dir));
69767
69952
  const pushSetup = await offerPushSetup2({ contentDir: res.dir, flags, io });
69768
69953
  print({ ok: true, app: name, dir: res.dir, ...template ? { template, source: "embedded" } : {}, files: res.files, ...keptPlan(res.kept), ...agent, pushSetup });
69769
69954
  return 0;
@@ -69865,7 +70050,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69865
70050
  const sourceDir = resolve47(str6(flags["source-dir"]) ?? appDir2);
69866
70051
  let meta;
69867
70052
  try {
69868
- meta = JSON.parse(readFileSync76(join62(sourceDir, TEMPLATE_META_FILE), "utf8"));
70053
+ meta = JSON.parse(readFileSync77(join63(sourceDir, TEMPLATE_META_FILE), "utf8"));
69869
70054
  } catch {
69870
70055
  io.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>)` }));
69871
70056
  return 2;
@@ -69875,7 +70060,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69875
70060
  return 2;
69876
70061
  }
69877
70062
  try {
69878
- const appearance = extractTemplateAppearance(JSON.parse(readFileSync76(join62(sourceDir, "tokens.json"), "utf8")));
70063
+ const appearance = extractTemplateAppearance(JSON.parse(readFileSync77(join63(sourceDir, "tokens.json"), "utf8")));
69879
70064
  meta = { ...appearance, ...meta };
69880
70065
  } catch {
69881
70066
  }
@@ -69897,7 +70082,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69897
70082
  const force = flags.force === true;
69898
70083
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
69899
70084
  const bres = await buildContentForValidation2(buildDir, { resolution: resolutionFlag(flags) });
69900
- const out = mkdtempSync4(join62(tmpdir5(), "himi-template-"));
70085
+ const out = mkdtempSync4(join63(tmpdir5(), "himi-template-"));
69901
70086
  publishScratch.push(out);
69902
70087
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
69903
70088
  const outcome = await buildReleaseFromContent2(buildDir, out, {
@@ -69951,7 +70136,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
69951
70136
  let iconHash;
69952
70137
  if (iconPath) {
69953
70138
  try {
69954
- const bytes2 = readFileSync76(iconPath);
70139
+ const bytes2 = readFileSync77(iconPath);
69955
70140
  payload.icon = bytes2.toString("base64");
69956
70141
  iconHash = createHash28("sha256").update(bytes2).digest("hex");
69957
70142
  } catch {
@@ -70194,7 +70379,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70194
70379
  const { navIssues: navIssues2 } = await Promise.resolve().then(() => (init_nav_lint(), nav_lint_exports));
70195
70380
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
70196
70381
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
70197
- const out = mkdtempSync4(join62(tmpdir5(), "himi-validate-"));
70382
+ const out = mkdtempSync4(join63(tmpdir5(), "himi-validate-"));
70198
70383
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
70199
70384
  const outcome = await buildReleaseFromContent2(dir, out, {
70200
70385
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -70237,7 +70422,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70237
70422
  const path2 = dpxSessionTokenPath();
70238
70423
  let raw;
70239
70424
  try {
70240
- raw = readFileSync76(path2, "utf8");
70425
+ raw = readFileSync77(path2, "utf8");
70241
70426
  } catch {
70242
70427
  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`);
70243
70428
  }
@@ -70357,13 +70542,13 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70357
70542
  case "studio": {
70358
70543
  const { openStudio: openStudio2 } = await Promise.resolve().then(() => (init_studio(), studio_exports));
70359
70544
  const dir = contentDir(flags);
70360
- if (!existsSync64(join62(dir, "himalaya.content.json"))) {
70545
+ if (!existsSync65(join63(dir, "himalaya.content.json"))) {
70361
70546
  io.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/" }));
70362
70547
  return 2;
70363
70548
  }
70364
70549
  if (pos[1] === "reel") {
70365
70550
  const { buildReel: buildReel2 } = await Promise.resolve().then(() => (init_studio_reel(), studio_reel_exports));
70366
- const out = resolve47(str6(flags.out) ?? join62(dir, ".himi", "making-of.html"));
70551
+ const out = resolve47(str6(flags.out) ?? join63(dir, ".himi", "making-of.html"));
70367
70552
  const { html, plan } = buildReel2(dir);
70368
70553
  mkdirSync31(dirname34(out), { recursive: true });
70369
70554
  writeFileSync29(out, html);
@@ -70429,8 +70614,8 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70429
70614
  formatBrief: formatBrief2
70430
70615
  } = await Promise.resolve().then(() => (init_progress(), progress_exports));
70431
70616
  const dir = contentDir(flags);
70432
- const journal = journalPath2(dir);
70433
- const isContentPackage = existsSync64(join62(dir, "himalaya.content.json"));
70617
+ const journal2 = journalPath2(dir);
70618
+ const isContentPackage = existsSync65(join63(dir, "himalaya.content.json"));
70434
70619
  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" };
70435
70620
  const message = pos.slice(1).join(" ").trim();
70436
70621
  const approvalFlags = ["approve", "done", "drop"].filter((f) => flags[f] !== void 0);
@@ -70482,7 +70667,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70482
70667
  }
70483
70668
  const { entries, skipped } = readProgress2(dir);
70484
70669
  const approvals = openApprovals2(readApprovals2(dir).events);
70485
- print({ ok: true, journal, ...summarizeProgress2(entries), entries, ...approvals.length ? { approvals } : {}, ...skipped ? { skipped } : {}, ...hint });
70670
+ print({ ok: true, journal: journal2, ...summarizeProgress2(entries), entries, ...approvals.length ? { approvals } : {}, ...skipped ? { skipped } : {}, ...hint });
70486
70671
  return 0;
70487
70672
  }
70488
70673
  let step;
@@ -70503,7 +70688,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70503
70688
  const entry = appendProgress2(dir, { message, ...step ? { step } : {}, images });
70504
70689
  const notified = flags.notify === true ? await notifyDesktop2(`himi \xB7 ${appLabel2(dir) ?? "your app"}`, message) : void 0;
70505
70690
  const { count: count2 } = summarizeProgress2(readProgress2(dir).entries);
70506
- print({ ok: true, entry, journal, count: count2, ...notified === void 0 ? {} : { notified }, ...hint });
70691
+ print({ ok: true, entry, journal: journal2, count: count2, ...notified === void 0 ? {} : { notified }, ...hint });
70507
70692
  return 0;
70508
70693
  } catch (e) {
70509
70694
  if (e instanceof ProgressError2) {
@@ -70566,7 +70751,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70566
70751
  case "test": {
70567
70752
  const dir = contentDir(flags);
70568
70753
  const structuredOutput = flags.json === true || io.isTTY !== true;
70569
- if (existsSync64(join62(dir, "functions"))) {
70754
+ if (existsSync65(join63(dir, "functions"))) {
70570
70755
  const { invokeLocalFunction: invokeLocalFunction2, loadFunctionsApp: loadFunctionsApp2 } = await Promise.resolve().then(() => (init_functions(), functions_exports));
70571
70756
  const loaded = await loadFunctionsApp2(dir);
70572
70757
  const results = [];
@@ -70653,7 +70838,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70653
70838
  }
70654
70839
  }
70655
70840
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
70656
- const out = mkdtempSync4(join62(tmpdir5(), "himi-test-"));
70841
+ const out = mkdtempSync4(join63(tmpdir5(), "himi-test-"));
70657
70842
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
70658
70843
  const outcome = await buildReleaseFromContent2(dir, out, {
70659
70844
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -70698,7 +70883,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70698
70883
  io.error(JSON.stringify({ error: `screen "${screen.screenId}" has no Tier-5 worker, so there is nothing for the browser loop to build` }));
70699
70884
  return 2;
70700
70885
  }
70701
- const sources = collectWorkerSources2(join62(dir, "tier5-src"), screen.bundleName);
70886
+ const sources = collectWorkerSources2(join63(dir, "tier5-src"), screen.bundleName);
70702
70887
  if (!sources) {
70703
70888
  io.error(JSON.stringify({ error: `no worker source found at tier5-src/${screen.bundleName} \u2014 the browser loop compiles SOURCE, not the built bundle` }));
70704
70889
  return 2;
@@ -70863,7 +71048,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70863
71048
  const sp = await runShotsPass2({
70864
71049
  descriptors: scopeDescriptors2(outcome.descriptors ?? [], pos.slice(1), invalidScreens),
70865
71050
  tokens: outcome.tokens,
70866
- outDir: join62(dir, ".himi", "shots"),
71051
+ outDir: join63(dir, ".himi", "shots"),
70867
71052
  width: 390,
70868
71053
  layers: { rules: shotRules, ...cmsSeed ? { cmsSeed } : {}, ...canned ? { canned: canned.byKey } : {} },
70869
71054
  ...budgetMs ? { budgetMs } : {},
@@ -70901,7 +71086,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70901
71086
  releaseId: result.releaseId,
70902
71087
  releasePayload: release,
70903
71088
  descriptors: attestedDescriptors,
70904
- screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join62(dir, ".himi", "shots", name)])),
71089
+ screenshots: Object.fromEntries(writtenShots.filter((name) => crawledScreens.has(name.replace(/\.png$/, ""))).map((name) => [name.replace(/\.png$/, ""), join63(dir, ".himi", "shots", name)])),
70905
71090
  coverage: {
70906
71091
  screens: crawledScreens.size,
70907
71092
  actionsDispatched: result.actions.exercised,
@@ -70913,14 +71098,30 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70913
71098
  counts: { errors: result.counts.errors, warnings: result.counts.warnings, gaps: ledger.length }
70914
71099
  }
70915
71100
  });
71101
+ const home = outcome.homeScreenId;
71102
+ const { afterTestRun: afterTestRun2 } = await Promise.resolve().then(() => (init_milestones(), milestones_exports));
71103
+ const afterRun = afterTestRun2(dir, {
71104
+ runId: attestation.runId,
71105
+ screens: crawledScreens.size,
71106
+ actions: result.actions.exercised,
71107
+ errors: result.counts.errors,
71108
+ ...result.navGraph ? { broken: result.navGraph.broken.length, unreached: result.navGraph.unreached.length } : {},
71109
+ ...home && writtenShots.includes(`${home}.png`) ? { homeShot: join63(dir, ".himi", "shots", `${home}.png`) } : {},
71110
+ boardOpen: boardOpen(dir)
71111
+ });
70916
71112
  if (structuredOutput) {
70917
- print(foldLegacyReport(legacy, {
70918
- source: "test",
70919
- evidence: { runId: attestation.runId, ts: attestation.runId.slice(0, 24), gitDiffHash },
70920
- gaps: ledger,
70921
- artifacts: result.shots?.dir ? [{ kind: "shots-dir", path: result.shots.dir }] : []
70922
- }));
70923
- } else io.log(formatReport2(result) + formatDelta2(delta) + formatLedger2(ledger, trend));
71113
+ print({
71114
+ ...foldLegacyReport(legacy, {
71115
+ source: "test",
71116
+ evidence: { runId: attestation.runId, ts: attestation.runId.slice(0, 24), gitDiffHash },
71117
+ gaps: ledger,
71118
+ artifacts: result.shots?.dir ? [{ kind: "shots-dir", path: result.shots.dir }] : []
71119
+ }),
71120
+ ...afterRun.postcard ? { postcard: afterRun.postcard } : {},
71121
+ next: afterRun.next
71122
+ });
71123
+ } else io.log(formatReport2(result) + formatDelta2(delta) + formatLedger2(ledger, trend) + `
71124
+ ${afterRun.next.join("\n")}`);
70924
71125
  await leaveForBoard(dir, "edges", result.edges ?? []);
70925
71126
  if (result.navGraph) await leaveForBoard(dir, "map", result.navGraph);
70926
71127
  return result.ok ? 0 : 1;
@@ -70992,7 +71193,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
70992
71193
  return 2;
70993
71194
  }
70994
71195
  try {
70995
- const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync76(stateArg.replace(/^@/, ""), "utf8");
71196
+ const raw = stateArg.trim().startsWith("{") ? stateArg : readFileSync77(stateArg.replace(/^@/, ""), "utf8");
70996
71197
  injectedState = JSON.parse(raw);
70997
71198
  } catch (e) {
70998
71199
  io.error(JSON.stringify({ error: `--state must be inline JSON or a path to a .json file \u2014 ${e.message}` }));
@@ -71007,7 +71208,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
71007
71208
  return 2;
71008
71209
  }
71009
71210
  try {
71010
- const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync76(inputArg.replace(/^@/, ""), "utf8");
71211
+ const raw = inputArg.trim().startsWith("{") ? inputArg : readFileSync77(inputArg.replace(/^@/, ""), "utf8");
71011
71212
  const parsed = JSON.parse(raw);
71012
71213
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) throw new Error("must be a JSON object");
71013
71214
  injectedInput = parsed;
@@ -71056,7 +71257,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
71056
71257
  }
71057
71258
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
71058
71259
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
71059
- const out = mkdtempSync4(join62(tmpdir5(), "himi-render-"));
71260
+ const out = mkdtempSync4(join63(tmpdir5(), "himi-render-"));
71060
71261
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
71061
71262
  const outcome = await buildReleaseFromContent2(dir, out, {
71062
71263
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -71070,7 +71271,7 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
71070
71271
  const diagnostics = (outcome.lint ?? []).find((s) => s.screenId === screenId)?.issues ?? [];
71071
71272
  let pngFile;
71072
71273
  let attestationPngFile;
71073
- const shotPath = join62(dir, ".himi", "shots", `${screenId}.png`);
71274
+ const shotPath = join63(dir, ".himi", "shots", `${screenId}.png`);
71074
71275
  const pngTarget = typeof flags.png === "string" ? str6(flags.png) : flags.png === true ? shotPath : void 0;
71075
71276
  if (pngTarget && r.pngBase64) {
71076
71277
  pngFile = writePngFile(pngTarget, r.pngBase64);
@@ -71138,15 +71339,20 @@ himi v${cliVersion()} \u2014 run \`himi --version\`. If authoring commands fail
71138
71339
  coverage: { screens: 1, actionsDispatched: 0, uncoveredFns: 0, gaps: provisional.counts.gaps },
71139
71340
  report: { ok: provisional.ok, counts: provisional.counts }
71140
71341
  });
71141
- print(foldLegacyReport(legacyRender, {
71142
- source: "render",
71143
- app,
71144
- screenId,
71145
- releaseId: outcome.manifest.releaseId,
71146
- findings: diagnostics,
71147
- evidence: { runId: attestation.runId, ts: attestation.runId.slice(0, 24), gitDiffHash },
71148
- artifacts: pngFile ? [{ kind: "png", path: pngFile }] : []
71149
- }));
71342
+ const afterPng = pngFile ? (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).afterRenderPng(dir, { screenId, homeScreenId: outcome.homeScreenId ?? null, pngFile, renderable: r.renderable, content: str6(flags.content) }) : null;
71343
+ print({
71344
+ ...foldLegacyReport(legacyRender, {
71345
+ source: "render",
71346
+ app,
71347
+ screenId,
71348
+ releaseId: outcome.manifest.releaseId,
71349
+ findings: diagnostics,
71350
+ evidence: { runId: attestation.runId, ts: attestation.runId.slice(0, 24), gitDiffHash },
71351
+ artifacts: pngFile ? [{ kind: "png", path: pngFile }] : []
71352
+ }),
71353
+ ...afterPng?.postcard ? { postcard: afterPng.postcard } : {},
71354
+ ...afterPng?.next.length ? { next: afterPng.next } : {}
71355
+ });
71150
71356
  return r.renderable ? 0 : 1;
71151
71357
  }
71152
71358
  case "tokens": {
@@ -71406,7 +71612,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71406
71612
  io.error(JSON.stringify(linked));
71407
71613
  return 1;
71408
71614
  }
71409
- if (existsSync64(dest)) {
71615
+ if (existsSync65(dest)) {
71410
71616
  if (!statSync17(dest).isDirectory()) {
71411
71617
  io.error(JSON.stringify({
71412
71618
  error: `${dest} exists and is not a directory`,
@@ -71419,7 +71625,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71419
71625
  io.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" }));
71420
71626
  return 1;
71421
71627
  }
71422
- if (replace && occupied && !existsSync64(join62(dest, "himalaya.content.json"))) {
71628
+ if (replace && occupied && !existsSync65(join63(dest, "himalaya.content.json"))) {
71423
71629
  io.error(JSON.stringify({
71424
71630
  error: `${dest} is not a content package \u2014 refusing to --replace it`,
71425
71631
  hint: "--replace only clears a directory that holds himalaya.content.json. Check --out/--name, or pass --force to merge into it instead."
@@ -71560,7 +71766,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71560
71766
  const force = flags.force === true;
71561
71767
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
71562
71768
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
71563
- const out = mkdtempSync4(join62(tmpdir5(), "himi-push-"));
71769
+ const out = mkdtempSync4(join63(tmpdir5(), "himi-push-"));
71564
71770
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
71565
71771
  const outcome = await buildReleaseFromContent2(dir, out, {
71566
71772
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -71586,8 +71792,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71586
71792
  const manifest = outcome.manifest;
71587
71793
  const payload = await loadReleaseFromDir(declared, out);
71588
71794
  const { shellQuote: shellQuote2 } = await Promise.resolve().then(() => (init_shell_quote(), shell_quote_exports));
71589
- const contentRel = relative11(process.cwd(), dir) || ".";
71590
- const contentArg = shellQuote2(contentRel);
71795
+ const contentRel = relative12(process.cwd(), dir) || ".";
71796
+ const contentArg2 = shellQuote2(contentRel);
71591
71797
  if (onlyRaw !== void 0) {
71592
71798
  const onlyDraft = flags.draft === true;
71593
71799
  const { parseOnlySelectors: parseOnlySelectors2, planFromPayload: planFromPayload2, notCarriedDiff: notCarriedDiff2 } = await Promise.resolve().then(() => (init_release_patch2(), release_patch_exports));
@@ -71631,7 +71837,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71631
71837
  });
71632
71838
  const patchUxContext = {
71633
71839
  releaseId: manifest.releaseId,
71634
- contentArg,
71840
+ contentArg: contentArg2,
71635
71841
  contentDir: contentRel,
71636
71842
  app,
71637
71843
  door: echoedInvocation("himi push", flags, shellQuote2),
@@ -71726,7 +71932,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71726
71932
  const staleEvidence = evidence.staleRuns > 0 ? `${evidence.staleRuns} retained attestation(s) were recorded at this commit, but none describes the content being published` : null;
71727
71933
  const attestationErrors = [...evidence.historyErrors, ...staleEvidence ? [staleEvidence] : []];
71728
71934
  if (attestationErrors.length > 0 && !force) {
71729
- const remedy = `\`himi test --content ${contentArg}\` (or \`himi render <screenId> --content ${contentArg}\`)`;
71935
+ const remedy = `\`himi test --content ${contentArg2}\` (or \`himi render <screenId> --content ${contentArg2}\`)`;
71730
71936
  const corrupted = evidence.historyErrors.length > 0;
71731
71937
  print({
71732
71938
  ok: false,
@@ -71764,7 +71970,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71764
71970
  // fixing one door and not the other is the drift the shared helpers exist to stop.
71765
71971
  hint: uxHandoverHint(uxEvidence, {
71766
71972
  releaseId: manifest.releaseId,
71767
- contentArg,
71973
+ contentArg: contentArg2,
71768
71974
  contentDir: contentRel,
71769
71975
  app,
71770
71976
  door: echoedInvocation("himi push", flags, shellQuote2),
@@ -71873,11 +72079,13 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71873
72079
  const base = serveBase();
71874
72080
  const explicitTarget = contextualDefaultTarget !== void 0 || flags.target !== void 0 || flags["serve-url"] !== void 0 || !!process.env.HIMI_SERVE_TARGET;
71875
72081
  const loopbackWarning = base ? loopbackTargetWarning(base, explicitTarget) : null;
72082
+ const afterDraft = draft ? (await Promise.resolve().then(() => (init_milestones(), milestones_exports))).afterDraftPush(dir, { app, releaseId: release, content: str6(flags.content) }) : null;
71876
72083
  print({
71877
72084
  ok: true,
71878
72085
  app,
71879
72086
  releaseId: release,
71880
72087
  draft,
72088
+ ...afterDraft?.postcard ? { postcard: afterDraft.postcard } : {},
71881
72089
  // Say so when the push acquired the slug on the caller's behalf. A claim takes a name
71882
72090
  // out of a global first-come namespace, counts against quota, and cannot be undone —
71883
72091
  // so it must never happen silently.
@@ -71918,7 +72126,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71918
72126
  // behind that: the plane keeps the previous attestation for this release id when a
71919
72127
  // push carries none, so the published release can look attested by an earlier run.
71920
72128
  ...!attestation && staleEvidence ? {
71921
- attestationNote: `${staleEvidence} \u2014 this release was published with no headless evidence of its own, and the serve plane may still show the attestation from an earlier push of this release id. Re-attest with \`himi test --content ${contentArg}\` and push again to attach one.`
72129
+ attestationNote: `${staleEvidence} \u2014 this release was published with no headless evidence of its own, and the serve plane may still show the attestation from an earlier push of this release id. Re-attest with \`himi test --content ${contentArg2}\` and push again to attach one.`
71922
72130
  } : {},
71923
72131
  // #2955 — the SAME honesty for the UX gate: `--force` published this preview without a
71924
72132
  // recorded mobile-UX drive for it, and the link below goes to a customer. Say it in the
@@ -71926,7 +72134,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71926
72134
  // the same silence one step apart.
71927
72135
  ...uxHandoverOverride(uxEvidence, {
71928
72136
  releaseId: manifest.releaseId,
71929
- contentArg,
72137
+ contentArg: contentArg2,
71930
72138
  contentDir: contentRel,
71931
72139
  app,
71932
72140
  door: echoedInvocation("himi push", flags, shellQuote2),
@@ -71972,7 +72180,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
71972
72180
  next: [
71973
72181
  `himi context --app ${app} --release ${release}${targetName() !== "local" ? ` --target ${targetName()}` : ""}`,
71974
72182
  `himi screens --app ${app} --release ${release}${targetName() !== "local" ? ` --target ${targetName()}` : ""}`,
71975
- `himi telemetry --app ${app} --release ${release}${targetName() !== "local" ? ` --target ${targetName()}` : ""}`
72183
+ `himi telemetry --app ${app} --release ${release}${targetName() !== "local" ? ` --target ${targetName()}` : ""}`,
72184
+ ...afterDraft ? [afterDraft.next] : []
71976
72185
  ],
71977
72186
  promote: draft ? `himi promote --app ${app} --release ${release}${forcedPastErrors ? " --force" : ""}${targetName() !== "local" ? ` --target ${targetName()}` : ""}` : null,
71978
72187
  ...(outcome.manifest.functions?.length ?? 0) > 0 ? {
@@ -72016,7 +72225,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72016
72225
  const displayName = str6(flags.name);
72017
72226
  const launchMode = flags.snapshot === true ? "snapshot" : "live";
72018
72227
  const packOpts = { createdBy: `himi-cli ${cliVersion()}`, launchMode, ...displayName ? { displayName } : {} };
72019
- const writePack = (root, appName, out, source) => format === "dir" ? packReleaseToDir2(root, appName, out, packOpts) : packRelease2(root, appName, out, { ...packOpts, ...source ? { source } : {} });
72228
+ const writePack = (root, appName2, out, source) => format === "dir" ? packReleaseToDir2(root, appName2, out, packOpts) : packRelease2(root, appName2, out, { ...packOpts, ...source ? { source } : {} });
72020
72229
  const releaseDir = str6(flags["release-dir"]);
72021
72230
  if (releaseDir) {
72022
72231
  const only = str6(flags.app);
@@ -72081,7 +72290,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72081
72290
  }
72082
72291
  const { buildContentForValidation: buildContentForValidation2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
72083
72292
  const bres = await buildContentForValidation2(dir, { resolution: resolutionFlag(flags) });
72084
- const built = mkdtempSync4(join62(tmpdir5(), "himi-pack-"));
72293
+ const built = mkdtempSync4(join63(tmpdir5(), "himi-pack-"));
72085
72294
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
72086
72295
  const outcome = await buildReleaseFromContent2(dir, built, {
72087
72296
  ...hasWorkers ? { tier5Dir: bundleOutDir2(dir) } : {},
@@ -72286,7 +72495,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72286
72495
  }));
72287
72496
  return 1;
72288
72497
  }
72289
- const archive = readFileSync76(resolve47(file));
72498
+ const archive = readFileSync77(resolve47(file));
72290
72499
  const result = await client2().createPackageShare({
72291
72500
  app: opened.header.app,
72292
72501
  releaseId: opened.header.releaseId,
@@ -72346,11 +72555,11 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72346
72555
  const app = await appId(dir2);
72347
72556
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
72348
72557
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
72349
- temp = mkdtempSync4(join62(tmpdir5(), "himi-run-build-"));
72558
+ temp = mkdtempSync4(join63(tmpdir5(), "himi-run-build-"));
72350
72559
  const hasWorkers = bres.built.length > 0 || bres.compileErrors.length > 0;
72351
72560
  const initial = await buildReleaseFromContent2(dir2, temp, { ...hasWorkers ? { tier5Dir: bundleOutDir2(dir2) } : {} });
72352
72561
  servedMissing = initial.missingBundles;
72353
- source = join62(temp, app);
72562
+ source = join63(temp, app);
72354
72563
  }
72355
72564
  const chromeArg = str6(flags.chrome) ?? "preview";
72356
72565
  if (chromeArg !== "preview" && chromeArg !== "app") {
@@ -72386,7 +72595,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72386
72595
  if (!recordKit) return;
72387
72596
  startedAt = (/* @__PURE__ */ new Date()).toISOString();
72388
72597
  const failure = record2(served2, { ok: true, at: startedAt });
72389
- if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join62(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
72598
+ if (!recorded) io.error(JSON.stringify({ run: "run_record_skipped", message: `${failure ?? `${join63(dir, ".himi")} is not a real directory`}, so the build board will not frame this preview.` }));
72390
72599
  await refreshBoard();
72391
72600
  };
72392
72601
  const retract = async () => {
@@ -72417,7 +72626,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72417
72626
  }
72418
72627
  const watchKit = watch2 ? await Promise.resolve().then(() => (init_run_watch(), run_watch_exports)) : null;
72419
72628
  const bus = watchKit ? new watchKit.ReloadBus() : void 0;
72420
- if (watch2) served = mkdtempSync4(join62(tmpdir5(), "himi-run-served-"));
72629
+ if (watch2) served = mkdtempSync4(join63(tmpdir5(), "himi-run-served-"));
72421
72630
  const running = await runPackage2(source, {
72422
72631
  port,
72423
72632
  chrome: chromeArg,
@@ -72439,8 +72648,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72439
72648
  await announce(running);
72440
72649
  if (watchKit && bus) {
72441
72650
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
72442
- const live2 = join62(running.releaseRoot, running.app);
72443
- const manifestOf = (tree) => JSON.parse(readFileSync76(join62(tree, "release-manifest.json"), "utf8"));
72651
+ const live2 = join63(running.releaseRoot, running.app);
72652
+ const manifestOf = (tree) => JSON.parse(readFileSync77(join63(tree, "release-manifest.json"), "utf8"));
72444
72653
  let current = manifestOf(live2);
72445
72654
  const failed2 = async (message) => {
72446
72655
  if (stopping) return;
@@ -72451,7 +72660,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72451
72660
  };
72452
72661
  rebuild = watchKit.serialize(async () => {
72453
72662
  const t0 = Date.now();
72454
- const next = mkdtempSync4(join62(tmpdir5(), "himi-run-build-"));
72663
+ const next = mkdtempSync4(join63(tmpdir5(), "himi-run-build-"));
72455
72664
  try {
72456
72665
  const bres = await buildContentBundles2(dir, { resolution: resolutionFlag(flags) });
72457
72666
  if (bres.compileErrors.length) {
@@ -72468,16 +72677,16 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
72468
72677
  return;
72469
72678
  }
72470
72679
  if (stopping) return;
72471
- const fresh = manifestOf(join62(next, running.app));
72680
+ const fresh = manifestOf(join63(next, running.app));
72472
72681
  const diff = watchKit.diffReleases(current, fresh);
72473
72682
  const { describeChanges: describeChanges2, headline: headline2 } = await Promise.resolve().then(() => (init_build_changes(), build_changes_exports));
72474
- const changes = describeChanges2(live2, join62(next, running.app), diff);
72683
+ const changes = describeChanges2(live2, join63(next, running.app), diff);
72475
72684
  const retired = `${live2}.retired-${Date.now()}`;
72476
72685
  renameSync7(live2, retired);
72477
72686
  try {
72478
- renameSync7(join62(next, running.app), live2);
72687
+ renameSync7(join63(next, running.app), live2);
72479
72688
  } catch {
72480
- cpSync3(join62(next, running.app), live2, { recursive: true });
72689
+ cpSync3(join63(next, running.app), live2, { recursive: true });
72481
72690
  }
72482
72691
  rmSync14(retired, { recursive: true, force: true });
72483
72692
  current = fresh;
@@ -73087,7 +73296,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
73087
73296
  io.error(JSON.stringify({ error: `patch ${kind} needs --file`, usage: PATCH_USAGE }));
73088
73297
  return 2;
73089
73298
  }
73090
- if (!existsSync64(file)) {
73299
+ if (!existsSync65(file)) {
73091
73300
  io.error(JSON.stringify({ error: `no such file: ${file}` }));
73092
73301
  return 2;
73093
73302
  }
@@ -73143,7 +73352,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
73143
73352
  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."
73144
73353
  };
73145
73354
  if (kind === "tokens" || kind === "app-config") {
73146
- const body = readFileSync76(file, "utf8");
73355
+ const body = readFileSync77(file, "utf8");
73147
73356
  const patch2 = kind === "tokens" ? { tokens: body } : { appConfig: body };
73148
73357
  return runReleasePatch({
73149
73358
  ...common,
@@ -73158,7 +73367,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
73158
73367
  io.error(JSON.stringify({ error: "patch screen needs a <screenId>", usage: PATCH_USAGE }));
73159
73368
  return 2;
73160
73369
  }
73161
- const descriptor = readFileSync76(file, "utf8");
73370
+ const descriptor = readFileSync77(file, "utf8");
73162
73371
  try {
73163
73372
  JSON.parse(descriptor);
73164
73373
  } catch (e) {
@@ -73198,7 +73407,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
73198
73407
  extra: { kind, screen: id, file: { path: file, bytes: Buffer.byteLength(descriptor, "utf8") } }
73199
73408
  });
73200
73409
  }
73201
- const bytes2 = readFileSync76(file);
73410
+ const bytes2 = readFileSync77(file);
73202
73411
  const sha = createHash28("sha256").update(bytes2).digest("hex");
73203
73412
  const forScreens = (str6(flags["for-screen"]) ?? "").split(",").map((s) => s.trim()).filter(Boolean);
73204
73413
  if (flags["no-rebind"] !== void 0 && flags["no-rebind"] !== true) {
@@ -73342,7 +73551,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
73342
73551
  }
73343
73552
  let def;
73344
73553
  try {
73345
- const parsed = JSON.parse(arg.startsWith("@") ? readFileSync76(arg.slice(1), "utf8") : arg);
73554
+ const parsed = JSON.parse(arg.startsWith("@") ? readFileSync77(arg.slice(1), "utf8") : arg);
73346
73555
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
73347
73556
  io.error(JSON.stringify({ error: "invalid_definition", problems: [`--definition must be a JSON object, got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`] }));
73348
73557
  return 2;
@@ -73806,7 +74015,7 @@ ${label2}`);
73806
74015
  const platform = str6(flags.platform)?.split(",").map((p) => p.trim()).filter(Boolean);
73807
74016
  {
73808
74017
  const distDir = contentDir(flags);
73809
- if (existsSync64(join62(distDir, "himalaya.content.json"))) {
74018
+ if (existsSync65(join63(distDir, "himalaya.content.json"))) {
73810
74019
  const issues = await iconIssues(distDir, flags.strict === true, { requireDeclared: true, requireVerified: true });
73811
74020
  const errors = issues.filter((i) => i.severity === "error");
73812
74021
  for (const i of issues.filter((i2) => i2.severity === "warning")) {
@@ -74075,7 +74284,7 @@ ${label2}`);
74075
74284
  const dpxPath = dpxSessionTokenPath();
74076
74285
  let dpx;
74077
74286
  try {
74078
- dpx = readFileSync76(dpxPath, "utf8").trim();
74287
+ dpx = readFileSync77(dpxPath, "utf8").trim();
74079
74288
  } catch {
74080
74289
  io.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` }));
74081
74290
  return 1;
@@ -74483,7 +74692,7 @@ ${report2.ok ? "OK \u2014 data + serve plane reachable." : typeof report2.hint =
74483
74692
  const path2 = dpxSessionTokenPath();
74484
74693
  let raw;
74485
74694
  try {
74486
- raw = readFileSync76(path2, "utf8");
74695
+ raw = readFileSync77(path2, "utf8");
74487
74696
  } catch {
74488
74697
  io.error(JSON.stringify({ error: `no DPX BO session at ${path2}` }));
74489
74698
  return 1;