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
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Math Tool
|
|
4
|
+
*
|
|
5
|
+
* Perform calculations, unit conversions, and math operations.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.convertTool = exports.mathTool = void 0;
|
|
9
|
+
exports.registerMathTools = registerMathTools;
|
|
10
|
+
const index_1 = require("./index");
|
|
11
|
+
const mathTool = {
|
|
12
|
+
definition: {
|
|
13
|
+
name: 'math',
|
|
14
|
+
description: 'Evaluate mathematical expressions and perform calculations.',
|
|
15
|
+
category: 'core',
|
|
16
|
+
parameters: [
|
|
17
|
+
{
|
|
18
|
+
name: 'expression',
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Mathematical expression to evaluate (e.g., "2 + 2", "sqrt(16)", "sin(45)")',
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
returns: 'Calculation result',
|
|
25
|
+
examples: [
|
|
26
|
+
'math({ expression: "2 + 2 * 3" })',
|
|
27
|
+
'math({ expression: "sqrt(144) + pow(2, 10)" })',
|
|
28
|
+
'math({ expression: "(100 * 1.15) - 50" })',
|
|
29
|
+
],
|
|
30
|
+
},
|
|
31
|
+
async execute(params, context) {
|
|
32
|
+
const { expression } = params;
|
|
33
|
+
if (!expression) {
|
|
34
|
+
return { success: false, error: 'Expression is required' };
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
// Safe math evaluation with supported functions
|
|
38
|
+
const mathFunctions = {
|
|
39
|
+
abs: Math.abs,
|
|
40
|
+
ceil: Math.ceil,
|
|
41
|
+
floor: Math.floor,
|
|
42
|
+
round: Math.round,
|
|
43
|
+
sqrt: Math.sqrt,
|
|
44
|
+
cbrt: Math.cbrt,
|
|
45
|
+
pow: Math.pow,
|
|
46
|
+
exp: Math.exp,
|
|
47
|
+
log: Math.log,
|
|
48
|
+
log10: Math.log10,
|
|
49
|
+
log2: Math.log2,
|
|
50
|
+
sin: Math.sin,
|
|
51
|
+
cos: Math.cos,
|
|
52
|
+
tan: Math.tan,
|
|
53
|
+
asin: Math.asin,
|
|
54
|
+
acos: Math.acos,
|
|
55
|
+
atan: Math.atan,
|
|
56
|
+
sinh: Math.sinh,
|
|
57
|
+
cosh: Math.cosh,
|
|
58
|
+
tanh: Math.tanh,
|
|
59
|
+
min: Math.min,
|
|
60
|
+
max: Math.max,
|
|
61
|
+
random: Math.random,
|
|
62
|
+
sign: Math.sign,
|
|
63
|
+
trunc: Math.trunc,
|
|
64
|
+
};
|
|
65
|
+
const constants = {
|
|
66
|
+
PI: Math.PI,
|
|
67
|
+
E: Math.E,
|
|
68
|
+
LN2: Math.LN2,
|
|
69
|
+
LN10: Math.LN10,
|
|
70
|
+
SQRT2: Math.SQRT2,
|
|
71
|
+
};
|
|
72
|
+
// Sanitize and prepare expression
|
|
73
|
+
let sanitized = expression
|
|
74
|
+
.replace(/\s+/g, ' ')
|
|
75
|
+
.trim();
|
|
76
|
+
// Replace constants
|
|
77
|
+
for (const [name, value] of Object.entries(constants)) {
|
|
78
|
+
sanitized = sanitized.replace(new RegExp(`\\b${name}\\b`, 'gi'), String(value));
|
|
79
|
+
}
|
|
80
|
+
// Check for dangerous patterns
|
|
81
|
+
if (/[;{}[\]]|function|eval|require|import|process|global/.test(sanitized)) {
|
|
82
|
+
return { success: false, error: 'Invalid expression: contains forbidden patterns' };
|
|
83
|
+
}
|
|
84
|
+
// Build safe evaluation context
|
|
85
|
+
const funcNames = Object.keys(mathFunctions).join(',');
|
|
86
|
+
const funcValues = Object.values(mathFunctions);
|
|
87
|
+
// Create function with math functions in scope
|
|
88
|
+
const evalFunc = new Function(...Object.keys(mathFunctions), `"use strict"; return (${sanitized});`);
|
|
89
|
+
const result = evalFunc(...funcValues);
|
|
90
|
+
if (typeof result !== 'number' || !isFinite(result)) {
|
|
91
|
+
return {
|
|
92
|
+
success: true,
|
|
93
|
+
data: {
|
|
94
|
+
expression,
|
|
95
|
+
result: String(result),
|
|
96
|
+
type: typeof result,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
data: {
|
|
103
|
+
expression,
|
|
104
|
+
result,
|
|
105
|
+
formatted: result.toLocaleString(),
|
|
106
|
+
scientific: result.toExponential(),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
return { success: false, error: `Evaluation error: ${error.message}` };
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
exports.mathTool = mathTool;
|
|
116
|
+
// Unit Conversion Tool
|
|
117
|
+
const convertTool = {
|
|
118
|
+
definition: {
|
|
119
|
+
name: 'convert',
|
|
120
|
+
description: 'Convert between units (length, weight, temperature, etc.)',
|
|
121
|
+
category: 'core',
|
|
122
|
+
parameters: [
|
|
123
|
+
{
|
|
124
|
+
name: 'value',
|
|
125
|
+
type: 'number',
|
|
126
|
+
description: 'Value to convert',
|
|
127
|
+
required: true,
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: 'from',
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: 'Source unit',
|
|
133
|
+
required: true,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'to',
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Target unit',
|
|
139
|
+
required: true,
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
returns: 'Converted value',
|
|
143
|
+
examples: [
|
|
144
|
+
'convert({ value: 100, from: "km", to: "miles" })',
|
|
145
|
+
'convert({ value: 32, from: "fahrenheit", to: "celsius" })',
|
|
146
|
+
'convert({ value: 1, from: "gb", to: "mb" })',
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
async execute(params, context) {
|
|
150
|
+
const { value, from, to } = params;
|
|
151
|
+
if (value === undefined || !from || !to) {
|
|
152
|
+
return { success: false, error: 'Value, from, and to are required' };
|
|
153
|
+
}
|
|
154
|
+
const conversions = {
|
|
155
|
+
// Length (base: meters)
|
|
156
|
+
m: { m: 1, km: 0.001, cm: 100, mm: 1000, mi: 0.000621371, miles: 0.000621371, ft: 3.28084, feet: 3.28084, in: 39.3701, inch: 39.3701, yd: 1.09361, yard: 1.09361 },
|
|
157
|
+
km: { m: 1000, km: 1, cm: 100000, mm: 1000000, mi: 0.621371, miles: 0.621371, ft: 3280.84, feet: 3280.84 },
|
|
158
|
+
miles: { km: 1.60934, m: 1609.34, mi: 1, miles: 1, ft: 5280, feet: 5280 },
|
|
159
|
+
mi: { km: 1.60934, m: 1609.34, mi: 1, miles: 1, ft: 5280, feet: 5280 },
|
|
160
|
+
cm: { m: 0.01, km: 0.00001, cm: 1, mm: 10, in: 0.393701, inch: 0.393701 },
|
|
161
|
+
ft: { m: 0.3048, cm: 30.48, in: 12, inch: 12, ft: 1, feet: 1 },
|
|
162
|
+
feet: { m: 0.3048, cm: 30.48, in: 12, inch: 12, ft: 1, feet: 1 },
|
|
163
|
+
in: { cm: 2.54, m: 0.0254, ft: 0.0833333, feet: 0.0833333, in: 1, inch: 1 },
|
|
164
|
+
inch: { cm: 2.54, m: 0.0254, ft: 0.0833333, feet: 0.0833333, in: 1, inch: 1 },
|
|
165
|
+
// Weight (base: kg)
|
|
166
|
+
kg: { kg: 1, g: 1000, lb: 2.20462, lbs: 2.20462, oz: 35.274 },
|
|
167
|
+
g: { kg: 0.001, g: 1, mg: 1000, lb: 0.00220462, oz: 0.035274 },
|
|
168
|
+
lb: { kg: 0.453592, g: 453.592, lb: 1, lbs: 1, oz: 16 },
|
|
169
|
+
lbs: { kg: 0.453592, g: 453.592, lb: 1, lbs: 1, oz: 16 },
|
|
170
|
+
oz: { g: 28.3495, kg: 0.0283495, lb: 0.0625, lbs: 0.0625, oz: 1 },
|
|
171
|
+
// Data (base: bytes)
|
|
172
|
+
b: { b: 1, kb: 0.001, mb: 0.000001, gb: 0.000000001, tb: 0.000000000001 },
|
|
173
|
+
kb: { b: 1000, kb: 1, mb: 0.001, gb: 0.000001, tb: 0.000000001 },
|
|
174
|
+
mb: { b: 1000000, kb: 1000, mb: 1, gb: 0.001, tb: 0.000001 },
|
|
175
|
+
gb: { b: 1000000000, kb: 1000000, mb: 1000, gb: 1, tb: 0.001 },
|
|
176
|
+
tb: { b: 1000000000000, kb: 1000000000, mb: 1000000, gb: 1000, tb: 1 },
|
|
177
|
+
// Time
|
|
178
|
+
sec: { sec: 1, min: 1 / 60, hr: 1 / 3600, day: 1 / 86400 },
|
|
179
|
+
min: { sec: 60, min: 1, hr: 1 / 60, day: 1 / 1440 },
|
|
180
|
+
hr: { sec: 3600, min: 60, hr: 1, day: 1 / 24 },
|
|
181
|
+
day: { sec: 86400, min: 1440, hr: 24, day: 1, week: 1 / 7 },
|
|
182
|
+
week: { day: 7, hr: 168, min: 10080, sec: 604800 },
|
|
183
|
+
};
|
|
184
|
+
const fromLower = from.toLowerCase();
|
|
185
|
+
const toLower = to.toLowerCase();
|
|
186
|
+
// Special case: temperature
|
|
187
|
+
if (['c', 'celsius', 'f', 'fahrenheit', 'k', 'kelvin'].includes(fromLower)) {
|
|
188
|
+
let celsius;
|
|
189
|
+
// Convert to Celsius first
|
|
190
|
+
if (fromLower === 'c' || fromLower === 'celsius') {
|
|
191
|
+
celsius = value;
|
|
192
|
+
}
|
|
193
|
+
else if (fromLower === 'f' || fromLower === 'fahrenheit') {
|
|
194
|
+
celsius = (value - 32) * 5 / 9;
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
celsius = value - 273.15;
|
|
198
|
+
}
|
|
199
|
+
// Convert from Celsius to target
|
|
200
|
+
let result;
|
|
201
|
+
if (toLower === 'c' || toLower === 'celsius') {
|
|
202
|
+
result = celsius;
|
|
203
|
+
}
|
|
204
|
+
else if (toLower === 'f' || toLower === 'fahrenheit') {
|
|
205
|
+
result = celsius * 9 / 5 + 32;
|
|
206
|
+
}
|
|
207
|
+
else if (toLower === 'k' || toLower === 'kelvin') {
|
|
208
|
+
result = celsius + 273.15;
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
return { success: false, error: `Unknown temperature unit: ${to}` };
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
success: true,
|
|
215
|
+
data: {
|
|
216
|
+
value,
|
|
217
|
+
from,
|
|
218
|
+
to,
|
|
219
|
+
result: Math.round(result * 100) / 100,
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
// Look up conversion
|
|
224
|
+
const fromConversions = conversions[fromLower];
|
|
225
|
+
if (!fromConversions) {
|
|
226
|
+
return { success: false, error: `Unknown unit: ${from}` };
|
|
227
|
+
}
|
|
228
|
+
const factor = fromConversions[toLower];
|
|
229
|
+
if (factor === undefined) {
|
|
230
|
+
return { success: false, error: `Cannot convert from ${from} to ${to}` };
|
|
231
|
+
}
|
|
232
|
+
const result = value * factor;
|
|
233
|
+
return {
|
|
234
|
+
success: true,
|
|
235
|
+
data: {
|
|
236
|
+
value,
|
|
237
|
+
from,
|
|
238
|
+
to,
|
|
239
|
+
result: Math.round(result * 1000000) / 1000000,
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
},
|
|
243
|
+
};
|
|
244
|
+
exports.convertTool = convertTool;
|
|
245
|
+
function registerMathTools() {
|
|
246
|
+
index_1.toolRegistry.register(mathTool);
|
|
247
|
+
index_1.toolRegistry.register(convertTool);
|
|
248
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Shell Tool
|
|
4
|
+
*
|
|
5
|
+
* Execute shell commands safely with timeout and output capture.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const shellTool = {
|
|
10
|
+
definition: {
|
|
11
|
+
name: 'shell',
|
|
12
|
+
description: 'Execute a shell command and return the output. Use for running scripts, system commands, or CLI tools.',
|
|
13
|
+
category: 'core',
|
|
14
|
+
parameters: [
|
|
15
|
+
{
|
|
16
|
+
name: 'command',
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: 'The shell command to execute',
|
|
19
|
+
required: true,
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
name: 'cwd',
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Working directory for the command',
|
|
25
|
+
required: false,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: 'timeout',
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: 'Timeout in milliseconds (default: 30000)',
|
|
31
|
+
required: false,
|
|
32
|
+
default: 30000,
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
returns: 'Command output (stdout and stderr)',
|
|
36
|
+
examples: [
|
|
37
|
+
'shell({ command: "ls -la" })',
|
|
38
|
+
'shell({ command: "npm install", cwd: "/path/to/project" })',
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
async execute(params, context) {
|
|
42
|
+
const { command, cwd, timeout = 30000 } = params;
|
|
43
|
+
if (!command) {
|
|
44
|
+
return { success: false, error: 'Command is required' };
|
|
45
|
+
}
|
|
46
|
+
// Security check - block dangerous commands
|
|
47
|
+
const dangerous = ['rm -rf /', 'format c:', ':(){:|:&};:', 'dd if=', 'mkfs'];
|
|
48
|
+
for (const pattern of dangerous) {
|
|
49
|
+
if (command.toLowerCase().includes(pattern.toLowerCase())) {
|
|
50
|
+
return { success: false, error: 'Blocked: potentially dangerous command' };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
const isWindows = process.platform === 'win32';
|
|
55
|
+
const shell = isWindows ? 'powershell.exe' : '/bin/bash';
|
|
56
|
+
const shellArgs = isWindows ? ['-Command', command] : ['-c', command];
|
|
57
|
+
const proc = (0, child_process_1.spawn)(shell, shellArgs, {
|
|
58
|
+
cwd: cwd || context.workspaceDir || process.cwd(),
|
|
59
|
+
windowsHide: true,
|
|
60
|
+
timeout,
|
|
61
|
+
});
|
|
62
|
+
let stdout = '';
|
|
63
|
+
let stderr = '';
|
|
64
|
+
proc.stdout.on('data', (data) => {
|
|
65
|
+
stdout += data.toString();
|
|
66
|
+
});
|
|
67
|
+
proc.stderr.on('data', (data) => {
|
|
68
|
+
stderr += data.toString();
|
|
69
|
+
});
|
|
70
|
+
const timeoutId = setTimeout(() => {
|
|
71
|
+
proc.kill('SIGTERM');
|
|
72
|
+
resolve({
|
|
73
|
+
success: false,
|
|
74
|
+
error: 'Command timed out',
|
|
75
|
+
data: { stdout, stderr },
|
|
76
|
+
});
|
|
77
|
+
}, timeout);
|
|
78
|
+
proc.on('close', (code) => {
|
|
79
|
+
clearTimeout(timeoutId);
|
|
80
|
+
resolve({
|
|
81
|
+
success: code === 0,
|
|
82
|
+
data: {
|
|
83
|
+
exitCode: code,
|
|
84
|
+
stdout: stdout.trim(),
|
|
85
|
+
stderr: stderr.trim(),
|
|
86
|
+
output: (stdout + stderr).trim(),
|
|
87
|
+
},
|
|
88
|
+
error: code !== 0 ? `Exit code: ${code}` : undefined,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
proc.on('error', (error) => {
|
|
92
|
+
clearTimeout(timeoutId);
|
|
93
|
+
resolve({
|
|
94
|
+
success: false,
|
|
95
|
+
error: error.message,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
validate(params) {
|
|
101
|
+
return typeof params.command === 'string' && params.command.length > 0;
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
exports.default = shellTool;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Web / Browser Tool
|
|
4
|
+
*
|
|
5
|
+
* Fetch web pages, search the web, and extract content.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.webSearchTool = exports.fetchUrlTool = void 0;
|
|
9
|
+
exports.registerWebTools = registerWebTools;
|
|
10
|
+
const index_1 = require("./index");
|
|
11
|
+
// Fetch URL Tool
|
|
12
|
+
const fetchUrlTool = {
|
|
13
|
+
definition: {
|
|
14
|
+
name: 'fetch_url',
|
|
15
|
+
description: 'Fetch content from a URL. Can extract text, HTML, or JSON.',
|
|
16
|
+
category: 'core',
|
|
17
|
+
parameters: [
|
|
18
|
+
{
|
|
19
|
+
name: 'url',
|
|
20
|
+
type: 'string',
|
|
21
|
+
description: 'URL to fetch',
|
|
22
|
+
required: true,
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'format',
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Response format: text, html, json',
|
|
28
|
+
required: false,
|
|
29
|
+
default: 'text',
|
|
30
|
+
enum: ['text', 'html', 'json'],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
name: 'timeout',
|
|
34
|
+
type: 'number',
|
|
35
|
+
description: 'Timeout in milliseconds',
|
|
36
|
+
required: false,
|
|
37
|
+
default: 10000,
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
returns: 'Fetched content',
|
|
41
|
+
examples: [
|
|
42
|
+
'fetch_url({ url: "https://example.com" })',
|
|
43
|
+
'fetch_url({ url: "https://api.example.com/data", format: "json" })',
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
async execute(params, context) {
|
|
47
|
+
const { url, format = 'text', timeout = 10000 } = params;
|
|
48
|
+
if (!url) {
|
|
49
|
+
return { success: false, error: 'URL is required' };
|
|
50
|
+
}
|
|
51
|
+
try {
|
|
52
|
+
const controller = new AbortController();
|
|
53
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
54
|
+
const response = await fetch(url, {
|
|
55
|
+
signal: controller.signal,
|
|
56
|
+
headers: {
|
|
57
|
+
'User-Agent': 'ApexBot/1.0',
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
clearTimeout(timeoutId);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
return {
|
|
63
|
+
success: false,
|
|
64
|
+
error: `HTTP ${response.status}: ${response.statusText}`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
let content;
|
|
68
|
+
const contentType = response.headers.get('content-type') || '';
|
|
69
|
+
if (format === 'json' || contentType.includes('application/json')) {
|
|
70
|
+
content = await response.json();
|
|
71
|
+
}
|
|
72
|
+
else if (format === 'html' || contentType.includes('text/html')) {
|
|
73
|
+
content = await response.text();
|
|
74
|
+
// Simple HTML to text conversion (strip tags)
|
|
75
|
+
if (format === 'text') {
|
|
76
|
+
content = content
|
|
77
|
+
.replace(/<script[^>]*>[\s\S]*?<\/script>/gi, '')
|
|
78
|
+
.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, '')
|
|
79
|
+
.replace(/<[^>]+>/g, ' ')
|
|
80
|
+
.replace(/\s+/g, ' ')
|
|
81
|
+
.trim();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
content = await response.text();
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
success: true,
|
|
89
|
+
data: {
|
|
90
|
+
url,
|
|
91
|
+
status: response.status,
|
|
92
|
+
contentType,
|
|
93
|
+
content,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
if (error.name === 'AbortError') {
|
|
99
|
+
return { success: false, error: 'Request timed out' };
|
|
100
|
+
}
|
|
101
|
+
return { success: false, error: error.message };
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
exports.fetchUrlTool = fetchUrlTool;
|
|
106
|
+
// Web Search Tool (using DuckDuckGo instant answers)
|
|
107
|
+
const webSearchTool = {
|
|
108
|
+
definition: {
|
|
109
|
+
name: 'web_search',
|
|
110
|
+
description: 'Search the web using DuckDuckGo and return results.',
|
|
111
|
+
category: 'core',
|
|
112
|
+
parameters: [
|
|
113
|
+
{
|
|
114
|
+
name: 'query',
|
|
115
|
+
type: 'string',
|
|
116
|
+
description: 'Search query',
|
|
117
|
+
required: true,
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'max_results',
|
|
121
|
+
type: 'number',
|
|
122
|
+
description: 'Maximum number of results',
|
|
123
|
+
required: false,
|
|
124
|
+
default: 5,
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
returns: 'Search results with titles and snippets',
|
|
128
|
+
examples: [
|
|
129
|
+
'web_search({ query: "weather in Tokyo" })',
|
|
130
|
+
'web_search({ query: "nodejs tutorial", max_results: 10 })',
|
|
131
|
+
],
|
|
132
|
+
},
|
|
133
|
+
async execute(params, context) {
|
|
134
|
+
const { query, max_results = 5 } = params;
|
|
135
|
+
if (!query) {
|
|
136
|
+
return { success: false, error: 'Query is required' };
|
|
137
|
+
}
|
|
138
|
+
try {
|
|
139
|
+
// Use DuckDuckGo instant answer API
|
|
140
|
+
const url = `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}&format=json&no_html=1`;
|
|
141
|
+
const response = await fetch(url, {
|
|
142
|
+
headers: { 'User-Agent': 'ApexBot/1.0' },
|
|
143
|
+
});
|
|
144
|
+
if (!response.ok) {
|
|
145
|
+
return { success: false, error: `Search failed: ${response.statusText}` };
|
|
146
|
+
}
|
|
147
|
+
const data = await response.json();
|
|
148
|
+
const results = [];
|
|
149
|
+
// Abstract (main answer)
|
|
150
|
+
if (data.Abstract) {
|
|
151
|
+
results.push({
|
|
152
|
+
title: data.Heading || 'Result',
|
|
153
|
+
snippet: data.Abstract,
|
|
154
|
+
url: data.AbstractURL || '',
|
|
155
|
+
source: data.AbstractSource || '',
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
// Related topics
|
|
159
|
+
if (data.RelatedTopics) {
|
|
160
|
+
for (const topic of data.RelatedTopics.slice(0, max_results - results.length)) {
|
|
161
|
+
if (topic.Text) {
|
|
162
|
+
results.push({
|
|
163
|
+
title: topic.Text.split(' - ')[0] || 'Related',
|
|
164
|
+
snippet: topic.Text,
|
|
165
|
+
url: topic.FirstURL || '',
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// Instant answer
|
|
171
|
+
if (data.Answer && results.length === 0) {
|
|
172
|
+
results.push({
|
|
173
|
+
title: 'Answer',
|
|
174
|
+
snippet: data.Answer,
|
|
175
|
+
url: '',
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
success: true,
|
|
180
|
+
data: {
|
|
181
|
+
query,
|
|
182
|
+
results: results.slice(0, max_results),
|
|
183
|
+
total: results.length,
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
return { success: false, error: error.message };
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
exports.webSearchTool = webSearchTool;
|
|
193
|
+
// Register web tools
|
|
194
|
+
function registerWebTools() {
|
|
195
|
+
index_1.toolRegistry.register(fetchUrlTool);
|
|
196
|
+
index_1.toolRegistry.register(webSearchTool);
|
|
197
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apexbot",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "ApexBot - Your free, private AI assistant. 100% free with Ollama (local AI). Multi-channel: Telegram, Discord, WebChat.
|
|
3
|
+
"version": "1.0.5",
|
|
4
|
+
"description": "ApexBot - Your free, private AI assistant. 100% free with Ollama (local AI). Multi-channel: Telegram, Discord, WebChat. Tools & Skills system with Spotify, Obsidian, and more!",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"apexbot": "./dist/cli/index.js"
|