@theokit/sdk-tools 0.27.0 → 0.27.2
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 +233 -0
- package/LICENSE +2 -2
- package/README.md +15 -2
- package/dist/index.cjs +56 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +397 -8
- package/dist/index.d.ts +397 -8
- package/dist/index.js +57 -25
- package/dist/index.js.map +1 -1
- package/package.json +10 -9
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { rm, mkdir, writeFile, readFile, readdir, open, stat
|
|
1
|
+
import { rm, mkdir, writeFile, readFile, readdir, open, stat } from 'fs/promises';
|
|
2
2
|
import { dirname, relative, join, isAbsolute, extname } from 'path';
|
|
3
3
|
import { Tool, ConfigurationError } from '@theokit/sdk';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import { safePathJoin, assertNoSymlinkEscape, PathTraversalError, ForbiddenPathError, isForbiddenPath, safeFilenameForId } from '@theokit/sdk/path-safety';
|
|
6
6
|
import { replaceFileAtomic } from '@theokit/sdk/persistence';
|
|
7
|
+
import { existsSync, statSync, mkdirSync, writeFileSync, openSync, fstatSync, readFileSync, closeSync, constants, readdirSync } from 'fs';
|
|
7
8
|
import { resolveFilesystem, FileNotFoundError, FilesystemSecurityError, FilesystemReadOnlyError, StaleFileError, FilesystemError } from '@theokit/sdk/filesystem';
|
|
8
|
-
import { existsSync, statSync, mkdirSync, writeFileSync, readFileSync, readdirSync } from 'fs';
|
|
9
9
|
import { resolveSandbox } from '@theokit/sdk/sandbox';
|
|
10
10
|
import { spawn } from 'child_process';
|
|
11
11
|
import { resolveInteractive, InteractiveUnavailableError, NoSuchSessionError } from '@theokit/sdk/interactive';
|
|
@@ -589,7 +589,18 @@ async function editViaLocal(projectRoot, path, old_string, new_string) {
|
|
|
589
589
|
}
|
|
590
590
|
const outcome = computeEdit(content, old_string, new_string);
|
|
591
591
|
if (!outcome.ok) return JSON.stringify({ ok: false, error: "no_match", path });
|
|
592
|
-
|
|
592
|
+
try {
|
|
593
|
+
await writeFile(`${absolutePath}.bak`, content, {
|
|
594
|
+
encoding: "utf-8",
|
|
595
|
+
flag: constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | constants.O_NOFOLLOW
|
|
596
|
+
});
|
|
597
|
+
} catch (err) {
|
|
598
|
+
const code = err.code;
|
|
599
|
+
if (code === "ELOOP" || code === "EEXIST") {
|
|
600
|
+
return JSON.stringify({ ok: false, error: "unsafe_backup_path", path });
|
|
601
|
+
}
|
|
602
|
+
throw err;
|
|
603
|
+
}
|
|
593
604
|
await writeFile(absolutePath, outcome.result, "utf-8");
|
|
594
605
|
return JSON.stringify({ ok: true, replacements: 1 });
|
|
595
606
|
}
|
|
@@ -859,15 +870,15 @@ function createGitStatusTool(opts) {
|
|
|
859
870
|
path: z.string().optional().describe("Optional project-relative path to scope the status report.")
|
|
860
871
|
}),
|
|
861
872
|
handler: async ({ path }, ctx) => {
|
|
862
|
-
if (!existsSync(join(projectRoot, ".git"))) {
|
|
863
|
-
return JSON.stringify({ ok: false, error: "not_a_repo" });
|
|
864
|
-
}
|
|
865
873
|
const scopeCheck = checkPathScope(path, projectRoot);
|
|
866
874
|
if (scopeCheck !== null) return scopeCheck;
|
|
867
875
|
const args = buildArgs(path, opts.includeBranch !== false);
|
|
868
876
|
if (opts.sandbox !== void 0) {
|
|
869
877
|
return statusViaSandbox(opts.sandbox, ctx, args, timeoutMs);
|
|
870
878
|
}
|
|
879
|
+
if (!existsSync(join(projectRoot, ".git"))) {
|
|
880
|
+
return JSON.stringify({ ok: false, error: "not_a_repo" });
|
|
881
|
+
}
|
|
871
882
|
const result = await runGitProcess(projectRoot, args, timeoutMs, maxStdoutBytes);
|
|
872
883
|
return formatGitResult(result, timeoutMs);
|
|
873
884
|
}
|
|
@@ -2037,6 +2048,12 @@ function validateVitestScope(path, projectRoot) {
|
|
|
2037
2048
|
}
|
|
2038
2049
|
return checkPathScope(path, projectRoot);
|
|
2039
2050
|
}
|
|
2051
|
+
var VITEST_MISSING = [
|
|
2052
|
+
/npx canceled due to missing packages/i,
|
|
2053
|
+
/could not determine executable to run/i,
|
|
2054
|
+
/vitest: (?:command )?not found/i,
|
|
2055
|
+
/command not found: vitest/i
|
|
2056
|
+
];
|
|
2040
2057
|
function formatVitestResult(result, timeoutMs) {
|
|
2041
2058
|
if (result.kind === "timeout") {
|
|
2042
2059
|
return JSON.stringify({ ok: false, error: "timeout", timeoutMs });
|
|
@@ -2046,6 +2063,9 @@ function formatVitestResult(result, timeoutMs) {
|
|
|
2046
2063
|
}
|
|
2047
2064
|
const summary = extractTrailingJson(result.stdout);
|
|
2048
2065
|
if (summary === null) {
|
|
2066
|
+
if (VITEST_MISSING.some((pattern) => pattern.test(result.stderr))) {
|
|
2067
|
+
return JSON.stringify({ ok: false, error: "no_vitest", detail: result.stderr.slice(0, 500) });
|
|
2068
|
+
}
|
|
2049
2069
|
return JSON.stringify({
|
|
2050
2070
|
ok: false,
|
|
2051
2071
|
error: "unparseable_output",
|
|
@@ -2575,9 +2595,9 @@ function truncateOutput(output, opts) {
|
|
|
2575
2595
|
return { content: output, truncated: false, originalBytes };
|
|
2576
2596
|
}
|
|
2577
2597
|
mkdirSync(outputDir, { recursive: true });
|
|
2578
|
-
const filename = `overflow-${Date.now()}-${randomUUID()
|
|
2598
|
+
const filename = `overflow-${Date.now()}-${randomUUID()}.txt`;
|
|
2579
2599
|
const overflowPath = join(outputDir, filename);
|
|
2580
|
-
writeFileSync(overflowPath, output, "utf-8");
|
|
2600
|
+
writeFileSync(overflowPath, output, { encoding: "utf-8", flag: "wx" });
|
|
2581
2601
|
const buf = Buffer.from(output, "utf-8");
|
|
2582
2602
|
const trailer = `
|
|
2583
2603
|
|
|
@@ -2659,27 +2679,14 @@ function createViewImageTool(options) {
|
|
|
2659
2679
|
});
|
|
2660
2680
|
}
|
|
2661
2681
|
const absolute = safePathJoin(projectRoot, input.path);
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
bytes = statSync(absolute).size;
|
|
2665
|
-
} catch {
|
|
2666
|
-
return json({ ok: false, error: "not_found", path: input.path });
|
|
2667
|
-
}
|
|
2668
|
-
if (bytes > maxBytes) {
|
|
2669
|
-
return json({
|
|
2670
|
-
ok: false,
|
|
2671
|
-
error: "image_too_large",
|
|
2672
|
-
path: input.path,
|
|
2673
|
-
bytes,
|
|
2674
|
-
limit_bytes: maxBytes
|
|
2675
|
-
});
|
|
2676
|
-
}
|
|
2682
|
+
const outcome = readWithinBudget(absolute, maxBytes);
|
|
2683
|
+
if (!outcome.ok) return json(failureFor(outcome, input.path, maxBytes));
|
|
2677
2684
|
return json({
|
|
2678
2685
|
ok: true,
|
|
2679
2686
|
path: input.path,
|
|
2680
2687
|
media_type: mediaType,
|
|
2681
|
-
bytes,
|
|
2682
|
-
data:
|
|
2688
|
+
bytes: outcome.bytes,
|
|
2689
|
+
data: outcome.data
|
|
2683
2690
|
});
|
|
2684
2691
|
},
|
|
2685
2692
|
/**
|
|
@@ -2705,6 +2712,31 @@ function createViewImageTool(options) {
|
|
|
2705
2712
|
}
|
|
2706
2713
|
});
|
|
2707
2714
|
}
|
|
2715
|
+
function readWithinBudget(absolute, maxBytes) {
|
|
2716
|
+
let fd;
|
|
2717
|
+
try {
|
|
2718
|
+
fd = openSync(absolute, "r");
|
|
2719
|
+
} catch {
|
|
2720
|
+
return { ok: false, error: "not_found" };
|
|
2721
|
+
}
|
|
2722
|
+
try {
|
|
2723
|
+
const bytes = fstatSync(fd).size;
|
|
2724
|
+
if (bytes > maxBytes) return { ok: false, error: "too_large", bytes };
|
|
2725
|
+
return { ok: true, bytes, data: readFileSync(fd).toString("base64") };
|
|
2726
|
+
} finally {
|
|
2727
|
+
closeSync(fd);
|
|
2728
|
+
}
|
|
2729
|
+
}
|
|
2730
|
+
function failureFor(outcome, path, maxBytes) {
|
|
2731
|
+
if (outcome.error === "not_found") return { ok: false, error: "not_found", path };
|
|
2732
|
+
return {
|
|
2733
|
+
ok: false,
|
|
2734
|
+
error: "image_too_large",
|
|
2735
|
+
path,
|
|
2736
|
+
bytes: outcome.bytes,
|
|
2737
|
+
limit_bytes: maxBytes
|
|
2738
|
+
};
|
|
2739
|
+
}
|
|
2708
2740
|
var DEFAULT_TIMEOUT_MS4 = 3e4;
|
|
2709
2741
|
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
2710
2742
|
function createWebFetchTool(opts) {
|