@supyagent/sdk 0.1.18 β 0.1.19
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 +217 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -1
- package/dist/index.d.ts +66 -1
- package/dist/index.js +212 -3
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -20,7 +20,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
buildSkillsSystemPrompt: () => buildSkillsSystemPrompt,
|
|
24
|
+
createApiCallTool: () => createApiCallTool,
|
|
23
25
|
createBashTool: () => createBashTool,
|
|
26
|
+
createLoadSkillTool: () => createLoadSkillTool,
|
|
27
|
+
findSkill: () => findSkill,
|
|
28
|
+
parseSkillsMarkdown: () => parseSkillsMarkdown,
|
|
24
29
|
supyagent: () => supyagent
|
|
25
30
|
});
|
|
26
31
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -150,12 +155,188 @@ function filterTools(tools, options) {
|
|
|
150
155
|
return filtered;
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
// src/core/skill-parser.ts
|
|
159
|
+
function parseFrontmatter(markdown) {
|
|
160
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
161
|
+
if (!match) {
|
|
162
|
+
return {
|
|
163
|
+
frontmatter: { name: "supyagent-skills", description: "" },
|
|
164
|
+
body: markdown
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const [, yamlBlock, body] = match;
|
|
168
|
+
const fields = {};
|
|
169
|
+
for (const line of yamlBlock.split("\n")) {
|
|
170
|
+
const colonIndex = line.indexOf(":");
|
|
171
|
+
if (colonIndex > 0) {
|
|
172
|
+
const key = line.slice(0, colonIndex).trim();
|
|
173
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
174
|
+
fields[key] = value;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
frontmatter: {
|
|
179
|
+
name: fields.name || "supyagent-skills",
|
|
180
|
+
description: fields.description || ""
|
|
181
|
+
},
|
|
182
|
+
body
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function extractSkillName(section) {
|
|
186
|
+
const match = section.match(/^#\s+(.+)/m);
|
|
187
|
+
return match ? match[1].trim() : "Unknown";
|
|
188
|
+
}
|
|
189
|
+
function parseSkillsMarkdown(markdown) {
|
|
190
|
+
const { frontmatter, body } = parseFrontmatter(markdown);
|
|
191
|
+
const parts = body.split(/\n---\n/);
|
|
192
|
+
const preamble = (parts[0] || "").trim();
|
|
193
|
+
const skills = [];
|
|
194
|
+
for (let i = 1; i < parts.length; i++) {
|
|
195
|
+
const content = parts[i].trim();
|
|
196
|
+
if (!content) continue;
|
|
197
|
+
const name = extractSkillName(content);
|
|
198
|
+
skills.push({ name, content });
|
|
199
|
+
}
|
|
200
|
+
return { frontmatter, preamble, skills };
|
|
201
|
+
}
|
|
202
|
+
function buildSkillsSystemPrompt(doc) {
|
|
203
|
+
if (doc.skills.length === 0) {
|
|
204
|
+
return "No skills are currently available. The user has no connected integrations.";
|
|
205
|
+
}
|
|
206
|
+
const skillList = doc.skills.map((s) => `- **${s.name}**`).join("\n");
|
|
207
|
+
return `You have access to the following skills via Supyagent:
|
|
208
|
+
|
|
209
|
+
${skillList}
|
|
210
|
+
|
|
211
|
+
To use a skill:
|
|
212
|
+
1. Call \`loadSkill\` with the skill name to get detailed API documentation
|
|
213
|
+
2. Call \`apiCall\` to make authenticated HTTP requests to the API endpoints described in the skill docs
|
|
214
|
+
|
|
215
|
+
Available skill names: ${doc.skills.map((s) => s.name).join(", ")}`;
|
|
216
|
+
}
|
|
217
|
+
function findSkill(skills, name) {
|
|
218
|
+
const normalized = name.toLowerCase().trim();
|
|
219
|
+
return skills.find((s) => s.name.toLowerCase() === normalized) ?? skills.find((s) => s.name.toLowerCase().includes(normalized)) ?? null;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/tools/skills.ts
|
|
223
|
+
var import_ai2 = require("ai");
|
|
224
|
+
function createLoadSkillTool(skills) {
|
|
225
|
+
const availableNames = skills.map((s) => s.name).join(", ");
|
|
226
|
+
return (0, import_ai2.tool)({
|
|
227
|
+
description: `Load detailed API documentation for a specific skill. Available skills: ${availableNames}. Call this before using apiCall to understand the available endpoints, parameters, and examples.`,
|
|
228
|
+
inputSchema: (0, import_ai2.jsonSchema)({
|
|
229
|
+
type: "object",
|
|
230
|
+
properties: {
|
|
231
|
+
name: {
|
|
232
|
+
type: "string",
|
|
233
|
+
description: `The skill name to load. Available: ${availableNames}`
|
|
234
|
+
}
|
|
235
|
+
},
|
|
236
|
+
required: ["name"]
|
|
237
|
+
}),
|
|
238
|
+
execute: async (args) => {
|
|
239
|
+
const { name } = args;
|
|
240
|
+
const skill = findSkill(skills, name);
|
|
241
|
+
if (!skill) {
|
|
242
|
+
return {
|
|
243
|
+
error: `Skill "${name}" not found. Available skills: ${availableNames}`
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
name: skill.name,
|
|
248
|
+
documentation: skill.content
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
var VALID_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
254
|
+
function createApiCallTool(baseUrl, apiKey) {
|
|
255
|
+
return (0, import_ai2.tool)({
|
|
256
|
+
description: "Make an authenticated HTTP request to the Supyagent API. Use loadSkill first to understand available endpoints. The authorization header and base URL are handled automatically \u2014 only provide the path (e.g., /api/v1/google/gmail/messages).",
|
|
257
|
+
inputSchema: (0, import_ai2.jsonSchema)({
|
|
258
|
+
type: "object",
|
|
259
|
+
properties: {
|
|
260
|
+
method: {
|
|
261
|
+
type: "string",
|
|
262
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
263
|
+
description: "HTTP method"
|
|
264
|
+
},
|
|
265
|
+
path: {
|
|
266
|
+
type: "string",
|
|
267
|
+
description: 'API path starting with /api/v1/ (e.g., "/api/v1/google/gmail/messages")'
|
|
268
|
+
},
|
|
269
|
+
body: {
|
|
270
|
+
type: "object",
|
|
271
|
+
description: "Request body for POST/PUT/PATCH requests",
|
|
272
|
+
additionalProperties: true
|
|
273
|
+
},
|
|
274
|
+
params: {
|
|
275
|
+
type: "object",
|
|
276
|
+
description: "Query parameters",
|
|
277
|
+
additionalProperties: true
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
required: ["method", "path"]
|
|
281
|
+
}),
|
|
282
|
+
execute: async (args) => {
|
|
283
|
+
const {
|
|
284
|
+
method: rawMethod,
|
|
285
|
+
path,
|
|
286
|
+
body,
|
|
287
|
+
params
|
|
288
|
+
} = args;
|
|
289
|
+
const method = rawMethod.toUpperCase();
|
|
290
|
+
if (!VALID_METHODS.has(method)) {
|
|
291
|
+
return { error: `Invalid HTTP method: ${method}` };
|
|
292
|
+
}
|
|
293
|
+
if (!path.startsWith("/api/v1/")) {
|
|
294
|
+
return { error: `Path must start with /api/v1/. Got: ${path}` };
|
|
295
|
+
}
|
|
296
|
+
let url = `${baseUrl}${path}`;
|
|
297
|
+
if (params && Object.keys(params).length > 0) {
|
|
298
|
+
const searchParams = new URLSearchParams();
|
|
299
|
+
for (const [key, value] of Object.entries(params)) {
|
|
300
|
+
if (value !== void 0 && value !== null) {
|
|
301
|
+
searchParams.set(key, String(value));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
const qs = searchParams.toString();
|
|
305
|
+
if (qs) url += `?${qs}`;
|
|
306
|
+
}
|
|
307
|
+
const fetchOptions = {
|
|
308
|
+
method,
|
|
309
|
+
headers: {
|
|
310
|
+
Authorization: `Bearer ${apiKey}`,
|
|
311
|
+
"Content-Type": "application/json"
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
if (body && method !== "GET" && method !== "DELETE") {
|
|
315
|
+
fetchOptions.body = JSON.stringify(body);
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const response = await fetch(url, fetchOptions);
|
|
319
|
+
const data = await response.json();
|
|
320
|
+
if (!response.ok) {
|
|
321
|
+
return { error: `API returned ${response.status}`, details: data };
|
|
322
|
+
}
|
|
323
|
+
return data;
|
|
324
|
+
} catch (err) {
|
|
325
|
+
return {
|
|
326
|
+
error: `Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
153
333
|
// src/core/client.ts
|
|
154
334
|
var DEFAULT_BASE_URL = "https://app.supyagent.com";
|
|
155
335
|
var CACHE_KEY = "tools";
|
|
156
336
|
function supyagent(options) {
|
|
157
337
|
const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;
|
|
158
338
|
const cache = new TTLCache();
|
|
339
|
+
const skillsCache = new TTLCache();
|
|
159
340
|
return {
|
|
160
341
|
async tools(filterOptions) {
|
|
161
342
|
const cacheTTL = resolveCacheTTL(filterOptions?.cache);
|
|
@@ -186,6 +367,34 @@ function supyagent(options) {
|
|
|
186
367
|
except: filterOptions?.except
|
|
187
368
|
});
|
|
188
369
|
return convertTools(filtered, toolBaseUrl, apiKey);
|
|
370
|
+
},
|
|
371
|
+
async skills(options2) {
|
|
372
|
+
const cacheTTL = resolveCacheTTL(options2?.cache);
|
|
373
|
+
let parsed = cacheTTL > 0 ? skillsCache.get("skills") : void 0;
|
|
374
|
+
if (!parsed) {
|
|
375
|
+
const res = await fetch(`${baseUrl}/api/v1/skills`, {
|
|
376
|
+
headers: {
|
|
377
|
+
Authorization: `Bearer ${apiKey}`
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
if (!res.ok) {
|
|
381
|
+
const error = await res.text();
|
|
382
|
+
throw new Error(
|
|
383
|
+
`Supyagent API error (${res.status}): ${error}`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
parsed = parseSkillsMarkdown(await res.text());
|
|
387
|
+
if (cacheTTL > 0) {
|
|
388
|
+
skillsCache.set("skills", parsed, cacheTTL);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return {
|
|
392
|
+
systemPrompt: buildSkillsSystemPrompt(parsed),
|
|
393
|
+
tools: {
|
|
394
|
+
loadSkill: createLoadSkillTool(parsed.skills),
|
|
395
|
+
apiCall: createApiCallTool(baseUrl, apiKey)
|
|
396
|
+
}
|
|
397
|
+
};
|
|
189
398
|
}
|
|
190
399
|
};
|
|
191
400
|
}
|
|
@@ -196,7 +405,7 @@ function resolveCacheTTL(cache) {
|
|
|
196
405
|
}
|
|
197
406
|
|
|
198
407
|
// src/tools/bash.ts
|
|
199
|
-
var
|
|
408
|
+
var import_ai3 = require("ai");
|
|
200
409
|
var import_node_child_process = require("child_process");
|
|
201
410
|
var MAX_OUTPUT = 3e4;
|
|
202
411
|
var DEFAULT_TIMEOUT = 3e4;
|
|
@@ -432,9 +641,9 @@ function createBashTool(options) {
|
|
|
432
641
|
const safety = classifyCommand(command, extraSafe, extraDeny);
|
|
433
642
|
return safety !== "safe";
|
|
434
643
|
};
|
|
435
|
-
return (0,
|
|
644
|
+
return (0, import_ai3.tool)({
|
|
436
645
|
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.",
|
|
437
|
-
inputSchema: (0,
|
|
646
|
+
inputSchema: (0, import_ai3.jsonSchema)(schema),
|
|
438
647
|
needsApproval,
|
|
439
648
|
execute: async (args) => {
|
|
440
649
|
const { command } = args;
|
|
@@ -493,7 +702,12 @@ function truncate(str, max) {
|
|
|
493
702
|
}
|
|
494
703
|
// Annotate the CommonJS export names for ESM import in node:
|
|
495
704
|
0 && (module.exports = {
|
|
705
|
+
buildSkillsSystemPrompt,
|
|
706
|
+
createApiCallTool,
|
|
496
707
|
createBashTool,
|
|
708
|
+
createLoadSkillTool,
|
|
709
|
+
findSkill,
|
|
710
|
+
parseSkillsMarkdown,
|
|
497
711
|
supyagent
|
|
498
712
|
});
|
|
499
713
|
//# sourceMappingURL=index.cjs.map
|
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\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/skill-parser.ts","../src/tools/skills.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 SkillsOptions,\n SkillsResult,\n ParsedSkill,\n ParsedSkillsDocument,\n} from \"./core/types.js\";\nexport { createBashTool } from \"./tools/bash.js\";\nexport type { BashToolOptions, BashToolResult } from \"./tools/bash.js\";\nexport { parseSkillsMarkdown, buildSkillsSystemPrompt, findSkill } from \"./core/skill-parser.js\";\nexport { createLoadSkillTool, createApiCallTool } from \"./tools/skills.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 { ParsedSkill, ParsedSkillsDocument } from \"./types.js\";\n\n/**\n * Parse YAML frontmatter from the beginning of a markdown document.\n */\nfunction parseFrontmatter(markdown: string): {\n frontmatter: { name: string; description: string };\n body: string;\n} {\n const match = markdown.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);\n if (!match) {\n return {\n frontmatter: { name: \"supyagent-skills\", description: \"\" },\n body: markdown,\n };\n }\n\n const [, yamlBlock, body] = match;\n const fields: Record<string, string> = {};\n for (const line of yamlBlock!.split(\"\\n\")) {\n const colonIndex = line.indexOf(\":\");\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n fields[key] = value;\n }\n }\n\n return {\n frontmatter: {\n name: fields.name || \"supyagent-skills\",\n description: fields.description || \"\",\n },\n body: body!,\n };\n}\n\n/**\n * Extract the skill name from a section starting with `# Name`.\n */\nfunction extractSkillName(section: string): string {\n const match = section.match(/^#\\s+(.+)/m);\n return match ? match[1].trim() : \"Unknown\";\n}\n\n/**\n * Parse the aggregated skills markdown into structured sections.\n */\nexport function parseSkillsMarkdown(markdown: string): ParsedSkillsDocument {\n const { frontmatter, body } = parseFrontmatter(markdown);\n\n // Split body on \\n---\\n (the section separator)\n const parts = body.split(/\\n---\\n/);\n\n // First part is the preamble (general header info)\n const preamble = (parts[0] || \"\").trim();\n\n // Remaining parts are individual skill sections\n const skills: ParsedSkill[] = [];\n for (let i = 1; i < parts.length; i++) {\n const content = parts[i]!.trim();\n if (!content) continue;\n const name = extractSkillName(content);\n skills.push({ name, content });\n }\n\n return { frontmatter, preamble, skills };\n}\n\n/**\n * Build a concise system prompt from parsed skills.\n * Lists available skills with one-line summaries, not full docs.\n */\nexport function buildSkillsSystemPrompt(\n doc: ParsedSkillsDocument,\n): string {\n if (doc.skills.length === 0) {\n return \"No skills are currently available. The user has no connected integrations.\";\n }\n\n const skillList = doc.skills.map((s) => `- **${s.name}**`).join(\"\\n\");\n\n return `You have access to the following skills via Supyagent:\n\n${skillList}\n\nTo use a skill:\n1. Call \\`loadSkill\\` with the skill name to get detailed API documentation\n2. Call \\`apiCall\\` to make authenticated HTTP requests to the API endpoints described in the skill docs\n\nAvailable skill names: ${doc.skills.map((s) => s.name).join(\", \")}`;\n}\n\n/**\n * Find a skill by name (case-insensitive).\n */\nexport function findSkill(\n skills: ParsedSkill[],\n name: string,\n): ParsedSkill | null {\n const normalized = name.toLowerCase().trim();\n return (\n skills.find((s) => s.name.toLowerCase() === normalized) ??\n skills.find((s) => s.name.toLowerCase().includes(normalized)) ??\n null\n );\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { ParsedSkill } from \"../core/types.js\";\nimport { findSkill } from \"../core/skill-parser.js\";\n\n/**\n * Create the loadSkill tool.\n * Returns detailed documentation for a specific skill.\n */\nexport function createLoadSkillTool(skills: ParsedSkill[]) {\n const availableNames = skills.map((s) => s.name).join(\", \");\n\n return tool({\n description: `Load detailed API documentation for a specific skill. Available skills: ${availableNames}. Call this before using apiCall to understand the available endpoints, parameters, and examples.`,\n inputSchema: jsonSchema({\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description: `The skill name to load. Available: ${availableNames}`,\n },\n },\n required: [\"name\"],\n } as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => {\n const { name } = args as { name: string };\n const skill = findSkill(skills, name);\n\n if (!skill) {\n return {\n error: `Skill \"${name}\" not found. Available skills: ${availableNames}`,\n };\n }\n\n return {\n name: skill.name,\n documentation: skill.content,\n };\n },\n });\n}\n\nconst VALID_METHODS = new Set([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\n/**\n * Create the apiCall tool.\n * Makes authenticated HTTP requests to the Supyagent API.\n */\nexport function createApiCallTool(baseUrl: string, apiKey: string) {\n return tool({\n description:\n \"Make an authenticated HTTP request to the Supyagent API. Use loadSkill first to understand available endpoints. The authorization header and base URL are handled automatically β only provide the path (e.g., /api/v1/google/gmail/messages).\",\n inputSchema: jsonSchema({\n type: \"object\",\n properties: {\n method: {\n type: \"string\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"],\n description: \"HTTP method\",\n },\n path: {\n type: \"string\",\n description:\n 'API path starting with /api/v1/ (e.g., \"/api/v1/google/gmail/messages\")',\n },\n body: {\n type: \"object\",\n description: \"Request body for POST/PUT/PATCH requests\",\n additionalProperties: true,\n },\n params: {\n type: \"object\",\n description: \"Query parameters\",\n additionalProperties: true,\n },\n },\n required: [\"method\", \"path\"],\n } as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => {\n const {\n method: rawMethod,\n path,\n body,\n params,\n } = args as {\n method: string;\n path: string;\n body?: Record<string, unknown>;\n params?: Record<string, unknown>;\n };\n\n const method = rawMethod.toUpperCase();\n\n if (!VALID_METHODS.has(method)) {\n return { error: `Invalid HTTP method: ${method}` };\n }\n\n if (!path.startsWith(\"/api/v1/\")) {\n return { error: `Path must start with /api/v1/. Got: ${path}` };\n }\n\n let url = `${baseUrl}${path}`;\n\n if (params && Object.keys(params).length > 0) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n searchParams.set(key, String(value));\n }\n }\n const qs = searchParams.toString();\n if (qs) url += `?${qs}`;\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (body && method !== \"GET\" && method !== \"DELETE\") {\n fetchOptions.body = JSON.stringify(body);\n }\n\n try {\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n\n if (!response.ok) {\n return { error: `API returned ${response.status}`, details: data };\n }\n\n return data;\n } catch (err) {\n return {\n error: `Request failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n SkillsOptions,\n SkillsResult,\n ParsedSkillsDocument,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\nimport { parseSkillsMarkdown, buildSkillsSystemPrompt } from \"./skill-parser.js\";\nimport { createLoadSkillTool, createApiCallTool } from \"../tools/skills.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 const skillsCache = new TTLCache<ParsedSkillsDocument>();\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 async skills(options?: SkillsOptions): Promise<SkillsResult> {\n const cacheTTL = resolveCacheTTL(options?.cache);\n\n let parsed = cacheTTL > 0 ? skillsCache.get(\"skills\") : undefined;\n\n if (!parsed) {\n const res = await fetch(`${baseUrl}/api/v1/skills`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\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 parsed = parseSkillsMarkdown(await res.text());\n\n if (cacheTTL > 0) {\n skillsCache.set(\"skills\", parsed, cacheTTL);\n }\n }\n\n return {\n systemPrompt: buildSkillsSystemPrompt(parsed),\n tools: {\n loadSkill: createLoadSkillTool(parsed.skills),\n apiCall: createApiCallTool(baseUrl, apiKey),\n },\n };\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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACKO,IAAM,WAAN,MAAkB;AAAA,EACf,QAAQ,oBAAI,IAA2B;AAAA,EAE/C,IAAI,KAA4B;AAC9B,UAAM,QAAQ,KAAK,MAAM,IAAI,GAAG;AAChC,QAAI,CAAC,MAAO,QAAO;AACnB,QAAI,KAAK,IAAI,IAAI,MAAM,WAAW;AAChC,WAAK,MAAM,OAAO,GAAG;AACrB,aAAO;AAAA,IACT;AACA,WAAO,MAAM;AAAA,EACf;AAAA,EAEA,IAAI,KAAa,MAAS,YAA0B;AAClD,SAAK,MAAM,IAAI,KAAK;AAAA,MAClB;AAAA,MACA,WAAW,KAAK,IAAI,IAAI,aAAa;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,QAAc;AACZ,SAAK,MAAM,MAAM;AAAA,EACnB;AACF;;;AC5BA,gBAAiC;;;ACU1B,SAAS,eACd,UACA,SACA,QACqD;AACrD,SAAO,OAAO,SAAkC;AAC9C,UAAM,EAAE,QAAQ,MAAM,aAAa,IAAI;AAGvC,UAAM,gBAAgB,EAAE,GAAG,KAAK;AAChC,QAAI,eAAe;AAEnB,UAAM,aAAa,KAAK,MAAM,YAAY;AAC1C,QAAI,YAAY;AACd,iBAAW,SAAS,YAAY;AAC9B,cAAM,YAAY,MAAM,MAAM,GAAG,EAAE;AACnC,YAAI,aAAa,eAAe;AAC9B,yBAAe,aAAa;AAAA,YAC1B;AAAA,YACA,mBAAmB,OAAO,cAAc,SAAS,CAAC,CAAC;AAAA,UACrD;AACA,iBAAO,cAAc,SAAS;AAAA,QAChC;AAAA,MACF;AAKA,UAAI,QAAQ,iBAAiB,aAAa,SAAS,GAAG,GAAG;AACvD,cAAM,aAAa,aAAa,MAAM,YAAY;AAClD,YAAI,YAAY;AACd,qBAAW,SAAS,YAAY;AAC9B,kBAAM,YAAY,MAAM,MAAM,GAAG,EAAE;AACnC,gBAAI,UAAU,YAAY,EAAE,SAAS,IAAI,GAAG;AAC1C,6BAAe,aAAa;AAAA,gBAC1B;AAAA,gBACA,mBAAmB,OAAO,cAAc,EAAE,CAAC;AAAA,cAC7C;AACA,qBAAO,cAAc;AACrB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,MAAM,GAAG,OAAO,GAAG,YAAY;AAEnC,UAAM,eAA4B;AAAA,MAChC;AAAA,MACA,SAAS;AAAA,QACP,eAAe,UAAU,MAAM;AAAA,QAC/B,gBAAgB;AAAA,MAClB;AAAA,IACF;AAEA,QAAI,WAAW,SAAS,WAAW,UAAU;AAE3C,YAAM,SAAS,IAAI,gBAAgB;AACnC,iBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,aAAa,GAAG;AACxD,YAAI,UAAU,UAAa,UAAU,MAAM;AACzC,iBAAO,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,QAC/B;AAAA,MACF;AACA,YAAM,KAAK,OAAO,SAAS;AAC3B,UAAI,GAAI,QAAO,IAAI,EAAE;AAAA,IACvB,OAAO;AAEL,YAAM,OAAO,EAAE,GAAG,cAAc,GAAG,cAAc;AACjD,mBAAa,OAAO,KAAK,UAAU,IAAI;AAAA,IACzC;AAEA,UAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAC9C,UAAM,OAAO,MAAM,SAAS,KAAK;AACjC,WAAO;AAAA,EACT;AACF;;;AD9EO,SAAS,aACd,OACA,SACA,QACmC;AACnC,QAAM,SAA4C,CAAC;AAEnD,aAAW,KAAK,OAAO;AACrB,UAAM,EAAE,MAAM,aAAa,WAAW,IAAI,EAAE;AAC5C,UAAM,WAAW,EAAE;AAEnB,WAAO,IAAI,QAAI,gBAAK;AAAA,MAClB;AAAA,MACA,iBAAa,sBAAW,UAA8C;AAAA,MACtE,SAAS,OAAO,SAAS,eAAe,UAAU,SAAS,MAAM,EAAE,IAA+B;AAAA,IACpG,CAAC;AAAA,EACH;AAEA,SAAO;AACT;AAKO,SAAS,YACd,OACA,SACc;AACd,MAAI,WAAW;AAEf,MAAI,QAAQ,QAAQ,QAAQ,KAAK,SAAS,GAAG;AAC3C,UAAM,UAAU,IAAI,IAAI,QAAQ,KAAK,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AAChE,eAAW,SAAS,OAAO,CAAC,MAAM;AAChC,YAAM,OAAO,EAAE,SAAS,KAAK,YAAY;AACzC,YAAM,WAAW,EAAE,SAAS,SAAS,YAAY;AACjD,YAAM,UAAU,EAAE,SAAS,QAAQ,YAAY;AAC/C,aAAO,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,IAAI,OAAO;AAAA,IAC1E,CAAC;AAAA,EACH;AAEA,MAAI,QAAQ,UAAU,QAAQ,OAAO,SAAS,GAAG;AAC/C,UAAM,YAAY,IAAI,IAAI,QAAQ,OAAO,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;AACpE,eAAW,SAAS,OAAO,CAAC,MAAM;AAChC,YAAM,OAAO,EAAE,SAAS,KAAK,YAAY;AACzC,YAAM,WAAW,EAAE,SAAS,SAAS,YAAY;AACjD,YAAM,UAAU,EAAE,SAAS,QAAQ,YAAY;AAC/C,aACE,CAAC,UAAU,IAAI,IAAI,KACnB,CAAC,UAAU,IAAI,QAAQ,KACvB,CAAC,UAAU,IAAI,OAAO;AAAA,IAE1B,CAAC;AAAA,EACH;AAEA,SAAO;AACT;;;AE1DA,SAAS,iBAAiB,UAGxB;AACA,QAAM,QAAQ,SAAS,MAAM,oCAAoC;AACjE,MAAI,CAAC,OAAO;AACV,WAAO;AAAA,MACL,aAAa,EAAE,MAAM,oBAAoB,aAAa,GAAG;AAAA,MACzD,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,CAAC,EAAE,WAAW,IAAI,IAAI;AAC5B,QAAM,SAAiC,CAAC;AACxC,aAAW,QAAQ,UAAW,MAAM,IAAI,GAAG;AACzC,UAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,QAAI,aAAa,GAAG;AAClB,YAAM,MAAM,KAAK,MAAM,GAAG,UAAU,EAAE,KAAK;AAC3C,YAAM,QAAQ,KAAK,MAAM,aAAa,CAAC,EAAE,KAAK;AAC9C,aAAO,GAAG,IAAI;AAAA,IAChB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,aAAa;AAAA,MACX,MAAM,OAAO,QAAQ;AAAA,MACrB,aAAa,OAAO,eAAe;AAAA,IACrC;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,iBAAiB,SAAyB;AACjD,QAAM,QAAQ,QAAQ,MAAM,YAAY;AACxC,SAAO,QAAQ,MAAM,CAAC,EAAE,KAAK,IAAI;AACnC;AAKO,SAAS,oBAAoB,UAAwC;AAC1E,QAAM,EAAE,aAAa,KAAK,IAAI,iBAAiB,QAAQ;AAGvD,QAAM,QAAQ,KAAK,MAAM,SAAS;AAGlC,QAAM,YAAY,MAAM,CAAC,KAAK,IAAI,KAAK;AAGvC,QAAM,SAAwB,CAAC;AAC/B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,UAAU,MAAM,CAAC,EAAG,KAAK;AAC/B,QAAI,CAAC,QAAS;AACd,UAAM,OAAO,iBAAiB,OAAO;AACrC,WAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAAA,EAC/B;AAEA,SAAO,EAAE,aAAa,UAAU,OAAO;AACzC;AAMO,SAAS,wBACd,KACQ;AACR,MAAI,IAAI,OAAO,WAAW,GAAG;AAC3B,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,IAAI,OAAO,IAAI,CAAC,MAAM,OAAO,EAAE,IAAI,IAAI,EAAE,KAAK,IAAI;AAEpE,SAAO;AAAA;AAAA,EAEP,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAMc,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI,CAAC;AACjE;AAKO,SAAS,UACd,QACA,MACoB;AACpB,QAAM,aAAa,KAAK,YAAY,EAAE,KAAK;AAC3C,SACE,OAAO,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,MAAM,UAAU,KACtD,OAAO,KAAK,CAAC,MAAM,EAAE,KAAK,YAAY,EAAE,SAAS,UAAU,CAAC,KAC5D;AAEJ;;;AC1GA,IAAAA,aAAiC;AAQ1B,SAAS,oBAAoB,QAAuB;AACzD,QAAM,iBAAiB,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,IAAI;AAE1D,aAAO,iBAAK;AAAA,IACV,aAAa,2EAA2E,cAAc;AAAA,IACtG,iBAAa,uBAAW;AAAA,MACtB,MAAM;AAAA,MACN,YAAY;AAAA,QACV,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa,sCAAsC,cAAc;AAAA,QACnE;AAAA,MACF;AAAA,MACA,UAAU,CAAC,MAAM;AAAA,IACnB,CAAqC;AAAA,IACrC,SAAS,OAAO,SAAS;AACvB,YAAM,EAAE,KAAK,IAAI;AACjB,YAAM,QAAQ,UAAU,QAAQ,IAAI;AAEpC,UAAI,CAAC,OAAO;AACV,eAAO;AAAA,UACL,OAAO,UAAU,IAAI,kCAAkC,cAAc;AAAA,QACvE;AAAA,MACF;AAEA,aAAO;AAAA,QACL,MAAM,MAAM;AAAA,QACZ,eAAe,MAAM;AAAA,MACvB;AAAA,IACF;AAAA,EACF,CAAC;AACH;AAEA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ,CAAC;AAMhE,SAAS,kBAAkB,SAAiB,QAAgB;AACjE,aAAO,iBAAK;AAAA,IACV,aACE;AAAA,IACF,iBAAa,uBAAW;AAAA,MACtB,MAAM;AAAA,MACN,YAAY;AAAA,QACV,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,MAAM,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ;AAAA,UAC9C,aAAa;AAAA,QACf;AAAA,QACA,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aACE;AAAA,QACJ;AAAA,QACA,MAAM;AAAA,UACJ,MAAM;AAAA,UACN,aAAa;AAAA,UACb,sBAAsB;AAAA,QACxB;AAAA,QACA,QAAQ;AAAA,UACN,MAAM;AAAA,UACN,aAAa;AAAA,UACb,sBAAsB;AAAA,QACxB;AAAA,MACF;AAAA,MACA,UAAU,CAAC,UAAU,MAAM;AAAA,IAC7B,CAAqC;AAAA,IACrC,SAAS,OAAO,SAAS;AACvB,YAAM;AAAA,QACJ,QAAQ;AAAA,QACR;AAAA,QACA;AAAA,QACA;AAAA,MACF,IAAI;AAOJ,YAAM,SAAS,UAAU,YAAY;AAErC,UAAI,CAAC,cAAc,IAAI,MAAM,GAAG;AAC9B,eAAO,EAAE,OAAO,wBAAwB,MAAM,GAAG;AAAA,MACnD;AAEA,UAAI,CAAC,KAAK,WAAW,UAAU,GAAG;AAChC,eAAO,EAAE,OAAO,uCAAuC,IAAI,GAAG;AAAA,MAChE;AAEA,UAAI,MAAM,GAAG,OAAO,GAAG,IAAI;AAE3B,UAAI,UAAU,OAAO,KAAK,MAAM,EAAE,SAAS,GAAG;AAC5C,cAAM,eAAe,IAAI,gBAAgB;AACzC,mBAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,MAAM,GAAG;AACjD,cAAI,UAAU,UAAa,UAAU,MAAM;AACzC,yBAAa,IAAI,KAAK,OAAO,KAAK,CAAC;AAAA,UACrC;AAAA,QACF;AACA,cAAM,KAAK,aAAa,SAAS;AACjC,YAAI,GAAI,QAAO,IAAI,EAAE;AAAA,MACvB;AAEA,YAAM,eAA4B;AAAA,QAChC;AAAA,QACA,SAAS;AAAA,UACP,eAAe,UAAU,MAAM;AAAA,UAC/B,gBAAgB;AAAA,QAClB;AAAA,MACF;AAEA,UAAI,QAAQ,WAAW,SAAS,WAAW,UAAU;AACnD,qBAAa,OAAO,KAAK,UAAU,IAAI;AAAA,MACzC;AAEA,UAAI;AACF,cAAM,WAAW,MAAM,MAAM,KAAK,YAAY;AAC9C,cAAM,OAAO,MAAM,SAAS,KAAK;AAEjC,YAAI,CAAC,SAAS,IAAI;AAChB,iBAAO,EAAE,OAAO,gBAAgB,SAAS,MAAM,IAAI,SAAS,KAAK;AAAA,QACnE;AAEA,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,OAAO,mBAAmB,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAAA,QAC5E;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAC;AACH;;;AC/HA,IAAM,mBAAmB;AACzB,IAAM,YAAY;AAaX,SAAS,UAAU,SAA4C;AACpE,QAAM,EAAE,QAAQ,UAAU,iBAAiB,IAAI;AAC/C,QAAM,QAAQ,IAAI,SAAwB;AAC1C,QAAM,cAAc,IAAI,SAA+B;AAEvD,SAAO;AAAA,IACL,MAAM,MAAM,eAAmC;AAC7C,YAAM,WAAW,gBAAgB,eAAe,KAAK;AAGrD,UAAI,WAAW,WAAW,IAAI,MAAM,IAAI,SAAS,IAAI;AAErD,UAAI,CAAC,UAAU;AAEb,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,iBAAiB;AAAA,UACjD,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,YAC/B,gBAAgB;AAAA,UAClB;AAAA,QACF,CAAC;AAED,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAM,IAAI;AAAA,YACR,wBAAwB,IAAI,MAAM,MAAM,KAAK;AAAA,UAC/C;AAAA,QACF;AAEA,cAAM,OAAO,MAAM,IAAI,KAAK;AAE5B,mBAAY,KAAK,QAAQ;AAGzB,YAAI,WAAW,GAAG;AAChB,gBAAM,IAAI,WAAW,UAAU,QAAQ;AAAA,QACzC;AAAA,MACF;AAGA,YAAM,cAAc,SAAS,YAAY;AAGzC,YAAM,aAAa,MAAM,QAAQ,SAAS,KAAK,IAAI,SAAS,QAAQ,CAAC;AAGrE,YAAM,WAAW,YAAY,YAAY;AAAA,QACvC,MAAM,eAAe;AAAA,QACrB,QAAQ,eAAe;AAAA,MACzB,CAAC;AAGD,aAAO,aAAa,UAAU,aAAa,MAAM;AAAA,IACnD;AAAA,IAEA,MAAM,OAAOC,UAAgD;AAC3D,YAAM,WAAW,gBAAgBA,UAAS,KAAK;AAE/C,UAAI,SAAS,WAAW,IAAI,YAAY,IAAI,QAAQ,IAAI;AAExD,UAAI,CAAC,QAAQ;AACX,cAAM,MAAM,MAAM,MAAM,GAAG,OAAO,kBAAkB;AAAA,UAClD,SAAS;AAAA,YACP,eAAe,UAAU,MAAM;AAAA,UACjC;AAAA,QACF,CAAC;AAED,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,QAAQ,MAAM,IAAI,KAAK;AAC7B,gBAAM,IAAI;AAAA,YACR,wBAAwB,IAAI,MAAM,MAAM,KAAK;AAAA,UAC/C;AAAA,QACF;AAEA,iBAAS,oBAAoB,MAAM,IAAI,KAAK,CAAC;AAE7C,YAAI,WAAW,GAAG;AAChB,sBAAY,IAAI,UAAU,QAAQ,QAAQ;AAAA,QAC5C;AAAA,MACF;AAEA,aAAO;AAAA,QACL,cAAc,wBAAwB,MAAM;AAAA,QAC5C,OAAO;AAAA,UACL,WAAW,oBAAoB,OAAO,MAAM;AAAA,UAC5C,SAAS,kBAAkB,SAAS,MAAM;AAAA,QAC5C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEA,SAAS,gBAAgB,OAAkC;AACzD,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,SAAO;AACT;;;AC3HA,IAAAC,aAAiC;AACjC,gCAAyC;AAEzC,IAAM,aAAa;AACnB,IAAM,kBAAkB;AAIxB,IAAM,kBAAkB;AAAA,EACtB;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAU;AAAA,EAAa;AAAA,EAC1C;AAAA,EAAS;AAAA,EAAY;AAAA,EAAY;AAAA,EACjC;AAAA,EAAU;AAAA,EAAY;AAAA,EAAU;AAAA,EAChC;AAAA,EAAmB;AAAA,EAAiB;AACtC;AAEA,IAAM,YAAY,oBAAI,IAAI;AAAA,EACxB;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAU;AAAA,EACnD;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAU;AAAA,EAAU;AAAA,EAAO;AAAA,EAC7C;AAAA,EAAY;AAAA,EAAa;AAAA,EACzB;AAAA,EAAqB;AAAA,EAAW;AAAA,EAAW;AAAA,EAC3C;AAAA,EAAmB;AAAA,EAAiB;AAAA,EACpC;AAAA,EAAa;AAAA,EAAe;AAAA,EAC5B;AAAA,EAAW;AAAA,EAAS;AAAA,EAAS;AAAA,EAAO;AAAA,EAAU;AAAA,EAC9C;AAAA,EAAY;AAAA,EAAY;AAAA,EAAU;AACpC,CAAC;AAED,SAAS,YACP,KACA,MACwB;AACxB,QAAM,SAAiC,CAAC;AACxC,QAAM,aAAa,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,IAAI;AAC3D,QAAM,aAAa,MAAM,UAAU,IAAI,IAAI,KAAK,OAAO,IAAI;AAE3D,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,GAAG,GAAG;AAC9C,QAAI,UAAU,OAAW;AACzB,QAAI,YAAY,IAAI,GAAG,EAAG;AAC1B,QAAI,YAAY,IAAI,GAAG,GAAG;AAAE,aAAO,GAAG,IAAI;AAAO;AAAA,IAAU;AAC3D,QAAI,UAAU,IAAI,GAAG,GAAG;AAAE,aAAO,GAAG,IAAI;AAAO;AAAA,IAAU;AACzD,QAAI,gBAAgB,KAAK,OAAK,EAAE,KAAK,GAAG,CAAC,EAAG;AAC5C,WAAO,GAAG,IAAI;AAAA,EAChB;AAEA,SAAO,OAAO;AACd,SAAO;AACT;AAEA,SAAS,SACP,QACA,MACwB;AACxB,MAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,WAAO,EAAE,GAAG,QAAQ,MAAM,OAAO;AAAA,EACnC;AACA,UAAQ,QAAQ;AAAA,IACd,KAAK;AACH,aAAO,EAAE,GAAG,QAAQ,KAAK,MAAM,OAAO;AAAA,IACxC,KAAK;AACH,aAAO;AAAA,QACL,MAAM,QAAQ,IAAI,QAAQ;AAAA,QAC1B,MAAM,QAAQ,IAAI,QAAQ;AAAA,QAC1B,MAAM;AAAA,MACR;AAAA,IACF,KAAK;AAAA,IACL;AACE,aAAO,YAAY,QAAQ,KAAK;AAAA,QAC9B,SAAS,MAAM;AAAA,QACf,SAAS,MAAM;AAAA,MACjB,CAAC;AAAA,EACL;AACF;AAMA,IAAM,gBAAgB,oBAAI,IAAI;AAAA,EAC5B;AAAA,EAAO;AAAA,EAAM;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAS;AAAA,EAC9D;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAM;AAAA,EAAM;AAAA,EAAS;AAAA,EAAO;AAAA,EAAO;AAAA,EAAO;AAAA,EACxD;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAS;AAAA,EACtD;AAAA,EAAQ;AAAA,EAAO;AAAA,EAAY;AAAA,EAAY;AAAA,EAAM;AAAA,EAAM;AAAA,EACnD;AAAA,EAAQ;AAAA,EAAM;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAQ;AAAA,EACtC;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAa;AAAA,EAAU;AAAA,EAAO;AAAA,EAChD;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAY;AAAA,EACtD;AAAA,EAAY;AAAA,EAAY;AAAA,EAAO;AAAA,EAAS;AAAA,EAAM;AAChD,CAAC;AAED,IAAM,uBAAuB,oBAAI,IAAI;AAAA,EACnC;AAAA,EAAU;AAAA,EAAO;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAU;AAAA,EAAO;AAAA,EAClD;AAAA,EAAS;AAAA,EAAY;AAAA,EAAY;AAAA,EAAS;AAAA,EAAU;AAAA,EACpD;AAAA,EAAY;AAAA,EAAW;AAAA,EAAY;AAAA,EAAY;AACjD,CAAC;AAED,IAAM,qBAAqB;AAAA,EACzB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,SAAS,gBACP,SACA,WACA,WACe;AACf,QAAM,UAAU,QAAQ,KAAK;AAG7B,MAAI,mBAAmB,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,EAAG,QAAO;AAC1D,MAAI,WAAW,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,EAAG,QAAO;AAGlD,QAAM,WAAW,QAAQ,QAAQ,kBAAkB,EAAE;AACrD,QAAM,QAAQ,SAAS,MAAM,KAAK;AAClC,QAAM,OAAO,MAAM,CAAC,GAAG,QAAQ,SAAS,EAAE;AAE1C,MAAI,CAAC,KAAM,QAAO;AAGlB,MAAI,SAAS,OAAO;AAClB,UAAM,MAAM,MAAM,CAAC;AACnB,QAAI,OAAO,qBAAqB,IAAI,GAAG,EAAG,QAAO;AACjD,WAAO;AAAA,EACT;AAEA,MAAI,cAAc,IAAI,IAAI,EAAG,QAAO;AACpC,MAAI,WAAW,IAAI,IAAI,EAAG,QAAO;AAEjC,SAAO;AACT;AAuEO,SAAS,eAAe,SAA2B;AACxD,QAAM,MAAM,SAAS,OAAO,QAAQ,IAAI;AACxC,QAAM,UAAU,KAAK,IAAI,SAAS,WAAW,iBAAiB,GAAO;AACrE,QAAM,YAAY,SAAS,aAAa;AACxC,QAAM,iBAAiB,SAAS,YAAY;AAE5C,QAAM,YAAY,SAAS,gBAAgB,IAAI,IAAI,QAAQ,aAAa,IAAI;AAC5E,QAAM,YAAY,SAAS;AAE3B,QAAM,UAAU,SAAS,SAAS,OAAO,QAAQ,OAAO;AAExD,QAAM,SAAS;AAAA,IACb,MAAM;AAAA,IACN,YAAY;AAAA,MACV,SAAS;AAAA,QACP,MAAM;AAAA,QACN,aAAa;AAAA,MACf;AAAA,IACF;AAAA,IACA,UAAU,CAAC,SAAS;AAAA,EACtB;AAGA,QAAM,gBACJ,mBAAmB,WACf,OACA,mBAAmB,UACjB,QACA,OAAO,SAAkB;AACvB,UAAM,EAAE,QAAQ,IAAI;AACpB,QAAI,OAAO,mBAAmB,YAAY;AACxC,aAAO,eAAe,OAAO;AAAA,IAC/B;AAEA,UAAM,SAAS,gBAAgB,SAAS,WAAW,SAAS;AAC5D,WAAO,WAAW;AAAA,EACpB;AAER,aAAO,iBAAK;AAAA,IACV,aACE;AAAA,IACF,iBAAa,uBAAW,MAA0C;AAAA,IAClE;AAAA,IACA,SAAS,OAAO,SAAkC;AAChD,YAAM,EAAE,QAAQ,IAAI;AAGpB,UACE,mBAAmB,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,KAC5C,WAAW,KAAK,OAAK,EAAE,KAAK,OAAO,CAAC,GACpC;AACA,eAAO;AAAA,UACL,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,YAAY;AAAA,QACd;AAAA,MACF;AAEA,YAAM,QAAQ,KAAK,IAAI;AAEvB,aAAO,IAAI,QAAwB,CAAC,YAAY;AAC9C,cAAM,YAAQ;AAAA,UACZ;AAAA,UACA;AAAA,YACE;AAAA,YACA;AAAA,YACA,OAAO;AAAA,YACP,WAAW,KAAK,OAAO;AAAA,YACvB,KAAK;AAAA,UACP;AAAA,UACA,CAAC,OAA6B,QAAgB,WAAmB;AAC/D,kBAAM,aAAa,KAAK,IAAI,IAAI;AAChC,kBAAM,WAAW,OAAO,WAAW;AACnC,kBAAM,WAAW,WACb,MACA,OAAO,OAAO,SAAS,WACrB,MAAM,OACN,QACE,IACA;AAER,oBAAQ;AAAA,cACN,QAAQ,SAAS,QAAQ,SAAS;AAAA,cAClC,QAAQ,SAAS,QAAQ,SAAS;AAAA,cAClC;AAAA,cACA;AAAA,cACA,GAAI,WAAW,EAAE,UAAU,KAAK,IAAI,CAAC;AAAA,YACvC,CAAC;AAAA,UACH;AAAA,QACF;AAEA,cAAM,GAAG,SAAS,MAAM;AACtB,kBAAQ;AAAA,YACN,QAAQ;AAAA,YACR,QAAQ;AAAA,YACR,UAAU;AAAA,YACV,YAAY,KAAK,IAAI,IAAI;AAAA,UAC3B,CAAC;AAAA,QACH,CAAC;AAAA,MACH,CAAC;AAAA,IACH;AAAA,EACF,CAAC;AACH;AAEA,SAAS,SAAS,KAAa,KAAqB;AAClD,MAAI,IAAI,UAAU,IAAK,QAAO;AAC9B,QAAM,OAAO,KAAK,MAAM,MAAM,CAAC,IAAI;AACnC,SACE,IAAI,MAAM,GAAG,IAAI,IACjB;AAAA;AAAA,OAAY,IAAI,SAAS,GAAG;AAAA;AAAA,IAC5B,IAAI,MAAM,CAAC,IAAI;AAEnB;","names":["import_ai","options","import_ai"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -45,9 +45,41 @@ interface ToolFilterOptions {
|
|
|
45
45
|
/** Cache TTL in seconds. true = 60s, number = custom TTL, false/undefined = no cache */
|
|
46
46
|
cache?: boolean | number;
|
|
47
47
|
}
|
|
48
|
+
/** A parsed skill section from the aggregated SKILL.md */
|
|
49
|
+
interface ParsedSkill {
|
|
50
|
+
/** Display name, e.g. "Gmail", "Calendar", "Slack" */
|
|
51
|
+
name: string;
|
|
52
|
+
/** The full markdown content for this skill section */
|
|
53
|
+
content: string;
|
|
54
|
+
}
|
|
55
|
+
/** The parsed result of the aggregated skills markdown */
|
|
56
|
+
interface ParsedSkillsDocument {
|
|
57
|
+
/** YAML frontmatter fields */
|
|
58
|
+
frontmatter: {
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
};
|
|
62
|
+
/** The preamble text (between frontmatter and first ---) */
|
|
63
|
+
preamble: string;
|
|
64
|
+
/** Individual skill sections split on \n---\n */
|
|
65
|
+
skills: ParsedSkill[];
|
|
66
|
+
}
|
|
67
|
+
/** Options for the skills() method */
|
|
68
|
+
interface SkillsOptions {
|
|
69
|
+
/** Cache TTL in seconds. true = 60s, number = custom TTL, false/undefined = no cache */
|
|
70
|
+
cache?: boolean | number;
|
|
71
|
+
}
|
|
72
|
+
/** Return type of client.skills() */
|
|
73
|
+
interface SkillsResult {
|
|
74
|
+
/** System prompt text with skill summaries for the LLM */
|
|
75
|
+
systemPrompt: string;
|
|
76
|
+
/** Tool objects: loadSkill and apiCall */
|
|
77
|
+
tools: Record<string, ai.Tool>;
|
|
78
|
+
}
|
|
48
79
|
/** The supyagent client interface */
|
|
49
80
|
interface SupyagentClient {
|
|
50
81
|
tools(options?: ToolFilterOptions): Promise<Record<string, ai.Tool>>;
|
|
82
|
+
skills(options?: SkillsOptions): Promise<SkillsResult>;
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
/**
|
|
@@ -124,4 +156,37 @@ interface BashToolResult {
|
|
|
124
156
|
*/
|
|
125
157
|
declare function createBashTool(options?: BashToolOptions): ai.Tool<unknown, BashToolResult>;
|
|
126
158
|
|
|
127
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Parse the aggregated skills markdown into structured sections.
|
|
161
|
+
*/
|
|
162
|
+
declare function parseSkillsMarkdown(markdown: string): ParsedSkillsDocument;
|
|
163
|
+
/**
|
|
164
|
+
* Build a concise system prompt from parsed skills.
|
|
165
|
+
* Lists available skills with one-line summaries, not full docs.
|
|
166
|
+
*/
|
|
167
|
+
declare function buildSkillsSystemPrompt(doc: ParsedSkillsDocument): string;
|
|
168
|
+
/**
|
|
169
|
+
* Find a skill by name (case-insensitive).
|
|
170
|
+
*/
|
|
171
|
+
declare function findSkill(skills: ParsedSkill[], name: string): ParsedSkill | null;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Create the loadSkill tool.
|
|
175
|
+
* Returns detailed documentation for a specific skill.
|
|
176
|
+
*/
|
|
177
|
+
declare function createLoadSkillTool(skills: ParsedSkill[]): ai.Tool<unknown, {
|
|
178
|
+
error: string;
|
|
179
|
+
name?: undefined;
|
|
180
|
+
documentation?: undefined;
|
|
181
|
+
} | {
|
|
182
|
+
name: string;
|
|
183
|
+
documentation: string;
|
|
184
|
+
error?: undefined;
|
|
185
|
+
}>;
|
|
186
|
+
/**
|
|
187
|
+
* Create the apiCall tool.
|
|
188
|
+
* Makes authenticated HTTP requests to the Supyagent API.
|
|
189
|
+
*/
|
|
190
|
+
declare function createApiCallTool(baseUrl: string, apiKey: string): ai.Tool<unknown, any>;
|
|
191
|
+
|
|
192
|
+
export { type BashToolOptions, type BashToolResult, type OpenAITool, type ParsedSkill, type ParsedSkillsDocument, type SkillsOptions, type SkillsResult, type SupyagentClient, type SupyagentOptions, type ToolFilterOptions, type ToolMetadata, type ToolsResponse, buildSkillsSystemPrompt, createApiCallTool, createBashTool, createLoadSkillTool, findSkill, parseSkillsMarkdown, supyagent };
|
package/dist/index.d.ts
CHANGED
|
@@ -45,9 +45,41 @@ interface ToolFilterOptions {
|
|
|
45
45
|
/** Cache TTL in seconds. true = 60s, number = custom TTL, false/undefined = no cache */
|
|
46
46
|
cache?: boolean | number;
|
|
47
47
|
}
|
|
48
|
+
/** A parsed skill section from the aggregated SKILL.md */
|
|
49
|
+
interface ParsedSkill {
|
|
50
|
+
/** Display name, e.g. "Gmail", "Calendar", "Slack" */
|
|
51
|
+
name: string;
|
|
52
|
+
/** The full markdown content for this skill section */
|
|
53
|
+
content: string;
|
|
54
|
+
}
|
|
55
|
+
/** The parsed result of the aggregated skills markdown */
|
|
56
|
+
interface ParsedSkillsDocument {
|
|
57
|
+
/** YAML frontmatter fields */
|
|
58
|
+
frontmatter: {
|
|
59
|
+
name: string;
|
|
60
|
+
description: string;
|
|
61
|
+
};
|
|
62
|
+
/** The preamble text (between frontmatter and first ---) */
|
|
63
|
+
preamble: string;
|
|
64
|
+
/** Individual skill sections split on \n---\n */
|
|
65
|
+
skills: ParsedSkill[];
|
|
66
|
+
}
|
|
67
|
+
/** Options for the skills() method */
|
|
68
|
+
interface SkillsOptions {
|
|
69
|
+
/** Cache TTL in seconds. true = 60s, number = custom TTL, false/undefined = no cache */
|
|
70
|
+
cache?: boolean | number;
|
|
71
|
+
}
|
|
72
|
+
/** Return type of client.skills() */
|
|
73
|
+
interface SkillsResult {
|
|
74
|
+
/** System prompt text with skill summaries for the LLM */
|
|
75
|
+
systemPrompt: string;
|
|
76
|
+
/** Tool objects: loadSkill and apiCall */
|
|
77
|
+
tools: Record<string, ai.Tool>;
|
|
78
|
+
}
|
|
48
79
|
/** The supyagent client interface */
|
|
49
80
|
interface SupyagentClient {
|
|
50
81
|
tools(options?: ToolFilterOptions): Promise<Record<string, ai.Tool>>;
|
|
82
|
+
skills(options?: SkillsOptions): Promise<SkillsResult>;
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
/**
|
|
@@ -124,4 +156,37 @@ interface BashToolResult {
|
|
|
124
156
|
*/
|
|
125
157
|
declare function createBashTool(options?: BashToolOptions): ai.Tool<unknown, BashToolResult>;
|
|
126
158
|
|
|
127
|
-
|
|
159
|
+
/**
|
|
160
|
+
* Parse the aggregated skills markdown into structured sections.
|
|
161
|
+
*/
|
|
162
|
+
declare function parseSkillsMarkdown(markdown: string): ParsedSkillsDocument;
|
|
163
|
+
/**
|
|
164
|
+
* Build a concise system prompt from parsed skills.
|
|
165
|
+
* Lists available skills with one-line summaries, not full docs.
|
|
166
|
+
*/
|
|
167
|
+
declare function buildSkillsSystemPrompt(doc: ParsedSkillsDocument): string;
|
|
168
|
+
/**
|
|
169
|
+
* Find a skill by name (case-insensitive).
|
|
170
|
+
*/
|
|
171
|
+
declare function findSkill(skills: ParsedSkill[], name: string): ParsedSkill | null;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Create the loadSkill tool.
|
|
175
|
+
* Returns detailed documentation for a specific skill.
|
|
176
|
+
*/
|
|
177
|
+
declare function createLoadSkillTool(skills: ParsedSkill[]): ai.Tool<unknown, {
|
|
178
|
+
error: string;
|
|
179
|
+
name?: undefined;
|
|
180
|
+
documentation?: undefined;
|
|
181
|
+
} | {
|
|
182
|
+
name: string;
|
|
183
|
+
documentation: string;
|
|
184
|
+
error?: undefined;
|
|
185
|
+
}>;
|
|
186
|
+
/**
|
|
187
|
+
* Create the apiCall tool.
|
|
188
|
+
* Makes authenticated HTTP requests to the Supyagent API.
|
|
189
|
+
*/
|
|
190
|
+
declare function createApiCallTool(baseUrl: string, apiKey: string): ai.Tool<unknown, any>;
|
|
191
|
+
|
|
192
|
+
export { type BashToolOptions, type BashToolResult, type OpenAITool, type ParsedSkill, type ParsedSkillsDocument, type SkillsOptions, type SkillsResult, type SupyagentClient, type SupyagentOptions, type ToolFilterOptions, type ToolMetadata, type ToolsResponse, buildSkillsSystemPrompt, createApiCallTool, createBashTool, createLoadSkillTool, findSkill, parseSkillsMarkdown, supyagent };
|
package/dist/index.js
CHANGED
|
@@ -123,12 +123,188 @@ function filterTools(tools, options) {
|
|
|
123
123
|
return filtered;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
// src/core/skill-parser.ts
|
|
127
|
+
function parseFrontmatter(markdown) {
|
|
128
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
129
|
+
if (!match) {
|
|
130
|
+
return {
|
|
131
|
+
frontmatter: { name: "supyagent-skills", description: "" },
|
|
132
|
+
body: markdown
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const [, yamlBlock, body] = match;
|
|
136
|
+
const fields = {};
|
|
137
|
+
for (const line of yamlBlock.split("\n")) {
|
|
138
|
+
const colonIndex = line.indexOf(":");
|
|
139
|
+
if (colonIndex > 0) {
|
|
140
|
+
const key = line.slice(0, colonIndex).trim();
|
|
141
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
142
|
+
fields[key] = value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return {
|
|
146
|
+
frontmatter: {
|
|
147
|
+
name: fields.name || "supyagent-skills",
|
|
148
|
+
description: fields.description || ""
|
|
149
|
+
},
|
|
150
|
+
body
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function extractSkillName(section) {
|
|
154
|
+
const match = section.match(/^#\s+(.+)/m);
|
|
155
|
+
return match ? match[1].trim() : "Unknown";
|
|
156
|
+
}
|
|
157
|
+
function parseSkillsMarkdown(markdown) {
|
|
158
|
+
const { frontmatter, body } = parseFrontmatter(markdown);
|
|
159
|
+
const parts = body.split(/\n---\n/);
|
|
160
|
+
const preamble = (parts[0] || "").trim();
|
|
161
|
+
const skills = [];
|
|
162
|
+
for (let i = 1; i < parts.length; i++) {
|
|
163
|
+
const content = parts[i].trim();
|
|
164
|
+
if (!content) continue;
|
|
165
|
+
const name = extractSkillName(content);
|
|
166
|
+
skills.push({ name, content });
|
|
167
|
+
}
|
|
168
|
+
return { frontmatter, preamble, skills };
|
|
169
|
+
}
|
|
170
|
+
function buildSkillsSystemPrompt(doc) {
|
|
171
|
+
if (doc.skills.length === 0) {
|
|
172
|
+
return "No skills are currently available. The user has no connected integrations.";
|
|
173
|
+
}
|
|
174
|
+
const skillList = doc.skills.map((s) => `- **${s.name}**`).join("\n");
|
|
175
|
+
return `You have access to the following skills via Supyagent:
|
|
176
|
+
|
|
177
|
+
${skillList}
|
|
178
|
+
|
|
179
|
+
To use a skill:
|
|
180
|
+
1. Call \`loadSkill\` with the skill name to get detailed API documentation
|
|
181
|
+
2. Call \`apiCall\` to make authenticated HTTP requests to the API endpoints described in the skill docs
|
|
182
|
+
|
|
183
|
+
Available skill names: ${doc.skills.map((s) => s.name).join(", ")}`;
|
|
184
|
+
}
|
|
185
|
+
function findSkill(skills, name) {
|
|
186
|
+
const normalized = name.toLowerCase().trim();
|
|
187
|
+
return skills.find((s) => s.name.toLowerCase() === normalized) ?? skills.find((s) => s.name.toLowerCase().includes(normalized)) ?? null;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// src/tools/skills.ts
|
|
191
|
+
import { tool as tool2, jsonSchema as jsonSchema2 } from "ai";
|
|
192
|
+
function createLoadSkillTool(skills) {
|
|
193
|
+
const availableNames = skills.map((s) => s.name).join(", ");
|
|
194
|
+
return tool2({
|
|
195
|
+
description: `Load detailed API documentation for a specific skill. Available skills: ${availableNames}. Call this before using apiCall to understand the available endpoints, parameters, and examples.`,
|
|
196
|
+
inputSchema: jsonSchema2({
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: {
|
|
199
|
+
name: {
|
|
200
|
+
type: "string",
|
|
201
|
+
description: `The skill name to load. Available: ${availableNames}`
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
required: ["name"]
|
|
205
|
+
}),
|
|
206
|
+
execute: async (args) => {
|
|
207
|
+
const { name } = args;
|
|
208
|
+
const skill = findSkill(skills, name);
|
|
209
|
+
if (!skill) {
|
|
210
|
+
return {
|
|
211
|
+
error: `Skill "${name}" not found. Available skills: ${availableNames}`
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return {
|
|
215
|
+
name: skill.name,
|
|
216
|
+
documentation: skill.content
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
var VALID_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "PATCH", "DELETE"]);
|
|
222
|
+
function createApiCallTool(baseUrl, apiKey) {
|
|
223
|
+
return tool2({
|
|
224
|
+
description: "Make an authenticated HTTP request to the Supyagent API. Use loadSkill first to understand available endpoints. The authorization header and base URL are handled automatically \u2014 only provide the path (e.g., /api/v1/google/gmail/messages).",
|
|
225
|
+
inputSchema: jsonSchema2({
|
|
226
|
+
type: "object",
|
|
227
|
+
properties: {
|
|
228
|
+
method: {
|
|
229
|
+
type: "string",
|
|
230
|
+
enum: ["GET", "POST", "PUT", "PATCH", "DELETE"],
|
|
231
|
+
description: "HTTP method"
|
|
232
|
+
},
|
|
233
|
+
path: {
|
|
234
|
+
type: "string",
|
|
235
|
+
description: 'API path starting with /api/v1/ (e.g., "/api/v1/google/gmail/messages")'
|
|
236
|
+
},
|
|
237
|
+
body: {
|
|
238
|
+
type: "object",
|
|
239
|
+
description: "Request body for POST/PUT/PATCH requests",
|
|
240
|
+
additionalProperties: true
|
|
241
|
+
},
|
|
242
|
+
params: {
|
|
243
|
+
type: "object",
|
|
244
|
+
description: "Query parameters",
|
|
245
|
+
additionalProperties: true
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
required: ["method", "path"]
|
|
249
|
+
}),
|
|
250
|
+
execute: async (args) => {
|
|
251
|
+
const {
|
|
252
|
+
method: rawMethod,
|
|
253
|
+
path,
|
|
254
|
+
body,
|
|
255
|
+
params
|
|
256
|
+
} = args;
|
|
257
|
+
const method = rawMethod.toUpperCase();
|
|
258
|
+
if (!VALID_METHODS.has(method)) {
|
|
259
|
+
return { error: `Invalid HTTP method: ${method}` };
|
|
260
|
+
}
|
|
261
|
+
if (!path.startsWith("/api/v1/")) {
|
|
262
|
+
return { error: `Path must start with /api/v1/. Got: ${path}` };
|
|
263
|
+
}
|
|
264
|
+
let url = `${baseUrl}${path}`;
|
|
265
|
+
if (params && Object.keys(params).length > 0) {
|
|
266
|
+
const searchParams = new URLSearchParams();
|
|
267
|
+
for (const [key, value] of Object.entries(params)) {
|
|
268
|
+
if (value !== void 0 && value !== null) {
|
|
269
|
+
searchParams.set(key, String(value));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const qs = searchParams.toString();
|
|
273
|
+
if (qs) url += `?${qs}`;
|
|
274
|
+
}
|
|
275
|
+
const fetchOptions = {
|
|
276
|
+
method,
|
|
277
|
+
headers: {
|
|
278
|
+
Authorization: `Bearer ${apiKey}`,
|
|
279
|
+
"Content-Type": "application/json"
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
if (body && method !== "GET" && method !== "DELETE") {
|
|
283
|
+
fetchOptions.body = JSON.stringify(body);
|
|
284
|
+
}
|
|
285
|
+
try {
|
|
286
|
+
const response = await fetch(url, fetchOptions);
|
|
287
|
+
const data = await response.json();
|
|
288
|
+
if (!response.ok) {
|
|
289
|
+
return { error: `API returned ${response.status}`, details: data };
|
|
290
|
+
}
|
|
291
|
+
return data;
|
|
292
|
+
} catch (err) {
|
|
293
|
+
return {
|
|
294
|
+
error: `Request failed: ${err instanceof Error ? err.message : String(err)}`
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
126
301
|
// src/core/client.ts
|
|
127
302
|
var DEFAULT_BASE_URL = "https://app.supyagent.com";
|
|
128
303
|
var CACHE_KEY = "tools";
|
|
129
304
|
function supyagent(options) {
|
|
130
305
|
const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;
|
|
131
306
|
const cache = new TTLCache();
|
|
307
|
+
const skillsCache = new TTLCache();
|
|
132
308
|
return {
|
|
133
309
|
async tools(filterOptions) {
|
|
134
310
|
const cacheTTL = resolveCacheTTL(filterOptions?.cache);
|
|
@@ -159,6 +335,34 @@ function supyagent(options) {
|
|
|
159
335
|
except: filterOptions?.except
|
|
160
336
|
});
|
|
161
337
|
return convertTools(filtered, toolBaseUrl, apiKey);
|
|
338
|
+
},
|
|
339
|
+
async skills(options2) {
|
|
340
|
+
const cacheTTL = resolveCacheTTL(options2?.cache);
|
|
341
|
+
let parsed = cacheTTL > 0 ? skillsCache.get("skills") : void 0;
|
|
342
|
+
if (!parsed) {
|
|
343
|
+
const res = await fetch(`${baseUrl}/api/v1/skills`, {
|
|
344
|
+
headers: {
|
|
345
|
+
Authorization: `Bearer ${apiKey}`
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
if (!res.ok) {
|
|
349
|
+
const error = await res.text();
|
|
350
|
+
throw new Error(
|
|
351
|
+
`Supyagent API error (${res.status}): ${error}`
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
parsed = parseSkillsMarkdown(await res.text());
|
|
355
|
+
if (cacheTTL > 0) {
|
|
356
|
+
skillsCache.set("skills", parsed, cacheTTL);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
systemPrompt: buildSkillsSystemPrompt(parsed),
|
|
361
|
+
tools: {
|
|
362
|
+
loadSkill: createLoadSkillTool(parsed.skills),
|
|
363
|
+
apiCall: createApiCallTool(baseUrl, apiKey)
|
|
364
|
+
}
|
|
365
|
+
};
|
|
162
366
|
}
|
|
163
367
|
};
|
|
164
368
|
}
|
|
@@ -169,7 +373,7 @@ function resolveCacheTTL(cache) {
|
|
|
169
373
|
}
|
|
170
374
|
|
|
171
375
|
// src/tools/bash.ts
|
|
172
|
-
import { tool as
|
|
376
|
+
import { tool as tool3, jsonSchema as jsonSchema3 } from "ai";
|
|
173
377
|
import { exec } from "child_process";
|
|
174
378
|
var MAX_OUTPUT = 3e4;
|
|
175
379
|
var DEFAULT_TIMEOUT = 3e4;
|
|
@@ -405,9 +609,9 @@ function createBashTool(options) {
|
|
|
405
609
|
const safety = classifyCommand(command, extraSafe, extraDeny);
|
|
406
610
|
return safety !== "safe";
|
|
407
611
|
};
|
|
408
|
-
return
|
|
612
|
+
return tool3({
|
|
409
613
|
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.",
|
|
410
|
-
inputSchema:
|
|
614
|
+
inputSchema: jsonSchema3(schema),
|
|
411
615
|
needsApproval,
|
|
412
616
|
execute: async (args) => {
|
|
413
617
|
const { command } = args;
|
|
@@ -465,7 +669,12 @@ function truncate(str, max) {
|
|
|
465
669
|
` + str.slice(-half);
|
|
466
670
|
}
|
|
467
671
|
export {
|
|
672
|
+
buildSkillsSystemPrompt,
|
|
673
|
+
createApiCallTool,
|
|
468
674
|
createBashTool,
|
|
675
|
+
createLoadSkillTool,
|
|
676
|
+
findSkill,
|
|
677
|
+
parseSkillsMarkdown,
|
|
469
678
|
supyagent
|
|
470
679
|
};
|
|
471
680
|
//# sourceMappingURL=index.js.map
|
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\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/core/cache.ts","../src/core/tool-converter.ts","../src/core/http-executor.ts","../src/core/skill-parser.ts","../src/tools/skills.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 { ParsedSkill, ParsedSkillsDocument } from \"./types.js\";\n\n/**\n * Parse YAML frontmatter from the beginning of a markdown document.\n */\nfunction parseFrontmatter(markdown: string): {\n frontmatter: { name: string; description: string };\n body: string;\n} {\n const match = markdown.match(/^---\\n([\\s\\S]*?)\\n---\\n?([\\s\\S]*)$/);\n if (!match) {\n return {\n frontmatter: { name: \"supyagent-skills\", description: \"\" },\n body: markdown,\n };\n }\n\n const [, yamlBlock, body] = match;\n const fields: Record<string, string> = {};\n for (const line of yamlBlock!.split(\"\\n\")) {\n const colonIndex = line.indexOf(\":\");\n if (colonIndex > 0) {\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n fields[key] = value;\n }\n }\n\n return {\n frontmatter: {\n name: fields.name || \"supyagent-skills\",\n description: fields.description || \"\",\n },\n body: body!,\n };\n}\n\n/**\n * Extract the skill name from a section starting with `# Name`.\n */\nfunction extractSkillName(section: string): string {\n const match = section.match(/^#\\s+(.+)/m);\n return match ? match[1].trim() : \"Unknown\";\n}\n\n/**\n * Parse the aggregated skills markdown into structured sections.\n */\nexport function parseSkillsMarkdown(markdown: string): ParsedSkillsDocument {\n const { frontmatter, body } = parseFrontmatter(markdown);\n\n // Split body on \\n---\\n (the section separator)\n const parts = body.split(/\\n---\\n/);\n\n // First part is the preamble (general header info)\n const preamble = (parts[0] || \"\").trim();\n\n // Remaining parts are individual skill sections\n const skills: ParsedSkill[] = [];\n for (let i = 1; i < parts.length; i++) {\n const content = parts[i]!.trim();\n if (!content) continue;\n const name = extractSkillName(content);\n skills.push({ name, content });\n }\n\n return { frontmatter, preamble, skills };\n}\n\n/**\n * Build a concise system prompt from parsed skills.\n * Lists available skills with one-line summaries, not full docs.\n */\nexport function buildSkillsSystemPrompt(\n doc: ParsedSkillsDocument,\n): string {\n if (doc.skills.length === 0) {\n return \"No skills are currently available. The user has no connected integrations.\";\n }\n\n const skillList = doc.skills.map((s) => `- **${s.name}**`).join(\"\\n\");\n\n return `You have access to the following skills via Supyagent:\n\n${skillList}\n\nTo use a skill:\n1. Call \\`loadSkill\\` with the skill name to get detailed API documentation\n2. Call \\`apiCall\\` to make authenticated HTTP requests to the API endpoints described in the skill docs\n\nAvailable skill names: ${doc.skills.map((s) => s.name).join(\", \")}`;\n}\n\n/**\n * Find a skill by name (case-insensitive).\n */\nexport function findSkill(\n skills: ParsedSkill[],\n name: string,\n): ParsedSkill | null {\n const normalized = name.toLowerCase().trim();\n return (\n skills.find((s) => s.name.toLowerCase() === normalized) ??\n skills.find((s) => s.name.toLowerCase().includes(normalized)) ??\n null\n );\n}\n","import { tool, jsonSchema } from \"ai\";\nimport type { ParsedSkill } from \"../core/types.js\";\nimport { findSkill } from \"../core/skill-parser.js\";\n\n/**\n * Create the loadSkill tool.\n * Returns detailed documentation for a specific skill.\n */\nexport function createLoadSkillTool(skills: ParsedSkill[]) {\n const availableNames = skills.map((s) => s.name).join(\", \");\n\n return tool({\n description: `Load detailed API documentation for a specific skill. Available skills: ${availableNames}. Call this before using apiCall to understand the available endpoints, parameters, and examples.`,\n inputSchema: jsonSchema({\n type: \"object\",\n properties: {\n name: {\n type: \"string\",\n description: `The skill name to load. Available: ${availableNames}`,\n },\n },\n required: [\"name\"],\n } as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => {\n const { name } = args as { name: string };\n const skill = findSkill(skills, name);\n\n if (!skill) {\n return {\n error: `Skill \"${name}\" not found. Available skills: ${availableNames}`,\n };\n }\n\n return {\n name: skill.name,\n documentation: skill.content,\n };\n },\n });\n}\n\nconst VALID_METHODS = new Set([\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"]);\n\n/**\n * Create the apiCall tool.\n * Makes authenticated HTTP requests to the Supyagent API.\n */\nexport function createApiCallTool(baseUrl: string, apiKey: string) {\n return tool({\n description:\n \"Make an authenticated HTTP request to the Supyagent API. Use loadSkill first to understand available endpoints. The authorization header and base URL are handled automatically β only provide the path (e.g., /api/v1/google/gmail/messages).\",\n inputSchema: jsonSchema({\n type: \"object\",\n properties: {\n method: {\n type: \"string\",\n enum: [\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"],\n description: \"HTTP method\",\n },\n path: {\n type: \"string\",\n description:\n 'API path starting with /api/v1/ (e.g., \"/api/v1/google/gmail/messages\")',\n },\n body: {\n type: \"object\",\n description: \"Request body for POST/PUT/PATCH requests\",\n additionalProperties: true,\n },\n params: {\n type: \"object\",\n description: \"Query parameters\",\n additionalProperties: true,\n },\n },\n required: [\"method\", \"path\"],\n } as Parameters<typeof jsonSchema>[0]),\n execute: async (args) => {\n const {\n method: rawMethod,\n path,\n body,\n params,\n } = args as {\n method: string;\n path: string;\n body?: Record<string, unknown>;\n params?: Record<string, unknown>;\n };\n\n const method = rawMethod.toUpperCase();\n\n if (!VALID_METHODS.has(method)) {\n return { error: `Invalid HTTP method: ${method}` };\n }\n\n if (!path.startsWith(\"/api/v1/\")) {\n return { error: `Path must start with /api/v1/. Got: ${path}` };\n }\n\n let url = `${baseUrl}${path}`;\n\n if (params && Object.keys(params).length > 0) {\n const searchParams = new URLSearchParams();\n for (const [key, value] of Object.entries(params)) {\n if (value !== undefined && value !== null) {\n searchParams.set(key, String(value));\n }\n }\n const qs = searchParams.toString();\n if (qs) url += `?${qs}`;\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers: {\n Authorization: `Bearer ${apiKey}`,\n \"Content-Type\": \"application/json\",\n },\n };\n\n if (body && method !== \"GET\" && method !== \"DELETE\") {\n fetchOptions.body = JSON.stringify(body);\n }\n\n try {\n const response = await fetch(url, fetchOptions);\n const data = await response.json();\n\n if (!response.ok) {\n return { error: `API returned ${response.status}`, details: data };\n }\n\n return data;\n } catch (err) {\n return {\n error: `Request failed: ${err instanceof Error ? err.message : String(err)}`,\n };\n }\n },\n });\n}\n","import type {\n SupyagentOptions,\n SupyagentClient,\n ToolFilterOptions,\n ToolsResponse,\n SkillsOptions,\n SkillsResult,\n ParsedSkillsDocument,\n} from \"./types.js\";\nimport { TTLCache } from \"./cache.js\";\nimport { convertTools, filterTools } from \"./tool-converter.js\";\nimport { parseSkillsMarkdown, buildSkillsSystemPrompt } from \"./skill-parser.js\";\nimport { createLoadSkillTool, createApiCallTool } from \"../tools/skills.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 const skillsCache = new TTLCache<ParsedSkillsDocument>();\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 async skills(options?: SkillsOptions): Promise<SkillsResult> {\n const cacheTTL = resolveCacheTTL(options?.cache);\n\n let parsed = cacheTTL > 0 ? skillsCache.get(\"skills\") : undefined;\n\n if (!parsed) {\n const res = await fetch(`${baseUrl}/api/v1/skills`, {\n headers: {\n Authorization: `Bearer ${apiKey}`,\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 parsed = parseSkillsMarkdown(await res.text());\n\n if (cacheTTL > 0) {\n skillsCache.set(\"skills\", parsed, cacheTTL);\n }\n }\n\n return {\n systemPrompt: buildSkillsSystemPrompt(parsed),\n tools: {\n loadSkill: createLoadSkillTool(parsed.skills),\n apiCall: createApiCallTool(baseUrl, apiKey),\n },\n };\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","tool","jsonSchema","options","tool","jsonSchema"]}
|