@zhin.js/plugin-code-runner 3.0.1 → 3.0.2

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,55 @@
1
+ ---
2
+ name: code-runner
3
+ description: >-
4
+ 在线代码执行能力。当用户要求运行代码片段、验证算法、测试函数输出、
5
+ 或想看某段代码的执行结果时使用。支持 Python、JavaScript、TypeScript、
6
+ Go、Rust、Java、C/C++、Ruby、PHP 等语言。即使用户只是说「帮我跑一下这段代码」
7
+ 或贴了一段代码想看结果,也应触发。
8
+ keywords:
9
+ - code
10
+ - run
11
+ - 运行
12
+ - 代码
13
+ - execute
14
+ - sandbox
15
+ - python
16
+ - javascript
17
+ - 编程
18
+ - 执行
19
+ - 测试
20
+ tags:
21
+ - code-runner
22
+ - development
23
+ - sandbox
24
+ tools:
25
+ - run_code
26
+ ---
27
+
28
+ # 在线代码执行技能
29
+
30
+ 通过 glot.io API 在隔离沙箱中执行代码,不影响本地环境。
31
+
32
+ ## 使用方式
33
+
34
+ 调用 `run_code` 工具,传入 `language`(语言标识)和 `code`(代码内容)。
35
+
36
+ **支持语言:** python, javascript, typescript, go, rust, java, c, cpp, ruby, php
37
+
38
+ **Example:**
39
+ ```
40
+ 用户: 帮我算一下 1 到 100 的和
41
+ → run_code(language="python", code="print(sum(range(1, 101)))")
42
+ ```
43
+
44
+ ## 限制
45
+
46
+ - 代码长度上限 **10000 字符**
47
+ - 执行超时 **30 秒**
48
+ - 无法访问网络和文件系统(沙箱隔离)
49
+ - 不支持交互式输入(stdin)
50
+
51
+ ## 易错点
52
+
53
+ 1. **language 参数必须是上面列出的标识符**,不接受中文或全名(如用 `python` 不用 `Python3`)。
54
+ 2. **C 和 C++ 是不同的 language 值**:`c` 和 `cpp`。
55
+ 3. **代码需要有输出语句**(print/console.log/fmt.Println 等),否则返回为空。
@@ -0,0 +1,17 @@
1
+ import { defineTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { formatResult, runCode } from '../../src/run-code.js';
4
+
5
+ export default defineTool<{ language: string; code: string }>({
6
+ description: '在沙箱中运行代码片段,返回 stdout/stderr/error',
7
+ inputSchema: z.object({
8
+ language: z.string().min(1),
9
+ code: z.string().min(1),
10
+ }),
11
+ keywords: ['运行代码', '执行代码', 'run code', 'execute', '代码'],
12
+ tags: ['code', 'run', 'execute', 'sandbox'],
13
+ async execute({ language, code }) {
14
+ const result = await runCode(language, code);
15
+ return formatResult(result);
16
+ },
17
+ });
@@ -0,0 +1,17 @@
1
+ import { defineTool } from '@zhin.js/agent/tools';
2
+ import { z } from 'zod';
3
+ import { formatResult, runCode } from '../../src/run-code.js';
4
+ export default defineTool({
5
+ description: '在沙箱中运行代码片段,返回 stdout/stderr/error',
6
+ inputSchema: z.object({
7
+ language: z.string().min(1),
8
+ code: z.string().min(1),
9
+ }),
10
+ keywords: ['运行代码', '执行代码', 'run code', 'execute', '代码'],
11
+ tags: ['code', 'run', 'execute', 'sandbox'],
12
+ async execute({ language, code }) {
13
+ const result = await runCode(language, code);
14
+ return formatResult(result);
15
+ },
16
+ });
17
+ //# sourceMappingURL=run_code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_code.js","sourceRoot":"","sources":["../../../agent/tools/run_code.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE9D,eAAe,UAAU,CAAqC;IAC5D,WAAW,EAAE,mCAAmC;IAChD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;QACpB,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;KACxB,CAAC;IACF,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC;IACvD,IAAI,EAAE,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,CAAC;IAC3C,KAAK,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC9B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC7C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;IAC9B,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * @zhin.js/plugin-code-runner
3
+ *
4
+ * 沙箱代码执行插件 —— 通过 glot.io API 安全运行代码片段
5
+ */
6
+ import { formatCompact, MessageCommand, usePlugin } from 'zhin.js';
7
+ import { formatResult, runCode } from './run-code.js';
8
+ const { addCommand, logger } = usePlugin();
9
+ addCommand(new MessageCommand('运行 <language:text> <code:text>')
10
+ .desc('在沙箱中运行代码片段')
11
+ .action(async (_message, result) => {
12
+ const { language, code } = result.params;
13
+ logger.debug(formatCompact({ op: 'run', language, len: code.length }));
14
+ const res = await runCode(language, code);
15
+ return formatResult(res);
16
+ }));
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAEtD,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,CAAC;AAE3C,UAAU,CACR,IAAI,cAAc,CAAC,gCAAgC,CAAC;KACjD,IAAI,CAAC,YAAY,CAAC;KAClB,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE;IACjC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,MAAM,CAAC,MAA4C,CAAC;IAC/E,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1C,OAAO,YAAY,CAAC,GAAG,CAAC,CAAC;AAC3B,CAAC,CAAC,CACL,CAAC"}
@@ -0,0 +1,72 @@
1
+ const MAX_CODE_LENGTH = 10000;
2
+ const SUPPORTED_LANGUAGES = {
3
+ python: 'main.py',
4
+ javascript: 'main.js',
5
+ typescript: 'main.ts',
6
+ go: 'main.go',
7
+ rust: 'main.rs',
8
+ java: 'Main.java',
9
+ c: 'main.c',
10
+ cpp: 'main.cpp',
11
+ ruby: 'main.rb',
12
+ php: 'main.php',
13
+ };
14
+ const GLOT_API_BASE = 'https://glot.io/api/run';
15
+ export async function runCode(language, code) {
16
+ const lang = language.toLowerCase().trim();
17
+ if (!SUPPORTED_LANGUAGES[lang]) {
18
+ return {
19
+ stdout: '',
20
+ stderr: '',
21
+ error: `不支持的语言: ${lang}。支持: ${Object.keys(SUPPORTED_LANGUAGES).join(', ')}`,
22
+ };
23
+ }
24
+ if (code.length > MAX_CODE_LENGTH) {
25
+ return {
26
+ stdout: '',
27
+ stderr: '',
28
+ error: `代码长度超过上限 ${MAX_CODE_LENGTH} 字符(当前 ${code.length})`,
29
+ };
30
+ }
31
+ const fileName = SUPPORTED_LANGUAGES[lang];
32
+ const url = `${GLOT_API_BASE}/${lang}/latest`;
33
+ try {
34
+ const response = await fetch(url, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({
38
+ files: [{ name: fileName, content: code }],
39
+ }),
40
+ signal: AbortSignal.timeout(30_000),
41
+ });
42
+ if (!response.ok) {
43
+ return {
44
+ stdout: '',
45
+ stderr: '',
46
+ error: `API 请求失败: ${response.status} ${response.statusText}`,
47
+ };
48
+ }
49
+ const data = (await response.json());
50
+ return {
51
+ stdout: data.stdout || '',
52
+ stderr: data.stderr || '',
53
+ error: data.error || '',
54
+ };
55
+ }
56
+ catch (e) {
57
+ const err = e;
58
+ const msg = err.name === 'TimeoutError' ? '请求超时(30s)' : err.message || '未知错误';
59
+ return { stdout: '', stderr: '', error: msg };
60
+ }
61
+ }
62
+ export function formatResult(result) {
63
+ const parts = [];
64
+ if (result.stdout)
65
+ parts.push(`[stdout]\n${result.stdout}`);
66
+ if (result.stderr)
67
+ parts.push(`[stderr]\n${result.stderr}`);
68
+ if (result.error)
69
+ parts.push(`[error]\n${result.error}`);
70
+ return parts.length ? parts.join('\n') : '(无输出)';
71
+ }
72
+ //# sourceMappingURL=run-code.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-code.js","sourceRoot":"","sources":["../../src/run-code.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhin.js/plugin-code-runner",
3
- "version": "3.0.1",
3
+ "version": "3.0.2",
4
4
  "description": "Sandboxed code execution plugin for Zhin.js — run code snippets via AI or command",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",
@@ -16,6 +16,7 @@
16
16
  "files": [
17
17
  "src",
18
18
  "lib",
19
+ "agent",
19
20
  "README.md"
20
21
  ],
21
22
  "keywords": [
@@ -33,13 +34,16 @@
33
34
  "url": "https://github.com/lc-cn"
34
35
  },
35
36
  "license": "MIT",
36
- "dependencies": {},
37
37
  "devDependencies": {
38
38
  "typescript": "^6.0.3",
39
- "zhin.js": "4.1.1"
39
+ "zod": "^4.4.3",
40
+ "zhin.js": "4.1.2",
41
+ "@zhin.js/agent": "1.0.3"
40
42
  },
41
43
  "peerDependencies": {
42
- "zhin.js": "4.1.1"
44
+ "zod": "^4.0.0",
45
+ "zhin.js": "4.1.2",
46
+ "@zhin.js/agent": "1.0.3"
43
47
  },
44
48
  "repository": {
45
49
  "type": "git",
@@ -50,8 +54,17 @@
50
54
  "access": "public",
51
55
  "registry": "https://registry.npmjs.org"
52
56
  },
57
+ "peerDependenciesMeta": {
58
+ "@zhin.js/agent": {
59
+ "optional": true
60
+ },
61
+ "zod": {
62
+ "optional": true
63
+ }
64
+ },
53
65
  "scripts": {
54
- "build": "tsc --build",
55
- "clean": "rimraf lib"
66
+ "build": "tsc",
67
+ "clean": "rimraf lib",
68
+ "test": "sh -c 'cd ../../.. && vitest run plugins/utils/code-runner/tests'"
56
69
  }
57
70
  }
package/src/index.ts CHANGED
@@ -2,133 +2,19 @@
2
2
  * @zhin.js/plugin-code-runner
3
3
  *
4
4
  * 沙箱代码执行插件 —— 通过 glot.io API 安全运行代码片段
5
- *
6
- * 功能:
7
- * - AI 工具 run_code:AI 可调用执行代码
8
- * - 命令「运行 <language> <code>」:用户直接运行代码
9
- *
10
- * 安全:
11
- * - 代码长度上限 10000 字符
12
- * - 语言白名单校验
13
- * - 不执行本地代码,全部通过 glot.io 远程沙箱
14
5
  */
15
- import { formatCompact, MessageCommand, usePlugin, ZhinTool } from 'zhin.js'
16
-
17
- const { addCommand, addTool, logger } = usePlugin()
18
-
19
- // ─── 常量 ────────────────────────────────────────────────────────────────────
20
-
21
- const MAX_CODE_LENGTH = 10000
22
-
23
- const SUPPORTED_LANGUAGES: Record<string, string> = {
24
- python: 'main.py',
25
- javascript: 'main.js',
26
- typescript: 'main.ts',
27
- go: 'main.go',
28
- rust: 'main.rs',
29
- java: 'Main.java',
30
- c: 'main.c',
31
- cpp: 'main.cpp',
32
- ruby: 'main.rb',
33
- php: 'main.php',
34
- }
35
-
36
- const GLOT_API_BASE = 'https://glot.io/api/run'
37
-
38
- // ─── 核心执行函数 ────────────────────────────────────────────────────────────
39
-
40
- interface RunResult {
41
- stdout: string
42
- stderr: string
43
- error: string
44
- }
45
-
46
- async function runCode(language: string, code: string): Promise<RunResult> {
47
- const lang = language.toLowerCase().trim()
48
-
49
- if (!SUPPORTED_LANGUAGES[lang]) {
50
- return {
51
- stdout: '',
52
- stderr: '',
53
- error: `不支持的语言: ${lang}。支持: ${Object.keys(SUPPORTED_LANGUAGES).join(', ')}`,
54
- }
55
- }
56
-
57
- if (code.length > MAX_CODE_LENGTH) {
58
- return {
59
- stdout: '',
60
- stderr: '',
61
- error: `代码长度超过上限 ${MAX_CODE_LENGTH} 字符(当前 ${code.length})`,
62
- }
63
- }
64
-
65
- const fileName = SUPPORTED_LANGUAGES[lang]
66
- const url = `${GLOT_API_BASE}/${lang}/latest`
67
-
68
- try {
69
- const response = await fetch(url, {
70
- method: 'POST',
71
- headers: { 'Content-Type': 'application/json' },
72
- body: JSON.stringify({
73
- files: [{ name: fileName, content: code }],
74
- }),
75
- signal: AbortSignal.timeout(30_000),
76
- })
77
-
78
- if (!response.ok) {
79
- return {
80
- stdout: '',
81
- stderr: '',
82
- error: `API 请求失败: ${response.status} ${response.statusText}`,
83
- }
84
- }
85
-
86
- const data = (await response.json()) as RunResult
87
- return {
88
- stdout: data.stdout || '',
89
- stderr: data.stderr || '',
90
- error: data.error || '',
91
- }
92
- } catch (e: unknown) {
93
- const err = e as Error;
94
- const msg = err.name === 'TimeoutError' ? '请求超时(30s)' : err.message || '未知错误'
95
- return { stdout: '', stderr: '', error: msg }
96
- }
97
- }
98
-
99
- function formatResult(result: RunResult): string {
100
- const parts: string[] = []
101
- if (result.stdout) parts.push(`[stdout]\n${result.stdout}`)
102
- if (result.stderr) parts.push(`[stderr]\n${result.stderr}`)
103
- if (result.error) parts.push(`[error]\n${result.error}`)
104
- return parts.length ? parts.join('\n') : '(无输出)'
105
- }
106
-
107
- // ─── AI Tool: run_code ───────────────────────────────────────────────────────
108
-
109
- addTool(
110
- new ZhinTool('run_code')
111
- .desc('在沙箱中运行代码片段,返回 stdout/stderr/error')
112
- .keyword('运行代码', '执行代码', 'run code', 'execute', '代码')
113
- .tag('code', 'run', 'execute', 'sandbox')
114
- .param('language', { type: 'string', description: '编程语言(python/javascript/typescript/go/rust/java/c/cpp/ruby/php)' }, true)
115
- .param('code', { type: 'string', description: '要执行的代码' }, true)
116
- .execute(async (args) => {
117
- const result = await runCode(args.language, args.code)
118
- return formatResult(result)
119
- })
120
- .toTool(),
121
- )
6
+ import { formatCompact, MessageCommand, usePlugin } from 'zhin.js';
7
+ import { formatResult, runCode } from './run-code.js';
122
8
 
123
- // ─── 命令: 运行 ─────────────────────────────────────────────────────────────
9
+ const { addCommand, logger } = usePlugin();
124
10
 
125
11
  addCommand(
126
12
  new MessageCommand('运行 <language:text> <code:text>')
127
13
  .desc('在沙箱中运行代码片段')
128
14
  .action(async (_message, result) => {
129
- const { language, code } = result.params as { language: string; code: string }
130
- logger.debug(formatCompact( { op: 'run', language, len: code.length }))
131
- const res = await runCode(language, code)
132
- return formatResult(res)
15
+ const { language, code } = result.params as { language: string; code: string };
16
+ logger.debug(formatCompact({ op: 'run', language, len: code.length }));
17
+ const res = await runCode(language, code);
18
+ return formatResult(res);
133
19
  }),
134
- )
20
+ );
@@ -0,0 +1,83 @@
1
+ const MAX_CODE_LENGTH = 10000;
2
+
3
+ const SUPPORTED_LANGUAGES: Record<string, string> = {
4
+ python: 'main.py',
5
+ javascript: 'main.js',
6
+ typescript: 'main.ts',
7
+ go: 'main.go',
8
+ rust: 'main.rs',
9
+ java: 'Main.java',
10
+ c: 'main.c',
11
+ cpp: 'main.cpp',
12
+ ruby: 'main.rb',
13
+ php: 'main.php',
14
+ };
15
+
16
+ const GLOT_API_BASE = 'https://glot.io/api/run';
17
+
18
+ interface RunResult {
19
+ stdout: string;
20
+ stderr: string;
21
+ error: string;
22
+ }
23
+
24
+ export async function runCode(language: string, code: string): Promise<RunResult> {
25
+ const lang = language.toLowerCase().trim();
26
+
27
+ if (!SUPPORTED_LANGUAGES[lang]) {
28
+ return {
29
+ stdout: '',
30
+ stderr: '',
31
+ error: `不支持的语言: ${lang}。支持: ${Object.keys(SUPPORTED_LANGUAGES).join(', ')}`,
32
+ };
33
+ }
34
+
35
+ if (code.length > MAX_CODE_LENGTH) {
36
+ return {
37
+ stdout: '',
38
+ stderr: '',
39
+ error: `代码长度超过上限 ${MAX_CODE_LENGTH} 字符(当前 ${code.length})`,
40
+ };
41
+ }
42
+
43
+ const fileName = SUPPORTED_LANGUAGES[lang];
44
+ const url = `${GLOT_API_BASE}/${lang}/latest`;
45
+
46
+ try {
47
+ const response = await fetch(url, {
48
+ method: 'POST',
49
+ headers: { 'Content-Type': 'application/json' },
50
+ body: JSON.stringify({
51
+ files: [{ name: fileName, content: code }],
52
+ }),
53
+ signal: AbortSignal.timeout(30_000),
54
+ });
55
+
56
+ if (!response.ok) {
57
+ return {
58
+ stdout: '',
59
+ stderr: '',
60
+ error: `API 请求失败: ${response.status} ${response.statusText}`,
61
+ };
62
+ }
63
+
64
+ const data = (await response.json()) as RunResult;
65
+ return {
66
+ stdout: data.stdout || '',
67
+ stderr: data.stderr || '',
68
+ error: data.error || '',
69
+ };
70
+ } catch (e: unknown) {
71
+ const err = e as Error;
72
+ const msg = err.name === 'TimeoutError' ? '请求超时(30s)' : err.message || '未知错误';
73
+ return { stdout: '', stderr: '', error: msg };
74
+ }
75
+ }
76
+
77
+ export function formatResult(result: RunResult): string {
78
+ const parts: string[] = [];
79
+ if (result.stdout) parts.push(`[stdout]\n${result.stdout}`);
80
+ if (result.stderr) parts.push(`[stderr]\n${result.stderr}`);
81
+ if (result.error) parts.push(`[error]\n${result.error}`);
82
+ return parts.length ? parts.join('\n') : '(无输出)';
83
+ }
package/lib/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/lib/index.js DELETED
@@ -1,110 +0,0 @@
1
- /**
2
- * @zhin.js/plugin-code-runner
3
- *
4
- * 沙箱代码执行插件 —— 通过 glot.io API 安全运行代码片段
5
- *
6
- * 功能:
7
- * - AI 工具 run_code:AI 可调用执行代码
8
- * - 命令「运行 <language> <code>」:用户直接运行代码
9
- *
10
- * 安全:
11
- * - 代码长度上限 10000 字符
12
- * - 语言白名单校验
13
- * - 不执行本地代码,全部通过 glot.io 远程沙箱
14
- */
15
- import { formatCompact, MessageCommand, usePlugin, ZhinTool } from 'zhin.js';
16
- const { addCommand, addTool, logger } = usePlugin();
17
- // ─── 常量 ────────────────────────────────────────────────────────────────────
18
- const MAX_CODE_LENGTH = 10000;
19
- const SUPPORTED_LANGUAGES = {
20
- python: 'main.py',
21
- javascript: 'main.js',
22
- typescript: 'main.ts',
23
- go: 'main.go',
24
- rust: 'main.rs',
25
- java: 'Main.java',
26
- c: 'main.c',
27
- cpp: 'main.cpp',
28
- ruby: 'main.rb',
29
- php: 'main.php',
30
- };
31
- const GLOT_API_BASE = 'https://glot.io/api/run';
32
- async function runCode(language, code) {
33
- const lang = language.toLowerCase().trim();
34
- if (!SUPPORTED_LANGUAGES[lang]) {
35
- return {
36
- stdout: '',
37
- stderr: '',
38
- error: `不支持的语言: ${lang}。支持: ${Object.keys(SUPPORTED_LANGUAGES).join(', ')}`,
39
- };
40
- }
41
- if (code.length > MAX_CODE_LENGTH) {
42
- return {
43
- stdout: '',
44
- stderr: '',
45
- error: `代码长度超过上限 ${MAX_CODE_LENGTH} 字符(当前 ${code.length})`,
46
- };
47
- }
48
- const fileName = SUPPORTED_LANGUAGES[lang];
49
- const url = `${GLOT_API_BASE}/${lang}/latest`;
50
- try {
51
- const response = await fetch(url, {
52
- method: 'POST',
53
- headers: { 'Content-Type': 'application/json' },
54
- body: JSON.stringify({
55
- files: [{ name: fileName, content: code }],
56
- }),
57
- signal: AbortSignal.timeout(30_000),
58
- });
59
- if (!response.ok) {
60
- return {
61
- stdout: '',
62
- stderr: '',
63
- error: `API 请求失败: ${response.status} ${response.statusText}`,
64
- };
65
- }
66
- const data = (await response.json());
67
- return {
68
- stdout: data.stdout || '',
69
- stderr: data.stderr || '',
70
- error: data.error || '',
71
- };
72
- }
73
- catch (e) {
74
- const err = e;
75
- const msg = err.name === 'TimeoutError' ? '请求超时(30s)' : err.message || '未知错误';
76
- return { stdout: '', stderr: '', error: msg };
77
- }
78
- }
79
- function formatResult(result) {
80
- const parts = [];
81
- if (result.stdout)
82
- parts.push(`[stdout]\n${result.stdout}`);
83
- if (result.stderr)
84
- parts.push(`[stderr]\n${result.stderr}`);
85
- if (result.error)
86
- parts.push(`[error]\n${result.error}`);
87
- return parts.length ? parts.join('\n') : '(无输出)';
88
- }
89
- // ─── AI Tool: run_code ───────────────────────────────────────────────────────
90
- addTool(new ZhinTool('run_code')
91
- .desc('在沙箱中运行代码片段,返回 stdout/stderr/error')
92
- .keyword('运行代码', '执行代码', 'run code', 'execute', '代码')
93
- .tag('code', 'run', 'execute', 'sandbox')
94
- .param('language', { type: 'string', description: '编程语言(python/javascript/typescript/go/rust/java/c/cpp/ruby/php)' }, true)
95
- .param('code', { type: 'string', description: '要执行的代码' }, true)
96
- .execute(async (args) => {
97
- const result = await runCode(args.language, args.code);
98
- return formatResult(result);
99
- })
100
- .toTool());
101
- // ─── 命令: 运行 ─────────────────────────────────────────────────────────────
102
- addCommand(new MessageCommand('运行 <language:text> <code:text>')
103
- .desc('在沙箱中运行代码片段')
104
- .action(async (_message, result) => {
105
- const { language, code } = result.params;
106
- logger.debug(formatCompact({ op: 'run', language, len: code.length }));
107
- const res = await runCode(language, code);
108
- return formatResult(res);
109
- }));
110
- //# sourceMappingURL=index.js.map
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}