@wix/himalaya-cli 0.1118.0 → 0.1119.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
@@ -24719,7 +24719,6 @@ function shouldRevealError(args) {
24719
24719
  var EMAIL_RE, URL_HOST_RE, INTEGER_RE, NUMBER_RE;
24720
24720
  var init_validation = __esm({
24721
24721
  "../../core/ts/src/validation.ts"() {
24722
- "use strict";
24723
24722
  EMAIL_RE = /^[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}$/;
24724
24723
  URL_HOST_RE = /^[A-Za-z0-9-]+(\.[A-Za-z0-9-]+)*(:[0-9]+)?$/;
24725
24724
  INTEGER_RE = /^[+-]?[0-9]+$/;
@@ -29120,7 +29119,6 @@ function validateWorkerSubtree(raw, slotId, policy, actionCatalog) {
29120
29119
  var WORKER_SUBTREE_CEILINGS, ID_PART, ACTION_ID;
29121
29120
  var init_worker_subtree = __esm({
29122
29121
  "../../core/ts/src/worker-subtree.ts"() {
29123
- "use strict";
29124
29122
  init_worker_subtree_safe_types_generated();
29125
29123
  WORKER_SUBTREE_CEILINGS = Object.freeze({
29126
29124
  maxEncodedBytes: 65536,
@@ -49227,6 +49225,7 @@ function readInitWatch(dir) {
49227
49225
  const r = JSON.parse(readFileSync52(file, "utf8"));
49228
49226
  if (r.v !== 1 || typeof r.pid !== "number" || !Number.isInteger(r.pid) || r.pid < 1) return null;
49229
49227
  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;
49228
+ if (r.root !== runRootId(dir)) return null;
49230
49229
  return r;
49231
49230
  } catch {
49232
49231
  return null;
@@ -49324,7 +49323,7 @@ async function startWatchAtInit(dir, opts) {
49324
49323
  });
49325
49324
  child.unref();
49326
49325
  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() };
49326
+ const record2 = { v: 1, pid: child.pid, root: runRootId(dir), url, board, log: WATCH_LOG, startedAt: now.toISOString() };
49328
49327
  if (realDirIn(dir, ".himi")) writeFileAtomic(join46(dir, INIT_WATCH_FILE), `${JSON.stringify(record2, null, 2)}
49329
49328
  `);
49330
49329
  return { started: true, pid: child.pid, url, board, log: WATCH_LOG };
@@ -49384,6 +49383,7 @@ var init_milestones = __esm({
49384
49383
  "src/milestones.ts"() {
49385
49384
  init_app_source();
49386
49385
  init_preview();
49386
+ init_run_identity();
49387
49387
  init_progress();
49388
49388
  init_shell_quote();
49389
49389
  init_studio_reel();
@@ -54035,6 +54035,15 @@ function symlinkComponent(base, abs) {
54035
54035
  }
54036
54036
  return void 0;
54037
54037
  }
54038
+ function workspaceRefusal(root, target) {
54039
+ const r = resolve45(root);
54040
+ const t = resolve45(r, target);
54041
+ if (!existsSync54(r)) return `the workspace folder ${r} does not exist`;
54042
+ if (!within(r, t) || !within(realpathSync7(r), realish(t))) return `"${target}" is outside the workspace folder ${r}`;
54043
+ const link = symlinkComponent(r, t);
54044
+ if (link) return `"${link}" is a symlink \u2014 paths must stay in real folders inside the workspace`;
54045
+ return null;
54046
+ }
54038
54047
  async function confinedAppDir(args, deps) {
54039
54048
  const root = resolve45(await deps.defaultDir());
54040
54049
  const dir = typeof args.dir === "string" && args.dir ? resolve45(root, args.dir) : root;
@@ -54305,6 +54314,15 @@ function guarded(h) {
54305
54314
  return async (args, deps) => {
54306
54315
  const bad2 = flagShapedInputs(args);
54307
54316
  if (bad2.length) return text6(`refused: ${bad2.join(", ")} may not start with "-" (it would be read as a himi flag)`, true);
54317
+ if (deps.workspaceBoundary) {
54318
+ const base = await deps.defaultDir();
54319
+ for (const k of PATH_ARGS) {
54320
+ const v = args[k];
54321
+ if (typeof v !== "string" || !v) continue;
54322
+ const why = workspaceRefusal(deps.workspaceBoundary, resolve46(base, v));
54323
+ if (why) return text6(`refused: ${k} ${why}`, true);
54324
+ }
54325
+ }
54308
54326
  return h(args, deps);
54309
54327
  };
54310
54328
  }
@@ -54557,7 +54575,7 @@ ${log}` : ""}`, true);
54557
54575
  }
54558
54576
  ];
54559
54577
  }
54560
- var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT;
54578
+ var MAX_TEXT2, clip, text6, json3, opt, sw, DIR2, LEARN, CHECK, LOCAL_WRITE, PUBLISH, PREVIEW_HINT, LOGIN_HINT, PATH_ARGS;
54561
54579
  var init_tools = __esm({
54562
54580
  "src/mcp/tools.ts"() {
54563
54581
  init_file_tools();
@@ -54575,6 +54593,7 @@ var init_tools = __esm({
54575
54593
  PUBLISH = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: true };
54576
54594
  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
54595
  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.";
54596
+ PATH_ARGS = ["dir", "parentDir", "file"];
54578
54597
  }
54579
54598
  });
54580
54599
 
@@ -54715,7 +54734,8 @@ async function startMcpServer(opts) {
54715
54734
  stop: (dir) => stopPreview(dir, previewDeps)
54716
54735
  },
54717
54736
  readFile: (p) => readFileSync60(p),
54718
- login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login })
54737
+ login: createLoginSession({ ...defaultLoginDeps, selfCommand: previewDeps.selfCommand, ...opts.login }),
54738
+ ...opts.workspace ? { workspaceBoundary: opts.workspace } : {}
54719
54739
  };
54720
54740
  const closed = new Promise((resolve54) => {
54721
54741
  stdin.once("end", resolve54);
@@ -57667,6 +57687,7 @@ __export(run_watch_exports, {
57667
57687
  isEmptyDiff: () => isEmptyDiff,
57668
57688
  rebuildBlocker: () => rebuildBlocker,
57669
57689
  reloadEvents: () => reloadEvents,
57690
+ removeInitWatchRecord: () => removeInitWatchRecord,
57670
57691
  removeRunRecord: () => removeRunRecord,
57671
57692
  serialize: () => serialize,
57672
57693
  watchContent: () => watchContent,
@@ -57836,6 +57857,24 @@ function removeRunRecord(dir, owner) {
57836
57857
  }
57837
57858
  rmSync15(join59(root, "run.json"), { force: true });
57838
57859
  }
57860
+ function removeInitWatchRecord(dir, owner) {
57861
+ const root = join59(dir, ".himi");
57862
+ try {
57863
+ if (!lstatSync13(root).isDirectory()) return;
57864
+ } catch {
57865
+ return;
57866
+ }
57867
+ const bytes2 = readConfined(root, "watch.json");
57868
+ if (!bytes2) return;
57869
+ let pid;
57870
+ try {
57871
+ pid = JSON.parse(bytes2.toString("utf8")).pid;
57872
+ } catch {
57873
+ return;
57874
+ }
57875
+ if (pid !== owner) return;
57876
+ rmSync15(join59(root, "watch.json"), { force: true });
57877
+ }
57839
57878
  function boardFile(dir, pathname) {
57840
57879
  if (pathname === RUN_IDENTITY_PATH) {
57841
57880
  return new Response(JSON.stringify(runIdentity(dir)), { status: 200, headers: { "content-type": "application/json", "cache-control": "no-store" } });
@@ -77537,9 +77576,30 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77537
77576
  let browserCopy = null;
77538
77577
  let servedMissing = [];
77539
77578
  const fromContent = !source;
77579
+ let dropEarlySignals;
77580
+ let dropInitRecord;
77540
77581
  try {
77541
77582
  if (!source) {
77542
77583
  const dir2 = contentDir(flags);
77584
+ if (watch2) {
77585
+ const { removeInitWatchRecord: removeInitWatchRecord2 } = await Promise.resolve().then(() => (init_run_watch(), run_watch_exports));
77586
+ dropInitRecord = () => removeInitWatchRecord2(dir2, process.pid);
77587
+ const onExit = () => dropInitRecord?.();
77588
+ process.once("exit", onExit);
77589
+ const early = [["SIGINT", 130], ["SIGTERM", 143], ["SIGHUP", 129]].map(([sig, code]) => {
77590
+ const onEarly = () => {
77591
+ dropInitRecord?.();
77592
+ process.exit(code);
77593
+ };
77594
+ process.once(sig, onEarly);
77595
+ return [sig, onEarly];
77596
+ });
77597
+ dropEarlySignals = () => {
77598
+ for (const [sig, onEarly] of early) process.removeListener(sig, onEarly);
77599
+ process.removeListener("exit", onExit);
77600
+ dropEarlySignals = void 0;
77601
+ };
77602
+ }
77543
77603
  const app = await appId(dir2);
77544
77604
  const { buildContentBundles: buildContentBundles2, buildReleaseFromContent: buildReleaseFromContent2, bundleOutDir: bundleOutDir2 } = await loadAuthoring();
77545
77605
  const bres = await buildContentBundles2(dir2, { resolution: resolutionFlag(flags) });
@@ -77573,7 +77633,10 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77573
77633
  let startedAt = "";
77574
77634
  const record2 = (served2, build2) => {
77575
77635
  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;
77636
+ if (recordKit?.writeRunRecord(dir, { url: served2.url, port: served2.port, ...served2.lanUrl ? { lanUrl: served2.lanUrl } : {}, pid: process.pid, startedAt, watching: watch2, build: build2 })) {
77637
+ recorded = true;
77638
+ if (watch2) recordKit.removeInitWatchRecord(dir, process.pid);
77639
+ }
77577
77640
  return null;
77578
77641
  } catch (e) {
77579
77642
  return e.message;
@@ -77610,6 +77673,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77610
77673
  const stop = () => {
77611
77674
  void retract().catch((e) => io.error(JSON.stringify({ run: "stop_cleanup_failed", message: e.message }))).finally(() => run2.close().finally(() => resolve54()));
77612
77675
  };
77676
+ dropEarlySignals?.();
77613
77677
  process.once("SIGINT", stop);
77614
77678
  process.once("SIGTERM", stop);
77615
77679
  });
@@ -77755,6 +77819,7 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77755
77819
  }
77756
77820
  };
77757
77821
  const onSignal = () => void stop();
77822
+ dropEarlySignals?.();
77758
77823
  for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) process.once(sig, onSignal);
77759
77824
  stopNow = (why) => void stop(why);
77760
77825
  armIdle();
@@ -77764,6 +77829,8 @@ default (no --glyph): ${DEFAULT_GLYPH2} \u2014 a mark that claims nothing, so th
77764
77829
  io.error(JSON.stringify({ error: e.message }));
77765
77830
  return 1;
77766
77831
  } finally {
77832
+ dropEarlySignals?.();
77833
+ dropInitRecord?.();
77767
77834
  if (temp) rmSync16(temp, { recursive: true, force: true });
77768
77835
  if (served) rmSync16(served, { recursive: true, force: true });
77769
77836
  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.1119.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": "095801b2c05f44cecb7cf7c8b66b69b17736a05e43bf01dd8c72dde5"
71
71
  }