@supyagent/sdk 0.1.15 → 0.1.17
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 +228 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +228 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -200,10 +200,220 @@ var import_ai2 = require("ai");
|
|
|
200
200
|
var import_node_child_process = require("child_process");
|
|
201
201
|
var MAX_OUTPUT = 3e4;
|
|
202
202
|
var DEFAULT_TIMEOUT = 3e4;
|
|
203
|
+
var SECRET_PATTERNS = [
|
|
204
|
+
/key/i,
|
|
205
|
+
/secret/i,
|
|
206
|
+
/token/i,
|
|
207
|
+
/password/i,
|
|
208
|
+
/credential/i,
|
|
209
|
+
/auth/i,
|
|
210
|
+
/private/i,
|
|
211
|
+
/api_key/i,
|
|
212
|
+
/apikey/i,
|
|
213
|
+
/^AWS_/i,
|
|
214
|
+
/^AZURE_/i,
|
|
215
|
+
/^GCP_/i,
|
|
216
|
+
/^GITHUB_/i,
|
|
217
|
+
/^DATABASE_URL$/i,
|
|
218
|
+
/^ENCRYPTION_/i,
|
|
219
|
+
/^SUPABASE_/i
|
|
220
|
+
];
|
|
221
|
+
var SAFE_VARS = /* @__PURE__ */ new Set([
|
|
222
|
+
"PATH",
|
|
223
|
+
"HOME",
|
|
224
|
+
"USER",
|
|
225
|
+
"SHELL",
|
|
226
|
+
"LANG",
|
|
227
|
+
"LC_ALL",
|
|
228
|
+
"LC_CTYPE",
|
|
229
|
+
"TERM",
|
|
230
|
+
"EDITOR",
|
|
231
|
+
"VISUAL",
|
|
232
|
+
"TMPDIR",
|
|
233
|
+
"TMP",
|
|
234
|
+
"TEMP",
|
|
235
|
+
"NODE_ENV",
|
|
236
|
+
"NODE_PATH",
|
|
237
|
+
"NODE_OPTIONS",
|
|
238
|
+
"NPM_CONFIG_PREFIX",
|
|
239
|
+
"NVM_DIR",
|
|
240
|
+
"NVM_BIN",
|
|
241
|
+
"NVM_INC",
|
|
242
|
+
"XDG_CONFIG_HOME",
|
|
243
|
+
"XDG_DATA_HOME",
|
|
244
|
+
"XDG_CACHE_HOME",
|
|
245
|
+
"COLORTERM",
|
|
246
|
+
"FORCE_COLOR",
|
|
247
|
+
"NO_COLOR",
|
|
248
|
+
"COLUMNS",
|
|
249
|
+
"LINES",
|
|
250
|
+
"SHLVL",
|
|
251
|
+
"PWD",
|
|
252
|
+
"OLDPWD",
|
|
253
|
+
"LOGNAME",
|
|
254
|
+
"HOSTNAME",
|
|
255
|
+
"HOSTTYPE",
|
|
256
|
+
"OSTYPE",
|
|
257
|
+
"MACHTYPE"
|
|
258
|
+
]);
|
|
259
|
+
function sanitizeEnv(env, opts) {
|
|
260
|
+
const result = {};
|
|
261
|
+
const includeSet = opts?.include ? new Set(opts.include) : null;
|
|
262
|
+
const excludeSet = opts?.exclude ? new Set(opts.exclude) : null;
|
|
263
|
+
for (const [key, value] of Object.entries(env)) {
|
|
264
|
+
if (value === void 0) continue;
|
|
265
|
+
if (excludeSet?.has(key)) continue;
|
|
266
|
+
if (includeSet?.has(key)) {
|
|
267
|
+
result[key] = value;
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
if (SAFE_VARS.has(key)) {
|
|
271
|
+
result[key] = value;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (SECRET_PATTERNS.some((p) => p.test(key))) continue;
|
|
275
|
+
result[key] = value;
|
|
276
|
+
}
|
|
277
|
+
result.TERM = "dumb";
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
function buildEnv(policy, opts) {
|
|
281
|
+
if (typeof policy === "object" && policy !== null) {
|
|
282
|
+
return { ...policy, TERM: "dumb" };
|
|
283
|
+
}
|
|
284
|
+
switch (policy) {
|
|
285
|
+
case "inherit":
|
|
286
|
+
return { ...process.env, TERM: "dumb" };
|
|
287
|
+
case "none":
|
|
288
|
+
return {
|
|
289
|
+
PATH: process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin",
|
|
290
|
+
HOME: process.env.HOME ?? "/tmp",
|
|
291
|
+
TERM: "dumb"
|
|
292
|
+
};
|
|
293
|
+
case "safe":
|
|
294
|
+
default:
|
|
295
|
+
return sanitizeEnv(process.env, {
|
|
296
|
+
include: opts?.envInclude,
|
|
297
|
+
exclude: opts?.envExclude
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
var SAFE_COMMANDS = /* @__PURE__ */ new Set([
|
|
302
|
+
"cat",
|
|
303
|
+
"cd",
|
|
304
|
+
"cut",
|
|
305
|
+
"echo",
|
|
306
|
+
"expr",
|
|
307
|
+
"false",
|
|
308
|
+
"grep",
|
|
309
|
+
"egrep",
|
|
310
|
+
"fgrep",
|
|
311
|
+
"head",
|
|
312
|
+
"id",
|
|
313
|
+
"ls",
|
|
314
|
+
"nl",
|
|
315
|
+
"paste",
|
|
316
|
+
"pwd",
|
|
317
|
+
"rev",
|
|
318
|
+
"seq",
|
|
319
|
+
"stat",
|
|
320
|
+
"tail",
|
|
321
|
+
"tr",
|
|
322
|
+
"true",
|
|
323
|
+
"uname",
|
|
324
|
+
"uniq",
|
|
325
|
+
"wc",
|
|
326
|
+
"which",
|
|
327
|
+
"whoami",
|
|
328
|
+
"date",
|
|
329
|
+
"env",
|
|
330
|
+
"printenv",
|
|
331
|
+
"hostname",
|
|
332
|
+
"df",
|
|
333
|
+
"du",
|
|
334
|
+
"file",
|
|
335
|
+
"find",
|
|
336
|
+
"rg",
|
|
337
|
+
"tree",
|
|
338
|
+
"less",
|
|
339
|
+
"more",
|
|
340
|
+
"sort",
|
|
341
|
+
"diff",
|
|
342
|
+
"md5sum",
|
|
343
|
+
"sha256sum",
|
|
344
|
+
"base64",
|
|
345
|
+
"xxd",
|
|
346
|
+
"hexdump",
|
|
347
|
+
"man",
|
|
348
|
+
"help",
|
|
349
|
+
"type",
|
|
350
|
+
"command",
|
|
351
|
+
"test",
|
|
352
|
+
"basename",
|
|
353
|
+
"dirname",
|
|
354
|
+
"realpath",
|
|
355
|
+
"readlink",
|
|
356
|
+
"tee",
|
|
357
|
+
"xargs",
|
|
358
|
+
"jq",
|
|
359
|
+
"yq"
|
|
360
|
+
]);
|
|
361
|
+
var SAFE_GIT_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
362
|
+
"status",
|
|
363
|
+
"log",
|
|
364
|
+
"diff",
|
|
365
|
+
"show",
|
|
366
|
+
"branch",
|
|
367
|
+
"tag",
|
|
368
|
+
"remote",
|
|
369
|
+
"stash",
|
|
370
|
+
"describe",
|
|
371
|
+
"shortlog",
|
|
372
|
+
"blame",
|
|
373
|
+
"reflog",
|
|
374
|
+
"rev-parse",
|
|
375
|
+
"ls-files",
|
|
376
|
+
"ls-tree",
|
|
377
|
+
"cat-file",
|
|
378
|
+
"rev-list",
|
|
379
|
+
"name-rev"
|
|
380
|
+
]);
|
|
381
|
+
var DANGEROUS_PATTERNS = [
|
|
382
|
+
/rm\s+(-[^\s]*f|-[^\s]*r|--force|--recursive).*\//,
|
|
383
|
+
/:\(\)\s*\{\s*:\|:&\s*\};:/,
|
|
384
|
+
/>\s*\/dev\/sd/,
|
|
385
|
+
/mkfs\./,
|
|
386
|
+
/dd\s+.*of=\/dev\//,
|
|
387
|
+
/shutdown|reboot|halt|poweroff/,
|
|
388
|
+
/chmod\s+777\s+\//,
|
|
389
|
+
/curl.*\|\s*(ba)?sh/,
|
|
390
|
+
/wget.*\|\s*(ba)?sh/
|
|
391
|
+
];
|
|
392
|
+
function classifyCommand(command, extraSafe, extraDeny) {
|
|
393
|
+
const trimmed = command.trim();
|
|
394
|
+
if (DANGEROUS_PATTERNS.some((p) => p.test(trimmed))) return "dangerous";
|
|
395
|
+
if (extraDeny?.some((p) => p.test(trimmed))) return "dangerous";
|
|
396
|
+
const stripped = trimmed.replace(/^(\w+=\S+\s+)*/, "");
|
|
397
|
+
const parts = stripped.split(/\s+/);
|
|
398
|
+
const base = parts[0]?.replace(/^.*\//, "");
|
|
399
|
+
if (!base) return "unknown";
|
|
400
|
+
if (base === "git") {
|
|
401
|
+
const sub = parts[1];
|
|
402
|
+
if (sub && SAFE_GIT_SUBCOMMANDS.has(sub)) return "safe";
|
|
403
|
+
return "unknown";
|
|
404
|
+
}
|
|
405
|
+
if (SAFE_COMMANDS.has(base)) return "safe";
|
|
406
|
+
if (extraSafe?.has(base)) return "safe";
|
|
407
|
+
return "unknown";
|
|
408
|
+
}
|
|
203
409
|
function createBashTool(options) {
|
|
204
410
|
const cwd = options?.cwd ?? process.cwd();
|
|
205
411
|
const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 6e5);
|
|
206
412
|
const maxOutput = options?.maxOutput ?? MAX_OUTPUT;
|
|
413
|
+
const approvalPolicy = options?.approval ?? "auto";
|
|
414
|
+
const extraSafe = options?.allowCommands ? new Set(options.allowCommands) : void 0;
|
|
415
|
+
const extraDeny = options?.denyPatterns;
|
|
416
|
+
const execEnv = buildEnv(options?.env ?? "safe", options);
|
|
207
417
|
const schema = {
|
|
208
418
|
type: "object",
|
|
209
419
|
properties: {
|
|
@@ -214,11 +424,28 @@ function createBashTool(options) {
|
|
|
214
424
|
},
|
|
215
425
|
required: ["command"]
|
|
216
426
|
};
|
|
427
|
+
const needsApproval = approvalPolicy === "always" ? true : approvalPolicy === "never" ? false : async (args) => {
|
|
428
|
+
const { command } = args;
|
|
429
|
+
if (typeof approvalPolicy === "function") {
|
|
430
|
+
return approvalPolicy(command);
|
|
431
|
+
}
|
|
432
|
+
const safety = classifyCommand(command, extraSafe, extraDeny);
|
|
433
|
+
return safety !== "safe";
|
|
434
|
+
};
|
|
217
435
|
return (0, import_ai2.tool)({
|
|
218
436
|
description: "Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.",
|
|
219
437
|
inputSchema: (0, import_ai2.jsonSchema)(schema),
|
|
438
|
+
needsApproval,
|
|
220
439
|
execute: async (args) => {
|
|
221
440
|
const { command } = args;
|
|
441
|
+
if (DANGEROUS_PATTERNS.some((p) => p.test(command)) || extraDeny?.some((p) => p.test(command))) {
|
|
442
|
+
return {
|
|
443
|
+
stdout: "",
|
|
444
|
+
stderr: "Command blocked by safety policy",
|
|
445
|
+
exitCode: 126,
|
|
446
|
+
durationMs: 0
|
|
447
|
+
};
|
|
448
|
+
}
|
|
222
449
|
const start = Date.now();
|
|
223
450
|
return new Promise((resolve) => {
|
|
224
451
|
const child = (0, import_node_child_process.exec)(
|
|
@@ -228,8 +455,7 @@ function createBashTool(options) {
|
|
|
228
455
|
timeout,
|
|
229
456
|
shell: "/bin/bash",
|
|
230
457
|
maxBuffer: 10 * 1024 * 1024,
|
|
231
|
-
|
|
232
|
-
env: { ...process.env, TERM: "dumb" }
|
|
458
|
+
env: execEnv
|
|
233
459
|
},
|
|
234
460
|
(error, stdout, stderr) => {
|
|
235
461
|
const durationMs = Date.now() - start;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/client.ts","../src/tools/bash.ts"],"sourcesContent":["export { supyagent } from \"./core/client.js\";\nexport type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolMetadata,\n OpenAITool,\n ToolsResponse,\n} from \"./core/types.js\";\nexport { createBashTool } from \"./tools/bash.js\";\nexport type { BashToolOptions, BashToolResult } from \"./tools/bash.js\";\n","interface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nexport class TTLCache<T> {\n private store = new Map<string, CacheEntry<T>>();\n\n get(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.data;\n }\n\n set(key: string, data: T, ttlSeconds: number): void {\n this.store.set(key, {\n data,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { OpenAITool } from \"./types.js\";\nimport { createExecutor } from \"./http-executor.js\";\n\n/**\n * Converts an array of OpenAI-format tools from the supyagent API\n * into a Record of AI SDK tool() instances ready for streamText/generateText.\n */\nexport function convertTools(\n tools: OpenAITool[],\n baseUrl: string,\n apiKey: string\n): Record<string, import(\"ai\").Tool> {\n const result: Record<string, import(\"ai\").Tool> = {};\n\n for (const t of tools) {\n const { name, description, parameters } = t.function;\n const metadata = t.metadata;\n\n result[name] = tool({\n description,\n inputSchema: jsonSchema(parameters as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => createExecutor(metadata, baseUrl, apiKey)(args as Record<string, unknown>),\n });\n }\n\n return result;\n}\n\n/**\n * Filter tools by provider, service, or tool name.\n */\nexport function filterTools(\n tools: OpenAITool[],\n options: { only?: string[]; except?: string[] }\n): OpenAITool[] {\n let filtered = tools;\n\n if (options.only && options.only.length > 0) {\n const onlySet = new Set(options.only.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return onlySet.has(name) || onlySet.has(provider) || onlySet.has(service);\n });\n }\n\n if (options.except && options.except.length > 0) {\n const exceptSet = new Set(options.except.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return (\n !exceptSet.has(name) &&\n !exceptSet.has(provider) &&\n !exceptSet.has(service)\n );\n });\n }\n\n return filtered;\n}\n","import type { ToolMetadata } from \"./types.js\";\n\n/**\n * Creates an execute function for a tool that calls the supyagent API.\n *\n * Handles:\n * - Path parameter substitution (e.g., {messageId} in /api/v1/gmail/messages/{messageId})\n * - GET/DELETE: remaining args → query string\n * - POST/PUT/PATCH: merge bodyDefaults + remaining args → JSON body\n */\nexport function createExecutor(\n metadata: ToolMetadata,\n baseUrl: string,\n apiKey: string\n): (args: Record<string, unknown>) => Promise<unknown> {\n return async (args: Record<string, unknown>) => {\n const { method, path, bodyDefaults } = metadata;\n\n // Extract path parameters and substitute into the path\n const remainingArgs = { ...args };\n let resolvedPath = path;\n\n const pathParams = path.match(/\\{(\\w+)\\}/g);\n if (pathParams) {\n for (const param of pathParams) {\n const paramName = param.slice(1, -1);\n if (paramName in remainingArgs) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs[paramName]))\n );\n delete remainingArgs[paramName];\n }\n }\n\n // Fallback: LLMs often send { id } instead of { messageId }, { eventId }, etc.\n // because list responses return objects with an `id` field.\n // For any unresolved {fooId} param, try `id` from the args (once only).\n if (\"id\" in remainingArgs && resolvedPath.includes(\"{\")) {\n const unresolved = resolvedPath.match(/\\{(\\w+)\\}/g);\n if (unresolved) {\n for (const param of unresolved) {\n const paramName = param.slice(1, -1);\n if (paramName.toLowerCase().endsWith(\"id\")) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs.id))\n );\n delete remainingArgs.id;\n break; // consume `id` only once\n }\n }\n }\n }\n }\n\n let url = `${baseUrl}${resolvedPath}`;\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (method === \"GET\" || method === \"DELETE\") {\n // Remaining args go as query parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(remainingArgs)) {\n if (value !== undefined && value !== null) {\n params.set(key, String(value));\n }\n }\n const qs = params.toString();\n if (qs) url += `?${qs}`;\n } else {\n // POST/PUT/PATCH: merge bodyDefaults with remaining args\n const body = { ...bodyDefaults, ...remainingArgs };\n fetchOptions.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n return data;\n };\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\n\nconst DEFAULT_BASE_URL = \"https://app.supyagent.com\";\nconst CACHE_KEY = \"tools\";\n\n/**\n * Create a supyagent client for fetching and converting tools.\n *\n * @example\n * ```ts\n * import { supyagent } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const tools = await client.tools({ cache: 300 });\n * ```\n */\nexport function supyagent(options: SupyagentOptions): SupyagentClient {\n const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;\n const cache = new TTLCache<ToolsResponse>();\n\n return {\n async tools(filterOptions?: ToolFilterOptions) {\n const cacheTTL = resolveCacheTTL(filterOptions?.cache);\n\n // Check cache\n let response = cacheTTL > 0 ? cache.get(CACHE_KEY) : undefined;\n\n if (!response) {\n // Fetch from API\n const res = await fetch(`${baseUrl}/api/v1/tools`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(\n `Supyagent API error (${res.status}): ${error}`\n );\n }\n\n const json = await res.json();\n // API wraps response in { ok, data: { tools, base_url, total } }\n response = (json.data ?? json) as ToolsResponse;\n\n // Store in cache if TTL > 0\n if (cacheTTL > 0) {\n cache.set(CACHE_KEY, response, cacheTTL);\n }\n }\n\n // Use API-reported base_url for tool execution\n const toolBaseUrl = response.base_url || baseUrl;\n\n // Guard against non-array tools (e.g., API returns empty or unexpected shape)\n const toolsArray = Array.isArray(response.tools) ? response.tools : [];\n\n // Filter\n const filtered = filterTools(toolsArray, {\n only: filterOptions?.only,\n except: filterOptions?.except,\n });\n\n // Convert to AI SDK tools\n return convertTools(filtered, toolBaseUrl, apiKey);\n },\n };\n}\n\nfunction resolveCacheTTL(cache?: boolean | number): number {\n if (cache === true) return 60;\n if (typeof cache === \"number\") return cache;\n return 0;\n}\n","import { tool, jsonSchema } from \"ai\";\nimport { exec, type ExecException } from \"node:child_process\";\n\nconst MAX_OUTPUT = 30_000;\nconst DEFAULT_TIMEOUT = 30_000; // 30 seconds\n\nexport interface BashToolOptions {\n /** Working directory for commands. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Command timeout in milliseconds. Defaults to 30000 (30s). Max 600000 (10m). */\n timeout?: number;\n /** Maximum output length in characters. Defaults to 30000. */\n maxOutput?: number;\n}\n\nexport interface BashToolResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut?: boolean;\n}\n\n/**\n * Create a bash tool that executes shell commands on the server.\n *\n * @example\n * ```ts\n * import { supyagent, createBashTool } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const supyagentTools = await client.tools({ cache: 300 });\n *\n * const tools = {\n * ...supyagentTools,\n * bash: createBashTool({ cwd: '/path/to/project' }),\n * };\n * ```\n */\nexport function createBashTool(options?: BashToolOptions) {\n const cwd = options?.cwd ?? process.cwd();\n const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 600_000);\n const maxOutput = options?.maxOutput ?? MAX_OUTPUT;\n\n const schema = {\n type: \"object\" as const,\n properties: {\n command: {\n type: \"string\",\n description: \"The bash command to execute\",\n },\n },\n required: [\"command\"],\n };\n\n return tool({\n description:\n \"Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.\",\n inputSchema: jsonSchema(schema as Parameters<typeof jsonSchema>[0]),\n execute: async (args): Promise<BashToolResult> => {\n const { command } = args as { command: string };\n const start = Date.now();\n\n return new Promise<BashToolResult>((resolve) => {\n const child = exec(\n command,\n {\n cwd,\n timeout,\n shell: \"/bin/bash\",\n maxBuffer: 10 * 1024 * 1024, // 10MB buffer\n env: { ...process.env, TERM: \"dumb\" },\n },\n (error: ExecException | null, stdout: string, stderr: string) => {\n const durationMs = Date.now() - start;\n const timedOut = error?.killed === true;\n const exitCode = timedOut\n ? 124\n : typeof error?.code === \"number\"\n ? error.code\n : error\n ? 1\n : 0;\n\n resolve({\n stdout: truncate(stdout, maxOutput),\n stderr: truncate(stderr, maxOutput),\n exitCode,\n durationMs,\n ...(timedOut ? { timedOut: true } : {}),\n });\n }\n );\n\n // Ensure the child process is killed on timeout\n child.on(\"error\", () => {\n resolve({\n stdout: \"\",\n stderr: \"Failed to start process\",\n exitCode: 127,\n durationMs: Date.now() - start,\n });\n });\n });\n },\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor(max / 2) - 50;\n return (\n str.slice(0, half) +\n `\\n\\n... (${str.length - max} characters truncated) ...\\n\\n` +\n str.slice(-half)\n );\n}\n"],"mappings":"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","names":["import_ai"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/client.ts","../src/tools/bash.ts"],"sourcesContent":["export { supyagent } from \"./core/client.js\";\nexport type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolMetadata,\n OpenAITool,\n ToolsResponse,\n} from \"./core/types.js\";\nexport { createBashTool } from \"./tools/bash.js\";\nexport type { BashToolOptions, BashToolResult } from \"./tools/bash.js\";\n","interface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nexport class TTLCache<T> {\n private store = new Map<string, CacheEntry<T>>();\n\n get(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.data;\n }\n\n set(key: string, data: T, ttlSeconds: number): void {\n this.store.set(key, {\n data,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { OpenAITool } from \"./types.js\";\nimport { createExecutor } from \"./http-executor.js\";\n\n/**\n * Converts an array of OpenAI-format tools from the supyagent API\n * into a Record of AI SDK tool() instances ready for streamText/generateText.\n */\nexport function convertTools(\n tools: OpenAITool[],\n baseUrl: string,\n apiKey: string\n): Record<string, import(\"ai\").Tool> {\n const result: Record<string, import(\"ai\").Tool> = {};\n\n for (const t of tools) {\n const { name, description, parameters } = t.function;\n const metadata = t.metadata;\n\n result[name] = tool({\n description,\n inputSchema: jsonSchema(parameters as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => createExecutor(metadata, baseUrl, apiKey)(args as Record<string, unknown>),\n });\n }\n\n return result;\n}\n\n/**\n * Filter tools by provider, service, or tool name.\n */\nexport function filterTools(\n tools: OpenAITool[],\n options: { only?: string[]; except?: string[] }\n): OpenAITool[] {\n let filtered = tools;\n\n if (options.only && options.only.length > 0) {\n const onlySet = new Set(options.only.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return onlySet.has(name) || onlySet.has(provider) || onlySet.has(service);\n });\n }\n\n if (options.except && options.except.length > 0) {\n const exceptSet = new Set(options.except.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return (\n !exceptSet.has(name) &&\n !exceptSet.has(provider) &&\n !exceptSet.has(service)\n );\n });\n }\n\n return filtered;\n}\n","import type { ToolMetadata } from \"./types.js\";\n\n/**\n * Creates an execute function for a tool that calls the supyagent API.\n *\n * Handles:\n * - Path parameter substitution (e.g., {messageId} in /api/v1/gmail/messages/{messageId})\n * - GET/DELETE: remaining args → query string\n * - POST/PUT/PATCH: merge bodyDefaults + remaining args → JSON body\n */\nexport function createExecutor(\n metadata: ToolMetadata,\n baseUrl: string,\n apiKey: string\n): (args: Record<string, unknown>) => Promise<unknown> {\n return async (args: Record<string, unknown>) => {\n const { method, path, bodyDefaults } = metadata;\n\n // Extract path parameters and substitute into the path\n const remainingArgs = { ...args };\n let resolvedPath = path;\n\n const pathParams = path.match(/\\{(\\w+)\\}/g);\n if (pathParams) {\n for (const param of pathParams) {\n const paramName = param.slice(1, -1);\n if (paramName in remainingArgs) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs[paramName]))\n );\n delete remainingArgs[paramName];\n }\n }\n\n // Fallback: LLMs often send { id } instead of { messageId }, { eventId }, etc.\n // because list responses return objects with an `id` field.\n // For any unresolved {fooId} param, try `id` from the args (once only).\n if (\"id\" in remainingArgs && resolvedPath.includes(\"{\")) {\n const unresolved = resolvedPath.match(/\\{(\\w+)\\}/g);\n if (unresolved) {\n for (const param of unresolved) {\n const paramName = param.slice(1, -1);\n if (paramName.toLowerCase().endsWith(\"id\")) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs.id))\n );\n delete remainingArgs.id;\n break; // consume `id` only once\n }\n }\n }\n }\n }\n\n let url = `${baseUrl}${resolvedPath}`;\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (method === \"GET\" || method === \"DELETE\") {\n // Remaining args go as query parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(remainingArgs)) {\n if (value !== undefined && value !== null) {\n params.set(key, String(value));\n }\n }\n const qs = params.toString();\n if (qs) url += `?${qs}`;\n } else {\n // POST/PUT/PATCH: merge bodyDefaults with remaining args\n const body = { ...bodyDefaults, ...remainingArgs };\n fetchOptions.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n return data;\n };\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\n\nconst DEFAULT_BASE_URL = \"https://app.supyagent.com\";\nconst CACHE_KEY = \"tools\";\n\n/**\n * Create a supyagent client for fetching and converting tools.\n *\n * @example\n * ```ts\n * import { supyagent } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const tools = await client.tools({ cache: 300 });\n * ```\n */\nexport function supyagent(options: SupyagentOptions): SupyagentClient {\n const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;\n const cache = new TTLCache<ToolsResponse>();\n\n return {\n async tools(filterOptions?: ToolFilterOptions) {\n const cacheTTL = resolveCacheTTL(filterOptions?.cache);\n\n // Check cache\n let response = cacheTTL > 0 ? cache.get(CACHE_KEY) : undefined;\n\n if (!response) {\n // Fetch from API\n const res = await fetch(`${baseUrl}/api/v1/tools`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(\n `Supyagent API error (${res.status}): ${error}`\n );\n }\n\n const json = await res.json();\n // API wraps response in { ok, data: { tools, base_url, total } }\n response = (json.data ?? json) as ToolsResponse;\n\n // Store in cache if TTL > 0\n if (cacheTTL > 0) {\n cache.set(CACHE_KEY, response, cacheTTL);\n }\n }\n\n // Use API-reported base_url for tool execution\n const toolBaseUrl = response.base_url || baseUrl;\n\n // Guard against non-array tools (e.g., API returns empty or unexpected shape)\n const toolsArray = Array.isArray(response.tools) ? response.tools : [];\n\n // Filter\n const filtered = filterTools(toolsArray, {\n only: filterOptions?.only,\n except: filterOptions?.except,\n });\n\n // Convert to AI SDK tools\n return convertTools(filtered, toolBaseUrl, apiKey);\n },\n };\n}\n\nfunction resolveCacheTTL(cache?: boolean | number): number {\n if (cache === true) return 60;\n if (typeof cache === \"number\") return cache;\n return 0;\n}\n","import { tool, jsonSchema } from \"ai\";\nimport { exec, type ExecException } from \"node:child_process\";\n\nconst MAX_OUTPUT = 30_000;\nconst DEFAULT_TIMEOUT = 30_000; // 30 seconds\n\n// ── Environment sanitization ────────────────────────────────────────────────\n\nconst SECRET_PATTERNS = [\n /key/i, /secret/i, /token/i, /password/i, /credential/i,\n /auth/i, /private/i, /api_key/i, /apikey/i,\n /^AWS_/i, /^AZURE_/i, /^GCP_/i, /^GITHUB_/i,\n /^DATABASE_URL$/i, /^ENCRYPTION_/i, /^SUPABASE_/i,\n];\n\nconst SAFE_VARS = new Set([\n \"PATH\", \"HOME\", \"USER\", \"SHELL\", \"LANG\", \"LC_ALL\", \"LC_CTYPE\",\n \"TERM\", \"EDITOR\", \"VISUAL\", \"TMPDIR\", \"TMP\", \"TEMP\",\n \"NODE_ENV\", \"NODE_PATH\", \"NODE_OPTIONS\",\n \"NPM_CONFIG_PREFIX\", \"NVM_DIR\", \"NVM_BIN\", \"NVM_INC\",\n \"XDG_CONFIG_HOME\", \"XDG_DATA_HOME\", \"XDG_CACHE_HOME\",\n \"COLORTERM\", \"FORCE_COLOR\", \"NO_COLOR\",\n \"COLUMNS\", \"LINES\", \"SHLVL\", \"PWD\", \"OLDPWD\", \"LOGNAME\",\n \"HOSTNAME\", \"HOSTTYPE\", \"OSTYPE\", \"MACHTYPE\",\n]);\n\nfunction sanitizeEnv(\n env: NodeJS.ProcessEnv,\n opts?: { include?: string[]; exclude?: string[] }\n): Record<string, string> {\n const result: Record<string, string> = {};\n const includeSet = opts?.include ? new Set(opts.include) : null;\n const excludeSet = opts?.exclude ? new Set(opts.exclude) : null;\n\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n if (excludeSet?.has(key)) continue;\n if (includeSet?.has(key)) { result[key] = value; continue; }\n if (SAFE_VARS.has(key)) { result[key] = value; continue; }\n if (SECRET_PATTERNS.some(p => p.test(key))) continue;\n result[key] = value;\n }\n\n result.TERM = \"dumb\";\n return result;\n}\n\nfunction buildEnv(\n policy: BashToolOptions[\"env\"],\n opts?: BashToolOptions\n): Record<string, string> {\n if (typeof policy === \"object\" && policy !== null) {\n return { ...policy, TERM: \"dumb\" };\n }\n switch (policy) {\n case \"inherit\":\n return { ...process.env, TERM: \"dumb\" } as Record<string, string>;\n case \"none\":\n return {\n PATH: process.env.PATH ?? \"/usr/local/bin:/usr/bin:/bin\",\n HOME: process.env.HOME ?? \"/tmp\",\n TERM: \"dumb\",\n };\n case \"safe\":\n default:\n return sanitizeEnv(process.env, {\n include: opts?.envInclude,\n exclude: opts?.envExclude,\n });\n }\n}\n\n// ── Command safety ──────────────────────────────────────────────────────────\n\ntype CommandSafety = \"safe\" | \"dangerous\" | \"unknown\";\n\nconst SAFE_COMMANDS = new Set([\n \"cat\", \"cd\", \"cut\", \"echo\", \"expr\", \"false\", \"grep\", \"egrep\", \"fgrep\",\n \"head\", \"id\", \"ls\", \"nl\", \"paste\", \"pwd\", \"rev\", \"seq\", \"stat\",\n \"tail\", \"tr\", \"true\", \"uname\", \"uniq\", \"wc\", \"which\", \"whoami\",\n \"date\", \"env\", \"printenv\", \"hostname\", \"df\", \"du\", \"file\",\n \"find\", \"rg\", \"tree\", \"less\", \"more\", \"sort\",\n \"diff\", \"md5sum\", \"sha256sum\", \"base64\", \"xxd\", \"hexdump\",\n \"man\", \"help\", \"type\", \"command\", \"test\", \"basename\", \"dirname\",\n \"realpath\", \"readlink\", \"tee\", \"xargs\", \"jq\", \"yq\",\n]);\n\nconst SAFE_GIT_SUBCOMMANDS = new Set([\n \"status\", \"log\", \"diff\", \"show\", \"branch\", \"tag\", \"remote\",\n \"stash\", \"describe\", \"shortlog\", \"blame\", \"reflog\", \"rev-parse\",\n \"ls-files\", \"ls-tree\", \"cat-file\", \"rev-list\", \"name-rev\",\n]);\n\nconst DANGEROUS_PATTERNS = [\n /rm\\s+(-[^\\s]*f|-[^\\s]*r|--force|--recursive).*\\//,\n /:\\(\\)\\s*\\{\\s*:\\|:&\\s*\\};:/,\n />\\s*\\/dev\\/sd/,\n /mkfs\\./,\n /dd\\s+.*of=\\/dev\\//,\n /shutdown|reboot|halt|poweroff/,\n /chmod\\s+777\\s+\\//,\n /curl.*\\|\\s*(ba)?sh/,\n /wget.*\\|\\s*(ba)?sh/,\n];\n\nfunction classifyCommand(\n command: string,\n extraSafe?: Set<string>,\n extraDeny?: RegExp[],\n): CommandSafety {\n const trimmed = command.trim();\n\n // Check dangerous patterns first\n if (DANGEROUS_PATTERNS.some(p => p.test(trimmed))) return \"dangerous\";\n if (extraDeny?.some(p => p.test(trimmed))) return \"dangerous\";\n\n // Extract base command (strip leading env var assignments)\n const stripped = trimmed.replace(/^(\\w+=\\S+\\s+)*/, \"\");\n const parts = stripped.split(/\\s+/);\n const base = parts[0]?.replace(/^.*\\//, \"\"); // strip path prefix\n\n if (!base) return \"unknown\";\n\n // Check git subcommands\n if (base === \"git\") {\n const sub = parts[1];\n if (sub && SAFE_GIT_SUBCOMMANDS.has(sub)) return \"safe\";\n return \"unknown\";\n }\n\n if (SAFE_COMMANDS.has(base)) return \"safe\";\n if (extraSafe?.has(base)) return \"safe\";\n\n return \"unknown\";\n}\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface BashToolOptions {\n /** Working directory for commands. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Command timeout in milliseconds. Defaults to 30000 (30s). Max 600000 (10m). */\n timeout?: number;\n /** Maximum output length in characters. Defaults to 30000. */\n maxOutput?: number;\n\n /**\n * Command approval policy. Default: \"auto\".\n * - `\"auto\"`: Safe read-only commands (ls, cat, grep...) auto-execute; others require user approval\n * - `\"always\"`: Every command requires user approval before execution\n * - `\"never\"`: No approval required (use only in trusted environments)\n * - `function`: Custom approval logic — return `true` to require approval\n */\n approval?: \"auto\" | \"always\" | \"never\" | ((command: string) => boolean | Promise<boolean>);\n\n /**\n * Environment variable policy. Default: \"safe\".\n * - `\"safe\"`: Strip vars matching secret patterns (KEY, TOKEN, SECRET, PASSWORD, etc.)\n * - `\"inherit\"`: Pass full process.env (NOT recommended for production)\n * - `\"none\"`: Minimal env (PATH, HOME, TERM only)\n * - `Record<string,string>`: Explicit env vars to use\n */\n env?: \"safe\" | \"inherit\" | \"none\" | Record<string, string>;\n\n /** Extra env vars to include even if they match secret patterns. Only used with `env: \"safe\"`. */\n envInclude?: string[];\n /** Extra env vars to exclude beyond default secret patterns. Only used with `env: \"safe\"`. */\n envExclude?: string[];\n\n /** Additional commands to treat as safe (auto-execute without approval in \"auto\" mode). */\n allowCommands?: string[];\n /** Additional patterns to block (never execute, even if approved). */\n denyPatterns?: RegExp[];\n}\n\nexport interface BashToolResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut?: boolean;\n}\n\n// ── Tool factory ────────────────────────────────────────────────────────────\n\n/**\n * Create a bash tool that executes shell commands on the server.\n *\n * By default, uses \"auto\" approval (safe commands run immediately, others\n * require user approval) and \"safe\" env isolation (strips secret-like\n * environment variables).\n *\n * @example\n * ```ts\n * import { supyagent, createBashTool } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const supyagentTools = await client.tools({ cache: 300 });\n *\n * const tools = {\n * ...supyagentTools,\n * bash: createBashTool({ cwd: '/path/to/project' }),\n * };\n * ```\n */\nexport function createBashTool(options?: BashToolOptions) {\n const cwd = options?.cwd ?? process.cwd();\n const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 600_000);\n const maxOutput = options?.maxOutput ?? MAX_OUTPUT;\n const approvalPolicy = options?.approval ?? \"auto\";\n\n const extraSafe = options?.allowCommands ? new Set(options.allowCommands) : undefined;\n const extraDeny = options?.denyPatterns;\n\n const execEnv = buildEnv(options?.env ?? \"safe\", options);\n\n const schema = {\n type: \"object\" as const,\n properties: {\n command: {\n type: \"string\",\n description: \"The bash command to execute\",\n },\n },\n required: [\"command\"],\n };\n\n // Resolve needsApproval based on policy\n const needsApproval =\n approvalPolicy === \"always\"\n ? true\n : approvalPolicy === \"never\"\n ? false\n : async (args: unknown) => {\n const { command } = args as { command: string };\n if (typeof approvalPolicy === \"function\") {\n return approvalPolicy(command);\n }\n // \"auto\" mode — safe commands pass, everything else needs approval\n const safety = classifyCommand(command, extraSafe, extraDeny);\n return safety !== \"safe\";\n };\n\n return tool({\n description:\n \"Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.\",\n inputSchema: jsonSchema(schema as Parameters<typeof jsonSchema>[0]),\n needsApproval,\n execute: async (args): Promise<BashToolResult> => {\n const { command } = args as { command: string };\n\n // Block dangerous commands even if somehow approved\n if (\n DANGEROUS_PATTERNS.some(p => p.test(command)) ||\n extraDeny?.some(p => p.test(command))\n ) {\n return {\n stdout: \"\",\n stderr: \"Command blocked by safety policy\",\n exitCode: 126,\n durationMs: 0,\n };\n }\n\n const start = Date.now();\n\n return new Promise<BashToolResult>((resolve) => {\n const child = exec(\n command,\n {\n cwd,\n timeout,\n shell: \"/bin/bash\",\n maxBuffer: 10 * 1024 * 1024,\n env: execEnv,\n },\n (error: ExecException | null, stdout: string, stderr: string) => {\n const durationMs = Date.now() - start;\n const timedOut = error?.killed === true;\n const exitCode = timedOut\n ? 124\n : typeof error?.code === \"number\"\n ? error.code\n : error\n ? 1\n : 0;\n\n resolve({\n stdout: truncate(stdout, maxOutput),\n stderr: truncate(stderr, maxOutput),\n exitCode,\n durationMs,\n ...(timedOut ? { timedOut: true } : {}),\n });\n }\n );\n\n child.on(\"error\", () => {\n resolve({\n stdout: \"\",\n stderr: \"Failed to start process\",\n exitCode: 127,\n durationMs: Date.now() - start,\n });\n });\n });\n },\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor(max / 2) - 50;\n return (\n str.slice(0, half) +\n `\\n\\n... (${str.length - max} characters truncated) ...\\n\\n` +\n str.slice(-half)\n );\n}\n"],"mappings":"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","names":["import_ai"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -70,6 +70,30 @@ interface BashToolOptions {
|
|
|
70
70
|
timeout?: number;
|
|
71
71
|
/** Maximum output length in characters. Defaults to 30000. */
|
|
72
72
|
maxOutput?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Command approval policy. Default: "auto".
|
|
75
|
+
* - `"auto"`: Safe read-only commands (ls, cat, grep...) auto-execute; others require user approval
|
|
76
|
+
* - `"always"`: Every command requires user approval before execution
|
|
77
|
+
* - `"never"`: No approval required (use only in trusted environments)
|
|
78
|
+
* - `function`: Custom approval logic — return `true` to require approval
|
|
79
|
+
*/
|
|
80
|
+
approval?: "auto" | "always" | "never" | ((command: string) => boolean | Promise<boolean>);
|
|
81
|
+
/**
|
|
82
|
+
* Environment variable policy. Default: "safe".
|
|
83
|
+
* - `"safe"`: Strip vars matching secret patterns (KEY, TOKEN, SECRET, PASSWORD, etc.)
|
|
84
|
+
* - `"inherit"`: Pass full process.env (NOT recommended for production)
|
|
85
|
+
* - `"none"`: Minimal env (PATH, HOME, TERM only)
|
|
86
|
+
* - `Record<string,string>`: Explicit env vars to use
|
|
87
|
+
*/
|
|
88
|
+
env?: "safe" | "inherit" | "none" | Record<string, string>;
|
|
89
|
+
/** Extra env vars to include even if they match secret patterns. Only used with `env: "safe"`. */
|
|
90
|
+
envInclude?: string[];
|
|
91
|
+
/** Extra env vars to exclude beyond default secret patterns. Only used with `env: "safe"`. */
|
|
92
|
+
envExclude?: string[];
|
|
93
|
+
/** Additional commands to treat as safe (auto-execute without approval in "auto" mode). */
|
|
94
|
+
allowCommands?: string[];
|
|
95
|
+
/** Additional patterns to block (never execute, even if approved). */
|
|
96
|
+
denyPatterns?: RegExp[];
|
|
73
97
|
}
|
|
74
98
|
interface BashToolResult {
|
|
75
99
|
stdout: string;
|
|
@@ -81,6 +105,10 @@ interface BashToolResult {
|
|
|
81
105
|
/**
|
|
82
106
|
* Create a bash tool that executes shell commands on the server.
|
|
83
107
|
*
|
|
108
|
+
* By default, uses "auto" approval (safe commands run immediately, others
|
|
109
|
+
* require user approval) and "safe" env isolation (strips secret-like
|
|
110
|
+
* environment variables).
|
|
111
|
+
*
|
|
84
112
|
* @example
|
|
85
113
|
* ```ts
|
|
86
114
|
* import { supyagent, createBashTool } from '@supyagent/sdk';
|
package/dist/index.d.ts
CHANGED
|
@@ -70,6 +70,30 @@ interface BashToolOptions {
|
|
|
70
70
|
timeout?: number;
|
|
71
71
|
/** Maximum output length in characters. Defaults to 30000. */
|
|
72
72
|
maxOutput?: number;
|
|
73
|
+
/**
|
|
74
|
+
* Command approval policy. Default: "auto".
|
|
75
|
+
* - `"auto"`: Safe read-only commands (ls, cat, grep...) auto-execute; others require user approval
|
|
76
|
+
* - `"always"`: Every command requires user approval before execution
|
|
77
|
+
* - `"never"`: No approval required (use only in trusted environments)
|
|
78
|
+
* - `function`: Custom approval logic — return `true` to require approval
|
|
79
|
+
*/
|
|
80
|
+
approval?: "auto" | "always" | "never" | ((command: string) => boolean | Promise<boolean>);
|
|
81
|
+
/**
|
|
82
|
+
* Environment variable policy. Default: "safe".
|
|
83
|
+
* - `"safe"`: Strip vars matching secret patterns (KEY, TOKEN, SECRET, PASSWORD, etc.)
|
|
84
|
+
* - `"inherit"`: Pass full process.env (NOT recommended for production)
|
|
85
|
+
* - `"none"`: Minimal env (PATH, HOME, TERM only)
|
|
86
|
+
* - `Record<string,string>`: Explicit env vars to use
|
|
87
|
+
*/
|
|
88
|
+
env?: "safe" | "inherit" | "none" | Record<string, string>;
|
|
89
|
+
/** Extra env vars to include even if they match secret patterns. Only used with `env: "safe"`. */
|
|
90
|
+
envInclude?: string[];
|
|
91
|
+
/** Extra env vars to exclude beyond default secret patterns. Only used with `env: "safe"`. */
|
|
92
|
+
envExclude?: string[];
|
|
93
|
+
/** Additional commands to treat as safe (auto-execute without approval in "auto" mode). */
|
|
94
|
+
allowCommands?: string[];
|
|
95
|
+
/** Additional patterns to block (never execute, even if approved). */
|
|
96
|
+
denyPatterns?: RegExp[];
|
|
73
97
|
}
|
|
74
98
|
interface BashToolResult {
|
|
75
99
|
stdout: string;
|
|
@@ -81,6 +105,10 @@ interface BashToolResult {
|
|
|
81
105
|
/**
|
|
82
106
|
* Create a bash tool that executes shell commands on the server.
|
|
83
107
|
*
|
|
108
|
+
* By default, uses "auto" approval (safe commands run immediately, others
|
|
109
|
+
* require user approval) and "safe" env isolation (strips secret-like
|
|
110
|
+
* environment variables).
|
|
111
|
+
*
|
|
84
112
|
* @example
|
|
85
113
|
* ```ts
|
|
86
114
|
* import { supyagent, createBashTool } from '@supyagent/sdk';
|
package/dist/index.js
CHANGED
|
@@ -173,10 +173,220 @@ import { tool as tool2, jsonSchema as jsonSchema2 } from "ai";
|
|
|
173
173
|
import { exec } from "child_process";
|
|
174
174
|
var MAX_OUTPUT = 3e4;
|
|
175
175
|
var DEFAULT_TIMEOUT = 3e4;
|
|
176
|
+
var SECRET_PATTERNS = [
|
|
177
|
+
/key/i,
|
|
178
|
+
/secret/i,
|
|
179
|
+
/token/i,
|
|
180
|
+
/password/i,
|
|
181
|
+
/credential/i,
|
|
182
|
+
/auth/i,
|
|
183
|
+
/private/i,
|
|
184
|
+
/api_key/i,
|
|
185
|
+
/apikey/i,
|
|
186
|
+
/^AWS_/i,
|
|
187
|
+
/^AZURE_/i,
|
|
188
|
+
/^GCP_/i,
|
|
189
|
+
/^GITHUB_/i,
|
|
190
|
+
/^DATABASE_URL$/i,
|
|
191
|
+
/^ENCRYPTION_/i,
|
|
192
|
+
/^SUPABASE_/i
|
|
193
|
+
];
|
|
194
|
+
var SAFE_VARS = /* @__PURE__ */ new Set([
|
|
195
|
+
"PATH",
|
|
196
|
+
"HOME",
|
|
197
|
+
"USER",
|
|
198
|
+
"SHELL",
|
|
199
|
+
"LANG",
|
|
200
|
+
"LC_ALL",
|
|
201
|
+
"LC_CTYPE",
|
|
202
|
+
"TERM",
|
|
203
|
+
"EDITOR",
|
|
204
|
+
"VISUAL",
|
|
205
|
+
"TMPDIR",
|
|
206
|
+
"TMP",
|
|
207
|
+
"TEMP",
|
|
208
|
+
"NODE_ENV",
|
|
209
|
+
"NODE_PATH",
|
|
210
|
+
"NODE_OPTIONS",
|
|
211
|
+
"NPM_CONFIG_PREFIX",
|
|
212
|
+
"NVM_DIR",
|
|
213
|
+
"NVM_BIN",
|
|
214
|
+
"NVM_INC",
|
|
215
|
+
"XDG_CONFIG_HOME",
|
|
216
|
+
"XDG_DATA_HOME",
|
|
217
|
+
"XDG_CACHE_HOME",
|
|
218
|
+
"COLORTERM",
|
|
219
|
+
"FORCE_COLOR",
|
|
220
|
+
"NO_COLOR",
|
|
221
|
+
"COLUMNS",
|
|
222
|
+
"LINES",
|
|
223
|
+
"SHLVL",
|
|
224
|
+
"PWD",
|
|
225
|
+
"OLDPWD",
|
|
226
|
+
"LOGNAME",
|
|
227
|
+
"HOSTNAME",
|
|
228
|
+
"HOSTTYPE",
|
|
229
|
+
"OSTYPE",
|
|
230
|
+
"MACHTYPE"
|
|
231
|
+
]);
|
|
232
|
+
function sanitizeEnv(env, opts) {
|
|
233
|
+
const result = {};
|
|
234
|
+
const includeSet = opts?.include ? new Set(opts.include) : null;
|
|
235
|
+
const excludeSet = opts?.exclude ? new Set(opts.exclude) : null;
|
|
236
|
+
for (const [key, value] of Object.entries(env)) {
|
|
237
|
+
if (value === void 0) continue;
|
|
238
|
+
if (excludeSet?.has(key)) continue;
|
|
239
|
+
if (includeSet?.has(key)) {
|
|
240
|
+
result[key] = value;
|
|
241
|
+
continue;
|
|
242
|
+
}
|
|
243
|
+
if (SAFE_VARS.has(key)) {
|
|
244
|
+
result[key] = value;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
if (SECRET_PATTERNS.some((p) => p.test(key))) continue;
|
|
248
|
+
result[key] = value;
|
|
249
|
+
}
|
|
250
|
+
result.TERM = "dumb";
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
function buildEnv(policy, opts) {
|
|
254
|
+
if (typeof policy === "object" && policy !== null) {
|
|
255
|
+
return { ...policy, TERM: "dumb" };
|
|
256
|
+
}
|
|
257
|
+
switch (policy) {
|
|
258
|
+
case "inherit":
|
|
259
|
+
return { ...process.env, TERM: "dumb" };
|
|
260
|
+
case "none":
|
|
261
|
+
return {
|
|
262
|
+
PATH: process.env.PATH ?? "/usr/local/bin:/usr/bin:/bin",
|
|
263
|
+
HOME: process.env.HOME ?? "/tmp",
|
|
264
|
+
TERM: "dumb"
|
|
265
|
+
};
|
|
266
|
+
case "safe":
|
|
267
|
+
default:
|
|
268
|
+
return sanitizeEnv(process.env, {
|
|
269
|
+
include: opts?.envInclude,
|
|
270
|
+
exclude: opts?.envExclude
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
var SAFE_COMMANDS = /* @__PURE__ */ new Set([
|
|
275
|
+
"cat",
|
|
276
|
+
"cd",
|
|
277
|
+
"cut",
|
|
278
|
+
"echo",
|
|
279
|
+
"expr",
|
|
280
|
+
"false",
|
|
281
|
+
"grep",
|
|
282
|
+
"egrep",
|
|
283
|
+
"fgrep",
|
|
284
|
+
"head",
|
|
285
|
+
"id",
|
|
286
|
+
"ls",
|
|
287
|
+
"nl",
|
|
288
|
+
"paste",
|
|
289
|
+
"pwd",
|
|
290
|
+
"rev",
|
|
291
|
+
"seq",
|
|
292
|
+
"stat",
|
|
293
|
+
"tail",
|
|
294
|
+
"tr",
|
|
295
|
+
"true",
|
|
296
|
+
"uname",
|
|
297
|
+
"uniq",
|
|
298
|
+
"wc",
|
|
299
|
+
"which",
|
|
300
|
+
"whoami",
|
|
301
|
+
"date",
|
|
302
|
+
"env",
|
|
303
|
+
"printenv",
|
|
304
|
+
"hostname",
|
|
305
|
+
"df",
|
|
306
|
+
"du",
|
|
307
|
+
"file",
|
|
308
|
+
"find",
|
|
309
|
+
"rg",
|
|
310
|
+
"tree",
|
|
311
|
+
"less",
|
|
312
|
+
"more",
|
|
313
|
+
"sort",
|
|
314
|
+
"diff",
|
|
315
|
+
"md5sum",
|
|
316
|
+
"sha256sum",
|
|
317
|
+
"base64",
|
|
318
|
+
"xxd",
|
|
319
|
+
"hexdump",
|
|
320
|
+
"man",
|
|
321
|
+
"help",
|
|
322
|
+
"type",
|
|
323
|
+
"command",
|
|
324
|
+
"test",
|
|
325
|
+
"basename",
|
|
326
|
+
"dirname",
|
|
327
|
+
"realpath",
|
|
328
|
+
"readlink",
|
|
329
|
+
"tee",
|
|
330
|
+
"xargs",
|
|
331
|
+
"jq",
|
|
332
|
+
"yq"
|
|
333
|
+
]);
|
|
334
|
+
var SAFE_GIT_SUBCOMMANDS = /* @__PURE__ */ new Set([
|
|
335
|
+
"status",
|
|
336
|
+
"log",
|
|
337
|
+
"diff",
|
|
338
|
+
"show",
|
|
339
|
+
"branch",
|
|
340
|
+
"tag",
|
|
341
|
+
"remote",
|
|
342
|
+
"stash",
|
|
343
|
+
"describe",
|
|
344
|
+
"shortlog",
|
|
345
|
+
"blame",
|
|
346
|
+
"reflog",
|
|
347
|
+
"rev-parse",
|
|
348
|
+
"ls-files",
|
|
349
|
+
"ls-tree",
|
|
350
|
+
"cat-file",
|
|
351
|
+
"rev-list",
|
|
352
|
+
"name-rev"
|
|
353
|
+
]);
|
|
354
|
+
var DANGEROUS_PATTERNS = [
|
|
355
|
+
/rm\s+(-[^\s]*f|-[^\s]*r|--force|--recursive).*\//,
|
|
356
|
+
/:\(\)\s*\{\s*:\|:&\s*\};:/,
|
|
357
|
+
/>\s*\/dev\/sd/,
|
|
358
|
+
/mkfs\./,
|
|
359
|
+
/dd\s+.*of=\/dev\//,
|
|
360
|
+
/shutdown|reboot|halt|poweroff/,
|
|
361
|
+
/chmod\s+777\s+\//,
|
|
362
|
+
/curl.*\|\s*(ba)?sh/,
|
|
363
|
+
/wget.*\|\s*(ba)?sh/
|
|
364
|
+
];
|
|
365
|
+
function classifyCommand(command, extraSafe, extraDeny) {
|
|
366
|
+
const trimmed = command.trim();
|
|
367
|
+
if (DANGEROUS_PATTERNS.some((p) => p.test(trimmed))) return "dangerous";
|
|
368
|
+
if (extraDeny?.some((p) => p.test(trimmed))) return "dangerous";
|
|
369
|
+
const stripped = trimmed.replace(/^(\w+=\S+\s+)*/, "");
|
|
370
|
+
const parts = stripped.split(/\s+/);
|
|
371
|
+
const base = parts[0]?.replace(/^.*\//, "");
|
|
372
|
+
if (!base) return "unknown";
|
|
373
|
+
if (base === "git") {
|
|
374
|
+
const sub = parts[1];
|
|
375
|
+
if (sub && SAFE_GIT_SUBCOMMANDS.has(sub)) return "safe";
|
|
376
|
+
return "unknown";
|
|
377
|
+
}
|
|
378
|
+
if (SAFE_COMMANDS.has(base)) return "safe";
|
|
379
|
+
if (extraSafe?.has(base)) return "safe";
|
|
380
|
+
return "unknown";
|
|
381
|
+
}
|
|
176
382
|
function createBashTool(options) {
|
|
177
383
|
const cwd = options?.cwd ?? process.cwd();
|
|
178
384
|
const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 6e5);
|
|
179
385
|
const maxOutput = options?.maxOutput ?? MAX_OUTPUT;
|
|
386
|
+
const approvalPolicy = options?.approval ?? "auto";
|
|
387
|
+
const extraSafe = options?.allowCommands ? new Set(options.allowCommands) : void 0;
|
|
388
|
+
const extraDeny = options?.denyPatterns;
|
|
389
|
+
const execEnv = buildEnv(options?.env ?? "safe", options);
|
|
180
390
|
const schema = {
|
|
181
391
|
type: "object",
|
|
182
392
|
properties: {
|
|
@@ -187,11 +397,28 @@ function createBashTool(options) {
|
|
|
187
397
|
},
|
|
188
398
|
required: ["command"]
|
|
189
399
|
};
|
|
400
|
+
const needsApproval = approvalPolicy === "always" ? true : approvalPolicy === "never" ? false : async (args) => {
|
|
401
|
+
const { command } = args;
|
|
402
|
+
if (typeof approvalPolicy === "function") {
|
|
403
|
+
return approvalPolicy(command);
|
|
404
|
+
}
|
|
405
|
+
const safety = classifyCommand(command, extraSafe, extraDeny);
|
|
406
|
+
return safety !== "safe";
|
|
407
|
+
};
|
|
190
408
|
return tool2({
|
|
191
409
|
description: "Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.",
|
|
192
410
|
inputSchema: jsonSchema2(schema),
|
|
411
|
+
needsApproval,
|
|
193
412
|
execute: async (args) => {
|
|
194
413
|
const { command } = args;
|
|
414
|
+
if (DANGEROUS_PATTERNS.some((p) => p.test(command)) || extraDeny?.some((p) => p.test(command))) {
|
|
415
|
+
return {
|
|
416
|
+
stdout: "",
|
|
417
|
+
stderr: "Command blocked by safety policy",
|
|
418
|
+
exitCode: 126,
|
|
419
|
+
durationMs: 0
|
|
420
|
+
};
|
|
421
|
+
}
|
|
195
422
|
const start = Date.now();
|
|
196
423
|
return new Promise((resolve) => {
|
|
197
424
|
const child = exec(
|
|
@@ -201,8 +428,7 @@ function createBashTool(options) {
|
|
|
201
428
|
timeout,
|
|
202
429
|
shell: "/bin/bash",
|
|
203
430
|
maxBuffer: 10 * 1024 * 1024,
|
|
204
|
-
|
|
205
|
-
env: { ...process.env, TERM: "dumb" }
|
|
431
|
+
env: execEnv
|
|
206
432
|
},
|
|
207
433
|
(error, stdout, stderr) => {
|
|
208
434
|
const durationMs = Date.now() - start;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/client.ts","../src/tools/bash.ts"],"sourcesContent":["interface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nexport class TTLCache<T> {\n private store = new Map<string, CacheEntry<T>>();\n\n get(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.data;\n }\n\n set(key: string, data: T, ttlSeconds: number): void {\n this.store.set(key, {\n data,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { OpenAITool } from \"./types.js\";\nimport { createExecutor } from \"./http-executor.js\";\n\n/**\n * Converts an array of OpenAI-format tools from the supyagent API\n * into a Record of AI SDK tool() instances ready for streamText/generateText.\n */\nexport function convertTools(\n tools: OpenAITool[],\n baseUrl: string,\n apiKey: string\n): Record<string, import(\"ai\").Tool> {\n const result: Record<string, import(\"ai\").Tool> = {};\n\n for (const t of tools) {\n const { name, description, parameters } = t.function;\n const metadata = t.metadata;\n\n result[name] = tool({\n description,\n inputSchema: jsonSchema(parameters as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => createExecutor(metadata, baseUrl, apiKey)(args as Record<string, unknown>),\n });\n }\n\n return result;\n}\n\n/**\n * Filter tools by provider, service, or tool name.\n */\nexport function filterTools(\n tools: OpenAITool[],\n options: { only?: string[]; except?: string[] }\n): OpenAITool[] {\n let filtered = tools;\n\n if (options.only && options.only.length > 0) {\n const onlySet = new Set(options.only.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return onlySet.has(name) || onlySet.has(provider) || onlySet.has(service);\n });\n }\n\n if (options.except && options.except.length > 0) {\n const exceptSet = new Set(options.except.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return (\n !exceptSet.has(name) &&\n !exceptSet.has(provider) &&\n !exceptSet.has(service)\n );\n });\n }\n\n return filtered;\n}\n","import type { ToolMetadata } from \"./types.js\";\n\n/**\n * Creates an execute function for a tool that calls the supyagent API.\n *\n * Handles:\n * - Path parameter substitution (e.g., {messageId} in /api/v1/gmail/messages/{messageId})\n * - GET/DELETE: remaining args → query string\n * - POST/PUT/PATCH: merge bodyDefaults + remaining args → JSON body\n */\nexport function createExecutor(\n metadata: ToolMetadata,\n baseUrl: string,\n apiKey: string\n): (args: Record<string, unknown>) => Promise<unknown> {\n return async (args: Record<string, unknown>) => {\n const { method, path, bodyDefaults } = metadata;\n\n // Extract path parameters and substitute into the path\n const remainingArgs = { ...args };\n let resolvedPath = path;\n\n const pathParams = path.match(/\\{(\\w+)\\}/g);\n if (pathParams) {\n for (const param of pathParams) {\n const paramName = param.slice(1, -1);\n if (paramName in remainingArgs) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs[paramName]))\n );\n delete remainingArgs[paramName];\n }\n }\n\n // Fallback: LLMs often send { id } instead of { messageId }, { eventId }, etc.\n // because list responses return objects with an `id` field.\n // For any unresolved {fooId} param, try `id` from the args (once only).\n if (\"id\" in remainingArgs && resolvedPath.includes(\"{\")) {\n const unresolved = resolvedPath.match(/\\{(\\w+)\\}/g);\n if (unresolved) {\n for (const param of unresolved) {\n const paramName = param.slice(1, -1);\n if (paramName.toLowerCase().endsWith(\"id\")) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs.id))\n );\n delete remainingArgs.id;\n break; // consume `id` only once\n }\n }\n }\n }\n }\n\n let url = `${baseUrl}${resolvedPath}`;\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (method === \"GET\" || method === \"DELETE\") {\n // Remaining args go as query parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(remainingArgs)) {\n if (value !== undefined && value !== null) {\n params.set(key, String(value));\n }\n }\n const qs = params.toString();\n if (qs) url += `?${qs}`;\n } else {\n // POST/PUT/PATCH: merge bodyDefaults with remaining args\n const body = { ...bodyDefaults, ...remainingArgs };\n fetchOptions.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n return data;\n };\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\n\nconst DEFAULT_BASE_URL = \"https://app.supyagent.com\";\nconst CACHE_KEY = \"tools\";\n\n/**\n * Create a supyagent client for fetching and converting tools.\n *\n * @example\n * ```ts\n * import { supyagent } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const tools = await client.tools({ cache: 300 });\n * ```\n */\nexport function supyagent(options: SupyagentOptions): SupyagentClient {\n const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;\n const cache = new TTLCache<ToolsResponse>();\n\n return {\n async tools(filterOptions?: ToolFilterOptions) {\n const cacheTTL = resolveCacheTTL(filterOptions?.cache);\n\n // Check cache\n let response = cacheTTL > 0 ? cache.get(CACHE_KEY) : undefined;\n\n if (!response) {\n // Fetch from API\n const res = await fetch(`${baseUrl}/api/v1/tools`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(\n `Supyagent API error (${res.status}): ${error}`\n );\n }\n\n const json = await res.json();\n // API wraps response in { ok, data: { tools, base_url, total } }\n response = (json.data ?? json) as ToolsResponse;\n\n // Store in cache if TTL > 0\n if (cacheTTL > 0) {\n cache.set(CACHE_KEY, response, cacheTTL);\n }\n }\n\n // Use API-reported base_url for tool execution\n const toolBaseUrl = response.base_url || baseUrl;\n\n // Guard against non-array tools (e.g., API returns empty or unexpected shape)\n const toolsArray = Array.isArray(response.tools) ? response.tools : [];\n\n // Filter\n const filtered = filterTools(toolsArray, {\n only: filterOptions?.only,\n except: filterOptions?.except,\n });\n\n // Convert to AI SDK tools\n return convertTools(filtered, toolBaseUrl, apiKey);\n },\n };\n}\n\nfunction resolveCacheTTL(cache?: boolean | number): number {\n if (cache === true) return 60;\n if (typeof cache === \"number\") return cache;\n return 0;\n}\n","import { tool, jsonSchema } from \"ai\";\nimport { exec, type ExecException } from \"node:child_process\";\n\nconst MAX_OUTPUT = 30_000;\nconst DEFAULT_TIMEOUT = 30_000; // 30 seconds\n\nexport interface BashToolOptions {\n /** Working directory for commands. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Command timeout in milliseconds. Defaults to 30000 (30s). Max 600000 (10m). */\n timeout?: number;\n /** Maximum output length in characters. Defaults to 30000. */\n maxOutput?: number;\n}\n\nexport interface BashToolResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut?: boolean;\n}\n\n/**\n * Create a bash tool that executes shell commands on the server.\n *\n * @example\n * ```ts\n * import { supyagent, createBashTool } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const supyagentTools = await client.tools({ cache: 300 });\n *\n * const tools = {\n * ...supyagentTools,\n * bash: createBashTool({ cwd: '/path/to/project' }),\n * };\n * ```\n */\nexport function createBashTool(options?: BashToolOptions) {\n const cwd = options?.cwd ?? process.cwd();\n const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 600_000);\n const maxOutput = options?.maxOutput ?? MAX_OUTPUT;\n\n const schema = {\n type: \"object\" as const,\n properties: {\n command: {\n type: \"string\",\n description: \"The bash command to execute\",\n },\n },\n required: [\"command\"],\n };\n\n return tool({\n description:\n \"Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.\",\n inputSchema: jsonSchema(schema as Parameters<typeof jsonSchema>[0]),\n execute: async (args): Promise<BashToolResult> => {\n const { command } = args as { command: string };\n const start = Date.now();\n\n return new Promise<BashToolResult>((resolve) => {\n const child = exec(\n command,\n {\n cwd,\n timeout,\n shell: \"/bin/bash\",\n maxBuffer: 10 * 1024 * 1024, // 10MB buffer\n env: { ...process.env, TERM: \"dumb\" },\n },\n (error: ExecException | null, stdout: string, stderr: string) => {\n const durationMs = Date.now() - start;\n const timedOut = error?.killed === true;\n const exitCode = timedOut\n ? 124\n : typeof error?.code === \"number\"\n ? error.code\n : error\n ? 1\n : 0;\n\n resolve({\n stdout: truncate(stdout, maxOutput),\n stderr: truncate(stderr, maxOutput),\n exitCode,\n durationMs,\n ...(timedOut ? { timedOut: true } : {}),\n });\n }\n );\n\n // Ensure the child process is killed on timeout\n child.on(\"error\", () => {\n resolve({\n stdout: \"\",\n stderr: \"Failed to start process\",\n exitCode: 127,\n durationMs: Date.now() - start,\n });\n });\n });\n },\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor(max / 2) - 50;\n return (\n str.slice(0, half) +\n `\\n\\n... (${str.length - max} characters truncated) ...\\n\\n` +\n str.slice(-half)\n );\n}\n"],"mappings":"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","names":["tool","jsonSchema"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/client.ts","../src/tools/bash.ts"],"sourcesContent":["interface CacheEntry<T> {\n data: T;\n expiresAt: number;\n}\n\nexport class TTLCache<T> {\n private store = new Map<string, CacheEntry<T>>();\n\n get(key: string): T | undefined {\n const entry = this.store.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expiresAt) {\n this.store.delete(key);\n return undefined;\n }\n return entry.data;\n }\n\n set(key: string, data: T, ttlSeconds: number): void {\n this.store.set(key, {\n data,\n expiresAt: Date.now() + ttlSeconds * 1000,\n });\n }\n\n clear(): void {\n this.store.clear();\n }\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { OpenAITool } from \"./types.js\";\nimport { createExecutor } from \"./http-executor.js\";\n\n/**\n * Converts an array of OpenAI-format tools from the supyagent API\n * into a Record of AI SDK tool() instances ready for streamText/generateText.\n */\nexport function convertTools(\n tools: OpenAITool[],\n baseUrl: string,\n apiKey: string\n): Record<string, import(\"ai\").Tool> {\n const result: Record<string, import(\"ai\").Tool> = {};\n\n for (const t of tools) {\n const { name, description, parameters } = t.function;\n const metadata = t.metadata;\n\n result[name] = tool({\n description,\n inputSchema: jsonSchema(parameters as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => createExecutor(metadata, baseUrl, apiKey)(args as Record<string, unknown>),\n });\n }\n\n return result;\n}\n\n/**\n * Filter tools by provider, service, or tool name.\n */\nexport function filterTools(\n tools: OpenAITool[],\n options: { only?: string[]; except?: string[] }\n): OpenAITool[] {\n let filtered = tools;\n\n if (options.only && options.only.length > 0) {\n const onlySet = new Set(options.only.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return onlySet.has(name) || onlySet.has(provider) || onlySet.has(service);\n });\n }\n\n if (options.except && options.except.length > 0) {\n const exceptSet = new Set(options.except.map((s) => s.toLowerCase()));\n filtered = filtered.filter((t) => {\n const name = t.function.name.toLowerCase();\n const provider = t.metadata.provider.toLowerCase();\n const service = t.metadata.service.toLowerCase();\n return (\n !exceptSet.has(name) &&\n !exceptSet.has(provider) &&\n !exceptSet.has(service)\n );\n });\n }\n\n return filtered;\n}\n","import type { ToolMetadata } from \"./types.js\";\n\n/**\n * Creates an execute function for a tool that calls the supyagent API.\n *\n * Handles:\n * - Path parameter substitution (e.g., {messageId} in /api/v1/gmail/messages/{messageId})\n * - GET/DELETE: remaining args → query string\n * - POST/PUT/PATCH: merge bodyDefaults + remaining args → JSON body\n */\nexport function createExecutor(\n metadata: ToolMetadata,\n baseUrl: string,\n apiKey: string\n): (args: Record<string, unknown>) => Promise<unknown> {\n return async (args: Record<string, unknown>) => {\n const { method, path, bodyDefaults } = metadata;\n\n // Extract path parameters and substitute into the path\n const remainingArgs = { ...args };\n let resolvedPath = path;\n\n const pathParams = path.match(/\\{(\\w+)\\}/g);\n if (pathParams) {\n for (const param of pathParams) {\n const paramName = param.slice(1, -1);\n if (paramName in remainingArgs) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs[paramName]))\n );\n delete remainingArgs[paramName];\n }\n }\n\n // Fallback: LLMs often send { id } instead of { messageId }, { eventId }, etc.\n // because list responses return objects with an `id` field.\n // For any unresolved {fooId} param, try `id` from the args (once only).\n if (\"id\" in remainingArgs && resolvedPath.includes(\"{\")) {\n const unresolved = resolvedPath.match(/\\{(\\w+)\\}/g);\n if (unresolved) {\n for (const param of unresolved) {\n const paramName = param.slice(1, -1);\n if (paramName.toLowerCase().endsWith(\"id\")) {\n resolvedPath = resolvedPath.replace(\n param,\n encodeURIComponent(String(remainingArgs.id))\n );\n delete remainingArgs.id;\n break; // consume `id` only once\n }\n }\n }\n }\n }\n\n let url = `${baseUrl}${resolvedPath}`;\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (method === \"GET\" || method === \"DELETE\") {\n // Remaining args go as query parameters\n const params = new URLSearchParams();\n for (const [key, value] of Object.entries(remainingArgs)) {\n if (value !== undefined && value !== null) {\n params.set(key, String(value));\n }\n }\n const qs = params.toString();\n if (qs) url += `?${qs}`;\n } else {\n // POST/PUT/PATCH: merge bodyDefaults with remaining args\n const body = { ...bodyDefaults, ...remainingArgs };\n fetchOptions.body = JSON.stringify(body);\n }\n\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n return data;\n };\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\n\nconst DEFAULT_BASE_URL = \"https://app.supyagent.com\";\nconst CACHE_KEY = \"tools\";\n\n/**\n * Create a supyagent client for fetching and converting tools.\n *\n * @example\n * ```ts\n * import { supyagent } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const tools = await client.tools({ cache: 300 });\n * ```\n */\nexport function supyagent(options: SupyagentOptions): SupyagentClient {\n const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;\n const cache = new TTLCache<ToolsResponse>();\n\n return {\n async tools(filterOptions?: ToolFilterOptions) {\n const cacheTTL = resolveCacheTTL(filterOptions?.cache);\n\n // Check cache\n let response = cacheTTL > 0 ? cache.get(CACHE_KEY) : undefined;\n\n if (!response) {\n // Fetch from API\n const res = await fetch(`${baseUrl}/api/v1/tools`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!res.ok) {\n const error = await res.text();\n throw new Error(\n `Supyagent API error (${res.status}): ${error}`\n );\n }\n\n const json = await res.json();\n // API wraps response in { ok, data: { tools, base_url, total } }\n response = (json.data ?? json) as ToolsResponse;\n\n // Store in cache if TTL > 0\n if (cacheTTL > 0) {\n cache.set(CACHE_KEY, response, cacheTTL);\n }\n }\n\n // Use API-reported base_url for tool execution\n const toolBaseUrl = response.base_url || baseUrl;\n\n // Guard against non-array tools (e.g., API returns empty or unexpected shape)\n const toolsArray = Array.isArray(response.tools) ? response.tools : [];\n\n // Filter\n const filtered = filterTools(toolsArray, {\n only: filterOptions?.only,\n except: filterOptions?.except,\n });\n\n // Convert to AI SDK tools\n return convertTools(filtered, toolBaseUrl, apiKey);\n },\n };\n}\n\nfunction resolveCacheTTL(cache?: boolean | number): number {\n if (cache === true) return 60;\n if (typeof cache === \"number\") return cache;\n return 0;\n}\n","import { tool, jsonSchema } from \"ai\";\nimport { exec, type ExecException } from \"node:child_process\";\n\nconst MAX_OUTPUT = 30_000;\nconst DEFAULT_TIMEOUT = 30_000; // 30 seconds\n\n// ── Environment sanitization ────────────────────────────────────────────────\n\nconst SECRET_PATTERNS = [\n /key/i, /secret/i, /token/i, /password/i, /credential/i,\n /auth/i, /private/i, /api_key/i, /apikey/i,\n /^AWS_/i, /^AZURE_/i, /^GCP_/i, /^GITHUB_/i,\n /^DATABASE_URL$/i, /^ENCRYPTION_/i, /^SUPABASE_/i,\n];\n\nconst SAFE_VARS = new Set([\n \"PATH\", \"HOME\", \"USER\", \"SHELL\", \"LANG\", \"LC_ALL\", \"LC_CTYPE\",\n \"TERM\", \"EDITOR\", \"VISUAL\", \"TMPDIR\", \"TMP\", \"TEMP\",\n \"NODE_ENV\", \"NODE_PATH\", \"NODE_OPTIONS\",\n \"NPM_CONFIG_PREFIX\", \"NVM_DIR\", \"NVM_BIN\", \"NVM_INC\",\n \"XDG_CONFIG_HOME\", \"XDG_DATA_HOME\", \"XDG_CACHE_HOME\",\n \"COLORTERM\", \"FORCE_COLOR\", \"NO_COLOR\",\n \"COLUMNS\", \"LINES\", \"SHLVL\", \"PWD\", \"OLDPWD\", \"LOGNAME\",\n \"HOSTNAME\", \"HOSTTYPE\", \"OSTYPE\", \"MACHTYPE\",\n]);\n\nfunction sanitizeEnv(\n env: NodeJS.ProcessEnv,\n opts?: { include?: string[]; exclude?: string[] }\n): Record<string, string> {\n const result: Record<string, string> = {};\n const includeSet = opts?.include ? new Set(opts.include) : null;\n const excludeSet = opts?.exclude ? new Set(opts.exclude) : null;\n\n for (const [key, value] of Object.entries(env)) {\n if (value === undefined) continue;\n if (excludeSet?.has(key)) continue;\n if (includeSet?.has(key)) { result[key] = value; continue; }\n if (SAFE_VARS.has(key)) { result[key] = value; continue; }\n if (SECRET_PATTERNS.some(p => p.test(key))) continue;\n result[key] = value;\n }\n\n result.TERM = \"dumb\";\n return result;\n}\n\nfunction buildEnv(\n policy: BashToolOptions[\"env\"],\n opts?: BashToolOptions\n): Record<string, string> {\n if (typeof policy === \"object\" && policy !== null) {\n return { ...policy, TERM: \"dumb\" };\n }\n switch (policy) {\n case \"inherit\":\n return { ...process.env, TERM: \"dumb\" } as Record<string, string>;\n case \"none\":\n return {\n PATH: process.env.PATH ?? \"/usr/local/bin:/usr/bin:/bin\",\n HOME: process.env.HOME ?? \"/tmp\",\n TERM: \"dumb\",\n };\n case \"safe\":\n default:\n return sanitizeEnv(process.env, {\n include: opts?.envInclude,\n exclude: opts?.envExclude,\n });\n }\n}\n\n// ── Command safety ──────────────────────────────────────────────────────────\n\ntype CommandSafety = \"safe\" | \"dangerous\" | \"unknown\";\n\nconst SAFE_COMMANDS = new Set([\n \"cat\", \"cd\", \"cut\", \"echo\", \"expr\", \"false\", \"grep\", \"egrep\", \"fgrep\",\n \"head\", \"id\", \"ls\", \"nl\", \"paste\", \"pwd\", \"rev\", \"seq\", \"stat\",\n \"tail\", \"tr\", \"true\", \"uname\", \"uniq\", \"wc\", \"which\", \"whoami\",\n \"date\", \"env\", \"printenv\", \"hostname\", \"df\", \"du\", \"file\",\n \"find\", \"rg\", \"tree\", \"less\", \"more\", \"sort\",\n \"diff\", \"md5sum\", \"sha256sum\", \"base64\", \"xxd\", \"hexdump\",\n \"man\", \"help\", \"type\", \"command\", \"test\", \"basename\", \"dirname\",\n \"realpath\", \"readlink\", \"tee\", \"xargs\", \"jq\", \"yq\",\n]);\n\nconst SAFE_GIT_SUBCOMMANDS = new Set([\n \"status\", \"log\", \"diff\", \"show\", \"branch\", \"tag\", \"remote\",\n \"stash\", \"describe\", \"shortlog\", \"blame\", \"reflog\", \"rev-parse\",\n \"ls-files\", \"ls-tree\", \"cat-file\", \"rev-list\", \"name-rev\",\n]);\n\nconst DANGEROUS_PATTERNS = [\n /rm\\s+(-[^\\s]*f|-[^\\s]*r|--force|--recursive).*\\//,\n /:\\(\\)\\s*\\{\\s*:\\|:&\\s*\\};:/,\n />\\s*\\/dev\\/sd/,\n /mkfs\\./,\n /dd\\s+.*of=\\/dev\\//,\n /shutdown|reboot|halt|poweroff/,\n /chmod\\s+777\\s+\\//,\n /curl.*\\|\\s*(ba)?sh/,\n /wget.*\\|\\s*(ba)?sh/,\n];\n\nfunction classifyCommand(\n command: string,\n extraSafe?: Set<string>,\n extraDeny?: RegExp[],\n): CommandSafety {\n const trimmed = command.trim();\n\n // Check dangerous patterns first\n if (DANGEROUS_PATTERNS.some(p => p.test(trimmed))) return \"dangerous\";\n if (extraDeny?.some(p => p.test(trimmed))) return \"dangerous\";\n\n // Extract base command (strip leading env var assignments)\n const stripped = trimmed.replace(/^(\\w+=\\S+\\s+)*/, \"\");\n const parts = stripped.split(/\\s+/);\n const base = parts[0]?.replace(/^.*\\//, \"\"); // strip path prefix\n\n if (!base) return \"unknown\";\n\n // Check git subcommands\n if (base === \"git\") {\n const sub = parts[1];\n if (sub && SAFE_GIT_SUBCOMMANDS.has(sub)) return \"safe\";\n return \"unknown\";\n }\n\n if (SAFE_COMMANDS.has(base)) return \"safe\";\n if (extraSafe?.has(base)) return \"safe\";\n\n return \"unknown\";\n}\n\n// ── Types ───────────────────────────────────────────────────────────────────\n\nexport interface BashToolOptions {\n /** Working directory for commands. Defaults to `process.cwd()`. */\n cwd?: string;\n /** Command timeout in milliseconds. Defaults to 30000 (30s). Max 600000 (10m). */\n timeout?: number;\n /** Maximum output length in characters. Defaults to 30000. */\n maxOutput?: number;\n\n /**\n * Command approval policy. Default: \"auto\".\n * - `\"auto\"`: Safe read-only commands (ls, cat, grep...) auto-execute; others require user approval\n * - `\"always\"`: Every command requires user approval before execution\n * - `\"never\"`: No approval required (use only in trusted environments)\n * - `function`: Custom approval logic — return `true` to require approval\n */\n approval?: \"auto\" | \"always\" | \"never\" | ((command: string) => boolean | Promise<boolean>);\n\n /**\n * Environment variable policy. Default: \"safe\".\n * - `\"safe\"`: Strip vars matching secret patterns (KEY, TOKEN, SECRET, PASSWORD, etc.)\n * - `\"inherit\"`: Pass full process.env (NOT recommended for production)\n * - `\"none\"`: Minimal env (PATH, HOME, TERM only)\n * - `Record<string,string>`: Explicit env vars to use\n */\n env?: \"safe\" | \"inherit\" | \"none\" | Record<string, string>;\n\n /** Extra env vars to include even if they match secret patterns. Only used with `env: \"safe\"`. */\n envInclude?: string[];\n /** Extra env vars to exclude beyond default secret patterns. Only used with `env: \"safe\"`. */\n envExclude?: string[];\n\n /** Additional commands to treat as safe (auto-execute without approval in \"auto\" mode). */\n allowCommands?: string[];\n /** Additional patterns to block (never execute, even if approved). */\n denyPatterns?: RegExp[];\n}\n\nexport interface BashToolResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n durationMs: number;\n timedOut?: boolean;\n}\n\n// ── Tool factory ────────────────────────────────────────────────────────────\n\n/**\n * Create a bash tool that executes shell commands on the server.\n *\n * By default, uses \"auto\" approval (safe commands run immediately, others\n * require user approval) and \"safe\" env isolation (strips secret-like\n * environment variables).\n *\n * @example\n * ```ts\n * import { supyagent, createBashTool } from '@supyagent/sdk';\n *\n * const client = supyagent({ apiKey: process.env.SUPYAGENT_API_KEY! });\n * const supyagentTools = await client.tools({ cache: 300 });\n *\n * const tools = {\n * ...supyagentTools,\n * bash: createBashTool({ cwd: '/path/to/project' }),\n * };\n * ```\n */\nexport function createBashTool(options?: BashToolOptions) {\n const cwd = options?.cwd ?? process.cwd();\n const timeout = Math.min(options?.timeout ?? DEFAULT_TIMEOUT, 600_000);\n const maxOutput = options?.maxOutput ?? MAX_OUTPUT;\n const approvalPolicy = options?.approval ?? \"auto\";\n\n const extraSafe = options?.allowCommands ? new Set(options.allowCommands) : undefined;\n const extraDeny = options?.denyPatterns;\n\n const execEnv = buildEnv(options?.env ?? \"safe\", options);\n\n const schema = {\n type: \"object\" as const,\n properties: {\n command: {\n type: \"string\",\n description: \"The bash command to execute\",\n },\n },\n required: [\"command\"],\n };\n\n // Resolve needsApproval based on policy\n const needsApproval =\n approvalPolicy === \"always\"\n ? true\n : approvalPolicy === \"never\"\n ? false\n : async (args: unknown) => {\n const { command } = args as { command: string };\n if (typeof approvalPolicy === \"function\") {\n return approvalPolicy(command);\n }\n // \"auto\" mode — safe commands pass, everything else needs approval\n const safety = classifyCommand(command, extraSafe, extraDeny);\n return safety !== \"safe\";\n };\n\n return tool({\n description:\n \"Execute a bash command on the server. Use this for running shell commands, scripts, package managers, git operations, file manipulation, and system tasks. Returns stdout, stderr, and exit code.\",\n inputSchema: jsonSchema(schema as Parameters<typeof jsonSchema>[0]),\n needsApproval,\n execute: async (args): Promise<BashToolResult> => {\n const { command } = args as { command: string };\n\n // Block dangerous commands even if somehow approved\n if (\n DANGEROUS_PATTERNS.some(p => p.test(command)) ||\n extraDeny?.some(p => p.test(command))\n ) {\n return {\n stdout: \"\",\n stderr: \"Command blocked by safety policy\",\n exitCode: 126,\n durationMs: 0,\n };\n }\n\n const start = Date.now();\n\n return new Promise<BashToolResult>((resolve) => {\n const child = exec(\n command,\n {\n cwd,\n timeout,\n shell: \"/bin/bash\",\n maxBuffer: 10 * 1024 * 1024,\n env: execEnv,\n },\n (error: ExecException | null, stdout: string, stderr: string) => {\n const durationMs = Date.now() - start;\n const timedOut = error?.killed === true;\n const exitCode = timedOut\n ? 124\n : typeof error?.code === \"number\"\n ? error.code\n : error\n ? 1\n : 0;\n\n resolve({\n stdout: truncate(stdout, maxOutput),\n stderr: truncate(stderr, maxOutput),\n exitCode,\n durationMs,\n ...(timedOut ? { timedOut: true } : {}),\n });\n }\n );\n\n child.on(\"error\", () => {\n resolve({\n stdout: \"\",\n stderr: \"Failed to start process\",\n exitCode: 127,\n durationMs: Date.now() - start,\n });\n });\n });\n },\n });\n}\n\nfunction truncate(str: string, max: number): string {\n if (str.length <= max) return str;\n const half = Math.floor(max / 2) - 50;\n return (\n str.slice(0, half) +\n `\\n\\n... (${str.length - max} characters truncated) ...\\n\\n` +\n str.slice(-half)\n );\n}\n"],"mappings":"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","names":["tool","jsonSchema"]}
|