@theokit/sdk-tools 0.15.0 → 0.15.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.15.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 4c5bd35: M15 review fixes (injected fs path only; local path unaffected): (1) the backend directory walk in
8
+ `glob_files`/`search_text` decides entry type via `stat` (which follows symlinks), so an in-boundary
9
+ symlink cycle could recurse until PATH_MAX — now depth-capped so it terminates; (2) `edit_file`'s
10
+ backend read mapped every failure to `not_found` — now only a genuinely missing file (`FileNotFoundError`)
11
+ maps to `not_found`; any other read error (e.g. a directory, a permission error) propagates (fail-loud),
12
+ matching the local path's ENOENT-only classification.
13
+
3
14
  ## 0.15.0
4
15
 
5
16
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -381,8 +381,11 @@ async function editViaBackend(filesystem$1, ctx, path, old_string, new_string) {
381
381
  let content;
382
382
  try {
383
383
  content = await backend.readFile(path);
384
- } catch {
385
- return JSON.stringify({ ok: false, error: "not_found", path });
384
+ } catch (err) {
385
+ if (err instanceof filesystem.FileNotFoundError) {
386
+ return JSON.stringify({ ok: false, error: "not_found", path });
387
+ }
388
+ throw err;
386
389
  }
387
390
  const outcome = computeEdit(content, old_string, new_string);
388
391
  if (!outcome.ok) return JSON.stringify({ ok: false, error: "no_match", path });
@@ -656,6 +659,7 @@ function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
656
659
  });
657
660
  }
658
661
  var DEFAULT_EXCLUDES = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", ".theo"]);
662
+ var MAX_BACKEND_WALK_DEPTH = 64;
659
663
  function createGlobTool(opts) {
660
664
  const { projectRoot, filesystem: filesystem$1 } = opts;
661
665
  return sdk.Tool.create({
@@ -673,7 +677,7 @@ function createGlobTool(opts) {
673
677
  const backend = await filesystem.resolveFilesystem(filesystem$1, ctx ?? {});
674
678
  const searchRel = cwd ?? "";
675
679
  const found = [];
676
- await walkDirBackend(backend, searchRel, searchRel, regex, found);
680
+ await walkDirBackend(backend, searchRel, searchRel, regex, found, 0);
677
681
  return JSON.stringify({ ok: true, files: found.sort(), count: found.length });
678
682
  }
679
683
  const searchRoot = cwd ? safePathJoin(projectRoot, cwd) : projectRoot;
@@ -714,7 +718,8 @@ async function walkDir(base, dir, pattern, results) {
714
718
  }
715
719
  }
716
720
  }
717
- async function walkDirBackend(backend, base, dir, pattern, results) {
721
+ async function walkDirBackend(backend, base, dir, pattern, results, depth) {
722
+ if (depth > MAX_BACKEND_WALK_DEPTH) return;
718
723
  let names;
719
724
  try {
720
725
  names = await backend.list(dir);
@@ -722,10 +727,10 @@ async function walkDirBackend(backend, base, dir, pattern, results) {
722
727
  return;
723
728
  }
724
729
  for (const name of names) {
725
- await walkBackendEntry(backend, base, dir, name, pattern, results);
730
+ await walkBackendEntry(backend, base, dir, name, pattern, results, depth);
726
731
  }
727
732
  }
728
- async function walkBackendEntry(backend, base, dir, name, pattern, results) {
733
+ async function walkBackendEntry(backend, base, dir, name, pattern, results, depth) {
729
734
  if (DEFAULT_EXCLUDES.has(name)) return;
730
735
  const fullRel = dir === "" ? name : `${dir}/${name}`;
731
736
  const relPath = base === "" ? fullRel : path.relative(base, fullRel);
@@ -736,7 +741,7 @@ async function walkBackendEntry(backend, base, dir, name, pattern, results) {
736
741
  return;
737
742
  }
738
743
  if (st.isDirectory) {
739
- await walkDirBackend(backend, base, fullRel, pattern, results);
744
+ await walkDirBackend(backend, base, fullRel, pattern, results, depth + 1);
740
745
  } else if (st.isFile && pattern.test(relPath)) {
741
746
  results.push(fullRel);
742
747
  }
@@ -1808,6 +1813,7 @@ var DEFAULT_MAX_MATCHES = 100;
1808
1813
  var DEFAULT_MAX_FILE_SIZE = 1024 * 1024;
1809
1814
  var BINARY_PROBE_BYTES2 = 8 * 1024;
1810
1815
  var PREVIEW_MAX = 200;
1816
+ var MAX_BACKEND_WALK_DEPTH2 = 64;
1811
1817
  function createSearchTextTool(opts) {
1812
1818
  const {
1813
1819
  projectRoot,
@@ -1836,7 +1842,7 @@ function createSearchTextTool(opts) {
1836
1842
  const scopeRel = resolveScopeRel(path, projectRoot);
1837
1843
  if ("error" in scopeRel) return scopeRel.error;
1838
1844
  const backend = await filesystem.resolveFilesystem(filesystem$1, ctx ?? {});
1839
- await walkBackend(backend, scopeRel.rel, state);
1845
+ await walkBackend(backend, scopeRel.rel, state, 0);
1840
1846
  return JSON.stringify({
1841
1847
  ok: true,
1842
1848
  matches: state.matches,
@@ -1946,8 +1952,9 @@ function resolveScopeRel(path, projectRoot) {
1946
1952
  throw err;
1947
1953
  }
1948
1954
  }
1949
- async function walkBackend(backend, dirRel, state) {
1955
+ async function walkBackend(backend, dirRel, state, depth) {
1950
1956
  if (state.truncated) return;
1957
+ if (depth > MAX_BACKEND_WALK_DEPTH2) return;
1951
1958
  let names;
1952
1959
  try {
1953
1960
  names = await backend.list(dirRel);
@@ -1956,10 +1963,10 @@ async function walkBackend(backend, dirRel, state) {
1956
1963
  }
1957
1964
  for (const name of names) {
1958
1965
  if (state.truncated) return;
1959
- await handleBackendEntry(backend, dirRel, name, state);
1966
+ await handleBackendEntry(backend, dirRel, name, state, depth);
1960
1967
  }
1961
1968
  }
1962
- async function handleBackendEntry(backend, dirRel, name, state) {
1969
+ async function handleBackendEntry(backend, dirRel, name, state, depth) {
1963
1970
  const entryRel = dirRel === "" ? name : `${dirRel}/${name}`;
1964
1971
  if (isForbiddenPath(entryRel)) return;
1965
1972
  let st;
@@ -1969,7 +1976,7 @@ async function handleBackendEntry(backend, dirRel, name, state) {
1969
1976
  return;
1970
1977
  }
1971
1978
  if (st.isDirectory) {
1972
- await walkBackend(backend, entryRel, state);
1979
+ await walkBackend(backend, entryRel, state, depth + 1);
1973
1980
  } else if (st.isFile) {
1974
1981
  await scanFileBackend(backend, entryRel, st.size, state);
1975
1982
  }