@theokit/sdk-tools 0.20.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +49 -137
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +18 -106
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -4,105 +4,17 @@ var promises = require('fs/promises');
|
|
|
4
4
|
var path = require('path');
|
|
5
5
|
var sdk = require('@theokit/sdk');
|
|
6
6
|
var zod = require('zod');
|
|
7
|
-
var fs = require('fs');
|
|
8
7
|
var pathSafety = require('@theokit/sdk/path-safety');
|
|
9
8
|
var persistence = require('@theokit/sdk/persistence');
|
|
10
9
|
var filesystem = require('@theokit/sdk/filesystem');
|
|
11
10
|
var child_process = require('child_process');
|
|
11
|
+
var fs = require('fs');
|
|
12
12
|
var sandbox = require('@theokit/sdk/sandbox');
|
|
13
13
|
var interactive = require('@theokit/sdk/interactive');
|
|
14
14
|
var promises$1 = require('dns/promises');
|
|
15
15
|
var net = require('net');
|
|
16
16
|
|
|
17
17
|
// src/apply-patch.ts
|
|
18
|
-
var PathTraversalError = class extends sdk.ConfigurationError {
|
|
19
|
-
name = "PathTraversalError";
|
|
20
|
-
constructor(input, resolvedPath) {
|
|
21
|
-
super(`Path traversal attempt: ${input} \u2192 ${resolvedPath}`, {
|
|
22
|
-
code: "path_traversal"
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
var ForbiddenPathError = class extends sdk.ConfigurationError {
|
|
27
|
-
name = "ForbiddenPathError";
|
|
28
|
-
constructor(path) {
|
|
29
|
-
super(
|
|
30
|
-
`Path '${path}' is in the sensitive-file blocklist (.env, .git/, node_modules/, .theo/, lock files)`,
|
|
31
|
-
{
|
|
32
|
-
code: "forbidden_path"
|
|
33
|
-
}
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
};
|
|
37
|
-
function safePathJoin(base, ...parts) {
|
|
38
|
-
if (base === "") {
|
|
39
|
-
throw new Error("safePathJoin: base must be non-empty");
|
|
40
|
-
}
|
|
41
|
-
const baseResolved = path.resolve(base);
|
|
42
|
-
const target = path.resolve(base, ...parts);
|
|
43
|
-
if (target !== baseResolved && !target.startsWith(baseResolved + path.sep)) {
|
|
44
|
-
throw new PathTraversalError(parts.join("/"), target);
|
|
45
|
-
}
|
|
46
|
-
return target;
|
|
47
|
-
}
|
|
48
|
-
function assertNoSymlinkEscape(path$1, base) {
|
|
49
|
-
let baseResolved;
|
|
50
|
-
try {
|
|
51
|
-
baseResolved = fs.realpathSync(base);
|
|
52
|
-
} catch {
|
|
53
|
-
baseResolved = path.resolve(base);
|
|
54
|
-
}
|
|
55
|
-
const resolved = realpathOfDeepestExisting(path$1);
|
|
56
|
-
if (resolved === void 0) return;
|
|
57
|
-
if (resolved !== baseResolved && !resolved.startsWith(baseResolved + path.sep)) {
|
|
58
|
-
throw new PathTraversalError(`symlink ${path$1}`, resolved);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function realpathOfDeepestExisting(path$1) {
|
|
62
|
-
try {
|
|
63
|
-
return fs.realpathSync(path$1);
|
|
64
|
-
} catch {
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
const stat3 = fs.lstatSync(path$1);
|
|
68
|
-
if (stat3.isSymbolicLink()) {
|
|
69
|
-
const target = fs.readlinkSync(path$1);
|
|
70
|
-
const parentReal = realpathOfDeepestExisting(path.dirname(path$1));
|
|
71
|
-
const parentBase = parentReal ?? path.dirname(path$1);
|
|
72
|
-
return path.resolve(parentBase, target);
|
|
73
|
-
}
|
|
74
|
-
} catch {
|
|
75
|
-
}
|
|
76
|
-
let cursor = path.dirname(path$1);
|
|
77
|
-
let suffix = path$1.slice(cursor.length);
|
|
78
|
-
while (cursor !== path.dirname(cursor)) {
|
|
79
|
-
try {
|
|
80
|
-
const real = fs.realpathSync(cursor);
|
|
81
|
-
return path.resolve(real, `.${suffix}`);
|
|
82
|
-
} catch {
|
|
83
|
-
suffix = path$1.slice(path.dirname(cursor).length);
|
|
84
|
-
cursor = path.dirname(cursor);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return void 0;
|
|
88
|
-
}
|
|
89
|
-
var LOCK_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]);
|
|
90
|
-
function isForbiddenPath(input) {
|
|
91
|
-
const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
92
|
-
if (normalized.length === 0) return false;
|
|
93
|
-
const segments = normalized.split("/").filter((s) => s.length > 0);
|
|
94
|
-
if (segments.length === 0) return false;
|
|
95
|
-
const first = segments[0];
|
|
96
|
-
if (first === ".env.example") return false;
|
|
97
|
-
if (first === ".env") return true;
|
|
98
|
-
if (/^\.env\./.test(first)) return true;
|
|
99
|
-
if (first === ".git") return true;
|
|
100
|
-
if (first === "node_modules") return true;
|
|
101
|
-
if (first === ".theo") return true;
|
|
102
|
-
const basename = segments[segments.length - 1];
|
|
103
|
-
if (LOCK_FILES.has(basename)) return true;
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
18
|
|
|
107
19
|
// src/internal/v4a-patch.ts
|
|
108
20
|
var BEGIN = "*** Begin Patch";
|
|
@@ -446,15 +358,15 @@ function isForbiddenRel(rel) {
|
|
|
446
358
|
function v4aScope(projectRoot, file) {
|
|
447
359
|
let abs;
|
|
448
360
|
try {
|
|
449
|
-
abs = safePathJoin(projectRoot, file);
|
|
450
|
-
assertNoSymlinkEscape(abs, projectRoot);
|
|
361
|
+
abs = pathSafety.safePathJoin(projectRoot, file);
|
|
362
|
+
pathSafety.assertNoSymlinkEscape(abs, projectRoot);
|
|
451
363
|
} catch (err) {
|
|
452
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
364
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
453
365
|
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: file }) };
|
|
454
366
|
}
|
|
455
367
|
throw err;
|
|
456
368
|
}
|
|
457
|
-
if (isForbiddenPath(file) || isForbiddenRel(path.relative(projectRoot, abs))) {
|
|
369
|
+
if (pathSafety.isForbiddenPath(file) || isForbiddenRel(path.relative(projectRoot, abs))) {
|
|
458
370
|
return { error: JSON.stringify({ ok: false, error: "forbidden_path", path: file }) };
|
|
459
371
|
}
|
|
460
372
|
return { abs };
|
|
@@ -665,7 +577,7 @@ async function editViaBackend(filesystem$1, ctx, path, old_string, new_string) {
|
|
|
665
577
|
return JSON.stringify({ ok: true, replacements: 1 });
|
|
666
578
|
}
|
|
667
579
|
async function editViaLocal(projectRoot, path, old_string, new_string) {
|
|
668
|
-
const absolutePath = safePathJoin(projectRoot, path);
|
|
580
|
+
const absolutePath = pathSafety.safePathJoin(projectRoot, path);
|
|
669
581
|
let content;
|
|
670
582
|
try {
|
|
671
583
|
content = await promises.readFile(absolutePath, "utf-8");
|
|
@@ -683,14 +595,14 @@ async function editViaLocal(projectRoot, path, old_string, new_string) {
|
|
|
683
595
|
return JSON.stringify({ ok: true, replacements: 1 });
|
|
684
596
|
}
|
|
685
597
|
function editScopeError(path, projectRoot) {
|
|
686
|
-
if (isForbiddenPath(path)) {
|
|
598
|
+
if (pathSafety.isForbiddenPath(path)) {
|
|
687
599
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
688
600
|
}
|
|
689
601
|
try {
|
|
690
|
-
assertNoSymlinkEscape(safePathJoin(projectRoot, path), projectRoot);
|
|
602
|
+
pathSafety.assertNoSymlinkEscape(pathSafety.safePathJoin(projectRoot, path), projectRoot);
|
|
691
603
|
return null;
|
|
692
604
|
} catch (err) {
|
|
693
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
605
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
694
606
|
return JSON.stringify({ ok: false, error: "path_traversal", path });
|
|
695
607
|
}
|
|
696
608
|
throw err;
|
|
@@ -776,11 +688,11 @@ function formatError(message, code) {
|
|
|
776
688
|
function checkPathScope(path, projectRoot) {
|
|
777
689
|
if (path === void 0 || path === "") return null;
|
|
778
690
|
try {
|
|
779
|
-
const abs = safePathJoin(projectRoot, path);
|
|
780
|
-
assertNoSymlinkEscape(abs, projectRoot);
|
|
691
|
+
const abs = pathSafety.safePathJoin(projectRoot, path);
|
|
692
|
+
pathSafety.assertNoSymlinkEscape(abs, projectRoot);
|
|
781
693
|
return null;
|
|
782
694
|
} catch (err) {
|
|
783
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
695
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
784
696
|
return JSON.stringify({ ok: false, error: "path_traversal", path });
|
|
785
697
|
}
|
|
786
698
|
throw err;
|
|
@@ -800,25 +712,25 @@ function createSettleGate(timer) {
|
|
|
800
712
|
}
|
|
801
713
|
};
|
|
802
714
|
}
|
|
803
|
-
function armTimeoutKill(child, timeoutMs, onTimeout,
|
|
715
|
+
function armTimeoutKill(child, timeoutMs, onTimeout, resolve) {
|
|
804
716
|
const timer = setTimeout(() => {
|
|
805
717
|
gate.fire(() => {
|
|
806
718
|
try {
|
|
807
719
|
process.kill(-(child.pid ?? 0), "SIGKILL");
|
|
808
720
|
} catch {
|
|
809
721
|
}
|
|
810
|
-
|
|
722
|
+
resolve(onTimeout());
|
|
811
723
|
});
|
|
812
724
|
}, timeoutMs);
|
|
813
725
|
const gate = createSettleGate(timer);
|
|
814
726
|
return gate;
|
|
815
727
|
}
|
|
816
|
-
function attachChildSettlers(child, gate, onClose, onError,
|
|
728
|
+
function attachChildSettlers(child, gate, onClose, onError, resolve) {
|
|
817
729
|
child.on("close", (code) => {
|
|
818
|
-
gate.fire(() =>
|
|
730
|
+
gate.fire(() => resolve(onClose(code)));
|
|
819
731
|
});
|
|
820
732
|
child.on("error", (err) => {
|
|
821
|
-
gate.fire(() =>
|
|
733
|
+
gate.fire(() => resolve(onError(err)));
|
|
822
734
|
});
|
|
823
735
|
}
|
|
824
736
|
|
|
@@ -885,7 +797,7 @@ function formatGitResult(result, timeoutMs) {
|
|
|
885
797
|
return JSON.stringify({ ok: true, diff: result.stdout, truncated: result.truncated });
|
|
886
798
|
}
|
|
887
799
|
function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
|
|
888
|
-
return new Promise((
|
|
800
|
+
return new Promise((resolve) => {
|
|
889
801
|
const child = child_process.spawn("git", args, { cwd, detached: true, stdio: ["ignore", "pipe", "pipe"] });
|
|
890
802
|
const stdoutChunks = [];
|
|
891
803
|
const stderrChunks = [];
|
|
@@ -895,7 +807,7 @@ function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
|
|
|
895
807
|
child,
|
|
896
808
|
timeoutMs,
|
|
897
809
|
() => ({ kind: "timeout" }),
|
|
898
|
-
|
|
810
|
+
resolve
|
|
899
811
|
);
|
|
900
812
|
child.stdout.on("data", (chunk) => {
|
|
901
813
|
if (gate.settled()) return;
|
|
@@ -925,7 +837,7 @@ function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
|
|
|
925
837
|
return code === 0 ? { kind: "ok", stdout, truncated } : { kind: "error", stderr };
|
|
926
838
|
},
|
|
927
839
|
(err) => ({ kind: "error", stderr: err.message }),
|
|
928
|
-
|
|
840
|
+
resolve
|
|
929
841
|
);
|
|
930
842
|
});
|
|
931
843
|
}
|
|
@@ -951,7 +863,7 @@ function createGlobTool(opts) {
|
|
|
951
863
|
await walkDirBackend(backend, searchRel, searchRel, regex, found, 0);
|
|
952
864
|
return JSON.stringify({ ok: true, files: found.sort(), count: found.length });
|
|
953
865
|
}
|
|
954
|
-
const searchRoot = cwd ? safePathJoin(projectRoot, cwd) : projectRoot;
|
|
866
|
+
const searchRoot = cwd ? pathSafety.safePathJoin(projectRoot, cwd) : projectRoot;
|
|
955
867
|
const files = [];
|
|
956
868
|
await walkDir(searchRoot, searchRoot, regex, files);
|
|
957
869
|
const relativePaths = files.map((f) => path.relative(projectRoot, f)).sort();
|
|
@@ -962,10 +874,10 @@ function createGlobTool(opts) {
|
|
|
962
874
|
function globScopeError(cwd, projectRoot) {
|
|
963
875
|
if (!cwd) return null;
|
|
964
876
|
try {
|
|
965
|
-
assertNoSymlinkEscape(safePathJoin(projectRoot, cwd), projectRoot);
|
|
877
|
+
pathSafety.assertNoSymlinkEscape(pathSafety.safePathJoin(projectRoot, cwd), projectRoot);
|
|
966
878
|
return null;
|
|
967
879
|
} catch (err) {
|
|
968
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
880
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
969
881
|
return JSON.stringify({ ok: false, error: "path_traversal", path: cwd });
|
|
970
882
|
}
|
|
971
883
|
throw err;
|
|
@@ -1605,7 +1517,7 @@ function createListDirTool(opts) {
|
|
|
1605
1517
|
}),
|
|
1606
1518
|
handler: async ({ path }, ctx) => {
|
|
1607
1519
|
const relative3 = path === "" || path === "." ? "." : path;
|
|
1608
|
-
if (relative3 !== "." && isForbiddenPath(relative3)) {
|
|
1520
|
+
if (relative3 !== "." && pathSafety.isForbiddenPath(relative3)) {
|
|
1609
1521
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
1610
1522
|
}
|
|
1611
1523
|
if (filesystem$1) {
|
|
@@ -1653,11 +1565,11 @@ async function listViaBackend(backend, relative3, originalPath, max) {
|
|
|
1653
1565
|
}
|
|
1654
1566
|
function resolveDirBoundary(relative3, projectRoot, originalPath) {
|
|
1655
1567
|
try {
|
|
1656
|
-
const absolutePath = relative3 === "." ? projectRoot : safePathJoin(projectRoot, relative3);
|
|
1657
|
-
assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
1568
|
+
const absolutePath = relative3 === "." ? projectRoot : pathSafety.safePathJoin(projectRoot, relative3);
|
|
1569
|
+
pathSafety.assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
1658
1570
|
return { absolutePath };
|
|
1659
1571
|
} catch (err) {
|
|
1660
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
1572
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
1661
1573
|
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: originalPath }) };
|
|
1662
1574
|
}
|
|
1663
1575
|
throw err;
|
|
@@ -1819,7 +1731,7 @@ function isForbiddenAtAnyDepth(path) {
|
|
|
1819
1731
|
});
|
|
1820
1732
|
}
|
|
1821
1733
|
function forbiddenReadError(path$1, allowAbsolute) {
|
|
1822
|
-
if (isForbiddenPath(path$1)) {
|
|
1734
|
+
if (pathSafety.isForbiddenPath(path$1)) {
|
|
1823
1735
|
return JSON.stringify({ ok: false, error: "forbidden_path", path: path$1 });
|
|
1824
1736
|
}
|
|
1825
1737
|
if (allowAbsolute && path.isAbsolute(path$1) && isForbiddenAtAnyDepth(path$1)) {
|
|
@@ -1906,11 +1818,11 @@ function resolveBoundary(path$1, projectRoot, allowAbsolute) {
|
|
|
1906
1818
|
return { absolutePath: path$1 };
|
|
1907
1819
|
}
|
|
1908
1820
|
try {
|
|
1909
|
-
const absolutePath = safePathJoin(projectRoot, path$1);
|
|
1910
|
-
assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
1821
|
+
const absolutePath = pathSafety.safePathJoin(projectRoot, path$1);
|
|
1822
|
+
pathSafety.assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
1911
1823
|
return { absolutePath };
|
|
1912
1824
|
} catch (err) {
|
|
1913
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
1825
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
1914
1826
|
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: path$1 }) };
|
|
1915
1827
|
}
|
|
1916
1828
|
throw err;
|
|
@@ -2032,7 +1944,7 @@ function createRunVitestTool(opts) {
|
|
|
2032
1944
|
});
|
|
2033
1945
|
}
|
|
2034
1946
|
function validateVitestScope(path, projectRoot) {
|
|
2035
|
-
if (path !== void 0 && path !== "" && isForbiddenPath(path)) {
|
|
1947
|
+
if (path !== void 0 && path !== "" && pathSafety.isForbiddenPath(path)) {
|
|
2036
1948
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
2037
1949
|
}
|
|
2038
1950
|
return checkPathScope(path, projectRoot);
|
|
@@ -2078,7 +1990,7 @@ function appendCapped(chunks, chunk, current, cap) {
|
|
|
2078
1990
|
return current + chunk.length;
|
|
2079
1991
|
}
|
|
2080
1992
|
function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
|
|
2081
|
-
return new Promise((
|
|
1993
|
+
return new Promise((resolve) => {
|
|
2082
1994
|
const child = child_process.spawn(command, args, {
|
|
2083
1995
|
cwd,
|
|
2084
1996
|
detached: true,
|
|
@@ -2091,7 +2003,7 @@ function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
|
|
|
2091
2003
|
child,
|
|
2092
2004
|
timeoutMs,
|
|
2093
2005
|
() => ({ kind: "timeout" }),
|
|
2094
|
-
|
|
2006
|
+
resolve
|
|
2095
2007
|
);
|
|
2096
2008
|
child.stdout.on("data", (chunk) => {
|
|
2097
2009
|
if (gate.settled()) return;
|
|
@@ -2110,7 +2022,7 @@ function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
|
|
|
2110
2022
|
exitCode: code ?? 0
|
|
2111
2023
|
}),
|
|
2112
2024
|
(err) => ({ kind: "spawn_error", message: err.message }),
|
|
2113
|
-
|
|
2025
|
+
resolve
|
|
2114
2026
|
);
|
|
2115
2027
|
});
|
|
2116
2028
|
}
|
|
@@ -2191,11 +2103,11 @@ function resolveSearchScope(path$1, projectRoot, allowAbsolute) {
|
|
|
2191
2103
|
return { scopeAbs: scopeRel };
|
|
2192
2104
|
}
|
|
2193
2105
|
try {
|
|
2194
|
-
const scopeAbs = scopeRel === "." ? projectRoot : safePathJoin(projectRoot, scopeRel);
|
|
2195
|
-
assertNoSymlinkEscape(scopeAbs, projectRoot);
|
|
2106
|
+
const scopeAbs = scopeRel === "." ? projectRoot : pathSafety.safePathJoin(projectRoot, scopeRel);
|
|
2107
|
+
pathSafety.assertNoSymlinkEscape(scopeAbs, projectRoot);
|
|
2196
2108
|
return { scopeAbs };
|
|
2197
2109
|
} catch (err) {
|
|
2198
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
2110
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
2199
2111
|
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: path$1 }) };
|
|
2200
2112
|
}
|
|
2201
2113
|
throw err;
|
|
@@ -2204,7 +2116,7 @@ function resolveSearchScope(path$1, projectRoot, allowAbsolute) {
|
|
|
2204
2116
|
async function handleEntry(entry, absDir, state) {
|
|
2205
2117
|
const entryAbs = path.join(absDir, entry.name);
|
|
2206
2118
|
const entryRel = path.relative(state.projectRoot, entryAbs);
|
|
2207
|
-
if (isForbiddenPath(entryRel)) return;
|
|
2119
|
+
if (pathSafety.isForbiddenPath(entryRel)) return;
|
|
2208
2120
|
if (entry.isDirectory()) {
|
|
2209
2121
|
await walk(entryAbs, state);
|
|
2210
2122
|
return;
|
|
@@ -2270,10 +2182,10 @@ function resolveScopeRel(path$1, projectRoot, allowAbsolute) {
|
|
|
2270
2182
|
if (scopeRel === "") return { rel: "" };
|
|
2271
2183
|
if (allowAbsolute && path.isAbsolute(scopeRel)) return { rel: scopeRel };
|
|
2272
2184
|
try {
|
|
2273
|
-
assertNoSymlinkEscape(safePathJoin(projectRoot, scopeRel), projectRoot);
|
|
2185
|
+
pathSafety.assertNoSymlinkEscape(pathSafety.safePathJoin(projectRoot, scopeRel), projectRoot);
|
|
2274
2186
|
return { rel: scopeRel };
|
|
2275
2187
|
} catch (err) {
|
|
2276
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
2188
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
2277
2189
|
return { error: JSON.stringify({ ok: false, error: "path_traversal", path: path$1 }) };
|
|
2278
2190
|
}
|
|
2279
2191
|
throw err;
|
|
@@ -2295,7 +2207,7 @@ async function walkBackend(backend, dirRel, state, depth) {
|
|
|
2295
2207
|
}
|
|
2296
2208
|
async function handleBackendEntry(backend, dirRel, name, state, depth) {
|
|
2297
2209
|
const entryRel = dirRel === "" ? name : `${dirRel}/${name}`;
|
|
2298
|
-
if (isForbiddenPath(entryRel)) return;
|
|
2210
|
+
if (pathSafety.isForbiddenPath(entryRel)) return;
|
|
2299
2211
|
let st;
|
|
2300
2212
|
try {
|
|
2301
2213
|
st = await backend.stat(entryRel);
|
|
@@ -2361,7 +2273,7 @@ function createShellTool(opts) {
|
|
|
2361
2273
|
});
|
|
2362
2274
|
}
|
|
2363
2275
|
function runShell(cwd, command, timeoutMs) {
|
|
2364
|
-
return new Promise((
|
|
2276
|
+
return new Promise((resolve) => {
|
|
2365
2277
|
const child = child_process.spawn("/bin/sh", ["-c", command], {
|
|
2366
2278
|
cwd,
|
|
2367
2279
|
detached: true,
|
|
@@ -2375,7 +2287,7 @@ function runShell(cwd, command, timeoutMs) {
|
|
|
2375
2287
|
child,
|
|
2376
2288
|
timeoutMs,
|
|
2377
2289
|
() => ({ kind: "timeout" }),
|
|
2378
|
-
(result) =>
|
|
2290
|
+
(result) => resolve(formatResult(result, timeoutMs))
|
|
2379
2291
|
);
|
|
2380
2292
|
child.stdout.on("data", (chunk) => {
|
|
2381
2293
|
if (gate.settled()) return;
|
|
@@ -2413,7 +2325,7 @@ function runShell(cwd, command, timeoutMs) {
|
|
|
2413
2325
|
exitCode: code
|
|
2414
2326
|
}),
|
|
2415
2327
|
(err) => ({ kind: "error", message: err.message }),
|
|
2416
|
-
(result) =>
|
|
2328
|
+
(result) => resolve(formatResult(result, timeoutMs))
|
|
2417
2329
|
);
|
|
2418
2330
|
});
|
|
2419
2331
|
}
|
|
@@ -2795,7 +2707,7 @@ function createWriteFileTool(opts) {
|
|
|
2795
2707
|
content: zod.z.string().describe("UTF-8 content to write.")
|
|
2796
2708
|
}),
|
|
2797
2709
|
handler: async ({ path, content }, ctx) => {
|
|
2798
|
-
if (isForbiddenPath(path)) {
|
|
2710
|
+
if (pathSafety.isForbiddenPath(path)) {
|
|
2799
2711
|
return JSON.stringify({ ok: false, error: "forbidden_path", path });
|
|
2800
2712
|
}
|
|
2801
2713
|
if (filesystem$1) {
|
|
@@ -2817,10 +2729,10 @@ function readBeforeWriteError(guard, path, currentMtimeMs) {
|
|
|
2817
2729
|
async function writeViaLocalFs(projectRoot, path$1, content, guard) {
|
|
2818
2730
|
let absolutePath;
|
|
2819
2731
|
try {
|
|
2820
|
-
absolutePath = safePathJoin(projectRoot, path$1);
|
|
2821
|
-
assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
2732
|
+
absolutePath = pathSafety.safePathJoin(projectRoot, path$1);
|
|
2733
|
+
pathSafety.assertNoSymlinkEscape(absolutePath, projectRoot);
|
|
2822
2734
|
} catch (err) {
|
|
2823
|
-
if (err instanceof PathTraversalError || err instanceof ForbiddenPathError) {
|
|
2735
|
+
if (err instanceof pathSafety.PathTraversalError || err instanceof pathSafety.ForbiddenPathError) {
|
|
2824
2736
|
return JSON.stringify({ ok: false, error: "path_traversal", path: path$1 });
|
|
2825
2737
|
}
|
|
2826
2738
|
throw err;
|