@jpssff/vanor 0.1.0
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/README-cn.md +166 -0
- package/README.md +120 -0
- package/base/config.js +162 -0
- package/base/core/compaction.js +58 -0
- package/base/core/harness.js +246 -0
- package/base/core/loop.js +72 -0
- package/base/core/prompt.js +126 -0
- package/base/core/session.js +255 -0
- package/base/events.js +54 -0
- package/base/i18n/index.js +80 -0
- package/base/i18n/locales/en.js +254 -0
- package/base/i18n/locales/zh-CN.js +252 -0
- package/base/llm/index.js +119 -0
- package/base/llm/providers/anthropic.js +147 -0
- package/base/llm/providers/openai.js +155 -0
- package/base/llm/sse.js +27 -0
- package/base/llm/trace.js +64 -0
- package/base/logger.js +57 -0
- package/base/memory/index.js +139 -0
- package/base/security/index.js +77 -0
- package/base/skills/loader.js +297 -0
- package/base/test/cli.test.js +91 -0
- package/base/test/config.test.js +63 -0
- package/base/test/core.test.js +154 -0
- package/base/test/i18n.test.js +32 -0
- package/base/test/loop.test.js +97 -0
- package/base/test/memory.test.js +47 -0
- package/base/test/message.test.js +38 -0
- package/base/test/session.test.js +324 -0
- package/base/test/skills.test.js +236 -0
- package/base/test/statusbar.test.js +143 -0
- package/base/test/tools.test.js +127 -0
- package/base/test/trace.test.js +62 -0
- package/base/test/tui.test.js +242 -0
- package/base/test/utils.test.js +35 -0
- package/base/tools/builtin.js +221 -0
- package/base/tools/index.js +157 -0
- package/base/transport/cli.js +417 -0
- package/base/transport/message.js +81 -0
- package/base/transport/statusbar.js +117 -0
- package/base/transport/tui.js +397 -0
- package/base/utils.js +150 -0
- package/docs/TECH_DESIGN.md +544 -0
- package/index.js +175 -0
- package/package.json +33 -0
package/base/events.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// 全局事件枚举:所有 JSONL 日志的 event 字段唯一定义源。
|
|
2
|
+
// 命名规范 <domain>.<action>,禁止在别处散落字符串字面量。
|
|
3
|
+
|
|
4
|
+
export const EVENTS = Object.freeze({
|
|
5
|
+
agent: {
|
|
6
|
+
turnStart: "agent.turn.start",
|
|
7
|
+
turnEnd: "agent.turn.end",
|
|
8
|
+
turnAborted: "agent.turn.aborted",
|
|
9
|
+
turnMaxIterations: "agent.turn.max_iterations",
|
|
10
|
+
},
|
|
11
|
+
llm: {
|
|
12
|
+
request: "llm.request",
|
|
13
|
+
response: "llm.response",
|
|
14
|
+
retry: "llm.retry",
|
|
15
|
+
error: "llm.error",
|
|
16
|
+
requestDetail: "llm.request_detail",
|
|
17
|
+
responseDetail: "llm.response_detail",
|
|
18
|
+
},
|
|
19
|
+
tool: {
|
|
20
|
+
call: "tool.call",
|
|
21
|
+
result: "tool.result",
|
|
22
|
+
error: "tool.error",
|
|
23
|
+
denied: "tool.denied",
|
|
24
|
+
},
|
|
25
|
+
memory: {
|
|
26
|
+
store: "memory.store",
|
|
27
|
+
retrieve: "memory.retrieve",
|
|
28
|
+
compress: "memory.compress",
|
|
29
|
+
promote: "memory.promote",
|
|
30
|
+
},
|
|
31
|
+
session: {
|
|
32
|
+
start: "session.start",
|
|
33
|
+
resume: "session.resume",
|
|
34
|
+
reset: "session.reset",
|
|
35
|
+
compact: "session.compact",
|
|
36
|
+
},
|
|
37
|
+
skill: {
|
|
38
|
+
load: "skill.load",
|
|
39
|
+
invoke: "skill.invoke",
|
|
40
|
+
save: "skill.save",
|
|
41
|
+
},
|
|
42
|
+
security: {
|
|
43
|
+
approve: "security.approve",
|
|
44
|
+
deny: "security.deny",
|
|
45
|
+
},
|
|
46
|
+
config: {
|
|
47
|
+
load: "config.load",
|
|
48
|
+
change: "config.change",
|
|
49
|
+
},
|
|
50
|
+
error: {
|
|
51
|
+
uncaught: "error.uncaught",
|
|
52
|
+
generic: "error.generic",
|
|
53
|
+
},
|
|
54
|
+
});
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// Lightweight i18n helpers. Keep this dependency-free so Vanor can run anywhere Node.js runs.
|
|
2
|
+
|
|
3
|
+
import en from "./locales/en.js";
|
|
4
|
+
import zhCN from "./locales/zh-CN.js";
|
|
5
|
+
|
|
6
|
+
export const SUPPORTED_LANGUAGES = ["en", "zh-CN"];
|
|
7
|
+
export const LANGUAGE_SETTINGS = ["auto", ...SUPPORTED_LANGUAGES];
|
|
8
|
+
|
|
9
|
+
const LOCALES = {
|
|
10
|
+
en,
|
|
11
|
+
"zh-CN": zhCN,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function normalizeLanguage(value) {
|
|
15
|
+
const raw = String(value || "").trim();
|
|
16
|
+
if (!raw) return "en";
|
|
17
|
+
const first = raw.split(":")[0].split(",")[0];
|
|
18
|
+
const normalized = first.replace(/_/g, "-").replace(/\..*$/, "").replace(/@.*$/, "").toLowerCase();
|
|
19
|
+
if (normalized === "zh" || normalized.startsWith("zh-")) return "zh-CN";
|
|
20
|
+
if (normalized === "en" || normalized.startsWith("en-")) return "en";
|
|
21
|
+
return "en";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function isSupportedLanguageSetting(value) {
|
|
25
|
+
return LANGUAGE_SETTINGS.includes(value);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function intlLocale(intl = globalThis.Intl) {
|
|
29
|
+
try {
|
|
30
|
+
return intl?.DateTimeFormat?.().resolvedOptions?.().locale || "";
|
|
31
|
+
} catch {
|
|
32
|
+
return "";
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function detectLanguage(config = {}, env = process.env, intl = globalThis.Intl) {
|
|
37
|
+
const configured = config.ui?.language || "auto";
|
|
38
|
+
if (configured && configured !== "auto") {
|
|
39
|
+
return { language: normalizeLanguage(configured), source: "config", configured };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
for (const name of ["VANOR_LANG", "LC_ALL", "LC_MESSAGES", "LANGUAGE", "LANG"]) {
|
|
43
|
+
if (env?.[name]) {
|
|
44
|
+
return { language: normalizeLanguage(env[name]), source: name, configured: "auto" };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const locale = intlLocale(intl);
|
|
49
|
+
if (locale) return { language: normalizeLanguage(locale), source: "Intl", configured: "auto" };
|
|
50
|
+
return { language: "en", source: "fallback", configured: "auto" };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function lookup(dict, key) {
|
|
54
|
+
let cur = dict;
|
|
55
|
+
for (const part of key.split(".")) {
|
|
56
|
+
cur = cur?.[part];
|
|
57
|
+
if (cur == null) return undefined;
|
|
58
|
+
}
|
|
59
|
+
return cur;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function interpolate(text, vars = {}) {
|
|
63
|
+
return String(text).replace(/\{([a-zA-Z0-9_]+)\}/g, (_, name) => (vars[name] == null ? "" : String(vars[name])));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function createI18n(languageOrConfig = "en", env = process.env, intl = globalThis.Intl) {
|
|
67
|
+
const detected =
|
|
68
|
+
typeof languageOrConfig === "string"
|
|
69
|
+
? { language: normalizeLanguage(languageOrConfig), source: "explicit", configured: languageOrConfig }
|
|
70
|
+
: detectLanguage(languageOrConfig, env, intl);
|
|
71
|
+
const language = SUPPORTED_LANGUAGES.includes(detected.language) ? detected.language : "en";
|
|
72
|
+
const dict = LOCALES[language] || en;
|
|
73
|
+
|
|
74
|
+
function t(key, vars) {
|
|
75
|
+
const value = lookup(dict, key) ?? lookup(en, key);
|
|
76
|
+
return value == null ? key : interpolate(value, vars);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return { ...detected, language, t };
|
|
80
|
+
}
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
common: {
|
|
3
|
+
exists: "exists",
|
|
4
|
+
missing: "missing",
|
|
5
|
+
},
|
|
6
|
+
config: {
|
|
7
|
+
errors: {
|
|
8
|
+
noDefaultModel: "llm.defaultModel is not configured",
|
|
9
|
+
noProviders: "no llm.providers configured",
|
|
10
|
+
providerMissingBaseURL: 'provider "{name}" is missing baseURL',
|
|
11
|
+
providerMissingApiKey: 'provider "{name}" is missing apiKey (you can use env:VAR)',
|
|
12
|
+
approvalInvalid: "security.approval must be ask | auto | deny",
|
|
13
|
+
languageInvalid: "ui.language must be auto | en | zh-CN",
|
|
14
|
+
defaultModelFormat: 'llm.defaultModel must use "<provider>/<model>": {model}',
|
|
15
|
+
defaultProviderMissing: 'default model references missing provider "{name}"',
|
|
16
|
+
invalidConfig: "Invalid config: {issues}",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
index: {
|
|
20
|
+
configWizard: {
|
|
21
|
+
title: "Configure Vanor LLM",
|
|
22
|
+
hint: "Tip: apiKey can be env:VAR_NAME to avoid storing secrets in config.",
|
|
23
|
+
providerName: "provider name [default]: ",
|
|
24
|
+
type: "type openai/anthropic [openai]: ",
|
|
25
|
+
baseURL: "baseURL [https://api.openai.com/v1]: ",
|
|
26
|
+
apiKey: "apiKey (or env:VAR): ",
|
|
27
|
+
modelId: "model id (e.g. deepseek/deepseek-v4-pro / deepseek/deepseek-v4-flash): ",
|
|
28
|
+
saved: "Saved: {file}",
|
|
29
|
+
defaultModel: "Default model: {model}",
|
|
30
|
+
next: "Run vanor to start chatting.",
|
|
31
|
+
},
|
|
32
|
+
doctor: {
|
|
33
|
+
title: "Vanor doctor",
|
|
34
|
+
configFile: "Config file: {path} [{state}]",
|
|
35
|
+
workspace: "Workspace: {workspace}",
|
|
36
|
+
approval: "Approval mode: {approval}",
|
|
37
|
+
autoRisk: "approval=auto will execute commands automatically; use with care",
|
|
38
|
+
ok: "No config issues found.",
|
|
39
|
+
issues: "Found issues:",
|
|
40
|
+
},
|
|
41
|
+
chat: {
|
|
42
|
+
setupIncomplete: "Configuration is incomplete:",
|
|
43
|
+
runConfig: "Please run: vanor config",
|
|
44
|
+
sessionNotFound: "Session not found: {id}",
|
|
45
|
+
sessionListHint: "Run vanor sessions to list resumable sessions.",
|
|
46
|
+
},
|
|
47
|
+
skills: {
|
|
48
|
+
none: "(no skills)",
|
|
49
|
+
},
|
|
50
|
+
update: "Update with npm: npm i -g @jpssff/vanor@latest",
|
|
51
|
+
help: {
|
|
52
|
+
usage: "Usage: vanor [command]",
|
|
53
|
+
chat: "(no command) start interactive chat",
|
|
54
|
+
config: "config configure LLM API",
|
|
55
|
+
doctor: "doctor diagnose config and environment",
|
|
56
|
+
sessions: "sessions list sessions",
|
|
57
|
+
resume: "resume [id] resume latest or specified session",
|
|
58
|
+
skills: "skills list loaded skills",
|
|
59
|
+
update: "update show update hint",
|
|
60
|
+
version: "version show version",
|
|
61
|
+
},
|
|
62
|
+
unknownCommand: "Unknown command: {cmd} (run vanor help)",
|
|
63
|
+
fatal: "Fatal error: {error}",
|
|
64
|
+
},
|
|
65
|
+
cli: {
|
|
66
|
+
help: {
|
|
67
|
+
text:
|
|
68
|
+
"Commands:\n" +
|
|
69
|
+
" /help show help\n" +
|
|
70
|
+
" /new start a new session\n" +
|
|
71
|
+
" /messages list user messages in the current session\n" +
|
|
72
|
+
" /compact compact context now\n" +
|
|
73
|
+
" /retry retry the last message\n" +
|
|
74
|
+
" /model [query] show or search and switch models\n" +
|
|
75
|
+
" /language [lang] show or switch language (auto | en | zh-CN)\n" +
|
|
76
|
+
" /usage show token usage\n" +
|
|
77
|
+
" /skills list loaded skills\n" +
|
|
78
|
+
" /reload reload config\n" +
|
|
79
|
+
" /auto-run [off] auto-approve actions for this session (off restores)\n" +
|
|
80
|
+
" /exit quit\n" +
|
|
81
|
+
"Interrupt: press Ctrl+C while a turn is running to interrupt it.",
|
|
82
|
+
},
|
|
83
|
+
banner: "Vanor",
|
|
84
|
+
intro: "A general-purpose CLI agent for developers. Type a message to start, /help for commands.",
|
|
85
|
+
restored: "Restored session: {id} ({count} context messages)",
|
|
86
|
+
interruptRequested: "[interrupt requested]",
|
|
87
|
+
goodbye: "Goodbye.",
|
|
88
|
+
error: "[Error] {message}",
|
|
89
|
+
confirmMessage: "Confirm action:",
|
|
90
|
+
confirmPrompt: "Confirm action [y/N] ",
|
|
91
|
+
modelMatched: "Found {count} matching models:",
|
|
92
|
+
chooseModel: "Choose model number [1-{count}] (Enter to cancel) ",
|
|
93
|
+
invalidChoice: "Invalid choice, cancelled.",
|
|
94
|
+
newSession: "Started a new session.",
|
|
95
|
+
compactRunning: "Compacting context...",
|
|
96
|
+
compactDone: "Context compacted.",
|
|
97
|
+
modelChanged: "Switched model: {model}",
|
|
98
|
+
modelSwitchFailed: "Switch failed: {message}",
|
|
99
|
+
searchableModels: "Searchable models:",
|
|
100
|
+
currentModel: "Current model: {model}",
|
|
101
|
+
usage: "prompt {prompt} / completion {completion} / total {total}",
|
|
102
|
+
messagesNone: "(no user messages)",
|
|
103
|
+
messagesHeader: "Time User message",
|
|
104
|
+
skillsNone: "(no skills)",
|
|
105
|
+
skillsTotal: "Total: {count} skills",
|
|
106
|
+
reloadFailed: "Config reload failed: {error}",
|
|
107
|
+
reloadDone: "Config reloaded. Current model: {model}",
|
|
108
|
+
reloadUnchanged: "Config unchanged.",
|
|
109
|
+
approvalRestored: "Restored approval mode: {approval}",
|
|
110
|
+
autoRunEnabled: "Auto-run enabled for this session. Denylisted dangerous commands are still blocked.",
|
|
111
|
+
retryNone: "No message to retry.",
|
|
112
|
+
unknownSlash: "Unknown command /{cmd}. Use /help.",
|
|
113
|
+
maxIterations: "[max iterations reached]",
|
|
114
|
+
aborted: "[interrupted]",
|
|
115
|
+
runtimeError: "[Runtime error] {message}",
|
|
116
|
+
sessionsNone: "(no sessions)",
|
|
117
|
+
sessionRow: "{id} {count} messages {model} {when}",
|
|
118
|
+
language: {
|
|
119
|
+
current: "Language: {language} (configured: {configured}, source: {source})",
|
|
120
|
+
changed: "Language changed to {language} (configured: {configured}, source: {source})",
|
|
121
|
+
invalid: "Unsupported language: {language}. Use auto, en, or zh-CN.",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
harness: {
|
|
125
|
+
busy: "Harness is busy (phase={phase})",
|
|
126
|
+
},
|
|
127
|
+
skills: {
|
|
128
|
+
promptIntro:
|
|
129
|
+
"Available skills (when needed, use read_file or skills.read to read the corresponding SKILL.md and follow it; use skills.write/edit to save new reusable workflows locally):",
|
|
130
|
+
promptItem: "- {name}: {description} (see {path})",
|
|
131
|
+
none: "(no skills)",
|
|
132
|
+
local: "local",
|
|
133
|
+
shared: "shared",
|
|
134
|
+
listItem: "- {name} [{scope}]: {description}\n {path}",
|
|
135
|
+
total: "Total: {count} skills",
|
|
136
|
+
dirCount: "- {dir}: {count}",
|
|
137
|
+
notFound: "Skill not found: {name}",
|
|
138
|
+
localNotFound: "Local skill not found: {name}",
|
|
139
|
+
readFailed: "Failed to read skill: {message}",
|
|
140
|
+
invalidName: "name is required and must be convertible to a safe directory name",
|
|
141
|
+
writeNeedsContent: "write requires content",
|
|
142
|
+
saved: "Skill saved: {name}\n{file}",
|
|
143
|
+
saveFailed: "Failed to save skill: {message}",
|
|
144
|
+
editNeedsOldText: "edit requires old_text",
|
|
145
|
+
oldTextNotFound: "old_text not found",
|
|
146
|
+
oldTextNotUnique: "old_text appears {count} times; it must match uniquely",
|
|
147
|
+
modified: "Skill updated: {name}",
|
|
148
|
+
modifyFailed: "Failed to update skill: {message}",
|
|
149
|
+
deleted: "Skill deleted: {name}",
|
|
150
|
+
deleteFailed: "Failed to delete skill: {message}",
|
|
151
|
+
readNeedsName: "read requires name",
|
|
152
|
+
editNeedsName: "edit requires name",
|
|
153
|
+
removeNeedsName: "remove requires name",
|
|
154
|
+
unknownAction: "Unknown action: {action}",
|
|
155
|
+
toolDescription:
|
|
156
|
+
"Manage Vanor skills. list/read can inspect loaded skills; write/edit/remove only modify local skills under ~/.vanor/skills.",
|
|
157
|
+
paramName: "Skill name; converted to a safe directory name when writing",
|
|
158
|
+
paramDescription: "Optional frontmatter description when writing",
|
|
159
|
+
paramContent: "SKILL.md body or full Markdown when writing",
|
|
160
|
+
paramOldText: "Original text to match uniquely when editing",
|
|
161
|
+
paramNewText: "Replacement text when editing",
|
|
162
|
+
},
|
|
163
|
+
schema: {
|
|
164
|
+
expectedType: "{loc}: expected type {type}",
|
|
165
|
+
enumValue: "{loc}: value must be one of {values}",
|
|
166
|
+
required: '{loc}: missing required field "{field}"',
|
|
167
|
+
},
|
|
168
|
+
security: {
|
|
169
|
+
invalidPath: "invalid path",
|
|
170
|
+
outsideWorkspace: "path is outside workspace {workspace}",
|
|
171
|
+
pathNotFound: "path does not exist",
|
|
172
|
+
notDirectory: "not a directory",
|
|
173
|
+
},
|
|
174
|
+
tools: {
|
|
175
|
+
describeExec: "Run command: {command}",
|
|
176
|
+
describeWriteFile: "Write file: {path}",
|
|
177
|
+
describeEditFile: "Edit file: {path}",
|
|
178
|
+
describeSkills: "Manage skills: {action}{name}",
|
|
179
|
+
describeGeneric: "Call tool {name}",
|
|
180
|
+
unknownTool: "Unknown tool: {name}",
|
|
181
|
+
invalidArgs: "Invalid arguments: {errors}",
|
|
182
|
+
denied: "The operation was denied by the user or policy",
|
|
183
|
+
exception: "Tool exception: {message}",
|
|
184
|
+
},
|
|
185
|
+
builtin: {
|
|
186
|
+
readFileDescription: "Read text content from a workspace file, optionally by line offset/limit.",
|
|
187
|
+
pathDescription: "Relative workspace path or absolute path",
|
|
188
|
+
offsetDescription: "Start line (1-based)",
|
|
189
|
+
limitDescription: "Number of lines to read",
|
|
190
|
+
readFailed: "Read failed: {message}",
|
|
191
|
+
truncatedContent: "[content too long, truncated]",
|
|
192
|
+
emptyFile: "(empty file)",
|
|
193
|
+
writeFileDescription: "Write or overwrite a workspace file and create parent directories automatically.",
|
|
194
|
+
writeFailed: "Write failed: {message}",
|
|
195
|
+
written: "Wrote {path} ({bytes} bytes)",
|
|
196
|
+
editFileDescription: "Replace the unique occurrence of old_string with new_string in a file.",
|
|
197
|
+
oldStringNotFound: "old_string not found",
|
|
198
|
+
oldStringNotUnique: "old_string appears {count} times; it must match uniquely",
|
|
199
|
+
edited: "Edited {path}",
|
|
200
|
+
listDirDescription: "List directory contents.",
|
|
201
|
+
emptyDir: "(empty directory)",
|
|
202
|
+
execDescription: "Run a shell command in the workspace and return its output. Subject to allowlist/denylist and approval.",
|
|
203
|
+
cwdDescription: "Working directory, defaults to workspace root",
|
|
204
|
+
timeoutDescription: "Timeout in milliseconds, defaults to 60000",
|
|
205
|
+
spawnFailed: "Could not start command: {message}",
|
|
206
|
+
commandAborted: "Command aborted",
|
|
207
|
+
execFailed: "Execution failed: {message}",
|
|
208
|
+
outputTruncated: "[output truncated]",
|
|
209
|
+
noOutput: "(no output)",
|
|
210
|
+
exitCode: "[exit code {code}]",
|
|
211
|
+
},
|
|
212
|
+
prompt: {
|
|
213
|
+
persona:
|
|
214
|
+
"You are Vanor, a general-purpose agent by Wanyou Intelligence.\n" +
|
|
215
|
+
"Principles: reason and plan independently, use tools to complete tasks, be concise and considerate, and focus on the user's real intent.\n" +
|
|
216
|
+
"When you need to read or write files or run commands, call the relevant tool directly instead of describing the action first.\n" +
|
|
217
|
+
"Tool results may contain errors (isError); use them to correct your approach and continue. Do not dump raw errors to the user.",
|
|
218
|
+
language: "Default response language: English. If the user explicitly requests another language, follow the user's request.",
|
|
219
|
+
channelCli: "You are interacting with the user through a command line interface (CLI). Default to terminal-friendly output.",
|
|
220
|
+
channelDefault: "Adapt your output format to the user's communication channel so it is clear and easy to read.",
|
|
221
|
+
runtimeTitle: "Runtime environment:",
|
|
222
|
+
os: "- OS: {name} ({platform}/{arch}, kernel {release})",
|
|
223
|
+
node: "- Runtime: Node.js {version}; Vanor Agent is built on Node.js and can call node / npm via exec",
|
|
224
|
+
shell: "- Default shell: {shell}",
|
|
225
|
+
workspace: "- Workspace: {workspace}",
|
|
226
|
+
time: "- Current time: {time} ({timezone}, {offset})",
|
|
227
|
+
contextTitle: "Vanor instance context:",
|
|
228
|
+
currentModel: "- Current model: {model}",
|
|
229
|
+
provider: "- Current provider: {provider}",
|
|
230
|
+
uiLanguage: "- UI language: {language} (configured: {configured}, source: {source})",
|
|
231
|
+
configPath: "- Config file: {path}",
|
|
232
|
+
session: "- Current session: {id}{restored}",
|
|
233
|
+
restored: " (restored from history)",
|
|
234
|
+
approval: "- Tool approval mode: {approval}",
|
|
235
|
+
runtimeRoot: "- Runtime root: {path}",
|
|
236
|
+
sessionsDir: "- Sessions directory: {path}",
|
|
237
|
+
memoryDir: "- Memory directory: {path}",
|
|
238
|
+
localSkillsDir: "- Vanor local skills directory: {path}",
|
|
239
|
+
logsDir: "- Logs directory: {path}",
|
|
240
|
+
skillDirs: "- Skill scan directories: {dirs}",
|
|
241
|
+
skillsCount: "- Loaded skills: {count}",
|
|
242
|
+
answerContext: "- If the user asks about model, config file, session, directories, language, or approval mode, answer directly from this context.",
|
|
243
|
+
noSecrets: "- Do not reveal API keys, Authorization headers, cookies, tokens, private keys, or other secrets. Only say whether a secret is configured or redacted.",
|
|
244
|
+
terminal:
|
|
245
|
+
"Terminal capabilities: TERM={term}, viewport about {cols} columns x {rows} rows, {color}. " +
|
|
246
|
+
"Fit output to the terminal: keep lines under {cols} columns when practical; avoid code blocks / tables wider than the viewport; {ansi}split long content into sections and avoid flooding the screen.",
|
|
247
|
+
colorSupported: "supports ANSI colors / styles{trueColor}",
|
|
248
|
+
colorUnsupported: "does not support color",
|
|
249
|
+
trueColor: " (true color)",
|
|
250
|
+
ansiHint: "you may use ANSI colors to emphasize important points; ",
|
|
251
|
+
historySummary: "# Conversation History Summary\n{summary}",
|
|
252
|
+
communication: "Communication channel: {guide}",
|
|
253
|
+
},
|
|
254
|
+
};
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
common: {
|
|
3
|
+
exists: "存在",
|
|
4
|
+
missing: "缺失",
|
|
5
|
+
},
|
|
6
|
+
config: {
|
|
7
|
+
errors: {
|
|
8
|
+
noDefaultModel: "未配置 llm.defaultModel",
|
|
9
|
+
noProviders: "未配置任何 llm.providers",
|
|
10
|
+
providerMissingBaseURL: 'provider "{name}" 缺少 baseURL',
|
|
11
|
+
providerMissingApiKey: 'provider "{name}" 缺少 apiKey(可用 env: 引用环境变量)',
|
|
12
|
+
approvalInvalid: "security.approval 必须为 ask | auto | deny",
|
|
13
|
+
languageInvalid: "ui.language 必须为 auto | en | zh-CN",
|
|
14
|
+
defaultModelFormat: 'llm.defaultModel 需为 "<provider>/<model>" 形式:{model}',
|
|
15
|
+
defaultProviderMissing: '默认模型引用的 provider "{name}" 不存在',
|
|
16
|
+
invalidConfig: "配置无效:{issues}",
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
index: {
|
|
20
|
+
configWizard: {
|
|
21
|
+
title: "配置 Vanor 大模型",
|
|
22
|
+
hint: "提示:apiKey 可填 env:VAR_NAME 以引用环境变量,避免明文入库。",
|
|
23
|
+
providerName: "provider 名称 [default]: ",
|
|
24
|
+
type: "类型 openai/anthropic [openai]: ",
|
|
25
|
+
baseURL: "baseURL [https://api.openai.com/v1]: ",
|
|
26
|
+
apiKey: "apiKey(或 env:VAR): ",
|
|
27
|
+
modelId: "模型 id(如 deepseek/deepseek-v4-pro / deepseek/deepseek-v4-flash): ",
|
|
28
|
+
saved: "已保存:{file}",
|
|
29
|
+
defaultModel: "默认模型:{model}",
|
|
30
|
+
next: "运行 vanor 开始对话。",
|
|
31
|
+
},
|
|
32
|
+
doctor: {
|
|
33
|
+
title: "Vanor doctor",
|
|
34
|
+
configFile: "配置文件: {path} [{state}]",
|
|
35
|
+
workspace: "工作区: {workspace}",
|
|
36
|
+
approval: "审批模式: {approval}",
|
|
37
|
+
autoRisk: "approval=auto 将自动执行命令,注意风险",
|
|
38
|
+
ok: "未发现配置问题。",
|
|
39
|
+
issues: "发现以下问题:",
|
|
40
|
+
},
|
|
41
|
+
chat: {
|
|
42
|
+
setupIncomplete: "尚未完成配置:",
|
|
43
|
+
runConfig: "请先运行: vanor config",
|
|
44
|
+
sessionNotFound: "未找到会话:{id}",
|
|
45
|
+
sessionListHint: "可运行 vanor sessions 查看可恢复的会话。",
|
|
46
|
+
},
|
|
47
|
+
skills: {
|
|
48
|
+
none: "(无技能)",
|
|
49
|
+
},
|
|
50
|
+
update: "通过 npm 更新: npm i -g @jpssff/vanor@latest",
|
|
51
|
+
help: {
|
|
52
|
+
usage: "用法: vanor [命令]",
|
|
53
|
+
chat: "(无命令) 启动交互对话",
|
|
54
|
+
config: "config 配置大模型 API",
|
|
55
|
+
doctor: "doctor 诊断配置与环境",
|
|
56
|
+
sessions: "sessions 列出历史会话",
|
|
57
|
+
resume: "resume [id] 恢复最近或指定会话",
|
|
58
|
+
skills: "skills 列出已加载技能",
|
|
59
|
+
update: "update 更新提示",
|
|
60
|
+
version: "version 显示版本",
|
|
61
|
+
},
|
|
62
|
+
unknownCommand: "未知命令:{cmd}(vanor help 查看用法)",
|
|
63
|
+
fatal: "致命错误:{error}",
|
|
64
|
+
},
|
|
65
|
+
cli: {
|
|
66
|
+
help: {
|
|
67
|
+
text:
|
|
68
|
+
"命令:\n" +
|
|
69
|
+
" /help 显示帮助\n" +
|
|
70
|
+
" /new 开启新会话\n" +
|
|
71
|
+
" /messages 查看当前会话的历史用户消息\n" +
|
|
72
|
+
" /compact 立即压缩上下文\n" +
|
|
73
|
+
" /retry 重发上一条消息\n" +
|
|
74
|
+
" /model [关键词] 查看或搜索切换模型\n" +
|
|
75
|
+
" /language [语言] 查看或切换语言(auto | en | zh-CN)\n" +
|
|
76
|
+
" /usage 查看本会话 token 用量\n" +
|
|
77
|
+
" /skills 列出已加载技能\n" +
|
|
78
|
+
" /reload 立即重载配置文件\n" +
|
|
79
|
+
" /auto-run [off] 本次对话自动执行操作,不再逐次确认(off 恢复)\n" +
|
|
80
|
+
" /exit 退出\n" +
|
|
81
|
+
"中断:对话进行中按 Ctrl+C 可中断当前任务。",
|
|
82
|
+
},
|
|
83
|
+
banner: "Vanor 万佑",
|
|
84
|
+
intro: "一个面向开发者的通用 CLI 智能体。输入消息开始对话,/help 查看命令。",
|
|
85
|
+
restored: "已恢复会话:{id}({count} 条上下文消息)",
|
|
86
|
+
interruptRequested: "[已请求中断]",
|
|
87
|
+
goodbye: "再见。",
|
|
88
|
+
error: "[错误] {message}",
|
|
89
|
+
confirmMessage: "确认操作:",
|
|
90
|
+
confirmPrompt: "确认操作 [y/N] ",
|
|
91
|
+
modelMatched: "匹配到 {count} 个模型:",
|
|
92
|
+
chooseModel: "选择模型编号 [1-{count}](回车取消) ",
|
|
93
|
+
invalidChoice: "选择无效,已取消。",
|
|
94
|
+
newSession: "已开启新会话。",
|
|
95
|
+
compactRunning: "正在压缩上下文…",
|
|
96
|
+
compactDone: "已压缩上下文。",
|
|
97
|
+
modelChanged: "已切换模型:{model}",
|
|
98
|
+
modelSwitchFailed: "切换失败:{message}",
|
|
99
|
+
searchableModels: "可搜索的模型:",
|
|
100
|
+
currentModel: "当前模型:{model}",
|
|
101
|
+
usage: "prompt {prompt} / completion {completion} / total {total}",
|
|
102
|
+
messagesNone: "(暂无用户消息)",
|
|
103
|
+
messagesHeader: "时间 用户内容",
|
|
104
|
+
skillsNone: "(无技能)",
|
|
105
|
+
skillsTotal: "总计:{count} 个技能",
|
|
106
|
+
reloadFailed: "配置重载失败:{error}",
|
|
107
|
+
reloadDone: "配置已重载。当前模型:{model}",
|
|
108
|
+
reloadUnchanged: "配置未变化。",
|
|
109
|
+
approvalRestored: "已恢复审批模式:{approval}",
|
|
110
|
+
autoRunEnabled: "已开启自动执行:本次对话不再逐次确认操作(denylist 危险命令仍会拦截)。",
|
|
111
|
+
retryNone: "无可重发的消息。",
|
|
112
|
+
unknownSlash: "未知命令 /{cmd},/help 查看帮助。",
|
|
113
|
+
maxIterations: "[达到最大迭代次数]",
|
|
114
|
+
aborted: "[已中断]",
|
|
115
|
+
runtimeError: "[运行错误] {message}",
|
|
116
|
+
sessionsNone: "(暂无会话)",
|
|
117
|
+
sessionRow: "{id} {count} 条 {model} {when}",
|
|
118
|
+
language: {
|
|
119
|
+
current: "语言:{language}(配置:{configured},来源:{source})",
|
|
120
|
+
changed: "语言已切换为 {language}(配置:{configured},来源:{source})",
|
|
121
|
+
invalid: "不支持的语言:{language}。请使用 auto、en 或 zh-CN。",
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
harness: {
|
|
125
|
+
busy: "Harness 忙碌中(phase={phase})",
|
|
126
|
+
},
|
|
127
|
+
skills: {
|
|
128
|
+
promptIntro: "可用技能(需要时用 read_file 或 skills.read 读取对应 SKILL.md 后按说明执行;如需沉淀新流程,可用 skills.write/edit 保存到本地):",
|
|
129
|
+
promptItem: "- {name}:{description}(详见 {path})",
|
|
130
|
+
none: "(无技能)",
|
|
131
|
+
local: "local",
|
|
132
|
+
shared: "shared",
|
|
133
|
+
listItem: "- {name} [{scope}]:{description}\n {path}",
|
|
134
|
+
total: "总计:{count} 个技能",
|
|
135
|
+
dirCount: "- {dir}:{count}",
|
|
136
|
+
notFound: "未找到技能:{name}",
|
|
137
|
+
localNotFound: "未找到本地技能:{name}",
|
|
138
|
+
readFailed: "读取技能失败:{message}",
|
|
139
|
+
invalidName: "name 不能为空,且需能转换为安全目录名",
|
|
140
|
+
writeNeedsContent: "write 需要 content",
|
|
141
|
+
saved: "技能已保存:{name}\n{file}",
|
|
142
|
+
saveFailed: "保存技能失败:{message}",
|
|
143
|
+
editNeedsOldText: "edit 需要 old_text",
|
|
144
|
+
oldTextNotFound: "未找到 old_text",
|
|
145
|
+
oldTextNotUnique: "old_text 出现 {count} 次,需唯一匹配",
|
|
146
|
+
modified: "技能已修改:{name}",
|
|
147
|
+
modifyFailed: "修改技能失败:{message}",
|
|
148
|
+
deleted: "技能已删除:{name}",
|
|
149
|
+
deleteFailed: "删除技能失败:{message}",
|
|
150
|
+
readNeedsName: "read 需要 name",
|
|
151
|
+
editNeedsName: "edit 需要 name",
|
|
152
|
+
removeNeedsName: "remove 需要 name",
|
|
153
|
+
unknownAction: "未知 action:{action}",
|
|
154
|
+
toolDescription: "管理 Vanor 技能。list/read 可查看已加载技能;write/edit/remove 只修改 ~/.vanor/skills 下的本地技能。",
|
|
155
|
+
paramName: "技能名;写入时会转换为安全目录名",
|
|
156
|
+
paramDescription: "write 时可写入 frontmatter description",
|
|
157
|
+
paramContent: "write 时的 SKILL.md 正文或完整 Markdown",
|
|
158
|
+
paramOldText: "edit 时用于唯一匹配的原文",
|
|
159
|
+
paramNewText: "edit 时替换为的新文本",
|
|
160
|
+
},
|
|
161
|
+
schema: {
|
|
162
|
+
expectedType: "{loc}: 期望类型 {type}",
|
|
163
|
+
enumValue: "{loc}: 取值须为 {values} 之一",
|
|
164
|
+
required: '{loc}: 缺少必填字段 "{field}"',
|
|
165
|
+
},
|
|
166
|
+
security: {
|
|
167
|
+
invalidPath: "路径无效",
|
|
168
|
+
outsideWorkspace: "路径超出工作区 {workspace}",
|
|
169
|
+
pathNotFound: "路径不存在",
|
|
170
|
+
notDirectory: "不是目录",
|
|
171
|
+
},
|
|
172
|
+
tools: {
|
|
173
|
+
describeExec: "运行命令:{command}",
|
|
174
|
+
describeWriteFile: "写入文件:{path}",
|
|
175
|
+
describeEditFile: "编辑文件:{path}",
|
|
176
|
+
describeSkills: "管理技能:{action}{name}",
|
|
177
|
+
describeGeneric: "调用工具 {name}",
|
|
178
|
+
unknownTool: "未知工具:{name}",
|
|
179
|
+
invalidArgs: "参数错误:{errors}",
|
|
180
|
+
denied: "用户或策略拒绝了该操作",
|
|
181
|
+
exception: "工具异常:{message}",
|
|
182
|
+
},
|
|
183
|
+
builtin: {
|
|
184
|
+
readFileDescription: "读取工作区内文件的文本内容,可选按行 offset/limit 截取。",
|
|
185
|
+
pathDescription: "相对工作区或绝对路径",
|
|
186
|
+
offsetDescription: "起始行(从 1 开始)",
|
|
187
|
+
limitDescription: "读取行数",
|
|
188
|
+
readFailed: "读取失败:{message}",
|
|
189
|
+
truncatedContent: "[内容过长,已截断]",
|
|
190
|
+
emptyFile: "(空文件)",
|
|
191
|
+
writeFileDescription: "写入/覆盖工作区内文件,自动创建父目录。",
|
|
192
|
+
writeFailed: "写入失败:{message}",
|
|
193
|
+
written: "已写入 {path}({bytes} 字节)",
|
|
194
|
+
editFileDescription: "在文件中将唯一出现的 old_string 替换为 new_string。",
|
|
195
|
+
oldStringNotFound: "未找到 old_string",
|
|
196
|
+
oldStringNotUnique: "old_string 出现 {count} 次,需唯一匹配",
|
|
197
|
+
edited: "已编辑 {path}",
|
|
198
|
+
listDirDescription: "列出目录内容。",
|
|
199
|
+
emptyDir: "(空目录)",
|
|
200
|
+
execDescription: "在工作区执行 shell 命令并返回输出。受 allowlist/denylist 与审批约束。",
|
|
201
|
+
cwdDescription: "工作目录,默认工作区根",
|
|
202
|
+
timeoutDescription: "超时毫秒,默认 60000",
|
|
203
|
+
spawnFailed: "无法启动命令:{message}",
|
|
204
|
+
commandAborted: "命令被中断",
|
|
205
|
+
execFailed: "执行失败:{message}",
|
|
206
|
+
outputTruncated: "[输出已截断]",
|
|
207
|
+
noOutput: "(无输出)",
|
|
208
|
+
exitCode: "[退出码 {code}]",
|
|
209
|
+
},
|
|
210
|
+
prompt: {
|
|
211
|
+
persona:
|
|
212
|
+
"你是 Vanor(万佑),万佑智算的通用智能体。\n" +
|
|
213
|
+
"原则:自主推理与规划,调用工具完成任务;输出简洁、高情商,始终从用户真实意图出发,不啰嗦、不复述。\n" +
|
|
214
|
+
"当需要读写文件或执行命令时,直接调用相应工具,不要先用文字描述将要做的操作。\n" +
|
|
215
|
+
"工具结果可能含错误(isError),据此自行纠正后继续,不要把原始错误直接抛给用户。",
|
|
216
|
+
language: "默认回复语言:简体中文。如果用户明确要求其它语言,请遵循用户请求。",
|
|
217
|
+
channelCli: "你正通过命令行(CLI)与用户交互:默认输出适合终端的内容格式。",
|
|
218
|
+
channelDefault: "请根据用户所在的通讯渠道调整输出格式,使其在该渠道上清晰、易读。",
|
|
219
|
+
runtimeTitle: "运行环境:",
|
|
220
|
+
os: "- 操作系统:{name}({platform}/{arch},内核 {release})",
|
|
221
|
+
node: "- 运行时:Node.js {version};Vanor Agent 基于 Node.js 构建,可通过 exec 调用 node / npm 等工具",
|
|
222
|
+
shell: "- 默认 Shell:{shell}",
|
|
223
|
+
workspace: "- 工作区:{workspace}",
|
|
224
|
+
time: "- 当前时间:{time}({timezone},{offset})",
|
|
225
|
+
contextTitle: "Vanor 实例上下文:",
|
|
226
|
+
currentModel: "- 当前模型:{model}",
|
|
227
|
+
provider: "- 当前 provider:{provider}",
|
|
228
|
+
uiLanguage: "- UI 语言:{language}(配置:{configured},来源:{source})",
|
|
229
|
+
configPath: "- 配置文件:{path}",
|
|
230
|
+
session: "- 当前会话:{id}{restored}",
|
|
231
|
+
restored: "(从历史恢复)",
|
|
232
|
+
approval: "- 工具审批模式:{approval}",
|
|
233
|
+
runtimeRoot: "- 运行时根目录:{path}",
|
|
234
|
+
sessionsDir: "- 会话目录:{path}",
|
|
235
|
+
memoryDir: "- 记忆目录:{path}",
|
|
236
|
+
localSkillsDir: "- Vanor 本地技能目录:{path}",
|
|
237
|
+
logsDir: "- 日志目录:{path}",
|
|
238
|
+
skillDirs: "- 技能扫描目录:{dirs}",
|
|
239
|
+
skillsCount: "- 已加载技能数:{count}",
|
|
240
|
+
answerContext: "- 如果用户询问模型、配置文件、会话、目录、语言或审批模式,可直接依据本上下文回答。",
|
|
241
|
+
noSecrets: "- 不要输出 API Key、Authorization、Cookie、token、私钥等敏感配置;涉及密钥时只说明是否已配置或已脱敏。",
|
|
242
|
+
terminal:
|
|
243
|
+
"终端能力:TERM={term},可视区约 {cols} 列 × {rows} 行,{color}。" +
|
|
244
|
+
"输出请适应终端:单行尽量不超过 {cols} 列,代码块 / 表格不要超出宽度;{ansi}较长内容分段,避免一次性刷屏。",
|
|
245
|
+
colorSupported: "支持 ANSI 颜色 / 样式{trueColor}",
|
|
246
|
+
colorUnsupported: "不支持颜色",
|
|
247
|
+
trueColor: "(真彩色)",
|
|
248
|
+
ansiHint: "可用 ANSI 颜色强调重点;",
|
|
249
|
+
historySummary: "# 对话历史摘要\n{summary}",
|
|
250
|
+
communication: "通讯方式:{guide}",
|
|
251
|
+
},
|
|
252
|
+
};
|