ningzhi-agent-cli 0.0.1
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/CLAUDE.md +197 -0
- package/README.md +35 -0
- package/dist/agent/agent.d.ts +24 -0
- package/dist/agent/agent.js +432 -0
- package/dist/agent/cli.d.ts +1 -0
- package/dist/agent/cli.js +209 -0
- package/dist/agent/colors.d.ts +19 -0
- package/dist/agent/colors.js +92 -0
- package/dist/agent/commands.d.ts +7 -0
- package/dist/agent/commands.js +73 -0
- package/dist/agent/config.d.ts +38 -0
- package/dist/agent/config.js +119 -0
- package/dist/agent/context.d.ts +17 -0
- package/dist/agent/context.js +146 -0
- package/dist/agent/db.d.ts +25 -0
- package/dist/agent/db.js +185 -0
- package/dist/agent/hooks/engine.d.ts +27 -0
- package/dist/agent/hooks/engine.js +122 -0
- package/dist/agent/hooks/index.d.ts +1 -0
- package/dist/agent/hooks/index.js +11 -0
- package/dist/agent/mcp/client.d.ts +31 -0
- package/dist/agent/mcp/client.js +138 -0
- package/dist/agent/mcp/index.d.ts +4 -0
- package/dist/agent/mcp/index.js +27 -0
- package/dist/agent/mcp/wrapper.d.ts +7 -0
- package/dist/agent/mcp/wrapper.js +26 -0
- package/dist/agent/model.d.ts +5 -0
- package/dist/agent/model.js +35 -0
- package/dist/agent/permission/dangerous-path.json +115 -0
- package/dist/agent/permission/exec.d.ts +14 -0
- package/dist/agent/permission/exec.js +35 -0
- package/dist/agent/permission/is-dangerous-path.d.ts +8 -0
- package/dist/agent/permission/is-dangerous-path.js +96 -0
- package/dist/agent/permission/is-safe-domains.d.ts +1 -0
- package/dist/agent/permission/is-safe-domains.js +145 -0
- package/dist/agent/permission/network.d.ts +8 -0
- package/dist/agent/permission/network.js +14 -0
- package/dist/agent/permission/read.d.ts +9 -0
- package/dist/agent/permission/read.js +17 -0
- package/dist/agent/permission/util.d.ts +11 -0
- package/dist/agent/permission/util.js +347 -0
- package/dist/agent/permission/write.d.ts +11 -0
- package/dist/agent/permission/write.js +21 -0
- package/dist/agent/prompt.d.ts +1 -0
- package/dist/agent/prompt.js +52 -0
- package/dist/agent/skills/oppose/SKILL.md +100 -0
- package/dist/agent/skills/planner/SKILL.md +90 -0
- package/dist/agent/skills.d.ts +8 -0
- package/dist/agent/skills.js +103 -0
- package/dist/agent/tools/agent_tool.d.ts +3 -0
- package/dist/agent/tools/agent_tool.js +44 -0
- package/dist/agent/tools/exec_tool.d.ts +3 -0
- package/dist/agent/tools/exec_tool.js +33 -0
- package/dist/agent/tools/load_skill_tool.d.ts +3 -0
- package/dist/agent/tools/load_skill_tool.js +16 -0
- package/dist/agent/tools/memory_create_tool.d.ts +6 -0
- package/dist/agent/tools/memory_create_tool.js +39 -0
- package/dist/agent/tools/memory_delete_tool.d.ts +3 -0
- package/dist/agent/tools/memory_delete_tool.js +28 -0
- package/dist/agent/tools/memory_retrieve_tool.d.ts +4 -0
- package/dist/agent/tools/memory_retrieve_tool.js +26 -0
- package/dist/agent/tools/profile_update_tool.d.ts +3 -0
- package/dist/agent/tools/profile_update_tool.js +51 -0
- package/dist/agent/tools/read_file_tool.d.ts +3 -0
- package/dist/agent/tools/read_file_tool.js +16 -0
- package/dist/agent/tools/run_js_tool.d.ts +3 -0
- package/dist/agent/tools/run_js_tool.js +47 -0
- package/dist/agent/tools/run_py_tool.d.ts +3 -0
- package/dist/agent/tools/run_py_tool.js +37 -0
- package/dist/agent/tools/web_fetch_tool.d.ts +3 -0
- package/dist/agent/tools/web_fetch_tool.js +28 -0
- package/dist/agent/tools/web_search_tool.d.ts +3 -0
- package/dist/agent/tools/web_search_tool.js +38 -0
- package/dist/agent/tools/write_file_tool.d.ts +4 -0
- package/dist/agent/tools/write_file_tool.js +15 -0
- package/dist/agent/tools.d.ts +14 -0
- package/dist/agent/tools.js +218 -0
- package/dist/agent/utils.d.ts +11 -0
- package/dist/agent/utils.js +43 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +81 -0
- package/dist/install.d.ts +1 -0
- package/dist/install.js +150 -0
- package/package.json +56 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tools = exports.nativeTools = void 0;
|
|
4
|
+
exports.maybePersistedOutput = maybePersistedOutput;
|
|
5
|
+
exports.initTools = initTools;
|
|
6
|
+
const tools_1 = require("@langchain/core/tools");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const promises_1 = require("node:fs/promises");
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const node_os_1 = require("node:os");
|
|
11
|
+
const read_file_tool_1 = require("./tools/read_file_tool");
|
|
12
|
+
const write_file_tool_1 = require("./tools/write_file_tool");
|
|
13
|
+
const exec_tool_1 = require("./tools/exec_tool");
|
|
14
|
+
const run_js_tool_1 = require("./tools/run_js_tool");
|
|
15
|
+
const web_search_tool_1 = require("./tools/web_search_tool");
|
|
16
|
+
const web_fetch_tool_1 = require("./tools/web_fetch_tool");
|
|
17
|
+
const load_skill_tool_1 = require("./tools/load_skill_tool");
|
|
18
|
+
const run_py_tool_1 = require("./tools/run_py_tool");
|
|
19
|
+
const memory_create_tool_1 = require("./tools/memory_create_tool");
|
|
20
|
+
const memory_retrieve_tool_1 = require("./tools/memory_retrieve_tool");
|
|
21
|
+
const memory_delete_tool_1 = require("./tools/memory_delete_tool");
|
|
22
|
+
const profile_update_tool_1 = require("./tools/profile_update_tool");
|
|
23
|
+
const agent_tool_1 = require("./tools/agent_tool");
|
|
24
|
+
const mcp_1 = require("./mcp");
|
|
25
|
+
function createNingzhiTool(impl, options, permission_level) {
|
|
26
|
+
const t = (0, tools_1.tool)(impl, options);
|
|
27
|
+
t.permission_level = permission_level;
|
|
28
|
+
return t;
|
|
29
|
+
}
|
|
30
|
+
// 读取文件工具
|
|
31
|
+
const readFileTool = createNingzhiTool(read_file_tool_1.readFileTool, {
|
|
32
|
+
name: "read_file",
|
|
33
|
+
description: "Read the contents of a file in the current directory.",
|
|
34
|
+
schema: zod_1.z.object({
|
|
35
|
+
filepath: zod_1.z
|
|
36
|
+
.string()
|
|
37
|
+
.describe("The path of the file to read. Can be relative, absolute, or start with ~."),
|
|
38
|
+
}),
|
|
39
|
+
}, "read");
|
|
40
|
+
// 写入文件工具
|
|
41
|
+
const writeFileTool = createNingzhiTool(write_file_tool_1.writeFileTool, {
|
|
42
|
+
name: "write_file",
|
|
43
|
+
description: "Create or overwrite a file in the current directory. Will create parent directories if needed.",
|
|
44
|
+
schema: zod_1.z.object({
|
|
45
|
+
filepath: zod_1.z
|
|
46
|
+
.string()
|
|
47
|
+
.describe("The path of the file to write. Can be relative, absolute, or start with ~."),
|
|
48
|
+
content: zod_1.z.string().describe("The content to write to the file."),
|
|
49
|
+
}),
|
|
50
|
+
}, "write");
|
|
51
|
+
// 执行命令工具
|
|
52
|
+
const execTool = createNingzhiTool(exec_tool_1.execTool, {
|
|
53
|
+
name: "exec",
|
|
54
|
+
description: "Execute a safe shell command in the current directory. Dangerous commands (rm, rmdir, etc.), absolute paths, and parent directory references are blocked.",
|
|
55
|
+
schema: zod_1.z.object({
|
|
56
|
+
command: zod_1.z.string().describe("The shell command to execute."),
|
|
57
|
+
}),
|
|
58
|
+
}, "exec");
|
|
59
|
+
// 执行JavaScript代码工具
|
|
60
|
+
const runJsTool = createNingzhiTool(run_js_tool_1.runJsTool, {
|
|
61
|
+
name: "run_js",
|
|
62
|
+
description: "Execute JavaScript code using Node.js in the current directory. Returns stdout/stderr or error messages.",
|
|
63
|
+
schema: zod_1.z.object({
|
|
64
|
+
code: zod_1.z.string().describe("The JavaScript code to execute."),
|
|
65
|
+
}),
|
|
66
|
+
}, "exec");
|
|
67
|
+
// 执行Python代码工具
|
|
68
|
+
const runPyTool = createNingzhiTool(run_py_tool_1.runPyTool, {
|
|
69
|
+
name: "run_py",
|
|
70
|
+
description: "Execute Python code using Python3 in the current directory. Returns stdout/stderr or error messages.",
|
|
71
|
+
schema: zod_1.z.object({
|
|
72
|
+
code: zod_1.z.string().describe("The Python code to execute."),
|
|
73
|
+
}),
|
|
74
|
+
}, "exec");
|
|
75
|
+
// 网络搜索工具
|
|
76
|
+
const webSearchTool = createNingzhiTool(web_search_tool_1.webSearchTool, {
|
|
77
|
+
name: "web_search",
|
|
78
|
+
description: "Search the web using Tavily. Useful for finding current information, news, and facts.",
|
|
79
|
+
schema: zod_1.z.object({
|
|
80
|
+
query: zod_1.z.string().describe("The search query."),
|
|
81
|
+
}),
|
|
82
|
+
}, "network");
|
|
83
|
+
// 网络获取工具
|
|
84
|
+
const webFetchTool = createNingzhiTool(web_fetch_tool_1.webFetchTool, {
|
|
85
|
+
name: "web_fetch",
|
|
86
|
+
description: "Fetch the content of a web page by URL. Returns the raw HTML/text content. Useful when you need to read a specific page.",
|
|
87
|
+
schema: zod_1.z.object({
|
|
88
|
+
url: zod_1.z.string().describe("The full URL of the web page to fetch."),
|
|
89
|
+
}),
|
|
90
|
+
}, "network");
|
|
91
|
+
// 加载技能工具
|
|
92
|
+
const loadSkillTool = createNingzhiTool(load_skill_tool_1.loadSkillTool, {
|
|
93
|
+
name: "load_skill",
|
|
94
|
+
description: "Load the full content of a skill by its name. Call this when you need to use a specific skill to handle the user request. You can only load one skill at a time.",
|
|
95
|
+
schema: zod_1.z.object({
|
|
96
|
+
name: zod_1.z.string().describe("The name of the skill to load."),
|
|
97
|
+
}),
|
|
98
|
+
}, "read");
|
|
99
|
+
// 创建记忆工具
|
|
100
|
+
const memoryCreateTool = createNingzhiTool(memory_create_tool_1.memoryCreateTool, {
|
|
101
|
+
name: "memory_create",
|
|
102
|
+
description: "Save a piece of memory to the database. Use this when the user shares something worth remembering, such as a personal fact, event, preference, or skill.",
|
|
103
|
+
schema: zod_1.z.object({
|
|
104
|
+
type: zod_1.z
|
|
105
|
+
.enum(["fact", "event", "preference", "skill"])
|
|
106
|
+
.describe("The type of memory to save."),
|
|
107
|
+
content: zod_1.z
|
|
108
|
+
.string()
|
|
109
|
+
.describe("The natural language description of the memory."),
|
|
110
|
+
keywords: zod_1.z
|
|
111
|
+
.array(zod_1.z.string())
|
|
112
|
+
.optional()
|
|
113
|
+
.describe("Optional keywords for retrieval, as an array of strings."),
|
|
114
|
+
importance: zod_1.z
|
|
115
|
+
.number()
|
|
116
|
+
.min(1)
|
|
117
|
+
.max(5)
|
|
118
|
+
.optional()
|
|
119
|
+
.describe("Importance level from 1 to 5. Default is 3."),
|
|
120
|
+
}),
|
|
121
|
+
}, "db");
|
|
122
|
+
// 检索记忆工具
|
|
123
|
+
const memoryRetrieveTool = createNingzhiTool(memory_retrieve_tool_1.memoryRetrieveTool, {
|
|
124
|
+
name: "memory_retrieve",
|
|
125
|
+
description: "Retrieve relevant memories from the database using full-text search. Use this when the user asks about something that may have been remembered before but is not in the current conversation context. Extract a few key keywords from the question and pass them as the query.",
|
|
126
|
+
schema: zod_1.z.object({
|
|
127
|
+
query: zod_1.z
|
|
128
|
+
.array(zod_1.z.string())
|
|
129
|
+
.describe("Keywords to search for in memories."),
|
|
130
|
+
limit: zod_1.z
|
|
131
|
+
.number()
|
|
132
|
+
.min(1)
|
|
133
|
+
.max(50)
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("Maximum number of memories to return. Default is 10."),
|
|
136
|
+
}),
|
|
137
|
+
}, "db");
|
|
138
|
+
// 删除记忆工具
|
|
139
|
+
const memoryDeleteTool = createNingzhiTool(memory_delete_tool_1.memoryDeleteTool, {
|
|
140
|
+
name: "memory_delete",
|
|
141
|
+
description: "Delete a memory from the database by its id. Use this when the user wants to forget or remove a specific memory.",
|
|
142
|
+
schema: zod_1.z.object({
|
|
143
|
+
id: zod_1.z
|
|
144
|
+
.number()
|
|
145
|
+
.int()
|
|
146
|
+
.positive()
|
|
147
|
+
.describe("The id of the memory to delete."),
|
|
148
|
+
}),
|
|
149
|
+
}, "db");
|
|
150
|
+
const profileUpdateTool = createNingzhiTool(profile_update_tool_1.profileUpdateTool, {
|
|
151
|
+
name: "profile_update",
|
|
152
|
+
description: "Update the user's profile information. When providing new profile info, also include all other fields mentioned in <profile_info> — all profile information must be updated together.",
|
|
153
|
+
schema: zod_1.z.object({
|
|
154
|
+
profile_info: zod_1.z
|
|
155
|
+
.string()
|
|
156
|
+
.describe("The complete profile information in markdown format, including all existing and updated fields."),
|
|
157
|
+
}),
|
|
158
|
+
}, "write");
|
|
159
|
+
const agentTool = createNingzhiTool(agent_tool_1.agentTool, {
|
|
160
|
+
name: "agent_tool",
|
|
161
|
+
description: "Launch a sub-agent to execute an independent task. The sub-agent has access to all tools (except this one), skills, memory, and hooks. It will run to completion and return its final result. Use this for tasks that can be delegated, such as research, code generation, or data processing.",
|
|
162
|
+
schema: zod_1.z.object({
|
|
163
|
+
prompt: zod_1.z
|
|
164
|
+
.string()
|
|
165
|
+
.describe("A clear, self-contained task prompt for the sub-agent. It should include all necessary context because the sub-agent cannot see the current conversation history."),
|
|
166
|
+
}),
|
|
167
|
+
}, "exec");
|
|
168
|
+
/**
|
|
169
|
+
* 可能持久化输出,如果输出长度超过最大限制则保存到文件
|
|
170
|
+
* @param {string} content - 要持久化的输出内容
|
|
171
|
+
* @param {string} toolCallId - 工具调用 ID,用于生成文件名
|
|
172
|
+
* @returns {Promise<string>} - 包含持久化信息的 HTML 片段
|
|
173
|
+
*/
|
|
174
|
+
async function maybePersistedOutput(content, toolCallId) {
|
|
175
|
+
const MAX_LENGTH = 50000;
|
|
176
|
+
if (content.length <= MAX_LENGTH) {
|
|
177
|
+
return content;
|
|
178
|
+
}
|
|
179
|
+
// 生成唯一文件名
|
|
180
|
+
const id = toolCallId || Math.random().toString(36).slice(2, 9);
|
|
181
|
+
const dir = (0, node_path_1.resolve)((0, node_os_1.homedir)(), ".ningzhiAgentCli", ".tool_output");
|
|
182
|
+
const filePath = (0, node_path_1.resolve)(dir, `tool_output_${id}.txt`);
|
|
183
|
+
// 确保目录存在
|
|
184
|
+
await (0, promises_1.mkdir)(dir, { recursive: true });
|
|
185
|
+
await (0, promises_1.writeFile)(filePath, content, "utf-8");
|
|
186
|
+
// 返回 提示词 片段
|
|
187
|
+
return `<persisted-output>
|
|
188
|
+
Output too large (${(content.length / 1024).toFixed(1)}KB).
|
|
189
|
+
Full output saved to: ${filePath}
|
|
190
|
+
|
|
191
|
+
If you need the complete content, it is recommended to read it in segments.
|
|
192
|
+
|
|
193
|
+
Preview (first 2KB):
|
|
194
|
+
${content.slice(0, 2000)}
|
|
195
|
+
...
|
|
196
|
+
</persisted-output>`;
|
|
197
|
+
}
|
|
198
|
+
// 导出所有工具
|
|
199
|
+
exports.nativeTools = [
|
|
200
|
+
readFileTool,
|
|
201
|
+
writeFileTool,
|
|
202
|
+
execTool,
|
|
203
|
+
runJsTool,
|
|
204
|
+
runPyTool,
|
|
205
|
+
webSearchTool,
|
|
206
|
+
webFetchTool,
|
|
207
|
+
loadSkillTool,
|
|
208
|
+
memoryCreateTool,
|
|
209
|
+
memoryRetrieveTool,
|
|
210
|
+
memoryDeleteTool,
|
|
211
|
+
profileUpdateTool,
|
|
212
|
+
agentTool,
|
|
213
|
+
];
|
|
214
|
+
exports.tools = [...exports.nativeTools];
|
|
215
|
+
async function initTools() {
|
|
216
|
+
const mcpTools = await (0, mcp_1.initMcpTools)();
|
|
217
|
+
exports.tools = [...exports.nativeTools, ...mcpTools];
|
|
218
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 将时间戳格式化为相对时间的中文描述(如"刚刚"、"5分钟内"、"3小时前")
|
|
3
|
+
* @param ts ISO 格式的时间字符串
|
|
4
|
+
*/
|
|
5
|
+
export declare function formatRelativeTime(ts: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* 截断超长字符串,超出部分以"..."结尾
|
|
8
|
+
* @param str 原始字符串(为空时返回占位符"-")
|
|
9
|
+
* @param maxLen 最大保留长度
|
|
10
|
+
*/
|
|
11
|
+
export declare function truncate(str: string | null, maxLen: number): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatRelativeTime = formatRelativeTime;
|
|
4
|
+
exports.truncate = truncate;
|
|
5
|
+
/**
|
|
6
|
+
* 将时间戳格式化为相对时间的中文描述(如"刚刚"、"5分钟内"、"3小时前")
|
|
7
|
+
* @param ts ISO 格式的时间字符串
|
|
8
|
+
*/
|
|
9
|
+
function formatRelativeTime(ts) {
|
|
10
|
+
// 计算与当前时间的时间差,单位是毫秒
|
|
11
|
+
const diffMs = Date.now() - new Date(ts).getTime();
|
|
12
|
+
const diffMin = Math.floor(diffMs / 60000); // 差值换算为分钟
|
|
13
|
+
const diffHour = Math.floor(diffMin / 60); // 分钟换算为小时
|
|
14
|
+
const diffDay = Math.floor(diffHour / 24); // 小时换算为天数
|
|
15
|
+
// 按时间区间从小到大返回对应的描述文案
|
|
16
|
+
if (diffMin < 1)
|
|
17
|
+
return "刚刚";
|
|
18
|
+
if (diffMin < 5)
|
|
19
|
+
return "1分钟内";
|
|
20
|
+
if (diffMin < 10)
|
|
21
|
+
return "5分钟内";
|
|
22
|
+
if (diffMin < 30)
|
|
23
|
+
return "10分钟内";
|
|
24
|
+
if (diffMin < 60)
|
|
25
|
+
return "30分钟内";
|
|
26
|
+
if (diffHour < 2)
|
|
27
|
+
return "1小时内";
|
|
28
|
+
if (diffHour < 24)
|
|
29
|
+
return `${diffHour}小时内`;
|
|
30
|
+
if (diffDay < 2)
|
|
31
|
+
return "1天前";
|
|
32
|
+
return `${diffDay}天前`;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 截断超长字符串,超出部分以"..."结尾
|
|
36
|
+
* @param str 原始字符串(为空时返回占位符"-")
|
|
37
|
+
* @param maxLen 最大保留长度
|
|
38
|
+
*/
|
|
39
|
+
function truncate(str, maxLen) {
|
|
40
|
+
if (!str)
|
|
41
|
+
return "-";
|
|
42
|
+
return str.length > maxLen ? str.slice(0, maxLen) + "..." : str;
|
|
43
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const node_fs_1 = require("node:fs");
|
|
39
|
+
const node_path_1 = require("node:path");
|
|
40
|
+
const config_1 = require("./agent/config");
|
|
41
|
+
const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(__dirname, "../package.json"), "utf-8"));
|
|
42
|
+
async function main() {
|
|
43
|
+
if (!(0, node_fs_1.existsSync)(config_1.CONFIG_PATH)) {
|
|
44
|
+
const { runInstall } = await Promise.resolve().then(() => __importStar(require("./install")));
|
|
45
|
+
await runInstall();
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const { initColors } = await Promise.resolve().then(() => __importStar(require("./agent/colors")));
|
|
49
|
+
const { initDb } = await Promise.resolve().then(() => __importStar(require("./agent/db")));
|
|
50
|
+
const { initAgent } = await Promise.resolve().then(() => __importStar(require("./agent/agent")));
|
|
51
|
+
const { interactiveChat } = await Promise.resolve().then(() => __importStar(require("./agent/cli")));
|
|
52
|
+
const { shutdownMcp } = await Promise.resolve().then(() => __importStar(require("./agent/mcp")));
|
|
53
|
+
const { checkModel } = await Promise.resolve().then(() => __importStar(require("./agent/model")));
|
|
54
|
+
await initColors();
|
|
55
|
+
const valid = await checkModel();
|
|
56
|
+
if (!valid) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
initDb();
|
|
60
|
+
await initAgent();
|
|
61
|
+
// SIGINT (Signal Interrupt,中断信号, 操作系统发给进程的一种"请你停下来"的信号)
|
|
62
|
+
// 注册一次性的 SIGINT 信号处理(用户按 Ctrl+C 时触发)
|
|
63
|
+
// 如果用户在退出流程进行中 再次 按 Ctrl+C,监听器已经被移除,不会重复执行`shutdownMcp()` ,避免重复关闭造成的错误
|
|
64
|
+
// 先优雅关闭所有 MCP 子进程连接,再正常退出,避免残留子进程(避免子进程变成孤儿进程。)
|
|
65
|
+
process.once("SIGINT", async () => {
|
|
66
|
+
await shutdownMcp();
|
|
67
|
+
process.exit(0);
|
|
68
|
+
});
|
|
69
|
+
const program = new commander_1.Command();
|
|
70
|
+
program.name(pkg.name).description(pkg.description).version(pkg.version);
|
|
71
|
+
// 如果带了命令参数,使用 commander 解析, 否则直接进入交互模式(pnpm dev 的情况)
|
|
72
|
+
if (process.argv.length > 2) {
|
|
73
|
+
program.parse();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
await interactiveChat();
|
|
77
|
+
}
|
|
78
|
+
// 确保在退出前关闭所有 MCP 子进程连接
|
|
79
|
+
await shutdownMcp();
|
|
80
|
+
}
|
|
81
|
+
main();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runInstall(): Promise<void>;
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.runInstall = runInstall;
|
|
37
|
+
const fs = __importStar(require("node:fs"));
|
|
38
|
+
const path = __importStar(require("node:path"));
|
|
39
|
+
const os = __importStar(require("node:os"));
|
|
40
|
+
const node_child_process_1 = require("node:child_process");
|
|
41
|
+
const config_1 = require("./agent/config");
|
|
42
|
+
const db_1 = require("./agent/db");
|
|
43
|
+
// 工作空间第三方 skills 目录
|
|
44
|
+
const SKILLS_DIR = path.join(config_1.WORKSPACE_DIR, ".agents", "skills");
|
|
45
|
+
// 同步执行 shell 命令;stdio: "inherit" 让子进程继承父进程的输入输出流,输出实时打印到终端
|
|
46
|
+
function run(command, options) {
|
|
47
|
+
// `execSync` 启动子进程,"inherit" —— 共享父进程的输入输出流,输出实时打印到终端
|
|
48
|
+
// `stdio` 选项就是控制“子进程的输入输出接到哪里”
|
|
49
|
+
(0, node_child_process_1.execSync)(command, { stdio: "inherit", ...options });
|
|
50
|
+
}
|
|
51
|
+
// 从 GitHub 仓库下载并安装单个 skill 到本地目标目录
|
|
52
|
+
function installSkill(name, // skill 名称
|
|
53
|
+
repo, // GitHub 仓库,如 "vercel-labs/skills"
|
|
54
|
+
skillPath, // 仓库内 skill 所在的子路径
|
|
55
|
+
targetDir, // 工作空间安装的第三方skill目录
|
|
56
|
+
tempDir) {
|
|
57
|
+
// 已存在则跳过,避免重复安装
|
|
58
|
+
if (fs.existsSync(targetDir)) {
|
|
59
|
+
console.log(` ✅ ${name} 已存在,跳过`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console.log(` 📥 正在下载 ${name}...`);
|
|
63
|
+
const tarPath = path.join(tempDir, `${name}.tar.gz`);
|
|
64
|
+
const extractDir = path.join(tempDir, `${name}-extract`);
|
|
65
|
+
fs.mkdirSync(extractDir, { recursive: true });
|
|
66
|
+
try {
|
|
67
|
+
// 下载仓库 main 分支的 tar.gz 压缩包
|
|
68
|
+
run(`curl -fsSL -o "${tarPath}" "https://github.com/${repo}/archive/refs/heads/main.tar.gz"`, { timeout: 30000 });
|
|
69
|
+
// 解压到临时目录
|
|
70
|
+
run(`tar -xzf "${tarPath}" -C "${extractDir}"`, { timeout: 20000 });
|
|
71
|
+
// 压缩包 解压后 顶层目录名 带哈希后缀(如 repo-main),取第一个即实际根目录
|
|
72
|
+
const subDir = fs.readdirSync(extractDir)[0];
|
|
73
|
+
// 把仓库内的 skill 子目录递归拷贝到目标目录
|
|
74
|
+
fs.cpSync(path.join(extractDir, subDir, skillPath), targetDir, {
|
|
75
|
+
recursive: true,
|
|
76
|
+
});
|
|
77
|
+
console.log(` ✅ ${name} 安装完成`);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
// 超时(execSync 被 kill,可能表现为 killed 或 ETIMEDOUT)或网络不通等
|
|
81
|
+
// 一律不阻断初始化,提示后继续;下次运行会因目录不存在而自动重试安装
|
|
82
|
+
const timedOut = error?.killed || error?.code === "ETIMEDOUT";
|
|
83
|
+
console.log(timedOut
|
|
84
|
+
? ` ⏱️ ${name} 下载/安装超时,已跳过`
|
|
85
|
+
: ` ⚠️ ${name} 安装失败(${error?.code ?? error?.message ?? "未知错误"}),已跳过`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// 首次运行初始化:建库、装默认 skills、生成配置模板
|
|
90
|
+
async function runInstall() {
|
|
91
|
+
console.log("🚀 欢迎使用 NingzhiAgentCli!首次使用需要进行初始化配置。\n");
|
|
92
|
+
// 1. 初始化数据库
|
|
93
|
+
console.log("📦 正在初始化数据库...");
|
|
94
|
+
(0, db_1.initDb)();
|
|
95
|
+
console.log("✅ 数据库初始化完成\n");
|
|
96
|
+
// 2. 安装 skills
|
|
97
|
+
console.log("🔧 正在安装默认 skills...");
|
|
98
|
+
fs.mkdirSync(SKILLS_DIR, { recursive: true });
|
|
99
|
+
// 创建系统临时目录,安装结束后在 finally 中清理
|
|
100
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ningzhi-"));
|
|
101
|
+
try {
|
|
102
|
+
installSkill("find-skills", // 要安装的skill名称
|
|
103
|
+
"vercel-labs/skills", // github仓库名称
|
|
104
|
+
"skills/find-skills", // 仓库内 skill 所在的子路径
|
|
105
|
+
path.join(SKILLS_DIR, "find-skills"), tempDir);
|
|
106
|
+
installSkill("skill-creator", "anthropics/skills", "skills/skill-creator", path.join(SKILLS_DIR, "skill-creator"), tempDir);
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
// Windows 下刚写入的文件可能被杀毒软件/索引服务短暂锁定(EBUSY),
|
|
110
|
+
// maxRetries 让 Node 内置重试;清理失败不应中断主流程,故仅提示
|
|
111
|
+
try {
|
|
112
|
+
fs.rmSync(tempDir, {
|
|
113
|
+
recursive: true,
|
|
114
|
+
force: true,
|
|
115
|
+
maxRetries: 5,
|
|
116
|
+
retryDelay: 200,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
console.log(` ⚠️ 临时目录清理失败(可稍后手动删除): ${tempDir}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
console.log("✅ Skills 安装完成\n");
|
|
124
|
+
// 3. 创建配置文件(写入占位符,待用户填入真实密钥)
|
|
125
|
+
console.log("⚙️ 正在创建配置文件...");
|
|
126
|
+
const configTemplate = {
|
|
127
|
+
model: {
|
|
128
|
+
model: "kimi-k2.6",
|
|
129
|
+
apiKey: "your-api-key",
|
|
130
|
+
baseURL: "https://api.moonshot.cn/v1",
|
|
131
|
+
},
|
|
132
|
+
env: {
|
|
133
|
+
TAVILY_API_KEY: "your-tavily-api-key",
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
fs.mkdirSync(path.dirname(config_1.CONFIG_PATH), { recursive: true });
|
|
137
|
+
fs.writeFileSync(config_1.CONFIG_PATH, JSON.stringify(configTemplate, null, 2), "utf-8");
|
|
138
|
+
console.log(`✅ 配置文件已创建: ${config_1.CONFIG_PATH}\n`);
|
|
139
|
+
// 4. 提示用户
|
|
140
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
141
|
+
console.log("🎉 初始化完成!");
|
|
142
|
+
console.log("");
|
|
143
|
+
console.log("⚠️ 请编辑配置文件,填入你的 API 密钥:");
|
|
144
|
+
console.log(` ${config_1.CONFIG_PATH}`);
|
|
145
|
+
console.log("");
|
|
146
|
+
console.log("📖 配置文档: https://chat.ningzhi2.site/getStart");
|
|
147
|
+
console.log("");
|
|
148
|
+
console.log("🔄 修改完成后,请重新运行 ningzhi");
|
|
149
|
+
console.log("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
|
|
150
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ningzhi-agent-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A personal AI Agent like OpenClaw, including tools, skills, memory, hook, sub-agent, MCP sever, etc. Run in the terminal.",
|
|
5
|
+
"type": "commonjs",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"ningzhi": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc && pnpm copy:skills",
|
|
13
|
+
"copy:skills": "node -e \"require('fs').cpSync('src/agent/skills', 'dist/agent/skills', { recursive: true })\"",
|
|
14
|
+
"dev": "ts-node src/index.ts",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"test:watch": "jest --watch",
|
|
18
|
+
"test:coverage": "jest --coverage",
|
|
19
|
+
"clean": "rm -rf dist"
|
|
20
|
+
},
|
|
21
|
+
"keywords": [],
|
|
22
|
+
"author": "柠致",
|
|
23
|
+
"docs": "https://chat.ningzhi2.site/",
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@langchain/core": "1.2.9",
|
|
27
|
+
"@langchain/langgraph": "1.4.12",
|
|
28
|
+
"@langchain/langgraph-checkpoint-sqlite": "1.0.4",
|
|
29
|
+
"@langchain/openai": "1.5.10",
|
|
30
|
+
"@langchain/tavily": "1.2.0",
|
|
31
|
+
"@modelcontextprotocol/sdk": "1.30.0",
|
|
32
|
+
"@types/better-sqlite3": "7.6.13",
|
|
33
|
+
"better-sqlite3": "12.11.1",
|
|
34
|
+
"boxen": "8.0.1",
|
|
35
|
+
"figlet": "1.11.4",
|
|
36
|
+
"chalk": "5.6.2",
|
|
37
|
+
"cli-table3": "0.6.5",
|
|
38
|
+
"commander": "15.0.0",
|
|
39
|
+
"dotenv": "17.4.2",
|
|
40
|
+
"langchain": "1.5.10",
|
|
41
|
+
"zod": "4.4.3"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/jest": "30.0.0",
|
|
45
|
+
"@types/node": "26.2.0",
|
|
46
|
+
"jest": "30.4.2",
|
|
47
|
+
"ts-jest": "29.4.12",
|
|
48
|
+
"ts-node": "10.9.2",
|
|
49
|
+
"typescript": "5.9.3"
|
|
50
|
+
},
|
|
51
|
+
"pnpm": {
|
|
52
|
+
"onlyBuiltDependencies": [
|
|
53
|
+
"better-sqlite3"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|