@wayai/cli 0.3.163 → 0.3.165

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/index.js CHANGED
@@ -17455,40 +17455,214 @@ var init_expected = __esm({
17455
17455
  }
17456
17456
  });
17457
17457
 
17458
- // src/lib/layout.ts
17458
+ // src/lib/fs-safety.ts
17459
17459
  import * as fs from "fs";
17460
17460
  import * as path from "path";
17461
+ function fileHasBytes(abs, data) {
17462
+ let st;
17463
+ try {
17464
+ st = fs.lstatSync(abs);
17465
+ } catch {
17466
+ return false;
17467
+ }
17468
+ if (!st.isFile() || st.isSymbolicLink()) return false;
17469
+ try {
17470
+ return fs.readFileSync(abs).equals(data);
17471
+ } catch {
17472
+ return false;
17473
+ }
17474
+ }
17475
+ function realpathContained(root, abs) {
17476
+ let realRoot;
17477
+ let realAbs;
17478
+ try {
17479
+ realRoot = fs.realpathSync(root);
17480
+ realAbs = fs.realpathSync(abs);
17481
+ } catch {
17482
+ return false;
17483
+ }
17484
+ return realAbs === realRoot || realAbs.startsWith(realRoot + path.sep);
17485
+ }
17486
+ function mkdirTolerateRace(dir) {
17487
+ try {
17488
+ fs.mkdirSync(dir);
17489
+ return true;
17490
+ } catch (err) {
17491
+ if (err.code !== "EEXIST") throw err;
17492
+ let st;
17493
+ try {
17494
+ st = fs.lstatSync(dir);
17495
+ } catch {
17496
+ st = void 0;
17497
+ }
17498
+ return !!st?.isDirectory();
17499
+ }
17500
+ }
17501
+ function ensureRealSubdirNoSymlink(root, target, create) {
17502
+ const rootResolved = path.resolve(root);
17503
+ const targetResolved = path.resolve(target);
17504
+ if (targetResolved !== rootResolved && !targetResolved.startsWith(rootResolved + path.sep)) return false;
17505
+ try {
17506
+ if (!fs.lstatSync(rootResolved).isDirectory()) return false;
17507
+ } catch {
17508
+ return false;
17509
+ }
17510
+ const rel = path.relative(rootResolved, targetResolved);
17511
+ if (rel === "") return true;
17512
+ let cur = rootResolved;
17513
+ for (const segment of rel.split(path.sep)) {
17514
+ cur = path.join(cur, segment);
17515
+ let st;
17516
+ try {
17517
+ st = fs.lstatSync(cur);
17518
+ } catch {
17519
+ st = void 0;
17520
+ }
17521
+ if (st) {
17522
+ if (!st.isDirectory()) return false;
17523
+ } else if (create) {
17524
+ if (!mkdirTolerateRace(cur)) return false;
17525
+ } else {
17526
+ return true;
17527
+ }
17528
+ }
17529
+ return true;
17530
+ }
17531
+ function requireRealSubdirNoSymlink(root, target, create) {
17532
+ if (ensureRealSubdirNoSymlink(root, target, create)) return;
17533
+ throw expected(
17534
+ `${path.relative(path.dirname(root), target)} is reached through a symlink (or a file is in the way). The CLI never reads or writes workspace config through a symlink \u2014 make it a real directory.`
17535
+ );
17536
+ }
17537
+ function requireRealHubFolder(hubFolder, create) {
17538
+ if (!create) {
17539
+ try {
17540
+ fs.lstatSync(hubFolder);
17541
+ } catch {
17542
+ return;
17543
+ }
17544
+ }
17545
+ requireRealSubdirNoSymlink(path.dirname(hubFolder), hubFolder, create);
17546
+ for (const sub of HUB_MANAGED_SUBDIRS) {
17547
+ requireRealSubdirNoSymlink(hubFolder, path.join(hubFolder, sub), false);
17548
+ }
17549
+ for (const name of HUB_CONFIG_FILES) {
17550
+ let st;
17551
+ try {
17552
+ st = fs.lstatSync(path.join(hubFolder, name));
17553
+ } catch {
17554
+ continue;
17555
+ }
17556
+ if (!st.isFile()) {
17557
+ throw expected(
17558
+ `${path.join(path.basename(hubFolder), name)} is a symlink (or not a file). The CLI never reads or writes workspace config through a symlink \u2014 make it a real file.`
17559
+ );
17560
+ }
17561
+ }
17562
+ }
17563
+ function readRealFileOrThrow(root, abs) {
17564
+ let st;
17565
+ try {
17566
+ st = fs.lstatSync(abs);
17567
+ } catch {
17568
+ return null;
17569
+ }
17570
+ const bytes = st.isFile() ? readFileNoFollow(root, abs) : null;
17571
+ if (!bytes) {
17572
+ throw expected(
17573
+ `${path.relative(path.dirname(root), abs)} is a symlink (or reached through one). The CLI never reads or writes workspace config through a symlink \u2014 make it a real file.`
17574
+ );
17575
+ }
17576
+ return bytes.toString("utf-8");
17577
+ }
17578
+ function readFileNoFollow(root, abs) {
17579
+ if (!ensureRealSubdirNoSymlink(root, path.dirname(abs), false)) return null;
17580
+ try {
17581
+ if (!fs.lstatSync(abs).isFile()) return null;
17582
+ return fs.readFileSync(abs);
17583
+ } catch {
17584
+ return null;
17585
+ }
17586
+ }
17587
+ function writeFileNoFollow(root, abs, data) {
17588
+ const parent = path.dirname(abs);
17589
+ if (!ensureRealSubdirNoSymlink(root, parent, true)) return false;
17590
+ let st;
17591
+ try {
17592
+ st = fs.lstatSync(abs);
17593
+ } catch {
17594
+ }
17595
+ if (st?.isSymbolicLink()) fs.rmSync(abs);
17596
+ fs.writeFileSync(abs, data);
17597
+ return true;
17598
+ }
17599
+ function createFileNoFollow(root, abs, data) {
17600
+ if (!ensureRealSubdirNoSymlink(root, path.dirname(abs), true)) return "refused";
17601
+ try {
17602
+ fs.writeFileSync(abs, data, { flag: "wx" });
17603
+ } catch (err) {
17604
+ if (err.code === "EEXIST") return "exists";
17605
+ throw err;
17606
+ }
17607
+ return "created";
17608
+ }
17609
+ var HUB_MANAGED_SUBDIRS, HUB_CONFIG_FILES;
17610
+ var init_fs_safety = __esm({
17611
+ "src/lib/fs-safety.ts"() {
17612
+ "use strict";
17613
+ init_expected();
17614
+ HUB_MANAGED_SUBDIRS = ["agents", "evals", "journeys", "resources", "attachments"];
17615
+ HUB_CONFIG_FILES = ["hub.yaml", "wayai.yaml"];
17616
+ }
17617
+ });
17618
+
17619
+ // src/lib/layout.ts
17620
+ import * as fs2 from "fs";
17621
+ import * as path2 from "path";
17461
17622
  function isDirectory(p) {
17462
17623
  try {
17463
- return fs.statSync(p).isDirectory();
17624
+ return fs2.statSync(p).isDirectory();
17625
+ } catch {
17626
+ return false;
17627
+ }
17628
+ }
17629
+ function isRealDirectory(p) {
17630
+ try {
17631
+ return fs2.lstatSync(p).isDirectory();
17464
17632
  } catch {
17465
17633
  return false;
17466
17634
  }
17467
17635
  }
17468
17636
  function resolveLayout(gitRoot, layout = WAYAI_LAYOUT) {
17469
- const newWs = path.join(gitRoot, layout.wsDir);
17470
- const legacyWs = path.join(gitRoot, layout.legacy.wsDir);
17471
- const legacyOrg = path.join(gitRoot, layout.legacy.orgAtRoot);
17472
- const legacyExists = isDirectory(legacyWs) || isDirectory(legacyOrg);
17473
- const basesDir = path.join(newWs, layout.basesSubdir);
17474
- const hubsDir = path.join(newWs, layout.hubsSubdir);
17475
- const orgDir = path.join(newWs, layout.orgSubdir);
17637
+ const newWs = path2.join(gitRoot, layout.wsDir);
17638
+ const legacyWs = path2.join(gitRoot, layout.legacy.wsDir);
17639
+ const legacyOrg = path2.join(gitRoot, layout.legacy.orgAtRoot);
17640
+ const legacyExists = isRealDirectory(legacyWs) || isRealDirectory(legacyOrg);
17641
+ const basesDir = path2.join(newWs, layout.basesSubdir);
17642
+ const hubsDir = path2.join(newWs, layout.hubsSubdir);
17643
+ const orgDir = path2.join(newWs, layout.orgSubdir);
17476
17644
  const newDisplacesLegacy = () => isDirectory(hubsDir) || isDirectory(orgDir) || isDirectory(basesDir);
17645
+ let resolved;
17477
17646
  if (legacyExists && !newDisplacesLegacy()) {
17478
- return { hubsDir: legacyWs, orgDir: legacyOrg, basesDir, isLegacy: true, legacyAlsoPresent: false };
17647
+ resolved = { hubsDir: legacyWs, orgDir: legacyOrg, basesDir, isLegacy: true, legacyAlsoPresent: false };
17648
+ } else {
17649
+ resolved = { hubsDir, orgDir, basesDir, isLegacy: false, legacyAlsoPresent: legacyExists };
17650
+ }
17651
+ for (const dir of [resolved.hubsDir, resolved.orgDir, resolved.basesDir]) {
17652
+ requireRealSubdirNoSymlink(gitRoot, dir, false);
17479
17653
  }
17480
- return { hubsDir, orgDir, basesDir, isLegacy: false, legacyAlsoPresent: legacyExists };
17654
+ return resolved;
17481
17655
  }
17482
17656
  function resolveBasesDir(gitRoot) {
17483
17657
  return resolveLayout(gitRoot).basesDir;
17484
17658
  }
17485
17659
  function hubsDirLabel(gitRoot) {
17486
- if (!gitRoot) return path.join(WAYAI_LAYOUT.wsDir, WAYAI_LAYOUT.hubsSubdir);
17487
- return path.relative(gitRoot, resolveLayout(gitRoot).hubsDir);
17660
+ if (!gitRoot) return path2.join(WAYAI_LAYOUT.wsDir, WAYAI_LAYOUT.hubsSubdir);
17661
+ return path2.relative(gitRoot, resolveLayout(gitRoot).hubsDir);
17488
17662
  }
17489
17663
  function basesDirLabel(gitRoot) {
17490
- if (!gitRoot) return path.join(WAYAI_LAYOUT.wsDir, WAYAI_LAYOUT.basesSubdir);
17491
- return path.relative(gitRoot, resolveLayout(gitRoot).basesDir);
17664
+ if (!gitRoot) return path2.join(WAYAI_LAYOUT.wsDir, WAYAI_LAYOUT.basesSubdir);
17665
+ return path2.relative(gitRoot, resolveLayout(gitRoot).basesDir);
17492
17666
  }
17493
17667
  function warnLayoutOnce(gitRoot) {
17494
17668
  const r = resolveLayout(gitRoot);
@@ -17516,6 +17690,7 @@ var init_layout = __esm({
17516
17690
  "src/lib/layout.ts"() {
17517
17691
  "use strict";
17518
17692
  init_dist();
17693
+ init_fs_safety();
17519
17694
  WAYAI_LAYOUT = {
17520
17695
  ...WAYAI_WORKSPACE_LAYOUT,
17521
17696
  legacy: { wsDir: "workspace", orgAtRoot: "org" }
@@ -17550,8 +17725,8 @@ __export(workspace_exports, {
17550
17725
  scanWorkspaceHubs: () => scanWorkspaceHubs
17551
17726
  });
17552
17727
  import { execFileSync } from "child_process";
17553
- import * as fs2 from "fs";
17554
- import * as path2 from "path";
17728
+ import * as fs3 from "fs";
17729
+ import * as path3 from "path";
17555
17730
  import * as yaml from "js-yaml";
17556
17731
  function findGitRoot() {
17557
17732
  try {
@@ -17566,8 +17741,8 @@ function findGitRoot() {
17566
17741
  function findRepoRootSync() {
17567
17742
  let current = process.cwd();
17568
17743
  while (true) {
17569
- if (fs2.existsSync(path2.join(current, ".git"))) return current;
17570
- const parent = path2.dirname(current);
17744
+ if (fs3.existsSync(path3.join(current, ".git"))) return current;
17745
+ const parent = path3.dirname(current);
17571
17746
  if (parent === current) return null;
17572
17747
  current = parent;
17573
17748
  }
@@ -17576,7 +17751,7 @@ function detectWorkspace() {
17576
17751
  const gitRoot = findGitRoot();
17577
17752
  if (!gitRoot) return null;
17578
17753
  const { hubsDir } = resolveLayout(gitRoot);
17579
- if (!fs2.existsSync(hubsDir) || !fs2.statSync(hubsDir).isDirectory()) {
17754
+ if (!fs3.existsSync(hubsDir) || !fs3.statSync(hubsDir).isDirectory()) {
17580
17755
  return null;
17581
17756
  }
17582
17757
  return { gitRoot, workspaceDir: hubsDir };
@@ -17592,9 +17767,9 @@ function resolveWorkspaceDir() {
17592
17767
  return resolveLayout(gitRoot).hubsDir;
17593
17768
  }
17594
17769
  function readHubYaml(yamlPath) {
17595
- if (!fs2.existsSync(yamlPath)) return null;
17770
+ if (!fs3.existsSync(yamlPath)) return null;
17596
17771
  try {
17597
- const content = fs2.readFileSync(yamlPath, "utf-8");
17772
+ const content = fs3.readFileSync(yamlPath, "utf-8");
17598
17773
  const config = yaml.load(content);
17599
17774
  if (!config?.hub_id || !config.hub_environment) return null;
17600
17775
  return { hubId: config.hub_id, hubEnvironment: config.hub_environment };
@@ -17605,9 +17780,9 @@ function readHubYaml(yamlPath) {
17605
17780
  function scanWorkspaceHubs(workspaceDir) {
17606
17781
  const hubs = [];
17607
17782
  for (const top of safeReaddir(workspaceDir)) {
17608
- const topDir = path2.join(workspaceDir, top);
17609
- if (!isDirectory(topDir)) continue;
17610
- const meta = readHubYaml(path2.join(topDir, "hub.yaml")) || readHubYaml(path2.join(topDir, "wayai.yaml"));
17783
+ const topDir = path3.join(workspaceDir, top);
17784
+ if (!isRealDirectory(topDir)) continue;
17785
+ const meta = readHubYaml(path3.join(topDir, "hub.yaml")) || readHubYaml(path3.join(topDir, "wayai.yaml"));
17611
17786
  if (meta) {
17612
17787
  hubs.push({ hubFolder: topDir, hubId: meta.hubId, hubEnvironment: meta.hubEnvironment });
17613
17788
  }
@@ -17621,9 +17796,9 @@ function filterPreviewHubs(hubs) {
17621
17796
  return hubs.filter(isPreviewHub);
17622
17797
  }
17623
17798
  function readNewHubYaml(yamlPath) {
17624
- if (!fs2.existsSync(yamlPath)) return null;
17799
+ if (!fs3.existsSync(yamlPath)) return null;
17625
17800
  try {
17626
- const content = fs2.readFileSync(yamlPath, "utf-8");
17801
+ const content = fs3.readFileSync(yamlPath, "utf-8");
17627
17802
  const config = yaml.load(content);
17628
17803
  if (config?.hub_id) return null;
17629
17804
  if (!config?.hub?.name) return null;
@@ -17635,9 +17810,9 @@ function readNewHubYaml(yamlPath) {
17635
17810
  function scanNewHubs(workspaceDir) {
17636
17811
  const hubs = [];
17637
17812
  for (const top of safeReaddir(workspaceDir)) {
17638
- const topDir = path2.join(workspaceDir, top);
17639
- if (!isDirectory(topDir)) continue;
17640
- const meta = readNewHubYaml(path2.join(topDir, "hub.yaml")) || readNewHubYaml(path2.join(topDir, "wayai.yaml"));
17813
+ const topDir = path3.join(workspaceDir, top);
17814
+ if (!isRealDirectory(topDir)) continue;
17815
+ const meta = readNewHubYaml(path3.join(topDir, "hub.yaml")) || readNewHubYaml(path3.join(topDir, "wayai.yaml"));
17641
17816
  if (meta) {
17642
17817
  hubs.push({ hubFolder: topDir, hubName: meta.hubName, hubType: meta.hubType });
17643
17818
  }
@@ -17657,22 +17832,22 @@ function resolveHubFolder(workspaceDir, hubId, hubName, hubEnvironment, previewL
17657
17832
  if (match) return match.hubFolder;
17658
17833
  }
17659
17834
  const hubFolderSlug = getHubFolderSlug(hubName, hubEnvironment, hubId || "", previewLabel, branchName);
17660
- return path2.join(workspaceDir, hubFolderSlug);
17835
+ return path3.join(workspaceDir, hubFolderSlug);
17661
17836
  }
17662
17837
  function findLocalHubFolder(hubId) {
17663
17838
  const workspace = detectWorkspace();
17664
17839
  const gitRoot = findGitRoot();
17665
17840
  const workspaceDir = workspace?.workspaceDir ?? (gitRoot ? resolveLayout(gitRoot).hubsDir : null);
17666
- if (!workspaceDir || !fs2.existsSync(workspaceDir)) return null;
17841
+ if (!workspaceDir || !fs3.existsSync(workspaceDir)) return null;
17667
17842
  const match = scanWorkspaceHubs(workspaceDir).find((h) => h.hubId === hubId);
17668
17843
  return match ? match.hubFolder : null;
17669
17844
  }
17670
17845
  function getChangedHubs(workspaceDir) {
17671
17846
  const gitRoot = findGitRoot();
17672
17847
  if (!gitRoot) return [];
17673
- const relWorkspace = path2.relative(gitRoot, workspaceDir);
17674
- const normalizedRel = path2.normalize(relWorkspace);
17675
- if (normalizedRel.startsWith("..") || path2.isAbsolute(normalizedRel)) {
17848
+ const relWorkspace = path3.relative(gitRoot, workspaceDir);
17849
+ const normalizedRel = path3.normalize(relWorkspace);
17850
+ if (normalizedRel.startsWith("..") || path3.isAbsolute(normalizedRel)) {
17676
17851
  return [];
17677
17852
  }
17678
17853
  let statusOutput;
@@ -17697,9 +17872,9 @@ function getChangedHubs(workspaceDir) {
17697
17872
  if (hubFiles.length === 0) return [];
17698
17873
  const hubFolders = /* @__PURE__ */ new Set();
17699
17874
  for (const file of hubFiles) {
17700
- const absFile = path2.resolve(gitRoot, file);
17875
+ const absFile = path3.resolve(gitRoot, file);
17701
17876
  if (file.endsWith("hub.yaml") || file.endsWith("wayai.yaml")) {
17702
- hubFolders.add(path2.dirname(absFile));
17877
+ hubFolders.add(path3.dirname(absFile));
17703
17878
  } else if (file.includes("/agents/")) {
17704
17879
  const agentsIndex = absFile.lastIndexOf("/agents/");
17705
17880
  hubFolders.add(absFile.substring(0, agentsIndex));
@@ -17707,7 +17882,7 @@ function getChangedHubs(workspaceDir) {
17707
17882
  }
17708
17883
  const hubs = [];
17709
17884
  for (const hubFolder of hubFolders) {
17710
- const meta = readHubYaml(path2.join(hubFolder, "hub.yaml")) || readHubYaml(path2.join(hubFolder, "wayai.yaml"));
17885
+ const meta = readHubYaml(path3.join(hubFolder, "hub.yaml")) || readHubYaml(path3.join(hubFolder, "wayai.yaml"));
17711
17886
  if (meta) {
17712
17887
  hubs.push({ hubFolder, hubId: meta.hubId, hubEnvironment: meta.hubEnvironment });
17713
17888
  }
@@ -17742,19 +17917,19 @@ function resolveExplicitHubPath(hubPath) {
17742
17917
  }
17743
17918
  function findHubByFolderName(workspaceDir, hubFolderName) {
17744
17919
  const all = scanWorkspaceHubs(workspaceDir);
17745
- return all.find((h) => path2.basename(h.hubFolder) === hubFolderName) ?? null;
17920
+ return all.find((h) => path3.basename(h.hubFolder) === hubFolderName) ?? null;
17746
17921
  }
17747
17922
  function autoRenameHubFolder(currentFolder, hubName, hubEnvironment, hubId, previewLabel, branchName) {
17748
17923
  const expectedSlug = getHubFolderSlug(hubName, hubEnvironment, hubId, previewLabel, branchName);
17749
- const currentSlug = path2.basename(currentFolder);
17924
+ const currentSlug = path3.basename(currentFolder);
17750
17925
  if (currentSlug === expectedSlug) return currentFolder;
17751
- const targetFolder = path2.join(path2.dirname(currentFolder), expectedSlug);
17752
- if (fs2.existsSync(targetFolder)) {
17926
+ const targetFolder = path3.join(path3.dirname(currentFolder), expectedSlug);
17927
+ if (fs3.existsSync(targetFolder)) {
17753
17928
  console.warn(` Warning: skipping hub folder rename ${currentSlug} \u2192 ${expectedSlug} (target already exists)`);
17754
17929
  return currentFolder;
17755
17930
  }
17756
17931
  try {
17757
- fs2.renameSync(currentFolder, targetFolder);
17932
+ fs3.renameSync(currentFolder, targetFolder);
17758
17933
  } catch (err) {
17759
17934
  console.warn(` Warning: could not rename ${currentSlug} \u2192 ${expectedSlug}: ${err instanceof Error ? err.message : String(err)}`);
17760
17935
  return currentFolder;
@@ -17765,28 +17940,28 @@ function autoRenameHubFolder(currentFolder, hubName, hubEnvironment, hubId, prev
17765
17940
  }
17766
17941
  function findEnclosingHubFolder(workspaceDir) {
17767
17942
  let current = process.cwd();
17768
- const stop = path2.resolve(workspaceDir);
17943
+ const stop = path3.resolve(workspaceDir);
17769
17944
  while (true) {
17770
- const meta = readHubYaml(path2.join(current, "hub.yaml")) || readHubYaml(path2.join(current, "wayai.yaml"));
17945
+ const meta = readHubYaml(path3.join(current, "hub.yaml")) || readHubYaml(path3.join(current, "wayai.yaml"));
17771
17946
  if (meta) {
17772
17947
  return { hubFolder: current, hubId: meta.hubId, hubEnvironment: meta.hubEnvironment };
17773
17948
  }
17774
17949
  if (current === stop) return null;
17775
- const parent = path2.dirname(current);
17950
+ const parent = path3.dirname(current);
17776
17951
  if (parent === current) return null;
17777
17952
  current = parent;
17778
17953
  }
17779
17954
  }
17780
17955
  function findEnclosingNewHubFolder(workspaceDir) {
17781
17956
  let current = process.cwd();
17782
- const stop = path2.resolve(workspaceDir);
17957
+ const stop = path3.resolve(workspaceDir);
17783
17958
  while (true) {
17784
- const meta = readNewHubYaml(path2.join(current, "hub.yaml")) || readNewHubYaml(path2.join(current, "wayai.yaml"));
17959
+ const meta = readNewHubYaml(path3.join(current, "hub.yaml")) || readNewHubYaml(path3.join(current, "wayai.yaml"));
17785
17960
  if (meta) {
17786
17961
  return { hubFolder: current, hubName: meta.hubName, hubType: meta.hubType };
17787
17962
  }
17788
17963
  if (current === stop) return null;
17789
- const parent = path2.dirname(current);
17964
+ const parent = path3.dirname(current);
17790
17965
  if (parent === current) return null;
17791
17966
  current = parent;
17792
17967
  }
@@ -17794,10 +17969,10 @@ function findEnclosingNewHubFolder(workspaceDir) {
17794
17969
  function resolvePushTarget(workspaceDir, existingHubs, newHubs, selector) {
17795
17970
  if (selector) {
17796
17971
  const existingMatch = existingHubs.find(
17797
- (h) => h.hubId === selector || path2.basename(h.hubFolder) === selector
17972
+ (h) => h.hubId === selector || path3.basename(h.hubFolder) === selector
17798
17973
  );
17799
17974
  if (existingMatch) return { ok: true, target: { kind: "existing", hub: existingMatch } };
17800
- const newMatch = newHubs.find((h) => path2.basename(h.hubFolder) === selector);
17975
+ const newMatch = newHubs.find((h) => path3.basename(h.hubFolder) === selector);
17801
17976
  if (newMatch) return { ok: true, target: { kind: "new", hub: newMatch } };
17802
17977
  return { ok: false, reason: "selector_miss" };
17803
17978
  }
@@ -17813,10 +17988,10 @@ function resolvePushTarget(workspaceDir, existingHubs, newHubs, selector) {
17813
17988
  }
17814
17989
  function resolveNewHubForCreate(workspaceDir, existingHubs, newHubs, selector) {
17815
17990
  if (selector) {
17816
- const newMatch = newHubs.find((h) => path2.basename(h.hubFolder) === selector);
17991
+ const newMatch = newHubs.find((h) => path3.basename(h.hubFolder) === selector);
17817
17992
  if (newMatch) return { ok: true, hub: newMatch };
17818
17993
  const existingMatch = existingHubs.find(
17819
- (h) => h.hubId === selector || path2.basename(h.hubFolder) === selector
17994
+ (h) => h.hubId === selector || path3.basename(h.hubFolder) === selector
17820
17995
  );
17821
17996
  if (existingMatch) return { ok: false, reason: "exists", existing: existingMatch };
17822
17997
  return { ok: false, reason: "selector_miss" };
@@ -17843,7 +18018,7 @@ function resolveActiveHubId(args2) {
17843
18018
  const gitRoot = findGitRoot();
17844
18019
  const workspaceDir = workspace?.workspaceDir ?? (gitRoot ? resolveLayout(gitRoot).hubsDir : null);
17845
18020
  const wsLabel = hubsDirLabel(gitRoot);
17846
- if (!workspaceDir || !fs2.existsSync(workspaceDir)) {
18021
+ if (!workspaceDir || !fs3.existsSync(workspaceDir)) {
17847
18022
  if (selector && UUID_RE2.test(selector)) return selector;
17848
18023
  console.error(`No ${wsLabel}/ directory found. Run \`wayai pull --hub <uuid>\` to fetch a hub, or pass --hub <uuid>.`);
17849
18024
  process.exit(1);
@@ -17851,7 +18026,7 @@ function resolveActiveHubId(args2) {
17851
18026
  const allHubs = scanWorkspaceHubs(workspaceDir);
17852
18027
  if (selector) {
17853
18028
  const match = allHubs.find(
17854
- (h) => h.hubId === selector || path2.basename(h.hubFolder) === selector
18029
+ (h) => h.hubId === selector || path3.basename(h.hubFolder) === selector
17855
18030
  );
17856
18031
  if (match) return match.hubId;
17857
18032
  if (UUID_RE2.test(selector)) return selector;
@@ -17868,7 +18043,7 @@ function resolveActiveHubId(args2) {
17868
18043
  }
17869
18044
  console.error(`Multiple hubs in ${wsLabel}/. Pass --hub <uuid|folder-name> or run from inside a hub folder:`);
17870
18045
  for (const h of previewHubs) {
17871
- console.error(` ${path2.basename(h.hubFolder)} (${h.hubId})`);
18046
+ console.error(` ${path3.basename(h.hubFolder)} (${h.hubId})`);
17872
18047
  }
17873
18048
  process.exit(1);
17874
18049
  }
@@ -17876,12 +18051,12 @@ function localHubEnvironment(hubId) {
17876
18051
  const workspace = detectWorkspace();
17877
18052
  const gitRoot = findGitRoot();
17878
18053
  const workspaceDir = workspace?.workspaceDir ?? (gitRoot ? resolveLayout(gitRoot).hubsDir : null);
17879
- if (!workspaceDir || !fs2.existsSync(workspaceDir)) return null;
18054
+ if (!workspaceDir || !fs3.existsSync(workspaceDir)) return null;
17880
18055
  return scanWorkspaceHubs(workspaceDir).find((h) => h.hubId === hubId)?.hubEnvironment ?? null;
17881
18056
  }
17882
18057
  function safeReaddir(dir) {
17883
18058
  try {
17884
- return fs2.readdirSync(dir).filter((entry) => !entry.startsWith("."));
18059
+ return fs3.readdirSync(dir).filter((entry) => !entry.startsWith("."));
17885
18060
  } catch {
17886
18061
  return [];
17887
18062
  }
@@ -17895,19 +18070,19 @@ var init_workspace = __esm({
17895
18070
  });
17896
18071
 
17897
18072
  // src/lib/workspace-manifest.ts
17898
- import * as fs3 from "fs";
17899
- import * as path3 from "path";
18073
+ import * as fs4 from "fs";
18074
+ import * as path4 from "path";
17900
18075
  import * as yaml2 from "js-yaml";
17901
18076
  function workspaceManifestPath(gitRoot) {
17902
- return path3.join(gitRoot, WAYAI_LAYOUT.wsDir, WORKSPACE_MANIFEST_FILE);
18077
+ return path4.join(gitRoot, WAYAI_LAYOUT.wsDir, WORKSPACE_MANIFEST_FILE);
17903
18078
  }
17904
18079
  function rootConfigPath(gitRoot) {
17905
- return path3.join(gitRoot, ROOT_CONFIG_FILE);
18080
+ return path4.join(gitRoot, ROOT_CONFIG_FILE);
17906
18081
  }
17907
18082
  function loadYamlMapping(file) {
17908
18083
  let raw;
17909
18084
  try {
17910
- raw = fs3.readFileSync(file, "utf-8");
18085
+ raw = fs4.readFileSync(file, "utf-8");
17911
18086
  } catch (err) {
17912
18087
  if (err.code === "ENOENT") return { kind: "absent" };
17913
18088
  return { kind: "malformed", reason: err instanceof Error ? err.message : String(err) };
@@ -17950,8 +18125,7 @@ __export(repo_config_exports, {
17950
18125
  resolveRepoConfig: () => resolveRepoConfig,
17951
18126
  writeRepoConfig: () => writeRepoConfig
17952
18127
  });
17953
- import * as fs4 from "fs";
17954
- import * as path4 from "path";
18128
+ import * as path5 from "path";
17955
18129
  import * as yaml3 from "js-yaml";
17956
18130
  function noticeOnce(key, emit) {
17957
18131
  if (noticed.has(key)) return;
@@ -17989,7 +18163,7 @@ function legacyFieldsIn(load11) {
17989
18163
  if (load11.kind !== "ok") return [];
17990
18164
  return LEGACY_FIELDS.filter((f) => load11.doc[f] !== void 0);
17991
18165
  }
17992
- function resolve2(gitRoot) {
18166
+ function resolve3(gitRoot) {
17993
18167
  const manifestFile = workspaceManifestPath(gitRoot);
17994
18168
  const manifest = declarationFrom(loadWorkspaceManifest(gitRoot));
17995
18169
  if (manifest.kind === "invalid") {
@@ -18098,7 +18272,7 @@ function report(r, posture) {
18098
18272
  function resolveRepoConfig(root) {
18099
18273
  const gitRoot = root ?? findGitRoot();
18100
18274
  if (!gitRoot) return null;
18101
- const r = resolve2(gitRoot);
18275
+ const r = resolve3(gitRoot);
18102
18276
  report(r, "soft");
18103
18277
  return r.kind === "ok" ? r.resolved : null;
18104
18278
  }
@@ -18108,7 +18282,7 @@ function readRepoConfig(root) {
18108
18282
  function readRepoConfigUnlessBlocked(root) {
18109
18283
  const gitRoot = root ?? findGitRoot();
18110
18284
  if (!gitRoot) return null;
18111
- const r = resolve2(gitRoot);
18285
+ const r = resolve3(gitRoot);
18112
18286
  switch (r.kind) {
18113
18287
  case "ok":
18114
18288
  report(r, "soft");
@@ -18123,7 +18297,7 @@ function readRepoConfigUnlessBlocked(root) {
18123
18297
  }
18124
18298
  function requireRepoConfig(root) {
18125
18299
  const gitRoot = root ?? findGitRoot();
18126
- const r = gitRoot ? resolve2(gitRoot) : { kind: "none" };
18300
+ const r = gitRoot ? resolve3(gitRoot) : { kind: "none" };
18127
18301
  report(r, "hard");
18128
18302
  if (r.kind === "ok") return r.resolved.config;
18129
18303
  return process.exit(1);
@@ -18131,7 +18305,7 @@ function requireRepoConfig(root) {
18131
18305
  function readRepoScopeBlocker(root) {
18132
18306
  const gitRoot = root ?? findGitRoot();
18133
18307
  if (!gitRoot) return null;
18134
- const r = resolve2(gitRoot);
18308
+ const r = resolve3(gitRoot);
18135
18309
  if (r.kind === "conflict") {
18136
18310
  return {
18137
18311
  kind: "conflict",
@@ -18168,6 +18342,7 @@ function writeRepoConfig(config, root) {
18168
18342
  if (!gitRoot) {
18169
18343
  throw expected("Not inside a git repository. Run `git init` first.");
18170
18344
  }
18345
+ readRealFileOrThrow(gitRoot, workspaceManifestPath(gitRoot));
18171
18346
  const stale = declarationFrom(loadYamlMapping(rootConfigPath(gitRoot)));
18172
18347
  if (stale.kind === "declared" && stale.config.organization_id !== config.organization_id) {
18173
18348
  throw expected(rebindRefusal(config.organization_id, stale.config.organization_id));
@@ -18183,12 +18358,8 @@ function writeRepoConfig(config, root) {
18183
18358
  if (config.organization_name?.trim()) doc.organization_name = config.organization_name.trim();
18184
18359
  Object.assign(doc, rest);
18185
18360
  const file = workspaceManifestPath(gitRoot);
18186
- fs4.mkdirSync(path4.dirname(file), { recursive: true });
18187
- fs4.writeFileSync(
18188
- file,
18189
- yaml3.dump(doc, { lineWidth: -1, quotingType: '"', forceQuotes: false }),
18190
- "utf-8"
18191
- );
18361
+ requireRealSubdirNoSymlink(gitRoot, path5.dirname(file), true);
18362
+ writeFileNoFollow(gitRoot, file, Buffer.from(yaml3.dump(doc, { lineWidth: -1, quotingType: '"', forceQuotes: false }), "utf-8"));
18192
18363
  return file;
18193
18364
  }
18194
18365
  var LEGACY_FIELDS, noticed, RECONCILE_REMEDY;
@@ -18196,6 +18367,7 @@ var init_repo_config = __esm({
18196
18367
  "src/lib/repo-config.ts"() {
18197
18368
  "use strict";
18198
18369
  init_expected();
18370
+ init_fs_safety();
18199
18371
  init_workspace();
18200
18372
  init_utils();
18201
18373
  init_workspace_manifest();
@@ -18210,7 +18382,7 @@ var init_repo_config = __esm({
18210
18382
 
18211
18383
  // src/lib/utils.ts
18212
18384
  import * as fs5 from "fs";
18213
- import * as path5 from "path";
18385
+ import * as path6 from "path";
18214
18386
  import * as readline from "readline";
18215
18387
  function prompt(question) {
18216
18388
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -18264,7 +18436,7 @@ function readStdin() {
18264
18436
  }
18265
18437
  function resolveHubYamlPath(hubFolder) {
18266
18438
  for (const filename of ["hub.yaml", "wayai.yaml"]) {
18267
- const yamlPath = path5.join(hubFolder, filename);
18439
+ const yamlPath = path6.join(hubFolder, filename);
18268
18440
  if (fs5.existsSync(yamlPath)) return yamlPath;
18269
18441
  }
18270
18442
  return null;
@@ -18730,17 +18902,17 @@ var init_registry = __esm({
18730
18902
  });
18731
18903
 
18732
18904
  // src/lib/version-cache.ts
18733
- import { existsSync as existsSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
18734
- import { dirname as dirname4, join as join6 } from "path";
18905
+ import { existsSync as existsSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
18906
+ import { dirname as dirname5, join as join7 } from "path";
18735
18907
  import { homedir as homedir2 } from "os";
18736
18908
  function getVersionCachePath(filename = CLI_CACHE_FILE) {
18737
- return join6(homedir2(), ".wayai", filename);
18909
+ return join7(homedir2(), ".wayai", filename);
18738
18910
  }
18739
18911
  function readVersionCache(filename = CLI_CACHE_FILE) {
18740
18912
  try {
18741
18913
  const path35 = getVersionCachePath(filename);
18742
18914
  if (!existsSync3(path35)) return null;
18743
- const parsed = JSON.parse(readFileSync5(path35, "utf-8"));
18915
+ const parsed = JSON.parse(readFileSync6(path35, "utf-8"));
18744
18916
  if (typeof parsed.lastCheck !== "number") return null;
18745
18917
  if (parsed.latest !== null && typeof parsed.latest !== "string") return null;
18746
18918
  return parsed;
@@ -18761,7 +18933,7 @@ function isVersionCacheStale(filename = CLI_CACHE_FILE, maxAgeMs = MAX_AGE_24H_M
18761
18933
  }
18762
18934
  function writeVersionCache(filename, cache) {
18763
18935
  const path35 = getVersionCachePath(filename);
18764
- const dir = dirname4(path35);
18936
+ const dir = dirname5(path35);
18765
18937
  if (!existsSync3(dir)) mkdirSync2(dir, { recursive: true });
18766
18938
  writeFileSync2(path35, JSON.stringify(cache));
18767
18939
  }
@@ -18780,8 +18952,8 @@ var init_version_cache = __esm({
18780
18952
  });
18781
18953
 
18782
18954
  // src/lib/skill-version.ts
18783
- import { existsSync as existsSync4, readFileSync as readFileSync6 } from "fs";
18784
- import { join as join7 } from "path";
18955
+ import { existsSync as existsSync4, readFileSync as readFileSync7 } from "fs";
18956
+ import { join as join8 } from "path";
18785
18957
  import * as yaml4 from "js-yaml";
18786
18958
  function skillInstallPaths(skillName) {
18787
18959
  return HARNESS_SKILL_DIRS.map((dir) => `${dir}/skills/${skillName}/${SKILL_FILENAME}`);
@@ -18802,11 +18974,11 @@ function parseFrontmatterVersion(content) {
18802
18974
  function findInstalledSkills(projectRoot, paths = SKILL_INSTALL_PATHS) {
18803
18975
  const found = [];
18804
18976
  for (const rel of paths) {
18805
- const path35 = join7(projectRoot, rel);
18977
+ const path35 = join8(projectRoot, rel);
18806
18978
  if (!existsSync4(path35)) continue;
18807
18979
  let version = null;
18808
18980
  try {
18809
- version = parseFrontmatterVersion(readFileSync6(path35, "utf-8"));
18981
+ version = parseFrontmatterVersion(readFileSync7(path35, "utf-8"));
18810
18982
  } catch {
18811
18983
  }
18812
18984
  found.push({ path: path35, version });
@@ -18841,13 +19013,13 @@ __export(config_exports, {
18841
19013
  writeConfig: () => writeConfig
18842
19014
  });
18843
19015
  import * as fs6 from "fs";
18844
- import * as path6 from "path";
19016
+ import * as path7 from "path";
18845
19017
  import * as os from "os";
18846
19018
  function configDir() {
18847
- return path6.join(os.homedir(), ".wayai");
19019
+ return path7.join(os.homedir(), ".wayai");
18848
19020
  }
18849
19021
  function configPath() {
18850
- return path6.join(configDir(), "config.json");
19022
+ return path7.join(configDir(), "config.json");
18851
19023
  }
18852
19024
  function getConfigPath() {
18853
19025
  return configPath();
@@ -18900,13 +19072,13 @@ var init_config = __esm({
18900
19072
 
18901
19073
  // src/lib/token-store.ts
18902
19074
  import * as fs7 from "fs";
18903
- import * as path7 from "path";
19075
+ import * as path8 from "path";
18904
19076
  import * as os2 from "os";
18905
19077
  function configDir2() {
18906
- return path7.join(os2.homedir(), ".wayai");
19078
+ return path8.join(os2.homedir(), ".wayai");
18907
19079
  }
18908
19080
  function configPath2() {
18909
- return path7.join(configDir2(), "config.json");
19081
+ return path8.join(configDir2(), "config.json");
18910
19082
  }
18911
19083
  function manualCleanupCommand(account) {
18912
19084
  if (process.platform === "darwin") {
@@ -19406,26 +19578,26 @@ __export(skill_symlink_exports, {
19406
19578
  healClaudeSkillLink: () => healClaudeSkillLink,
19407
19579
  healSkillLinkForCommand: () => healSkillLinkForCommand
19408
19580
  });
19409
- import { existsSync as existsSync7, lstatSync, mkdirSync as mkdirSync5, rmSync, symlinkSync } from "fs";
19410
- import { join as join10 } from "path";
19581
+ import { existsSync as existsSync7, lstatSync as lstatSync3, mkdirSync as mkdirSync5, rmSync as rmSync2, symlinkSync } from "fs";
19582
+ import { join as join11 } from "path";
19411
19583
  function healClaudeSkillLink(root) {
19412
19584
  try {
19413
- const source = join10(root, ".agents", "skills", SKILL_NAME);
19414
- if (!existsSync7(join10(source, SKILL_FILENAME))) return false;
19415
- const link = join10(root, ".claude", "skills", SKILL_NAME);
19416
- if (existsSync7(join10(link, SKILL_FILENAME))) return false;
19585
+ const source = join11(root, ".agents", "skills", SKILL_NAME);
19586
+ if (!existsSync7(join11(source, SKILL_FILENAME))) return false;
19587
+ const link = join11(root, ".claude", "skills", SKILL_NAME);
19588
+ if (existsSync7(join11(link, SKILL_FILENAME))) return false;
19417
19589
  let entry = null;
19418
19590
  try {
19419
- entry = lstatSync(link);
19591
+ entry = lstatSync3(link);
19420
19592
  } catch {
19421
19593
  entry = null;
19422
19594
  }
19423
19595
  if (entry) {
19424
19596
  if (!entry.isSymbolicLink()) return false;
19425
- rmSync(link, { force: true });
19597
+ rmSync2(link, { force: true });
19426
19598
  }
19427
- mkdirSync5(join10(root, ".claude", "skills"), { recursive: true });
19428
- symlinkSync(join10("..", "..", ".agents", "skills", SKILL_NAME), link, "dir");
19599
+ mkdirSync5(join11(root, ".claude", "skills"), { recursive: true });
19600
+ symlinkSync(join11("..", "..", ".agents", "skills", SKILL_NAME), link, "dir");
19429
19601
  return true;
19430
19602
  } catch {
19431
19603
  return false;
@@ -19709,7 +19881,7 @@ var init_base_id = __esm({
19709
19881
  // src/lib/worktree-scope.ts
19710
19882
  import { execFileSync as execFileSync2 } from "child_process";
19711
19883
  import * as fs8 from "fs";
19712
- import * as path8 from "path";
19884
+ import * as path9 from "path";
19713
19885
  import * as yaml5 from "js-yaml";
19714
19886
  function axisNoun(axis) {
19715
19887
  return AXES[axis].noun;
@@ -19724,7 +19896,7 @@ function resolveGitDir() {
19724
19896
  encoding: "utf-8",
19725
19897
  stdio: ["pipe", "pipe", "pipe"]
19726
19898
  }).trim();
19727
- gitDir = raw ? path8.resolve(cwd, raw) : null;
19899
+ gitDir = raw ? path9.resolve(cwd, raw) : null;
19728
19900
  } catch {
19729
19901
  gitDir = null;
19730
19902
  }
@@ -19733,12 +19905,12 @@ function resolveGitDir() {
19733
19905
  }
19734
19906
  function getScopePath() {
19735
19907
  const gitDir = resolveGitDir();
19736
- return gitDir ? path8.join(gitDir, SCOPE_FILE) : null;
19908
+ return gitDir ? path9.join(gitDir, SCOPE_FILE) : null;
19737
19909
  }
19738
19910
  function legacyPaths() {
19739
19911
  const gitDir = resolveGitDir();
19740
19912
  if (!gitDir) return [];
19741
- return AXIS_ORDER.flatMap((axis) => AXES[axis].legacyFiles).map((f) => path8.join(gitDir, f));
19913
+ return AXIS_ORDER.flatMap((axis) => AXES[axis].legacyFiles).map((f) => path9.join(gitDir, f));
19742
19914
  }
19743
19915
  function emptyScope() {
19744
19916
  return { hubs: [], bases: [] };
@@ -19762,7 +19934,7 @@ function readLegacyAxis(axis) {
19762
19934
  for (const filename of legacyFiles) {
19763
19935
  let raw;
19764
19936
  try {
19765
- raw = fs8.readFileSync(path8.join(gitDir, filename), "utf-8");
19937
+ raw = fs8.readFileSync(path9.join(gitDir, filename), "utf-8");
19766
19938
  } catch (err) {
19767
19939
  if (err.code === "ENOENT") continue;
19768
19940
  throw err;
@@ -19982,8 +20154,13 @@ function buildSkillState() {
19982
20154
  }
19983
20155
  function buildWorkspaceState(resolved) {
19984
20156
  if (!resolved) return { scoped: false, path: null, hub_count: 0 };
19985
- const ws = detectWorkspace();
19986
- const hubCount = ws ? scanWorkspaceHubs(ws.workspaceDir).length : 0;
20157
+ let hubCount = 0;
20158
+ try {
20159
+ const ws = detectWorkspace();
20160
+ hubCount = ws ? scanWorkspaceHubs(ws.workspaceDir).length : 0;
20161
+ } catch {
20162
+ hubCount = 0;
20163
+ }
19987
20164
  return { scoped: true, path: resolved.path, hub_count: hubCount };
19988
20165
  }
19989
20166
  async function statusCommand(args2, pkg2) {
@@ -20221,12 +20398,12 @@ __export(init_exports, {
20221
20398
  parseArgs: () => parseArgs3
20222
20399
  });
20223
20400
  import { mkdirSync as mkdirSync6 } from "fs";
20224
- import path9 from "path";
20401
+ import path10 from "path";
20225
20402
  function ensureHubsDir(gitRoot) {
20226
20403
  const hubsDir = resolveLayout(gitRoot).hubsDir;
20227
20404
  if (isDirectory(hubsDir)) return null;
20228
20405
  mkdirSync6(hubsDir, { recursive: true });
20229
- return `${path9.relative(gitRoot, hubsDir)}/`;
20406
+ return `${path10.relative(gitRoot, hubsDir)}/`;
20230
20407
  }
20231
20408
  function parseArgs3(args2) {
20232
20409
  let orgId;
@@ -20355,108 +20532,8 @@ var init_init = __esm({
20355
20532
  }
20356
20533
  });
20357
20534
 
20358
- // src/lib/fs-safety.ts
20359
- import * as fs9 from "fs";
20360
- import * as path10 from "path";
20361
- function fileHasBytes(abs, data) {
20362
- let st;
20363
- try {
20364
- st = fs9.lstatSync(abs);
20365
- } catch {
20366
- return false;
20367
- }
20368
- if (!st.isFile() || st.isSymbolicLink()) return false;
20369
- try {
20370
- return fs9.readFileSync(abs).equals(data);
20371
- } catch {
20372
- return false;
20373
- }
20374
- }
20375
- function realpathContained(root, abs) {
20376
- let realRoot;
20377
- let realAbs;
20378
- try {
20379
- realRoot = fs9.realpathSync(root);
20380
- realAbs = fs9.realpathSync(abs);
20381
- } catch {
20382
- return false;
20383
- }
20384
- return realAbs === realRoot || realAbs.startsWith(realRoot + path10.sep);
20385
- }
20386
- function mkdirTolerateRace(dir) {
20387
- try {
20388
- fs9.mkdirSync(dir);
20389
- return true;
20390
- } catch (err) {
20391
- if (err.code !== "EEXIST") throw err;
20392
- let st;
20393
- try {
20394
- st = fs9.lstatSync(dir);
20395
- } catch {
20396
- st = void 0;
20397
- }
20398
- return !!st?.isDirectory();
20399
- }
20400
- }
20401
- function ensureRealSubdirNoSymlink(root, target, create) {
20402
- const rootResolved = path10.resolve(root);
20403
- const targetResolved = path10.resolve(target);
20404
- if (targetResolved !== rootResolved && !targetResolved.startsWith(rootResolved + path10.sep)) return false;
20405
- try {
20406
- if (!fs9.lstatSync(rootResolved).isDirectory()) return false;
20407
- } catch {
20408
- return false;
20409
- }
20410
- const rel = path10.relative(rootResolved, targetResolved);
20411
- if (rel === "") return true;
20412
- let cur = rootResolved;
20413
- for (const segment of rel.split(path10.sep)) {
20414
- cur = path10.join(cur, segment);
20415
- let st;
20416
- try {
20417
- st = fs9.lstatSync(cur);
20418
- } catch {
20419
- st = void 0;
20420
- }
20421
- if (st) {
20422
- if (!st.isDirectory()) return false;
20423
- } else if (create) {
20424
- if (!mkdirTolerateRace(cur)) return false;
20425
- } else {
20426
- return true;
20427
- }
20428
- }
20429
- return true;
20430
- }
20431
- function readFileNoFollow(root, abs) {
20432
- if (!ensureRealSubdirNoSymlink(root, path10.dirname(abs), false)) return null;
20433
- try {
20434
- if (!fs9.lstatSync(abs).isFile()) return null;
20435
- return fs9.readFileSync(abs);
20436
- } catch {
20437
- return null;
20438
- }
20439
- }
20440
- function writeFileNoFollow(root, abs, data) {
20441
- const parent = path10.dirname(abs);
20442
- if (!ensureRealSubdirNoSymlink(root, parent, true)) return false;
20443
- let st;
20444
- try {
20445
- st = fs9.lstatSync(abs);
20446
- } catch {
20447
- }
20448
- if (st?.isSymbolicLink()) fs9.rmSync(abs);
20449
- fs9.writeFileSync(abs, data);
20450
- return true;
20451
- }
20452
- var init_fs_safety = __esm({
20453
- "src/lib/fs-safety.ts"() {
20454
- "use strict";
20455
- }
20456
- });
20457
-
20458
20535
  // src/lib/resource-files.ts
20459
- import * as fs10 from "fs";
20536
+ import * as fs9 from "fs";
20460
20537
  import * as path11 from "path";
20461
20538
  import * as crypto3 from "crypto";
20462
20539
  function isBinaryFile(filename) {
@@ -20503,14 +20580,14 @@ function computeHash(data) {
20503
20580
  }
20504
20581
  function scanResourceFiles(dir, prefix = "") {
20505
20582
  const files = [];
20506
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
20583
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
20507
20584
  for (const entry of entries) {
20508
20585
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
20509
20586
  if (entry.isDirectory()) {
20510
20587
  files.push(...scanResourceFiles(path11.join(dir, entry.name), relPath));
20511
20588
  } else if (entry.isFile()) {
20512
20589
  const fullPath = path11.join(dir, entry.name);
20513
- const stat2 = fs10.statSync(fullPath);
20590
+ const stat2 = fs9.statSync(fullPath);
20514
20591
  if (stat2.size > MAX_RESOURCE_FILE_SIZE3) {
20515
20592
  console.warn(` Warning: skipping ${relPath} (${(stat2.size / 1024 / 1024).toFixed(1)}MB exceeds 10MB limit)`);
20516
20593
  continue;
@@ -20520,7 +20597,7 @@ function scanResourceFiles(dir, prefix = "") {
20520
20597
  mime_type: guessMimeType(entry.name),
20521
20598
  file_size: stat2.size
20522
20599
  };
20523
- const data = fs10.readFileSync(fullPath);
20600
+ const data = fs9.readFileSync(fullPath);
20524
20601
  fileEntry.hash = computeHash(data);
20525
20602
  if (isBinaryFile(entry.name)) {
20526
20603
  fileEntry.content_base64 = data.toString("base64");
@@ -20580,7 +20657,7 @@ function writeResourceFileTree(resDir, files, root, log) {
20580
20657
  return;
20581
20658
  }
20582
20659
  if (files.length === 0) {
20583
- if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
20660
+ if (fs9.existsSync(resDir)) cleanOrphanFiles(resDir, "", /* @__PURE__ */ new Set(), root, log);
20584
20661
  return;
20585
20662
  }
20586
20663
  const writtenPaths = /* @__PURE__ */ new Set();
@@ -20601,18 +20678,18 @@ function writeResourceFileTree(resDir, files, root, log) {
20601
20678
  writtenPaths.delete(file.path);
20602
20679
  }
20603
20680
  }
20604
- if (fs10.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
20681
+ if (fs9.existsSync(resDir)) cleanOrphanFiles(resDir, "", writtenPaths, root, log);
20605
20682
  }
20606
20683
  function cleanOrphanFiles(dir, prefix, writtenPaths, root, log) {
20607
- const entries = fs10.readdirSync(dir, { withFileTypes: true });
20684
+ const entries = fs9.readdirSync(dir, { withFileTypes: true });
20608
20685
  for (const entry of entries) {
20609
20686
  const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
20610
20687
  const fullPath = path11.join(dir, entry.name);
20611
20688
  if (entry.isDirectory()) {
20612
20689
  cleanOrphanFiles(fullPath, relPath, writtenPaths, root, log);
20613
- if (fs10.readdirSync(fullPath).length === 0) fs10.rmdirSync(fullPath);
20690
+ if (fs9.readdirSync(fullPath).length === 0) fs9.rmdirSync(fullPath);
20614
20691
  } else if (!writtenPaths.has(relPath)) {
20615
- fs10.unlinkSync(fullPath);
20692
+ fs9.unlinkSync(fullPath);
20616
20693
  if (log && root) log.removed.push(path11.relative(root, fullPath));
20617
20694
  }
20618
20695
  }
@@ -20633,8 +20710,8 @@ async function downloadBinaryFiles(resDir, files, root, log) {
20633
20710
  }
20634
20711
  if (file.hash) {
20635
20712
  try {
20636
- const st = fs10.lstatSync(filePath);
20637
- if (st.isFile() && !st.isSymbolicLink() && computeHash(fs10.readFileSync(filePath)) === file.hash) continue;
20713
+ const st = fs9.lstatSync(filePath);
20714
+ if (st.isFile() && !st.isSymbolicLink() && computeHash(fs9.readFileSync(filePath)) === file.hash) continue;
20638
20715
  } catch {
20639
20716
  }
20640
20717
  }
@@ -20688,7 +20765,7 @@ var init_resource_files = __esm({
20688
20765
  });
20689
20766
 
20690
20767
  // src/lib/eval-attachments.ts
20691
- import * as fs11 from "fs";
20768
+ import * as fs10 from "fs";
20692
20769
  import * as path12 from "path";
20693
20770
  function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
20694
20771
  const raw = turn.attachments;
@@ -20712,7 +20789,7 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
20712
20789
  }
20713
20790
  let stat2;
20714
20791
  try {
20715
- stat2 = fs11.statSync(abs);
20792
+ stat2 = fs10.statSync(abs);
20716
20793
  } catch {
20717
20794
  }
20718
20795
  if (!stat2?.isFile()) {
@@ -20726,7 +20803,7 @@ function resolveTurnAttachments(hubFolder, turn, label, bytesByHash) {
20726
20803
  `${label}: attachment "${entry}" is ${(stat2.size / 1024 / 1024).toFixed(1)}MB, over the 10MB limit.`
20727
20804
  );
20728
20805
  }
20729
- const data = fs11.readFileSync(abs);
20806
+ const data = fs10.readFileSync(abs);
20730
20807
  const hash = computeHash(data);
20731
20808
  const fileName = path12.basename(abs);
20732
20809
  const mimeType = guessMimeType(fileName) ?? "application/octet-stream";
@@ -20834,16 +20911,16 @@ async function downloadEvalAttachments(hubFolder, downloads) {
20834
20911
  console.warn(` Warning: ${ATTACHMENTS_DIR}/ is not reachable inside the hub folder without a symlink; skipping attachment sync.`);
20835
20912
  return result;
20836
20913
  }
20837
- if (!fs11.existsSync(attachDir)) return result;
20914
+ if (!fs10.existsSync(attachDir)) return result;
20838
20915
  for (const dl of downloads) {
20839
20916
  const abs = path12.resolve(hubFolder, dl.relPath);
20840
20917
  if (abs !== attachDir && !abs.startsWith(attachDir + path12.sep)) continue;
20841
20918
  let existing;
20842
20919
  try {
20843
- existing = fs11.lstatSync(abs);
20920
+ existing = fs10.lstatSync(abs);
20844
20921
  } catch {
20845
20922
  }
20846
- if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs11.readFileSync(abs)) === dl.hash) {
20923
+ if (existing?.isFile() && !existing.isSymbolicLink() && computeHash(fs10.readFileSync(abs)) === dl.hash) {
20847
20924
  continue;
20848
20925
  }
20849
20926
  try {
@@ -20861,11 +20938,11 @@ async function downloadEvalAttachments(hubFolder, downloads) {
20861
20938
  }
20862
20939
  }
20863
20940
  const keep = new Set(downloads.map((d) => path12.resolve(hubFolder, d.relPath)));
20864
- for (const name of fs11.readdirSync(attachDir)) {
20941
+ for (const name of fs10.readdirSync(attachDir)) {
20865
20942
  const abs = path12.join(attachDir, name);
20866
- const st = fs11.lstatSync(abs);
20943
+ const st = fs10.lstatSync(abs);
20867
20944
  if ((st.isFile() || st.isSymbolicLink()) && !keep.has(abs)) {
20868
- fs11.rmSync(abs);
20945
+ fs10.rmSync(abs);
20869
20946
  result.removed.push(path12.relative(hubFolder, abs));
20870
20947
  }
20871
20948
  }
@@ -20883,15 +20960,16 @@ var init_eval_attachments = __esm({
20883
20960
  });
20884
20961
 
20885
20962
  // src/lib/parser.ts
20886
- import * as fs12 from "fs";
20963
+ import * as fs11 from "fs";
20887
20964
  import * as path13 from "path";
20888
20965
  import * as yaml6 from "js-yaml";
20889
20966
  function parseHubFolder(hubFolder, opts) {
20967
+ requireRealHubFolder(hubFolder, false);
20890
20968
  const yamlPath = resolveHubYamlPath(hubFolder);
20891
20969
  if (!yamlPath) {
20892
20970
  throw expected(`hub.yaml not found in ${hubFolder}`);
20893
20971
  }
20894
- const yamlContent = fs12.readFileSync(yamlPath, "utf-8");
20972
+ const yamlContent = readRealFileOrThrow(hubFolder, yamlPath) ?? "";
20895
20973
  let config;
20896
20974
  try {
20897
20975
  config = yaml6.load(yamlContent);
@@ -20919,8 +20997,8 @@ function parseHubFolder(hubFolder, opts) {
20919
20997
  if (typeof resolved.instructions === "string" && resolved.instructions.endsWith(".md")) {
20920
20998
  const instrValue = resolved.instructions;
20921
20999
  const instructionsPath = instrValue.startsWith("agents/") ? path13.join(hubFolder, instrValue) : path13.join(hubFolder, "agents", instrValue);
20922
- if (fs12.existsSync(instructionsPath)) {
20923
- resolved.instructions = fs12.readFileSync(instructionsPath, "utf-8");
21000
+ if (fs11.existsSync(instructionsPath)) {
21001
+ resolved.instructions = fs11.readFileSync(instructionsPath, "utf-8");
20924
21002
  } else {
20925
21003
  throw expected(
20926
21004
  `Agent instructions file not found: ${instructionsPath} (referenced by agent "${agent.name}")`
@@ -20928,8 +21006,8 @@ function parseHubFolder(hubFolder, opts) {
20928
21006
  }
20929
21007
  } else if (resolved.instructions === void 0 && typeof agent.name === "string") {
20930
21008
  const conventionPath = path13.join(hubFolder, "agents", `${slugify(agent.name)}.md`);
20931
- if (fs12.existsSync(conventionPath)) {
20932
- resolved.instructions = fs12.readFileSync(conventionPath, "utf-8");
21009
+ if (fs11.existsSync(conventionPath)) {
21010
+ resolved.instructions = fs11.readFileSync(conventionPath, "utf-8");
20933
21011
  }
20934
21012
  }
20935
21013
  return resolved;
@@ -20984,17 +21062,17 @@ function parseHubFolder(hubFolder, opts) {
20984
21062
  }
20985
21063
  function scanEvalYamlFiles(hubFolder, bytesByHash) {
20986
21064
  const evalsDir = path13.join(hubFolder, "evals");
20987
- if (!fs12.existsSync(evalsDir)) return [];
21065
+ if (!fs11.existsSync(evalsDir)) return [];
20988
21066
  const evals = [];
20989
21067
  const seen = /* @__PURE__ */ new Set();
20990
- const topEntries = fs12.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21068
+ const topEntries = fs11.readdirSync(evalsDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
20991
21069
  for (const entry of topEntries) {
20992
21070
  if (entry.isFile() && entry.name.endsWith(".yaml")) {
20993
21071
  collectEval(evals, seen, path13.join(evalsDir, entry.name), `evals/${entry.name}`, null, hubFolder, bytesByHash);
20994
21072
  } else if (entry.isDirectory()) {
20995
21073
  const setName = entry.name;
20996
21074
  const setDir = path13.join(evalsDir, setName);
20997
- const setEntries = fs12.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21075
+ const setEntries = fs11.readdirSync(setDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
20998
21076
  for (const sub of setEntries) {
20999
21077
  if (sub.isDirectory()) {
21000
21078
  throw expected(
@@ -21023,7 +21101,7 @@ function collectEval(evals, seen, filePath, relPath, setName, hubFolder, bytesBy
21023
21101
  evals.push(evalEntry);
21024
21102
  }
21025
21103
  function parseEvalYaml(filePath, relPath, setName, hubFolder, bytesByHash) {
21026
- const content = fs12.readFileSync(filePath, "utf-8");
21104
+ const content = fs11.readFileSync(filePath, "utf-8");
21027
21105
  let raw;
21028
21106
  try {
21029
21107
  raw = yaml6.load(content);
@@ -21126,10 +21204,10 @@ function parseFixtureField(raw, label, key) {
21126
21204
  }
21127
21205
  function scanJourneyYamlFiles(hubFolder, bytesByHash) {
21128
21206
  const journeysDir = path13.join(hubFolder, "journeys");
21129
- if (!fs12.existsSync(journeysDir)) return [];
21207
+ if (!fs11.existsSync(journeysDir)) return [];
21130
21208
  const journeys = [];
21131
21209
  const seen = /* @__PURE__ */ new Set();
21132
- const entries = fs12.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21210
+ const entries = fs11.readdirSync(journeysDir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
21133
21211
  for (const entry of entries) {
21134
21212
  if (entry.isDirectory()) {
21135
21213
  throw expected(
@@ -21150,7 +21228,7 @@ function scanJourneyYamlFiles(hubFolder, bytesByHash) {
21150
21228
  return journeys;
21151
21229
  }
21152
21230
  function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
21153
- const content = fs12.readFileSync(filePath, "utf-8");
21231
+ const content = fs11.readFileSync(filePath, "utf-8");
21154
21232
  let raw;
21155
21233
  try {
21156
21234
  raw = yaml6.load(content);
@@ -21206,13 +21284,13 @@ function parseJourneyYaml(filePath, relPath, hubFolder, bytesByHash) {
21206
21284
  }
21207
21285
  function scanAgentYamlFiles(hubFolder) {
21208
21286
  const agentsDir = path13.join(hubFolder, "agents");
21209
- if (!fs12.existsSync(agentsDir)) return [];
21210
- const yamlFiles = fs12.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
21287
+ if (!fs11.existsSync(agentsDir)) return [];
21288
+ const yamlFiles = fs11.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml")).sort();
21211
21289
  if (yamlFiles.length === 0) return [];
21212
21290
  const agents = [];
21213
21291
  for (const file of yamlFiles) {
21214
21292
  const filePath = path13.join(agentsDir, file);
21215
- const content = fs12.readFileSync(filePath, "utf-8");
21293
+ const content = fs11.readFileSync(filePath, "utf-8");
21216
21294
  let agent;
21217
21295
  try {
21218
21296
  agent = yaml6.load(content);
@@ -21247,7 +21325,7 @@ function parseResources(hubFolder, configResources) {
21247
21325
  if (res.skill_name) resource.skill_name = res.skill_name;
21248
21326
  const resSlug = slugify(resource.name);
21249
21327
  const resDir = path13.join(resourcesDir, resSlug);
21250
- if (fs12.existsSync(resDir)) {
21328
+ if (fs11.existsSync(resDir)) {
21251
21329
  const files = scanResourceFiles(resDir, "");
21252
21330
  if (files.length > 0) {
21253
21331
  resource.files = files;
@@ -21264,6 +21342,7 @@ var init_parser = __esm({
21264
21342
  init_resource_files();
21265
21343
  init_eval_attachments();
21266
21344
  init_expected();
21345
+ init_fs_safety();
21267
21346
  }
21268
21347
  });
21269
21348
 
@@ -21295,7 +21374,6 @@ var init_diff_display = __esm({
21295
21374
  });
21296
21375
 
21297
21376
  // src/lib/workspace-files.ts
21298
- import * as fs13 from "fs";
21299
21377
  import * as path14 from "path";
21300
21378
  function perHubAgentsMd(hubFolderName) {
21301
21379
  return [
@@ -21311,19 +21389,18 @@ function perHubAgentsMd(hubFolderName) {
21311
21389
  ""
21312
21390
  ].join("\n");
21313
21391
  }
21314
- function writeIfAbsent(filePath, content) {
21315
- if (fs13.existsSync(filePath)) return null;
21316
- fs13.writeFileSync(filePath, content, "utf-8");
21317
- return path14.basename(filePath);
21392
+ function writeIfAbsent(root, filePath, content) {
21393
+ return createFileNoFollow(root, filePath, Buffer.from(content, "utf-8")) === "created" ? path14.basename(filePath) : null;
21318
21394
  }
21319
21395
  var init_workspace_files = __esm({
21320
21396
  "src/lib/workspace-files.ts"() {
21321
21397
  "use strict";
21398
+ init_fs_safety();
21322
21399
  }
21323
21400
  });
21324
21401
 
21325
21402
  // src/lib/yaml-writer.ts
21326
- import * as fs14 from "fs";
21403
+ import * as fs12 from "fs";
21327
21404
  import * as path15 from "path";
21328
21405
  import * as yaml7 from "js-yaml";
21329
21406
  function writeFileIfChanged(hubFolder, absPath, content, log) {
@@ -21374,11 +21451,11 @@ function buildEvalYamlObject(evalEntry, slug) {
21374
21451
  function writeHubFolder(hubFolder, payload, options = {}) {
21375
21452
  const log = { changed: [], removed: [] };
21376
21453
  const agentsDir = path15.join(hubFolder, "agents");
21377
- if (!fs14.existsSync(hubFolder)) {
21378
- fs14.mkdirSync(hubFolder, { recursive: true });
21454
+ if (!fs12.existsSync(hubFolder)) {
21455
+ fs12.mkdirSync(hubFolder, { recursive: true });
21379
21456
  }
21380
- if (!fs14.existsSync(agentsDir)) {
21381
- fs14.mkdirSync(agentsDir, { recursive: true });
21457
+ if (!fs12.existsSync(agentsDir)) {
21458
+ fs12.mkdirSync(agentsDir, { recursive: true });
21382
21459
  }
21383
21460
  const yamlPayload = buildYamlPayload(payload);
21384
21461
  const agentFiles = extractAgentFiles(payload);
@@ -21386,14 +21463,15 @@ function writeHubFolder(hubFolder, payload, options = {}) {
21386
21463
  writeFileIfChanged(hubFolder, path15.join(hubFolder, "hub.yaml"), yamlContent, log);
21387
21464
  if (options.seedAgentContext ?? true) {
21388
21465
  const seeded = writeIfAbsent(
21466
+ hubFolder,
21389
21467
  path15.join(hubFolder, "AGENTS.md"),
21390
21468
  perHubAgentsMd(path15.basename(hubFolder))
21391
21469
  );
21392
21470
  if (seeded) log.changed.push(seeded);
21393
21471
  }
21394
21472
  const oldYamlPath = path15.join(hubFolder, "wayai.yaml");
21395
- if (fs14.existsSync(oldYamlPath)) {
21396
- fs14.unlinkSync(oldYamlPath);
21473
+ if (fs12.existsSync(oldYamlPath)) {
21474
+ fs12.unlinkSync(oldYamlPath);
21397
21475
  log.removed.push(path15.relative(hubFolder, oldYamlPath));
21398
21476
  }
21399
21477
  const yamlSlugs = writeAgentYamlFiles(hubFolder, agentsDir, payload.agents || [], log);
@@ -21402,20 +21480,20 @@ function writeHubFolder(hubFolder, payload, options = {}) {
21402
21480
  mdSlugs.add(slug);
21403
21481
  writeFileIfChanged(hubFolder, path15.join(agentsDir, `${slug}.md`), content, log);
21404
21482
  }
21405
- const existingFiles = fs14.readdirSync(agentsDir);
21483
+ const existingFiles = fs12.readdirSync(agentsDir);
21406
21484
  for (const file of existingFiles) {
21407
21485
  if (file.endsWith(".yaml")) {
21408
21486
  const slug = file.slice(0, -5);
21409
21487
  if (!yamlSlugs.has(slug)) {
21410
21488
  const orphan = path15.join(agentsDir, file);
21411
- fs14.unlinkSync(orphan);
21489
+ fs12.unlinkSync(orphan);
21412
21490
  log.removed.push(path15.relative(hubFolder, orphan));
21413
21491
  }
21414
21492
  } else if (file.endsWith(".md")) {
21415
21493
  const slug = file.slice(0, -3);
21416
21494
  if (!mdSlugs.has(slug)) {
21417
21495
  const orphan = path15.join(agentsDir, file);
21418
- fs14.unlinkSync(orphan);
21496
+ fs12.unlinkSync(orphan);
21419
21497
  log.removed.push(path15.relative(hubFolder, orphan));
21420
21498
  }
21421
21499
  }
@@ -21426,12 +21504,13 @@ function writeHubFolder(hubFolder, payload, options = {}) {
21426
21504
  return log;
21427
21505
  }
21428
21506
  function setPreviewLabelInHubYaml(hubFolder, label) {
21507
+ requireRealHubFolder(hubFolder, false);
21429
21508
  const yamlPath = resolveHubYamlPath(hubFolder);
21430
21509
  if (!yamlPath) return;
21431
- const obj = yaml7.load(fs14.readFileSync(yamlPath, "utf-8")) ?? {};
21510
+ const obj = yaml7.load(readRealFileOrThrow(hubFolder, yamlPath) ?? "") ?? {};
21432
21511
  if (label) obj.preview_label = label;
21433
21512
  else delete obj.preview_label;
21434
- fs14.writeFileSync(yamlPath, yaml7.dump(obj, YAML_DUMP_OPTIONS), "utf-8");
21513
+ writeFileNoFollow(hubFolder, yamlPath, Buffer.from(yaml7.dump(obj, YAML_DUMP_OPTIONS), "utf-8"));
21435
21514
  }
21436
21515
  function buildYamlPayload(payload) {
21437
21516
  const result = {
@@ -21496,17 +21575,17 @@ function extractAgentFiles(payload) {
21496
21575
  function writeEvalYamlFiles(hubFolder, evals, log) {
21497
21576
  const evalsDir = path15.join(hubFolder, "evals");
21498
21577
  if (evals.length === 0) {
21499
- if (fs14.existsSync(evalsDir)) {
21578
+ if (fs12.existsSync(evalsDir)) {
21500
21579
  cleanEvalOrphans(hubFolder, evalsDir, /* @__PURE__ */ new Set(), log);
21501
21580
  try {
21502
- if (fs14.readdirSync(evalsDir).length === 0) fs14.rmdirSync(evalsDir);
21581
+ if (fs12.readdirSync(evalsDir).length === 0) fs12.rmdirSync(evalsDir);
21503
21582
  } catch {
21504
21583
  }
21505
21584
  }
21506
21585
  return;
21507
21586
  }
21508
- if (!fs14.existsSync(evalsDir)) {
21509
- fs14.mkdirSync(evalsDir, { recursive: true });
21587
+ if (!fs12.existsSync(evalsDir)) {
21588
+ fs12.mkdirSync(evalsDir, { recursive: true });
21510
21589
  }
21511
21590
  const writtenRelPaths = /* @__PURE__ */ new Set();
21512
21591
  for (const evalEntry of evals) {
@@ -21518,12 +21597,6 @@ function writeEvalYamlFiles(hubFolder, evals, log) {
21518
21597
  console.warn(` Warning: skipping eval "${evalEntry.name}" (scenario set "${setName}" escapes evals/ \u2014 possible bad backend data)`);
21519
21598
  continue;
21520
21599
  }
21521
- if (setName) {
21522
- const setDir = path15.join(evalsDir, setName);
21523
- if (!fs14.existsSync(setDir)) {
21524
- fs14.mkdirSync(setDir, { recursive: true });
21525
- }
21526
- }
21527
21600
  const yamlContent = yaml7.dump(buildEvalYamlObject(evalEntry, slug), YAML_DUMP_OPTIONS);
21528
21601
  writeFileIfChanged(hubFolder, targetPath, yamlContent, log);
21529
21602
  writtenRelPaths.add(relPath);
@@ -21531,31 +21604,31 @@ function writeEvalYamlFiles(hubFolder, evals, log) {
21531
21604
  cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log);
21532
21605
  }
21533
21606
  function cleanEvalOrphans(hubFolder, evalsDir, writtenRelPaths, log) {
21534
- const entries = fs14.readdirSync(evalsDir, { withFileTypes: true });
21607
+ const entries = fs12.readdirSync(evalsDir, { withFileTypes: true });
21535
21608
  for (const entry of entries) {
21536
21609
  const fullPath = path15.join(evalsDir, entry.name);
21537
21610
  if (entry.isFile()) {
21538
21611
  if (entry.name.endsWith(".yaml") && !writtenRelPaths.has(entry.name)) {
21539
- fs14.unlinkSync(fullPath);
21612
+ fs12.unlinkSync(fullPath);
21540
21613
  log.removed.push(path15.relative(hubFolder, fullPath));
21541
21614
  }
21542
21615
  continue;
21543
21616
  }
21544
21617
  if (entry.isDirectory()) {
21545
21618
  const setName = entry.name;
21546
- const subEntries = fs14.readdirSync(fullPath, { withFileTypes: true });
21619
+ const subEntries = fs12.readdirSync(fullPath, { withFileTypes: true });
21547
21620
  for (const sub of subEntries) {
21548
21621
  if (sub.isFile() && sub.name.endsWith(".yaml")) {
21549
21622
  const relPath = `${setName}/${sub.name}`;
21550
21623
  if (!writtenRelPaths.has(relPath)) {
21551
21624
  const orphan = path15.join(fullPath, sub.name);
21552
- fs14.unlinkSync(orphan);
21625
+ fs12.unlinkSync(orphan);
21553
21626
  log.removed.push(path15.relative(hubFolder, orphan));
21554
21627
  }
21555
21628
  }
21556
21629
  }
21557
21630
  try {
21558
- if (fs14.readdirSync(fullPath).length === 0) fs14.rmdirSync(fullPath);
21631
+ if (fs12.readdirSync(fullPath).length === 0) fs12.rmdirSync(fullPath);
21559
21632
  } catch {
21560
21633
  }
21561
21634
  }
@@ -21594,17 +21667,17 @@ function buildJourneyYamlObject(journeyEntry, slug) {
21594
21667
  function writeJourneyYamlFiles(hubFolder, journeys, log) {
21595
21668
  const journeysDir = path15.join(hubFolder, "journeys");
21596
21669
  if (journeys.length === 0) {
21597
- if (fs14.existsSync(journeysDir)) {
21670
+ if (fs12.existsSync(journeysDir)) {
21598
21671
  cleanJourneyOrphans(hubFolder, journeysDir, /* @__PURE__ */ new Set(), log);
21599
21672
  try {
21600
- if (fs14.readdirSync(journeysDir).length === 0) fs14.rmdirSync(journeysDir);
21673
+ if (fs12.readdirSync(journeysDir).length === 0) fs12.rmdirSync(journeysDir);
21601
21674
  } catch {
21602
21675
  }
21603
21676
  }
21604
21677
  return;
21605
21678
  }
21606
- if (!fs14.existsSync(journeysDir)) {
21607
- fs14.mkdirSync(journeysDir, { recursive: true });
21679
+ if (!fs12.existsSync(journeysDir)) {
21680
+ fs12.mkdirSync(journeysDir, { recursive: true });
21608
21681
  }
21609
21682
  const writtenFiles = /* @__PURE__ */ new Set();
21610
21683
  const usedSlugs = /* @__PURE__ */ new Set();
@@ -21618,11 +21691,11 @@ function writeJourneyYamlFiles(hubFolder, journeys, log) {
21618
21691
  cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log);
21619
21692
  }
21620
21693
  function cleanJourneyOrphans(hubFolder, journeysDir, writtenFiles, log) {
21621
- const entries = fs14.readdirSync(journeysDir, { withFileTypes: true });
21694
+ const entries = fs12.readdirSync(journeysDir, { withFileTypes: true });
21622
21695
  for (const entry of entries) {
21623
21696
  if (entry.isFile() && entry.name.endsWith(".yaml") && !writtenFiles.has(entry.name)) {
21624
21697
  const orphan = path15.join(journeysDir, entry.name);
21625
- fs14.unlinkSync(orphan);
21698
+ fs12.unlinkSync(orphan);
21626
21699
  log.removed.push(path15.relative(hubFolder, orphan));
21627
21700
  }
21628
21701
  }
@@ -21636,12 +21709,12 @@ function writeResourceFiles(hubFolder, resources, log) {
21636
21709
  const resDir = path15.join(resourcesDir, resSlug);
21637
21710
  writeResourceFileTree(resDir, resource.files || [], hubFolder, log);
21638
21711
  }
21639
- if (fs14.existsSync(resourcesDir)) {
21640
- const existingDirs = fs14.readdirSync(resourcesDir, { withFileTypes: true });
21712
+ if (fs12.existsSync(resourcesDir)) {
21713
+ const existingDirs = fs12.readdirSync(resourcesDir, { withFileTypes: true });
21641
21714
  for (const entry of existingDirs) {
21642
21715
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
21643
21716
  const orphanDir = path15.join(resourcesDir, entry.name);
21644
- fs14.rmSync(orphanDir, { recursive: true, force: true });
21717
+ fs12.rmSync(orphanDir, { recursive: true, force: true });
21645
21718
  log.removed.push(`${path15.relative(hubFolder, orphanDir)}/`);
21646
21719
  }
21647
21720
  }
@@ -21678,6 +21751,7 @@ async function materializeHubFolder(hubFolder, payload, options = {}) {
21678
21751
  );
21679
21752
  }
21680
21753
  materialized.add(payload);
21754
+ requireRealHubFolder(hubFolder, true);
21681
21755
  const attachmentDownloads = rewriteEvalAttachmentsToLocalPaths(payload);
21682
21756
  const writeLog = writeHubFolder(hubFolder, payload, options);
21683
21757
  const resourceDownloads = await downloadBinaryResourceFiles(hubFolder, payload);
@@ -21709,6 +21783,7 @@ var init_hub_materializer = __esm({
21709
21783
  init_yaml_writer();
21710
21784
  init_eval_attachments();
21711
21785
  init_resource_files();
21786
+ init_fs_safety();
21712
21787
  init_utils();
21713
21788
  materialized = /* @__PURE__ */ new WeakSet();
21714
21789
  }
@@ -21730,7 +21805,7 @@ var init_terminal_output = __esm({
21730
21805
  });
21731
21806
 
21732
21807
  // src/lib/base-workspace.ts
21733
- import * as fs15 from "fs";
21808
+ import * as fs13 from "fs";
21734
21809
  import * as path17 from "path";
21735
21810
  import * as yaml8 from "js-yaml";
21736
21811
  function readBaseMeta(folder) {
@@ -21748,7 +21823,7 @@ function readBaseMeta(folder) {
21748
21823
  function listBaseFolders(basesDir) {
21749
21824
  let entries;
21750
21825
  try {
21751
- entries = fs15.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
21826
+ entries = fs13.readdirSync(basesDir).filter((entry) => !entry.startsWith("."));
21752
21827
  } catch {
21753
21828
  return [];
21754
21829
  }
@@ -21835,7 +21910,7 @@ function resolveBaseSelectorToId(gitRoot, selector) {
21835
21910
  }
21836
21911
  function hasBaseMetaFile(folder) {
21837
21912
  try {
21838
- return fs15.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
21913
+ return fs13.lstatSync(path17.join(folder, BASE_META_FILE)).isFile();
21839
21914
  } catch {
21840
21915
  return false;
21841
21916
  }
@@ -22148,7 +22223,7 @@ var init_client = __esm({
22148
22223
  });
22149
22224
 
22150
22225
  // src/data/helpers.ts
22151
- import { readFileSync as readFileSync15 } from "fs";
22226
+ import { readFileSync as readFileSync14 } from "fs";
22152
22227
  function pathSegment(id, label = "id") {
22153
22228
  if (!isPathSafeId(id)) {
22154
22229
  throw expected(`Invalid ${label}: ${JSON.stringify(id)}. An id may use ${PATH_SAFE_ID_RULE}.`);
@@ -22176,7 +22251,7 @@ function parseData(data, flag) {
22176
22251
  let text = data;
22177
22252
  if (source !== void 0) {
22178
22253
  try {
22179
- text = readFileSync15(source, "utf-8");
22254
+ text = readFileSync14(source, "utf-8");
22180
22255
  } catch (e) {
22181
22256
  throw expected(`${prefix}${source}: ${e instanceof Error ? e.message : "could not be read"}`);
22182
22257
  }
@@ -22301,7 +22376,7 @@ var init_types2 = __esm({
22301
22376
  });
22302
22377
 
22303
22378
  // src/data/config-as-code/config-writer.ts
22304
- import * as fs16 from "fs";
22379
+ import * as fs14 from "fs";
22305
22380
  import * as path19 from "path";
22306
22381
  import * as yaml9 from "js-yaml";
22307
22382
  function dump5(value) {
@@ -22336,14 +22411,14 @@ function pruneOrphans(folder, dir, keep, log) {
22336
22411
  if (!ensureRealSubdirNoSymlink(folder, dir, false)) return;
22337
22412
  let entries;
22338
22413
  try {
22339
- entries = fs16.readdirSync(dir);
22414
+ entries = fs14.readdirSync(dir);
22340
22415
  } catch {
22341
22416
  return;
22342
22417
  }
22343
22418
  for (const file of entries) {
22344
22419
  if (!file.endsWith(".yaml") || keep.has(file)) continue;
22345
22420
  const abs = path19.join(dir, file);
22346
- fs16.rmSync(abs);
22421
+ fs14.rmSync(abs);
22347
22422
  log.removed.push(path19.relative(folder, abs));
22348
22423
  }
22349
22424
  }
@@ -22357,7 +22432,7 @@ function metaFileObject(meta) {
22357
22432
  function writeBaseFolder(folder, meta, config) {
22358
22433
  const delta = { changed: [], removed: [] };
22359
22434
  const parent = path19.dirname(folder);
22360
- fs16.mkdirSync(parent, { recursive: true });
22435
+ fs14.mkdirSync(parent, { recursive: true });
22361
22436
  if (!ensureRealSubdirNoSymlink(parent, folder, true)) {
22362
22437
  throw expected(
22363
22438
  `Refusing to write ${folder}: the path crosses a symlink. Remove it and pull again.`
@@ -22384,7 +22459,7 @@ function writeBaseFolder(folder, meta, config) {
22384
22459
  for (const deprecated of DEPRECATED_ENTITY_DIRS) {
22385
22460
  const dir = path19.join(folder, deprecated);
22386
22461
  if (ensureRealSubdirNoSymlink(folder, dir, false)) {
22387
- fs16.rmSync(dir, { recursive: true, force: true });
22462
+ fs14.rmSync(dir, { recursive: true, force: true });
22388
22463
  }
22389
22464
  }
22390
22465
  return delta;
@@ -22536,7 +22611,7 @@ var init_api = __esm({
22536
22611
  });
22537
22612
 
22538
22613
  // src/data/config-as-code/config-parser.ts
22539
- import * as fs17 from "fs";
22614
+ import * as fs15 from "fs";
22540
22615
  import * as path20 from "path";
22541
22616
  import * as yaml10 from "js-yaml";
22542
22617
  function readEntityDir(folder, dir) {
@@ -22545,7 +22620,7 @@ function readEntityDir(folder, dir) {
22545
22620
  }
22546
22621
  if (!isDirectory(dir)) return [];
22547
22622
  const out = [];
22548
- for (const file of fs17.readdirSync(dir).sort()) {
22623
+ for (const file of fs15.readdirSync(dir).sort()) {
22549
22624
  if (!file.endsWith(".yaml")) continue;
22550
22625
  const abs = path20.join(dir, file);
22551
22626
  const bytes = readFileNoFollow(folder, abs);
@@ -22932,7 +23007,7 @@ __export(push_exports, {
22932
23007
  shouldWarnIgnoredPreviewLabel: () => shouldWarnIgnoredPreviewLabel,
22933
23008
  syncAfterPush: () => syncAfterPush
22934
23009
  });
22935
- import * as fs18 from "fs";
23010
+ import * as fs16 from "fs";
22936
23011
  import * as path22 from "path";
22937
23012
  import * as yaml11 from "js-yaml";
22938
23013
  function parseArgs5(args2) {
@@ -23041,13 +23116,14 @@ function printLocalFileChanges(delta) {
23041
23116
  emit(delta.changed, "~");
23042
23117
  }
23043
23118
  async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, remoteConfig) {
23119
+ requireRealHubFolder(hubFolder, false);
23044
23120
  const agentsDir = path22.join(hubFolder, "agents");
23045
23121
  let agentsWithIds = [];
23046
- if (fs18.existsSync(agentsDir)) {
23047
- const yamlFiles = fs18.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
23122
+ if (fs16.existsSync(agentsDir)) {
23123
+ const yamlFiles = fs16.readdirSync(agentsDir).filter((f) => f.endsWith(".yaml"));
23048
23124
  for (const file of yamlFiles) {
23049
23125
  try {
23050
- const content = fs18.readFileSync(path22.join(agentsDir, file), "utf-8");
23126
+ const content = fs16.readFileSync(path22.join(agentsDir, file), "utf-8");
23051
23127
  const agent = yaml11.load(content);
23052
23128
  if (agent?.id && agent.name) {
23053
23129
  agentsWithIds.push({ id: agent.id, name: agent.name });
@@ -23060,7 +23136,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23060
23136
  if (agentsWithIds.length === 0) {
23061
23137
  const yamlPath = resolveHubYamlPath(hubFolder);
23062
23138
  if (!yamlPath) return;
23063
- const yamlContent = fs18.readFileSync(yamlPath, "utf-8");
23139
+ const yamlContent = readRealFileOrThrow(hubFolder, yamlPath) ?? "";
23064
23140
  const config = yaml11.load(yamlContent);
23065
23141
  agentsWithIds = (config.agents || []).filter((a) => !!a.id && !!a.name);
23066
23142
  }
@@ -23092,18 +23168,18 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23092
23168
  }
23093
23169
  }
23094
23170
  if (renames.length === 0) return;
23095
- if (!fs18.existsSync(agentsDir)) return;
23096
- for (const file of fs18.readdirSync(agentsDir)) {
23171
+ if (!fs16.existsSync(agentsDir)) return;
23172
+ for (const file of fs16.readdirSync(agentsDir)) {
23097
23173
  if (file.startsWith("__rename_temp_") && (file.endsWith(".md") || file.endsWith(".yaml"))) {
23098
23174
  console.warn(` Warning: removing orphaned temp file agents/${file}`);
23099
- fs18.unlinkSync(path22.join(agentsDir, file));
23175
+ fs16.unlinkSync(path22.join(agentsDir, file));
23100
23176
  }
23101
23177
  }
23102
23178
  const renameFileIfExists = (dir, oldName, newName) => {
23103
23179
  const oldPath = path22.join(dir, oldName);
23104
23180
  const newPath = path22.join(dir, newName);
23105
- if (!fs18.existsSync(oldPath)) return false;
23106
- fs18.renameSync(oldPath, newPath);
23181
+ if (!fs16.existsSync(oldPath)) return false;
23182
+ fs16.renameSync(oldPath, newPath);
23107
23183
  return true;
23108
23184
  };
23109
23185
  const oldSlugs = new Set(renames.map((r) => r.oldSlug));
@@ -23136,9 +23212,9 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23136
23212
  }
23137
23213
  } else {
23138
23214
  for (const { oldSlug, newSlug } of renames) {
23139
- const hasOldFile = extensions.some((ext) => fs18.existsSync(path22.join(agentsDir, `${oldSlug}${ext}`)));
23215
+ const hasOldFile = extensions.some((ext) => fs16.existsSync(path22.join(agentsDir, `${oldSlug}${ext}`)));
23140
23216
  if (!hasOldFile) continue;
23141
- const hasNewFile = extensions.some((ext) => fs18.existsSync(path22.join(agentsDir, `${newSlug}${ext}`)));
23217
+ const hasNewFile = extensions.some((ext) => fs16.existsSync(path22.join(agentsDir, `${newSlug}${ext}`)));
23142
23218
  if (hasNewFile) {
23143
23219
  console.warn(` Warning: skipping rename agents/${oldSlug}.* \u2192 agents/${newSlug}.* (target already exists)`);
23144
23220
  continue;
@@ -23153,7 +23229,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23153
23229
  if (completedRenames.length === 0) return;
23154
23230
  const mainYamlPath = resolveHubYamlPath(hubFolder);
23155
23231
  if (mainYamlPath) {
23156
- const mainYamlContent = fs18.readFileSync(mainYamlPath, "utf-8");
23232
+ const mainYamlContent = readRealFileOrThrow(hubFolder, mainYamlPath) ?? "";
23157
23233
  const substitutionMap = /* @__PURE__ */ new Map();
23158
23234
  for (const { oldSlug, newSlug } of completedRenames) {
23159
23235
  substitutionMap.set(`agents/${oldSlug}.md`, `agents/${newSlug}.md`);
@@ -23164,7 +23240,7 @@ async function autoRenameAgentFiles(client, hubId, hubFolder, organizationId, re
23164
23240
  (_match, prefix, pathMatch) => `${prefix}${substitutionMap.get(pathMatch) ?? pathMatch}`
23165
23241
  );
23166
23242
  if (updatedYaml !== mainYamlContent) {
23167
- fs18.writeFileSync(mainYamlPath, updatedYaml, "utf-8");
23243
+ writeFileNoFollow(hubFolder, mainYamlPath, Buffer.from(updatedYaml, "utf-8"));
23168
23244
  console.log(` Updated instructions paths in ${path22.basename(mainYamlPath)}`);
23169
23245
  }
23170
23246
  }
@@ -23258,11 +23334,13 @@ New hub: "${newHub.hubName}" (${hubType})`);
23258
23334
  return;
23259
23335
  }
23260
23336
  }
23337
+ requireRealHubFolder(newHub.hubFolder, false);
23261
23338
  const hubYamlPath = resolveHubYamlPath(newHub.hubFolder);
23262
23339
  if (!hubYamlPath) {
23263
23340
  console.error(`hub.yaml not found in ${newHub.hubFolder}. Cannot proceed with hub creation.`);
23264
23341
  process.exit(1);
23265
23342
  }
23343
+ const content = readRealFileOrThrow(newHub.hubFolder, hubYamlPath) ?? "";
23266
23344
  console.log("Creating hub...");
23267
23345
  const createResult = await client.createHub(opts.organizationId, {
23268
23346
  hubName: newHub.hubName,
@@ -23276,7 +23354,6 @@ New hub: "${newHub.hubName}" (${hubType})`);
23276
23354
  process.exit(1);
23277
23355
  }
23278
23356
  console.log(`Hub created: ${createdHub.hub_name} (${hubId})`);
23279
- const content = fs18.readFileSync(hubYamlPath, "utf-8");
23280
23357
  const hasVersion = content.match(/^version:\s/m);
23281
23358
  let updated;
23282
23359
  if (hasVersion) {
@@ -23292,7 +23369,7 @@ hub_id: "${hubId}"
23292
23369
  hub_environment: preview
23293
23370
  ${content}`;
23294
23371
  }
23295
- fs18.writeFileSync(hubYamlPath, updated, "utf-8");
23372
+ writeFileNoFollow(newHub.hubFolder, hubYamlPath, Buffer.from(updated, "utf-8"));
23296
23373
  seedScopeIfEmpty("hubs", hubId);
23297
23374
  await pushSingleHub(client, hubId, newHub.hubFolder, opts.autoConfirm, opts.organizationId, { skipAgentRename: true });
23298
23375
  }
@@ -23338,7 +23415,7 @@ async function pushCommand(args2) {
23338
23415
  const client = new ApiClient({ apiUrl: config.api_url, accessToken });
23339
23416
  const workspaceDir = resolveWorkspaceDir();
23340
23417
  const wsLabel = hubsDirLabel(gitRoot);
23341
- if (!fs18.existsSync(workspaceDir)) {
23418
+ if (!fs16.existsSync(workspaceDir)) {
23342
23419
  console.error(`No ${wsLabel}/ directory found. Run \`wayai pull\` first or create hub files in ${wsLabel}/<hub>/hub.yaml.`);
23343
23420
  process.exit(1);
23344
23421
  }
@@ -23382,6 +23459,7 @@ var init_push = __esm({
23382
23459
  init_repo_config();
23383
23460
  init_worktree_scope();
23384
23461
  init_subtree_routing();
23462
+ init_fs_safety();
23385
23463
  LOCAL_CHANGE_LIST_CAP = 20;
23386
23464
  }
23387
23465
  });
@@ -23394,7 +23472,7 @@ __export(pull_exports, {
23394
23472
  resolveHubTarget: () => resolveHubTarget,
23395
23473
  writeProductionMirror: () => writeProductionMirror2
23396
23474
  });
23397
- import * as fs19 from "fs";
23475
+ import * as fs17 from "fs";
23398
23476
  import * as path23 from "path";
23399
23477
  function parseArgs6(args2) {
23400
23478
  return { autoConfirm: args2.includes("--yes") || args2.includes("-y") };
@@ -23461,7 +23539,7 @@ async function pullCommand(args2) {
23461
23539
  payload.preview_label,
23462
23540
  payload.branch_name
23463
23541
  );
23464
- fs19.mkdirSync(path23.dirname(hubFolder), { recursive: true });
23542
+ fs17.mkdirSync(path23.dirname(hubFolder), { recursive: true });
23465
23543
  console.log("Writing hub configuration...");
23466
23544
  await materializeHubFolder(hubFolder, payload);
23467
23545
  const finalFolder = autoRenameHubFolder(hubFolder, payload.hub.name, payload.hub_environment, payload.hub_id, payload.preview_label, payload.branch_name);
@@ -23515,7 +23593,7 @@ async function pullCommand(args2) {
23515
23593
  }
23516
23594
  async function writeProductionMirror2(workspaceDir, prodPayload) {
23517
23595
  const folder = resolveHubFolder(workspaceDir, prodPayload.hub_id, prodPayload.hub.name, "production", null, null);
23518
- fs19.mkdirSync(path23.dirname(folder), { recursive: true });
23596
+ fs17.mkdirSync(path23.dirname(folder), { recursive: true });
23519
23597
  await materializeHubFolder(folder, prodPayload, { seedAgentContext: false });
23520
23598
  const finalFolder = autoRenameHubFolder(folder, prodPayload.hub.name, "production", prodPayload.hub_id, null, null);
23521
23599
  prependMirrorMarker(finalFolder, prodPayload.hub_id);
@@ -23533,11 +23611,11 @@ async function mirrorLinkedProduction2(client, workspaceDir, productionHubId, or
23533
23611
  function prependMirrorMarker(hubFolder, productionHubId) {
23534
23612
  const hubYaml = path23.join(hubFolder, "hub.yaml");
23535
23613
  try {
23536
- const content = fs19.readFileSync(hubYaml, "utf-8");
23614
+ const content = fs17.readFileSync(hubYaml, "utf-8");
23537
23615
  if (content.startsWith(MIRROR_MARKER_PREFIX2)) return;
23538
23616
  const marker = `${MIRROR_MARKER_PREFIX2} ${productionHubId}. Edits are ignored; push is blocked. Edit the linked preview hub instead.
23539
23617
  `;
23540
- fs19.writeFileSync(hubYaml, marker + content, "utf-8");
23618
+ fs17.writeFileSync(hubYaml, marker + content, "utf-8");
23541
23619
  } catch {
23542
23620
  }
23543
23621
  }
@@ -23581,7 +23659,7 @@ __export(create_exports, {
23581
23659
  createCommand: () => createCommand
23582
23660
  });
23583
23661
  import * as path24 from "path";
23584
- import * as fs20 from "fs";
23662
+ import * as fs18 from "fs";
23585
23663
  function parseArgs7(args2) {
23586
23664
  let autoConfirm = false;
23587
23665
  let folderSelector;
@@ -23608,7 +23686,7 @@ async function createCommand(args2) {
23608
23686
  const gitRoot = findGitRoot();
23609
23687
  const wsLabel = hubsDirLabel(gitRoot);
23610
23688
  if (gitRoot) warnLayoutOnce(gitRoot);
23611
- if (!fs20.existsSync(workspaceDir)) {
23689
+ if (!fs18.existsSync(workspaceDir)) {
23612
23690
  console.error(`No ${wsLabel}/ directory found. Create hub files in ${wsLabel}/<hub>/hub.yaml with \`hub: { name: ... }\` first.`);
23613
23691
  process.exit(1);
23614
23692
  }
@@ -23742,7 +23820,7 @@ var replicate_exports = {};
23742
23820
  __export(replicate_exports, {
23743
23821
  replicateCommand: () => replicateCommand
23744
23822
  });
23745
- import * as fs21 from "fs";
23823
+ import * as fs19 from "fs";
23746
23824
  import * as path25 from "path";
23747
23825
  function parseArgs9(args2) {
23748
23826
  let label;
@@ -23789,8 +23867,8 @@ async function replicateCommand(args2) {
23789
23867
  payload.preview_label,
23790
23868
  payload.branch_name
23791
23869
  );
23792
- const folderPreExisted = fs21.existsSync(hubFolder);
23793
- fs21.mkdirSync(path25.dirname(hubFolder), { recursive: true });
23870
+ const folderPreExisted = fs19.existsSync(hubFolder);
23871
+ fs19.mkdirSync(path25.dirname(hubFolder), { recursive: true });
23794
23872
  const delta = await materializeHubFolder(hubFolder, payload);
23795
23873
  hubFolder = autoRenameHubFolder(
23796
23874
  hubFolder,
@@ -24217,7 +24295,7 @@ __export(migrate_exports, {
24217
24295
  migrateCommand: () => migrateCommand
24218
24296
  });
24219
24297
  import { execFileSync as execFileSync3 } from "child_process";
24220
- import * as fs22 from "fs";
24298
+ import * as fs20 from "fs";
24221
24299
  import * as path29 from "path";
24222
24300
  function isTracked(gitRoot, p) {
24223
24301
  try {
@@ -24231,7 +24309,7 @@ function isTracked(gitRoot, p) {
24231
24309
  }
24232
24310
  }
24233
24311
  function moveDir(gitRoot, from, to) {
24234
- fs22.mkdirSync(path29.dirname(to), { recursive: true });
24312
+ fs20.mkdirSync(path29.dirname(to), { recursive: true });
24235
24313
  if (isTracked(gitRoot, from)) {
24236
24314
  try {
24237
24315
  execFileSync3("git", ["mv", path29.relative(gitRoot, from), path29.relative(gitRoot, to)], {
@@ -24242,7 +24320,7 @@ function moveDir(gitRoot, from, to) {
24242
24320
  } catch {
24243
24321
  }
24244
24322
  }
24245
- fs22.renameSync(from, to);
24323
+ fs20.renameSync(from, to);
24246
24324
  return "fs";
24247
24325
  }
24248
24326
  async function migrateCommand(_args) {
@@ -24257,8 +24335,11 @@ async function migrateCommand(_args) {
24257
24335
  const legacyOrg = path29.join(gitRoot, WAYAI_LAYOUT.legacy.orgAtRoot);
24258
24336
  const newHubs = path29.join(newWs, WAYAI_LAYOUT.hubsSubdir);
24259
24337
  const newOrg = path29.join(newWs, WAYAI_LAYOUT.orgSubdir);
24260
- const hasLegacyWs = isDirectory(legacyWs);
24261
- const hasLegacyOrg = isDirectory(legacyOrg);
24338
+ requireRealSubdirNoSymlink(gitRoot, newHubs, false);
24339
+ requireRealSubdirNoSymlink(gitRoot, newOrg, false);
24340
+ readRealFileOrThrow(gitRoot, workspaceManifestPath(gitRoot));
24341
+ const hasLegacyWs = isRealDirectory(legacyWs);
24342
+ const hasLegacyOrg = isRealDirectory(legacyOrg);
24262
24343
  const orgPlan = planOrgMigration(gitRoot);
24263
24344
  if (orgPlan.kind === "refuse") {
24264
24345
  console.error(orgPlan.message);
@@ -24322,6 +24403,7 @@ var init_migrate = __esm({
24322
24403
  "use strict";
24323
24404
  init_workspace();
24324
24405
  init_layout();
24406
+ init_fs_safety();
24325
24407
  init_repo_config();
24326
24408
  init_workspace_manifest();
24327
24409
  }
@@ -24405,12 +24487,12 @@ var send_message_exports = {};
24405
24487
  __export(send_message_exports, {
24406
24488
  sendMessageCommand: () => sendMessageCommand
24407
24489
  });
24408
- import * as fs23 from "fs";
24490
+ import * as fs21 from "fs";
24409
24491
  import * as path30 from "path";
24410
24492
  function statAttachment(filePath) {
24411
24493
  let stat2;
24412
24494
  try {
24413
- stat2 = fs23.statSync(filePath);
24495
+ stat2 = fs21.statSync(filePath);
24414
24496
  } catch {
24415
24497
  console.error(`Error: file not found: ${filePath}`);
24416
24498
  process.exit(1);
@@ -24426,7 +24508,7 @@ function readAttachment(filePath, size) {
24426
24508
  const ext = path30.extname(fileName).replace(/^\./, "");
24427
24509
  return {
24428
24510
  file_name: fileName,
24429
- file_binary: fs23.readFileSync(filePath).toString("base64"),
24511
+ file_binary: fs21.readFileSync(filePath).toString("base64"),
24430
24512
  file_size: size,
24431
24513
  ...ext && { file_extension: ext }
24432
24514
  };
@@ -26625,7 +26707,7 @@ var eval_capture_exports = {};
26625
26707
  __export(eval_capture_exports, {
26626
26708
  evalCaptureCommand: () => evalCaptureCommand
26627
26709
  });
26628
- import * as fs24 from "fs";
26710
+ import * as fs22 from "fs";
26629
26711
  import * as path31 from "path";
26630
26712
  import * as yaml12 from "js-yaml";
26631
26713
  function isValidSetName(name) {
@@ -26699,7 +26781,17 @@ async function evalCaptureCommand(args2) {
26699
26781
  console.error(`Resolved path "${path31.relative(hubFolder, targetPath)}" escapes evals/. Aborting.`);
26700
26782
  process.exit(1);
26701
26783
  }
26702
- if (fs24.existsSync(targetPath)) {
26784
+ if (!ensureRealSubdirNoSymlink(hubFolder, targetDir, false)) {
26785
+ console.error(`${path31.relative(hubFolder, targetDir)} is reached through a symlink. Aborting.`);
26786
+ process.exit(1);
26787
+ }
26788
+ let targetTaken = true;
26789
+ try {
26790
+ fs22.lstatSync(targetPath);
26791
+ } catch {
26792
+ targetTaken = false;
26793
+ }
26794
+ if (targetTaken) {
26703
26795
  console.error(`File already exists: ${path31.relative(hubFolder, targetPath)}. Use --name to choose a different name.`);
26704
26796
  process.exit(1);
26705
26797
  }
@@ -26732,8 +26824,13 @@ async function evalCaptureCommand(args2) {
26732
26824
  ...captured.evaluator_instructions ? { evaluator_instructions: captured.evaluator_instructions } : {}
26733
26825
  };
26734
26826
  const yamlObj = buildEvalYamlObject(evalEntry, slug);
26735
- fs24.mkdirSync(targetDir, { recursive: true });
26736
- fs24.writeFileSync(targetPath, yaml12.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8");
26827
+ const outcome = createFileNoFollow(hubFolder, targetPath, Buffer.from(yaml12.dump(yamlObj, YAML_DUMP_OPTIONS), "utf-8"));
26828
+ if (outcome !== "created") {
26829
+ console.error(
26830
+ `Could not write ${path31.relative(hubFolder, targetPath)} (${outcome === "exists" ? "it appeared meanwhile" : "its folder is reached through a symlink"}). The scenario was created on the platform as ${captured.eval_id} \u2014 run \`wayai pull\` to fetch it.`
26831
+ );
26832
+ process.exit(1);
26833
+ }
26737
26834
  const relPath = path31.relative(process.cwd(), targetPath);
26738
26835
  console.log(`
26739
26836
  Wrote ${relPath}`);
@@ -26748,6 +26845,7 @@ var init_eval_capture = __esm({
26748
26845
  init_workspace();
26749
26846
  init_utils();
26750
26847
  init_yaml_writer();
26848
+ init_fs_safety();
26751
26849
  }
26752
26850
  });
26753
26851
 
@@ -28105,23 +28203,21 @@ var init_set_connection_credential = __esm({
28105
28203
  });
28106
28204
 
28107
28205
  // src/lib/org-workspace.ts
28108
- import * as fs25 from "fs";
28206
+ import * as fs23 from "fs";
28109
28207
  import * as path32 from "path";
28110
28208
  import * as yaml13 from "js-yaml";
28111
28209
  function getOrgDir(gitRoot) {
28112
28210
  return resolveLayout(gitRoot).orgDir;
28113
28211
  }
28114
28212
  function orgManifestExists(orgDir) {
28115
- return fs25.existsSync(path32.join(orgDir, ORG_MANIFEST_NAME));
28213
+ return fs23.existsSync(path32.join(orgDir, ORG_MANIFEST_NAME));
28116
28214
  }
28117
28215
  function parseOrgResources(orgDir) {
28118
- const manifestPath = path32.join(orgDir, ORG_MANIFEST_NAME);
28119
- let manifest = {};
28120
- if (fs25.existsSync(manifestPath)) {
28121
- manifest = yaml13.load(fs25.readFileSync(manifestPath, "utf-8")) ?? {};
28122
- }
28123
- const rawResources = Array.isArray(manifest.resources) ? manifest.resources : [];
28124
28216
  const resourcesDir = path32.join(orgDir, "resources");
28217
+ requireRealSubdirNoSymlink(orgDir, resourcesDir, false);
28218
+ const manifestText = readRealFileOrThrow(orgDir, path32.join(orgDir, ORG_MANIFEST_NAME));
28219
+ const manifest = (manifestText !== null ? yaml13.load(manifestText) : null) ?? {};
28220
+ const rawResources = Array.isArray(manifest.resources) ? manifest.resources : [];
28125
28221
  const resources = rawResources.map((res) => {
28126
28222
  const resource = { name: res.name };
28127
28223
  if (res.id) resource.id = res.id;
@@ -28134,7 +28230,7 @@ function parseOrgResources(orgDir) {
28134
28230
  if (Array.isArray(res.tags)) resource.tags = res.tags;
28135
28231
  if (Array.isArray(res.folders)) resource.folders = res.folders;
28136
28232
  const resDir = path32.join(resourcesDir, slugify(resource.name));
28137
- if (fs25.existsSync(resDir)) {
28233
+ if (fs23.existsSync(resDir)) {
28138
28234
  const files = scanResourceFiles(resDir, "");
28139
28235
  if (files.length > 0) resource.files = files;
28140
28236
  }
@@ -28143,28 +28239,29 @@ function parseOrgResources(orgDir) {
28143
28239
  return { version: 1, resources };
28144
28240
  }
28145
28241
  function writeOrgResources(orgDir, payload) {
28146
- fs25.mkdirSync(orgDir, { recursive: true });
28242
+ fs23.mkdirSync(orgDir, { recursive: true });
28243
+ const resourcesDir = path32.join(orgDir, "resources");
28244
+ requireRealSubdirNoSymlink(orgDir, resourcesDir, false);
28147
28245
  const resources = payload.resources ?? [];
28148
28246
  const manifestResources = resources.map((r) => {
28149
28247
  const { files: _files, ...rest } = r;
28150
28248
  return rest;
28151
28249
  });
28152
- fs25.writeFileSync(
28250
+ writeFileNoFollow(
28251
+ orgDir,
28153
28252
  path32.join(orgDir, ORG_MANIFEST_NAME),
28154
- yaml13.dump({ version: 1, resources: manifestResources }, YAML_DUMP_OPTIONS),
28155
- "utf-8"
28253
+ Buffer.from(yaml13.dump({ version: 1, resources: manifestResources }, YAML_DUMP_OPTIONS), "utf-8")
28156
28254
  );
28157
- const resourcesDir = path32.join(orgDir, "resources");
28158
28255
  const currentSlugs = /* @__PURE__ */ new Set();
28159
28256
  for (const resource of resources) {
28160
28257
  const resSlug = slugify(resource.name);
28161
28258
  currentSlugs.add(resSlug);
28162
28259
  writeResourceFileTree(path32.join(resourcesDir, resSlug), resource.files || [], orgDir);
28163
28260
  }
28164
- if (fs25.existsSync(resourcesDir)) {
28165
- for (const entry of fs25.readdirSync(resourcesDir, { withFileTypes: true })) {
28261
+ if (fs23.existsSync(resourcesDir)) {
28262
+ for (const entry of fs23.readdirSync(resourcesDir, { withFileTypes: true })) {
28166
28263
  if (entry.isDirectory() && !currentSlugs.has(entry.name)) {
28167
- fs25.rmSync(path32.join(resourcesDir, entry.name), { recursive: true, force: true });
28264
+ fs23.rmSync(path32.join(resourcesDir, entry.name), { recursive: true, force: true });
28168
28265
  }
28169
28266
  }
28170
28267
  }
@@ -28184,6 +28281,7 @@ var init_org_workspace = __esm({
28184
28281
  "use strict";
28185
28282
  init_utils();
28186
28283
  init_layout();
28284
+ init_fs_safety();
28187
28285
  init_resource_files();
28188
28286
  init_yaml_writer();
28189
28287
  ORG_MANIFEST_NAME = "resources.yaml";
@@ -28469,7 +28567,7 @@ var init_report_edit_args = __esm({
28469
28567
  });
28470
28568
 
28471
28569
  // src/lib/file-map.ts
28472
- import * as fs26 from "fs";
28570
+ import * as fs24 from "fs";
28473
28571
  import * as path33 from "path";
28474
28572
  function isSafeRelPath(rel) {
28475
28573
  if (rel.length === 0 || rel.length > 300) return false;
@@ -28486,8 +28584,8 @@ function writeFileMap(targetDir, files) {
28486
28584
  throw new Error(`Refusing to write unsafe path: ${rel}`);
28487
28585
  }
28488
28586
  const abs = path33.join(targetDir, rel);
28489
- fs26.mkdirSync(path33.dirname(abs), { recursive: true });
28490
- fs26.writeFileSync(abs, body, "utf-8");
28587
+ fs24.mkdirSync(path33.dirname(abs), { recursive: true });
28588
+ fs24.writeFileSync(abs, body, "utf-8");
28491
28589
  written.push(rel);
28492
28590
  }
28493
28591
  return written;
@@ -28503,7 +28601,7 @@ var admin_exports = {};
28503
28601
  __export(admin_exports, {
28504
28602
  adminCommand: () => adminCommand
28505
28603
  });
28506
- import * as fs27 from "fs";
28604
+ import * as fs25 from "fs";
28507
28605
  import * as path34 from "path";
28508
28606
  async function adminCommand(args2) {
28509
28607
  const [group, ...afterGroup] = args2;
@@ -28819,7 +28917,7 @@ async function runArchiveRead(positional, flagArgs) {
28819
28917
  exitOnApiError(err);
28820
28918
  throw err;
28821
28919
  }
28822
- fs27.writeFileSync(outPath, zip);
28920
+ fs25.writeFileSync(outPath, zip);
28823
28921
  console.log(`Wrote ${zip.byteLength} bytes to ${outPath}`);
28824
28922
  return;
28825
28923
  }
@@ -28961,7 +29059,7 @@ async function runSkillInstall(positional) {
28961
29059
  throw err;
28962
29060
  }
28963
29061
  const root = findGitRoot() ?? process.cwd();
28964
- const present = HARNESS_SKILL_DIRS.filter((dir) => fs27.existsSync(path34.join(root, dir)));
29062
+ const present = HARNESS_SKILL_DIRS.filter((dir) => fs25.existsSync(path34.join(root, dir)));
28965
29063
  const targets = present.length > 0 ? present : HARNESS_SKILL_DIRS;
28966
29064
  const fileCount = Object.keys(res.files).length;
28967
29065
  const relDirs = targets.map((harness) => {
@@ -30519,7 +30617,7 @@ var init_actions = __esm({
30519
30617
 
30520
30618
  // src/data/commands/attachments.ts
30521
30619
  import { Command as Command2 } from "commander";
30522
- import { readFileSync as readFileSync20 } from "fs";
30620
+ import { readFileSync as readFileSync18 } from "fs";
30523
30621
  function findAttachmentByFilename(attachments, filename) {
30524
30622
  return attachments.find((a) => a.key.endsWith(`/${filename}`)) ?? null;
30525
30623
  }
@@ -30560,7 +30658,7 @@ function buildAttachmentsCommand() {
30560
30658
  printOutput(data, outputFormat(this));
30561
30659
  return;
30562
30660
  }
30563
- const body = readFileSync20(opts.file);
30661
+ const body = readFileSync18(opts.file);
30564
30662
  await client.upload(uploadPathFrom(data?.upload_url), body, opts.contentType);
30565
30663
  printOutput({ ...data, uploaded: true }, outputFormat(this));
30566
30664
  });
@@ -30960,7 +31058,7 @@ var init_import = __esm({
30960
31058
 
30961
31059
  // src/data/commands/providers.ts
30962
31060
  import { Command as Command5 } from "commander";
30963
- import { writeFileSync as writeFileSync15 } from "fs";
31061
+ import { writeFileSync as writeFileSync9 } from "fs";
30964
31062
  function providerSegment(provider) {
30965
31063
  if (!VALID_PROVIDERS.includes(provider)) {
30966
31064
  throw expected(`Unknown provider ${JSON.stringify(provider)}. Expected one of: ${VALID_PROVIDERS_HELP}.`);
@@ -30993,7 +31091,7 @@ function buildBasesProvidersCommand() {
30993
31091
  );
30994
31092
  if (opts.to) {
30995
31093
  try {
30996
- writeFileSync15(opts.to, JSON.stringify(data, null, 2));
31094
+ writeFileSync9(opts.to, JSON.stringify(data, null, 2));
30997
31095
  } catch (e) {
30998
31096
  throw expected(`--to ${opts.to}: ${e instanceof Error ? e.message : "could not be written"}`);
30999
31097
  }
@@ -31040,7 +31138,7 @@ var init_providers = __esm({
31040
31138
 
31041
31139
  // src/data/commands/report.ts
31042
31140
  import { Command as Command6 } from "commander";
31043
- import { readFileSync as readFileSync21 } from "fs";
31141
+ import { readFileSync as readFileSync19 } from "fs";
31044
31142
  import { dirname as dirname12, join as join30 } from "path";
31045
31143
  import { fileURLToPath as fileURLToPath2 } from "url";
31046
31144
  function resolveCliVersion() {
@@ -31049,7 +31147,7 @@ function resolveCliVersion() {
31049
31147
  join30(here, "..", "..", "..", "package.json")
31050
31148
  ]) {
31051
31149
  try {
31052
- const version = JSON.parse(readFileSync21(candidate, "utf-8")).version;
31150
+ const version = JSON.parse(readFileSync19(candidate, "utf-8")).version;
31053
31151
  if (typeof version === "string" && version) return version;
31054
31152
  } catch {
31055
31153
  }
@@ -31251,7 +31349,7 @@ var init_report2 = __esm({
31251
31349
 
31252
31350
  // src/data/commands/credentials.ts
31253
31351
  import { Command as Command7 } from "commander";
31254
- import { readFileSync as readFileSync22 } from "fs";
31352
+ import { readFileSync as readFileSync20 } from "fs";
31255
31353
  function withValueSourceOptions(cmd, what) {
31256
31354
  return cmd.option(
31257
31355
  "--file <path>",
@@ -31264,7 +31362,7 @@ async function resolveValue(opts, label) {
31264
31362
  throw expected("--file cannot be combined with --value-stdin or --value-prompt \u2014 pass one.");
31265
31363
  }
31266
31364
  try {
31267
- return readFileSync22(opts.file).toString("base64");
31365
+ return readFileSync20(opts.file).toString("base64");
31268
31366
  } catch (e) {
31269
31367
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
31270
31368
  }
@@ -31413,7 +31511,7 @@ var init_credentials = __esm({
31413
31511
 
31414
31512
  // src/data/commands/sql.ts
31415
31513
  import { Command as Command8 } from "commander";
31416
- import { readFileSync as readFileSync23 } from "fs";
31514
+ import { readFileSync as readFileSync21 } from "fs";
31417
31515
  function buildBasesSqlCommand() {
31418
31516
  return withBaseOption(new Command8("sql")).description("Execute a read-only SQL query against base data").argument("[query]", "SQL query (SELECT only)").option("--file <path>", "Read SQL from a file instead of the argument").option(
31419
31517
  "--param <kv...>",
@@ -31423,7 +31521,7 @@ function buildBasesSqlCommand() {
31423
31521
  let query;
31424
31522
  if (opts.file) {
31425
31523
  try {
31426
- query = readFileSync23(opts.file, "utf-8").trim();
31524
+ query = readFileSync21(opts.file, "utf-8").trim();
31427
31525
  } catch (e) {
31428
31526
  throw expected(`--file ${opts.file}: ${e instanceof Error ? e.message : "could not be read"}`);
31429
31527
  }
@@ -32078,8 +32176,8 @@ var init_file_types = __esm({
32078
32176
 
32079
32177
  // src/data/commands/files.ts
32080
32178
  import { Command as Command12 } from "commander";
32081
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync16 } from "fs";
32082
- import { basename as basename18 } from "path";
32179
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync10 } from "fs";
32180
+ import { basename as basename19 } from "path";
32083
32181
  function renderFileDiff(fileType, filePath, from, to, d) {
32084
32182
  console.log(sanitizeTerminalText(`${fileType}/${filePath}: v${from} \u2192 v${to}`));
32085
32183
  const md = d.metadata_delta;
@@ -32111,7 +32209,7 @@ function renderFileDiff(fileType, filePath, from, to, d) {
32111
32209
  }
32112
32210
  function downloadTarget(remotePath, to) {
32113
32211
  if (to) return to;
32114
- const derived = basename18(remotePath);
32212
+ const derived = basename19(remotePath);
32115
32213
  if (derived === "" || derived === "." || derived === "..") {
32116
32214
  throw expected(
32117
32215
  `Cannot derive a local filename from "${remotePath}" \u2014 pass --to <local> to name it.`
@@ -32127,7 +32225,7 @@ function buildFilesCommand() {
32127
32225
  "Upload a local file to a path (e.g. wayai files put reports q3/summary.pdf --file ./summary.pdf)"
32128
32226
  ).requiredOption("--file <local>", "Local file to upload").option("--content-type <type>", "MIME type", "application/octet-stream").action(async function(fileType, filePath, opts) {
32129
32227
  const base = pathSegment(requireBase(this), "--base");
32130
- const body = readFileSync24(opts.file);
32228
+ const body = readFileSync22(opts.file);
32131
32229
  const client = await createDataClient();
32132
32230
  printOutput(
32133
32231
  await client.upload(
@@ -32158,7 +32256,7 @@ function buildFilesCommand() {
32158
32256
  const { bytes } = await client.download(
32159
32257
  `/v1/${base}/files/${pathSegment(fileType, "file_type")}/${encoded}${versionQs ? `?${versionQs}` : ""}`
32160
32258
  );
32161
- writeFileSync16(out, bytes);
32259
+ writeFileSync10(out, bytes);
32162
32260
  console.log(`Downloaded to ${out}`);
32163
32261
  });
32164
32262
  files.command("history <file_type> <path>").description("List the content versions of a file (newest first)").option("--limit <n>", "Max versions to return").option("--offset <n>", "Pagination offset").action(async function(fileType, filePath, opts) {
@@ -33176,7 +33274,7 @@ init_errors2();
33176
33274
  init_mask_secrets();
33177
33275
  init_utils();
33178
33276
  init_registry();
33179
- import { readFileSync as readFileSync25 } from "fs";
33277
+ import { readFileSync as readFileSync23 } from "fs";
33180
33278
  import { fileURLToPath as fileURLToPath3 } from "url";
33181
33279
  import { dirname as dirname13, join as join31 } from "path";
33182
33280
 
@@ -33334,7 +33432,7 @@ Run \`wayai admin skill install\` to update.`);
33334
33432
 
33335
33433
  // src/index.ts
33336
33434
  var __dirname = dirname13(fileURLToPath3(import.meta.url));
33337
- var pkg = JSON.parse(readFileSync25(join31(__dirname, "..", "package.json"), "utf-8"));
33435
+ var pkg = JSON.parse(readFileSync23(join31(__dirname, "..", "package.json"), "utf-8"));
33338
33436
  var [, , command, ...args] = process.argv;
33339
33437
  var isBackgroundRefresh = command === REFRESH_COMMAND;
33340
33438
  if (!isBackgroundRefresh) initSentry(command, pkg.version);