@jcheesepkg/nanobot 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +139 -0
- package/dist/_virtual/_rolldown/runtime.mjs +40 -0
- package/dist/agent/context.d.mts +44 -0
- package/dist/agent/context.d.mts.map +1 -0
- package/dist/agent/context.mjs +153 -0
- package/dist/agent/context.mjs.map +1 -0
- package/dist/agent/loop.d.mts +56 -0
- package/dist/agent/loop.d.mts.map +1 -0
- package/dist/agent/loop.mjs +232 -0
- package/dist/agent/loop.mjs.map +1 -0
- package/dist/agent/memory.d.mts +30 -0
- package/dist/agent/memory.d.mts.map +1 -0
- package/dist/agent/memory.mjs +77 -0
- package/dist/agent/memory.mjs.map +1 -0
- package/dist/agent/skills.d.mts +43 -0
- package/dist/agent/skills.d.mts.map +1 -0
- package/dist/agent/skills.mjs +159 -0
- package/dist/agent/skills.mjs.map +1 -0
- package/dist/agent/subagent.d.mts +39 -0
- package/dist/agent/subagent.d.mts.map +1 -0
- package/dist/agent/subagent.mjs +167 -0
- package/dist/agent/subagent.mjs.map +1 -0
- package/dist/agent/tools/base.d.mts +16 -0
- package/dist/agent/tools/base.d.mts.map +1 -0
- package/dist/agent/tools/base.mjs +19 -0
- package/dist/agent/tools/base.mjs.map +1 -0
- package/dist/agent/tools/cron.d.mts +49 -0
- package/dist/agent/tools/cron.d.mts.map +1 -0
- package/dist/agent/tools/cron.mjs +101 -0
- package/dist/agent/tools/cron.mjs.map +1 -0
- package/dist/agent/tools/filesystem.d.mts +87 -0
- package/dist/agent/tools/filesystem.d.mts.map +1 -0
- package/dist/agent/tools/filesystem.mjs +145 -0
- package/dist/agent/tools/filesystem.mjs.map +1 -0
- package/dist/agent/tools/message.d.mts +44 -0
- package/dist/agent/tools/message.d.mts.map +1 -0
- package/dist/agent/tools/message.mjs +68 -0
- package/dist/agent/tools/message.mjs.map +1 -0
- package/dist/agent/tools/registry.d.mts +29 -0
- package/dist/agent/tools/registry.d.mts.map +1 -0
- package/dist/agent/tools/registry.mjs +49 -0
- package/dist/agent/tools/registry.mjs.map +1 -0
- package/dist/agent/tools/shell.d.mts +36 -0
- package/dist/agent/tools/shell.d.mts.map +1 -0
- package/dist/agent/tools/shell.mjs +73 -0
- package/dist/agent/tools/shell.mjs.map +1 -0
- package/dist/agent/tools/spawn.d.mts +35 -0
- package/dist/agent/tools/spawn.d.mts.map +1 -0
- package/dist/agent/tools/spawn.mjs +50 -0
- package/dist/agent/tools/spawn.mjs.map +1 -0
- package/dist/agent/tools/web.d.mts +63 -0
- package/dist/agent/tools/web.d.mts.map +1 -0
- package/dist/agent/tools/web.mjs +195 -0
- package/dist/agent/tools/web.mjs.map +1 -0
- package/dist/bus/events.d.mts +29 -0
- package/dist/bus/events.d.mts.map +1 -0
- package/dist/bus/events.mjs +30 -0
- package/dist/bus/events.mjs.map +1 -0
- package/dist/bus/queue.d.mts +38 -0
- package/dist/bus/queue.d.mts.map +1 -0
- package/dist/bus/queue.mjs +90 -0
- package/dist/bus/queue.mjs.map +1 -0
- package/dist/channels/base.d.mts +31 -0
- package/dist/channels/base.d.mts.map +1 -0
- package/dist/channels/base.mjs +49 -0
- package/dist/channels/base.mjs.map +1 -0
- package/dist/channels/manager.d.mts +30 -0
- package/dist/channels/manager.d.mts.map +1 -0
- package/dist/channels/manager.mjs +100 -0
- package/dist/channels/manager.mjs.map +1 -0
- package/dist/channels/telegram.d.mts +23 -0
- package/dist/channels/telegram.d.mts.map +1 -0
- package/dist/channels/telegram.mjs +161 -0
- package/dist/channels/telegram.mjs.map +1 -0
- package/dist/cli/index.d.mts +1 -0
- package/dist/cli/index.mjs +337 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/config/loader.d.mts +14 -0
- package/dist/config/loader.d.mts.map +1 -0
- package/dist/config/loader.mjs +40 -0
- package/dist/config/loader.mjs.map +1 -0
- package/dist/config/schema.d.mts +724 -0
- package/dist/config/schema.d.mts.map +1 -0
- package/dist/config/schema.mjs +123 -0
- package/dist/config/schema.mjs.map +1 -0
- package/dist/cron/service.d.mts +42 -0
- package/dist/cron/service.d.mts.map +1 -0
- package/dist/cron/service.mjs +256 -0
- package/dist/cron/service.mjs.map +1 -0
- package/dist/cron/types.d.mts +55 -0
- package/dist/cron/types.d.mts.map +1 -0
- package/dist/cron/types.mjs +30 -0
- package/dist/cron/types.mjs.map +1 -0
- package/dist/heartbeat/service.d.mts +29 -0
- package/dist/heartbeat/service.d.mts.map +1 -0
- package/dist/heartbeat/service.mjs +96 -0
- package/dist/heartbeat/service.mjs.map +1 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +23 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers/base.d.mts +65 -0
- package/dist/providers/base.d.mts.map +1 -0
- package/dist/providers/base.mjs +1 -0
- package/dist/providers/openai-provider.d.mts +27 -0
- package/dist/providers/openai-provider.d.mts.map +1 -0
- package/dist/providers/openai-provider.mjs +67 -0
- package/dist/providers/openai-provider.mjs.map +1 -0
- package/dist/providers/transcription.d.mts +14 -0
- package/dist/providers/transcription.d.mts.map +1 -0
- package/dist/providers/transcription.mjs +46 -0
- package/dist/providers/transcription.mjs.map +1 -0
- package/dist/session/manager.d.mts +56 -0
- package/dist/session/manager.d.mts.map +1 -0
- package/dist/session/manager.mjs +144 -0
- package/dist/session/manager.mjs.map +1 -0
- package/dist/utils/helpers.d.mts +26 -0
- package/dist/utils/helpers.d.mts.map +1 -0
- package/dist/utils/helpers.mjs +60 -0
- package/dist/utils/helpers.mjs.map +1 -0
- package/dist/utils/which.d.mts +6 -0
- package/dist/utils/which.d.mts.map +1 -0
- package/dist/utils/which.mjs +20 -0
- package/dist/utils/which.mjs.map +1 -0
- package/package.json +45 -0
- package/skills/cron/SKILL.md +40 -0
- package/skills/github/SKILL.md +48 -0
- package/skills/skill-creator/SKILL.md +73 -0
- package/skills/summarize/SKILL.md +47 -0
- package/skills/weather/SKILL.md +43 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { createInboundMessage, init_events } from "../bus/events.mjs";
|
|
2
|
+
import { ToolRegistry } from "./tools/registry.mjs";
|
|
3
|
+
import { ListDirTool, ReadFileTool, WriteFileTool } from "./tools/filesystem.mjs";
|
|
4
|
+
import { ExecTool } from "./tools/shell.mjs";
|
|
5
|
+
import { WebFetchTool, WebSearchTool } from "./tools/web.mjs";
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
|
|
8
|
+
//#region src/agent/subagent.ts
|
|
9
|
+
init_events();
|
|
10
|
+
/**
|
|
11
|
+
* Manages background subagent execution.
|
|
12
|
+
*/
|
|
13
|
+
var SubagentManager = class {
|
|
14
|
+
provider;
|
|
15
|
+
workspace;
|
|
16
|
+
bus;
|
|
17
|
+
model;
|
|
18
|
+
braveApiKey;
|
|
19
|
+
execConfig;
|
|
20
|
+
runningTasks = /* @__PURE__ */ new Map();
|
|
21
|
+
constructor(params) {
|
|
22
|
+
this.provider = params.provider;
|
|
23
|
+
this.workspace = params.workspace;
|
|
24
|
+
this.bus = params.bus;
|
|
25
|
+
this.model = params.model ?? params.provider.getDefaultModel();
|
|
26
|
+
this.braveApiKey = params.braveApiKey;
|
|
27
|
+
this.execConfig = params.execConfig ?? {
|
|
28
|
+
timeout: 60,
|
|
29
|
+
restrictToWorkspace: false
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/** Spawn a subagent to execute a task in the background. */
|
|
33
|
+
async spawn(params) {
|
|
34
|
+
const taskId = randomUUID().slice(0, 8);
|
|
35
|
+
const displayLabel = params.label ?? (params.task.length > 30 ? params.task.slice(0, 30) + "..." : params.task);
|
|
36
|
+
const origin = {
|
|
37
|
+
channel: params.originChannel ?? "cli",
|
|
38
|
+
chatId: params.originChatId ?? "direct"
|
|
39
|
+
};
|
|
40
|
+
const controller = new AbortController();
|
|
41
|
+
this.runningTasks.set(taskId, controller);
|
|
42
|
+
this.runSubagent(taskId, params.task, displayLabel, origin).finally(() => this.runningTasks.delete(taskId));
|
|
43
|
+
console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);
|
|
44
|
+
return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;
|
|
45
|
+
}
|
|
46
|
+
async runSubagent(taskId, task, label, origin) {
|
|
47
|
+
console.log(`Subagent [${taskId}] starting task: ${label}`);
|
|
48
|
+
try {
|
|
49
|
+
const tools = new ToolRegistry();
|
|
50
|
+
tools.register(new ReadFileTool());
|
|
51
|
+
tools.register(new WriteFileTool());
|
|
52
|
+
tools.register(new ListDirTool());
|
|
53
|
+
tools.register(new ExecTool({
|
|
54
|
+
workingDir: this.workspace,
|
|
55
|
+
timeout: this.execConfig.timeout,
|
|
56
|
+
restrictToWorkspace: this.execConfig.restrictToWorkspace
|
|
57
|
+
}));
|
|
58
|
+
tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));
|
|
59
|
+
tools.register(new WebFetchTool());
|
|
60
|
+
const messages = [{
|
|
61
|
+
role: "system",
|
|
62
|
+
content: this.buildSubagentPrompt(task)
|
|
63
|
+
}, {
|
|
64
|
+
role: "user",
|
|
65
|
+
content: task
|
|
66
|
+
}];
|
|
67
|
+
const maxIterations = 15;
|
|
68
|
+
let finalResult = null;
|
|
69
|
+
for (let i = 0; i < maxIterations; i++) {
|
|
70
|
+
const response = await this.provider.chat({
|
|
71
|
+
messages,
|
|
72
|
+
tools: tools.getDefinitions(),
|
|
73
|
+
model: this.model
|
|
74
|
+
});
|
|
75
|
+
if (response.hasToolCalls) {
|
|
76
|
+
const toolCallDicts = response.toolCalls.map((tc) => ({
|
|
77
|
+
id: tc.id,
|
|
78
|
+
type: "function",
|
|
79
|
+
function: {
|
|
80
|
+
name: tc.name,
|
|
81
|
+
arguments: JSON.stringify(tc.arguments)
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
messages.push({
|
|
85
|
+
role: "assistant",
|
|
86
|
+
content: response.content ?? "",
|
|
87
|
+
tool_calls: toolCallDicts
|
|
88
|
+
});
|
|
89
|
+
for (const tc of response.toolCalls) {
|
|
90
|
+
console.log(`Subagent [${taskId}] executing: ${tc.name}`);
|
|
91
|
+
const result = await tools.execute(tc.name, tc.arguments);
|
|
92
|
+
messages.push({
|
|
93
|
+
role: "tool",
|
|
94
|
+
tool_call_id: tc.id,
|
|
95
|
+
name: tc.name,
|
|
96
|
+
content: result
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
} else {
|
|
100
|
+
finalResult = response.content;
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finalResult ??= "Task completed but no final response was generated.";
|
|
105
|
+
console.log(`Subagent [${taskId}] completed successfully`);
|
|
106
|
+
await this.announceResult(taskId, label, task, finalResult, origin, "ok");
|
|
107
|
+
} catch (err) {
|
|
108
|
+
const errorMsg = `Error: ${err instanceof Error ? err.message : err}`;
|
|
109
|
+
console.error(`Subagent [${taskId}] failed:`, err);
|
|
110
|
+
await this.announceResult(taskId, label, task, errorMsg, origin, "error");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async announceResult(taskId, label, task, result, origin, status) {
|
|
114
|
+
const content = `[Subagent '${label}' ${status === "ok" ? "completed successfully" : "failed"}]
|
|
115
|
+
|
|
116
|
+
Task: ${task}
|
|
117
|
+
|
|
118
|
+
Result:
|
|
119
|
+
${result}
|
|
120
|
+
|
|
121
|
+
Summarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like "subagent" or task IDs.`;
|
|
122
|
+
const msg = createInboundMessage({
|
|
123
|
+
channel: "system",
|
|
124
|
+
senderId: "subagent",
|
|
125
|
+
chatId: `${origin.channel}:${origin.chatId}`,
|
|
126
|
+
content
|
|
127
|
+
});
|
|
128
|
+
await this.bus.publishInbound(msg);
|
|
129
|
+
}
|
|
130
|
+
buildSubagentPrompt(task) {
|
|
131
|
+
return `# Subagent
|
|
132
|
+
|
|
133
|
+
You are a subagent spawned by the main agent to complete a specific task.
|
|
134
|
+
|
|
135
|
+
## Your Task
|
|
136
|
+
${task}
|
|
137
|
+
|
|
138
|
+
## Rules
|
|
139
|
+
1. Stay focused - complete only the assigned task, nothing else
|
|
140
|
+
2. Your final response will be reported back to the main agent
|
|
141
|
+
3. Do not initiate conversations or take on side tasks
|
|
142
|
+
4. Be concise but informative in your findings
|
|
143
|
+
|
|
144
|
+
## What You Can Do
|
|
145
|
+
- Read and write files in the workspace
|
|
146
|
+
- Execute shell commands
|
|
147
|
+
- Search the web and fetch web pages
|
|
148
|
+
- Complete the task thoroughly
|
|
149
|
+
|
|
150
|
+
## What You Cannot Do
|
|
151
|
+
- Send messages directly to users (no message tool available)
|
|
152
|
+
- Spawn other subagents
|
|
153
|
+
- Access the main agent's conversation history
|
|
154
|
+
|
|
155
|
+
## Workspace
|
|
156
|
+
Your workspace is at: ${this.workspace}
|
|
157
|
+
|
|
158
|
+
When you have completed the task, provide a clear summary of your findings or actions.`;
|
|
159
|
+
}
|
|
160
|
+
get runningCount() {
|
|
161
|
+
return this.runningTasks.size;
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
//#endregion
|
|
166
|
+
export { SubagentManager };
|
|
167
|
+
//# sourceMappingURL=subagent.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subagent.mjs","names":[],"sources":["../../src/agent/subagent.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport type { LLMProvider, ChatMessage } from \"../providers/base.js\";\nimport type { MessageBus } from \"../bus/queue.js\";\nimport { createInboundMessage } from \"../bus/events.js\";\nimport { ToolRegistry } from \"./tools/registry.js\";\nimport { ReadFileTool, WriteFileTool, ListDirTool } from \"./tools/filesystem.js\";\nimport { ExecTool } from \"./tools/shell.js\";\nimport { WebSearchTool, WebFetchTool } from \"./tools/web.js\";\nimport type { ExecToolConfig } from \"../config/schema.js\";\n\n/**\n * Manages background subagent execution.\n */\nexport class SubagentManager {\n private provider: LLMProvider;\n private workspace: string;\n private bus: MessageBus;\n private model: string;\n private braveApiKey?: string;\n private execConfig: ExecToolConfig;\n private runningTasks = new Map<string, AbortController>();\n\n constructor(params: {\n provider: LLMProvider;\n workspace: string;\n bus: MessageBus;\n model?: string;\n braveApiKey?: string;\n execConfig?: ExecToolConfig;\n }) {\n this.provider = params.provider;\n this.workspace = params.workspace;\n this.bus = params.bus;\n this.model = params.model ?? params.provider.getDefaultModel();\n this.braveApiKey = params.braveApiKey;\n this.execConfig = params.execConfig ?? { timeout: 60, restrictToWorkspace: false };\n }\n\n /** Spawn a subagent to execute a task in the background. */\n async spawn(params: {\n task: string;\n label?: string;\n originChannel?: string;\n originChatId?: string;\n }): Promise<string> {\n const taskId = randomUUID().slice(0, 8);\n const displayLabel =\n params.label ??\n (params.task.length > 30\n ? params.task.slice(0, 30) + \"...\"\n : params.task);\n\n const origin = {\n channel: params.originChannel ?? \"cli\",\n chatId: params.originChatId ?? \"direct\",\n };\n\n const controller = new AbortController();\n this.runningTasks.set(taskId, controller);\n\n // Run in background (don't await)\n this.runSubagent(taskId, params.task, displayLabel, origin)\n .finally(() => this.runningTasks.delete(taskId));\n\n console.log(`Spawned subagent [${taskId}]: ${displayLabel}`);\n return `Subagent [${displayLabel}] started (id: ${taskId}). I'll notify you when it completes.`;\n }\n\n private async runSubagent(\n taskId: string,\n task: string,\n label: string,\n origin: { channel: string; chatId: string },\n ): Promise<void> {\n console.log(`Subagent [${taskId}] starting task: ${label}`);\n\n try {\n // Build subagent tools (no message, no spawn)\n const tools = new ToolRegistry();\n tools.register(new ReadFileTool());\n tools.register(new WriteFileTool());\n tools.register(new ListDirTool());\n tools.register(\n new ExecTool({\n workingDir: this.workspace,\n timeout: this.execConfig.timeout,\n restrictToWorkspace: this.execConfig.restrictToWorkspace,\n }),\n );\n tools.register(new WebSearchTool({ apiKey: this.braveApiKey }));\n tools.register(new WebFetchTool());\n\n const systemPrompt = this.buildSubagentPrompt(task);\n const messages: ChatMessage[] = [\n { role: \"system\", content: systemPrompt },\n { role: \"user\", content: task },\n ];\n\n const maxIterations = 15;\n let finalResult: string | null = null;\n\n for (let i = 0; i < maxIterations; i++) {\n const response = await this.provider.chat({\n messages,\n tools: tools.getDefinitions(),\n model: this.model,\n });\n\n if (response.hasToolCalls) {\n const toolCallDicts = response.toolCalls.map((tc) => ({\n id: tc.id,\n type: \"function\" as const,\n function: {\n name: tc.name,\n arguments: JSON.stringify(tc.arguments),\n },\n }));\n\n messages.push({\n role: \"assistant\",\n content: response.content ?? \"\",\n tool_calls: toolCallDicts,\n });\n\n for (const tc of response.toolCalls) {\n console.log(\n `Subagent [${taskId}] executing: ${tc.name}`,\n );\n const result = await tools.execute(tc.name, tc.arguments);\n messages.push({\n role: \"tool\",\n tool_call_id: tc.id,\n name: tc.name,\n content: result,\n });\n }\n } else {\n finalResult = response.content;\n break;\n }\n }\n\n finalResult ??= \"Task completed but no final response was generated.\";\n console.log(`Subagent [${taskId}] completed successfully`);\n await this.announceResult(taskId, label, task, finalResult, origin, \"ok\");\n } catch (err) {\n const errorMsg = `Error: ${err instanceof Error ? err.message : err}`;\n console.error(`Subagent [${taskId}] failed:`, err);\n await this.announceResult(\n taskId,\n label,\n task,\n errorMsg,\n origin,\n \"error\",\n );\n }\n }\n\n private async announceResult(\n taskId: string,\n label: string,\n task: string,\n result: string,\n origin: { channel: string; chatId: string },\n status: string,\n ): Promise<void> {\n const statusText =\n status === \"ok\" ? \"completed successfully\" : \"failed\";\n\n const content = `[Subagent '${label}' ${statusText}]\n\nTask: ${task}\n\nResult:\n${result}\n\nSummarize this naturally for the user. Keep it brief (1-2 sentences). Do not mention technical details like \"subagent\" or task IDs.`;\n\n const msg = createInboundMessage({\n channel: \"system\",\n senderId: \"subagent\",\n chatId: `${origin.channel}:${origin.chatId}`,\n content,\n });\n\n await this.bus.publishInbound(msg);\n }\n\n private buildSubagentPrompt(task: string): string {\n return `# Subagent\n\nYou are a subagent spawned by the main agent to complete a specific task.\n\n## Your Task\n${task}\n\n## Rules\n1. Stay focused - complete only the assigned task, nothing else\n2. Your final response will be reported back to the main agent\n3. Do not initiate conversations or take on side tasks\n4. Be concise but informative in your findings\n\n## What You Can Do\n- Read and write files in the workspace\n- Execute shell commands\n- Search the web and fetch web pages\n- Complete the task thoroughly\n\n## What You Cannot Do\n- Send messages directly to users (no message tool available)\n- Spawn other subagents\n- Access the main agent's conversation history\n\n## Workspace\nYour workspace is at: ${this.workspace}\n\nWhen you have completed the task, provide a clear summary of your findings or actions.`;\n }\n\n get runningCount(): number {\n return this.runningTasks.size;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ToolDefinition } from "../../providers/base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent/tools/base.d.ts
|
|
4
|
+
/** Abstract base class for tools. */
|
|
5
|
+
declare abstract class Tool {
|
|
6
|
+
abstract readonly name: string;
|
|
7
|
+
abstract readonly description: string;
|
|
8
|
+
abstract readonly parameters: Record<string, unknown>;
|
|
9
|
+
/** Execute the tool with the given arguments. */
|
|
10
|
+
abstract execute(args: Record<string, unknown>): Promise<string>;
|
|
11
|
+
/** Get the tool definition for the LLM. */
|
|
12
|
+
getDefinition(): ToolDefinition;
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { Tool };
|
|
16
|
+
//# sourceMappingURL=base.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.d.mts","names":[],"sources":["../../../src/agent/tools/base.ts"],"mappings":";;;;uBAGsB,IAAA;EAAA,kBACF,IAAA;EAAA,kBACA,WAAA;EAAA,kBACA,UAAA,EAAY,MAAA;EAAA;EAAA,SAGrB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA;EAGjD,aAAA,CAAA,GAAiB,cAAA;AAAA"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/agent/tools/base.ts
|
|
2
|
+
/** Abstract base class for tools. */
|
|
3
|
+
var Tool = class {
|
|
4
|
+
/** Get the tool definition for the LLM. */
|
|
5
|
+
getDefinition() {
|
|
6
|
+
return {
|
|
7
|
+
type: "function",
|
|
8
|
+
function: {
|
|
9
|
+
name: this.name,
|
|
10
|
+
description: this.description,
|
|
11
|
+
parameters: this.parameters
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { Tool };
|
|
19
|
+
//# sourceMappingURL=base.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base.mjs","names":[],"sources":["../../../src/agent/tools/base.ts"],"sourcesContent":["import type { ToolDefinition } from \"../../providers/base.js\";\n\n/** Abstract base class for tools. */\nexport abstract class Tool {\n abstract readonly name: string;\n abstract readonly description: string;\n abstract readonly parameters: Record<string, unknown>;\n\n /** Execute the tool with the given arguments. */\n abstract execute(args: Record<string, unknown>): Promise<string>;\n\n /** Get the tool definition for the LLM. */\n getDefinition(): ToolDefinition {\n return {\n type: \"function\",\n function: {\n name: this.name,\n description: this.description,\n parameters: this.parameters,\n },\n };\n }\n}\n"],"mappings":";;AAGA,IAAsB,OAAtB,MAA2B;;CASzB,gBAAgC;AAC9B,SAAO;GACL,MAAM;GACN,UAAU;IACR,MAAM,KAAK;IACX,aAAa,KAAK;IAClB,YAAY,KAAK;IAClB;GACF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Tool } from "./base.mjs";
|
|
2
|
+
import { CronService } from "../../cron/service.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/agent/tools/cron.d.ts
|
|
5
|
+
/** Tool to schedule reminders and recurring tasks. */
|
|
6
|
+
declare class CronTool extends Tool {
|
|
7
|
+
readonly name = "cron";
|
|
8
|
+
readonly description = "Schedule reminders and recurring tasks. Actions: add, list, remove.";
|
|
9
|
+
readonly parameters: {
|
|
10
|
+
type: string;
|
|
11
|
+
properties: {
|
|
12
|
+
action: {
|
|
13
|
+
type: string;
|
|
14
|
+
enum: string[];
|
|
15
|
+
description: string;
|
|
16
|
+
};
|
|
17
|
+
message: {
|
|
18
|
+
type: string;
|
|
19
|
+
description: string;
|
|
20
|
+
};
|
|
21
|
+
every_seconds: {
|
|
22
|
+
type: string;
|
|
23
|
+
description: string;
|
|
24
|
+
};
|
|
25
|
+
cron_expr: {
|
|
26
|
+
type: string;
|
|
27
|
+
description: string;
|
|
28
|
+
};
|
|
29
|
+
job_id: {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
required: string[];
|
|
35
|
+
};
|
|
36
|
+
private cronService;
|
|
37
|
+
private channel;
|
|
38
|
+
private chatId;
|
|
39
|
+
constructor(cronService: CronService);
|
|
40
|
+
/** Set the current session context for delivery. */
|
|
41
|
+
setContext(channel: string, chatId: string): void;
|
|
42
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
43
|
+
private addJob;
|
|
44
|
+
private listJobs;
|
|
45
|
+
private removeJob;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { CronTool };
|
|
49
|
+
//# sourceMappingURL=cron.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.d.mts","names":[],"sources":["../../../src/agent/tools/cron.ts"],"mappings":";;;;;cAKa,QAAA,SAAiB,IAAA;EAAA,SACnB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BD,WAAA;EAAA,QACA,OAAA;EAAA,QACA,MAAA;cAEI,WAAA,EAAa,WAAA;EAHjB;EASR,UAAA,CAAW,OAAA,UAAiB,MAAA;EAKtB,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;EAAA,QActC,MAAA;EAAA,QA+BA,QAAA;EAAA,QASA,SAAA;AAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { Tool } from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent/tools/cron.ts
|
|
4
|
+
/** Tool to schedule reminders and recurring tasks. */
|
|
5
|
+
var CronTool = class extends Tool {
|
|
6
|
+
name = "cron";
|
|
7
|
+
description = "Schedule reminders and recurring tasks. Actions: add, list, remove.";
|
|
8
|
+
parameters = {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
action: {
|
|
12
|
+
type: "string",
|
|
13
|
+
enum: [
|
|
14
|
+
"add",
|
|
15
|
+
"list",
|
|
16
|
+
"remove"
|
|
17
|
+
],
|
|
18
|
+
description: "Action to perform"
|
|
19
|
+
},
|
|
20
|
+
message: {
|
|
21
|
+
type: "string",
|
|
22
|
+
description: "Reminder message (for add)"
|
|
23
|
+
},
|
|
24
|
+
every_seconds: {
|
|
25
|
+
type: "integer",
|
|
26
|
+
description: "Interval in seconds (for recurring tasks)"
|
|
27
|
+
},
|
|
28
|
+
cron_expr: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "Cron expression like '0 9 * * *' (for scheduled tasks)"
|
|
31
|
+
},
|
|
32
|
+
job_id: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Job ID (for remove)"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: ["action"]
|
|
38
|
+
};
|
|
39
|
+
cronService;
|
|
40
|
+
channel = "";
|
|
41
|
+
chatId = "";
|
|
42
|
+
constructor(cronService) {
|
|
43
|
+
super();
|
|
44
|
+
this.cronService = cronService;
|
|
45
|
+
}
|
|
46
|
+
/** Set the current session context for delivery. */
|
|
47
|
+
setContext(channel, chatId) {
|
|
48
|
+
this.channel = channel;
|
|
49
|
+
this.chatId = chatId;
|
|
50
|
+
}
|
|
51
|
+
async execute(args) {
|
|
52
|
+
const action = String(args.action);
|
|
53
|
+
switch (action) {
|
|
54
|
+
case "add": return this.addJob(args);
|
|
55
|
+
case "list": return this.listJobs();
|
|
56
|
+
case "remove": return this.removeJob(args);
|
|
57
|
+
default: return `Unknown action: ${action}`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
addJob(args) {
|
|
61
|
+
const message = args.message ? String(args.message) : "";
|
|
62
|
+
const everySeconds = args.every_seconds ? Number(args.every_seconds) : null;
|
|
63
|
+
const cronExpr = args.cron_expr ? String(args.cron_expr) : null;
|
|
64
|
+
if (!message) return "Error: message is required for add";
|
|
65
|
+
if (!this.channel || !this.chatId) return "Error: no session context (channel/chatId)";
|
|
66
|
+
let schedule;
|
|
67
|
+
if (everySeconds) schedule = {
|
|
68
|
+
kind: "every",
|
|
69
|
+
everyMs: everySeconds * 1e3
|
|
70
|
+
};
|
|
71
|
+
else if (cronExpr) schedule = {
|
|
72
|
+
kind: "cron",
|
|
73
|
+
expr: cronExpr
|
|
74
|
+
};
|
|
75
|
+
else return "Error: either every_seconds or cron_expr is required";
|
|
76
|
+
const job = this.cronService.addJob({
|
|
77
|
+
name: message.slice(0, 30),
|
|
78
|
+
schedule,
|
|
79
|
+
message,
|
|
80
|
+
deliver: true,
|
|
81
|
+
channel: this.channel,
|
|
82
|
+
to: this.chatId
|
|
83
|
+
});
|
|
84
|
+
return `Created job '${job.name}' (id: ${job.id})`;
|
|
85
|
+
}
|
|
86
|
+
listJobs() {
|
|
87
|
+
const jobs = this.cronService.listJobs();
|
|
88
|
+
if (jobs.length === 0) return "No scheduled jobs.";
|
|
89
|
+
return "Scheduled jobs:\n" + jobs.map((j) => `- ${j.name} (id: ${j.id}, ${j.schedule.kind})`).join("\n");
|
|
90
|
+
}
|
|
91
|
+
removeJob(args) {
|
|
92
|
+
const jobId = args.job_id ? String(args.job_id) : null;
|
|
93
|
+
if (!jobId) return "Error: job_id is required for remove";
|
|
94
|
+
if (this.cronService.removeJob(jobId)) return `Removed job ${jobId}`;
|
|
95
|
+
return `Job ${jobId} not found`;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
//#endregion
|
|
100
|
+
export { CronTool };
|
|
101
|
+
//# sourceMappingURL=cron.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cron.mjs","names":[],"sources":["../../../src/agent/tools/cron.ts"],"sourcesContent":["import { Tool } from \"./base.js\";\nimport type { CronService } from \"../../cron/service.js\";\nimport type { CronSchedule } from \"../../cron/types.js\";\n\n/** Tool to schedule reminders and recurring tasks. */\nexport class CronTool extends Tool {\n readonly name = \"cron\";\n readonly description =\n \"Schedule reminders and recurring tasks. Actions: add, list, remove.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n action: {\n type: \"string\",\n enum: [\"add\", \"list\", \"remove\"],\n description: \"Action to perform\",\n },\n message: {\n type: \"string\",\n description: \"Reminder message (for add)\",\n },\n every_seconds: {\n type: \"integer\",\n description: \"Interval in seconds (for recurring tasks)\",\n },\n cron_expr: {\n type: \"string\",\n description: \"Cron expression like '0 9 * * *' (for scheduled tasks)\",\n },\n job_id: {\n type: \"string\",\n description: \"Job ID (for remove)\",\n },\n },\n required: [\"action\"],\n };\n\n private cronService: CronService;\n private channel = \"\";\n private chatId = \"\";\n\n constructor(cronService: CronService) {\n super();\n this.cronService = cronService;\n }\n\n /** Set the current session context for delivery. */\n setContext(channel: string, chatId: string): void {\n this.channel = channel;\n this.chatId = chatId;\n }\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const action = String(args.action);\n switch (action) {\n case \"add\":\n return this.addJob(args);\n case \"list\":\n return this.listJobs();\n case \"remove\":\n return this.removeJob(args);\n default:\n return `Unknown action: ${action}`;\n }\n }\n\n private addJob(args: Record<string, unknown>): string {\n const message = args.message ? String(args.message) : \"\";\n const everySeconds = args.every_seconds\n ? Number(args.every_seconds)\n : null;\n const cronExpr = args.cron_expr ? String(args.cron_expr) : null;\n\n if (!message) return \"Error: message is required for add\";\n if (!this.channel || !this.chatId)\n return \"Error: no session context (channel/chatId)\";\n\n let schedule: CronSchedule;\n if (everySeconds) {\n schedule = { kind: \"every\", everyMs: everySeconds * 1000 };\n } else if (cronExpr) {\n schedule = { kind: \"cron\", expr: cronExpr };\n } else {\n return \"Error: either every_seconds or cron_expr is required\";\n }\n\n const job = this.cronService.addJob({\n name: message.slice(0, 30),\n schedule,\n message,\n deliver: true,\n channel: this.channel,\n to: this.chatId,\n });\n return `Created job '${job.name}' (id: ${job.id})`;\n }\n\n private listJobs(): string {\n const jobs = this.cronService.listJobs();\n if (jobs.length === 0) return \"No scheduled jobs.\";\n const lines = jobs.map(\n (j) => `- ${j.name} (id: ${j.id}, ${j.schedule.kind})`,\n );\n return \"Scheduled jobs:\\n\" + lines.join(\"\\n\");\n }\n\n private removeJob(args: Record<string, unknown>): string {\n const jobId = args.job_id ? String(args.job_id) : null;\n if (!jobId) return \"Error: job_id is required for remove\";\n if (this.cronService.removeJob(jobId)) {\n return `Removed job ${jobId}`;\n }\n return `Job ${jobId} not found`;\n }\n}\n"],"mappings":";;;;AAKA,IAAa,WAAb,cAA8B,KAAK;CACjC,AAAS,OAAO;CAChB,AAAS,cACP;CACF,AAAS,aAAa;EACpB,MAAM;EACN,YAAY;GACV,QAAQ;IACN,MAAM;IACN,MAAM;KAAC;KAAO;KAAQ;KAAS;IAC/B,aAAa;IACd;GACD,SAAS;IACP,MAAM;IACN,aAAa;IACd;GACD,eAAe;IACb,MAAM;IACN,aAAa;IACd;GACD,WAAW;IACT,MAAM;IACN,aAAa;IACd;GACD,QAAQ;IACN,MAAM;IACN,aAAa;IACd;GACF;EACD,UAAU,CAAC,SAAS;EACrB;CAED,AAAQ;CACR,AAAQ,UAAU;CAClB,AAAQ,SAAS;CAEjB,YAAY,aAA0B;AACpC,SAAO;AACP,OAAK,cAAc;;;CAIrB,WAAW,SAAiB,QAAsB;AAChD,OAAK,UAAU;AACf,OAAK,SAAS;;CAGhB,MAAM,QAAQ,MAAgD;EAC5D,MAAM,SAAS,OAAO,KAAK,OAAO;AAClC,UAAQ,QAAR;GACE,KAAK,MACH,QAAO,KAAK,OAAO,KAAK;GAC1B,KAAK,OACH,QAAO,KAAK,UAAU;GACxB,KAAK,SACH,QAAO,KAAK,UAAU,KAAK;GAC7B,QACE,QAAO,mBAAmB;;;CAIhC,AAAQ,OAAO,MAAuC;EACpD,MAAM,UAAU,KAAK,UAAU,OAAO,KAAK,QAAQ,GAAG;EACtD,MAAM,eAAe,KAAK,gBACtB,OAAO,KAAK,cAAc,GAC1B;EACJ,MAAM,WAAW,KAAK,YAAY,OAAO,KAAK,UAAU,GAAG;AAE3D,MAAI,CAAC,QAAS,QAAO;AACrB,MAAI,CAAC,KAAK,WAAW,CAAC,KAAK,OACzB,QAAO;EAET,IAAI;AACJ,MAAI,aACF,YAAW;GAAE,MAAM;GAAS,SAAS,eAAe;GAAM;WACjD,SACT,YAAW;GAAE,MAAM;GAAQ,MAAM;GAAU;MAE3C,QAAO;EAGT,MAAM,MAAM,KAAK,YAAY,OAAO;GAClC,MAAM,QAAQ,MAAM,GAAG,GAAG;GAC1B;GACA;GACA,SAAS;GACT,SAAS,KAAK;GACd,IAAI,KAAK;GACV,CAAC;AACF,SAAO,gBAAgB,IAAI,KAAK,SAAS,IAAI,GAAG;;CAGlD,AAAQ,WAAmB;EACzB,MAAM,OAAO,KAAK,YAAY,UAAU;AACxC,MAAI,KAAK,WAAW,EAAG,QAAO;AAI9B,SAAO,sBAHO,KAAK,KAChB,MAAM,KAAK,EAAE,KAAK,QAAQ,EAAE,GAAG,IAAI,EAAE,SAAS,KAAK,GACrD,CACkC,KAAK,KAAK;;CAG/C,AAAQ,UAAU,MAAuC;EACvD,MAAM,QAAQ,KAAK,SAAS,OAAO,KAAK,OAAO,GAAG;AAClD,MAAI,CAAC,MAAO,QAAO;AACnB,MAAI,KAAK,YAAY,UAAU,MAAM,CACnC,QAAO,eAAe;AAExB,SAAO,OAAO,MAAM"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Tool } from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/agent/tools/filesystem.d.ts
|
|
4
|
+
/** Read a file's contents. */
|
|
5
|
+
declare class ReadFileTool extends Tool {
|
|
6
|
+
readonly name = "read_file";
|
|
7
|
+
readonly description = "Read the contents of a file.";
|
|
8
|
+
readonly parameters: {
|
|
9
|
+
type: string;
|
|
10
|
+
properties: {
|
|
11
|
+
path: {
|
|
12
|
+
type: string;
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
maxChars: {
|
|
16
|
+
type: string;
|
|
17
|
+
description: string;
|
|
18
|
+
minimum: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
required: string[];
|
|
22
|
+
};
|
|
23
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
/** Write content to a file. */
|
|
26
|
+
declare class WriteFileTool extends Tool {
|
|
27
|
+
readonly name = "write_file";
|
|
28
|
+
readonly description = "Write content to a file. Creates parent directories if needed.";
|
|
29
|
+
readonly parameters: {
|
|
30
|
+
type: string;
|
|
31
|
+
properties: {
|
|
32
|
+
path: {
|
|
33
|
+
type: string;
|
|
34
|
+
description: string;
|
|
35
|
+
};
|
|
36
|
+
content: {
|
|
37
|
+
type: string;
|
|
38
|
+
description: string;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
required: string[];
|
|
42
|
+
};
|
|
43
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
44
|
+
}
|
|
45
|
+
/** Edit a file by replacing text. */
|
|
46
|
+
declare class EditFileTool extends Tool {
|
|
47
|
+
readonly name = "edit_file";
|
|
48
|
+
readonly description = "Edit a file by replacing occurrences of old_text with new_text.";
|
|
49
|
+
readonly parameters: {
|
|
50
|
+
type: string;
|
|
51
|
+
properties: {
|
|
52
|
+
path: {
|
|
53
|
+
type: string;
|
|
54
|
+
description: string;
|
|
55
|
+
};
|
|
56
|
+
old_text: {
|
|
57
|
+
type: string;
|
|
58
|
+
description: string;
|
|
59
|
+
};
|
|
60
|
+
new_text: {
|
|
61
|
+
type: string;
|
|
62
|
+
description: string;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
required: string[];
|
|
66
|
+
};
|
|
67
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
68
|
+
}
|
|
69
|
+
/** List directory contents. */
|
|
70
|
+
declare class ListDirTool extends Tool {
|
|
71
|
+
readonly name = "list_dir";
|
|
72
|
+
readonly description = "List files and directories at the given path.";
|
|
73
|
+
readonly parameters: {
|
|
74
|
+
type: string;
|
|
75
|
+
properties: {
|
|
76
|
+
path: {
|
|
77
|
+
type: string;
|
|
78
|
+
description: string;
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
required: string[];
|
|
82
|
+
};
|
|
83
|
+
execute(args: Record<string, unknown>): Promise<string>;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
export { EditFileTool, ListDirTool, ReadFileTool, WriteFileTool };
|
|
87
|
+
//# sourceMappingURL=filesystem.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.d.mts","names":[],"sources":["../../../src/agent/tools/filesystem.ts"],"mappings":";;;;cAYa,YAAA,SAAqB,IAAA;EAAA,SACvB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;;;;;;EAaH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAoBnC,aAAA,SAAsB,IAAA;EAAA,SACxB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;;;;;EASH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAkBnC,YAAA,SAAqB,IAAA;EAAA,SACvB,IAAA;EAAA,SACA,WAAA;EAAA,SAEA,UAAA;;;;;;;;;;;;;;;;;;EAUH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA;;cAuBnC,WAAA,SAAoB,IAAA;EAAA,SACtB,IAAA;EAAA,SACA,WAAA;EAAA,SACA,UAAA;;;;;;;;;;EAQH,OAAA,CAAQ,IAAA,EAAM,MAAA,oBAA0B,OAAA;AAAA"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { Tool } from "./base.mjs";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
|
|
5
|
+
//#region src/agent/tools/filesystem.ts
|
|
6
|
+
/** Read a file's contents. */
|
|
7
|
+
var ReadFileTool = class extends Tool {
|
|
8
|
+
name = "read_file";
|
|
9
|
+
description = "Read the contents of a file.";
|
|
10
|
+
parameters = {
|
|
11
|
+
type: "object",
|
|
12
|
+
properties: {
|
|
13
|
+
path: {
|
|
14
|
+
type: "string",
|
|
15
|
+
description: "File path to read"
|
|
16
|
+
},
|
|
17
|
+
maxChars: {
|
|
18
|
+
type: "integer",
|
|
19
|
+
description: "Max characters to return",
|
|
20
|
+
minimum: 1
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
required: ["path"]
|
|
24
|
+
};
|
|
25
|
+
async execute(args) {
|
|
26
|
+
const filePath = String(args.path);
|
|
27
|
+
const maxChars = args.maxChars ? Number(args.maxChars) : void 0;
|
|
28
|
+
try {
|
|
29
|
+
if (!existsSync(filePath)) return `Error: File not found: ${filePath}`;
|
|
30
|
+
let content = readFileSync(filePath, "utf-8");
|
|
31
|
+
if (maxChars && content.length > maxChars) content = content.slice(0, maxChars) + "\n... (truncated)";
|
|
32
|
+
return content;
|
|
33
|
+
} catch (err) {
|
|
34
|
+
return `Error reading file: ${err instanceof Error ? err.message : err}`;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
/** Write content to a file. */
|
|
39
|
+
var WriteFileTool = class extends Tool {
|
|
40
|
+
name = "write_file";
|
|
41
|
+
description = "Write content to a file. Creates parent directories if needed.";
|
|
42
|
+
parameters = {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
path: {
|
|
46
|
+
type: "string",
|
|
47
|
+
description: "File path to write"
|
|
48
|
+
},
|
|
49
|
+
content: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "Content to write"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
required: ["path", "content"]
|
|
55
|
+
};
|
|
56
|
+
async execute(args) {
|
|
57
|
+
const filePath = String(args.path);
|
|
58
|
+
const content = String(args.content);
|
|
59
|
+
try {
|
|
60
|
+
const dir = dirname(filePath);
|
|
61
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
62
|
+
writeFileSync(filePath, content, "utf-8");
|
|
63
|
+
return `Written ${content.length} chars to ${filePath}`;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
return `Error writing file: ${err instanceof Error ? err.message : err}`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
/** Edit a file by replacing text. */
|
|
70
|
+
var EditFileTool = class extends Tool {
|
|
71
|
+
name = "edit_file";
|
|
72
|
+
description = "Edit a file by replacing occurrences of old_text with new_text.";
|
|
73
|
+
parameters = {
|
|
74
|
+
type: "object",
|
|
75
|
+
properties: {
|
|
76
|
+
path: {
|
|
77
|
+
type: "string",
|
|
78
|
+
description: "File path to edit"
|
|
79
|
+
},
|
|
80
|
+
old_text: {
|
|
81
|
+
type: "string",
|
|
82
|
+
description: "Text to find and replace"
|
|
83
|
+
},
|
|
84
|
+
new_text: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Replacement text"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
required: [
|
|
90
|
+
"path",
|
|
91
|
+
"old_text",
|
|
92
|
+
"new_text"
|
|
93
|
+
]
|
|
94
|
+
};
|
|
95
|
+
async execute(args) {
|
|
96
|
+
const filePath = String(args.path);
|
|
97
|
+
const oldText = String(args.old_text);
|
|
98
|
+
const newText = String(args.new_text);
|
|
99
|
+
try {
|
|
100
|
+
if (!existsSync(filePath)) return `Error: File not found: ${filePath}`;
|
|
101
|
+
const content = readFileSync(filePath, "utf-8");
|
|
102
|
+
if (!content.includes(oldText)) return `Error: old_text not found in ${filePath}`;
|
|
103
|
+
writeFileSync(filePath, content.replace(oldText, newText), "utf-8");
|
|
104
|
+
return `Edited ${filePath}: replaced ${oldText.length} chars`;
|
|
105
|
+
} catch (err) {
|
|
106
|
+
return `Error editing file: ${err instanceof Error ? err.message : err}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
/** List directory contents. */
|
|
111
|
+
var ListDirTool = class extends Tool {
|
|
112
|
+
name = "list_dir";
|
|
113
|
+
description = "List files and directories at the given path.";
|
|
114
|
+
parameters = {
|
|
115
|
+
type: "object",
|
|
116
|
+
properties: { path: {
|
|
117
|
+
type: "string",
|
|
118
|
+
description: "Directory path to list"
|
|
119
|
+
} },
|
|
120
|
+
required: ["path"]
|
|
121
|
+
};
|
|
122
|
+
async execute(args) {
|
|
123
|
+
const dirPath = String(args.path);
|
|
124
|
+
try {
|
|
125
|
+
if (!existsSync(dirPath)) return `Error: Directory not found: ${dirPath}`;
|
|
126
|
+
const entries = readdirSync(dirPath);
|
|
127
|
+
const lines = [];
|
|
128
|
+
for (const entry of entries) try {
|
|
129
|
+
const stat = statSync(join(dirPath, entry));
|
|
130
|
+
const type = stat.isDirectory() ? "dir" : "file";
|
|
131
|
+
const size = stat.isDirectory() ? "" : ` (${stat.size}b)`;
|
|
132
|
+
lines.push(`${type}\t${entry}${size}`);
|
|
133
|
+
} catch {
|
|
134
|
+
lines.push(`?\t${entry}`);
|
|
135
|
+
}
|
|
136
|
+
return lines.length > 0 ? lines.join("\n") : "(empty directory)";
|
|
137
|
+
} catch (err) {
|
|
138
|
+
return `Error listing directory: ${err instanceof Error ? err.message : err}`;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
//#endregion
|
|
144
|
+
export { EditFileTool, ListDirTool, ReadFileTool, WriteFileTool };
|
|
145
|
+
//# sourceMappingURL=filesystem.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.mjs","names":[],"sources":["../../../src/agent/tools/filesystem.ts"],"sourcesContent":["import {\n readFileSync,\n writeFileSync,\n readdirSync,\n existsSync,\n statSync,\n mkdirSync,\n} from \"node:fs\";\nimport { join, dirname, resolve } from \"node:path\";\nimport { Tool } from \"./base.js\";\n\n/** Read a file's contents. */\nexport class ReadFileTool extends Tool {\n readonly name = \"read_file\";\n readonly description = \"Read the contents of a file.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to read\" },\n maxChars: {\n type: \"integer\",\n description: \"Max characters to return\",\n minimum: 1,\n },\n },\n required: [\"path\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const maxChars = args.maxChars ? Number(args.maxChars) : undefined;\n\n try {\n if (!existsSync(filePath)) {\n return `Error: File not found: ${filePath}`;\n }\n let content = readFileSync(filePath, \"utf-8\");\n if (maxChars && content.length > maxChars) {\n content = content.slice(0, maxChars) + \"\\n... (truncated)\";\n }\n return content;\n } catch (err) {\n return `Error reading file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** Write content to a file. */\nexport class WriteFileTool extends Tool {\n readonly name = \"write_file\";\n readonly description = \"Write content to a file. Creates parent directories if needed.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to write\" },\n content: { type: \"string\", description: \"Content to write\" },\n },\n required: [\"path\", \"content\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const content = String(args.content);\n\n try {\n const dir = dirname(filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(filePath, content, \"utf-8\");\n return `Written ${content.length} chars to ${filePath}`;\n } catch (err) {\n return `Error writing file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** Edit a file by replacing text. */\nexport class EditFileTool extends Tool {\n readonly name = \"edit_file\";\n readonly description =\n \"Edit a file by replacing occurrences of old_text with new_text.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"File path to edit\" },\n old_text: { type: \"string\", description: \"Text to find and replace\" },\n new_text: { type: \"string\", description: \"Replacement text\" },\n },\n required: [\"path\", \"old_text\", \"new_text\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const filePath = String(args.path);\n const oldText = String(args.old_text);\n const newText = String(args.new_text);\n\n try {\n if (!existsSync(filePath)) {\n return `Error: File not found: ${filePath}`;\n }\n const content = readFileSync(filePath, \"utf-8\");\n if (!content.includes(oldText)) {\n return `Error: old_text not found in ${filePath}`;\n }\n const newContent = content.replace(oldText, newText);\n writeFileSync(filePath, newContent, \"utf-8\");\n return `Edited ${filePath}: replaced ${oldText.length} chars`;\n } catch (err) {\n return `Error editing file: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n\n/** List directory contents. */\nexport class ListDirTool extends Tool {\n readonly name = \"list_dir\";\n readonly description = \"List files and directories at the given path.\";\n readonly parameters = {\n type: \"object\",\n properties: {\n path: { type: \"string\", description: \"Directory path to list\" },\n },\n required: [\"path\"],\n };\n\n async execute(args: Record<string, unknown>): Promise<string> {\n const dirPath = String(args.path);\n\n try {\n if (!existsSync(dirPath)) {\n return `Error: Directory not found: ${dirPath}`;\n }\n const entries = readdirSync(dirPath);\n const lines: string[] = [];\n for (const entry of entries) {\n try {\n const fullPath = join(dirPath, entry);\n const stat = statSync(fullPath);\n const type = stat.isDirectory() ? \"dir\" : \"file\";\n const size = stat.isDirectory() ? \"\" : ` (${stat.size}b)`;\n lines.push(`${type}\\t${entry}${size}`);\n } catch {\n lines.push(`?\\t${entry}`);\n }\n }\n return lines.length > 0 ? lines.join(\"\\n\") : \"(empty directory)\";\n } catch (err) {\n return `Error listing directory: ${err instanceof Error ? err.message : err}`;\n }\n }\n}\n"],"mappings":"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"}
|