@ai-zen/agents-sdk 0.2.0-alpha.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.
Files changed (97) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +100 -0
  3. package/dist/capabilities/Capabilities.d.ts +71 -0
  4. package/dist/capabilities/Capabilities.js +161 -0
  5. package/dist/capabilities/PermissionEvaluator.d.ts +19 -0
  6. package/dist/capabilities/PermissionEvaluator.js +50 -0
  7. package/dist/capabilities/disclosure.d.ts +10 -0
  8. package/dist/capabilities/disclosure.js +17 -0
  9. package/dist/capabilities/discovery/builtin.d.ts +8 -0
  10. package/dist/capabilities/discovery/builtin.js +17 -0
  11. package/dist/capabilities/discovery/mcp.d.ts +7 -0
  12. package/dist/capabilities/discovery/mcp.js +29 -0
  13. package/dist/capabilities/discovery/skills.d.ts +43 -0
  14. package/dist/capabilities/discovery/skills.js +175 -0
  15. package/dist/capabilities/discovery/subagents.d.ts +7 -0
  16. package/dist/capabilities/discovery/subagents.js +39 -0
  17. package/dist/capabilities/discovery/usertools.d.ts +13 -0
  18. package/dist/capabilities/discovery/usertools.js +136 -0
  19. package/dist/capabilities/implements/builtin/batchEdit.d.ts +2 -0
  20. package/dist/capabilities/implements/builtin/batchEdit.js +76 -0
  21. package/dist/capabilities/implements/builtin/copy.d.ts +2 -0
  22. package/dist/capabilities/implements/builtin/copy.js +38 -0
  23. package/dist/capabilities/implements/builtin/cwd.d.ts +2 -0
  24. package/dist/capabilities/implements/builtin/cwd.js +15 -0
  25. package/dist/capabilities/implements/builtin/downloadFile.d.ts +2 -0
  26. package/dist/capabilities/implements/builtin/downloadFile.js +70 -0
  27. package/dist/capabilities/implements/builtin/edit.d.ts +2 -0
  28. package/dist/capabilities/implements/builtin/edit.js +79 -0
  29. package/dist/capabilities/implements/builtin/exec.d.ts +2 -0
  30. package/dist/capabilities/implements/builtin/exec.js +31 -0
  31. package/dist/capabilities/implements/builtin/exist.d.ts +2 -0
  32. package/dist/capabilities/implements/builtin/exist.js +25 -0
  33. package/dist/capabilities/implements/builtin/findText.d.ts +2 -0
  34. package/dist/capabilities/implements/builtin/findText.js +86 -0
  35. package/dist/capabilities/implements/builtin/generateImage.d.ts +9 -0
  36. package/dist/capabilities/implements/builtin/generateImage.js +112 -0
  37. package/dist/capabilities/implements/builtin/glob.d.ts +2 -0
  38. package/dist/capabilities/implements/builtin/glob.js +45 -0
  39. package/dist/capabilities/implements/builtin/index.d.ts +19 -0
  40. package/dist/capabilities/implements/builtin/index.js +34 -0
  41. package/dist/capabilities/implements/builtin/ls.d.ts +2 -0
  42. package/dist/capabilities/implements/builtin/ls.js +28 -0
  43. package/dist/capabilities/implements/builtin/mkdir.d.ts +2 -0
  44. package/dist/capabilities/implements/builtin/mkdir.js +34 -0
  45. package/dist/capabilities/implements/builtin/readFile.d.ts +2 -0
  46. package/dist/capabilities/implements/builtin/readFile.js +31 -0
  47. package/dist/capabilities/implements/builtin/rename.d.ts +2 -0
  48. package/dist/capabilities/implements/builtin/rename.js +32 -0
  49. package/dist/capabilities/implements/builtin/rm.d.ts +2 -0
  50. package/dist/capabilities/implements/builtin/rm.js +34 -0
  51. package/dist/capabilities/implements/builtin/writeFile.d.ts +2 -0
  52. package/dist/capabilities/implements/builtin/writeFile.js +34 -0
  53. package/dist/capabilities/implements/mcpTools.d.ts +20 -0
  54. package/dist/capabilities/implements/mcpTools.js +147 -0
  55. package/dist/capabilities/implements/skillTools.d.ts +15 -0
  56. package/dist/capabilities/implements/skillTools.js +113 -0
  57. package/dist/capabilities/implements/subAgentTools.d.ts +11 -0
  58. package/dist/capabilities/implements/subAgentTools.js +53 -0
  59. package/dist/config/ConfigManager.d.ts +49 -0
  60. package/dist/config/ConfigManager.js +126 -0
  61. package/dist/config/constants.d.ts +20 -0
  62. package/dist/config/constants.js +185 -0
  63. package/dist/crud/AgentRepository.d.ts +9 -0
  64. package/dist/crud/AgentRepository.js +10 -0
  65. package/dist/crud/ConversationRepository.d.ts +9 -0
  66. package/dist/crud/ConversationRepository.js +10 -0
  67. package/dist/crud/DraftRepository.d.ts +15 -0
  68. package/dist/crud/DraftRepository.js +41 -0
  69. package/dist/index.d.ts +40 -0
  70. package/dist/index.js +37 -0
  71. package/dist/plugin/AutoDraftPlugin.d.ts +26 -0
  72. package/dist/plugin/AutoDraftPlugin.js +58 -0
  73. package/dist/plugin/AutoMigratePlugin.d.ts +25 -0
  74. package/dist/plugin/AutoMigratePlugin.js +74 -0
  75. package/dist/plugin/AutoRefreshToolsPlugin.d.ts +12 -0
  76. package/dist/plugin/AutoRefreshToolsPlugin.js +23 -0
  77. package/dist/runtime/McpConnectionManager.d.ts +42 -0
  78. package/dist/runtime/McpConnectionManager.js +238 -0
  79. package/dist/runtime/Provider.d.ts +44 -0
  80. package/dist/runtime/Provider.js +38 -0
  81. package/dist/runtime/SdkAgent.d.ts +100 -0
  82. package/dist/runtime/SdkAgent.js +103 -0
  83. package/dist/runtime/TaskMigrationService.d.ts +24 -0
  84. package/dist/runtime/TaskMigrationService.js +71 -0
  85. package/dist/runtime/createAgent.d.ts +14 -0
  86. package/dist/runtime/createAgent.js +41 -0
  87. package/dist/runtime/createModel.d.ts +11 -0
  88. package/dist/runtime/createModel.js +28 -0
  89. package/dist/shared/EntityRepository.d.ts +24 -0
  90. package/dist/shared/EntityRepository.js +62 -0
  91. package/dist/shared/errors.d.ts +8 -0
  92. package/dist/shared/errors.js +15 -0
  93. package/dist/shared/logger.d.ts +10 -0
  94. package/dist/shared/logger.js +10 -0
  95. package/dist/types/index.d.ts +178 -0
  96. package/dist/types/index.js +5 -0
  97. package/package.json +47 -0
@@ -0,0 +1,175 @@
1
+ import { readdirSync, existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { createLogger } from "../../shared/logger.js";
4
+ const log = createLogger();
5
+ // ---- 发现(返回完整 SkillInfo,不再丢失 subAgent 等信息)----
6
+ /**
7
+ * 扫描多个目录中发现的所有 Skill(含 SKILL.md 的子目录)。
8
+ * 按优先级从高到低传入路径列表,同名 skill 靠前的路径优先(先到先得)。
9
+ * 解析 YAML frontmatter 中的 name 和 description,同时校验合规。
10
+ */
11
+ export function discoverSkills(paths) {
12
+ const seen = new Set();
13
+ const items = [];
14
+ for (const dir of paths) {
15
+ if (!existsSync(dir))
16
+ continue;
17
+ let entries;
18
+ try {
19
+ entries = readdirSync(dir, { withFileTypes: true });
20
+ }
21
+ catch {
22
+ continue;
23
+ }
24
+ for (const entry of entries) {
25
+ if (!entry.isDirectory())
26
+ continue;
27
+ if (seen.has(entry.name))
28
+ continue;
29
+ const skillMdPath = join(dir, entry.name, "SKILL.md");
30
+ if (!existsSync(skillMdPath))
31
+ continue;
32
+ const skill = readSkillFromPath(entry.name, skillMdPath);
33
+ if (skill) {
34
+ seen.add(entry.name);
35
+ items.push(skill);
36
+ }
37
+ }
38
+ }
39
+ return items;
40
+ }
41
+ // ---- 完整读取(用于加载 Skill 正文和子 Agent 判断)----
42
+ /**
43
+ * 读取单个 Skill 的完整内容与元数据。
44
+ * 按优先级从高到低传入路径列表,靠前的路径优先(先到先得)。
45
+ * 返回 null 如果 skill 在所有目录中都不存在或 SKILL.md 不可读。
46
+ */
47
+ export function readSkill(skillDirs, skillId) {
48
+ for (const dir of skillDirs) {
49
+ const candidate = join(dir, skillId, "SKILL.md");
50
+ if (existsSync(candidate)) {
51
+ return readSkillFromPath(skillId, candidate);
52
+ }
53
+ }
54
+ return null;
55
+ }
56
+ // ---- 内部 ----
57
+ function readSkillFromPath(skillId, skillMdPath) {
58
+ try {
59
+ const content = readFileSync(skillMdPath, "utf-8");
60
+ const fm = parseFrontmatter(content);
61
+ if (!fm.name)
62
+ return null;
63
+ // 规范合规校验
64
+ const errors = validateSkill(skillId, fm);
65
+ for (const err of errors) {
66
+ log.warn(`[skills] ${err.skillId}: ${err.field} — ${err.message}`);
67
+ }
68
+ return {
69
+ id: skillId,
70
+ name: fm.name,
71
+ description: fm.description ?? "",
72
+ subAgent: fm.subAgent ?? false,
73
+ content,
74
+ license: fm.license,
75
+ compatibility: fm.compatibility,
76
+ metadata: fm.metadata,
77
+ allowedTools: fm.allowedTools,
78
+ };
79
+ }
80
+ catch {
81
+ return null;
82
+ }
83
+ }
84
+ // ---- 解析 ----
85
+ /** @internal */
86
+ export function parseFrontmatter(content) {
87
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
88
+ if (!match)
89
+ return {};
90
+ const result = {};
91
+ const lines = match[1].split("\n");
92
+ let i = 0;
93
+ while (i < lines.length) {
94
+ const line = lines[i];
95
+ const metaHeader = line.match(/^metadata:\s*$/);
96
+ if (metaHeader) {
97
+ const metadata = {};
98
+ i++;
99
+ while (i < lines.length) {
100
+ const subMatch = lines[i].match(/^ (\S+):\s*(.*)/);
101
+ if (!subMatch)
102
+ break;
103
+ metadata[subMatch[1]] = stripQuotes(subMatch[2].trim());
104
+ i++;
105
+ }
106
+ if (Object.keys(metadata).length > 0) {
107
+ result.metadata = metadata;
108
+ }
109
+ continue;
110
+ }
111
+ const kv = line.match(/^(\S+):\s*(.*)/);
112
+ if (kv) {
113
+ const key = kv[1];
114
+ const val = kv[2].trim();
115
+ switch (key) {
116
+ case "name":
117
+ result.name = val;
118
+ break;
119
+ case "description":
120
+ result.description = val;
121
+ break;
122
+ case "sub-agent":
123
+ result.subAgent = val === "true";
124
+ break;
125
+ case "license":
126
+ result.license = val;
127
+ break;
128
+ case "compatibility":
129
+ result.compatibility = val;
130
+ break;
131
+ case "allowed-tools":
132
+ result.allowedTools = val ? val.split(/\s+/).filter(Boolean) : [];
133
+ break;
134
+ }
135
+ }
136
+ i++;
137
+ }
138
+ return result;
139
+ }
140
+ function stripQuotes(s) {
141
+ if ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'"))) {
142
+ return s.slice(1, -1);
143
+ }
144
+ return s;
145
+ }
146
+ // ---- 校验 ----
147
+ const VALID_NAME_RE = /^[a-z0-9]+(-[a-z0-9]+)*$/;
148
+ /** @internal */
149
+ export function validateSkill(skillId, fm) {
150
+ const errors = [];
151
+ if (!fm.name || fm.name.length === 0) {
152
+ errors.push({ skillId, field: "name", message: "必须存在且非空" });
153
+ }
154
+ else {
155
+ if (fm.name.length > 64) {
156
+ errors.push({ skillId, field: "name", message: `超过 64 字符(当前 ${fm.name.length})` });
157
+ }
158
+ if (!VALID_NAME_RE.test(fm.name)) {
159
+ errors.push({ skillId, field: "name", message: `"${fm.name}" 不符合规范:仅允许小写字母、数字、连字符,且不能首尾或连续连字符` });
160
+ }
161
+ if (fm.name !== skillId) {
162
+ errors.push({ skillId, field: "name", message: `"${fm.name}" 与目录名 "${skillId}" 不一致` });
163
+ }
164
+ }
165
+ if (!fm.description || fm.description.length === 0) {
166
+ errors.push({ skillId, field: "description", message: "必须存在且非空" });
167
+ }
168
+ else if (fm.description.length > 1024) {
169
+ errors.push({ skillId, field: "description", message: `超过 1024 字符(当前 ${fm.description.length})` });
170
+ }
171
+ if (fm.compatibility && fm.compatibility.length > 500) {
172
+ errors.push({ skillId, field: "compatibility", message: `超过 500 字符(当前 ${fm.compatibility.length})` });
173
+ }
174
+ return errors;
175
+ }
@@ -0,0 +1,7 @@
1
+ import type { AgentDefinition } from "../../types/index.js";
2
+ /**
3
+ * 扫描多个目录中发现的所有 SubAgent 完整定义。
4
+ * 按优先级从高到低传入路径列表,同名 function.name 靠前的路径优先(先到先得)。
5
+ * 跳过无 function 的普通 Agent 和解析失败的文件。
6
+ */
7
+ export declare function discoverSubAgents(paths: string[]): AgentDefinition[];
@@ -0,0 +1,39 @@
1
+ import { readdirSync, existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /**
4
+ * 扫描多个目录中发现的所有 SubAgent 完整定义。
5
+ * 按优先级从高到低传入路径列表,同名 function.name 靠前的路径优先(先到先得)。
6
+ * 跳过无 function 的普通 Agent 和解析失败的文件。
7
+ */
8
+ export function discoverSubAgents(paths) {
9
+ const seen = new Set();
10
+ const agents = [];
11
+ for (const dir of paths) {
12
+ if (!existsSync(dir))
13
+ continue;
14
+ let files;
15
+ try {
16
+ files = readdirSync(dir).filter((f) => f.endsWith(".json"));
17
+ }
18
+ catch {
19
+ continue;
20
+ }
21
+ for (const file of files) {
22
+ try {
23
+ const raw = readFileSync(join(dir, file), "utf-8");
24
+ const def = JSON.parse(raw);
25
+ if (def.function) {
26
+ const funcName = def.function.name;
27
+ if (!seen.has(funcName)) {
28
+ seen.add(funcName);
29
+ agents.push(def);
30
+ }
31
+ }
32
+ }
33
+ catch {
34
+ // 跳过解析失败的文件
35
+ }
36
+ }
37
+ }
38
+ return agents;
39
+ }
@@ -0,0 +1,13 @@
1
+ import { Tool } from "@ai-zen/agents-core";
2
+ /**
3
+ * 扫描多个 tools/ 目录,发现所有 .js 文件,动态加载为 Tool 实例。
4
+ * 按优先级从高到低传入路径列表,同名工具靠前的路径优先(先到先得)。
5
+ * 按文件名排序以保证确定性。
6
+ *
7
+ * 加载方式:
8
+ * 1. 使用 vm.compileFunction 创建安全的执行上下文
9
+ * 2. 工具文件应通过 module.exports 或 exports.default 导出 Tool 实例
10
+ * 3. 支持直接导出 { function, exec } 格式的对象(自动适配为 Tool)
11
+ * 4. 支持导出 CallbackTool 兼容的 { function, callback } 格式
12
+ */
13
+ export declare function discoverUserTools(paths: string[]): Tool[];
@@ -0,0 +1,136 @@
1
+ import { readdirSync, existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import vm from "node:vm";
4
+ import { Tool, CallbackTool } from "@ai-zen/agents-core";
5
+ /**
6
+ * 扫描多个 tools/ 目录,发现所有 .js 文件,动态加载为 Tool 实例。
7
+ * 按优先级从高到低传入路径列表,同名工具靠前的路径优先(先到先得)。
8
+ * 按文件名排序以保证确定性。
9
+ *
10
+ * 加载方式:
11
+ * 1. 使用 vm.compileFunction 创建安全的执行上下文
12
+ * 2. 工具文件应通过 module.exports 或 exports.default 导出 Tool 实例
13
+ * 3. 支持直接导出 { function, exec } 格式的对象(自动适配为 Tool)
14
+ * 4. 支持导出 CallbackTool 兼容的 { function, callback } 格式
15
+ */
16
+ export function discoverUserTools(paths) {
17
+ const seen = new Set();
18
+ const tools = [];
19
+ for (const dir of paths) {
20
+ if (!existsSync(dir))
21
+ continue;
22
+ try {
23
+ const files = readdirSync(dir)
24
+ .filter((f) => f.endsWith(".js"))
25
+ .map((f) => f.slice(0, -3))
26
+ .sort();
27
+ for (const name of files) {
28
+ if (!seen.has(name)) {
29
+ seen.add(name);
30
+ try {
31
+ const tool = loadToolFile(join(dir, name + ".js"));
32
+ if (tool) {
33
+ tools.push(tool);
34
+ }
35
+ }
36
+ catch {
37
+ // 跳过加载失败的文件
38
+ }
39
+ }
40
+ }
41
+ }
42
+ catch {
43
+ continue;
44
+ }
45
+ }
46
+ return tools;
47
+ }
48
+ /**
49
+ * 从 .js 文件加载 Tool 实例。
50
+ *
51
+ * 使用 vm.compileFunction 创建沙箱执行环境,避免污染全局作用域。
52
+ * 注入有限的全局 API(console, setTimeout 等),不暴露 process/require。
53
+ */
54
+ function loadToolFile(filepath) {
55
+ const code = readFileSync(filepath, "utf-8");
56
+ // 构造沙箱上下文
57
+ const sandbox = {
58
+ // 有限全局 API
59
+ console: console,
60
+ setTimeout: setTimeout,
61
+ clearTimeout: clearTimeout,
62
+ setInterval: setInterval,
63
+ clearInterval: clearInterval,
64
+ Buffer: Buffer,
65
+ URL: URL,
66
+ URLSearchParams: URLSearchParams,
67
+ TextEncoder: TextEncoder,
68
+ TextDecoder: TextDecoder,
69
+ // 导出的结果存放处
70
+ module: { exports: {} },
71
+ exports: {},
72
+ };
73
+ sandbox.global = sandbox;
74
+ sandbox.globalThis = sandbox;
75
+ const context = vm.createContext(sandbox);
76
+ const script = new vm.Script(code, { filename: filepath });
77
+ try {
78
+ script.runInContext(context, { timeout: 5000 });
79
+ // 获取导出结果
80
+ const exported = sandbox.module.exports ?? sandbox.exports;
81
+ // 空导出
82
+ if (!exported || (typeof exported === "object" && Object.keys(exported).length === 0)) {
83
+ return null;
84
+ }
85
+ // 适配不同导出格式
86
+ return normalizeToolExport(exported, filepath);
87
+ }
88
+ catch (err) {
89
+ console.error(`[usertools] 加载工具文件失败: ${filepath} — ${err?.message ?? err}`);
90
+ return null;
91
+ }
92
+ }
93
+ /**
94
+ * 统一各种导出格式为 Tool 实例。
95
+ */
96
+ function normalizeToolExport(exported, filepath) {
97
+ // 情况 1:直接是 Tool 实例
98
+ if (exported instanceof Tool) {
99
+ return exported;
100
+ }
101
+ // 情况 2:默认导出(exported.default)
102
+ const target = exported.default ?? exported;
103
+ if (target instanceof Tool) {
104
+ return target;
105
+ }
106
+ // 情况 3:{ function, exec } 格式 → 适配为 Tool 子类
107
+ if (target && typeof target === "object" && target.function && typeof target.exec === "function") {
108
+ return createToolFromObject(target, filepath);
109
+ }
110
+ // 情况 4:{ function, callback } 格式 → 适配为 CallbackTool
111
+ if (target && typeof target === "object" && target.function && typeof target.callback === "function") {
112
+ return new CallbackTool({
113
+ function: target.function,
114
+ callback: target.callback,
115
+ });
116
+ }
117
+ console.error(`[usertools] 无法识别的工具格式: ${filepath}`);
118
+ return null;
119
+ }
120
+ /**
121
+ * 将 { function, exec } 对象适配为 Tool 子类实例。
122
+ */
123
+ function createToolFromObject(obj, filepath) {
124
+ return new (class extends Tool {
125
+ constructor() {
126
+ super({ function: obj.function });
127
+ }
128
+ async exec(ctx) {
129
+ const result = await obj.exec.call(ctx, ctx.parsed_args);
130
+ if (typeof result !== "string") {
131
+ return JSON.stringify(result) ?? "";
132
+ }
133
+ return result;
134
+ }
135
+ })();
136
+ }
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const batchEditTool: CallbackTool;
@@ -0,0 +1,76 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ import * as fsp from "fs/promises";
3
+ export const batchEditTool = new CallbackTool({
4
+ function: {
5
+ name: "batchEdit",
6
+ description: "批量编辑文件文本,可以优先使用这个工具对文件进行编辑",
7
+ parameters: {
8
+ type: "object",
9
+ properties: {
10
+ path: {
11
+ type: "string",
12
+ description: "文件路径",
13
+ },
14
+ replacements: {
15
+ type: "array",
16
+ description: "要替换的文本数组",
17
+ items: {
18
+ type: "object",
19
+ properties: {
20
+ oldText: {
21
+ type: "string",
22
+ description: "要替换的文本",
23
+ },
24
+ newText: {
25
+ type: "string",
26
+ description: "替换后的文本",
27
+ },
28
+ isReplaceAll: {
29
+ type: "boolean",
30
+ description: "是否替换所有匹配的文本(默认仅替换首次匹配)。使用此功能前应确保你提供的 oldText 足够精确,避免误替换",
31
+ default: false,
32
+ },
33
+ },
34
+ required: ["oldText", "newText"],
35
+ additionalProperties: false,
36
+ },
37
+ },
38
+ },
39
+ required: ["path", "replacements"],
40
+ additionalProperties: false,
41
+ },
42
+ },
43
+ async callback(input) {
44
+ try {
45
+ const content = await fsp.readFile(input.path, "utf-8");
46
+ let newContent = content;
47
+ const results = [];
48
+ for (const replacement of input.replacements) {
49
+ if (!newContent.includes(replacement.oldText)) {
50
+ results.push({
51
+ oldText: replacement.oldText,
52
+ newText: replacement.newText,
53
+ result: "文件中未精确匹配到要替换的文本",
54
+ });
55
+ continue;
56
+ }
57
+ if (replacement.isReplaceAll) {
58
+ newContent = newContent.replaceAll(replacement.oldText, replacement.newText);
59
+ }
60
+ else {
61
+ newContent = newContent.replace(replacement.oldText, replacement.newText);
62
+ }
63
+ results.push({
64
+ oldText: replacement.oldText,
65
+ newText: replacement.newText,
66
+ result: "success",
67
+ });
68
+ }
69
+ await fsp.writeFile(input.path, newContent);
70
+ return JSON.stringify(results);
71
+ }
72
+ catch (error) {
73
+ return error?.message;
74
+ }
75
+ },
76
+ });
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const copyTool: CallbackTool;
@@ -0,0 +1,38 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ import * as fsp from "fs/promises";
3
+ export const copyTool = new CallbackTool({
4
+ function: {
5
+ name: "copy",
6
+ description: "复制文件或目录(相当于 cp 命令)",
7
+ parameters: {
8
+ type: "object",
9
+ properties: {
10
+ src: {
11
+ type: "string",
12
+ description: "源路径",
13
+ },
14
+ dest: {
15
+ type: "string",
16
+ description: "目标路径",
17
+ },
18
+ recursive: {
19
+ type: "boolean",
20
+ description: "是否递归复制目录,复制目录时需设为 true",
21
+ },
22
+ },
23
+ required: ["src", "dest"],
24
+ additionalProperties: false,
25
+ },
26
+ },
27
+ async callback(input) {
28
+ try {
29
+ await fsp.cp(input.src, input.dest, {
30
+ recursive: input.recursive ?? false,
31
+ });
32
+ return "success";
33
+ }
34
+ catch (error) {
35
+ return error?.message;
36
+ }
37
+ },
38
+ });
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const cwdTool: CallbackTool;
@@ -0,0 +1,15 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export const cwdTool = new CallbackTool({
3
+ function: {
4
+ name: "cwd",
5
+ description: "获取当前工作目录 cwd",
6
+ parameters: {
7
+ type: "object",
8
+ properties: {},
9
+ required: [],
10
+ },
11
+ },
12
+ async callback() {
13
+ return process.cwd();
14
+ },
15
+ });
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const downloadFileTool: CallbackTool;
@@ -0,0 +1,70 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ import * as fsp from "fs/promises";
3
+ import * as path from "path";
4
+ export const downloadFileTool = new CallbackTool({
5
+ function: {
6
+ name: "downloadFile",
7
+ description: "从 URL 下载文件(图片、PDF 等二进制文件)并保存到本地磁盘。自动从 URL 推断文件名,也可指定保存路径。",
8
+ parameters: {
9
+ type: "object",
10
+ properties: {
11
+ url: {
12
+ type: "string",
13
+ description: "文件下载链接",
14
+ },
15
+ outputPath: {
16
+ type: "string",
17
+ description: "保存路径(可选)。可以是目录或完整文件路径。不指定则下载到当前工作目录,自动从 URL 推断文件名。",
18
+ },
19
+ },
20
+ required: ["url"],
21
+ additionalProperties: false,
22
+ },
23
+ },
24
+ async callback(input) {
25
+ try {
26
+ const url = input.url;
27
+ if (!url)
28
+ throw new Error("url 不能为空");
29
+ let savePath;
30
+ if (input.outputPath) {
31
+ savePath = input.outputPath;
32
+ const stat = await fsp.stat(savePath).catch(() => null);
33
+ if (stat?.isDirectory()) {
34
+ const urlPath = new URL(url).pathname;
35
+ const fileName = path.basename(urlPath) || `download_${Date.now()}`;
36
+ savePath = path.join(savePath, fileName);
37
+ }
38
+ }
39
+ else {
40
+ const urlPath = new URL(url).pathname;
41
+ const fileName = path.basename(urlPath) || `download_${Date.now()}`;
42
+ savePath = path.join(process.cwd(), fileName);
43
+ }
44
+ await fsp.mkdir(path.dirname(savePath), { recursive: true });
45
+ const response = await fetch(url);
46
+ if (!response.ok) {
47
+ throw new Error(`下载失败: HTTP ${response.status} ${response.statusText}`);
48
+ }
49
+ const contentType = response.headers.get("content-type") || "";
50
+ const arrayBuffer = await response.arrayBuffer();
51
+ const buffer = Buffer.from(arrayBuffer);
52
+ await fsp.writeFile(savePath, buffer);
53
+ const stats = await fsp.stat(savePath);
54
+ const fileSizeKB = (stats.size / 1024).toFixed(1);
55
+ return JSON.stringify({
56
+ success: true,
57
+ filePath: path.resolve(savePath),
58
+ fileSize: `${fileSizeKB} KB`,
59
+ contentType,
60
+ note: "文件已保存到本地",
61
+ });
62
+ }
63
+ catch (error) {
64
+ return JSON.stringify({
65
+ success: false,
66
+ error: error?.message || "下载失败",
67
+ });
68
+ }
69
+ },
70
+ });
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const editTool: CallbackTool;
@@ -0,0 +1,79 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ import * as fsp from "fs/promises";
3
+ function countOccurrences(content, text) {
4
+ let count = 0;
5
+ let pos = 0;
6
+ while (true) {
7
+ pos = content.indexOf(text, pos);
8
+ if (pos === -1)
9
+ break;
10
+ count++;
11
+ pos += text.length;
12
+ }
13
+ return count;
14
+ }
15
+ export const editTool = new CallbackTool({
16
+ function: {
17
+ name: "edit",
18
+ description: "编辑文件中的文本,支持精确替换或替换所有匹配项",
19
+ parameters: {
20
+ type: "object",
21
+ properties: {
22
+ path: {
23
+ type: "string",
24
+ description: "文件路径",
25
+ },
26
+ oldText: {
27
+ type: "string",
28
+ description: "要替换的文本",
29
+ },
30
+ newText: {
31
+ type: "string",
32
+ description: "替换后的文本",
33
+ },
34
+ isReplaceAll: {
35
+ type: "boolean",
36
+ description: "是否替换所有匹配的文本(默认 false,仅替换首次匹配)。使用此功能前应确保你提供的 oldText 足够精确,避免误替换",
37
+ default: false,
38
+ },
39
+ },
40
+ required: ["path", "oldText", "newText"],
41
+ additionalProperties: false,
42
+ },
43
+ },
44
+ async callback(input) {
45
+ try {
46
+ const path = input.path;
47
+ const oldText = input.oldText;
48
+ const newText = input.newText;
49
+ const isReplaceAll = input.isReplaceAll;
50
+ const content = await fsp.readFile(path, "utf-8");
51
+ const count = countOccurrences(content, oldText);
52
+ if (count === 0) {
53
+ return JSON.stringify({
54
+ result: "文件中未精确匹配到要替换的文本",
55
+ matchCount: 0,
56
+ });
57
+ }
58
+ const newContent = isReplaceAll
59
+ ? content.replaceAll(oldText, newText)
60
+ : content.replace(oldText, newText);
61
+ await fsp.writeFile(path, newContent);
62
+ if (!isReplaceAll && count > 1) {
63
+ return JSON.stringify({
64
+ result: `警告:未开启替换所有(isReplaceAll=false),但文本 "${oldText}" 在文件中出现了 ${count} 次,仅替换了首次匹配。如果希望替换所有匹配,请将 isReplaceAll 设为 true`,
65
+ matchCount: count,
66
+ replacedCount: 1,
67
+ });
68
+ }
69
+ return JSON.stringify({
70
+ result: "success",
71
+ matchCount: count,
72
+ replacedCount: isReplaceAll ? count : 1,
73
+ });
74
+ }
75
+ catch (error) {
76
+ return error?.message;
77
+ }
78
+ },
79
+ });
@@ -0,0 +1,2 @@
1
+ import { CallbackTool } from "@ai-zen/agents-core";
2
+ export declare const execTool: CallbackTool;