@supyagent/sdk 0.2.1 → 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 +501 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +191 -1
- package/dist/index.d.ts +191 -1
- package/dist/index.js +495 -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,
|
|
@@ -843,7 +849,7 @@ function createBashTool(options) {
|
|
|
843
849
|
};
|
|
844
850
|
}
|
|
845
851
|
const start = Date.now();
|
|
846
|
-
return new Promise((
|
|
852
|
+
return new Promise((resolve6) => {
|
|
847
853
|
const child = (0, import_node_child_process.exec)(
|
|
848
854
|
command,
|
|
849
855
|
{
|
|
@@ -857,7 +863,7 @@ function createBashTool(options) {
|
|
|
857
863
|
const durationMs = Date.now() - start;
|
|
858
864
|
const timedOut = error?.killed === true;
|
|
859
865
|
const exitCode = timedOut ? 124 : typeof error?.code === "number" ? error.code : error ? 1 : 0;
|
|
860
|
-
|
|
866
|
+
resolve6({
|
|
861
867
|
stdout: truncate(stdout, maxOutput),
|
|
862
868
|
stderr: truncate(stderr, maxOutput),
|
|
863
869
|
exitCode,
|
|
@@ -867,7 +873,7 @@ function createBashTool(options) {
|
|
|
867
873
|
}
|
|
868
874
|
);
|
|
869
875
|
child.on("error", () => {
|
|
870
|
-
|
|
876
|
+
resolve6({
|
|
871
877
|
stdout: "",
|
|
872
878
|
stderr: "Failed to start process",
|
|
873
879
|
exitCode: 127,
|
|
@@ -910,12 +916,504 @@ function createViewImageTool(options) {
|
|
|
910
916
|
}
|
|
911
917
|
});
|
|
912
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
|
+
}
|
|
913
1405
|
// Annotate the CommonJS export names for ESM import in node:
|
|
914
1406
|
0 && (module.exports = {
|
|
915
1407
|
buildSkillsSystemPrompt,
|
|
916
1408
|
createApiCallTool,
|
|
1409
|
+
createAppendFileTool,
|
|
917
1410
|
createBashTool,
|
|
1411
|
+
createEditFileTool,
|
|
1412
|
+
createFindTool,
|
|
1413
|
+
createGrepTool,
|
|
1414
|
+
createHttpRequestTool,
|
|
918
1415
|
createLoadSkillTool,
|
|
1416
|
+
createReadFileRangeTool,
|
|
919
1417
|
createViewImageTool,
|
|
920
1418
|
findSkill,
|
|
921
1419
|
parseSkillsMarkdown,
|