apexbot 1.0.2 → 1.0.5
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/agent/agentManager.js +52 -5
- package/dist/agent/toolExecutor.js +144 -0
- package/dist/channels/channelManager.js +3 -1
- package/dist/cli/index.js +127 -0
- package/dist/gateway/index.js +62 -8
- package/dist/skills/index.js +212 -0
- package/dist/skills/obsidian.js +440 -0
- package/dist/skills/reminder.js +430 -0
- package/dist/skills/spotify.js +611 -0
- package/dist/skills/system.js +360 -0
- package/dist/skills/weather.js +144 -0
- package/dist/tools/datetime.js +188 -0
- package/dist/tools/file.js +352 -0
- package/dist/tools/index.js +111 -0
- package/dist/tools/loader.js +68 -0
- package/dist/tools/math.js +248 -0
- package/dist/tools/shell.js +104 -0
- package/dist/tools/web.js +197 -0
- package/package.json +2 -2
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
8
|
exports.AgentManager = void 0;
|
|
9
9
|
const generative_ai_1 = require("@google/generative-ai");
|
|
10
|
+
const tools_1 = require("../tools");
|
|
11
|
+
const toolExecutor_1 = require("./toolExecutor");
|
|
10
12
|
class AgentManager {
|
|
11
13
|
config = null;
|
|
12
14
|
googleClient = null;
|
|
@@ -42,6 +44,7 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
42
44
|
maxTokens: 4096,
|
|
43
45
|
temperature: 0.7,
|
|
44
46
|
systemPrompt: this.defaultSystemPrompt,
|
|
47
|
+
enableTools: true, // Tools enabled by default
|
|
45
48
|
...config,
|
|
46
49
|
};
|
|
47
50
|
// Initialize client based on provider
|
|
@@ -68,6 +71,7 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
68
71
|
return { text: 'Agent not configured. Please set up an AI provider.' };
|
|
69
72
|
}
|
|
70
73
|
const userText = message.text || '';
|
|
74
|
+
console.log(`[Agent] Processing message: ${userText.slice(0, 50)}...`);
|
|
71
75
|
// Handle slash commands
|
|
72
76
|
if (userText.startsWith('/')) {
|
|
73
77
|
return this.handleCommand(session, userText);
|
|
@@ -95,6 +99,43 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
95
99
|
default:
|
|
96
100
|
response = { text: 'Unknown AI provider' };
|
|
97
101
|
}
|
|
102
|
+
// Check for tool calls in response
|
|
103
|
+
if (this.config.enableTools) {
|
|
104
|
+
const toolCalls = (0, toolExecutor_1.parseToolCalls)(response.text);
|
|
105
|
+
if (toolCalls.length > 0) {
|
|
106
|
+
console.log(`[Agent] Found ${toolCalls.length} tool calls`);
|
|
107
|
+
// Build tool context
|
|
108
|
+
const context = (0, toolExecutor_1.buildToolContext)(session.id, message.userId || 'unknown', message.channel, { workspaceDir: process.cwd() });
|
|
109
|
+
// Execute tools
|
|
110
|
+
const toolResults = await (0, toolExecutor_1.executeToolCalls)(toolCalls, context);
|
|
111
|
+
response.toolCalls = toolResults;
|
|
112
|
+
// Format results and continue conversation
|
|
113
|
+
const resultsText = (0, toolExecutor_1.formatToolResults)(toolResults);
|
|
114
|
+
if (resultsText) {
|
|
115
|
+
// Add tool results to history and get follow-up response
|
|
116
|
+
const followUpHistory = [
|
|
117
|
+
...history,
|
|
118
|
+
{ role: 'assistant', content: response.text, timestamp: Date.now() },
|
|
119
|
+
{ role: 'user', content: `Tool results:\n${resultsText}\n\nPlease continue based on these results.`, timestamp: Date.now() },
|
|
120
|
+
];
|
|
121
|
+
// Get follow-up response
|
|
122
|
+
let followUp;
|
|
123
|
+
switch (this.config.provider) {
|
|
124
|
+
case 'ollama':
|
|
125
|
+
followUp = await this.processWithOllama(followUpHistory);
|
|
126
|
+
break;
|
|
127
|
+
case 'google':
|
|
128
|
+
followUp = await this.processWithGemini(followUpHistory);
|
|
129
|
+
break;
|
|
130
|
+
default:
|
|
131
|
+
followUp = { text: resultsText };
|
|
132
|
+
}
|
|
133
|
+
// Combine responses
|
|
134
|
+
response.text = followUp.text;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
console.log(`[Agent] Generated response: ${response.text.slice(0, 50)}...`);
|
|
98
139
|
// Save to session
|
|
99
140
|
session.messages.push({ role: 'user', content: userText, timestamp: Date.now() });
|
|
100
141
|
session.messages.push({ role: 'assistant', content: response.text, timestamp: Date.now() });
|
|
@@ -103,9 +144,8 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
103
144
|
return response;
|
|
104
145
|
}
|
|
105
146
|
catch (e) {
|
|
106
|
-
console.error('[Agent] Error:', e);
|
|
107
|
-
|
|
108
|
-
const errorMsg = (e.message || 'Unknown error').slice(0, 200).replace(/[_*\[\]()~`>#+=|{}.!-]/g, '\\$&');
|
|
147
|
+
console.error('[Agent] Error processing:', e);
|
|
148
|
+
const errorMsg = (e.message || 'Unknown error').slice(0, 200);
|
|
109
149
|
return { text: `Error: ${errorMsg}` };
|
|
110
150
|
}
|
|
111
151
|
}
|
|
@@ -160,6 +200,7 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
160
200
|
const apiUrl = cfg.apiUrl || 'http://localhost:11434';
|
|
161
201
|
const model = cfg.model || 'llama3.2';
|
|
162
202
|
const temperature = cfg.temperature ?? 0.7;
|
|
203
|
+
console.log(`[Agent] Calling Ollama at ${apiUrl} with model ${model}...`);
|
|
163
204
|
// Build messages array for Ollama chat API
|
|
164
205
|
const messages = history.map(m => ({
|
|
165
206
|
role: m.role,
|
|
@@ -183,10 +224,12 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
183
224
|
});
|
|
184
225
|
if (!res.ok) {
|
|
185
226
|
const err = await res.text().catch(() => res.statusText);
|
|
227
|
+
console.error(`[Agent] Ollama error: ${err}`);
|
|
186
228
|
throw new Error(`Ollama error: ${err}`);
|
|
187
229
|
}
|
|
188
230
|
const data = await res.json();
|
|
189
231
|
const text = data.message?.content || '';
|
|
232
|
+
console.log(`[Agent] Ollama response received: ${text.slice(0, 50)}...`);
|
|
190
233
|
return {
|
|
191
234
|
text: String(text).trim() || 'No response from model',
|
|
192
235
|
usage: {
|
|
@@ -196,6 +239,7 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
196
239
|
};
|
|
197
240
|
}
|
|
198
241
|
catch (error) {
|
|
242
|
+
console.error('[Agent] Ollama fetch error:', error);
|
|
199
243
|
if (error.code === 'ECONNREFUSED' || error.message?.includes('fetch failed')) {
|
|
200
244
|
return {
|
|
201
245
|
text: `Ollama not running.\n\nPlease:\n1. Install Ollama: https://ollama.com\n2. Pull a model: ollama pull llama3.2\n3. Start Ollama: ollama serve`,
|
|
@@ -206,8 +250,11 @@ You are running locally on the user's machine. No data leaves their computer. Yo
|
|
|
206
250
|
}
|
|
207
251
|
buildHistory(session, currentMessage) {
|
|
208
252
|
const history = [];
|
|
209
|
-
// System prompt
|
|
210
|
-
|
|
253
|
+
// System prompt with tools if enabled
|
|
254
|
+
let systemPrompt = session.systemPrompt || this.config?.systemPrompt || this.defaultSystemPrompt;
|
|
255
|
+
if (this.config?.enableTools && tools_1.toolRegistry.list().length > 0) {
|
|
256
|
+
systemPrompt += '\n\n' + (0, toolExecutor_1.getToolsSystemPrompt)();
|
|
257
|
+
}
|
|
211
258
|
history.push({ role: 'system', content: systemPrompt, timestamp: 0 });
|
|
212
259
|
// Previous messages
|
|
213
260
|
for (const msg of session.messages) {
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Tool Executor - Handles tool calling from AI models
|
|
4
|
+
*
|
|
5
|
+
* This module integrates the tool system with AI responses,
|
|
6
|
+
* parsing tool calls and executing them.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.parseToolCalls = parseToolCalls;
|
|
10
|
+
exports.executeTool = executeTool;
|
|
11
|
+
exports.executeToolCalls = executeToolCalls;
|
|
12
|
+
exports.formatToolResults = formatToolResults;
|
|
13
|
+
exports.getToolsSystemPrompt = getToolsSystemPrompt;
|
|
14
|
+
exports.buildToolContext = buildToolContext;
|
|
15
|
+
const tools_1 = require("../tools");
|
|
16
|
+
/**
|
|
17
|
+
* Parse tool calls from AI response
|
|
18
|
+
* Supports multiple formats:
|
|
19
|
+
* 1. JSON block: ```json\n{"tool": "name", "args": {...}}```
|
|
20
|
+
* 2. XML-style: <tool name="name">{"args": {...}}</tool>
|
|
21
|
+
* 3. Function call style: tool_name({"arg": "value"})
|
|
22
|
+
*/
|
|
23
|
+
function parseToolCalls(text) {
|
|
24
|
+
const calls = [];
|
|
25
|
+
// Pattern 1: JSON blocks with tool calls
|
|
26
|
+
const jsonBlockRegex = /```(?:json)?\s*\n?\s*\{[\s\S]*?"(?:tool|function|name)"[\s\S]*?\}\s*```/gi;
|
|
27
|
+
const jsonBlocks = text.match(jsonBlockRegex) || [];
|
|
28
|
+
for (const block of jsonBlocks) {
|
|
29
|
+
try {
|
|
30
|
+
const jsonStr = block.replace(/```(?:json)?\s*\n?/gi, '').replace(/\s*```/g, '');
|
|
31
|
+
const parsed = JSON.parse(jsonStr);
|
|
32
|
+
if (parsed.tool || parsed.function || parsed.name) {
|
|
33
|
+
calls.push({
|
|
34
|
+
name: parsed.tool || parsed.function || parsed.name,
|
|
35
|
+
arguments: parsed.args || parsed.arguments || parsed.parameters || {},
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
// Invalid JSON, skip
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Pattern 2: XML-style tool calls
|
|
44
|
+
const xmlRegex = /<tool\s+name=["']([^"']+)["']>\s*([\s\S]*?)\s*<\/tool>/gi;
|
|
45
|
+
let xmlMatch;
|
|
46
|
+
while ((xmlMatch = xmlRegex.exec(text)) !== null) {
|
|
47
|
+
try {
|
|
48
|
+
const name = xmlMatch[1];
|
|
49
|
+
const argsStr = xmlMatch[2].trim();
|
|
50
|
+
const args = argsStr ? JSON.parse(argsStr) : {};
|
|
51
|
+
calls.push({ name, arguments: args });
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
// Invalid args, skip
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Pattern 3: Function call style: tool_name({"arg": "value"})
|
|
58
|
+
const funcRegex = /\b([a-z_][a-z0-9_]*)\s*\(\s*(\{[\s\S]*?\})\s*\)/gi;
|
|
59
|
+
let funcMatch;
|
|
60
|
+
while ((funcMatch = funcRegex.exec(text)) !== null) {
|
|
61
|
+
const name = funcMatch[1];
|
|
62
|
+
// Check if it's a registered tool
|
|
63
|
+
if (tools_1.toolRegistry.has(name)) {
|
|
64
|
+
try {
|
|
65
|
+
const args = JSON.parse(funcMatch[2]);
|
|
66
|
+
calls.push({ name, arguments: args });
|
|
67
|
+
}
|
|
68
|
+
catch (e) {
|
|
69
|
+
// Invalid args, skip
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return calls;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Execute a single tool call
|
|
77
|
+
*/
|
|
78
|
+
async function executeTool(call, context) {
|
|
79
|
+
console.log(`[ToolExecutor] Executing: ${call.name}`, call.arguments);
|
|
80
|
+
return tools_1.toolRegistry.execute(call.name, call.arguments, context);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Execute multiple tool calls in sequence
|
|
84
|
+
*/
|
|
85
|
+
async function executeToolCalls(calls, context) {
|
|
86
|
+
const results = [];
|
|
87
|
+
for (const call of calls) {
|
|
88
|
+
const result = await executeTool(call, context);
|
|
89
|
+
results.push({ call, result });
|
|
90
|
+
}
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Format tool results for inclusion in AI response
|
|
95
|
+
*/
|
|
96
|
+
function formatToolResults(results) {
|
|
97
|
+
if (results.length === 0)
|
|
98
|
+
return '';
|
|
99
|
+
return results.map(({ call, result }) => {
|
|
100
|
+
const status = result.success ? 'SUCCESS' : 'ERROR';
|
|
101
|
+
const data = result.success
|
|
102
|
+
? JSON.stringify(result.data, null, 2)
|
|
103
|
+
: result.error;
|
|
104
|
+
return `[Tool: ${call.name}] ${status}\n${data}`;
|
|
105
|
+
}).join('\n\n');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the system prompt addition for tool usage
|
|
109
|
+
*/
|
|
110
|
+
function getToolsSystemPrompt() {
|
|
111
|
+
const tools = tools_1.toolRegistry.list();
|
|
112
|
+
if (tools.length === 0) {
|
|
113
|
+
return '';
|
|
114
|
+
}
|
|
115
|
+
const toolDescriptions = tools.map(t => {
|
|
116
|
+
const params = t.parameters.map(p => ` - ${p.name} (${p.type}${p.required ? ', required' : ''}): ${p.description}`).join('\n');
|
|
117
|
+
return `**${t.name}**: ${t.description}\nParameters:\n${params}`;
|
|
118
|
+
}).join('\n\n');
|
|
119
|
+
return `
|
|
120
|
+
You have access to the following tools. To use a tool, include a JSON block in your response:
|
|
121
|
+
|
|
122
|
+
\`\`\`json
|
|
123
|
+
{"tool": "tool_name", "args": {"param1": "value1"}}
|
|
124
|
+
\`\`\`
|
|
125
|
+
|
|
126
|
+
Available tools:
|
|
127
|
+
|
|
128
|
+
${toolDescriptions}
|
|
129
|
+
|
|
130
|
+
After I execute the tool, I'll show you the result so you can incorporate it into your response.
|
|
131
|
+
`;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Build context for tool execution
|
|
135
|
+
*/
|
|
136
|
+
function buildToolContext(sessionId, userId, channel, config) {
|
|
137
|
+
return {
|
|
138
|
+
sessionId,
|
|
139
|
+
userId,
|
|
140
|
+
channel,
|
|
141
|
+
workspaceDir: config?.workspaceDir || process.cwd(),
|
|
142
|
+
config,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -47,10 +47,12 @@ class ChannelManager {
|
|
|
47
47
|
if (!channel) {
|
|
48
48
|
// For webchat, broadcast via gateway
|
|
49
49
|
if (channelName === 'webchat') {
|
|
50
|
+
console.log(`[Channels] Broadcasting to webchat: ${text.slice(0, 50)}...`);
|
|
50
51
|
this.gateway.broadcast({ type: 'response', text, to });
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
|
-
|
|
54
|
+
console.error(`[Channels] Channel not found: ${channelName}`);
|
|
55
|
+
return;
|
|
54
56
|
}
|
|
55
57
|
await channel.send(to, text, replyTo);
|
|
56
58
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -452,9 +452,12 @@ async function startGatewayServer(config, options = {}) {
|
|
|
452
452
|
host: '127.0.0.1',
|
|
453
453
|
token: config.gateway?.token,
|
|
454
454
|
verbose: options.verbose,
|
|
455
|
+
configDir: CONFIG_DIR,
|
|
455
456
|
});
|
|
456
457
|
// Configure agent
|
|
457
458
|
gateway.agents.configure(config.agent);
|
|
459
|
+
// Initialize tools and skills system
|
|
460
|
+
await gateway.initializeTools();
|
|
458
461
|
// Register channels
|
|
459
462
|
if (config.channels?.telegram?.botToken) {
|
|
460
463
|
const telegram = new telegram_1.TelegramChannel({
|
|
@@ -466,6 +469,9 @@ async function startGatewayServer(config, options = {}) {
|
|
|
466
469
|
// Start gateway
|
|
467
470
|
await gateway.start();
|
|
468
471
|
await gateway.channels.connectAll();
|
|
472
|
+
// Show tool info
|
|
473
|
+
const tools = gateway.getToolRegistry().list();
|
|
474
|
+
const skills = gateway.getSkillManager()?.list() || [];
|
|
469
475
|
console.log('');
|
|
470
476
|
console.log(chalk.green('ApexBot is running!'));
|
|
471
477
|
console.log('');
|
|
@@ -473,6 +479,9 @@ async function startGatewayServer(config, options = {}) {
|
|
|
473
479
|
console.log(` ${chalk.cyan('API:')} http://127.0.0.1:${port}`);
|
|
474
480
|
console.log(` ${chalk.cyan('WebSocket:')} ws://127.0.0.1:${port}`);
|
|
475
481
|
console.log('');
|
|
482
|
+
console.log(` ${chalk.cyan('Tools:')} ${tools.length} available`);
|
|
483
|
+
console.log(` ${chalk.cyan('Skills:')} ${skills.length} registered`);
|
|
484
|
+
console.log('');
|
|
476
485
|
console.log(chalk.gray('Press Ctrl+C to stop.'));
|
|
477
486
|
console.log('');
|
|
478
487
|
// Handle shutdown
|
|
@@ -828,6 +837,124 @@ program
|
|
|
828
837
|
}
|
|
829
838
|
});
|
|
830
839
|
// ─────────────────────────────────────────────────────────────────
|
|
840
|
+
// SKILLS Command - Manage skills
|
|
841
|
+
// ─────────────────────────────────────────────────────────────────
|
|
842
|
+
program
|
|
843
|
+
.command('skills [action]')
|
|
844
|
+
.alias('skill')
|
|
845
|
+
.description('Manage ApexBot skills (integrations)')
|
|
846
|
+
.option('-n, --name <name>', 'Skill name')
|
|
847
|
+
.action(async (action = 'list', options) => {
|
|
848
|
+
const config = loadConfig();
|
|
849
|
+
const port = config.gateway?.port || 18789;
|
|
850
|
+
switch (action) {
|
|
851
|
+
case 'list':
|
|
852
|
+
console.log('');
|
|
853
|
+
console.log(chalk.cyan('Available Skills:'));
|
|
854
|
+
console.log('');
|
|
855
|
+
try {
|
|
856
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/skills`);
|
|
857
|
+
const data = await res.json();
|
|
858
|
+
for (const skill of data.skills) {
|
|
859
|
+
const status = skill.enabled ? chalk.green('[ENABLED]') : chalk.gray('[disabled]');
|
|
860
|
+
console.log(` ${status} ${chalk.white(skill.name)} v${skill.version}`);
|
|
861
|
+
console.log(` ${chalk.gray(skill.description)}`);
|
|
862
|
+
console.log(` Tools: ${skill.tools.join(', ')}`);
|
|
863
|
+
console.log('');
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
catch (e) {
|
|
867
|
+
console.log(chalk.yellow('Gateway not running or error connecting.'));
|
|
868
|
+
console.log(chalk.gray(`(${e.message || 'Connection refused'})\n`));
|
|
869
|
+
console.log('Available skills:');
|
|
870
|
+
console.log('');
|
|
871
|
+
console.log(' - obsidian Obsidian note-taking integration');
|
|
872
|
+
console.log(' - weather Weather forecasts');
|
|
873
|
+
console.log(' - reminder Reminders and timers');
|
|
874
|
+
console.log(' - system System info and process management');
|
|
875
|
+
console.log(' - spotify Spotify music playback control');
|
|
876
|
+
console.log('');
|
|
877
|
+
}
|
|
878
|
+
break;
|
|
879
|
+
case 'enable':
|
|
880
|
+
if (!options.name) {
|
|
881
|
+
console.log(chalk.red('Please specify skill name: --name <skill>'));
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
console.log(chalk.yellow(`Enabling skill: ${options.name}...`));
|
|
885
|
+
console.log(chalk.gray('(Skill configuration will be saved to ~/.apexbot/skills.json)'));
|
|
886
|
+
break;
|
|
887
|
+
case 'disable':
|
|
888
|
+
if (!options.name) {
|
|
889
|
+
console.log(chalk.red('Please specify skill name: --name <skill>'));
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
console.log(chalk.yellow(`Disabling skill: ${options.name}...`));
|
|
893
|
+
break;
|
|
894
|
+
default:
|
|
895
|
+
console.log('Usage: apexbot skills [list|enable|disable] --name <skill>');
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
// ─────────────────────────────────────────────────────────────────
|
|
899
|
+
// TOOLS Command - List available tools
|
|
900
|
+
// ─────────────────────────────────────────────────────────────────
|
|
901
|
+
program
|
|
902
|
+
.command('tools')
|
|
903
|
+
.alias('tool')
|
|
904
|
+
.description('List available tools')
|
|
905
|
+
.action(async () => {
|
|
906
|
+
const config = loadConfig();
|
|
907
|
+
const port = config.gateway?.port || 18789;
|
|
908
|
+
console.log('');
|
|
909
|
+
console.log(chalk.cyan('Available Tools:'));
|
|
910
|
+
console.log('');
|
|
911
|
+
try {
|
|
912
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/tools`);
|
|
913
|
+
const data = await res.json();
|
|
914
|
+
// Group by category
|
|
915
|
+
const byCategory = {};
|
|
916
|
+
for (const tool of data.tools) {
|
|
917
|
+
const cat = tool.category || 'other';
|
|
918
|
+
if (!byCategory[cat])
|
|
919
|
+
byCategory[cat] = [];
|
|
920
|
+
byCategory[cat].push(tool);
|
|
921
|
+
}
|
|
922
|
+
for (const [category, tools] of Object.entries(byCategory)) {
|
|
923
|
+
console.log(chalk.yellow(` ${category.toUpperCase()}`));
|
|
924
|
+
for (const tool of tools) {
|
|
925
|
+
console.log(` ${chalk.white(tool.name)} - ${chalk.gray(tool.description)}`);
|
|
926
|
+
}
|
|
927
|
+
console.log('');
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
catch (e) {
|
|
931
|
+
console.log(chalk.yellow('Gateway not running or error connecting.'));
|
|
932
|
+
console.log(chalk.gray(`(${e.message || 'Connection refused'})\n`));
|
|
933
|
+
console.log('Built-in tools:');
|
|
934
|
+
console.log('');
|
|
935
|
+
console.log(' CORE');
|
|
936
|
+
console.log(' shell Execute shell commands');
|
|
937
|
+
console.log(' read_file Read file contents');
|
|
938
|
+
console.log(' write_file Write to files');
|
|
939
|
+
console.log(' list_dir List directory contents');
|
|
940
|
+
console.log(' delete_file Delete files/directories');
|
|
941
|
+
console.log(' edit_file Edit files (search/replace)');
|
|
942
|
+
console.log(' fetch_url Fetch web pages');
|
|
943
|
+
console.log(' web_search Search the web');
|
|
944
|
+
console.log(' datetime Date/time operations');
|
|
945
|
+
console.log(' math Mathematical calculations');
|
|
946
|
+
console.log(' convert Unit conversions');
|
|
947
|
+
console.log('');
|
|
948
|
+
console.log(' SKILLS (when enabled)');
|
|
949
|
+
console.log(' obsidian_* Obsidian notes');
|
|
950
|
+
console.log(' weather Weather forecasts');
|
|
951
|
+
console.log(' reminder_* Reminders & timers');
|
|
952
|
+
console.log(' system_* System info');
|
|
953
|
+
console.log(' spotify_* Spotify control');
|
|
954
|
+
console.log('');
|
|
955
|
+
}
|
|
956
|
+
});
|
|
957
|
+
// ─────────────────────────────────────────────────────────────────
|
|
831
958
|
// Default action - show help with banner
|
|
832
959
|
// ─────────────────────────────────────────────────────────────────
|
|
833
960
|
program
|
package/dist/gateway/index.js
CHANGED
|
@@ -10,16 +10,19 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.Gateway = void 0;
|
|
11
11
|
const ws_1 = require("ws");
|
|
12
12
|
const http_1 = __importDefault(require("http"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
13
14
|
const eventBus_1 = require("../core/eventBus");
|
|
14
15
|
const sessionManager_1 = require("../sessions/sessionManager");
|
|
15
16
|
const channelManager_1 = require("../channels/channelManager");
|
|
16
17
|
const agentManager_1 = require("../agent/agentManager");
|
|
17
18
|
const dashboard_1 = require("./dashboard");
|
|
19
|
+
const loader_1 = require("../tools/loader");
|
|
18
20
|
class Gateway {
|
|
19
21
|
config;
|
|
20
22
|
server = null;
|
|
21
23
|
wss = null;
|
|
22
24
|
clients = new Map();
|
|
25
|
+
skillManager = null;
|
|
23
26
|
sessions;
|
|
24
27
|
channels;
|
|
25
28
|
agents;
|
|
@@ -29,34 +32,69 @@ class Gateway {
|
|
|
29
32
|
host: config.host || '127.0.0.1',
|
|
30
33
|
token: config.token,
|
|
31
34
|
verbose: config.verbose || false,
|
|
35
|
+
configDir: config.configDir || path_1.default.join(process.env.HOME || process.env.USERPROFILE || '', '.apexbot'),
|
|
32
36
|
};
|
|
33
37
|
this.sessions = new sessionManager_1.SessionManager();
|
|
34
38
|
this.channels = new channelManager_1.ChannelManager(this);
|
|
35
39
|
this.agents = new agentManager_1.AgentManager();
|
|
36
40
|
this.setupEventHandlers();
|
|
37
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Initialize tools and skills system
|
|
44
|
+
*/
|
|
45
|
+
async initializeTools() {
|
|
46
|
+
console.log('[Gateway] Initializing tools system...');
|
|
47
|
+
this.skillManager = await (0, loader_1.initializeToolsSystem)(this.config.configDir);
|
|
48
|
+
console.log(`[Gateway] Tools ready: ${loader_1.toolRegistry.list().length} tools, ${this.skillManager.list().length} skills`);
|
|
49
|
+
}
|
|
50
|
+
getSkillManager() {
|
|
51
|
+
return this.skillManager;
|
|
52
|
+
}
|
|
53
|
+
getToolRegistry() {
|
|
54
|
+
return loader_1.toolRegistry;
|
|
55
|
+
}
|
|
38
56
|
setupEventHandlers() {
|
|
39
57
|
// Route incoming messages to agent
|
|
40
58
|
(0, eventBus_1.on)('channel:message', async (msg) => {
|
|
41
|
-
|
|
42
|
-
console.log(`[Gateway] Message from ${msg.channel}:${msg.from}: ${msg.text?.slice(0, 50)}...`);
|
|
43
|
-
}
|
|
59
|
+
console.log(`[Gateway] Message from ${msg.channel}:${msg.from}: ${msg.text?.slice(0, 50)}...`);
|
|
44
60
|
// Get or create session
|
|
45
61
|
const session = this.sessions.getOrCreate(msg.channel, msg.from, msg.isGroup);
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
try {
|
|
63
|
+
// Process with agent
|
|
64
|
+
const response = await this.agents.process(session, msg);
|
|
65
|
+
console.log(`[Gateway] Agent response: ${response?.text?.slice(0, 50)}...`);
|
|
66
|
+
// Send response back
|
|
67
|
+
if (response && response.text) {
|
|
68
|
+
(0, eventBus_1.emit)('channel:send', {
|
|
69
|
+
channel: msg.channel,
|
|
70
|
+
to: msg.from,
|
|
71
|
+
text: response.text,
|
|
72
|
+
replyTo: msg.id,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.warn('[Gateway] No response from agent');
|
|
77
|
+
(0, eventBus_1.emit)('channel:send', {
|
|
78
|
+
channel: msg.channel,
|
|
79
|
+
to: msg.from,
|
|
80
|
+
text: 'Sorry, I could not generate a response. Please try again.',
|
|
81
|
+
replyTo: msg.id,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
console.error('[Gateway] Error processing message:', error);
|
|
50
87
|
(0, eventBus_1.emit)('channel:send', {
|
|
51
88
|
channel: msg.channel,
|
|
52
89
|
to: msg.from,
|
|
53
|
-
text:
|
|
90
|
+
text: `Error: ${error.message || 'Unknown error'}`,
|
|
54
91
|
replyTo: msg.id,
|
|
55
92
|
});
|
|
56
93
|
}
|
|
57
94
|
});
|
|
58
95
|
// Handle outgoing messages
|
|
59
96
|
(0, eventBus_1.on)('channel:send', async (msg) => {
|
|
97
|
+
console.log(`[Gateway] Sending to ${msg.channel}:${msg.to}: ${msg.text?.slice(0, 50)}...`);
|
|
60
98
|
await this.channels.send(msg.channel, msg.to, msg.text, msg.replyTo);
|
|
61
99
|
});
|
|
62
100
|
}
|
|
@@ -131,6 +169,22 @@ class Gateway {
|
|
|
131
169
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
132
170
|
res.end(JSON.stringify(this.sessions.getAll()));
|
|
133
171
|
break;
|
|
172
|
+
case '/api/tools':
|
|
173
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
174
|
+
res.end(JSON.stringify({
|
|
175
|
+
tools: loader_1.toolRegistry.list(),
|
|
176
|
+
count: loader_1.toolRegistry.list().length,
|
|
177
|
+
categories: loader_1.toolRegistry.getCategories(),
|
|
178
|
+
}));
|
|
179
|
+
break;
|
|
180
|
+
case '/api/skills':
|
|
181
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
182
|
+
const sm = this.getSkillManager();
|
|
183
|
+
res.end(JSON.stringify({
|
|
184
|
+
skills: sm ? sm.list() : [],
|
|
185
|
+
enabled: sm ? sm.listEnabled() : [],
|
|
186
|
+
}));
|
|
187
|
+
break;
|
|
134
188
|
default:
|
|
135
189
|
// Serve Dashboard UI
|
|
136
190
|
if (url.pathname.startsWith('/chat') || url.pathname === '/ui' || url.pathname === '/dashboard') {
|