@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.js CHANGED
@@ -1,106 +1,18 @@
1
1
  import { rm, mkdir, writeFile, readFile, readdir, open, stat, copyFile } from 'fs/promises';
2
- import { dirname, relative, join, isAbsolute, resolve, sep } from 'path';
2
+ import { dirname, relative, join, isAbsolute } from 'path';
3
3
  import { Tool, ConfigurationError } from '@theokit/sdk';
4
4
  import { z } from 'zod';
5
- import { existsSync, statSync, mkdirSync, writeFileSync, realpathSync, readFileSync, lstatSync, readlinkSync, readdirSync } from 'fs';
6
- import { safeFilenameForId, safePathJoin as safePathJoin$1 } from '@theokit/sdk/path-safety';
5
+ import { safePathJoin, assertNoSymlinkEscape, PathTraversalError, ForbiddenPathError, isForbiddenPath, safeFilenameForId } from '@theokit/sdk/path-safety';
7
6
  import { replaceFileAtomic } from '@theokit/sdk/persistence';
8
7
  import { resolveFilesystem, FileNotFoundError, FilesystemSecurityError, FilesystemReadOnlyError, StaleFileError, FilesystemError } from '@theokit/sdk/filesystem';
9
8
  import { spawn } from 'child_process';
9
+ import { existsSync, statSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
10
10
  import { resolveSandbox } from '@theokit/sdk/sandbox';
11
11
  import { resolveInteractive, InteractiveUnavailableError, NoSuchSessionError } from '@theokit/sdk/interactive';
12
12
  import { lookup } from 'dns/promises';
13
13
  import { isIP } from 'net';
14
14
 
15
15
  // src/apply-patch.ts
16
- var PathTraversalError = class extends ConfigurationError {
17
- name = "PathTraversalError";
18
- constructor(input, resolvedPath) {
19
- super(`Path traversal attempt: ${input} \u2192 ${resolvedPath}`, {
20
- code: "path_traversal"
21
- });
22
- }
23
- };
24
- var ForbiddenPathError = class extends ConfigurationError {
25
- name = "ForbiddenPathError";
26
- constructor(path) {
27
- super(
28
- `Path '${path}' is in the sensitive-file blocklist (.env, .git/, node_modules/, .theo/, lock files)`,
29
- {
30
- code: "forbidden_path"
31
- }
32
- );
33
- }
34
- };
35
- function safePathJoin(base, ...parts) {
36
- if (base === "") {
37
- throw new Error("safePathJoin: base must be non-empty");
38
- }
39
- const baseResolved = resolve(base);
40
- const target = resolve(base, ...parts);
41
- if (target !== baseResolved && !target.startsWith(baseResolved + sep)) {
42
- throw new PathTraversalError(parts.join("/"), target);
43
- }
44
- return target;
45
- }
46
- function assertNoSymlinkEscape(path, base) {
47
- let baseResolved;
48
- try {
49
- baseResolved = realpathSync(base);
50
- } catch {
51
- baseResolved = resolve(base);
52
- }
53
- const resolved = realpathOfDeepestExisting(path);
54
- if (resolved === void 0) return;
55
- if (resolved !== baseResolved && !resolved.startsWith(baseResolved + sep)) {
56
- throw new PathTraversalError(`symlink ${path}`, resolved);
57
- }
58
- }
59
- function realpathOfDeepestExisting(path) {
60
- try {
61
- return realpathSync(path);
62
- } catch {
63
- }
64
- try {
65
- const stat3 = lstatSync(path);
66
- if (stat3.isSymbolicLink()) {
67
- const target = readlinkSync(path);
68
- const parentReal = realpathOfDeepestExisting(dirname(path));
69
- const parentBase = parentReal ?? dirname(path);
70
- return resolve(parentBase, target);
71
- }
72
- } catch {
73
- }
74
- let cursor = dirname(path);
75
- let suffix = path.slice(cursor.length);
76
- while (cursor !== dirname(cursor)) {
77
- try {
78
- const real = realpathSync(cursor);
79
- return resolve(real, `.${suffix}`);
80
- } catch {
81
- suffix = path.slice(dirname(cursor).length);
82
- cursor = dirname(cursor);
83
- }
84
- }
85
- return void 0;
86
- }
87
- var LOCK_FILES = /* @__PURE__ */ new Set(["pnpm-lock.yaml", "package-lock.json", "yarn.lock", "bun.lockb"]);
88
- function isForbiddenPath(input) {
89
- const normalized = input.replace(/\\/g, "/").replace(/^\.\//, "");
90
- if (normalized.length === 0) return false;
91
- const segments = normalized.split("/").filter((s) => s.length > 0);
92
- if (segments.length === 0) return false;
93
- const first = segments[0];
94
- if (first === ".env.example") return false;
95
- if (first === ".env") return true;
96
- if (/^\.env\./.test(first)) return true;
97
- if (first === ".git") return true;
98
- if (first === "node_modules") return true;
99
- if (first === ".theo") return true;
100
- const basename = segments[segments.length - 1];
101
- if (LOCK_FILES.has(basename)) return true;
102
- return false;
103
- }
104
16
 
105
17
  // src/internal/v4a-patch.ts
106
18
  var BEGIN = "*** Begin Patch";
@@ -462,7 +374,7 @@ function createSessionArtifactStore(options) {
462
374
  const idStrategy = options.idStrategy ?? ((id) => safeFilenameForId(id));
463
375
  const extension = options.extension ?? ".md";
464
376
  function path(id) {
465
- return safePathJoin$1(dir, `${idStrategy(id)}${extension}`);
377
+ return safePathJoin(dir, `${idStrategy(id)}${extension}`);
466
378
  }
467
379
  async function write(id, content) {
468
380
  const target = path(id);
@@ -798,25 +710,25 @@ function createSettleGate(timer) {
798
710
  }
799
711
  };
800
712
  }
801
- function armTimeoutKill(child, timeoutMs, onTimeout, resolve2) {
713
+ function armTimeoutKill(child, timeoutMs, onTimeout, resolve) {
802
714
  const timer = setTimeout(() => {
803
715
  gate.fire(() => {
804
716
  try {
805
717
  process.kill(-(child.pid ?? 0), "SIGKILL");
806
718
  } catch {
807
719
  }
808
- resolve2(onTimeout());
720
+ resolve(onTimeout());
809
721
  });
810
722
  }, timeoutMs);
811
723
  const gate = createSettleGate(timer);
812
724
  return gate;
813
725
  }
814
- function attachChildSettlers(child, gate, onClose, onError, resolve2) {
726
+ function attachChildSettlers(child, gate, onClose, onError, resolve) {
815
727
  child.on("close", (code) => {
816
- gate.fire(() => resolve2(onClose(code)));
728
+ gate.fire(() => resolve(onClose(code)));
817
729
  });
818
730
  child.on("error", (err) => {
819
- gate.fire(() => resolve2(onError(err)));
731
+ gate.fire(() => resolve(onError(err)));
820
732
  });
821
733
  }
822
734
 
@@ -883,7 +795,7 @@ function formatGitResult(result, timeoutMs) {
883
795
  return JSON.stringify({ ok: true, diff: result.stdout, truncated: result.truncated });
884
796
  }
885
797
  function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
886
- return new Promise((resolve2) => {
798
+ return new Promise((resolve) => {
887
799
  const child = spawn("git", args, { cwd, detached: true, stdio: ["ignore", "pipe", "pipe"] });
888
800
  const stdoutChunks = [];
889
801
  const stderrChunks = [];
@@ -893,7 +805,7 @@ function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
893
805
  child,
894
806
  timeoutMs,
895
807
  () => ({ kind: "timeout" }),
896
- resolve2
808
+ resolve
897
809
  );
898
810
  child.stdout.on("data", (chunk) => {
899
811
  if (gate.settled()) return;
@@ -923,7 +835,7 @@ function runGitProcess(cwd, args, timeoutMs, maxStdoutBytes) {
923
835
  return code === 0 ? { kind: "ok", stdout, truncated } : { kind: "error", stderr };
924
836
  },
925
837
  (err) => ({ kind: "error", stderr: err.message }),
926
- resolve2
838
+ resolve
927
839
  );
928
840
  });
929
841
  }
@@ -2076,7 +1988,7 @@ function appendCapped(chunks, chunk, current, cap) {
2076
1988
  return current + chunk.length;
2077
1989
  }
2078
1990
  function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
2079
- return new Promise((resolve2) => {
1991
+ return new Promise((resolve) => {
2080
1992
  const child = spawn(command, args, {
2081
1993
  cwd,
2082
1994
  detached: true,
@@ -2089,7 +2001,7 @@ function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
2089
2001
  child,
2090
2002
  timeoutMs,
2091
2003
  () => ({ kind: "timeout" }),
2092
- resolve2
2004
+ resolve
2093
2005
  );
2094
2006
  child.stdout.on("data", (chunk) => {
2095
2007
  if (gate.settled()) return;
@@ -2108,7 +2020,7 @@ function runProcess(cwd, command, args, timeoutMs, maxStdoutBytes) {
2108
2020
  exitCode: code ?? 0
2109
2021
  }),
2110
2022
  (err) => ({ kind: "spawn_error", message: err.message }),
2111
- resolve2
2023
+ resolve
2112
2024
  );
2113
2025
  });
2114
2026
  }
@@ -2359,7 +2271,7 @@ function createShellTool(opts) {
2359
2271
  });
2360
2272
  }
2361
2273
  function runShell(cwd, command, timeoutMs) {
2362
- return new Promise((resolve2) => {
2274
+ return new Promise((resolve) => {
2363
2275
  const child = spawn("/bin/sh", ["-c", command], {
2364
2276
  cwd,
2365
2277
  detached: true,
@@ -2373,7 +2285,7 @@ function runShell(cwd, command, timeoutMs) {
2373
2285
  child,
2374
2286
  timeoutMs,
2375
2287
  () => ({ kind: "timeout" }),
2376
- (result) => resolve2(formatResult(result, timeoutMs))
2288
+ (result) => resolve(formatResult(result, timeoutMs))
2377
2289
  );
2378
2290
  child.stdout.on("data", (chunk) => {
2379
2291
  if (gate.settled()) return;
@@ -2411,7 +2323,7 @@ function runShell(cwd, command, timeoutMs) {
2411
2323
  exitCode: code
2412
2324
  }),
2413
2325
  (err) => ({ kind: "error", message: err.message }),
2414
- (result) => resolve2(formatResult(result, timeoutMs))
2326
+ (result) => resolve(formatResult(result, timeoutMs))
2415
2327
  );
2416
2328
  });
2417
2329
  }