opc-agent 1.3.2 → 2.0.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/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +14 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- package/.github/workflows/ci.yml +24 -0
- package/CHANGELOG.md +48 -63
- package/CONTRIBUTING.md +21 -60
- package/README.md +284 -348
- package/README.zh-CN.md +415 -415
- package/dist/channels/slack.js +93 -10
- package/dist/channels/telegram.d.ts +30 -9
- package/dist/channels/telegram.js +125 -33
- package/dist/channels/web.d.ts +10 -0
- package/dist/channels/web.js +33 -2
- package/dist/cli.js +667 -65
- package/dist/core/agent.d.ts +23 -0
- package/dist/core/agent.js +120 -3
- package/dist/core/runtime.d.ts +5 -0
- package/dist/core/runtime.js +71 -0
- package/dist/core/scheduler.d.ts +52 -0
- package/dist/core/scheduler.js +168 -0
- package/dist/core/subagent.d.ts +28 -0
- package/dist/core/subagent.js +65 -0
- package/dist/daemon.d.ts +3 -0
- package/dist/daemon.js +134 -0
- package/dist/deploy/hermes.js +22 -22
- package/dist/deploy/openclaw.js +31 -40
- package/dist/index.d.ts +10 -10
- package/dist/index.js +22 -15
- package/dist/providers/index.d.ts +6 -2
- package/dist/providers/index.js +22 -9
- package/dist/schema/oad.d.ts +180 -6
- package/dist/schema/oad.js +12 -1
- package/dist/skills/auto-learn.d.ts +28 -0
- package/dist/skills/auto-learn.js +257 -0
- package/dist/templates/code-reviewer.d.ts +0 -8
- package/dist/templates/code-reviewer.js +5 -9
- package/dist/templates/customer-service.d.ts +0 -8
- package/dist/templates/customer-service.js +2 -6
- package/dist/templates/data-analyst.d.ts +0 -8
- package/dist/templates/data-analyst.js +5 -9
- package/dist/templates/knowledge-base.d.ts +0 -8
- package/dist/templates/knowledge-base.js +2 -6
- package/dist/templates/sales-assistant.d.ts +0 -8
- package/dist/templates/sales-assistant.js +4 -8
- package/dist/templates/teacher.d.ts +0 -8
- package/dist/templates/teacher.js +6 -10
- package/dist/tools/builtin/datetime.d.ts +3 -0
- package/dist/tools/builtin/datetime.js +44 -0
- package/dist/tools/builtin/file.d.ts +3 -0
- package/dist/tools/builtin/file.js +151 -0
- package/dist/tools/builtin/index.d.ts +15 -0
- package/dist/tools/builtin/index.js +30 -0
- package/dist/tools/builtin/shell.d.ts +3 -0
- package/dist/tools/builtin/shell.js +43 -0
- package/dist/tools/builtin/web.d.ts +3 -0
- package/dist/tools/builtin/web.js +37 -0
- package/dist/tools/mcp-client.d.ts +24 -0
- package/dist/tools/mcp-client.js +119 -0
- package/dist/traces/index.d.ts +49 -0
- package/dist/traces/index.js +102 -0
- package/docs/.vitepress/config.ts +103 -103
- package/docs/api/cli.md +48 -48
- package/docs/api/oad-schema.md +64 -64
- package/docs/api/sdk.md +80 -80
- package/docs/guide/concepts.md +51 -51
- package/docs/guide/configuration.md +79 -79
- package/docs/guide/deployment.md +42 -42
- package/docs/guide/getting-started.md +44 -44
- package/docs/guide/templates.md +28 -28
- package/docs/guide/testing.md +84 -84
- package/docs/index.md +27 -27
- package/docs/zh/api/cli.md +54 -54
- package/docs/zh/api/oad-schema.md +87 -87
- package/docs/zh/api/sdk.md +102 -102
- package/docs/zh/guide/concepts.md +104 -104
- package/docs/zh/guide/configuration.md +135 -135
- package/docs/zh/guide/deployment.md +81 -81
- package/docs/zh/guide/getting-started.md +82 -82
- package/docs/zh/guide/templates.md +84 -84
- package/docs/zh/guide/testing.md +88 -88
- package/docs/zh/index.md +27 -27
- package/examples/README.md +22 -0
- package/examples/basic-agent.ts +90 -0
- package/examples/brain-integration.ts +71 -0
- package/examples/customer-service-demo/README.md +90 -90
- package/examples/customer-service-demo/oad.yaml +107 -107
- package/examples/multi-channel.ts +74 -0
- package/package.json +1 -1
- package/src/analytics/index.ts +66 -66
- package/src/channels/discord.ts +192 -192
- package/src/channels/email.ts +177 -177
- package/src/channels/feishu.ts +236 -236
- package/src/channels/index.ts +15 -15
- package/src/channels/slack.ts +217 -160
- package/src/channels/telegram.ts +155 -33
- package/src/channels/voice.ts +106 -106
- package/src/channels/web.ts +38 -2
- package/src/channels/webhook.ts +199 -199
- package/src/channels/websocket.ts +87 -87
- package/src/channels/wechat.ts +149 -149
- package/src/cli.ts +697 -63
- package/src/core/a2a.ts +143 -143
- package/src/core/agent.ts +146 -3
- package/src/core/analytics-engine.ts +186 -186
- package/src/core/auth.ts +57 -57
- package/src/core/cache.ts +141 -141
- package/src/core/compose.ts +77 -77
- package/src/core/config.ts +14 -14
- package/src/core/errors.ts +148 -148
- package/src/core/hitl.ts +138 -138
- package/src/core/logger.ts +57 -57
- package/src/core/orchestrator.ts +215 -215
- package/src/core/performance.ts +187 -187
- package/src/core/rate-limiter.ts +128 -128
- package/src/core/room.ts +109 -109
- package/src/core/runtime.ts +230 -152
- package/src/core/sandbox.ts +101 -101
- package/src/core/scheduler.ts +187 -0
- package/src/core/security.ts +171 -171
- package/src/core/subagent.ts +98 -0
- package/src/core/types.ts +68 -68
- package/src/core/versioning.ts +106 -106
- package/src/core/watch.ts +178 -178
- package/src/core/workflow.ts +235 -235
- package/src/daemon.ts +96 -0
- package/src/deploy/hermes.ts +156 -156
- package/src/deploy/openclaw.ts +190 -200
- package/src/i18n/index.ts +216 -216
- package/src/index.ts +14 -10
- package/src/memory/deepbrain.ts +108 -108
- package/src/memory/index.ts +34 -34
- package/src/plugins/index.ts +208 -208
- package/src/providers/index.ts +354 -331
- package/src/schema/oad.ts +14 -2
- package/src/skills/auto-learn.ts +262 -0
- package/src/skills/base.ts +16 -16
- package/src/skills/document.ts +100 -100
- package/src/skills/http.ts +35 -35
- package/src/skills/index.ts +27 -27
- package/src/skills/scheduler.ts +80 -80
- package/src/skills/webhook-trigger.ts +59 -59
- package/src/templates/code-reviewer.ts +30 -34
- package/src/templates/customer-service.ts +76 -80
- package/src/templates/data-analyst.ts +66 -70
- package/src/templates/executive-assistant.ts +71 -71
- package/src/templates/financial-advisor.ts +60 -60
- package/src/templates/knowledge-base.ts +27 -31
- package/src/templates/legal-assistant.ts +71 -71
- package/src/templates/sales-assistant.ts +75 -79
- package/src/templates/teacher.ts +75 -79
- package/src/testing/index.ts +181 -181
- package/src/tools/builtin/datetime.ts +41 -0
- package/src/tools/builtin/file.ts +107 -0
- package/src/tools/builtin/index.ts +28 -0
- package/src/tools/builtin/shell.ts +43 -0
- package/src/tools/builtin/web.ts +35 -0
- package/src/tools/calculator.ts +73 -73
- package/src/tools/datetime.ts +149 -149
- package/src/tools/json-transform.ts +187 -187
- package/src/tools/mcp-client.ts +131 -0
- package/src/tools/mcp.ts +76 -76
- package/src/tools/text-analysis.ts +116 -116
- package/src/traces/index.ts +132 -0
- package/templates/Dockerfile +15 -15
- package/templates/code-reviewer/README.md +27 -27
- package/templates/code-reviewer/oad.yaml +41 -41
- package/templates/customer-service/README.md +22 -22
- package/templates/customer-service/oad.yaml +36 -36
- package/templates/docker-compose.yml +21 -21
- package/templates/ecommerce-assistant/README.md +45 -45
- package/templates/ecommerce-assistant/oad.yaml +47 -47
- package/templates/knowledge-base/README.md +28 -28
- package/templates/knowledge-base/oad.yaml +38 -38
- package/templates/sales-assistant/README.md +26 -26
- package/templates/sales-assistant/oad.yaml +43 -43
- package/templates/tech-support/README.md +43 -43
- package/templates/tech-support/oad.yaml +45 -45
- package/test-agent/Dockerfile +9 -0
- package/test-agent/README.md +50 -0
- package/test-agent/agent.yaml +23 -0
- package/test-agent/docker-compose.yml +11 -0
- package/test-agent/oad.yaml +31 -0
- package/test-agent/package-lock.json +1492 -0
- package/test-agent/package.json +18 -0
- package/test-agent/src/index.ts +24 -0
- package/test-agent/src/skills/echo.ts +15 -0
- package/test-agent/tsconfig.json +25 -0
- package/tests/a2a.test.ts +66 -66
- package/tests/agent.test.ts +72 -72
- package/tests/analytics.test.ts +50 -50
- package/tests/auto-learn.test.ts +105 -0
- package/tests/builtin-tools.test.ts +83 -0
- package/tests/channel.test.ts +39 -39
- package/tests/cli.test.ts +46 -0
- package/tests/e2e.test.ts +134 -134
- package/tests/errors.test.ts +83 -83
- package/tests/hitl.test.ts +71 -71
- package/tests/i18n.test.ts +41 -41
- package/tests/mcp.test.ts +54 -54
- package/tests/oad.test.ts +68 -68
- package/tests/performance.test.ts +115 -115
- package/tests/plugin.test.ts +74 -74
- package/tests/room.test.ts +106 -106
- package/tests/runtime.test.ts +42 -42
- package/tests/sandbox.test.ts +46 -46
- package/tests/security.test.ts +60 -60
- package/tests/subagent.test.ts +130 -0
- package/tests/telegram-discord.test.ts +60 -0
- package/tests/templates.test.ts +77 -77
- package/tests/v070.test.ts +76 -76
- package/tests/versioning.test.ts +75 -75
- package/tests/voice.test.ts +61 -61
- package/tests/webhook.test.ts +29 -29
- package/tests/workflow.test.ts +143 -143
- package/tsconfig.json +19 -19
- package/vitest.config.ts +9 -9
- package/dist/core/dashboard.d.ts +0 -35
- package/dist/core/dashboard.js +0 -157
- package/dist/core/priority.d.ts +0 -52
- package/dist/core/priority.js +0 -102
- package/src/core/dashboard.ts +0 -219
- package/src/core/priority.ts +0 -140
- package/src/dtv/data.ts +0 -29
- package/src/dtv/trust.ts +0 -43
- package/src/dtv/value.ts +0 -47
- package/src/marketplace/index.ts +0 -223
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { MCPTool } from '../mcp';
|
|
2
|
+
import { fileTool } from './file';
|
|
3
|
+
import { webTool } from './web';
|
|
4
|
+
import { shellTool } from './shell';
|
|
5
|
+
import { datetimeTool } from './datetime';
|
|
6
|
+
|
|
7
|
+
export { fileTool, webTool, shellTool, datetimeTool };
|
|
8
|
+
|
|
9
|
+
const ALL_BUILTIN_TOOLS: MCPTool[] = [fileTool, webTool, shellTool, datetimeTool];
|
|
10
|
+
|
|
11
|
+
const BUILTIN_MAP = new Map<string, MCPTool>(
|
|
12
|
+
ALL_BUILTIN_TOOLS.map(t => [t.name, t])
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get all built-in tools.
|
|
17
|
+
*/
|
|
18
|
+
export function getBuiltinTools(): MCPTool[] {
|
|
19
|
+
return [...ALL_BUILTIN_TOOLS];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Get specific built-in tools by name. If no names given, returns all.
|
|
24
|
+
*/
|
|
25
|
+
export function getBuiltinToolsByName(names?: string[]): MCPTool[] {
|
|
26
|
+
if (!names || names.length === 0) return getBuiltinTools();
|
|
27
|
+
return names.map(n => BUILTIN_MAP.get(n)).filter((t): t is MCPTool => !!t);
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { execSync } from 'child_process';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
4
|
+
|
|
5
|
+
export const shellTool: MCPTool = {
|
|
6
|
+
name: 'shell_exec',
|
|
7
|
+
description: 'Execute a shell command (sandboxed to workspace)',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
command: { type: 'string' },
|
|
12
|
+
timeout: { type: 'number', default: 30000 },
|
|
13
|
+
},
|
|
14
|
+
required: ['command'],
|
|
15
|
+
},
|
|
16
|
+
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
17
|
+
const command = input.command as string;
|
|
18
|
+
const timeout = (input.timeout as number) || 30000;
|
|
19
|
+
const workspace = process.cwd();
|
|
20
|
+
|
|
21
|
+
// Block path traversal attempts
|
|
22
|
+
if (command.includes('..')) {
|
|
23
|
+
return { content: 'Commands with ".." are not allowed for security', isError: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const output = execSync(command, {
|
|
28
|
+
cwd: workspace,
|
|
29
|
+
timeout,
|
|
30
|
+
encoding: 'utf-8',
|
|
31
|
+
maxBuffer: 1024 * 1024,
|
|
32
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
33
|
+
});
|
|
34
|
+
const result = (output || '').slice(0, 5000);
|
|
35
|
+
return { content: result || '(no output)', isError: false };
|
|
36
|
+
} catch (err: any) {
|
|
37
|
+
const stderr = err.stderr ? String(err.stderr).slice(0, 2500) : '';
|
|
38
|
+
const stdout = err.stdout ? String(err.stdout).slice(0, 2500) : '';
|
|
39
|
+
const output = [stdout, stderr].filter(Boolean).join('\n') || err.message;
|
|
40
|
+
return { content: `Command failed: ${output.slice(0, 5000)}`, isError: true };
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { MCPTool, MCPToolResult } from '../mcp';
|
|
2
|
+
|
|
3
|
+
export const webTool: MCPTool = {
|
|
4
|
+
name: 'web_fetch',
|
|
5
|
+
description: 'Fetch content from a URL',
|
|
6
|
+
inputSchema: {
|
|
7
|
+
type: 'object',
|
|
8
|
+
properties: {
|
|
9
|
+
url: { type: 'string' },
|
|
10
|
+
method: { type: 'string', enum: ['GET', 'POST'], default: 'GET' },
|
|
11
|
+
maxLength: { type: 'number', default: 5000 },
|
|
12
|
+
},
|
|
13
|
+
required: ['url'],
|
|
14
|
+
},
|
|
15
|
+
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
16
|
+
const url = input.url as string;
|
|
17
|
+
const method = (input.method as string) || 'GET';
|
|
18
|
+
const maxLength = (input.maxLength as number) || 5000;
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(url, { method, signal: AbortSignal.timeout(15000) });
|
|
22
|
+
const text = await response.text();
|
|
23
|
+
const truncated = text.length > maxLength ? text.slice(0, maxLength) + '\n...[truncated]' : text;
|
|
24
|
+
return {
|
|
25
|
+
content: `Status: ${response.status}\n\n${truncated}`,
|
|
26
|
+
isError: false,
|
|
27
|
+
};
|
|
28
|
+
} catch (err) {
|
|
29
|
+
return {
|
|
30
|
+
content: `Fetch error: ${err instanceof Error ? err.message : String(err)}`,
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
};
|
package/src/tools/calculator.ts
CHANGED
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
import type { MCPTool, MCPToolResult } from './mcp';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Calculator Tool — v0.8.0
|
|
5
|
-
* Safe math expression evaluation as an LLM function tool.
|
|
6
|
-
*/
|
|
7
|
-
export const CalculatorTool: MCPTool = {
|
|
8
|
-
name: 'calculator',
|
|
9
|
-
description: 'Evaluate a mathematical expression. Supports basic arithmetic, powers, sqrt, abs, min, max, round, ceil, floor, PI, E.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: 'object',
|
|
12
|
-
properties: {
|
|
13
|
-
expression: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
description: 'Mathematical expression to evaluate, e.g. "2 + 3 * 4" or "sqrt(144) + PI"',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
required: ['expression'],
|
|
19
|
-
},
|
|
20
|
-
|
|
21
|
-
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
22
|
-
const expr = String(input.expression ?? '');
|
|
23
|
-
try {
|
|
24
|
-
const result = safeEval(expr);
|
|
25
|
-
return { content: String(result) };
|
|
26
|
-
} catch (err) {
|
|
27
|
-
return { content: `Error: ${(err as Error).message}`, isError: true };
|
|
28
|
-
}
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
/** Safe math evaluator — no eval(), no arbitrary code */
|
|
33
|
-
function safeEval(expr: string): number {
|
|
34
|
-
// Whitelist: digits, operators, parens, dots, commas, spaces, and known functions
|
|
35
|
-
const sanitized = expr.replace(/\s+/g, '');
|
|
36
|
-
const allowed = /^[0-9+\-*/().,%^a-zA-Z_]+$/;
|
|
37
|
-
if (!allowed.test(sanitized)) {
|
|
38
|
-
throw new Error('Invalid characters in expression');
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// Replace known math functions/constants
|
|
42
|
-
const prepared = sanitized
|
|
43
|
-
.replace(/\bPI\b/gi, String(Math.PI))
|
|
44
|
-
.replace(/\bE\b/g, String(Math.E))
|
|
45
|
-
.replace(/\bsqrt\b/gi, 'Math.sqrt')
|
|
46
|
-
.replace(/\babs\b/gi, 'Math.abs')
|
|
47
|
-
.replace(/\bmin\b/gi, 'Math.min')
|
|
48
|
-
.replace(/\bmax\b/gi, 'Math.max')
|
|
49
|
-
.replace(/\bround\b/gi, 'Math.round')
|
|
50
|
-
.replace(/\bceil\b/gi, 'Math.ceil')
|
|
51
|
-
.replace(/\bfloor\b/gi, 'Math.floor')
|
|
52
|
-
.replace(/\bpow\b/gi, 'Math.pow')
|
|
53
|
-
.replace(/\blog\b/gi, 'Math.log')
|
|
54
|
-
.replace(/\blog10\b/gi, 'Math.log10')
|
|
55
|
-
.replace(/\bsin\b/gi, 'Math.sin')
|
|
56
|
-
.replace(/\bcos\b/gi, 'Math.cos')
|
|
57
|
-
.replace(/\btan\b/gi, 'Math.tan')
|
|
58
|
-
.replace(/\^/g, '**');
|
|
59
|
-
|
|
60
|
-
// Block anything that isn't math
|
|
61
|
-
if (/[a-zA-Z_]/.test(prepared.replace(/Math\.\w+/g, ''))) {
|
|
62
|
-
throw new Error('Unsupported function or variable in expression');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Use Function constructor with restricted scope
|
|
66
|
-
const fn = new Function('Math', `"use strict"; return (${prepared});`);
|
|
67
|
-
const result = fn(Math);
|
|
68
|
-
|
|
69
|
-
if (typeof result !== 'number' || !isFinite(result)) {
|
|
70
|
-
throw new Error('Expression did not evaluate to a finite number');
|
|
71
|
-
}
|
|
72
|
-
return result;
|
|
73
|
-
}
|
|
1
|
+
import type { MCPTool, MCPToolResult } from './mcp';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Calculator Tool — v0.8.0
|
|
5
|
+
* Safe math expression evaluation as an LLM function tool.
|
|
6
|
+
*/
|
|
7
|
+
export const CalculatorTool: MCPTool = {
|
|
8
|
+
name: 'calculator',
|
|
9
|
+
description: 'Evaluate a mathematical expression. Supports basic arithmetic, powers, sqrt, abs, min, max, round, ceil, floor, PI, E.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
expression: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Mathematical expression to evaluate, e.g. "2 + 3 * 4" or "sqrt(144) + PI"',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
required: ['expression'],
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
22
|
+
const expr = String(input.expression ?? '');
|
|
23
|
+
try {
|
|
24
|
+
const result = safeEval(expr);
|
|
25
|
+
return { content: String(result) };
|
|
26
|
+
} catch (err) {
|
|
27
|
+
return { content: `Error: ${(err as Error).message}`, isError: true };
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Safe math evaluator — no eval(), no arbitrary code */
|
|
33
|
+
function safeEval(expr: string): number {
|
|
34
|
+
// Whitelist: digits, operators, parens, dots, commas, spaces, and known functions
|
|
35
|
+
const sanitized = expr.replace(/\s+/g, '');
|
|
36
|
+
const allowed = /^[0-9+\-*/().,%^a-zA-Z_]+$/;
|
|
37
|
+
if (!allowed.test(sanitized)) {
|
|
38
|
+
throw new Error('Invalid characters in expression');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Replace known math functions/constants
|
|
42
|
+
const prepared = sanitized
|
|
43
|
+
.replace(/\bPI\b/gi, String(Math.PI))
|
|
44
|
+
.replace(/\bE\b/g, String(Math.E))
|
|
45
|
+
.replace(/\bsqrt\b/gi, 'Math.sqrt')
|
|
46
|
+
.replace(/\babs\b/gi, 'Math.abs')
|
|
47
|
+
.replace(/\bmin\b/gi, 'Math.min')
|
|
48
|
+
.replace(/\bmax\b/gi, 'Math.max')
|
|
49
|
+
.replace(/\bround\b/gi, 'Math.round')
|
|
50
|
+
.replace(/\bceil\b/gi, 'Math.ceil')
|
|
51
|
+
.replace(/\bfloor\b/gi, 'Math.floor')
|
|
52
|
+
.replace(/\bpow\b/gi, 'Math.pow')
|
|
53
|
+
.replace(/\blog\b/gi, 'Math.log')
|
|
54
|
+
.replace(/\blog10\b/gi, 'Math.log10')
|
|
55
|
+
.replace(/\bsin\b/gi, 'Math.sin')
|
|
56
|
+
.replace(/\bcos\b/gi, 'Math.cos')
|
|
57
|
+
.replace(/\btan\b/gi, 'Math.tan')
|
|
58
|
+
.replace(/\^/g, '**');
|
|
59
|
+
|
|
60
|
+
// Block anything that isn't math
|
|
61
|
+
if (/[a-zA-Z_]/.test(prepared.replace(/Math\.\w+/g, ''))) {
|
|
62
|
+
throw new Error('Unsupported function or variable in expression');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Use Function constructor with restricted scope
|
|
66
|
+
const fn = new Function('Math', `"use strict"; return (${prepared});`);
|
|
67
|
+
const result = fn(Math);
|
|
68
|
+
|
|
69
|
+
if (typeof result !== 'number' || !isFinite(result)) {
|
|
70
|
+
throw new Error('Expression did not evaluate to a finite number');
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
package/src/tools/datetime.ts
CHANGED
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
import type { MCPTool, MCPToolResult } from './mcp';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* DateTime Tool — v0.8.0
|
|
5
|
-
* Date/time operations as an LLM function tool.
|
|
6
|
-
*/
|
|
7
|
-
export const DateTimeTool: MCPTool = {
|
|
8
|
-
name: 'datetime',
|
|
9
|
-
description: 'Get current date/time, format dates, calculate date differences, or add/subtract time.',
|
|
10
|
-
inputSchema: {
|
|
11
|
-
type: 'object',
|
|
12
|
-
properties: {
|
|
13
|
-
operation: {
|
|
14
|
-
type: 'string',
|
|
15
|
-
enum: ['now', 'format', 'diff', 'add', 'parse'],
|
|
16
|
-
description: 'Operation to perform',
|
|
17
|
-
},
|
|
18
|
-
date: {
|
|
19
|
-
type: 'string',
|
|
20
|
-
description: 'Date string (ISO 8601 or common format)',
|
|
21
|
-
},
|
|
22
|
-
date2: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
description: 'Second date for diff operation',
|
|
25
|
-
},
|
|
26
|
-
format: {
|
|
27
|
-
type: 'string',
|
|
28
|
-
description: 'Output format: iso, date, time, datetime, unix, relative',
|
|
29
|
-
},
|
|
30
|
-
amount: {
|
|
31
|
-
type: 'number',
|
|
32
|
-
description: 'Amount to add (can be negative)',
|
|
33
|
-
},
|
|
34
|
-
unit: {
|
|
35
|
-
type: 'string',
|
|
36
|
-
enum: ['milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'],
|
|
37
|
-
description: 'Unit for add operation',
|
|
38
|
-
},
|
|
39
|
-
timezone: {
|
|
40
|
-
type: 'string',
|
|
41
|
-
description: 'IANA timezone (e.g. Asia/Shanghai, America/New_York)',
|
|
42
|
-
},
|
|
43
|
-
},
|
|
44
|
-
required: ['operation'],
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
48
|
-
try {
|
|
49
|
-
const op = String(input.operation);
|
|
50
|
-
const tz = input.timezone as string | undefined;
|
|
51
|
-
|
|
52
|
-
switch (op) {
|
|
53
|
-
case 'now': {
|
|
54
|
-
const now = new Date();
|
|
55
|
-
const fmt = (input.format as string) ?? 'iso';
|
|
56
|
-
return { content: formatDate(now, fmt, tz) };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
case 'format': {
|
|
60
|
-
const d = parseDate(input.date as string);
|
|
61
|
-
const fmt = (input.format as string) ?? 'iso';
|
|
62
|
-
return { content: formatDate(d, fmt, tz) };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
case 'diff': {
|
|
66
|
-
const d1 = parseDate(input.date as string);
|
|
67
|
-
const d2 = input.date2 ? parseDate(input.date2 as string) : new Date();
|
|
68
|
-
const diffMs = d2.getTime() - d1.getTime();
|
|
69
|
-
const days = Math.floor(Math.abs(diffMs) / 86400000);
|
|
70
|
-
const hours = Math.floor((Math.abs(diffMs) % 86400000) / 3600000);
|
|
71
|
-
const mins = Math.floor((Math.abs(diffMs) % 3600000) / 60000);
|
|
72
|
-
const sign = diffMs < 0 ? '-' : '';
|
|
73
|
-
return {
|
|
74
|
-
content: JSON.stringify({
|
|
75
|
-
milliseconds: diffMs,
|
|
76
|
-
readable: `${sign}${days}d ${hours}h ${mins}m`,
|
|
77
|
-
days: diffMs / 86400000,
|
|
78
|
-
}),
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
case 'add': {
|
|
83
|
-
const d = input.date ? parseDate(input.date as string) : new Date();
|
|
84
|
-
const amount = Number(input.amount ?? 0);
|
|
85
|
-
const unit = String(input.unit ?? 'days');
|
|
86
|
-
const result = addTime(d, amount, unit);
|
|
87
|
-
const fmt = (input.format as string) ?? 'iso';
|
|
88
|
-
return { content: formatDate(result, fmt, tz) };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
case 'parse': {
|
|
92
|
-
const d = parseDate(input.date as string);
|
|
93
|
-
return {
|
|
94
|
-
content: JSON.stringify({
|
|
95
|
-
iso: d.toISOString(),
|
|
96
|
-
unix: d.getTime(),
|
|
97
|
-
year: d.getFullYear(),
|
|
98
|
-
month: d.getMonth() + 1,
|
|
99
|
-
day: d.getDate(),
|
|
100
|
-
weekday: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()],
|
|
101
|
-
}),
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
default:
|
|
106
|
-
return { content: `Unknown operation: ${op}`, isError: true };
|
|
107
|
-
}
|
|
108
|
-
} catch (err) {
|
|
109
|
-
return { content: `Error: ${(err as Error).message}`, isError: true };
|
|
110
|
-
}
|
|
111
|
-
},
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
function parseDate(s: string): Date {
|
|
115
|
-
const d = new Date(s);
|
|
116
|
-
if (isNaN(d.getTime())) throw new Error(`Invalid date: ${s}`);
|
|
117
|
-
return d;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function formatDate(d: Date, fmt: string, tz?: string): string {
|
|
121
|
-
if (tz) {
|
|
122
|
-
const localeStr = d.toLocaleString('en-US', { timeZone: tz });
|
|
123
|
-
d = new Date(localeStr);
|
|
124
|
-
}
|
|
125
|
-
switch (fmt) {
|
|
126
|
-
case 'iso': return d.toISOString();
|
|
127
|
-
case 'date': return d.toISOString().split('T')[0];
|
|
128
|
-
case 'time': return d.toTimeString().split(' ')[0];
|
|
129
|
-
case 'datetime': return d.toISOString().replace('T', ' ').replace(/\.\d+Z$/, '');
|
|
130
|
-
case 'unix': return String(d.getTime());
|
|
131
|
-
default: return d.toISOString();
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function addTime(d: Date, amount: number, unit: string): Date {
|
|
136
|
-
const result = new Date(d);
|
|
137
|
-
switch (unit) {
|
|
138
|
-
case 'milliseconds': result.setTime(result.getTime() + amount); break;
|
|
139
|
-
case 'seconds': result.setTime(result.getTime() + amount * 1000); break;
|
|
140
|
-
case 'minutes': result.setTime(result.getTime() + amount * 60000); break;
|
|
141
|
-
case 'hours': result.setTime(result.getTime() + amount * 3600000); break;
|
|
142
|
-
case 'days': result.setDate(result.getDate() + amount); break;
|
|
143
|
-
case 'weeks': result.setDate(result.getDate() + amount * 7); break;
|
|
144
|
-
case 'months': result.setMonth(result.getMonth() + amount); break;
|
|
145
|
-
case 'years': result.setFullYear(result.getFullYear() + amount); break;
|
|
146
|
-
default: throw new Error(`Unknown unit: ${unit}`);
|
|
147
|
-
}
|
|
148
|
-
return result;
|
|
149
|
-
}
|
|
1
|
+
import type { MCPTool, MCPToolResult } from './mcp';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* DateTime Tool — v0.8.0
|
|
5
|
+
* Date/time operations as an LLM function tool.
|
|
6
|
+
*/
|
|
7
|
+
export const DateTimeTool: MCPTool = {
|
|
8
|
+
name: 'datetime',
|
|
9
|
+
description: 'Get current date/time, format dates, calculate date differences, or add/subtract time.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
operation: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
enum: ['now', 'format', 'diff', 'add', 'parse'],
|
|
16
|
+
description: 'Operation to perform',
|
|
17
|
+
},
|
|
18
|
+
date: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Date string (ISO 8601 or common format)',
|
|
21
|
+
},
|
|
22
|
+
date2: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
description: 'Second date for diff operation',
|
|
25
|
+
},
|
|
26
|
+
format: {
|
|
27
|
+
type: 'string',
|
|
28
|
+
description: 'Output format: iso, date, time, datetime, unix, relative',
|
|
29
|
+
},
|
|
30
|
+
amount: {
|
|
31
|
+
type: 'number',
|
|
32
|
+
description: 'Amount to add (can be negative)',
|
|
33
|
+
},
|
|
34
|
+
unit: {
|
|
35
|
+
type: 'string',
|
|
36
|
+
enum: ['milliseconds', 'seconds', 'minutes', 'hours', 'days', 'weeks', 'months', 'years'],
|
|
37
|
+
description: 'Unit for add operation',
|
|
38
|
+
},
|
|
39
|
+
timezone: {
|
|
40
|
+
type: 'string',
|
|
41
|
+
description: 'IANA timezone (e.g. Asia/Shanghai, America/New_York)',
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
required: ['operation'],
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
async execute(input: Record<string, unknown>): Promise<MCPToolResult> {
|
|
48
|
+
try {
|
|
49
|
+
const op = String(input.operation);
|
|
50
|
+
const tz = input.timezone as string | undefined;
|
|
51
|
+
|
|
52
|
+
switch (op) {
|
|
53
|
+
case 'now': {
|
|
54
|
+
const now = new Date();
|
|
55
|
+
const fmt = (input.format as string) ?? 'iso';
|
|
56
|
+
return { content: formatDate(now, fmt, tz) };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'format': {
|
|
60
|
+
const d = parseDate(input.date as string);
|
|
61
|
+
const fmt = (input.format as string) ?? 'iso';
|
|
62
|
+
return { content: formatDate(d, fmt, tz) };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
case 'diff': {
|
|
66
|
+
const d1 = parseDate(input.date as string);
|
|
67
|
+
const d2 = input.date2 ? parseDate(input.date2 as string) : new Date();
|
|
68
|
+
const diffMs = d2.getTime() - d1.getTime();
|
|
69
|
+
const days = Math.floor(Math.abs(diffMs) / 86400000);
|
|
70
|
+
const hours = Math.floor((Math.abs(diffMs) % 86400000) / 3600000);
|
|
71
|
+
const mins = Math.floor((Math.abs(diffMs) % 3600000) / 60000);
|
|
72
|
+
const sign = diffMs < 0 ? '-' : '';
|
|
73
|
+
return {
|
|
74
|
+
content: JSON.stringify({
|
|
75
|
+
milliseconds: diffMs,
|
|
76
|
+
readable: `${sign}${days}d ${hours}h ${mins}m`,
|
|
77
|
+
days: diffMs / 86400000,
|
|
78
|
+
}),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
case 'add': {
|
|
83
|
+
const d = input.date ? parseDate(input.date as string) : new Date();
|
|
84
|
+
const amount = Number(input.amount ?? 0);
|
|
85
|
+
const unit = String(input.unit ?? 'days');
|
|
86
|
+
const result = addTime(d, amount, unit);
|
|
87
|
+
const fmt = (input.format as string) ?? 'iso';
|
|
88
|
+
return { content: formatDate(result, fmt, tz) };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
case 'parse': {
|
|
92
|
+
const d = parseDate(input.date as string);
|
|
93
|
+
return {
|
|
94
|
+
content: JSON.stringify({
|
|
95
|
+
iso: d.toISOString(),
|
|
96
|
+
unix: d.getTime(),
|
|
97
|
+
year: d.getFullYear(),
|
|
98
|
+
month: d.getMonth() + 1,
|
|
99
|
+
day: d.getDate(),
|
|
100
|
+
weekday: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'][d.getDay()],
|
|
101
|
+
}),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
default:
|
|
106
|
+
return { content: `Unknown operation: ${op}`, isError: true };
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
return { content: `Error: ${(err as Error).message}`, isError: true };
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function parseDate(s: string): Date {
|
|
115
|
+
const d = new Date(s);
|
|
116
|
+
if (isNaN(d.getTime())) throw new Error(`Invalid date: ${s}`);
|
|
117
|
+
return d;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function formatDate(d: Date, fmt: string, tz?: string): string {
|
|
121
|
+
if (tz) {
|
|
122
|
+
const localeStr = d.toLocaleString('en-US', { timeZone: tz });
|
|
123
|
+
d = new Date(localeStr);
|
|
124
|
+
}
|
|
125
|
+
switch (fmt) {
|
|
126
|
+
case 'iso': return d.toISOString();
|
|
127
|
+
case 'date': return d.toISOString().split('T')[0];
|
|
128
|
+
case 'time': return d.toTimeString().split(' ')[0];
|
|
129
|
+
case 'datetime': return d.toISOString().replace('T', ' ').replace(/\.\d+Z$/, '');
|
|
130
|
+
case 'unix': return String(d.getTime());
|
|
131
|
+
default: return d.toISOString();
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function addTime(d: Date, amount: number, unit: string): Date {
|
|
136
|
+
const result = new Date(d);
|
|
137
|
+
switch (unit) {
|
|
138
|
+
case 'milliseconds': result.setTime(result.getTime() + amount); break;
|
|
139
|
+
case 'seconds': result.setTime(result.getTime() + amount * 1000); break;
|
|
140
|
+
case 'minutes': result.setTime(result.getTime() + amount * 60000); break;
|
|
141
|
+
case 'hours': result.setTime(result.getTime() + amount * 3600000); break;
|
|
142
|
+
case 'days': result.setDate(result.getDate() + amount); break;
|
|
143
|
+
case 'weeks': result.setDate(result.getDate() + amount * 7); break;
|
|
144
|
+
case 'months': result.setMonth(result.getMonth() + amount); break;
|
|
145
|
+
case 'years': result.setFullYear(result.getFullYear() + amount); break;
|
|
146
|
+
default: throw new Error(`Unknown unit: ${unit}`);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
}
|