@wix/himalaya-cli 0.1118.0 → 0.1120.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
@@ -6762,7 +6762,6 @@ function walk(node, registry, path2, out2) {
6762
6762
  var SYSTEM_FONT_FAMILY, TONAL_ELEVATION_ALPHA;
6763
6763
  var init_tokens = __esm({
6764
6764
  "../../core/ts/src/tokens.ts"() {
6765
- "use strict";
6766
6765
  SYSTEM_FONT_FAMILY = "$system";
6767
6766
  TONAL_ELEVATION_ALPHA = [0, 0.05, 0.08, 0.11, 0.12, 0.14];
6768
6767
  }
@@ -49227,6 +49226,7 @@ function readInitWatch(dir) {
49227
49226
  const r = JSON.parse(readFileSync52(file, "utf8"));
49228
49227
  if (r.v !== 1 || typeof r.pid !== "number" || !Number.isInteger(r.pid) || r.pid < 1) return null;
49229
49228
  if (typeof r.url !== "string" || !/^http:\/\/127\.0\.0\.1:\d+\//.test(r.url) || typeof r.board !== "string" || typeof r.log !== "string" || typeof r.startedAt !== "string") return null;
49229
+ if (r.root !== runRootId(dir)) return null;
49230
49230
  return r;
49231
49231
  } catch {
49232
49232
  return null;
@@ -49324,7 +49324,7 @@ async function startWatchAtInit(dir, opts) {
49324
49324
  });
49325
49325
  child.unref();
49326
49326
  if (!child.pid) return { skipped: "the live preview did not start (no process) \u2014 start it yourself: `himi run --watch --content <dir> &`" };
49327
- const record2 = { v: 1, pid: child.pid, url, board, log: WATCH_LOG, startedAt: now.toISOString() };
49327
+ const record2 = { v: 1, pid: child.pid, root: runRootId(dir), url, board, log: WATCH_LOG, startedAt: now.toISOString() };
49328
49328
  if (realDirIn(dir, ".himi")) writeFileAtomic(join46(dir, INIT_WATCH_FILE), `${JSON.stringify(record2, null, 2)}
49329
49329
  `);
49330
49330
  return { started: true, pid: child.pid, url, board, log: WATCH_LOG };
@@ -49384,6 +49384,7 @@ var init_milestones = __esm({
49384
49384
  "src/milestones.ts"() {
49385
49385
  init_app_source();
49386
49386
  init_preview();
49387
+ init_run_identity();
49387
49388
  init_progress();
49388
49389
  init_shell_quote();
49389
49390
  init_studio_reel();
@@ -54035,6 +54036,15 @@ function symlinkComponent(base, abs) {
54035
54036
  }
54036
54037
  return void 0;
54037
54038
  }
54039
+ function workspaceRefusal(root, target) {
54040
+ const r = resolve45(root);
54041
+ const t = resolve45(r, target);
54042
+ if (!existsSync54(r)) return `the workspace folder ${r} does not exist`;
54043
+ if (!within(r, t) || !within(realpathSync7(r), realish(t))) return `"${target}" is outside the workspace folder ${r}`;
54044
+ const link = symlinkComponent(r, t);
54045
+ if (link) return `"${link}" is a symlink \u2014 paths must stay in real folders inside the workspace`;
54046
+ return null;
54047
+ }
54038
54048
  async function confinedAppDir(args, deps) {
54039
54049
  const root = resolve45(await deps.defaultDir());
54040
54050
  const dir = typeof args.dir === "string" && args.dir ? resolve45(root, args.dir) : root;
@@ -54305,6 +54315,15 @@ function guarded(h) {
54305
54315
  return async (args, deps) => {
54306
54316
  const bad2 = flagShapedInputs(args);
54307
54317
  if (bad2.length) return text6(`refused: ${bad2.join(", ")} may not start with "-" (it would be read as a himi flag)`, true);
54318
+ if (deps.workspaceBoundary) {
54319
+ const base = await deps.defaultDir();
54320
+ for (const k of PATH_ARGS) {
54321
+ const v = args[k];
54322
+ if (typeof v !== "string" || !v) continue;
54323
+ const why = workspaceRefusal(deps.workspaceBoundary, resolve46(base, v));
54324
+ if (why) return text6(`refused: ${k} ${why}`, true);
54325
+ }
54326
+ }
54308
54327
  return h(args, deps);
54309
54328
  };
54310
54329
  }
@@ -54557,7 +54576,7 @@ ${log}` : ""}`, true);
54557
54576
  }
54558
54577
  ];
54559
54578
  }
54560
- var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT;
54579
+ var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT, PATH_ARGS;
54561
54580
  var init_tools = __esm({
54562
54581
  "src/mcp/tools.ts"() {
54563
54582
  init_file_tools();
@@ -54575,6 +54594,7 @@ var init_tools = __esm({
54575
54594
  PUBLISH = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
54576
54595
  PREVIEW_HINT = "The preview is already running: open `url` in your built-in browser \u2014 Claude Code desktop's browser pane, Cursor's Browser tool, the Browser in Codex inside the ChatGPT desktop app, VS Code's integrated browser. Do not also start the `himi-preview` launch configuration: it serves the same port, and is the way to start a preview only when none is running. A terminal-only agent should show the owner `himi_render` PNGs instead. `board` (when present) is the build board on the same origin.";
54577
54596
  LOGIN_HINT = "Not signed in to the himi serve plane. Call himi_login: it opens a browser window where the user signs in to Wix (this server never sees the credential). A user with a terminal can run `himi login --browser` instead. Then retry.";
54597
+ PATH_ARGS = ["dir", "parentDir", "file"];
54578
54598
  }
54579
54599
  });
54580
54600
 
@@ -54715,7 +54735,8 @@ async function startMcpServer(opts) {
54715
54735
  stop: (dir) => stopPreview(dir, previewDeps)
54716
54736
  },
54717
54737
  readFile: (p) => readFileSync60(p),
54718
- login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login })
54738
+ login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login }),
54739
+ ...opts.workspace ? { workspaceBoundary: opts.workspace } : {}
54719
54740
  };
54720
54741
  const closed = new Promise((resolve54) => {
54721
54742
  stdin.once("end", resolve54);
@@ -57667,6 +57688,7 @@ __export(run_watch_exports, {
57667
57688
  isEmptyDiff: () => isEmptyDiff,
57668
57689
  rebuildBlocker: () => rebuildBlocker,
57669
57690
  reloadEvents: () => reloadEvents,
57691
+ removeInitWatchRecord: () => removeInitWatchRecord,
57670
57692
  removeRunRecord: () => removeRunRecord,
57671
57693
  serialize: () => serialize,
57672
57694
  watchContent: () => watchContent,
@@ -57836,6 +57858,24 @@ function removeRunRecord(dir, owner) {
57836
57858
  }
57837
57859
  rmSync15(join59(root, "run.json"), { force: true });
57838
57860
  }
57861
+ function removeInitWatchRecord(dir, owner) {
57862
+ const root = join59(dir, ".himi");
57863
+ try {
57864
+ if (!lstatSync13(root).isDirectory()) return;
57865
+ } catch {
57866
+ return;
57867
+ }
57868
+ const bytes2 = readConfined(root, "watch.json");
57869
+ if (!bytes2) return;
57870
+ let pid;
57871
+ try {
57872
+ pid = JSON.parse(bytes2.toString("utf8")).pid;
57873
+ } catch {
57874
+ return;
57875
+ }
57876
+ if (pid !== owner) return;
57877
+ rmSync15(join59(root, "watch.json"), { force: true });
57878
+ }
57839
57879
  function boardFile(dir, pathname) {
57840
57880
  if (pathname === RUN_IDENTITY_PATH) {
57841
57881
  return new Response(JSON.stringify(runIdentity(dir)), { status: 200, headers: { "content-type": "application/json", "cache-control": "no-store" } });
@@ -77537,9 +77577,30 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77537
77577
  let browserCopy = null;
77538
77578
  let servedMissing = [];
77539
77579
  const fromContent = !source;
77580
+ let dropEarlySignals;
77581
+ let dropInitRecord;
77540
77582
  try {
77541
77583
  if (!source) {
77542
77584
  const dir2 = contentDir(flags);
77585
+ if (watch2) {
77586
+ const { removeInitWatchRecord: removeInitWatchRecord2 } = await Promise.resolve().then(() => (init_run_watch(), run_watch_exports));
77587
+ dropInitRecord = () => removeInitWatchRecord2(dir2, process.pid);
77588
+ const onExit = () => dropInitRecord?.();
77589
+ process.once("exit", onExit);
77590
+ const early = [["SIGINT", 130], ["SIGTERM", 143], ["SIGHUP", 129]].map(([sig, code]) => {
77591
+ const onEarly = () => {
77592
+ dropInitRecord?.();
77593
+ process.exit(code);
77594
+ };
77595
+ process.once(sig, onEarly);
77596
+ return [sig, onEarly];
77597
+ });
77598
+ dropEarlySignals = () => {
77599
+ for (const [sig, onEarly] of early) process.removeListener(sig, onEarly);
77600
+ process.removeListener("exit", onExit);
77601
+ dropEarlySignals = void 0;
77602
+ };
77603
+ }
77543
77604
  const app = await appId(dir2);
77544
77605
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
77545
77606
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
@@ -77573,7 +77634,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77573
77634
  let startedAt = "";
77574
77635
  const record2 = (served2, build2) => {
77575
77636
  try {
77576
- if (recordKit?.writeRunRecord(dir, { url: served2.url, port: served2.port, ...served2.lanUrl ? { lanUrl: served2.lanUrl } : {}, pid: process.pid, startedAt, watching: watch2, build: build2 })) recorded = true;
77637
+ if (recordKit?.writeRunRecord(dir, { url: served2.url, port: served2.port, ...served2.lanUrl ? { lanUrl: served2.lanUrl } : {}, pid: process.pid, startedAt, watching: watch2, build: build2 })) {
77638
+ recorded = true;
77639
+ if (watch2) recordKit.removeInitWatchRecord(dir, process.pid);
77640
+ }
77577
77641
  return null;
77578
77642
  } catch (e) {
77579
77643
  return e.message;
@@ -77610,6 +77674,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77610
77674
  const stop = () => {
77611
77675
  void retract().catch((e) => io.error(JSON.stringify({ run: "stop_cleanup_failed", message: e.message }))).finally(() => run2.close().finally(() => resolve54()));
77612
77676
  };
77677
+ dropEarlySignals?.();
77613
77678
  process.once("SIGINT", stop);
77614
77679
  process.once("SIGTERM", stop);
77615
77680
  });
@@ -77755,6 +77820,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77755
77820
  }
77756
77821
  };
77757
77822
  const onSignal = () => void stop();
77823
+ dropEarlySignals?.();
77758
77824
  for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, onSignal);
77759
77825
  stopNow = (why) => void stop(why);
77760
77826
  armIdle();
@@ -77764,6 +77830,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77764
77830
  io.error(JSON.stringify({ error: e.message }));
77765
77831
  return 1;
77766
77832
  } finally {
77833
+ dropEarlySignals?.();
77834
+ dropInitRecord?.();
77767
77835
  if (temp) rmSync16(temp, { recursive: true, force: true });
77768
77836
  if (served) rmSync16(served, { recursive: true, force: true });
77769
77837
  if (browserCopy) rmSync16(browserCopy, { recursive: true, force: true });
@@ -90,7 +90,9 @@ without following links. They judge the deny-list in every spelling, because mac
90
90
  write or delete anything the source deny-list keeps off the machine: `.env` files, the `.himi/`
91
91
  secret store, MCP host configs with their keys, key files, and `node_modules`, `dist` and `.git`
92
92
  at any depth. When the host names
93
- no workspace, `HIMI_WORKSPACE` sets the folder. Coding agents edit files themselves, so the flag is
93
+ no workspace, `HIMI_WORKSPACE` sets the folder. It is then a hard boundary for EVERY tool: a
94
+ `dir`, `parentDir` or `file` argument outside it, or through a symlink, is refused. That covers the
95
+ publish and share tools, not only the file tools. Coding agents edit files themselves, so the flag is
94
96
  off by default.
95
97
 
96
98
  **What the tools will not do:** make a release live, build store binaries, touch secrets, write to a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/himalaya-cli",
3
- "version": "0.1118.0",
3
+ "version": "0.1120.0",
4
4
  "type": "module",
5
5
  "description": "himi — the Himalaya authoring CLI. Build a Tier-5 content package on your own machine, push it to the production serve as a draft, preview it on web + Himi Player, and read worker logs back — the remote dev loop. Wraps @himalaya/serve-cli for transport.",
6
6
  "keywords": [
@@ -67,5 +67,5 @@
67
67
  "artifactId": "himalaya-cli"
68
68
  }
69
69
  },
70
- "falconPackageHash": "0784e4485922664a2162f44a261e577db768d5fea176a56d70f0803f"
70
+ "falconPackageHash": "98cf64fc20ceea962129bd83a948d2491f2a9672af068c757d9ca1d3"
71
71
  }