zhitalk 0.0.0 → 0.0.3
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 +98 -0
- package/README.md +36 -1
- package/dist/agent/agent.d.ts +24 -0
- package/dist/agent/agent.js +345 -0
- package/dist/agent/cli.d.ts +1 -0
- package/dist/agent/cli.js +185 -0
- package/dist/agent/colors.d.ts +19 -0
- package/dist/agent/colors.js +81 -0
- package/dist/agent/commands.d.ts +7 -0
- package/dist/agent/commands.js +69 -0
- package/dist/agent/config.d.ts +38 -0
- package/dist/agent/config.js +116 -0
- package/dist/agent/context.d.ts +3 -0
- package/dist/agent/context.js +107 -0
- package/dist/agent/db.d.ts +21 -0
- package/dist/agent/db.js +159 -0
- package/dist/agent/hooks.d.ts +27 -0
- package/dist/agent/hooks.js +115 -0
- package/dist/agent/mcp/client.d.ts +25 -0
- package/dist/agent/mcp/client.js +113 -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 +25 -0
- package/dist/agent/model.d.ts +5 -0
- package/dist/agent/model.js +31 -0
- package/dist/agent/permission/dangerous-path.json +115 -0
- package/dist/agent/permission/exec.d.ts +11 -0
- package/dist/agent/permission/exec.js +34 -0
- package/dist/agent/permission/is-dangerous-path.d.ts +1 -0
- package/dist/agent/permission/is-dangerous-path.js +53 -0
- package/dist/agent/permission/is-safe-domains.d.ts +1 -0
- package/dist/agent/permission/is-safe-domains.js +142 -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 +160 -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 +50 -0
- package/dist/agent/skills.d.ts +8 -0
- package/dist/agent/skills.js +91 -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 +28 -0
- package/dist/agent/tools/load_skill_tool.d.ts +3 -0
- package/dist/agent/tools/load_skill_tool.js +15 -0
- package/dist/agent/tools/memory_create_tool.d.ts +6 -0
- package/dist/agent/tools/memory_create_tool.js +37 -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 +24 -0
- package/dist/agent/tools/profile_update_tool.d.ts +3 -0
- package/dist/agent/tools/profile_update_tool.js +50 -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 +36 -0
- package/dist/agent/tools/run_py_tool.d.ts +3 -0
- package/dist/agent/tools/run_py_tool.js +27 -0
- package/dist/agent/tools/search.d.ts +3 -0
- package/dist/agent/tools/search.js +10 -0
- package/dist/agent/tools/web_fetch_tool.d.ts +3 -0
- package/dist/agent/tools/web_fetch_tool.js +33 -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 +8 -0
- package/dist/agent/tools.js +193 -0
- package/dist/agent/utils.d.ts +2 -0
- package/dist/agent/utils.js +30 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +72 -0
- package/dist/install.d.ts +1 -0
- package/dist/install.js +108 -0
- package/package.json +43 -6
- package/index.js +0 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkExecPermission = checkExecPermission;
|
|
4
|
+
const util_1 = require("./util");
|
|
5
|
+
function checkExecPermission(toolCall) {
|
|
6
|
+
const command = toolCall.args?.command;
|
|
7
|
+
if (typeof command !== 'string') {
|
|
8
|
+
return { action: 'confirm' };
|
|
9
|
+
}
|
|
10
|
+
if ((0, util_1.isSafeCommand)(command)) {
|
|
11
|
+
return { action: 'allow' };
|
|
12
|
+
}
|
|
13
|
+
if ((0, util_1.isChangingDirectory)(command)) {
|
|
14
|
+
return {
|
|
15
|
+
action: 'block',
|
|
16
|
+
reason: `命令包含切换目录操作,为防止目录逃逸,禁止执行。如有需要请手动操作。`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const scriptCheck = (0, util_1.isScriptExecution)(command);
|
|
20
|
+
if (scriptCheck.blocked) {
|
|
21
|
+
return {
|
|
22
|
+
action: 'block',
|
|
23
|
+
reason: scriptCheck.reason,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const dangerCheck = (0, util_1.isDangerousOperation)(command);
|
|
27
|
+
if (dangerCheck.blocked) {
|
|
28
|
+
return {
|
|
29
|
+
action: 'block',
|
|
30
|
+
reason: dangerCheck.reason,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
return { action: 'confirm' };
|
|
34
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isDangerousPath(filepath: string): boolean;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isDangerousPath = isDangerousPath;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const dangerous_path_json_1 = __importDefault(require("./dangerous-path.json"));
|
|
10
|
+
function expandPath(filepath) {
|
|
11
|
+
let expanded = filepath;
|
|
12
|
+
if (expanded.startsWith('~')) {
|
|
13
|
+
expanded = path_1.default.join(os_1.default.homedir(), expanded.slice(1));
|
|
14
|
+
}
|
|
15
|
+
expanded = expanded.replace(/%USERPROFILE%/gi, os_1.default.homedir());
|
|
16
|
+
expanded = expanded.replace(/%APPDATA%/gi, process.env.APPDATA || path_1.default.join(os_1.default.homedir(), 'AppData', 'Roaming'));
|
|
17
|
+
expanded = expanded.replace(/%LOCALAPPDATA%/gi, process.env.LOCALAPPDATA || path_1.default.join(os_1.default.homedir(), 'AppData', 'Local'));
|
|
18
|
+
if (!path_1.default.isAbsolute(expanded)) {
|
|
19
|
+
expanded = path_1.default.resolve(expanded);
|
|
20
|
+
}
|
|
21
|
+
expanded = path_1.default.normalize(expanded);
|
|
22
|
+
return expanded;
|
|
23
|
+
}
|
|
24
|
+
function globToRegex(pattern) {
|
|
25
|
+
const escaped = pattern
|
|
26
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
27
|
+
.replace(/\*/g, '[^\\\\/]*');
|
|
28
|
+
return new RegExp(`^${escaped}(?:[\\\\/].*)?$`);
|
|
29
|
+
}
|
|
30
|
+
function isDangerousPath(filepath) {
|
|
31
|
+
const platform = process.platform === 'win32'
|
|
32
|
+
? 'windows'
|
|
33
|
+
: process.platform === 'darwin'
|
|
34
|
+
? 'macos'
|
|
35
|
+
: 'linux';
|
|
36
|
+
const dangerousList = dangerous_path_json_1.default[platform];
|
|
37
|
+
const expandedInput = expandPath(filepath);
|
|
38
|
+
for (const dangerous of dangerousList) {
|
|
39
|
+
const expandedDangerous = expandPath(dangerous);
|
|
40
|
+
if (dangerous.includes('*')) {
|
|
41
|
+
const regex = globToRegex(expandedDangerous);
|
|
42
|
+
if (regex.test(expandedInput))
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (expandedInput === expandedDangerous)
|
|
47
|
+
return true;
|
|
48
|
+
if (expandedInput.startsWith(expandedDangerous + path_1.default.sep))
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isSafeDomain(url: string): boolean;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isSafeDomain = isSafeDomain;
|
|
4
|
+
const commonDomainsCNForDevelopers = [
|
|
5
|
+
// 搜索 / AI
|
|
6
|
+
'google.com',
|
|
7
|
+
'baidu.com',
|
|
8
|
+
'bing.com',
|
|
9
|
+
'openai.com',
|
|
10
|
+
'claude.ai',
|
|
11
|
+
'anthropic.com',
|
|
12
|
+
'perplexity.ai',
|
|
13
|
+
'kimi.moonshot.cn',
|
|
14
|
+
'doubao.com',
|
|
15
|
+
'deepseek.com',
|
|
16
|
+
// 代码托管 / 开发平台
|
|
17
|
+
'github.com',
|
|
18
|
+
'gitlab.com',
|
|
19
|
+
'gitee.com',
|
|
20
|
+
'bitbucket.org',
|
|
21
|
+
// 技术社区
|
|
22
|
+
'stackoverflow.com',
|
|
23
|
+
'v2ex.com',
|
|
24
|
+
'zhihu.com',
|
|
25
|
+
'juejin.cn',
|
|
26
|
+
'csdn.net',
|
|
27
|
+
'oschina.net',
|
|
28
|
+
'segmentfault.com',
|
|
29
|
+
'infoq.cn',
|
|
30
|
+
'51cto.com',
|
|
31
|
+
'cnblogs.com',
|
|
32
|
+
'linux.do',
|
|
33
|
+
'reddit.com',
|
|
34
|
+
'medium.com',
|
|
35
|
+
'dev.to',
|
|
36
|
+
'hackernoon.com',
|
|
37
|
+
// 文档 / API / 开源
|
|
38
|
+
'npmjs.com',
|
|
39
|
+
'nodejs.org',
|
|
40
|
+
'python.org',
|
|
41
|
+
'pypi.org',
|
|
42
|
+
'rust-lang.org',
|
|
43
|
+
'golang.org',
|
|
44
|
+
'docker.com',
|
|
45
|
+
'kubernetes.io',
|
|
46
|
+
'mozilla.org',
|
|
47
|
+
'developer.mozilla.org',
|
|
48
|
+
'w3.org',
|
|
49
|
+
'react.dev',
|
|
50
|
+
'nextjs.org',
|
|
51
|
+
'vuejs.org',
|
|
52
|
+
'nuxt.com',
|
|
53
|
+
'tailwindcss.com',
|
|
54
|
+
'vercel.com',
|
|
55
|
+
'supabase.com',
|
|
56
|
+
'cloudflare.com',
|
|
57
|
+
// 云服务 / DevOps
|
|
58
|
+
'aliyun.com',
|
|
59
|
+
'tencent.com',
|
|
60
|
+
'huaweicloud.com',
|
|
61
|
+
'aws.amazon.com',
|
|
62
|
+
'azure.com',
|
|
63
|
+
'googleapis.com',
|
|
64
|
+
'firebase.google.com',
|
|
65
|
+
'netlify.com',
|
|
66
|
+
'railway.app',
|
|
67
|
+
'render.com',
|
|
68
|
+
// CI/CD / 工程工具
|
|
69
|
+
'jenkins.io',
|
|
70
|
+
'sonarqube.org',
|
|
71
|
+
'sentry.io',
|
|
72
|
+
'postman.com',
|
|
73
|
+
'swagger.io',
|
|
74
|
+
'openapi.org',
|
|
75
|
+
// 数据库 / 中间件
|
|
76
|
+
'mysql.com',
|
|
77
|
+
'postgresql.org',
|
|
78
|
+
'redis.io',
|
|
79
|
+
'mongodb.com',
|
|
80
|
+
'sqlite.org',
|
|
81
|
+
'apache.org',
|
|
82
|
+
'elastic.co',
|
|
83
|
+
'rabbitmq.com',
|
|
84
|
+
'kafka.apache.org',
|
|
85
|
+
// 办公 / 协作
|
|
86
|
+
'feishu.cn',
|
|
87
|
+
'dingtalk.com',
|
|
88
|
+
'yuque.com',
|
|
89
|
+
'notion.so',
|
|
90
|
+
'atlassian.com',
|
|
91
|
+
'jira.com',
|
|
92
|
+
'confluence.com',
|
|
93
|
+
'slack.com',
|
|
94
|
+
'trello.com',
|
|
95
|
+
// 下载 / 软件
|
|
96
|
+
'jetbrains.com',
|
|
97
|
+
'visualstudio.com',
|
|
98
|
+
'microsoft.com',
|
|
99
|
+
'apple.com',
|
|
100
|
+
'homebrew.sh',
|
|
101
|
+
'iterm2.com',
|
|
102
|
+
// 中国互联网公司
|
|
103
|
+
'qq.com',
|
|
104
|
+
'wechat.com',
|
|
105
|
+
'weibo.com',
|
|
106
|
+
'bilibili.com',
|
|
107
|
+
'douyin.com',
|
|
108
|
+
'xiaohongshu.com',
|
|
109
|
+
'taobao.com',
|
|
110
|
+
'tmall.com',
|
|
111
|
+
'jd.com',
|
|
112
|
+
'aliyun.com',
|
|
113
|
+
// 学习 / 视频
|
|
114
|
+
'youtube.com',
|
|
115
|
+
'coursera.org',
|
|
116
|
+
'udemy.com',
|
|
117
|
+
'leetcode.com',
|
|
118
|
+
'nowcoder.com',
|
|
119
|
+
'geeksforgeeks.org',
|
|
120
|
+
// AI / Agent / 自动化
|
|
121
|
+
'langchain.com',
|
|
122
|
+
'langgraph.dev',
|
|
123
|
+
'huggingface.co',
|
|
124
|
+
'replicate.com',
|
|
125
|
+
'ollama.com',
|
|
126
|
+
'openrouter.ai',
|
|
127
|
+
'tavily.com',
|
|
128
|
+
'smith.langchain.com',
|
|
129
|
+
];
|
|
130
|
+
function isSafeDomain(url) {
|
|
131
|
+
let hostname;
|
|
132
|
+
try {
|
|
133
|
+
hostname = new URL(url).hostname.toLowerCase();
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
return commonDomainsCNForDevelopers.some((domain) => {
|
|
139
|
+
const d = domain.toLowerCase();
|
|
140
|
+
return hostname === d || hostname.endsWith('.' + d);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkNetworkPermission = checkNetworkPermission;
|
|
4
|
+
const is_safe_domains_1 = require("./is-safe-domains");
|
|
5
|
+
function checkNetworkPermission(toolCall) {
|
|
6
|
+
const url = toolCall.args?.url;
|
|
7
|
+
if (typeof url !== 'string') {
|
|
8
|
+
return { action: 'allow' };
|
|
9
|
+
}
|
|
10
|
+
if ((0, is_safe_domains_1.isSafeDomain)(url)) {
|
|
11
|
+
return { action: 'allow' };
|
|
12
|
+
}
|
|
13
|
+
return { action: 'confirm' };
|
|
14
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkReadPermission = checkReadPermission;
|
|
4
|
+
const is_dangerous_path_1 = require("./is-dangerous-path");
|
|
5
|
+
function checkReadPermission(toolCall) {
|
|
6
|
+
const filepath = toolCall.args?.filepath;
|
|
7
|
+
if (filepath == null) {
|
|
8
|
+
return { action: 'allow' };
|
|
9
|
+
}
|
|
10
|
+
if ((0, is_dangerous_path_1.isDangerousPath)(filepath)) {
|
|
11
|
+
return {
|
|
12
|
+
action: 'block',
|
|
13
|
+
reason: `路径 "${filepath}" 是系统敏感目录,为保护系统安全,禁止访问。如有需要请手动操作。`,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return { action: 'allow' };
|
|
17
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare function isInProjectDir(filepath: string): boolean;
|
|
2
|
+
export declare function isChangingDirectory(command: string): boolean;
|
|
3
|
+
export declare function isScriptExecution(command: string): {
|
|
4
|
+
blocked: boolean;
|
|
5
|
+
reason?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function isSafeCommand(command: string): boolean;
|
|
8
|
+
export declare function isDangerousOperation(command: string): {
|
|
9
|
+
blocked: boolean;
|
|
10
|
+
reason?: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isInProjectDir = isInProjectDir;
|
|
7
|
+
exports.isChangingDirectory = isChangingDirectory;
|
|
8
|
+
exports.isScriptExecution = isScriptExecution;
|
|
9
|
+
exports.isSafeCommand = isSafeCommand;
|
|
10
|
+
exports.isDangerousOperation = isDangerousOperation;
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const config_1 = require("../config");
|
|
13
|
+
function isInProjectDir(filepath) {
|
|
14
|
+
const resolved = path_1.default.resolve(filepath);
|
|
15
|
+
const cwd = path_1.default.resolve(process.cwd());
|
|
16
|
+
if (resolved === cwd || resolved.startsWith(cwd + path_1.default.sep)) {
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
const zhitalkDir = config_1.WORKSPACE_DIR;
|
|
20
|
+
return resolved === zhitalkDir || resolved.startsWith(zhitalkDir + path_1.default.sep);
|
|
21
|
+
}
|
|
22
|
+
function isChangingDirectory(command) {
|
|
23
|
+
return /(?:^|[\s;|&])(?:cd|chdir|pushd|popd)(?:\s|$)/i.test(command);
|
|
24
|
+
}
|
|
25
|
+
const PYTHON_TOOLS = ['python', 'python3', 'python2'];
|
|
26
|
+
const JS_TOOLS = ['node', 'nodejs', 'tsx', 'ts-node'];
|
|
27
|
+
const OTHER_LANG_TOOLS = [
|
|
28
|
+
'java', 'javac', 'dotnet', 'go', 'cargo', 'rustc',
|
|
29
|
+
'ruby', 'php', 'perl', 'lua', 'rscript',
|
|
30
|
+
];
|
|
31
|
+
const BLOCKED_EXTENSIONS = [
|
|
32
|
+
'.py', '.js', '.ts', '.mjs', '.cjs',
|
|
33
|
+
'.java', '.cs', '.go', '.rb', '.rs', '.php', '.pl', '.lua',
|
|
34
|
+
];
|
|
35
|
+
function getFirstToken(command) {
|
|
36
|
+
const trimmed = command.trim();
|
|
37
|
+
const match = trimmed.match(/^([^\s]+)/);
|
|
38
|
+
return match ? match[1] : trimmed;
|
|
39
|
+
}
|
|
40
|
+
function isScriptExecution(command) {
|
|
41
|
+
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
42
|
+
for (const sub of subCommands) {
|
|
43
|
+
const token = getFirstToken(sub);
|
|
44
|
+
const tool = path_1.default.basename(token).toLowerCase();
|
|
45
|
+
const ext = path_1.default.extname(token).toLowerCase();
|
|
46
|
+
if (PYTHON_TOOLS.includes(tool) || ext === '.py') {
|
|
47
|
+
return { blocked: true, reason: '检测到 Python 脚本执行,请使用 run_py tool 执行 Python 代码。' };
|
|
48
|
+
}
|
|
49
|
+
if (JS_TOOLS.includes(tool) || ['.js', '.ts', '.mjs', '.cjs'].includes(ext)) {
|
|
50
|
+
return { blocked: true, reason: '检测到 JavaScript/TypeScript 脚本执行,请使用 run_js tool 执行 JavaScript/TypeScript 代码。' };
|
|
51
|
+
}
|
|
52
|
+
if (OTHER_LANG_TOOLS.includes(tool) || BLOCKED_EXTENSIONS.includes(ext)) {
|
|
53
|
+
return { blocked: true, reason: `检测到 ${tool || ext} 脚本执行,exec tool 仅允许执行 shell/bash/sh 脚本。` };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { blocked: false };
|
|
57
|
+
}
|
|
58
|
+
const DANGEROUS_COMMANDS = [
|
|
59
|
+
'sudo', 'doas',
|
|
60
|
+
'rm', 'rmdir', 'unlink', 'shred',
|
|
61
|
+
'del', 'erase', 'rd',
|
|
62
|
+
'remove-item', 'clear-item',
|
|
63
|
+
'mv', 'move', 'rename', 'ren',
|
|
64
|
+
'cp', 'copy', 'xcopy', 'robocopy',
|
|
65
|
+
'touch', 'dd', 'truncate', 'tee',
|
|
66
|
+
'mkdir', 'md', 'mkfifo', 'mknod',
|
|
67
|
+
'new-item',
|
|
68
|
+
'chmod', 'chown', 'chgrp', 'umask', 'setfacl', 'chflags',
|
|
69
|
+
'icacls', 'cacls', 'takeown', 'attrib',
|
|
70
|
+
'kill', 'killall', 'pkill', 'xkill',
|
|
71
|
+
'systemctl', 'service', 'launchctl',
|
|
72
|
+
'init', 'shutdown', 'reboot', 'halt', 'poweroff',
|
|
73
|
+
'crontab', 'at', 'batch',
|
|
74
|
+
'taskkill',
|
|
75
|
+
'sc', 'net', 'netsh',
|
|
76
|
+
'stop-process', 'start-process', 'restart-computer',
|
|
77
|
+
'useradd', 'usermod', 'userdel',
|
|
78
|
+
'groupadd', 'groupmod', 'groupdel',
|
|
79
|
+
'passwd', 'chsh', 'chfn', 'chage',
|
|
80
|
+
'su', 'runas',
|
|
81
|
+
'dscl', 'dseditgroup',
|
|
82
|
+
'env', 'printenv', 'history',
|
|
83
|
+
'ssh', 'scp', 'sftp', 'ftp', 'telnet',
|
|
84
|
+
'nc', 'netcat', 'ncat',
|
|
85
|
+
'curl', 'wget',
|
|
86
|
+
'nmap', 'masscan', 'zmap',
|
|
87
|
+
'iptables', 'ufw', 'firewall-cmd', 'nft',
|
|
88
|
+
'socat', 'proxychains',
|
|
89
|
+
];
|
|
90
|
+
const SHELL_COMMAND_SWITCH = /(?:bash|sh|zsh|cmd|powershell)\s+(?:-c\s+(".*?"|'.*?'|\S+)|\/c\s+(".*?"|'.*?'|\S+)|-Command\s+(".*?"|'.*?'|\S+))/i;
|
|
91
|
+
function unquote(str) {
|
|
92
|
+
if ((str.startsWith('"') && str.endsWith('"')) || (str.startsWith("'") && str.endsWith("'"))) {
|
|
93
|
+
return str.slice(1, -1);
|
|
94
|
+
}
|
|
95
|
+
return str;
|
|
96
|
+
}
|
|
97
|
+
function extractShellSubCommand(command) {
|
|
98
|
+
const match = command.match(SHELL_COMMAND_SWITCH);
|
|
99
|
+
if (!match)
|
|
100
|
+
return null;
|
|
101
|
+
return unquote(match[1] || match[2] || match[3] || '');
|
|
102
|
+
}
|
|
103
|
+
function hasOutputRedirect(command) {
|
|
104
|
+
return /(^|[\s;|&])\d?>>?\s/.test(command);
|
|
105
|
+
}
|
|
106
|
+
const SAFE_COMMANDS = [
|
|
107
|
+
'ls', 'pwd', 'cat', 'head', 'tail', 'grep', 'find',
|
|
108
|
+
'echo', 'date', 'whoami', 'id', 'uname', 'wc', 'sort', 'uniq',
|
|
109
|
+
'diff', 'which', 'whereis', 'file', 'stat', 'df', 'du',
|
|
110
|
+
'ps', 'pgrep', 'pstree', 'uptime', 'hostname',
|
|
111
|
+
'printf', 'readlink', 'realpath', 'tput', 'clear', 'reset',
|
|
112
|
+
'seq', 'yes', 'true', 'false',
|
|
113
|
+
'dir', 'type', 'findstr', 'more', 'tree', 'ver', 'vol',
|
|
114
|
+
'gci', 'gc', 'gl', 'sls', 'write',
|
|
115
|
+
];
|
|
116
|
+
const SAFE_GIT_SUBCOMMANDS = ['status', 'diff', 'log'];
|
|
117
|
+
function getSecondToken(command) {
|
|
118
|
+
const tokens = command.trim().split(/\s+/);
|
|
119
|
+
return tokens[1] || null;
|
|
120
|
+
}
|
|
121
|
+
function isSafeCommand(command) {
|
|
122
|
+
if (hasOutputRedirect(command))
|
|
123
|
+
return false;
|
|
124
|
+
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
125
|
+
for (const sub of subCommands) {
|
|
126
|
+
const token = getFirstToken(sub);
|
|
127
|
+
const tool = path_1.default.basename(token).toLowerCase();
|
|
128
|
+
if (SAFE_COMMANDS.includes(tool)) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (tool === 'git') {
|
|
132
|
+
const subCommand = getSecondToken(sub)?.toLowerCase();
|
|
133
|
+
if (subCommand && SAFE_GIT_SUBCOMMANDS.includes(subCommand)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function isDangerousOperation(command) {
|
|
142
|
+
const subCommand = extractShellSubCommand(command);
|
|
143
|
+
if (subCommand) {
|
|
144
|
+
const result = isDangerousOperation(subCommand);
|
|
145
|
+
if (result.blocked)
|
|
146
|
+
return result;
|
|
147
|
+
}
|
|
148
|
+
const subCommands = command.split(/[;|&]+/).filter(Boolean);
|
|
149
|
+
for (const sub of subCommands) {
|
|
150
|
+
const token = getFirstToken(sub);
|
|
151
|
+
const tool = path_1.default.basename(token).toLowerCase();
|
|
152
|
+
if (DANGEROUS_COMMANDS.includes(tool)) {
|
|
153
|
+
return { blocked: true, reason: `检测到危险操作 "${tool}",exec tool 禁止执行该命令。如有需要请手动操作。` };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (hasOutputRedirect(command)) {
|
|
157
|
+
return { blocked: true, reason: '检测到输出重定向,exec tool 禁止修改文件系统。如有需要请手动操作。' };
|
|
158
|
+
}
|
|
159
|
+
return { blocked: false };
|
|
160
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkWritePermission = checkWritePermission;
|
|
4
|
+
const is_dangerous_path_1 = require("./is-dangerous-path");
|
|
5
|
+
const util_1 = require("./util");
|
|
6
|
+
function checkWritePermission(toolCall) {
|
|
7
|
+
const filepath = toolCall.args?.filepath;
|
|
8
|
+
if (filepath == null) {
|
|
9
|
+
return { action: 'allow' };
|
|
10
|
+
}
|
|
11
|
+
if ((0, is_dangerous_path_1.isDangerousPath)(filepath)) {
|
|
12
|
+
return {
|
|
13
|
+
action: 'block',
|
|
14
|
+
reason: `路径 "${filepath}" 是系统敏感目录,为保护系统安全,禁止访问。如有需要请手动操作。`,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if ((0, util_1.isInProjectDir)(filepath)) {
|
|
18
|
+
return { action: 'allow' };
|
|
19
|
+
}
|
|
20
|
+
return { action: 'confirm' };
|
|
21
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const systemPrompt: string;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.systemPrompt = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const skills_1 = require("./skills");
|
|
10
|
+
const config_1 = require("./config");
|
|
11
|
+
// ── Skills ────────────────────────────────────────────────
|
|
12
|
+
(0, skills_1.discoverSkills)();
|
|
13
|
+
const skillsText = (0, skills_1.getSkillsListText)();
|
|
14
|
+
const basePrompt = 'You are zhitalk, 中文名字叫智语, a personal AI Agent like OpenClaw, including tools, skills, memory, hook, sub-agent, MCP server, etc. Run in the terminal.';
|
|
15
|
+
function readProfile() {
|
|
16
|
+
try {
|
|
17
|
+
const filePath = path_1.default.join(config_1.WORKSPACE_DIR, '.data', 'profile.md');
|
|
18
|
+
const content = fs_1.default.readFileSync(filePath, 'utf-8').trim();
|
|
19
|
+
return `<profile_info>${content}</profile_info>`;
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return '<profile_info></profile_info>';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const profilePrompt = `## User Profile
|
|
26
|
+
|
|
27
|
+
When you learn information about the user that fits the following categories, store it as profile information for future personalization.
|
|
28
|
+
|
|
29
|
+
<profile_template>
|
|
30
|
+
- Basic identity: name, nickname, gender, age, region, language
|
|
31
|
+
- Appearance: height, weight, skin tone, body type
|
|
32
|
+
- Personality and communication preferences
|
|
33
|
+
- Hobbies and interests
|
|
34
|
+
- Skills
|
|
35
|
+
- Work / occupation
|
|
36
|
+
</profile_template>
|
|
37
|
+
|
|
38
|
+
Here is the user's current profile information:
|
|
39
|
+
${readProfile()}`;
|
|
40
|
+
const memoryPrompt = `## Memory Management Rules
|
|
41
|
+
|
|
42
|
+
- When deleting a memory, first use memory_retrieve to find its id, then call memory_delete with that id. If no matching memory is found, politely inform the user.
|
|
43
|
+
- When updating a memory, first delete the old memory, then create a new one.
|
|
44
|
+
- When information fits the categories in <profile_template>, do not record it as a memory. It will be stored in the profile file instead.`;
|
|
45
|
+
const dateTimePrompt = `## Current DateTime
|
|
46
|
+
|
|
47
|
+
${new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' })}`;
|
|
48
|
+
exports.systemPrompt = skillsText
|
|
49
|
+
? `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n## Available Skills\n\nYou have access to the following skills. When a user's request matches a skill's description, you MUST call the \`load_skill\` tool to load that skill's full instructions, then follow them.\n\n${skillsText}\n\n${dateTimePrompt}`
|
|
50
|
+
: `${basePrompt}\n\n${profilePrompt}\n\n${memoryPrompt}\n\n${dateTimePrompt}`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface SkillInfo {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
dirPath: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function discoverSkills(): SkillInfo[];
|
|
7
|
+
export declare function loadSkill(name: string): string | null;
|
|
8
|
+
export declare function getSkillsListText(): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.discoverSkills = discoverSkills;
|
|
4
|
+
exports.loadSkill = loadSkill;
|
|
5
|
+
exports.getSkillsListText = getSkillsListText;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const os_1 = require("os");
|
|
9
|
+
const config_1 = require("./config");
|
|
10
|
+
const skills = [];
|
|
11
|
+
const skillContentMap = new Map();
|
|
12
|
+
function parseFrontmatter(content) {
|
|
13
|
+
if (!content.startsWith('---')) {
|
|
14
|
+
return { name: '', description: '' };
|
|
15
|
+
}
|
|
16
|
+
const endIdx = content.indexOf('---', 3);
|
|
17
|
+
if (endIdx === -1) {
|
|
18
|
+
return { name: '', description: '' };
|
|
19
|
+
}
|
|
20
|
+
const frontmatter = content.slice(3, endIdx).trim();
|
|
21
|
+
const lines = frontmatter.split('\n');
|
|
22
|
+
const result = {};
|
|
23
|
+
for (const line of lines) {
|
|
24
|
+
const colonIdx = line.indexOf(':');
|
|
25
|
+
if (colonIdx === -1)
|
|
26
|
+
continue;
|
|
27
|
+
const key = line.slice(0, colonIdx).trim();
|
|
28
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
29
|
+
result[key] = value;
|
|
30
|
+
}
|
|
31
|
+
return { name: result.name || '', description: result.description || '' };
|
|
32
|
+
}
|
|
33
|
+
function discoverSkills() {
|
|
34
|
+
skills.length = 0;
|
|
35
|
+
skillContentMap.clear();
|
|
36
|
+
const skillDirs = [
|
|
37
|
+
(0, path_1.join)((0, os_1.homedir)(), '.agents', 'skills'),
|
|
38
|
+
(0, path_1.join)(config_1.WORKSPACE_DIR, '.agents', 'skills'),
|
|
39
|
+
(0, path_1.join)(config_1.WORKSPACE_DIR, 'skills'),
|
|
40
|
+
];
|
|
41
|
+
for (const skillsDir of skillDirs) {
|
|
42
|
+
let entries;
|
|
43
|
+
try {
|
|
44
|
+
entries = (0, fs_1.readdirSync)(skillsDir);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
const entryPath = (0, path_1.join)(skillsDir, entry);
|
|
51
|
+
try {
|
|
52
|
+
const entryStat = (0, fs_1.statSync)(entryPath);
|
|
53
|
+
if (!entryStat.isDirectory())
|
|
54
|
+
continue;
|
|
55
|
+
const skillMdPath = (0, path_1.join)(entryPath, 'SKILL.md');
|
|
56
|
+
const content = (0, fs_1.readFileSync)(skillMdPath, 'utf-8');
|
|
57
|
+
const { name, description } = parseFrontmatter(content);
|
|
58
|
+
if (name && description) {
|
|
59
|
+
const existing = skills.find((s) => s.name === name);
|
|
60
|
+
if (existing) {
|
|
61
|
+
existing.description = description;
|
|
62
|
+
existing.dirPath = entryPath;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
skills.push({ name, description, dirPath: entryPath });
|
|
66
|
+
}
|
|
67
|
+
skillContentMap.set(name, content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Ignore directories without SKILL.md or parse errors
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return skills;
|
|
76
|
+
}
|
|
77
|
+
function loadSkill(name) {
|
|
78
|
+
return skillContentMap.get(name) ?? null;
|
|
79
|
+
}
|
|
80
|
+
function getSkillsListText() {
|
|
81
|
+
const lines = skills.map((s) => `- **${s.name}**: ${s.description}`);
|
|
82
|
+
if (skills.length > 0) {
|
|
83
|
+
lines.push('');
|
|
84
|
+
lines.push('skills 的目录:');
|
|
85
|
+
lines.push((0, path_1.join)((0, os_1.homedir)(), '.agents', 'skills'));
|
|
86
|
+
lines.push((0, path_1.join)(config_1.WORKSPACE_DIR, '.agents', 'skills'));
|
|
87
|
+
lines.push((0, path_1.join)(config_1.WORKSPACE_DIR, 'skills'));
|
|
88
|
+
lines.push(`如果增加新 skill,必须放在 ${(0, path_1.join)(config_1.WORKSPACE_DIR, 'skills')} 目录下`);
|
|
89
|
+
}
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|