@theokit/sdk-tools 0.26.3 → 0.27.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 +233 -0
- package/LICENSE +2 -2
- package/README.md +15 -2
- package/dist/index.cjs +123 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +452 -9
- package/dist/index.d.ts +452 -9
- package/dist/index.js +124 -12
- package/dist/index.js.map +1 -1
- package/package.json +9 -8
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { rm, mkdir, writeFile, readFile, readdir, open, stat
|
|
2
|
-
import { dirname, relative, join, isAbsolute } from 'path';
|
|
1
|
+
import { rm, mkdir, writeFile, readFile, readdir, open, stat } from 'fs/promises';
|
|
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
|
|
|
@@ -2625,6 +2645,98 @@ function createUpdatePlanTool() {
|
|
|
2625
2645
|
}
|
|
2626
2646
|
});
|
|
2627
2647
|
}
|
|
2648
|
+
var MEDIA_TYPES = /* @__PURE__ */ new Map([
|
|
2649
|
+
[".png", "image/png"],
|
|
2650
|
+
[".jpg", "image/jpeg"],
|
|
2651
|
+
[".jpeg", "image/jpeg"],
|
|
2652
|
+
[".gif", "image/gif"],
|
|
2653
|
+
[".webp", "image/webp"]
|
|
2654
|
+
]);
|
|
2655
|
+
var DEFAULT_MAX_IMAGE_BYTES = 5 * 1024 * 1024;
|
|
2656
|
+
var json = (result) => JSON.stringify(result);
|
|
2657
|
+
function createViewImageTool(options) {
|
|
2658
|
+
const { projectRoot } = options;
|
|
2659
|
+
const maxBytes = options.maxBytes ?? DEFAULT_MAX_IMAGE_BYTES;
|
|
2660
|
+
return Tool.create({
|
|
2661
|
+
name: options.name ?? "view_image",
|
|
2662
|
+
description: options.description ?? "Read an image file from the project and show it to the model. Supports png, jpeg, gif and webp.",
|
|
2663
|
+
inputSchema: z.object({
|
|
2664
|
+
path: z.string().describe("Path to the image, relative to the project root.")
|
|
2665
|
+
}),
|
|
2666
|
+
handler: (input) => {
|
|
2667
|
+
const refused = checkPathScope(input.path, projectRoot);
|
|
2668
|
+
if (refused !== null) return refused;
|
|
2669
|
+
if (isForbiddenAtAnyDepth(input.path)) {
|
|
2670
|
+
return json({ ok: false, error: "path_traversal", path: input.path });
|
|
2671
|
+
}
|
|
2672
|
+
const mediaType = MEDIA_TYPES.get(extname(input.path).toLowerCase());
|
|
2673
|
+
if (mediaType === void 0) {
|
|
2674
|
+
return json({
|
|
2675
|
+
ok: false,
|
|
2676
|
+
error: "unsupported_image_type",
|
|
2677
|
+
path: input.path,
|
|
2678
|
+
supported: [...MEDIA_TYPES.keys()]
|
|
2679
|
+
});
|
|
2680
|
+
}
|
|
2681
|
+
const absolute = safePathJoin(projectRoot, input.path);
|
|
2682
|
+
const outcome = readWithinBudget(absolute, maxBytes);
|
|
2683
|
+
if (!outcome.ok) return json(failureFor(outcome, input.path, maxBytes));
|
|
2684
|
+
return json({
|
|
2685
|
+
ok: true,
|
|
2686
|
+
path: input.path,
|
|
2687
|
+
media_type: mediaType,
|
|
2688
|
+
bytes: outcome.bytes,
|
|
2689
|
+
data: outcome.data
|
|
2690
|
+
});
|
|
2691
|
+
},
|
|
2692
|
+
/**
|
|
2693
|
+
* Turn a successful read into an image block.
|
|
2694
|
+
*
|
|
2695
|
+
* A failed read stays TEXT on purpose: the model needs to read "not_found" and try another path,
|
|
2696
|
+
* and an error is not something to look at.
|
|
2697
|
+
*/
|
|
2698
|
+
toModelOutput: (output) => {
|
|
2699
|
+
let result;
|
|
2700
|
+
try {
|
|
2701
|
+
result = JSON.parse(output);
|
|
2702
|
+
} catch {
|
|
2703
|
+
return output;
|
|
2704
|
+
}
|
|
2705
|
+
if (result.ok !== true) return output;
|
|
2706
|
+
return [
|
|
2707
|
+
{
|
|
2708
|
+
type: "image",
|
|
2709
|
+
source: { type: "base64", media_type: result.media_type, data: result.data }
|
|
2710
|
+
}
|
|
2711
|
+
];
|
|
2712
|
+
}
|
|
2713
|
+
});
|
|
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
|
+
}
|
|
2628
2740
|
var DEFAULT_TIMEOUT_MS4 = 3e4;
|
|
2629
2741
|
var MAX_BODY_BYTES = 1 * 1024 * 1024;
|
|
2630
2742
|
function createWebFetchTool(opts) {
|
|
@@ -2766,8 +2878,8 @@ function createBraveWebSearchAdapter(opts = {}) {
|
|
|
2766
2878
|
headers: { "X-Subscription-Token": apiKey, Accept: "application/json" }
|
|
2767
2879
|
});
|
|
2768
2880
|
if (!res.ok) throw new Error(`brave_search_failed: HTTP ${res.status}`);
|
|
2769
|
-
const
|
|
2770
|
-
const results =
|
|
2881
|
+
const json2 = await res.json();
|
|
2882
|
+
const results = json2?.web?.results ?? [];
|
|
2771
2883
|
return results.map((r) => ({
|
|
2772
2884
|
title: String(r?.title ?? ""),
|
|
2773
2885
|
url: String(r?.url ?? ""),
|
|
@@ -2927,6 +3039,6 @@ async function isBinaryFile(absolutePath) {
|
|
|
2927
3039
|
}
|
|
2928
3040
|
}
|
|
2929
3041
|
|
|
2930
|
-
export { CatastrophicCommandError, ContextMatchError, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
3042
|
+
export { CatastrophicCommandError, ContextMatchError, DEFAULT_MAX_IMAGE_BYTES, DEFAULT_TOOL_GUIDANCE, ReadTracker, ReasoningTools, RedirectBlockedError, SsrfBlockedError, buildEnvContext, buildRepoMap, catastrophicShellReason, commandDenialReason, createApplyPatchTool, createBraveWebSearchAdapter, createCurrentTimeTool, createEditFileTool, createGenericHttpSearchAdapter, createGitDiffTool, createGitStatusTool, createGlobTool, createInteractiveShellTool, createListDirTool, createPlanModeTool, createQuestionTool, createReadFileTool, createRunVitestTool, createSearchTextTool, createSessionArtifactStore, createShellTool, createTodolistTool, createUpdatePlanTool, createViewImageTool, createWebFetchTool, createWebSearchTool, createWriteFileTool, createWriteStdinTool, denyCatastrophicCommands, formatCode, formatDiff, formatError, formatFileList, injectGuidance, isBlockedIp, isCommandAllowed, renderToolList, replaceUnique, resolveAndScreen, screenedFetch, todoItemsToPlanNodes, truncateOutput, withDefaultGuidance, withDescription, withName, withShellExitGuidance, withToolResultGuidance };
|
|
2931
3043
|
//# sourceMappingURL=index.js.map
|
|
2932
3044
|
//# sourceMappingURL=index.js.map
|