ai-helper-agent 1.14.2 → 1.14.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/package.json +1 -1
- package/src/auth.js +34 -7
- package/src/config.js +13 -1
- package/src/executor.js +15 -3
- package/src/i18n.js +93 -0
- package/src/locales/en.json +214 -0
- package/src/locales/zh.json +214 -0
- package/src/mcp/client.js +29 -7
- package/src/search.js +34 -8
- package/src/security.js +64 -37
- package/src/server.js +186 -154
- package/src/skill/executor.js +37 -15
- package/src/skill/loader.js +31 -9
- package/src/skill/markdown-loader.js +46 -24
- package/src/trash.js +33 -7
package/package.json
CHANGED
package/src/auth.js
CHANGED
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
// agent/src/auth.js - 配对认证模块
|
|
2
2
|
import crypto from 'crypto';
|
|
3
3
|
import { loadConfig, loadPairings, savePairing } from './config.js';
|
|
4
|
+
import { t as translate } from './i18n.js';
|
|
5
|
+
|
|
6
|
+
// 默认使用 zh 语言(独立调用场景);server.js 调用时会传入 req 的 lang
|
|
7
|
+
let currentLang = 'zh';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 设置 auth 模块当前使用的语言(由 server.js 在请求入口处调用)
|
|
11
|
+
* @param {string} lang - 语言代码('zh' | 'en')
|
|
12
|
+
*/
|
|
13
|
+
export function setAuthLang(lang) {
|
|
14
|
+
if (lang) currentLang = lang;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 翻译辅助:使用当前模块语言或传入的 t 函数
|
|
19
|
+
* @param {string} key - 翻译 key
|
|
20
|
+
* @param {object} [params] - 插值参数
|
|
21
|
+
* @param {Function} [tFn] - 可选的 t 函数(优先使用)
|
|
22
|
+
* @returns {string}
|
|
23
|
+
*/
|
|
24
|
+
function tr(key, params, tFn) {
|
|
25
|
+
if (typeof tFn === 'function') return tFn(key, params);
|
|
26
|
+
return translate(currentLang, key, params);
|
|
27
|
+
}
|
|
4
28
|
|
|
5
29
|
// 当前有效的配对码(定时刷新 + 一次性使用)
|
|
6
30
|
let currentPairCode = null;
|
|
@@ -106,14 +130,17 @@ function verifyToken(token) {
|
|
|
106
130
|
* 安全策略:
|
|
107
131
|
* - 失败锁定:连续失败 PAIR_FAIL_LIMIT 次后临时锁定,并立即轮换配对码使已观测码失效
|
|
108
132
|
* - 一次性使用:配对成功后立即轮换配对码,防止配对码被重复利用
|
|
133
|
+
* @param {string} code - 配对码
|
|
134
|
+
* @param {string} extensionId - 扩展 ID
|
|
135
|
+
* @param {Function} [tFn] - 可选的翻译函数(由 server.js 传入)
|
|
109
136
|
*/
|
|
110
|
-
async function handlePairRequest(code, extensionId) {
|
|
137
|
+
async function handlePairRequest(code, extensionId, tFn) {
|
|
111
138
|
// 锁定期内直接拒绝(不泄露是否处于锁定状态细节)
|
|
112
139
|
if (Date.now() < pairLockUntil) {
|
|
113
|
-
return { success: false, error: '
|
|
140
|
+
return { success: false, error: tr('auth.tooFrequent', undefined, tFn) };
|
|
114
141
|
}
|
|
115
142
|
if (!code || !extensionId) {
|
|
116
|
-
return { success: false, error: '
|
|
143
|
+
return { success: false, error: tr('auth.missingCodeOrExtId', undefined, tFn) };
|
|
117
144
|
}
|
|
118
145
|
if (code.toUpperCase() !== currentPairCode) {
|
|
119
146
|
pairFailCount++;
|
|
@@ -124,7 +151,7 @@ async function handlePairRequest(code, extensionId) {
|
|
|
124
151
|
currentPairCode = generatePairCode();
|
|
125
152
|
console.log('[Agent] 配对失败次数过多,已临时锁定 60s 并轮换配对码');
|
|
126
153
|
}
|
|
127
|
-
return { success: false, error: '
|
|
154
|
+
return { success: false, error: tr('auth.invalidCode', undefined, tFn) };
|
|
128
155
|
}
|
|
129
156
|
// 配对码正确:重置失败计数
|
|
130
157
|
pairFailCount = 0;
|
|
@@ -135,19 +162,19 @@ async function handlePairRequest(code, extensionId) {
|
|
|
135
162
|
// 一次性使用:成功后立即轮换配对码
|
|
136
163
|
currentPairCode = generatePairCode();
|
|
137
164
|
console.log('[Agent] 配对码校验通过,配对码已轮换(一次性使用)');
|
|
138
|
-
return { success: true, token: pairings[extensionId].token, message: '
|
|
165
|
+
return { success: true, token: pairings[extensionId].token, message: tr('auth.alreadyPaired', undefined, tFn) };
|
|
139
166
|
}
|
|
140
167
|
// 生成新 token 并保存
|
|
141
168
|
const token = crypto.randomBytes(32).toString('hex');
|
|
142
169
|
try {
|
|
143
170
|
await savePairing(extensionId, token);
|
|
144
171
|
} catch (err) {
|
|
145
|
-
return { success: false, error:
|
|
172
|
+
return { success: false, error: tr('auth.saveFailed', { message: err.message }, tFn) };
|
|
146
173
|
}
|
|
147
174
|
// 一次性使用:成功后立即轮换配对码
|
|
148
175
|
currentPairCode = generatePairCode();
|
|
149
176
|
console.log('[Agent] 配对成功,配对码已轮换(一次性使用)');
|
|
150
|
-
return { success: true, token, message: '
|
|
177
|
+
return { success: true, token, message: tr('auth.pairSuccess', undefined, tFn) };
|
|
151
178
|
}
|
|
152
179
|
|
|
153
180
|
export {
|
package/src/config.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { homedir } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import { readFileSync, writeFileSync, existsSync, mkdirSync, statSync } from 'fs';
|
|
5
|
+
import { t as translate } from './i18n.js';
|
|
5
6
|
|
|
6
7
|
const AGENT_DIR = join(homedir(), '.ai-helper-agent');
|
|
7
8
|
const CONFIG_FILE = join(AGENT_DIR, 'config.json');
|
|
@@ -32,6 +33,17 @@ let pairingsCacheMtime = 0;
|
|
|
32
33
|
// 写入互斥锁(防止并发写互相覆盖)
|
|
33
34
|
let writeLock = Promise.resolve();
|
|
34
35
|
|
|
36
|
+
// 当前模块使用的语言(由 server.js 在请求入口处设置)
|
|
37
|
+
let currentLang = 'zh';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 设置 config 模块当前使用的语言(由 server.js 在请求入口处调用)
|
|
41
|
+
* @param {string} lang - 语言代码('zh' | 'en')
|
|
42
|
+
*/
|
|
43
|
+
export function setConfigLang(lang) {
|
|
44
|
+
if (lang) currentLang = lang;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
function withWriteLock(fn) {
|
|
36
48
|
const prev = writeLock;
|
|
37
49
|
let release;
|
|
@@ -147,7 +159,7 @@ export async function saveConfig(config) {
|
|
|
147
159
|
*/
|
|
148
160
|
export async function updateConfigField(key, value) {
|
|
149
161
|
if (!VALID_CONFIG_KEYS.includes(key)) {
|
|
150
|
-
throw new Error(
|
|
162
|
+
throw new Error(translate(currentLang, 'error.invalidConfigKey', { key }));
|
|
151
163
|
}
|
|
152
164
|
return withWriteLock(async () => {
|
|
153
165
|
const config = loadConfig();
|
package/src/executor.js
CHANGED
|
@@ -4,6 +4,18 @@ import { existsSync } from 'fs';
|
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import os from 'os';
|
|
6
6
|
import { loadConfig } from './config.js';
|
|
7
|
+
import { t as translate } from './i18n.js';
|
|
8
|
+
|
|
9
|
+
// 当前模块使用的语言(由 server.js 在请求入口处设置)
|
|
10
|
+
let currentLang = 'zh';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 设置 executor 模块当前使用的语言(由 server.js 在请求入口处调用)
|
|
14
|
+
* @param {string} lang - 语言代码('zh' | 'en')
|
|
15
|
+
*/
|
|
16
|
+
export function setExecutorLang(lang) {
|
|
17
|
+
if (lang) currentLang = lang;
|
|
18
|
+
}
|
|
7
19
|
|
|
8
20
|
function getShellForExec() {
|
|
9
21
|
const platform = os.platform();
|
|
@@ -208,7 +220,7 @@ function executeCommand(command, cwd, wsClient, onComplete, collectOutput = fals
|
|
|
208
220
|
entry.stdoutBuf = appendWithLimit(entry.stdoutBuf, str, truncState);
|
|
209
221
|
entry.truncStdout = truncState.truncated;
|
|
210
222
|
if (truncState.truncated) {
|
|
211
|
-
broadcast({ type: 'stdout_truncated', message:
|
|
223
|
+
broadcast({ type: 'stdout_truncated', message: translate(currentLang, 'error.stdoutTruncated'), execId });
|
|
212
224
|
}
|
|
213
225
|
broadcast({ type: 'stdout', data: str, execId });
|
|
214
226
|
});
|
|
@@ -221,7 +233,7 @@ function executeCommand(command, cwd, wsClient, onComplete, collectOutput = fals
|
|
|
221
233
|
entry.stderrBuf = appendWithLimit(entry.stderrBuf, str, truncState);
|
|
222
234
|
entry.truncStderr = truncState.truncated;
|
|
223
235
|
if (truncState.truncated) {
|
|
224
|
-
broadcast({ type: 'stderr_truncated', message:
|
|
236
|
+
broadcast({ type: 'stderr_truncated', message: translate(currentLang, 'error.stderrTruncated'), execId });
|
|
225
237
|
}
|
|
226
238
|
broadcast({ type: 'stderr', data: str, execId });
|
|
227
239
|
});
|
|
@@ -313,7 +325,7 @@ function executeCommandSync(command, cwd) {
|
|
|
313
325
|
if (!resolved) {
|
|
314
326
|
resolved = true;
|
|
315
327
|
killProcess(execId);
|
|
316
|
-
resolve({ execId, exitCode: -1, killed: true, stdout: '', stderr: '', error:
|
|
328
|
+
resolve({ execId, exitCode: -1, killed: true, stdout: '', stderr: '', error: translate(currentLang, 'error.commandTimeout') });
|
|
317
329
|
}
|
|
318
330
|
}, timeout + 5000);
|
|
319
331
|
});
|
package/src/i18n.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// agent/src/i18n.js - 代理端国际化模块
|
|
2
|
+
// 解析 HTTP 请求的 Accept-Language 头,返回对应语言的文案
|
|
3
|
+
// 默认中文(zh),支持英文(en)
|
|
4
|
+
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { join, dirname } from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
|
|
9
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
10
|
+
|
|
11
|
+
// 加载语言包
|
|
12
|
+
const translations = {
|
|
13
|
+
zh: JSON.parse(readFileSync(join(__dirname, 'locales', 'zh.json'), 'utf-8')),
|
|
14
|
+
en: JSON.parse(readFileSync(join(__dirname, 'locales', 'en.json'), 'utf-8')),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEFAULT_LANG = 'zh';
|
|
18
|
+
const SUPPORTED_LANGS = Object.keys(translations);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 从 Accept-Language 头解析最佳匹配语言
|
|
22
|
+
* 支持格式:zh, zh-CN, en-US, zh;q=0.9,en;q=0.8
|
|
23
|
+
* @param {string} [acceptLanguage] - Accept-Language 头的值
|
|
24
|
+
* @returns {string} 匹配的语言代码(zh 或 en)
|
|
25
|
+
*/
|
|
26
|
+
export function parseAcceptLanguage(acceptLanguage) {
|
|
27
|
+
if (!acceptLanguage || typeof acceptLanguage !== 'string') return DEFAULT_LANG;
|
|
28
|
+
|
|
29
|
+
// 解析语言列表并按权重排序
|
|
30
|
+
const langs = acceptLanguage
|
|
31
|
+
.split(',')
|
|
32
|
+
.map(part => {
|
|
33
|
+
const [code, ...params] = part.trim().split(';');
|
|
34
|
+
const qParam = params.find(p => p.trim().startsWith('q='));
|
|
35
|
+
const q = qParam ? parseFloat(qParam.trim().slice(2)) : 1;
|
|
36
|
+
return { code: code.trim().toLowerCase(), q };
|
|
37
|
+
})
|
|
38
|
+
.filter(item => item.code)
|
|
39
|
+
.sort((a, b) => b.q - a.q);
|
|
40
|
+
|
|
41
|
+
for (const { code } of langs) {
|
|
42
|
+
// 精确匹配
|
|
43
|
+
if (SUPPORTED_LANGS.includes(code)) return code;
|
|
44
|
+
// 前缀匹配(如 zh-CN → zh)
|
|
45
|
+
const prefix = code.split('-')[0];
|
|
46
|
+
if (SUPPORTED_LANGS.includes(prefix)) return prefix;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return DEFAULT_LANG;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* 翻译函数
|
|
54
|
+
* @param {string} lang - 语言代码
|
|
55
|
+
* @param {string} key - 翻译键(支持点号分隔,如 'error.fileNotFound')
|
|
56
|
+
* @param {object} [params] - 插值参数(如 { path: '/foo' } 替换 {path})
|
|
57
|
+
* @returns {string} 翻译后的文本,找不到则返回 key
|
|
58
|
+
*/
|
|
59
|
+
export function t(lang, key, params) {
|
|
60
|
+
const dict = translations[lang] || translations[DEFAULT_LANG];
|
|
61
|
+
let value = key
|
|
62
|
+
.split('.')
|
|
63
|
+
.reduce((obj, k) => (obj && typeof obj === 'object' ? obj[k] : undefined), dict);
|
|
64
|
+
|
|
65
|
+
// 回退到默认语言
|
|
66
|
+
if (value === undefined && lang !== DEFAULT_LANG) {
|
|
67
|
+
value = key
|
|
68
|
+
.split('.')
|
|
69
|
+
.reduce((obj, k) => (obj && typeof obj === 'object' ? obj[k] : undefined), translations[DEFAULT_LANG]);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (value === undefined) return key;
|
|
73
|
+
|
|
74
|
+
// 参数插值
|
|
75
|
+
if (params && typeof value === 'string') {
|
|
76
|
+
return value.replace(/\{(\w+)\}/g, (_, name) => (params[name] !== undefined ? params[name] : `{${name}}`));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* 从 HTTP 请求中提取语言并返回翻译函数
|
|
84
|
+
* @param {import('http').IncomingMessage} req - HTTP 请求对象
|
|
85
|
+
* @returns {{ lang: string, t: (key: string, params?: object) => string }}
|
|
86
|
+
*/
|
|
87
|
+
export function getRequestI18n(req) {
|
|
88
|
+
const lang = parseAcceptLanguage(req.headers['accept-language']);
|
|
89
|
+
return {
|
|
90
|
+
lang,
|
|
91
|
+
t: (key, params) => t(lang, key, params),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
{
|
|
2
|
+
"error": {
|
|
3
|
+
"internal": "Internal server error",
|
|
4
|
+
"invalidUrl": "Invalid request URL format",
|
|
5
|
+
"noToken": "No authentication token provided",
|
|
6
|
+
"invalidToken": "Invalid authentication token",
|
|
7
|
+
"operationInProgress": "An operation is already in progress, please try again later",
|
|
8
|
+
"missingPath": "Missing path parameter",
|
|
9
|
+
"missingWorkdir": "Missing workdir parameter",
|
|
10
|
+
"workdirNotAbsolute": "workdir must be an absolute path",
|
|
11
|
+
"workdirIsSystemDir": "Cannot set the Agent system directory as working directory",
|
|
12
|
+
"pathNotAbsolute": "path must be an absolute path",
|
|
13
|
+
"cannotRemoveWorkdir": "Cannot remove the current working directory, please switch to another directory first",
|
|
14
|
+
"pathNotInAllowedList": "This directory is not in the allowed list",
|
|
15
|
+
"fileNotFound": "File does not exist",
|
|
16
|
+
"dirNotFound": "Directory does not exist",
|
|
17
|
+
"pathIsDir": "Path is a directory",
|
|
18
|
+
"pathNotDir": "Path is not a directory",
|
|
19
|
+
"fileOrDirNotFound": "File/directory does not exist",
|
|
20
|
+
"dirExists": "Directory already exists",
|
|
21
|
+
"missingPathOrNewName": "Missing path or newName parameter",
|
|
22
|
+
"newNameHasSeparator": "New name cannot contain path separators",
|
|
23
|
+
"newNameInvalidChars": "New name contains Windows invalid characters (: * ? \" < > |)",
|
|
24
|
+
"missingPathsParam": "Missing paths parameter or empty array",
|
|
25
|
+
"missingPathOrPaths": "Missing path or paths parameter",
|
|
26
|
+
"processNotFound": "Process does not exist or has ended",
|
|
27
|
+
"npmInstallTimeout": "npm install timeout (90s)",
|
|
28
|
+
"updateFailed": "Update failed, please run manually: npm install -g ai-helper-agent@latest",
|
|
29
|
+
"bodyTooLarge": "Request body too large",
|
|
30
|
+
"invalidJson": "Request body is not valid JSON",
|
|
31
|
+
"readRequestFailed": "Failed to read request",
|
|
32
|
+
"invalidMultipart": "Not a valid multipart/form-data request",
|
|
33
|
+
"fileTooLarge": "File too large, exceeds limit",
|
|
34
|
+
"malformedMultipart": "Malformed multipart data format",
|
|
35
|
+
"malformedMultipartHeaders": "Malformed multipart headers format",
|
|
36
|
+
"noFileData": "No file data found",
|
|
37
|
+
"readFileFailed": "Failed to read file",
|
|
38
|
+
"fileSizeExceeded": "File too large (received {received} > {limit})",
|
|
39
|
+
"targetExists": "Target name already exists",
|
|
40
|
+
"missingPathOrDestDir": "Missing path or destDir parameter",
|
|
41
|
+
"sourceNotFound": "Source file/directory does not exist",
|
|
42
|
+
"targetNotDir": "Target is not a valid directory",
|
|
43
|
+
"targetNameExists": "A file/directory with the same name already exists at the target",
|
|
44
|
+
"missingCommand": "Missing command parameter (must be a string)",
|
|
45
|
+
"execDirCheckFailed": "Execution directory validation failed: {reason}",
|
|
46
|
+
"missingTrashId": "Missing trashId parameter",
|
|
47
|
+
"missingName": "Missing name parameter",
|
|
48
|
+
"missingNameAndSteps": "Missing required parameters: name, steps",
|
|
49
|
+
"missingMarkdownOrPrompt": "Missing markdown/prompt parameter",
|
|
50
|
+
"builtinSkillNotEditable": "\"{name}\" is a built-in skill and cannot be edited",
|
|
51
|
+
"missingZipData": "Missing zipData parameter (base64-encoded zip content)",
|
|
52
|
+
"missingUrl": "Missing url parameter",
|
|
53
|
+
"missingId": "Missing required parameter: id",
|
|
54
|
+
"missingIdParam": "Missing id parameter",
|
|
55
|
+
"pathNotAllowed": "Path \"{path}\" is not allowed",
|
|
56
|
+
"pathNotExist": "Path \"{path}\" does not exist",
|
|
57
|
+
"missingIdAndEnabled": "Missing parameters: id, enabled",
|
|
58
|
+
"missingServerIdAndToolName": "Missing parameters: serverId, toolName",
|
|
59
|
+
"notAgentSkill": "This Skill is not an Agent type",
|
|
60
|
+
"missingNameOrResource": "Missing name or resource parameter",
|
|
61
|
+
"resourceNotFound": "Resource \"{resource}\" does not exist",
|
|
62
|
+
"unknownApiPath": "Unknown API path",
|
|
63
|
+
"httpRequiresUrl": "HTTP transport requires a url",
|
|
64
|
+
"stdioRequiresCommand": "stdio transport requires a command",
|
|
65
|
+
"tooFrequent": "Too frequent, please try again in {wait} seconds",
|
|
66
|
+
"fileTooLargeDeclared": "File too large ({declared} > {limit})",
|
|
67
|
+
"fileTooLargeStat": "File too large ({size} > {limit})",
|
|
68
|
+
"contentTooLarge": "Content too large ({size} > {limit})",
|
|
69
|
+
"switchWorkdirFailed": "Failed to switch working directory: {message}",
|
|
70
|
+
"removeAllowedPathFailed": "Failed to remove allowed path: {message}",
|
|
71
|
+
"fileSearchFailed": "File search error: {message}",
|
|
72
|
+
"contentSearchFailed": "Content search error: {message}",
|
|
73
|
+
"downloadFailed": "Download failed: {message}",
|
|
74
|
+
"packFailed": "Pack failed: {message}",
|
|
75
|
+
"parseFailed": "Parse failed: {message}",
|
|
76
|
+
"targetPathNotAllowed": "Target path not allowed: {reason}",
|
|
77
|
+
"moveTargetNotAllowed": "Move target path not allowed: {reason}",
|
|
78
|
+
"execFailed": "Command execution error: {message}",
|
|
79
|
+
"zipImportFailed": "Zip import failed: {message}",
|
|
80
|
+
"urlImportFailed": "URL import failed: {message}",
|
|
81
|
+
"mcpCommandBlocked": "MCP command blocked: {reason}",
|
|
82
|
+
"readResourceFailed": "Failed to read resource: {message}",
|
|
83
|
+
"npmInstallExitCode": "npm install exit code: {code}",
|
|
84
|
+
"invalidConfigKey": "Invalid config key: {key}",
|
|
85
|
+
"stdoutTruncated": "stdout output exceeded 5MB limit, truncated",
|
|
86
|
+
"commandTimeout": "Command timed out",
|
|
87
|
+
"stderrTruncated": "stderr output exceeded 5MB limit, truncated"
|
|
88
|
+
},
|
|
89
|
+
"message": {
|
|
90
|
+
"shuttingDown": "Agent is shutting down...",
|
|
91
|
+
"restarting": "Agent is restarting...",
|
|
92
|
+
"updateSuccess": "Update successful, Agent is restarting...",
|
|
93
|
+
"workdirSwitched": "Working directory switched",
|
|
94
|
+
"removedFromAllowedList": "Removed from allowed list",
|
|
95
|
+
"movedToTrash": "Moved to trash (auto-cleaned after 7 days)",
|
|
96
|
+
"mcpCommandConfirm": "MCP command needs confirmation: {reason}"
|
|
97
|
+
},
|
|
98
|
+
"auth": {
|
|
99
|
+
"pairFailed": "Pairing failed",
|
|
100
|
+
"cannotConnect": "Cannot connect to Agent: {message}",
|
|
101
|
+
"agentNotPaired": "Agent not paired",
|
|
102
|
+
"agentNotConnected": "Agent service not connected, please ensure the agent service is running",
|
|
103
|
+
"unnamedAgent": "Unnamed Agent",
|
|
104
|
+
"tooFrequent": "Pairing attempts too frequent, please try again later",
|
|
105
|
+
"missingCodeOrExtId": "Missing pairing code or extension ID",
|
|
106
|
+
"invalidCode": "Invalid pairing code",
|
|
107
|
+
"alreadyPaired": "Already paired, returning existing token",
|
|
108
|
+
"saveFailed": "Pairing save failed: {message}",
|
|
109
|
+
"pairSuccess": "Pairing successful"
|
|
110
|
+
},
|
|
111
|
+
"exec": {
|
|
112
|
+
"requestTimeout": "Request timeout ({ms}ms)",
|
|
113
|
+
"requestFailed": "Agent request failed: {message}",
|
|
114
|
+
"uploadTimeout": "File upload timeout (60s)",
|
|
115
|
+
"uploadFailed": "File upload failed: {message}"
|
|
116
|
+
},
|
|
117
|
+
"security": {
|
|
118
|
+
"pathInvalid": "Invalid path",
|
|
119
|
+
"forbiddenAgentFile": "Access to agent system files is forbidden",
|
|
120
|
+
"cannotResolvePath": "Cannot resolve path: {message}",
|
|
121
|
+
"pathNotInAllowedDir": "Path is not within allowed directories",
|
|
122
|
+
"pathOutOfRange": "Path points to a location outside the allowed range",
|
|
123
|
+
"pathCheckError": "Path check error: {message}",
|
|
124
|
+
"commandInvalid": "Invalid command",
|
|
125
|
+
"commandBlocked": "High-risk command blocked: \"{command}\"",
|
|
126
|
+
"cmdSudo": "Command requires administrator privileges (sudo)",
|
|
127
|
+
"cmdNpmGlobal": "Global npm package installation",
|
|
128
|
+
"cmdPip": "pip install/uninstall package",
|
|
129
|
+
"cmdChmod777": "Recursively change permissions to 777",
|
|
130
|
+
"cmdRmrf": "Recursively force delete files/directories",
|
|
131
|
+
"cmdGitForcePush": "Git force push",
|
|
132
|
+
"cmdShutdown": "Shutdown or reboot system",
|
|
133
|
+
"cmdHosts": "Modify system hosts file",
|
|
134
|
+
"cmdEval": "Execute command via eval",
|
|
135
|
+
"cmdExecExternalScript": "Execute external script file",
|
|
136
|
+
"cmdExecScriptDirectly": "Execute script file directly",
|
|
137
|
+
"cmdChmodExec": "Modify file execution permission",
|
|
138
|
+
"cmdRmdirS": "Recursively delete directory (rd/rmdir /s)",
|
|
139
|
+
"cmdDelF": "Force delete file (del /f)",
|
|
140
|
+
"cmdIcacls": "Modify file permissions (icacls)",
|
|
141
|
+
"cmdTakeown": "Take file ownership (takeown)",
|
|
142
|
+
"cmdReg": "Modify registry (reg)",
|
|
143
|
+
"cmdNet": "User/group management (net)",
|
|
144
|
+
"cmdSc": "Service management (sc)",
|
|
145
|
+
"cmdTaskkillF": "Force kill process (taskkill /f)"
|
|
146
|
+
},
|
|
147
|
+
"trash": {
|
|
148
|
+
"moveFailed": "Move to trash failed: {message}",
|
|
149
|
+
"entryNotFound": "Trash entry does not exist or has expired",
|
|
150
|
+
"fileNotFound": "Trash file no longer exists",
|
|
151
|
+
"originalPathOccupied": "Original location already has a file: {path}",
|
|
152
|
+
"restoreFailed": "Restore failed: {message}"
|
|
153
|
+
},
|
|
154
|
+
"search": {
|
|
155
|
+
"pathNotExist": "Search path does not exist",
|
|
156
|
+
"pathNotDir": "Search path is not a directory",
|
|
157
|
+
"emptyPattern": "Search content cannot be empty"
|
|
158
|
+
},
|
|
159
|
+
"skill": {
|
|
160
|
+
"fileWritten": "File written: {path}",
|
|
161
|
+
"fileDeleted": "Deleted: {path}",
|
|
162
|
+
"unknownAction": "Unknown action: {action}",
|
|
163
|
+
"commandNeedsConfirm": "Command requires user confirmation: {reason}",
|
|
164
|
+
"unknownTool": "Unknown tool: {toolName}",
|
|
165
|
+
"missingParam": "Missing required parameter: {key}",
|
|
166
|
+
"execError": "Execution error: {message}",
|
|
167
|
+
"execIdNotFound": "Execution ID \"{execId}\" does not exist",
|
|
168
|
+
"validationFailed": "Validation failed: {errors}",
|
|
169
|
+
"saveFailed": "Save failed: {message}",
|
|
170
|
+
"deleteFailed": "Delete failed: {message}",
|
|
171
|
+
"skillNotFound": "Skill \"{name}\" does not exist",
|
|
172
|
+
"missingNameField": "Missing name field",
|
|
173
|
+
"missingPromptContent": "Missing prompt content",
|
|
174
|
+
"missingDescriptionField": "Missing description field",
|
|
175
|
+
"missingHostname": "Missing hostname",
|
|
176
|
+
"forbiddenLocalAddress": "Access to local addresses is forbidden",
|
|
177
|
+
"forbiddenPrivateAddress": "Access to private network addresses is forbidden",
|
|
178
|
+
"dnsEmptyResult": "DNS lookup returned empty: {hostname}",
|
|
179
|
+
"dnsResolvesPrivate": "Domain {hostname} resolves to private address {address}",
|
|
180
|
+
"dnsFailed": "DNS lookup failed: {hostname}",
|
|
181
|
+
"zipNoSkillMd": "SKILL.md file not found in Zip package",
|
|
182
|
+
"invalidSkillName": "Invalid Skill name: \"{name}\" (only letters, digits, underscores, and hyphens are allowed)",
|
|
183
|
+
"targetPathEscape": "Target path escapes the allowed directory, write rejected",
|
|
184
|
+
"importFailed": "Import failed: {message}",
|
|
185
|
+
"invalidUrl": "Invalid URL",
|
|
186
|
+
"urlProtocolNotSupported": "Only http/https protocol URLs are supported",
|
|
187
|
+
"downloadFailed": "Download failed: {message}",
|
|
188
|
+
"stepsMustBeArray": "steps must be a non-empty array",
|
|
189
|
+
"stepMissingId": "Step {index}: missing id",
|
|
190
|
+
"stepMissingTool": "Step {index}: missing tool",
|
|
191
|
+
"conditionNotMet": "Condition not met",
|
|
192
|
+
"executing": "Executing...",
|
|
193
|
+
"execSuccess": "Execution succeeded",
|
|
194
|
+
"execFailed": "Execution failed",
|
|
195
|
+
"skillExecTimeout": "Skill execution timed out (5 minutes)",
|
|
196
|
+
"skillExecPartial": "Skill \"{name}\" execution completed ({count} steps failed)",
|
|
197
|
+
"skillExecDone": "Skill \"{name}\" execution completed ({count} steps)",
|
|
198
|
+
"invalidRedirectUrl": "Invalid redirect URL",
|
|
199
|
+
"protocolNotSupported": "Only http/https protocols are supported",
|
|
200
|
+
"redirectBlocked": "Redirect blocked: {error}",
|
|
201
|
+
"tooManyRedirects": "Too many redirects",
|
|
202
|
+
"downloadTooLarge": "Download exceeds limit of {size} bytes",
|
|
203
|
+
"downloadTimeout": "Download timed out"
|
|
204
|
+
},
|
|
205
|
+
"mcp": {
|
|
206
|
+
"sseRequiresUrl": "SSE transport requires a url",
|
|
207
|
+
"streamableHttpRequiresUrl": "StreamableHTTP transport requires a url",
|
|
208
|
+
"websocketRequiresUrl": "WebSocket transport requires a url",
|
|
209
|
+
"stdioRequiresCommand": "stdio transport requires a command",
|
|
210
|
+
"toolCallError": "Tool call error: {message}",
|
|
211
|
+
"serverNotConnected": "MCP Server \"{name}\" not connected",
|
|
212
|
+
"toolCallFailed": "Tool call failed"
|
|
213
|
+
}
|
|
214
|
+
}
|