@keyou007/agent-bot 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/README.md +14 -1
  3. package/README.zh.md +14 -1
  4. package/dist/cli/FeishuAppConfiguration.js +10 -10
  5. package/dist/cli/FeishuAppConfiguration.js.map +1 -1
  6. package/dist/cli/FeishuAppRegistration.js +9 -9
  7. package/dist/cli/FeishuAppRegistration.js.map +1 -1
  8. package/dist/cli/Initializer.js +64 -5
  9. package/dist/cli/Initializer.js.map +1 -1
  10. package/dist/cli/LocalControlClient.js +2 -2
  11. package/dist/cli/LocalControlClient.js.map +1 -1
  12. package/dist/cli/LocalControlServer.js +1 -1
  13. package/dist/cli/LocalControlServer.js.map +1 -1
  14. package/dist/cli/ServerStarter.js +3 -3
  15. package/dist/cli/ServerStarter.js.map +1 -1
  16. package/dist/cli/SkillRegistry.js +4 -4
  17. package/dist/cli/SkillRegistry.js.map +1 -1
  18. package/dist/cli/help.js +9 -57
  19. package/dist/cli/help.js.map +1 -1
  20. package/dist/cli/initOptions.js +24 -0
  21. package/dist/cli/initOptions.js.map +1 -0
  22. package/dist/cli/packageVersion.js +1 -1
  23. package/dist/cli/packageVersion.js.map +1 -1
  24. package/dist/cli/profile.js +3 -3
  25. package/dist/cli/profile.js.map +1 -1
  26. package/dist/cli/serverStatus.js +37 -0
  27. package/dist/cli/serverStatus.js.map +1 -0
  28. package/dist/cli/taskChatRoute.js +2 -2
  29. package/dist/cli/taskChatRoute.js.map +1 -1
  30. package/dist/cli.js +154 -136
  31. package/dist/cli.js.map +1 -1
  32. package/dist/feishu/CardRenderer.js +1 -1
  33. package/dist/feishu/CardRenderer.js.map +1 -1
  34. package/dist/feishu/transport.js +1 -1
  35. package/dist/feishu/transport.js.map +1 -1
  36. package/dist/index.js +8 -4
  37. package/dist/index.js.map +1 -1
  38. package/dist/proxy/ProxySessionController.js +10 -10
  39. package/dist/proxy/ProxySessionController.js.map +1 -1
  40. package/dist/startup/StartupNotifier.js +1 -0
  41. package/dist/startup/StartupNotifier.js.map +1 -1
  42. package/docs/technical-reference.md +5 -1
  43. package/docs/technical-reference.zh.md +5 -1
  44. package/npm-shrinkwrap.json +2 -2
  45. package/package.json +1 -1
  46. package/skills/agent-bot/SKILL.md +3 -1
  47. package/src/cli/FeishuAppConfiguration.ts +10 -10
  48. package/src/cli/FeishuAppRegistration.ts +9 -9
  49. package/src/cli/Initializer.ts +76 -6
  50. package/src/cli/LocalControlClient.ts +2 -2
  51. package/src/cli/LocalControlServer.ts +1 -1
  52. package/src/cli/ServerStarter.ts +3 -3
  53. package/src/cli/SkillRegistry.ts +4 -4
  54. package/src/cli/help.ts +9 -66
  55. package/src/cli/initOptions.ts +33 -0
  56. package/src/cli/packageVersion.ts +1 -1
  57. package/src/cli/profile.ts +3 -3
  58. package/src/cli/serverStatus.ts +39 -0
  59. package/src/cli/taskChatRoute.ts +2 -2
  60. package/src/cli.ts +158 -139
  61. package/src/feishu/CardRenderer.ts +2 -0
  62. package/src/feishu/transport.ts +1 -1
  63. package/src/index.ts +8 -4
  64. package/src/proxy/ProxySessionController.ts +10 -10
  65. package/src/startup/StartupNotifier.ts +2 -0
package/dist/cli.js CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import fs from "node:fs";
2
3
  import { spawnSync } from "node:child_process";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import qrcode from "qrcode-terminal";
@@ -6,9 +7,11 @@ import { loadConfig } from "./config/loadConfig.js";
6
7
  import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.js";
7
8
  import { controlEndpoint, } from "./cli/controlProtocol.js";
8
9
  import { acquireInitializationLock, cleanupFeishuCredentialTemporaryFiles, initializeAgentBot, readFeishuCredentials, shouldCreateFeishuApp, writeFeishuCredentials, } from "./cli/Initializer.js";
10
+ import { parseInitCommandOptions } from "./cli/initOptions.js";
9
11
  import { registerFeishuApp, } from "./cli/FeishuAppRegistration.js";
10
12
  import { ensureFeishuAppConfiguration, } from "./cli/FeishuAppConfiguration.js";
11
13
  import { renderCliHelp } from "./cli/help.js";
14
+ import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
12
15
  import { resolveSystemSkillsRoot, SkillRegistry } from "./cli/SkillRegistry.js";
13
16
  import { readPackageVersion } from "./cli/packageVersion.js";
14
17
  import { applyExplicitProfile, parseGlobalOptions } from "./cli/profile.js";
@@ -16,9 +19,10 @@ import { startInitializedServer, startServer, } from "./cli/ServerStarter.js";
16
19
  import { taskChatRoute } from "./cli/taskChatRoute.js";
17
20
  import { StateStore } from "./state/StateStore.js";
18
21
  import { nodeDiagnosticReportArguments, prepareCrashReportDirectory, resolveSupervisorDiagnosticsPaths, } from "./supervision/SupervisorDiagnostics.js";
22
+ import { agentBotHome, defaultSqlitePath } from "./config/paths.js";
19
23
  const args = process.argv.slice(2);
20
24
  void main(args).catch((error) => {
21
- process.stderr.write(`错误:${error instanceof Error ? error.message : String(error)}\n`);
25
+ process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
22
26
  process.exitCode = 1;
23
27
  });
24
28
  async function main(input) {
@@ -37,7 +41,7 @@ async function main(input) {
37
41
  return;
38
42
  }
39
43
  if (command === "init") {
40
- await initCommand(rest, parsed.configPath);
44
+ await initCommand(rest, parsed.configPath, Boolean(parsed.profilePath));
41
45
  return;
42
46
  }
43
47
  if (command === "console") {
@@ -56,39 +60,36 @@ async function main(input) {
56
60
  skillsCommand(rest);
57
61
  return;
58
62
  }
59
- throw new Error(`未知命令:${command}。使用 agent-bot --help 查看帮助。`);
63
+ throw new Error(`Unknown command: ${command}. Run agent-bot --help for usage.`);
60
64
  }
61
- async function initCommand(input, configPath) {
62
- const supported = new Set(["--json", "--skip-feishu", "--reconfigure-feishu"]);
63
- const unsupported = input.filter((value) => !supported.has(value));
64
- if (unsupported.length > 0)
65
- throw new Error(`init 不支持参数:${unsupported.join(" ")}`);
66
- const json = input.includes("--json");
67
- const skipFeishu = input.includes("--skip-feishu");
68
- const reconfigureFeishu = input.includes("--reconfigure-feishu");
69
- if (skipFeishu && reconfigureFeishu) {
70
- throw new Error("--skip-feishu 和 --reconfigure-feishu 不能同时使用。");
71
- }
72
- const paths = initializeAgentBot({ configPath });
73
- if (!json)
74
- printInitializationPaths(paths);
75
- const initializationLock = acquireInitializationLock(paths.home.path);
65
+ async function initCommand(input, configPath, explicitProfile) {
66
+ const options = parseInitCommandOptions(input, explicitProfile);
67
+ let initializationLock = options.reset
68
+ ? acquireInitializationLock(agentBotHome())
69
+ : undefined;
70
+ let paths;
76
71
  let initialized;
77
72
  try {
73
+ if (options.reset)
74
+ await assertResetProfileServerStopped(configPath);
75
+ paths = initializeAgentBot({ configPath, reset: options.reset });
76
+ if (!options.json)
77
+ printInitializationPaths(paths);
78
+ initializationLock ??= acquireInitializationLock(paths.home.path);
78
79
  cleanupFeishuCredentialTemporaryFiles(paths.env.path);
79
- const feishu = await initializeFeishu(paths, { json, skipFeishu, reconfigureFeishu });
80
+ const feishu = await initializeFeishu(paths, options);
80
81
  initialized = { ...paths, feishu };
81
82
  }
82
83
  finally {
83
- initializationLock.release();
84
+ initializationLock?.release();
84
85
  }
85
- if (!json)
86
+ if (!options.json)
86
87
  printInitializationResult(initialized);
87
- if (!json && !skipFeishu)
88
- process.stdout.write("\n正在启动 Agent Bot server...\n");
89
- const server = await startInitializedServer({ skipFeishu, configPath });
88
+ if (!options.json && !options.skipFeishu)
89
+ process.stdout.write("\nStarting Agent Bot server...\n");
90
+ const server = await startInitializedServer({ skipFeishu: options.skipFeishu, configPath });
90
91
  const result = { ...initialized, server };
91
- if (json)
92
+ if (options.json)
92
93
  printJson(result);
93
94
  else
94
95
  printInitializationServerResult(server);
@@ -102,7 +103,7 @@ async function initializeFeishu(paths, options) {
102
103
  };
103
104
  }
104
105
  const controller = new AbortController();
105
- const onInterrupt = () => controller.abort(new Error("已取消初始化。"));
106
+ const onInterrupt = () => controller.abort(new Error("Initialization was cancelled."));
106
107
  process.once("SIGINT", onInterrupt);
107
108
  process.once("SIGTERM", onInterrupt);
108
109
  try {
@@ -118,7 +119,7 @@ async function initializeFeishu(paths, options) {
118
119
  }
119
120
  else {
120
121
  if (existing.status === "incomplete" && !options.json) {
121
- process.stdout.write("\n检测到未完整保存的飞书凭据,将重新创建机器人。\n");
122
+ process.stdout.write("\nIncomplete Lark credentials were found. A new bot will be created.\n");
122
123
  }
123
124
  credentials = await registerFeishuApp({
124
125
  signal: controller.signal,
@@ -128,7 +129,7 @@ async function initializeFeishu(paths, options) {
128
129
  status = "created";
129
130
  }
130
131
  if (!options.json)
131
- process.stdout.write("\n正在检查飞书应用权限、事件和回调配置...\n");
132
+ process.stdout.write("\nChecking Lark app permissions, events, and callbacks...\n");
132
133
  const configuration = await ensureFeishuAppConfiguration(credentials, {
133
134
  signal: controller.signal,
134
135
  onVerification: (challenge) => printFeishuConfigurationVerification(challenge, options.json),
@@ -144,6 +145,23 @@ async function initializeFeishu(paths, options) {
144
145
  process.removeListener("SIGTERM", onInterrupt);
145
146
  }
146
147
  }
148
+ async function assertResetProfileServerStopped(configPath) {
149
+ const endpoints = new Set([controlEndpoint(defaultSqlitePath())]);
150
+ const selectedConfigPath = configPath ?? process.env.AGENT_BOT_CONFIG;
151
+ if (selectedConfigPath && fs.existsSync(selectedConfigPath)) {
152
+ try {
153
+ endpoints.add(controlEndpoint(loadConfig(selectedConfigPath).storage.sqlitePath));
154
+ }
155
+ catch {
156
+ // Reset must remain available when the existing configuration is invalid.
157
+ }
158
+ }
159
+ for (const endpoint of endpoints) {
160
+ if (await isServerReachable(endpoint)) {
161
+ throw new Error("Cannot reset a running profile. Stop it with agent-bot --profile <directory> server stop, then try again.");
162
+ }
163
+ }
164
+ }
147
165
  function skillsCommand(input) {
148
166
  const [action = "status", ...rest] = input;
149
167
  const sourcePath = fileURLToPath(new URL("../skills/agent-bot/", import.meta.url));
@@ -156,8 +174,8 @@ function skillsCommand(input) {
156
174
  printJson(result);
157
175
  else
158
176
  process.stdout.write(result.updated
159
- ? `已注册 Agent Bot Skill:${result.status.targetPath}\n`
160
- : `Agent Bot Skill 已是最新版本:${result.status.targetPath}\n`);
177
+ ? `Installed the Agent Bot Skill: ${result.status.targetPath}\n`
178
+ : `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`);
161
179
  return;
162
180
  }
163
181
  if (action === "uninstall" || action === "unregister") {
@@ -166,8 +184,8 @@ function skillsCommand(input) {
166
184
  printJson({ removed, targetPath: registry.targetPath });
167
185
  else
168
186
  process.stdout.write(removed
169
- ? `已反注册 Agent Bot Skill:${registry.targetPath}\n`
170
- : `Agent Bot Skill 尚未注册:${registry.targetPath}\n`);
187
+ ? `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`
188
+ : `The Agent Bot Skill is not installed: ${registry.targetPath}\n`);
171
189
  return;
172
190
  }
173
191
  if (action === "status") {
@@ -183,20 +201,20 @@ function skillsCommand(input) {
183
201
  if (json)
184
202
  printJson(paths);
185
203
  else {
186
- process.stdout.write(`内置 Skill:${paths.sourcePath}\n`);
187
- process.stdout.write(`系统 Skills:${paths.skillsRoot}\n`);
188
- process.stdout.write(`注册位置:${paths.targetPath}\n`);
204
+ process.stdout.write(`Built-in Skill: ${paths.sourcePath}\n`);
205
+ process.stdout.write(`System Skills directory: ${paths.skillsRoot}\n`);
206
+ process.stdout.write(`Installation path: ${paths.targetPath}\n`);
189
207
  }
190
208
  return;
191
209
  }
192
- throw new Error(`未知 skills 命令:${action}`);
210
+ throw new Error(`Unknown skills command: ${action}`);
193
211
  }
194
212
  async function consoleCommand(input) {
195
213
  const config = loadConfig();
196
214
  const endpoint = controlEndpoint(config.storage.sqlitePath);
197
215
  const force = input.includes("--force");
198
216
  if (!force && await isServerReachable(endpoint)) {
199
- throw new Error("agent-bot server 正在运行。为避免争用同一任务状态,请先停止 server,或明确使用 --force。");
217
+ throw new Error("The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.");
200
218
  }
201
219
  const entry = fileURLToPath(new URL("./index.js", import.meta.url));
202
220
  const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
@@ -222,16 +240,21 @@ async function serverCommand(input) {
222
240
  try {
223
241
  const response = await sendControlRequest(endpoint, { action: "health" }, 1_500);
224
242
  ensureOk(response);
243
+ const status = withConfiguredFeishuAppId(response.data, config.feishu.appId);
225
244
  if (rest.includes("--json"))
226
- printJson(response.data);
245
+ printJson(status);
227
246
  else
228
- printServerStatus(response.data);
247
+ process.stdout.write(formatServerStatus(status));
229
248
  }
230
249
  catch {
250
+ const status = {
251
+ running: false,
252
+ feishuAppId: config.feishu.appId ?? null,
253
+ };
231
254
  if (rest.includes("--json"))
232
- printJson({ running: false });
255
+ printJson(status);
233
256
  else
234
- process.stdout.write("agent-bot server:未运行\n");
257
+ process.stdout.write(formatServerStatus(status));
235
258
  process.exitCode = 3;
236
259
  }
237
260
  return;
@@ -241,21 +264,23 @@ async function serverCommand(input) {
241
264
  return;
242
265
  }
243
266
  if (action === "stop") {
244
- printResponse(await sendControlRequest(endpoint, { action: "server_stop" }));
267
+ ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
268
+ process.stdout.write("Agent Bot server stop requested.\n");
245
269
  return;
246
270
  }
247
271
  if (action === "restart") {
248
272
  const immediate = rest.includes("--immediate") || rest.includes("--force");
249
273
  const reason = optionValue(rest, "--reason")
250
- ?? (immediate ? "通过 agent-bot CLI 立即重启" : "通过 agent-bot CLI 安全重启");
251
- printResponse(await sendControlRequest(endpoint, {
274
+ ?? (immediate ? "通过 Agent Bot CLI 立即重启" : "通过 Agent Bot CLI 安全重启");
275
+ ensureOk(await sendControlRequest(endpoint, {
252
276
  action: "server_restart",
253
277
  mode: immediate ? "immediate" : "safe",
254
278
  reason,
255
279
  }));
280
+ process.stdout.write(immediate ? "Immediate restart requested.\n" : "Safe restart requested.\n");
256
281
  return;
257
282
  }
258
- throw new Error(`未知 server 命令:${action}`);
283
+ throw new Error(`Unknown server command: ${action}`);
259
284
  }
260
285
  async function taskCommand(input) {
261
286
  const [action = "list", ...rest] = input;
@@ -266,17 +291,18 @@ async function taskCommand(input) {
266
291
  if (action === "prompt" || action === "send") {
267
292
  const [reference, ...promptParts] = rest;
268
293
  if (!reference)
269
- throw new Error(`task ${action} 需要任务序号或任务 ID。`);
294
+ throw new Error(`task ${action} requires a task number or task ID.`);
270
295
  const text = promptParts.join(" ").trim();
271
296
  if (!text)
272
- throw new Error(`task ${action} 需要要发送的 Prompt。`);
297
+ throw new Error(`task ${action} requires a Prompt.`);
273
298
  const session = resolveTask(allSessions, reference);
274
299
  const endpoint = controlEndpoint(config.storage.sqlitePath);
275
- printResponse(await sendControlRequest(endpoint, {
300
+ ensureOk(await sendControlRequest(endpoint, {
276
301
  action: "task_prompt",
277
302
  localSessionId: session.localSessionId,
278
303
  text,
279
304
  }, 60_000));
305
+ process.stdout.write("Prompt submitted.\n");
280
306
  return;
281
307
  }
282
308
  const sessions = filterSessions(allSessions, rest);
@@ -289,7 +315,7 @@ async function taskCommand(input) {
289
315
  }
290
316
  const reference = rest.find((value) => !value.startsWith("--"));
291
317
  if (!reference)
292
- throw new Error(`task ${action} 需要任务序号或任务 ID。`);
318
+ throw new Error(`task ${action} requires a task number or task ID.`);
293
319
  const session = resolveTask(sessions, reference);
294
320
  if (action === "chat") {
295
321
  const route = taskChatRoute(session);
@@ -329,7 +355,8 @@ async function taskCommand(input) {
329
355
  }
330
356
  const endpoint = controlEndpoint(config.storage.sqlitePath);
331
357
  if (action === "stop") {
332
- printResponse(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
358
+ ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
359
+ process.stdout.write("Task stop requested.\n");
333
360
  return;
334
361
  }
335
362
  if (action === "title") {
@@ -338,15 +365,16 @@ async function taskCommand(input) {
338
365
  const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
339
366
  const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
340
367
  if (!title)
341
- throw new Error("task title 需要新标题。");
342
- printResponse(await sendControlRequest(endpoint, {
368
+ throw new Error("task title requires a new title.");
369
+ ensureOk(await sendControlRequest(endpoint, {
343
370
  action: "task_title",
344
371
  localSessionId: session.localSessionId,
345
372
  title,
346
373
  }));
374
+ process.stdout.write("Task title updated.\n");
347
375
  return;
348
376
  }
349
- throw new Error(`未知 task 命令:${action}`);
377
+ throw new Error(`Unknown task command: ${action}`);
350
378
  }
351
379
  finally {
352
380
  store.close();
@@ -363,7 +391,7 @@ function resolveTask(sessions, reference) {
363
391
  const index = Number(reference) - 1;
364
392
  const session = sessions[index];
365
393
  if (!session)
366
- throw new Error(`任务序号超出范围:${reference}`);
394
+ throw new Error(`Task number is out of range: ${reference}`);
367
395
  return session;
368
396
  }
369
397
  const exact = sessions.find((session) => session.localSessionId === reference || session.remoteSessionId === reference || session.acpSessionId === reference);
@@ -375,143 +403,131 @@ function resolveTask(sessions, reference) {
375
403
  if (matches.length === 1)
376
404
  return matches[0];
377
405
  if (matches.length > 1)
378
- throw new Error(`任务 ID 前缀不唯一:${reference}`);
379
- throw new Error(`找不到任务:${reference}`);
406
+ throw new Error(`Task ID prefix is ambiguous: ${reference}`);
407
+ throw new Error(`Task not found: ${reference}`);
380
408
  }
381
409
  function printTaskList(sessions) {
382
410
  if (sessions.length === 0) {
383
- process.stdout.write("没有任务。\n");
411
+ process.stdout.write("No tasks.\n");
384
412
  return;
385
413
  }
386
414
  for (const [index, session] of sessions.entries()) {
387
415
  const id = session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId;
388
- process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "未命名任务"}\n`);
416
+ process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "Untitled task"}\n`);
389
417
  process.stdout.write(` ${id} · ${session.contextKey} · ${session.updatedAt}\n`);
390
418
  }
391
419
  }
392
420
  function printTaskStatus(session, snapshot, remote) {
393
- process.stdout.write(`标题:${session.title ?? "未命名任务"}\n`);
394
- process.stdout.write(`任务 ID:${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
395
- process.stdout.write(`本地 ID:${session.localSessionId}\n`);
396
- process.stdout.write(`上下文:${session.contextKey}\n`);
397
- process.stdout.write(`状态:${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
398
- process.stdout.write(`目录:${session.cwd}\n`);
399
- process.stdout.write(`最后轮次:${session.lastTurnId ?? "-"}\n`);
421
+ process.stdout.write(`Title: ${session.title ?? "Untitled task"}\n`);
422
+ process.stdout.write(`Task ID: ${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
423
+ process.stdout.write(`Local ID: ${session.localSessionId}\n`);
424
+ process.stdout.write(`Context: ${session.contextKey}\n`);
425
+ process.stdout.write(`Status: ${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
426
+ process.stdout.write(`Directory: ${session.cwd}\n`);
427
+ process.stdout.write(`Last turn: ${session.lastTurnId ?? "-"}\n`);
400
428
  let displayedFinalResponse;
401
429
  if (snapshot && typeof snapshot === "object") {
402
430
  const state = snapshot;
403
431
  if (typeof state.durationMs === "number")
404
- process.stdout.write(`耗时:${Math.round(state.durationMs / 1_000)}s\n`);
432
+ process.stdout.write(`Duration: ${Math.round(state.durationMs / 1_000)}s\n`);
405
433
  if (typeof state.totalTokens === "number")
406
434
  process.stdout.write(`Tokens:${state.totalTokens}\n`);
407
435
  if (typeof state.totalToolCount === "number")
408
- process.stdout.write(`工具调用:${state.totalToolCount}\n`);
436
+ process.stdout.write(`Tool calls: ${state.totalToolCount}\n`);
409
437
  if (typeof state.finalResponse === "string" && state.finalResponse.trim())
410
438
  displayedFinalResponse = state.finalResponse.trim();
411
439
  }
412
440
  if (!snapshot && remote?.lastTurnToolCount !== undefined)
413
- process.stdout.write(`工具调用:${remote.lastTurnToolCount}\n`);
441
+ process.stdout.write(`Tool calls: ${remote.lastTurnToolCount}\n`);
414
442
  displayedFinalResponse = remote?.finalResponse?.trim() || displayedFinalResponse;
415
443
  if (displayedFinalResponse)
416
- process.stdout.write(`最终结果:\n${displayedFinalResponse}\n`);
417
- }
418
- function printServerStatus(data) {
419
- const value = data && typeof data === "object" ? data : {};
420
- const activity = value.activity && typeof value.activity === "object"
421
- ? value.activity
422
- : {};
423
- process.stdout.write(value.ready === false
424
- ? "agent-bot server:启动中(正在连接飞书机器人)\n"
425
- : "agent-bot server:运行中\n");
426
- process.stdout.write(`PID:${value.pid ?? "-"}\n`);
427
- process.stdout.write(`启动时间:${value.startedAt ?? "-"}\n`);
428
- process.stdout.write(`Supervisor:${value.supervised ? "已启用" : "未启用"}\n`);
429
- process.stdout.write(`运行任务:${activity.runningSessions ?? 0}\n`);
430
- process.stdout.write(`待投递结果:${activity.pendingFinalDeliveries ?? 0}\n`);
431
- process.stdout.write(`安全重启:${value.safeRestartScheduled ? `等待中(${value.safeRestartReason ?? "未注明原因"})` : "未安排"}\n`);
444
+ process.stdout.write(`Final response:\n${displayedFinalResponse}\n`);
432
445
  }
433
446
  function printSkillStatus(status) {
434
- process.stdout.write(`Agent Bot Skill:${status.registered ? "已注册" : "未注册"}\n`);
435
- process.stdout.write(`系统目录:${status.skillsRoot}\n`);
436
- process.stdout.write(`注册位置:${status.targetPath}\n`);
447
+ process.stdout.write(`Agent Bot Skill: ${status.registered ? "installed" : "not installed"}\n`);
448
+ process.stdout.write(`System directory: ${status.skillsRoot}\n`);
449
+ process.stdout.write(`Installation path: ${status.targetPath}\n`);
437
450
  if (status.registered) {
438
- process.stdout.write(`归属:${status.managed ? "由 Agent Bot 管理" : "外部目录(不会覆盖或删除)"}\n`);
439
- process.stdout.write(`版本:${status.upToDate ? "最新" : "需要更新"}\n`);
451
+ process.stdout.write(`Ownership: ${status.managed ? "managed by Agent Bot" : "external directory (will not be modified or removed)"}\n`);
452
+ process.stdout.write(`Version: ${status.upToDate ? "up to date" : "update required"}\n`);
440
453
  }
441
454
  }
442
455
  function printInitializationPaths(result) {
443
- process.stdout.write("正在准备 Agent Bot 用户环境...\n");
444
- process.stdout.write(`用户目录:${result.home.path}(${initializationStatusLabel(result.home.status)})\n`);
445
- process.stdout.write(`配置文件:${result.config.path}(${initializationStatusLabel(result.config.status)})\n`);
446
- process.stdout.write(`环境文件:${result.env.path}(${initializationStatusLabel(result.env.status)})\n`);
447
- process.stdout.write(`数据目录:${result.data.path}(${initializationStatusLabel(result.data.status)})\n`);
448
- process.stdout.write(`日志目录:${result.logs.path}(${initializationStatusLabel(result.logs.status)})\n`);
456
+ process.stdout.write("Preparing the Agent Bot user environment...\n");
457
+ if (result.reset) {
458
+ process.stdout.write(`Reset backup: ${result.reset.backupPath}\n`);
459
+ }
460
+ process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
461
+ process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
462
+ process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
463
+ process.stdout.write(`Data directory: ${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
464
+ process.stdout.write(`Log directory: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
449
465
  }
450
466
  function printInitializationResult(result) {
451
- process.stdout.write("\nAgent Bot 初始化完成。\n");
467
+ process.stdout.write("\nAgent Bot initialization completed.\n");
452
468
  if (result.feishu.status === "created") {
453
- process.stdout.write(`飞书应用:已创建并保存凭证(${result.feishu.appId})\n`);
469
+ process.stdout.write(`Lark app: created and credentials saved (${result.feishu.appId})\n`);
454
470
  }
455
471
  else if (result.feishu.status === "existing") {
456
- process.stdout.write(`飞书应用:已配置,未修改(${result.feishu.appId})\n`);
472
+ process.stdout.write(`Lark app: already configured and unchanged (${result.feishu.appId})\n`);
457
473
  }
458
474
  else {
459
- process.stdout.write("飞书应用:已跳过,可使用 Console 模式或稍后重新运行 init。\n");
475
+ process.stdout.write("Lark app: skipped; use Console mode or run init again later.\n");
460
476
  }
461
477
  if (result.feishu.configuration?.status === "updated") {
462
478
  const added = result.feishu.configuration.added;
463
- process.stdout.write(`飞书配置:已补齐 ${added.scopes.length} 项权限、${added.events.length} 项事件、${added.callbacks.length} 项回调\n`);
479
+ process.stdout.write(`Lark configuration: added ${added.scopes.length} scopes, ${added.events.length} events, and ${added.callbacks.length} callbacks\n`);
464
480
  }
465
481
  else if (result.feishu.configuration?.status === "partial") {
466
482
  const configuration = result.feishu.configuration;
467
483
  const addedCount = configuration.added.scopes.length + configuration.added.events.length + configuration.added.callbacks.length;
468
484
  if (addedCount > 0) {
469
- process.stdout.write(`飞书配置:核心能力已就绪,已补齐 ${configuration.added.scopes.length} 项权限、${configuration.added.events.length} 项事件、${configuration.added.callbacks.length} 项回调\n`);
485
+ process.stdout.write(`Lark configuration: core capabilities are ready; added ${configuration.added.scopes.length} scopes, ${configuration.added.events.length} events, and ${configuration.added.callbacks.length} callbacks\n`);
470
486
  }
471
487
  else {
472
- process.stdout.write("飞书配置:核心能力已就绪\n");
488
+ process.stdout.write("Lark configuration: core capabilities are ready\n");
473
489
  }
474
490
  printOptionalFeishuConfigurationWarnings(configuration.remaining);
475
491
  }
476
492
  else if (result.feishu.configuration?.status === "ready") {
477
- process.stdout.write("飞书配置:权限、事件和回调均已就绪\n");
493
+ process.stdout.write("Lark configuration: scopes, events, and callbacks are ready\n");
478
494
  }
479
- process.stdout.write(`配置文件:${result.config.path}\n`);
495
+ process.stdout.write(`Config file: ${result.config.path}\n`);
480
496
  }
481
497
  function printInitializationServerResult(result) {
482
498
  if (result.status === "skipped") {
483
- process.stdout.write("Agent Bot server:已跳过(未配置飞书,可使用 Console 模式)\n");
499
+ process.stdout.write("Agent Bot server: skipped (Lark is not configured; Console mode is available)\n");
484
500
  return;
485
501
  }
486
502
  printServerStartResult(result);
487
503
  }
488
504
  function printServerStartResult(result) {
489
505
  process.stdout.write(result.status === "already-running"
490
- ? "agent-bot server 已在运行。\n"
491
- : "agent-bot server 已启动。\n");
506
+ ? "Agent Bot server is already running.\n"
507
+ : "Agent Bot server started.\n");
492
508
  }
493
509
  function printFeishuVerification(challenge, json) {
494
- process.stderr.write("\n请使用飞书扫码,或在浏览器中打开下面的链接创建机器人应用:\n\n");
510
+ process.stderr.write("\nScan with Lark or open the link below in a browser to create the bot app:\n\n");
495
511
  process.stderr.write(`${challenge.verificationUrl}\n\n`);
496
512
  if (!json) {
497
513
  qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
498
514
  process.stderr.write(`${output}\n`);
499
515
  });
500
516
  }
501
- process.stderr.write(`链接约在 ${Math.ceil(challenge.expiresIn / 60)} 分钟后过期,正在等待确认...\n`);
517
+ process.stderr.write(`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`);
502
518
  }
503
519
  function printFeishuConfigurationVerification(challenge, json) {
504
- process.stderr.write("\n飞书应用还缺少以下配置:\n");
520
+ process.stderr.write("\nThe Lark app is missing the following configuration:\n");
505
521
  if (challenge.missing.scopes.length > 0) {
506
- process.stderr.write(`权限:${challenge.missing.scopes.join(", ")}\n`);
522
+ process.stderr.write(`Scopes: ${challenge.missing.scopes.join(", ")}\n`);
507
523
  }
508
524
  if (challenge.missing.events.length > 0) {
509
- process.stderr.write(`事件:${challenge.missing.events.join(", ")}\n`);
525
+ process.stderr.write(`Events: ${challenge.missing.events.join(", ")}\n`);
510
526
  }
511
527
  if (challenge.missing.callbacks.length > 0) {
512
- process.stderr.write(`回调:${challenge.missing.callbacks.join(", ")}\n`);
528
+ process.stderr.write(`Callbacks: ${challenge.missing.callbacks.join(", ")}\n`);
513
529
  }
514
- process.stderr.write("\n请使用飞书扫码,或在浏览器中打开下面的链接补齐配置:\n\n");
530
+ process.stderr.write("\nScan with Lark or open the link below in a browser to complete the configuration:\n\n");
515
531
  process.stderr.write(`${challenge.verificationUrl}\n\n`);
516
532
  if (!json) {
517
533
  qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
@@ -519,25 +535,25 @@ function printFeishuConfigurationVerification(challenge, json) {
519
535
  });
520
536
  }
521
537
  if (challenge.blocking) {
522
- process.stderr.write("正在等待核心权限和消息事件发布生效...\n");
538
+ process.stderr.write("Waiting for the core scopes and message event to become active...\n");
523
539
  }
524
540
  else {
525
- process.stderr.write("以上配置不影响基础消息收发;初始化将继续,可稍后打开链接授权。\n");
541
+ process.stderr.write("These optional items do not block basic messaging. Initialization will continue; grant them later using the link.\n");
526
542
  }
527
543
  }
528
544
  function printOptionalFeishuConfigurationWarnings(missing) {
529
- process.stdout.write("提醒:以下非核心配置尚未生效,部分功能可能不可用。\n");
545
+ process.stdout.write("Warning: some optional configuration is not active, so some features may be unavailable.\n");
530
546
  if (missing.scopes.length > 0) {
531
- process.stdout.write(`未生效权限:${missing.scopes.join(", ")}\n`);
547
+ process.stdout.write(`Inactive scopes: ${missing.scopes.join(", ")}\n`);
532
548
  }
533
549
  if (missing.events.length > 0) {
534
- process.stdout.write(`未生效事件:${missing.events.join(", ")}\n`);
550
+ process.stdout.write(`Inactive events: ${missing.events.join(", ")}\n`);
535
551
  }
536
552
  if (missing.callbacks.length > 0) {
537
- process.stdout.write(`未生效回调:${missing.callbacks.join(", ")}\n`);
553
+ process.stdout.write(`Inactive callbacks: ${missing.callbacks.join(", ")}\n`);
538
554
  }
539
555
  for (const feature of optionalFeishuFeatureWarnings(missing)) {
540
- process.stdout.write(`可能缺失功能:${feature}\n`);
556
+ process.stdout.write(`Affected feature: ${feature}\n`);
541
557
  }
542
558
  }
543
559
  function optionalFeishuFeatureWarnings(missing) {
@@ -546,40 +562,42 @@ function optionalFeishuFeatureWarnings(missing) {
546
562
  const callbacks = new Set(missing.callbacks);
547
563
  const warnings = [];
548
564
  if (scopes.has("im:chat:create")) {
549
- warnings.push("无法使用 /newgroup 和 /forkgroup 创建飞书群");
565
+ warnings.push("creating Lark groups with /newgroup and /forkgroup");
550
566
  }
551
567
  if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
552
- warnings.push("修改飞书群名称时无法同步 Agent Bot 任务标题");
568
+ warnings.push("synchronizing Agent Bot task titles after Lark group renames");
553
569
  }
554
570
  if (scopes.has("im:message.reactions:write_only")) {
555
- warnings.push("无法用表情显示消息处理状态");
571
+ warnings.push("showing message-processing status with reactions");
556
572
  }
557
573
  if (scopes.has("im:message:readonly")) {
558
- warnings.push("可能无法读取用户消息中的图片");
574
+ warnings.push("reading images from user messages");
559
575
  }
560
576
  if (scopes.has("im:resource")) {
561
- warnings.push("可能无法上传图片、发送本地图片或设置群头像");
577
+ warnings.push("uploading images, sending local images, or setting group avatars");
562
578
  }
563
579
  if (scopes.has("im:message:update")) {
564
- warnings.push("可能无法更新已发送的进度卡片");
580
+ warnings.push("updating sent progress cards");
565
581
  }
566
582
  if (callbacks.has("card.action.trigger")) {
567
- warnings.push("卡片按钮和交互操作不可用");
583
+ warnings.push("card buttons and interactive actions");
568
584
  }
569
585
  return warnings;
570
586
  }
571
587
  function initializationStatusLabel(status) {
572
588
  if (status === "created")
573
- return "已创建";
574
- return "已存在,未修改";
575
- }
576
- function printResponse(response) {
577
- ensureOk(response);
578
- process.stdout.write(`${response.message ?? "操作成功。"}\n`);
589
+ return "created";
590
+ if (status === "reset")
591
+ return "reset from template";
592
+ return "already exists; unchanged";
579
593
  }
580
594
  function ensureOk(response) {
581
- if (!response.ok)
582
- throw new Error(response.message ?? "Agent Bot 控制操作失败。");
595
+ if (response.ok)
596
+ return;
597
+ const message = response.message?.trim();
598
+ throw new Error(message && !/[一-龥]/u.test(message)
599
+ ? message
600
+ : "Agent Bot control operation failed. Check the server logs for details.");
583
601
  }
584
602
  function optionValue(args, name) {
585
603
  const index = args.indexOf(name);