@supyagent/sdk 0.2.0 → 0.3.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 +528 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +219 -1
- package/dist/index.d.ts +219 -1
- package/dist/index.js +522 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -22,8 +22,14 @@ var src_exports = {};
|
|
|
22
22
|
__export(src_exports, {
|
|
23
23
|
buildSkillsSystemPrompt: () => buildSkillsSystemPrompt,
|
|
24
24
|
createApiCallTool: () => createApiCallTool,
|
|
25
|
+
createAppendFileTool: () => createAppendFileTool,
|
|
25
26
|
createBashTool: () => createBashTool,
|
|
27
|
+
createEditFileTool: () => createEditFileTool,
|
|
28
|
+
createFindTool: () => createFindTool,
|
|
29
|
+
createGrepTool: () => createGrepTool,
|
|
30
|
+
createHttpRequestTool: () => createHttpRequestTool,
|
|
26
31
|
createLoadSkillTool: () => createLoadSkillTool,
|
|
32
|
+
createReadFileRangeTool: () => createReadFileRangeTool,
|
|
27
33
|
createViewImageTool: () => createViewImageTool,
|
|
28
34
|
findSkill: () => findSkill,
|
|
29
35
|
parseSkillsMarkdown: () => parseSkillsMarkdown,
|
|
@@ -411,6 +417,33 @@ function createDataPlane(fetcher, baseUrl, apiKey, accountId) {
|
|
|
411
417
|
}
|
|
412
418
|
}
|
|
413
419
|
return response;
|
|
420
|
+
},
|
|
421
|
+
async searchTools(query) {
|
|
422
|
+
const res = await fetcher(`/api/v1/tools/search/${encodeURIComponent(query)}`);
|
|
423
|
+
const json = await res.json();
|
|
424
|
+
const data = json.data ?? json;
|
|
425
|
+
return {
|
|
426
|
+
tools: Array.isArray(data.tools) ? data.tools : [],
|
|
427
|
+
total: data.total ?? 0
|
|
428
|
+
};
|
|
429
|
+
},
|
|
430
|
+
async toolsByProvider(provider) {
|
|
431
|
+
const res = await fetcher(`/api/v1/tools/provider/${encodeURIComponent(provider)}`);
|
|
432
|
+
const json = await res.json();
|
|
433
|
+
const data = json.data ?? json;
|
|
434
|
+
return {
|
|
435
|
+
tools: Array.isArray(data.tools) ? data.tools : [],
|
|
436
|
+
total: data.total ?? 0
|
|
437
|
+
};
|
|
438
|
+
},
|
|
439
|
+
async toolsByService(service) {
|
|
440
|
+
const res = await fetcher(`/api/v1/tools/service/${encodeURIComponent(service)}`);
|
|
441
|
+
const json = await res.json();
|
|
442
|
+
const data = json.data ?? json;
|
|
443
|
+
return {
|
|
444
|
+
tools: Array.isArray(data.tools) ? data.tools : [],
|
|
445
|
+
total: data.total ?? 0
|
|
446
|
+
};
|
|
414
447
|
}
|
|
415
448
|
};
|
|
416
449
|
}
|
|
@@ -816,7 +849,7 @@ function createBashTool(options) {
|
|
|
816
849
|
};
|
|
817
850
|
}
|
|
818
851
|
const start = Date.now();
|
|
819
|
-
return new Promise((
|
|
852
|
+
return new Promise((resolve6) => {
|
|
820
853
|
const child = (0, import_node_child_process.exec)(
|
|
821
854
|
command,
|
|
822
855
|
{
|
|
@@ -830,7 +863,7 @@ function createBashTool(options) {
|
|
|
830
863
|
const durationMs = Date.now() - start;
|
|
831
864
|
const timedOut = error?.killed === true;
|
|
832
865
|
const exitCode = timedOut ? 124 : typeof error?.code === "number" ? error.code : error ? 1 : 0;
|
|
833
|
-
|
|
866
|
+
resolve6({
|
|
834
867
|
stdout: truncate(stdout, maxOutput),
|
|
835
868
|
stderr: truncate(stderr, maxOutput),
|
|
836
869
|
exitCode,
|
|
@@ -840,7 +873,7 @@ function createBashTool(options) {
|
|
|
840
873
|
}
|
|
841
874
|
);
|
|
842
875
|
child.on("error", () => {
|
|
843
|
-
|
|
876
|
+
resolve6({
|
|
844
877
|
stdout: "",
|
|
845
878
|
stderr: "Failed to start process",
|
|
846
879
|
exitCode: 127,
|
|
@@ -883,12 +916,504 @@ function createViewImageTool(options) {
|
|
|
883
916
|
}
|
|
884
917
|
});
|
|
885
918
|
}
|
|
919
|
+
|
|
920
|
+
// src/tools/edit-file.ts
|
|
921
|
+
var import_ai5 = require("ai");
|
|
922
|
+
var import_promises = require("fs/promises");
|
|
923
|
+
var import_node_path = require("path");
|
|
924
|
+
function createEditFileTool(options) {
|
|
925
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
926
|
+
const maxFileSize = options?.maxFileSize ?? 1048576;
|
|
927
|
+
const schema = {
|
|
928
|
+
type: "object",
|
|
929
|
+
properties: {
|
|
930
|
+
path: {
|
|
931
|
+
type: "string",
|
|
932
|
+
description: "File path to edit (absolute or relative to working directory)"
|
|
933
|
+
},
|
|
934
|
+
edits: {
|
|
935
|
+
type: "array",
|
|
936
|
+
description: "Array of search-and-replace operations applied sequentially. Each edit replaces the first occurrence of oldText with newText.",
|
|
937
|
+
items: {
|
|
938
|
+
type: "object",
|
|
939
|
+
properties: {
|
|
940
|
+
oldText: {
|
|
941
|
+
type: "string",
|
|
942
|
+
description: "The exact text to find in the file"
|
|
943
|
+
},
|
|
944
|
+
newText: {
|
|
945
|
+
type: "string",
|
|
946
|
+
description: "The replacement text"
|
|
947
|
+
}
|
|
948
|
+
},
|
|
949
|
+
required: ["oldText", "newText"]
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
},
|
|
953
|
+
required: ["path", "edits"]
|
|
954
|
+
};
|
|
955
|
+
return (0, import_ai5.tool)({
|
|
956
|
+
description: "Edit a file by applying one or more search-and-replace operations. Each edit replaces the first occurrence of oldText with newText. More efficient than rewriting entire files \u2014 only send the changed parts. Returns an error with the current file content if any oldText is not found.",
|
|
957
|
+
inputSchema: (0, import_ai5.jsonSchema)(schema),
|
|
958
|
+
execute: async (args) => {
|
|
959
|
+
const { path: filePath, edits } = args;
|
|
960
|
+
const fullPath = (0, import_node_path.resolve)(cwd, filePath);
|
|
961
|
+
let content;
|
|
962
|
+
try {
|
|
963
|
+
const buf = await (0, import_promises.readFile)(fullPath);
|
|
964
|
+
if (buf.length > maxFileSize) {
|
|
965
|
+
return { error: `File too large (${buf.length} bytes). Max: ${maxFileSize} bytes.` };
|
|
966
|
+
}
|
|
967
|
+
content = buf.toString("utf-8");
|
|
968
|
+
} catch (err) {
|
|
969
|
+
const code = err.code;
|
|
970
|
+
if (code === "ENOENT") {
|
|
971
|
+
return { error: `File not found: ${filePath}` };
|
|
972
|
+
}
|
|
973
|
+
return { error: `Failed to read file: ${err.message}` };
|
|
974
|
+
}
|
|
975
|
+
let replacements = 0;
|
|
976
|
+
for (const edit of edits) {
|
|
977
|
+
const idx = content.indexOf(edit.oldText);
|
|
978
|
+
if (idx === -1) {
|
|
979
|
+
return {
|
|
980
|
+
error: `oldText not found in file \u2014 it may have been modified. Re-read the file and retry.
|
|
981
|
+
|
|
982
|
+
Searched for:
|
|
983
|
+
${edit.oldText}`,
|
|
984
|
+
currentContent: content
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
content = content.slice(0, idx) + edit.newText + content.slice(idx + edit.oldText.length);
|
|
988
|
+
replacements++;
|
|
989
|
+
}
|
|
990
|
+
await (0, import_promises.writeFile)(fullPath, content, "utf-8");
|
|
991
|
+
return { path: filePath, replacements };
|
|
992
|
+
}
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
// src/tools/grep.ts
|
|
997
|
+
var import_ai6 = require("ai");
|
|
998
|
+
var import_node_child_process2 = require("child_process");
|
|
999
|
+
var import_node_path2 = require("path");
|
|
1000
|
+
var MAX_OUTPUT2 = 3e4;
|
|
1001
|
+
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1002
|
+
function createGrepTool(options) {
|
|
1003
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
1004
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT2;
|
|
1005
|
+
const maxOutput = options?.maxOutput ?? MAX_OUTPUT2;
|
|
1006
|
+
const schema = {
|
|
1007
|
+
type: "object",
|
|
1008
|
+
properties: {
|
|
1009
|
+
pattern: {
|
|
1010
|
+
type: "string",
|
|
1011
|
+
description: "Search pattern (regular expression)"
|
|
1012
|
+
},
|
|
1013
|
+
path: {
|
|
1014
|
+
type: "string",
|
|
1015
|
+
description: "File or directory to search in (relative to working directory). Defaults to current directory."
|
|
1016
|
+
},
|
|
1017
|
+
include: {
|
|
1018
|
+
type: "string",
|
|
1019
|
+
description: 'Glob pattern to filter files, e.g. "*.ts", "*.{js,jsx}". Only matching files are searched.'
|
|
1020
|
+
},
|
|
1021
|
+
ignoreCase: {
|
|
1022
|
+
type: "boolean",
|
|
1023
|
+
description: "Case-insensitive search. Defaults to false."
|
|
1024
|
+
},
|
|
1025
|
+
maxResults: {
|
|
1026
|
+
type: "number",
|
|
1027
|
+
description: "Maximum number of matching lines to return. Defaults to 100."
|
|
1028
|
+
}
|
|
1029
|
+
},
|
|
1030
|
+
required: ["pattern"]
|
|
1031
|
+
};
|
|
1032
|
+
return (0, import_ai6.tool)({
|
|
1033
|
+
description: "Search file contents for a pattern (regular expression). Returns matching lines with file paths and line numbers. Useful for finding function definitions, usages, imports, config values, and more.",
|
|
1034
|
+
inputSchema: (0, import_ai6.jsonSchema)(schema),
|
|
1035
|
+
execute: async (args) => {
|
|
1036
|
+
const {
|
|
1037
|
+
pattern,
|
|
1038
|
+
path: searchPath,
|
|
1039
|
+
include,
|
|
1040
|
+
ignoreCase,
|
|
1041
|
+
maxResults
|
|
1042
|
+
} = args;
|
|
1043
|
+
const target = (0, import_node_path2.resolve)(cwd, searchPath ?? ".");
|
|
1044
|
+
const limit = maxResults ?? 100;
|
|
1045
|
+
const rgArgs = [
|
|
1046
|
+
"--no-heading",
|
|
1047
|
+
"--line-number",
|
|
1048
|
+
"--color=never",
|
|
1049
|
+
`-m ${limit}`
|
|
1050
|
+
];
|
|
1051
|
+
if (ignoreCase) rgArgs.push("-i");
|
|
1052
|
+
if (include) rgArgs.push(`--glob '${include}'`);
|
|
1053
|
+
const grepArgs = [
|
|
1054
|
+
"-rn",
|
|
1055
|
+
"--color=never",
|
|
1056
|
+
`-m ${limit}`
|
|
1057
|
+
];
|
|
1058
|
+
if (ignoreCase) grepArgs.push("-i");
|
|
1059
|
+
if (include) grepArgs.push(`--include='${include}'`);
|
|
1060
|
+
const escaped = pattern.replace(/'/g, "'\\''");
|
|
1061
|
+
const command = `rg ${rgArgs.join(" ")} '${escaped}' '${target}' 2>/dev/null || grep ${grepArgs.join(" ")} '${escaped}' '${target}' 2>/dev/null`;
|
|
1062
|
+
return new Promise((resolvePromise) => {
|
|
1063
|
+
(0, import_node_child_process2.exec)(
|
|
1064
|
+
command,
|
|
1065
|
+
{
|
|
1066
|
+
cwd,
|
|
1067
|
+
timeout,
|
|
1068
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
1069
|
+
env: { ...process.env, TERM: "dumb" }
|
|
1070
|
+
},
|
|
1071
|
+
(error, stdout) => {
|
|
1072
|
+
if (error && error.code !== 1 && !error.killed) {
|
|
1073
|
+
resolvePromise({ error: `Search failed: ${error.message}` });
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1076
|
+
if (error?.killed) {
|
|
1077
|
+
resolvePromise({ error: "Search timed out" });
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
const output = stdout.trim();
|
|
1081
|
+
if (!output) {
|
|
1082
|
+
resolvePromise({ matches: "", matchCount: 0, truncated: false });
|
|
1083
|
+
return;
|
|
1084
|
+
}
|
|
1085
|
+
const truncated = output.length > maxOutput;
|
|
1086
|
+
const matches = truncated ? output.slice(0, maxOutput) + "\n... (truncated)" : output;
|
|
1087
|
+
const matchCount = matches.split("\n").filter(Boolean).length;
|
|
1088
|
+
resolvePromise({ matches, matchCount, truncated });
|
|
1089
|
+
}
|
|
1090
|
+
);
|
|
1091
|
+
});
|
|
1092
|
+
}
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// src/tools/find.ts
|
|
1097
|
+
var import_ai7 = require("ai");
|
|
1098
|
+
var import_node_child_process3 = require("child_process");
|
|
1099
|
+
var import_node_path3 = require("path");
|
|
1100
|
+
var DEFAULT_TIMEOUT3 = 15e3;
|
|
1101
|
+
var MAX_OUTPUT3 = 3e4;
|
|
1102
|
+
function createFindTool(options) {
|
|
1103
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
1104
|
+
const timeout = options?.timeout ?? DEFAULT_TIMEOUT3;
|
|
1105
|
+
const maxOutput = options?.maxOutput ?? MAX_OUTPUT3;
|
|
1106
|
+
const schema = {
|
|
1107
|
+
type: "object",
|
|
1108
|
+
properties: {
|
|
1109
|
+
pattern: {
|
|
1110
|
+
type: "string",
|
|
1111
|
+
description: 'File name or glob pattern to search for, e.g. "*.ts", "config.json", "README*"'
|
|
1112
|
+
},
|
|
1113
|
+
path: {
|
|
1114
|
+
type: "string",
|
|
1115
|
+
description: "Directory to search in (relative to working directory). Defaults to current directory."
|
|
1116
|
+
},
|
|
1117
|
+
type: {
|
|
1118
|
+
type: "string",
|
|
1119
|
+
description: 'Filter by type: "f" for files only, "d" for directories only. Defaults to both.',
|
|
1120
|
+
enum: ["f", "d"]
|
|
1121
|
+
},
|
|
1122
|
+
maxDepth: {
|
|
1123
|
+
type: "number",
|
|
1124
|
+
description: "Maximum directory depth to search. Defaults to no limit."
|
|
1125
|
+
},
|
|
1126
|
+
maxResults: {
|
|
1127
|
+
type: "number",
|
|
1128
|
+
description: "Maximum number of results to return. Defaults to 200."
|
|
1129
|
+
}
|
|
1130
|
+
},
|
|
1131
|
+
required: ["pattern"]
|
|
1132
|
+
};
|
|
1133
|
+
const IGNORE_DIRS = [
|
|
1134
|
+
"node_modules",
|
|
1135
|
+
".git",
|
|
1136
|
+
"dist",
|
|
1137
|
+
"build",
|
|
1138
|
+
".next",
|
|
1139
|
+
"__pycache__",
|
|
1140
|
+
".venv",
|
|
1141
|
+
"venv",
|
|
1142
|
+
".cache",
|
|
1143
|
+
"coverage",
|
|
1144
|
+
".turbo"
|
|
1145
|
+
];
|
|
1146
|
+
return (0, import_ai7.tool)({
|
|
1147
|
+
description: "Find files and directories by name or glob pattern. Returns matching paths relative to the working directory. Automatically ignores common directories (node_modules, .git, dist, etc.).",
|
|
1148
|
+
inputSchema: (0, import_ai7.jsonSchema)(schema),
|
|
1149
|
+
execute: async (args) => {
|
|
1150
|
+
const {
|
|
1151
|
+
pattern,
|
|
1152
|
+
path: searchPath,
|
|
1153
|
+
type: fileType,
|
|
1154
|
+
maxDepth,
|
|
1155
|
+
maxResults
|
|
1156
|
+
} = args;
|
|
1157
|
+
const target = (0, import_node_path3.resolve)(cwd, searchPath ?? ".");
|
|
1158
|
+
const limit = maxResults ?? 200;
|
|
1159
|
+
const pruneExpr = IGNORE_DIRS.map((d) => `-name '${d}' -prune`).join(" -o ");
|
|
1160
|
+
const parts = [
|
|
1161
|
+
`find '${target}'`,
|
|
1162
|
+
`\\( ${pruneExpr} \\)`,
|
|
1163
|
+
`-o -name '${pattern.replace(/'/g, "'\\''")}'`
|
|
1164
|
+
];
|
|
1165
|
+
if (fileType) parts.push(`-type ${fileType}`);
|
|
1166
|
+
parts.push("-print");
|
|
1167
|
+
parts.push(`| head -n ${limit}`);
|
|
1168
|
+
if (maxDepth !== void 0) {
|
|
1169
|
+
parts.splice(1, 0, `-maxdepth ${maxDepth}`);
|
|
1170
|
+
}
|
|
1171
|
+
const command = parts.join(" ");
|
|
1172
|
+
return new Promise((resolvePromise) => {
|
|
1173
|
+
(0, import_node_child_process3.exec)(
|
|
1174
|
+
command,
|
|
1175
|
+
{
|
|
1176
|
+
cwd,
|
|
1177
|
+
timeout,
|
|
1178
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
1179
|
+
env: { ...process.env, TERM: "dumb" }
|
|
1180
|
+
},
|
|
1181
|
+
(error, stdout) => {
|
|
1182
|
+
if (error?.killed) {
|
|
1183
|
+
resolvePromise({ error: "Search timed out" });
|
|
1184
|
+
return;
|
|
1185
|
+
}
|
|
1186
|
+
const output = stdout.trim();
|
|
1187
|
+
if (!output) {
|
|
1188
|
+
resolvePromise({ files: [], count: 0, truncated: false });
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
const truncated = output.length > maxOutput;
|
|
1192
|
+
const raw = truncated ? output.slice(0, maxOutput) : output;
|
|
1193
|
+
const files = raw.split("\n").filter(Boolean);
|
|
1194
|
+
resolvePromise({
|
|
1195
|
+
files,
|
|
1196
|
+
count: files.length,
|
|
1197
|
+
truncated: truncated || files.length >= limit
|
|
1198
|
+
});
|
|
1199
|
+
}
|
|
1200
|
+
);
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// src/tools/read-file-range.ts
|
|
1207
|
+
var import_ai8 = require("ai");
|
|
1208
|
+
var import_promises2 = require("fs/promises");
|
|
1209
|
+
var import_node_path4 = require("path");
|
|
1210
|
+
var DEFAULT_MAX_LINES = 200;
|
|
1211
|
+
function createReadFileRangeTool(options) {
|
|
1212
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
1213
|
+
const maxLines = options?.maxLines ?? DEFAULT_MAX_LINES;
|
|
1214
|
+
const schema = {
|
|
1215
|
+
type: "object",
|
|
1216
|
+
properties: {
|
|
1217
|
+
path: {
|
|
1218
|
+
type: "string",
|
|
1219
|
+
description: "File path to read (absolute or relative to working directory)"
|
|
1220
|
+
},
|
|
1221
|
+
startLine: {
|
|
1222
|
+
type: "number",
|
|
1223
|
+
description: "First line to read (1-based, inclusive). Defaults to 1."
|
|
1224
|
+
},
|
|
1225
|
+
endLine: {
|
|
1226
|
+
type: "number",
|
|
1227
|
+
description: "Last line to read (1-based, inclusive). Defaults to startLine + 200."
|
|
1228
|
+
}
|
|
1229
|
+
},
|
|
1230
|
+
required: ["path"]
|
|
1231
|
+
};
|
|
1232
|
+
return (0, import_ai8.tool)({
|
|
1233
|
+
description: "Read a specific range of lines from a file, with line numbers. More efficient than reading entire files \u2014 use this after grep to read context around matches, or to inspect specific sections of large files.",
|
|
1234
|
+
inputSchema: (0, import_ai8.jsonSchema)(schema),
|
|
1235
|
+
execute: async (args) => {
|
|
1236
|
+
const { path: filePath, startLine, endLine } = args;
|
|
1237
|
+
const fullPath = (0, import_node_path4.resolve)(cwd, filePath);
|
|
1238
|
+
const start = Math.max(1, startLine ?? 1);
|
|
1239
|
+
const requestedEnd = endLine ?? start + maxLines - 1;
|
|
1240
|
+
const end = Math.min(requestedEnd, start + maxLines - 1);
|
|
1241
|
+
let fh;
|
|
1242
|
+
try {
|
|
1243
|
+
fh = await (0, import_promises2.open)(fullPath, "r");
|
|
1244
|
+
} catch (err) {
|
|
1245
|
+
const code = err.code;
|
|
1246
|
+
if (code === "ENOENT") {
|
|
1247
|
+
return { error: `File not found: ${filePath}` };
|
|
1248
|
+
}
|
|
1249
|
+
return { error: `Failed to open file: ${err.message}` };
|
|
1250
|
+
}
|
|
1251
|
+
try {
|
|
1252
|
+
const lines = [];
|
|
1253
|
+
let lineNum = 0;
|
|
1254
|
+
for await (const line of fh.readLines()) {
|
|
1255
|
+
lineNum++;
|
|
1256
|
+
if (lineNum > end) break;
|
|
1257
|
+
if (lineNum >= start) {
|
|
1258
|
+
lines.push(`${lineNum} ${line}`);
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
if (lines.length === 0 && start > 1) {
|
|
1262
|
+
return {
|
|
1263
|
+
error: `File only has ${lineNum} lines. Requested start line: ${start}.`
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
return {
|
|
1267
|
+
path: filePath,
|
|
1268
|
+
content: lines.join("\n"),
|
|
1269
|
+
startLine: start,
|
|
1270
|
+
endLine: Math.min(end, lineNum),
|
|
1271
|
+
totalLines: lineNum
|
|
1272
|
+
};
|
|
1273
|
+
} finally {
|
|
1274
|
+
await fh.close();
|
|
1275
|
+
}
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
// src/tools/append-file.ts
|
|
1281
|
+
var import_ai9 = require("ai");
|
|
1282
|
+
var import_promises3 = require("fs/promises");
|
|
1283
|
+
var import_node_path5 = require("path");
|
|
1284
|
+
function createAppendFileTool(options) {
|
|
1285
|
+
const cwd = options?.cwd ?? process.cwd();
|
|
1286
|
+
const schema = {
|
|
1287
|
+
type: "object",
|
|
1288
|
+
properties: {
|
|
1289
|
+
path: {
|
|
1290
|
+
type: "string",
|
|
1291
|
+
description: "File path to append to (absolute or relative to working directory). Created if it doesn't exist."
|
|
1292
|
+
},
|
|
1293
|
+
content: {
|
|
1294
|
+
type: "string",
|
|
1295
|
+
description: "Content to append to the end of the file. Include a leading newline if you want separation from existing content."
|
|
1296
|
+
}
|
|
1297
|
+
},
|
|
1298
|
+
required: ["path", "content"]
|
|
1299
|
+
};
|
|
1300
|
+
return (0, import_ai9.tool)({
|
|
1301
|
+
description: "Append content to the end of a file. Creates the file if it doesn't exist. More efficient than reading a file and rewriting it \u2014 use this for building up logs, CSVs, reports, or any file where you only need to add content.",
|
|
1302
|
+
inputSchema: (0, import_ai9.jsonSchema)(schema),
|
|
1303
|
+
execute: async (args) => {
|
|
1304
|
+
const { path: filePath, content } = args;
|
|
1305
|
+
const fullPath = (0, import_node_path5.resolve)(cwd, filePath);
|
|
1306
|
+
try {
|
|
1307
|
+
await (0, import_promises3.appendFile)(fullPath, content, "utf-8");
|
|
1308
|
+
return {
|
|
1309
|
+
path: filePath,
|
|
1310
|
+
bytesAppended: Buffer.byteLength(content, "utf-8")
|
|
1311
|
+
};
|
|
1312
|
+
} catch (err) {
|
|
1313
|
+
return { error: `Failed to append to file: ${err.message}` };
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
});
|
|
1317
|
+
}
|
|
1318
|
+
|
|
1319
|
+
// src/tools/http-request.ts
|
|
1320
|
+
var import_ai10 = require("ai");
|
|
1321
|
+
var DEFAULT_TIMEOUT4 = 3e4;
|
|
1322
|
+
var MAX_RESPONSE_SIZE = 1e5;
|
|
1323
|
+
function createHttpRequestTool(options) {
|
|
1324
|
+
const defaultTimeout = options?.timeout ?? DEFAULT_TIMEOUT4;
|
|
1325
|
+
const maxResponseSize = options?.maxResponseSize ?? MAX_RESPONSE_SIZE;
|
|
1326
|
+
const defaultHeaders = options?.defaultHeaders ?? {};
|
|
1327
|
+
const schema = {
|
|
1328
|
+
type: "object",
|
|
1329
|
+
properties: {
|
|
1330
|
+
url: {
|
|
1331
|
+
type: "string",
|
|
1332
|
+
description: "The URL to request (must include protocol, e.g. https://)"
|
|
1333
|
+
},
|
|
1334
|
+
method: {
|
|
1335
|
+
type: "string",
|
|
1336
|
+
description: "HTTP method. Defaults to GET.",
|
|
1337
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"]
|
|
1338
|
+
},
|
|
1339
|
+
headers: {
|
|
1340
|
+
type: "object",
|
|
1341
|
+
description: 'Request headers as key-value pairs, e.g. { "Content-Type": "application/json" }',
|
|
1342
|
+
additionalProperties: { type: "string" }
|
|
1343
|
+
},
|
|
1344
|
+
body: {
|
|
1345
|
+
type: "string",
|
|
1346
|
+
description: "Request body as a string. For JSON, stringify the object first."
|
|
1347
|
+
},
|
|
1348
|
+
timeout: {
|
|
1349
|
+
type: "number",
|
|
1350
|
+
description: "Request timeout in milliseconds. Defaults to 30000."
|
|
1351
|
+
}
|
|
1352
|
+
},
|
|
1353
|
+
required: ["url"]
|
|
1354
|
+
};
|
|
1355
|
+
return (0, import_ai10.tool)({
|
|
1356
|
+
description: "Make an HTTP request to any URL. Supports GET, POST, PUT, PATCH, DELETE with custom headers and body. Returns status code, headers, and response body. Use this for calling APIs, checking endpoints, fetching data, or sending webhooks.",
|
|
1357
|
+
inputSchema: (0, import_ai10.jsonSchema)(schema),
|
|
1358
|
+
execute: async (args) => {
|
|
1359
|
+
const {
|
|
1360
|
+
url,
|
|
1361
|
+
method,
|
|
1362
|
+
headers,
|
|
1363
|
+
body,
|
|
1364
|
+
timeout: reqTimeout
|
|
1365
|
+
} = args;
|
|
1366
|
+
const effectiveTimeout = reqTimeout ?? defaultTimeout;
|
|
1367
|
+
try {
|
|
1368
|
+
const controller = new AbortController();
|
|
1369
|
+
const timer = setTimeout(() => controller.abort(), effectiveTimeout);
|
|
1370
|
+
const start = Date.now();
|
|
1371
|
+
const response = await fetch(url, {
|
|
1372
|
+
method: method ?? "GET",
|
|
1373
|
+
headers: { ...defaultHeaders, ...headers },
|
|
1374
|
+
body: body ?? void 0,
|
|
1375
|
+
signal: controller.signal
|
|
1376
|
+
});
|
|
1377
|
+
const durationMs = Date.now() - start;
|
|
1378
|
+
clearTimeout(timer);
|
|
1379
|
+
const responseHeaders = {};
|
|
1380
|
+
response.headers.forEach((value, key) => {
|
|
1381
|
+
responseHeaders[key] = value;
|
|
1382
|
+
});
|
|
1383
|
+
let responseBody = await response.text();
|
|
1384
|
+
const truncated = responseBody.length > maxResponseSize;
|
|
1385
|
+
if (truncated) {
|
|
1386
|
+
responseBody = responseBody.slice(0, maxResponseSize) + "\n... (truncated)";
|
|
1387
|
+
}
|
|
1388
|
+
return {
|
|
1389
|
+
status: response.status,
|
|
1390
|
+
statusText: response.statusText,
|
|
1391
|
+
headers: responseHeaders,
|
|
1392
|
+
body: responseBody,
|
|
1393
|
+
durationMs,
|
|
1394
|
+
truncated
|
|
1395
|
+
};
|
|
1396
|
+
} catch (err) {
|
|
1397
|
+
if (err instanceof DOMException && err.name === "AbortError") {
|
|
1398
|
+
return { error: `Request timed out after ${effectiveTimeout}ms` };
|
|
1399
|
+
}
|
|
1400
|
+
return { error: `Request failed: ${err.message}` };
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
});
|
|
1404
|
+
}
|
|
886
1405
|
// Annotate the CommonJS export names for ESM import in node:
|
|
887
1406
|
0 && (module.exports = {
|
|
888
1407
|
buildSkillsSystemPrompt,
|
|
889
1408
|
createApiCallTool,
|
|
1409
|
+
createAppendFileTool,
|
|
890
1410
|
createBashTool,
|
|
1411
|
+
createEditFileTool,
|
|
1412
|
+
createFindTool,
|
|
1413
|
+
createGrepTool,
|
|
1414
|
+
createHttpRequestTool,
|
|
891
1415
|
createLoadSkillTool,
|
|
1416
|
+
createReadFileRangeTool,
|
|
892
1417
|
createViewImageTool,
|
|
893
1418
|
findSkill,
|
|
894
1419
|
parseSkillsMarkdown,
|