apexbot 1.0.1 → 1.0.4
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 +127 -146
- package/dist/agent/agentManager.js +65 -18
- package/dist/agent/toolExecutor.js +144 -0
- package/dist/channels/channelManager.js +3 -1
- package/dist/cli/index.js +149 -33
- package/dist/gateway/dashboard.js +182 -266
- package/dist/gateway/index.js +65 -11
- package/dist/index.js +12 -12
- package/dist/skills/index.js +212 -0
- package/dist/skills/obsidian.js +440 -0
- package/dist/skills/reminder.js +430 -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 +66 -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
|
@@ -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
|
@@ -181,7 +181,7 @@ you log in on this machine, or a bot account like Telegram/Discord).
|
|
|
181
181
|
${chalk.yellow("If you're new to this, start with Ollama and least privilege.")} It helps limit what
|
|
182
182
|
an agent can do if it's tricked or makes a mistake.
|
|
183
183
|
|
|
184
|
-
${chalk.cyan('
|
|
184
|
+
${chalk.cyan('Good news:')} ApexBot uses ${chalk.green('Ollama (local AI)')} by default — your data never leaves
|
|
185
185
|
your computer. No cloud APIs, no tracking, 100% private and FREE.`);
|
|
186
186
|
console.log('');
|
|
187
187
|
const { continueSetup } = await inquirer.prompt([{
|
|
@@ -339,9 +339,9 @@ Configure at least one channel to start chatting.`);
|
|
|
339
339
|
name: 'channels',
|
|
340
340
|
message: 'Select channels to configure (space to toggle, enter to confirm):',
|
|
341
341
|
choices: [
|
|
342
|
-
{ name: '
|
|
343
|
-
{ name: '
|
|
344
|
-
{ name: '
|
|
342
|
+
{ name: 'Telegram', value: 'telegram' },
|
|
343
|
+
{ name: 'Discord', value: 'discord' },
|
|
344
|
+
{ name: 'WebChat (built-in)', value: 'webchat' },
|
|
345
345
|
],
|
|
346
346
|
validate: (input) => {
|
|
347
347
|
if (input.length === 0) {
|
|
@@ -413,15 +413,15 @@ WebChat UI will be available at http://localhost:<port>/chat`);
|
|
|
413
413
|
console.log('');
|
|
414
414
|
console.log(chalk.gray('─'.repeat(60)));
|
|
415
415
|
console.log('');
|
|
416
|
-
showBox(`${chalk.green('
|
|
416
|
+
showBox(`${chalk.green('Configuration saved!')}
|
|
417
417
|
|
|
418
|
-
Config file: ${chalk.cyan(CONFIG_FILE)}`, '
|
|
418
|
+
Config file: ${chalk.cyan(CONFIG_FILE)}`, 'Setup Complete');
|
|
419
419
|
console.log('');
|
|
420
420
|
// Ask to start gateway immediately
|
|
421
421
|
const { startNow } = await inquirer.prompt([{
|
|
422
422
|
type: 'confirm',
|
|
423
423
|
name: 'startNow',
|
|
424
|
-
message: chalk.yellow('
|
|
424
|
+
message: chalk.yellow('Start ApexBot gateway now?'),
|
|
425
425
|
default: true,
|
|
426
426
|
}]);
|
|
427
427
|
if (startNow) {
|
|
@@ -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,19 +469,25 @@ 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
|
-
console.log(chalk.green('
|
|
476
|
+
console.log(chalk.green('ApexBot is running!'));
|
|
471
477
|
console.log('');
|
|
472
478
|
console.log(` ${chalk.cyan('Dashboard:')} http://127.0.0.1:${port}/chat`);
|
|
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
|
|
479
488
|
process.on('SIGINT', async () => {
|
|
480
489
|
console.log('');
|
|
481
|
-
console.log(chalk.yellow('
|
|
490
|
+
console.log(chalk.yellow('Shutting down...'));
|
|
482
491
|
await gateway.stop();
|
|
483
492
|
process.exit(0);
|
|
484
493
|
});
|
|
@@ -497,7 +506,7 @@ program
|
|
|
497
506
|
const config = loadConfig();
|
|
498
507
|
if (!config.agent?.provider) {
|
|
499
508
|
console.log('');
|
|
500
|
-
console.log(chalk.yellow('
|
|
509
|
+
console.log(chalk.yellow('ApexBot is not configured yet.'));
|
|
501
510
|
console.log('');
|
|
502
511
|
console.log(`Run ${chalk.green('apexbot onboard')} to set up your bot.`);
|
|
503
512
|
console.log('');
|
|
@@ -520,12 +529,12 @@ program
|
|
|
520
529
|
// Check config
|
|
521
530
|
const hasConfig = fs.existsSync(CONFIG_FILE);
|
|
522
531
|
// Check Ollama
|
|
523
|
-
let ollamaStatus = '
|
|
532
|
+
let ollamaStatus = 'Not checked';
|
|
524
533
|
if (config.agent?.provider === 'ollama') {
|
|
525
534
|
const ollama = await checkOllama(config.agent.apiUrl || 'http://localhost:11434');
|
|
526
535
|
ollamaStatus = ollama.running
|
|
527
|
-
? chalk.green(
|
|
528
|
-
: chalk.red('
|
|
536
|
+
? chalk.green(`Running (${ollama.models.length} models)`)
|
|
537
|
+
: chalk.red('Not running');
|
|
529
538
|
}
|
|
530
539
|
// Check gateway
|
|
531
540
|
let gatewayStatus = chalk.gray('Not running');
|
|
@@ -534,7 +543,7 @@ program
|
|
|
534
543
|
const res = await fetch(`http://127.0.0.1:${port}/health`);
|
|
535
544
|
if (res.ok) {
|
|
536
545
|
const data = await res.json();
|
|
537
|
-
gatewayStatus = chalk.green(
|
|
546
|
+
gatewayStatus = chalk.green(`Running (${data.sessions} sessions)`);
|
|
538
547
|
}
|
|
539
548
|
}
|
|
540
549
|
catch (e) {
|
|
@@ -543,7 +552,7 @@ program
|
|
|
543
552
|
spinner.stop();
|
|
544
553
|
console.log(chalk.cyan('Status:'));
|
|
545
554
|
console.log('');
|
|
546
|
-
console.log(` Config: ${hasConfig ? chalk.green(
|
|
555
|
+
console.log(` Config: ${hasConfig ? chalk.green(CONFIG_FILE) : chalk.yellow('Not configured')}`);
|
|
547
556
|
console.log(` Provider: ${config.agent?.provider || chalk.gray('None')}`);
|
|
548
557
|
console.log(` Model: ${config.agent?.model || chalk.gray('None')}`);
|
|
549
558
|
console.log(` Ollama: ${ollamaStatus}`);
|
|
@@ -575,7 +584,7 @@ program
|
|
|
575
584
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
576
585
|
fs.unlinkSync(CONFIG_FILE);
|
|
577
586
|
}
|
|
578
|
-
console.log(chalk.green('
|
|
587
|
+
console.log(chalk.green('Configuration reset.'));
|
|
579
588
|
console.log(`Run ${chalk.cyan('apexbot onboard')} to set up again.`);
|
|
580
589
|
}
|
|
581
590
|
return;
|
|
@@ -625,7 +634,7 @@ program
|
|
|
625
634
|
console.log(stdout);
|
|
626
635
|
if (stderr)
|
|
627
636
|
console.log(stderr);
|
|
628
|
-
console.log(chalk.green('
|
|
637
|
+
console.log(chalk.green('Model pulled successfully!'));
|
|
629
638
|
});
|
|
630
639
|
return;
|
|
631
640
|
}
|
|
@@ -634,7 +643,7 @@ program
|
|
|
634
643
|
spinner.stop();
|
|
635
644
|
if (!ollama.running) {
|
|
636
645
|
console.log('');
|
|
637
|
-
console.log(chalk.red('
|
|
646
|
+
console.log(chalk.red('Ollama is not running.'));
|
|
638
647
|
console.log(chalk.gray('Start it with: ollama serve'));
|
|
639
648
|
console.log('');
|
|
640
649
|
return;
|
|
@@ -677,7 +686,7 @@ program
|
|
|
677
686
|
try {
|
|
678
687
|
process.kill(parseInt(oldPid), 0);
|
|
679
688
|
console.log('');
|
|
680
|
-
console.log(chalk.yellow(
|
|
689
|
+
console.log(chalk.yellow(`Daemon already running (PID: ${oldPid})`));
|
|
681
690
|
console.log(chalk.gray(`Use 'apexbot daemon restart' to restart.`));
|
|
682
691
|
console.log('');
|
|
683
692
|
return;
|
|
@@ -688,23 +697,24 @@ program
|
|
|
688
697
|
}
|
|
689
698
|
}
|
|
690
699
|
console.log('');
|
|
691
|
-
console.log(chalk.cyan('
|
|
700
|
+
console.log(chalk.cyan('Starting ApexBot daemon...'));
|
|
692
701
|
// Spawn detached process (cross-platform)
|
|
693
702
|
// Use 'apexbot gateway' command directly - works with global npm install
|
|
694
703
|
const { spawn } = require('child_process');
|
|
695
704
|
const out = fs.openSync(logFile, 'a');
|
|
696
705
|
const err = fs.openSync(logFile, 'a');
|
|
697
706
|
const isWindows = process.platform === 'win32';
|
|
698
|
-
const child = spawn(isWindows ? 'cmd.exe' : '/bin/sh', isWindows
|
|
707
|
+
const child = spawn(isWindows ? process.env.ComSpec || 'cmd.exe' : '/bin/sh', isWindows
|
|
699
708
|
? ['/c', 'apexbot', 'gateway']
|
|
700
709
|
: ['-c', 'apexbot gateway'], {
|
|
701
710
|
detached: true,
|
|
702
711
|
stdio: ['ignore', out, err],
|
|
703
712
|
shell: false,
|
|
713
|
+
windowsHide: true, // Hide console window on Windows
|
|
704
714
|
});
|
|
705
715
|
fs.writeFileSync(pidFile, String(child.pid));
|
|
706
716
|
child.unref();
|
|
707
|
-
console.log(chalk.green(
|
|
717
|
+
console.log(chalk.green('Daemon started (PID: ' + child.pid + ')'));
|
|
708
718
|
console.log('');
|
|
709
719
|
console.log(` ${chalk.cyan('Dashboard:')} http://127.0.0.1:${config.gateway?.port || 18789}/chat`);
|
|
710
720
|
console.log(` ${chalk.cyan('Logs:')} ${logFile}`);
|
|
@@ -717,25 +727,25 @@ program
|
|
|
717
727
|
case 'stop': {
|
|
718
728
|
if (!fs.existsSync(pidFile)) {
|
|
719
729
|
console.log('');
|
|
720
|
-
console.log(chalk.yellow('
|
|
730
|
+
console.log(chalk.yellow('Daemon is not running.'));
|
|
721
731
|
console.log('');
|
|
722
732
|
return;
|
|
723
733
|
}
|
|
724
734
|
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim());
|
|
725
735
|
console.log('');
|
|
726
|
-
console.log(chalk.cyan(
|
|
736
|
+
console.log(chalk.cyan(`Stopping daemon (PID: ${pid})...`));
|
|
727
737
|
try {
|
|
728
738
|
process.kill(pid, 'SIGTERM');
|
|
729
739
|
fs.unlinkSync(pidFile);
|
|
730
|
-
console.log(chalk.green('
|
|
740
|
+
console.log(chalk.green('Daemon stopped.'));
|
|
731
741
|
}
|
|
732
742
|
catch (e) {
|
|
733
743
|
if (e.code === 'ESRCH') {
|
|
734
744
|
fs.unlinkSync(pidFile);
|
|
735
|
-
console.log(chalk.yellow('
|
|
745
|
+
console.log(chalk.yellow('Daemon was not running (stale PID file removed).'));
|
|
736
746
|
}
|
|
737
747
|
else {
|
|
738
|
-
console.log(chalk.red(
|
|
748
|
+
console.log(chalk.red(`Failed to stop daemon: ${e.message}`));
|
|
739
749
|
}
|
|
740
750
|
}
|
|
741
751
|
console.log('');
|
|
@@ -743,7 +753,7 @@ program
|
|
|
743
753
|
}
|
|
744
754
|
case 'restart': {
|
|
745
755
|
console.log('');
|
|
746
|
-
console.log(chalk.cyan('
|
|
756
|
+
console.log(chalk.cyan('Restarting daemon...'));
|
|
747
757
|
// Stop first
|
|
748
758
|
if (fs.existsSync(pidFile)) {
|
|
749
759
|
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim());
|
|
@@ -761,16 +771,17 @@ program
|
|
|
761
771
|
const out = fs.openSync(logFile, 'a');
|
|
762
772
|
const err = fs.openSync(logFile, 'a');
|
|
763
773
|
const isWindows = process.platform === 'win32';
|
|
764
|
-
const child = spawn(isWindows ? 'cmd.exe' : '/bin/sh', isWindows
|
|
774
|
+
const child = spawn(isWindows ? process.env.ComSpec || 'cmd.exe' : '/bin/sh', isWindows
|
|
765
775
|
? ['/c', 'apexbot', 'gateway']
|
|
766
776
|
: ['-c', 'apexbot gateway'], {
|
|
767
777
|
detached: true,
|
|
768
778
|
stdio: ['ignore', out, err],
|
|
769
779
|
shell: false,
|
|
780
|
+
windowsHide: true,
|
|
770
781
|
});
|
|
771
782
|
fs.writeFileSync(pidFile, String(child.pid));
|
|
772
783
|
child.unref();
|
|
773
|
-
console.log(chalk.green(
|
|
784
|
+
console.log(chalk.green('Daemon restarted (PID: ' + child.pid + ')'));
|
|
774
785
|
console.log('');
|
|
775
786
|
break;
|
|
776
787
|
}
|
|
@@ -779,7 +790,7 @@ program
|
|
|
779
790
|
console.log(chalk.cyan(MINI_LOGO));
|
|
780
791
|
console.log('');
|
|
781
792
|
if (!fs.existsSync(pidFile)) {
|
|
782
|
-
console.log(chalk.yellow('
|
|
793
|
+
console.log(chalk.yellow('Daemon is not running.'));
|
|
783
794
|
console.log('');
|
|
784
795
|
console.log(chalk.gray('Start with: apexbot daemon start'));
|
|
785
796
|
console.log('');
|
|
@@ -795,7 +806,7 @@ program
|
|
|
795
806
|
isRunning = false;
|
|
796
807
|
}
|
|
797
808
|
if (isRunning) {
|
|
798
|
-
console.log(chalk.green(
|
|
809
|
+
console.log(chalk.green('Daemon is running (PID: ' + pid + ')'));
|
|
799
810
|
console.log('');
|
|
800
811
|
// Try to get status from gateway
|
|
801
812
|
try {
|
|
@@ -811,7 +822,7 @@ program
|
|
|
811
822
|
}
|
|
812
823
|
}
|
|
813
824
|
else {
|
|
814
|
-
console.log(chalk.yellow('
|
|
825
|
+
console.log(chalk.yellow('Daemon process died (cleaning up PID file).'));
|
|
815
826
|
fs.unlinkSync(pidFile);
|
|
816
827
|
}
|
|
817
828
|
console.log('');
|
|
@@ -826,6 +837,111 @@ program
|
|
|
826
837
|
}
|
|
827
838
|
});
|
|
828
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. Available skills:'));
|
|
868
|
+
console.log('');
|
|
869
|
+
console.log(' - obsidian Obsidian note-taking integration');
|
|
870
|
+
console.log(' - weather Weather forecasts');
|
|
871
|
+
console.log(' - reminder Reminders and timers');
|
|
872
|
+
console.log(' - system System info and process management');
|
|
873
|
+
console.log('');
|
|
874
|
+
}
|
|
875
|
+
break;
|
|
876
|
+
case 'enable':
|
|
877
|
+
if (!options.name) {
|
|
878
|
+
console.log(chalk.red('Please specify skill name: --name <skill>'));
|
|
879
|
+
return;
|
|
880
|
+
}
|
|
881
|
+
console.log(chalk.yellow(`Enabling skill: ${options.name}...`));
|
|
882
|
+
console.log(chalk.gray('(Skill configuration will be saved to ~/.apexbot/skills.json)'));
|
|
883
|
+
break;
|
|
884
|
+
case 'disable':
|
|
885
|
+
if (!options.name) {
|
|
886
|
+
console.log(chalk.red('Please specify skill name: --name <skill>'));
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
console.log(chalk.yellow(`Disabling skill: ${options.name}...`));
|
|
890
|
+
break;
|
|
891
|
+
default:
|
|
892
|
+
console.log('Usage: apexbot skills [list|enable|disable] --name <skill>');
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
// ─────────────────────────────────────────────────────────────────
|
|
896
|
+
// TOOLS Command - List available tools
|
|
897
|
+
// ─────────────────────────────────────────────────────────────────
|
|
898
|
+
program
|
|
899
|
+
.command('tools')
|
|
900
|
+
.alias('tool')
|
|
901
|
+
.description('List available tools')
|
|
902
|
+
.action(async () => {
|
|
903
|
+
const config = loadConfig();
|
|
904
|
+
const port = config.gateway?.port || 18789;
|
|
905
|
+
console.log('');
|
|
906
|
+
console.log(chalk.cyan('Available Tools:'));
|
|
907
|
+
console.log('');
|
|
908
|
+
try {
|
|
909
|
+
const res = await fetch(`http://127.0.0.1:${port}/api/tools`);
|
|
910
|
+
const data = await res.json();
|
|
911
|
+
// Group by category
|
|
912
|
+
const byCategory = {};
|
|
913
|
+
for (const tool of data.tools) {
|
|
914
|
+
const cat = tool.category || 'other';
|
|
915
|
+
if (!byCategory[cat])
|
|
916
|
+
byCategory[cat] = [];
|
|
917
|
+
byCategory[cat].push(tool);
|
|
918
|
+
}
|
|
919
|
+
for (const [category, tools] of Object.entries(byCategory)) {
|
|
920
|
+
console.log(chalk.yellow(` ${category.toUpperCase()}`));
|
|
921
|
+
for (const tool of tools) {
|
|
922
|
+
console.log(` ${chalk.white(tool.name)} - ${chalk.gray(tool.description)}`);
|
|
923
|
+
}
|
|
924
|
+
console.log('');
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
catch (e) {
|
|
928
|
+
console.log(chalk.yellow('Gateway not running. Built-in tools:'));
|
|
929
|
+
console.log('');
|
|
930
|
+
console.log(' CORE');
|
|
931
|
+
console.log(' shell Execute shell commands');
|
|
932
|
+
console.log(' read_file Read file contents');
|
|
933
|
+
console.log(' write_file Write to files');
|
|
934
|
+
console.log(' list_dir List directory contents');
|
|
935
|
+
console.log(' edit_file Edit files (search/replace)');
|
|
936
|
+
console.log(' fetch_url Fetch web pages');
|
|
937
|
+
console.log(' web_search Search the web');
|
|
938
|
+
console.log(' datetime Date/time operations');
|
|
939
|
+
console.log(' math Mathematical calculations');
|
|
940
|
+
console.log(' convert Unit conversions');
|
|
941
|
+
console.log('');
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
// ─────────────────────────────────────────────────────────────────
|
|
829
945
|
// Default action - show help with banner
|
|
830
946
|
// ─────────────────────────────────────────────────────────────────
|
|
831
947
|
program
|