apexbot 1.0.2 → 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.
@@ -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.2",
4
- "description": "ApexBot - Your free, private AI assistant. 100% free with Ollama (local AI). Multi-channel: Telegram, Discord, WebChat. Like Clawdbot but open-source!",
3
+ "version": "1.0.4",
4
+ "description": "ApexBot - Your free, private AI assistant. 100% free with Ollama (local AI). Multi-channel: Telegram, Discord, WebChat. Tools & Skills system like Clawdbot!",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
7
  "apexbot": "./dist/cli/index.js"