@keyou007/agent-bot 0.1.2 → 0.1.4

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