@keyou007/agent-bot 0.1.9 → 0.1.11

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 (119) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +22 -10
  3. package/README.zh.md +22 -10
  4. package/assets/agent-bot-logo.png +0 -0
  5. package/dist/acp/AcpSessionManager.js +78 -46
  6. package/dist/acp/AcpSessionManager.js.map +1 -1
  7. package/dist/cli/FeishuAppConfiguration.js +84 -22
  8. package/dist/cli/FeishuAppConfiguration.js.map +1 -1
  9. package/dist/cli/FeishuAppRegistration.js +12 -9
  10. package/dist/cli/FeishuAppRegistration.js.map +1 -1
  11. package/dist/cli/Initializer.js +7 -6
  12. package/dist/cli/Initializer.js.map +1 -1
  13. package/dist/cli/LocalControlClient.js +3 -2
  14. package/dist/cli/LocalControlClient.js.map +1 -1
  15. package/dist/cli/LocalControlServer.js +2 -1
  16. package/dist/cli/LocalControlServer.js.map +1 -1
  17. package/dist/cli/OptionalAuthorizationSkip.js +27 -6
  18. package/dist/cli/OptionalAuthorizationSkip.js.map +1 -1
  19. package/dist/cli/ServerStarter.js +12 -3
  20. package/dist/cli/ServerStarter.js.map +1 -1
  21. package/dist/cli/SkillRegistry.js +5 -4
  22. package/dist/cli/SkillRegistry.js.map +1 -1
  23. package/dist/cli/VerificationOutput.js +2 -1
  24. package/dist/cli/VerificationOutput.js.map +1 -1
  25. package/dist/cli/help.js +48 -1
  26. package/dist/cli/help.js.map +1 -1
  27. package/dist/cli/i18n.js +27 -0
  28. package/dist/cli/i18n.js.map +1 -0
  29. package/dist/cli/initOptions.js +5 -4
  30. package/dist/cli/initOptions.js.map +1 -1
  31. package/dist/cli/packageVersion.js +2 -1
  32. package/dist/cli/packageVersion.js.map +1 -1
  33. package/dist/cli/profile.js +4 -3
  34. package/dist/cli/profile.js.map +1 -1
  35. package/dist/cli/serverStatus.js +15 -14
  36. package/dist/cli/serverStatus.js.map +1 -1
  37. package/dist/cli/taskChatRoute.js +3 -2
  38. package/dist/cli/taskChatRoute.js.map +1 -1
  39. package/dist/cli.js +165 -107
  40. package/dist/cli.js.map +1 -1
  41. package/dist/codex/AppServerConnection.js +15 -1
  42. package/dist/codex/AppServerConnection.js.map +1 -1
  43. package/dist/codex/CodexLocalActivityDetector.js +4 -1
  44. package/dist/codex/CodexLocalActivityDetector.js.map +1 -1
  45. package/dist/codex/CodexProcessManager.js +1 -0
  46. package/dist/codex/CodexProcessManager.js.map +1 -1
  47. package/dist/codex/CodexRuntime.js +77 -2
  48. package/dist/codex/CodexRuntime.js.map +1 -1
  49. package/dist/commands/CommandRouter.js +108 -47
  50. package/dist/commands/CommandRouter.js.map +1 -1
  51. package/dist/console/ConsoleTurnPresenter.js +1 -1
  52. package/dist/console/ConsoleTurnPresenter.js.map +1 -1
  53. package/dist/feishu/CardRenderer.js +345 -11
  54. package/dist/feishu/CardRenderer.js.map +1 -1
  55. package/dist/feishu/FeishuMessageClient.js +51 -5
  56. package/dist/feishu/FeishuMessageClient.js.map +1 -1
  57. package/dist/feishu/FeishuTurnPresenter.js +8 -3
  58. package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
  59. package/dist/index.js +18 -17
  60. package/dist/index.js.map +1 -1
  61. package/dist/proxy/ProxySessionController.js +595 -205
  62. package/dist/proxy/ProxySessionController.js.map +1 -1
  63. package/dist/runtime/AcpRuntimeAdapter.js +1 -0
  64. package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
  65. package/dist/runtime/AgentRuntimeRegistry.js +7 -4
  66. package/dist/runtime/AgentRuntimeRegistry.js.map +1 -1
  67. package/dist/runtime/createAgentRuntimeRegistry.js +22 -0
  68. package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -0
  69. package/dist/startup/SessionMetadataHydrator.js +2 -2
  70. package/dist/startup/SessionMetadataHydrator.js.map +1 -1
  71. package/dist/startup/StartupNotifier.js +1 -0
  72. package/dist/startup/StartupNotifier.js.map +1 -1
  73. package/dist/state/StateStore.js +21 -15
  74. package/dist/state/StateStore.js.map +1 -1
  75. package/dist/supervision/SafeRestartScheduler.js +5 -0
  76. package/dist/supervision/SafeRestartScheduler.js.map +1 -1
  77. package/docs/technical-reference.md +27 -6
  78. package/docs/technical-reference.zh.md +27 -6
  79. package/npm-shrinkwrap.json +2 -2
  80. package/package.json +2 -1
  81. package/skills/agent-bot/SKILL.md +19 -9
  82. package/src/acp/AcpSessionManager.ts +73 -54
  83. package/src/cli/FeishuAppConfiguration.ts +130 -18
  84. package/src/cli/FeishuAppRegistration.ts +24 -9
  85. package/src/cli/Initializer.ts +25 -6
  86. package/src/cli/LocalControlClient.ts +9 -2
  87. package/src/cli/LocalControlServer.ts +2 -1
  88. package/src/cli/OptionalAuthorizationSkip.ts +51 -6
  89. package/src/cli/ServerStarter.ts +20 -3
  90. package/src/cli/SkillRegistry.ts +17 -4
  91. package/src/cli/VerificationOutput.ts +4 -1
  92. package/src/cli/help.ts +50 -1
  93. package/src/cli/i18n.ts +38 -0
  94. package/src/cli/initOptions.ts +17 -4
  95. package/src/cli/packageVersion.ts +5 -1
  96. package/src/cli/profile.ts +13 -3
  97. package/src/cli/serverStatus.ts +19 -14
  98. package/src/cli/taskChatRoute.ts +9 -2
  99. package/src/cli.ts +290 -103
  100. package/src/codex/AppServerConnection.ts +21 -1
  101. package/src/codex/CodexLocalActivityDetector.ts +8 -1
  102. package/src/codex/CodexProcessManager.ts +1 -0
  103. package/src/codex/CodexRuntime.ts +91 -2
  104. package/src/commands/CommandRouter.ts +123 -46
  105. package/src/commands/commandTypes.ts +6 -11
  106. package/src/console/ConsoleTurnPresenter.ts +1 -1
  107. package/src/feishu/CardRenderer.ts +418 -11
  108. package/src/feishu/FeishuMessageClient.ts +66 -3
  109. package/src/feishu/FeishuTurnPresenter.ts +7 -2
  110. package/src/index.ts +21 -22
  111. package/src/proxy/ProxySessionController.ts +712 -208
  112. package/src/runtime/AcpRuntimeAdapter.ts +1 -0
  113. package/src/runtime/AgentRuntimeRegistry.ts +7 -6
  114. package/src/runtime/createAgentRuntimeRegistry.ts +25 -0
  115. package/src/runtime/types.ts +18 -0
  116. package/src/startup/SessionMetadataHydrator.ts +2 -2
  117. package/src/startup/StartupNotifier.ts +1 -0
  118. package/src/state/StateStore.ts +29 -15
  119. package/src/supervision/SafeRestartScheduler.ts +5 -0
package/src/cli.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  } from "./cli/Initializer.js";
22
22
  import { parseInitCommandOptions, type InitCommandOptions } from "./cli/initOptions.js";
23
23
  import {
24
+ listenForManualPermissionSkip,
24
25
  listenForOptionalAuthorizationSkip,
25
26
  type OptionalAuthorizationSkipListener,
26
27
  } from "./cli/OptionalAuthorizationSkip.js";
@@ -35,6 +36,7 @@ import {
35
36
  type FeishuConfigurationChallenge,
36
37
  } from "./cli/FeishuAppConfiguration.js";
37
38
  import { renderCliHelp } from "./cli/help.js";
39
+ import { cliLanguage, cliText, localizeCliErrorMessage } from "./cli/i18n.js";
38
40
  import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
39
41
  import { resolveSystemSkillsRoot, SkillRegistry, type SkillRegistrationStatus } from "./cli/SkillRegistry.js";
40
42
  import { readPackageVersion } from "./cli/packageVersion.js";
@@ -58,7 +60,8 @@ import { agentBotHome, defaultSqlitePath } from "./config/paths.js";
58
60
  const args = process.argv.slice(2);
59
61
 
60
62
  void main(args).catch((error: unknown) => {
61
- process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
63
+ const message = error instanceof Error ? error.message : String(error);
64
+ process.stderr.write(`${cliText("Error: ", "错误:")}${localizeCliErrorMessage(message)}\n`);
62
65
  process.exitCode = 1;
63
66
  });
64
67
 
@@ -95,7 +98,10 @@ async function main(input: string[]): Promise<void> {
95
98
  skillsCommand(rest);
96
99
  return;
97
100
  }
98
- throw new Error(`Unknown command: ${command}. Run agent-bot --help for usage.`);
101
+ throw new Error(cliText(
102
+ `Unknown command: ${command}. Run agent-bot --help for usage.`,
103
+ `未知命令:${command}。运行 agent-bot --help 查看用法。`,
104
+ ));
99
105
  }
100
106
 
101
107
  type FeishuInitializationStatus = "created" | "existing" | "skipped";
@@ -136,7 +142,9 @@ async function initCommand(
136
142
  }
137
143
 
138
144
  if (!options.json) printInitializationResult(initialized);
139
- if (!options.json && !options.skipFeishu) process.stdout.write("\nStarting Agent Bot server...\n");
145
+ if (!options.json && !options.skipFeishu) {
146
+ process.stdout.write(cliText("\nStarting Agent Bot server...\n", "\n正在启动 Agent Bot 服务...\n"));
147
+ }
140
148
  const server = await startInitializedServer({ skipFeishu: options.skipFeishu, configPath });
141
149
  const result: InitCommandResult = { ...initialized, server };
142
150
  if (options.json) printJson(result);
@@ -156,7 +164,10 @@ async function initializeFeishu(
156
164
  }
157
165
 
158
166
  const controller = new AbortController();
159
- const onInterrupt = (): void => controller.abort(new Error("Initialization was cancelled."));
167
+ const onInterrupt = (): void => controller.abort(new Error(cliText(
168
+ "Initialization was cancelled.",
169
+ "初始化已取消。",
170
+ )));
160
171
  process.once("SIGINT", onInterrupt);
161
172
  process.once("SIGTERM", onInterrupt);
162
173
  try {
@@ -171,7 +182,10 @@ async function initializeFeishu(
171
182
  status = "existing";
172
183
  } else {
173
184
  if (existing.status === "incomplete" && !options.json) {
174
- process.stdout.write("\nIncomplete Lark credentials were found. A new bot will be created.\n");
185
+ process.stdout.write(cliText(
186
+ "\nIncomplete Lark credentials were found. A new bot will be created.\n",
187
+ "\n发现不完整的飞书凭据,将创建一个新机器人。\n",
188
+ ));
175
189
  }
176
190
  credentials = await registerFeishuApp({
177
191
  signal: controller.signal,
@@ -181,15 +195,26 @@ async function initializeFeishu(
181
195
  status = "created";
182
196
  }
183
197
 
184
- if (!options.json) process.stdout.write("\nChecking Lark app permissions, events, and callbacks...\n");
198
+ if (!options.json) {
199
+ process.stdout.write(cliText(
200
+ "\nChecking Lark app permissions, events, and callbacks...\n",
201
+ "\n正在检查飞书应用权限、事件和回调...\n",
202
+ ));
203
+ }
204
+ const manualPermissionSkip = new AbortController();
185
205
  const optionalSkip = new AbortController();
186
206
  let skipListener: OptionalAuthorizationSkipListener | undefined;
187
207
  const configuration = await ensureFeishuAppConfiguration(credentials, {
188
208
  signal: controller.signal,
209
+ manualPermissionSkipSignal: manualPermissionSkip.signal,
189
210
  optionalSkipSignal: optionalSkip.signal,
190
211
  onVerification: (challenge) => {
191
212
  printFeishuConfigurationVerification(challenge, options.json);
192
- if (!challenge.blocking) {
213
+ if (challenge.kind === "manual_scope" && challenge.blocking) {
214
+ skipListener?.close();
215
+ skipListener = listenForManualPermissionSkip(() => manualPermissionSkip.abort());
216
+ } else if (!challenge.blocking) {
217
+ skipListener?.close();
193
218
  skipListener = listenForOptionalAuthorizationSkip(() => optionalSkip.abort());
194
219
  }
195
220
  },
@@ -220,7 +245,10 @@ async function assertResetProfileServerStopped(configPath?: string): Promise<voi
220
245
  for (const endpoint of endpoints) {
221
246
  if (await isServerReachable(endpoint)) {
222
247
  throw new Error(
223
- "Cannot reset a running profile. Stop it with agent-bot --profile <directory> server stop, then try again.",
248
+ cliText(
249
+ "Cannot reset a running profile. Stop it with agent-bot --profile <directory> server stop, then try again.",
250
+ "无法重置正在运行的 Profile。请先执行 agent-bot --profile <目录> server stop,然后重试。",
251
+ ),
224
252
  );
225
253
  }
226
254
  }
@@ -237,16 +265,28 @@ function skillsCommand(input: string[]): void {
237
265
  const result = registry.install();
238
266
  if (json) printJson(result);
239
267
  else process.stdout.write(result.updated
240
- ? `Installed the Agent Bot Skill: ${result.status.targetPath}\n`
241
- : `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`);
268
+ ? cliText(
269
+ `Installed the Agent Bot Skill: ${result.status.targetPath}\n`,
270
+ `已安装 Agent Bot Skill:${result.status.targetPath}\n`,
271
+ )
272
+ : cliText(
273
+ `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`,
274
+ `Agent Bot Skill 已是最新版本:${result.status.targetPath}\n`,
275
+ ));
242
276
  return;
243
277
  }
244
278
  if (action === "uninstall" || action === "unregister") {
245
279
  const removed = registry.uninstall();
246
280
  if (json) printJson({ removed, targetPath: registry.targetPath });
247
281
  else process.stdout.write(removed
248
- ? `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`
249
- : `The Agent Bot Skill is not installed: ${registry.targetPath}\n`);
282
+ ? cliText(
283
+ `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`,
284
+ `已卸载 Agent Bot Skill:${registry.targetPath}\n`,
285
+ )
286
+ : cliText(
287
+ `The Agent Bot Skill is not installed: ${registry.targetPath}\n`,
288
+ `Agent Bot Skill 尚未安装:${registry.targetPath}\n`,
289
+ ));
250
290
  return;
251
291
  }
252
292
  if (action === "status") {
@@ -259,13 +299,13 @@ function skillsCommand(input: string[]): void {
259
299
  const paths = { sourcePath: registry.sourcePath, skillsRoot: registry.skillsRoot, targetPath: registry.targetPath };
260
300
  if (json) printJson(paths);
261
301
  else {
262
- process.stdout.write(`Built-in Skill: ${paths.sourcePath}\n`);
263
- process.stdout.write(`System Skills directory: ${paths.skillsRoot}\n`);
264
- process.stdout.write(`Installation path: ${paths.targetPath}\n`);
302
+ process.stdout.write(`${cliText("Built-in Skill: ", "内置 Skill:")}${paths.sourcePath}\n`);
303
+ process.stdout.write(`${cliText("System Skills directory: ", "系统 Skills 目录:")}${paths.skillsRoot}\n`);
304
+ process.stdout.write(`${cliText("Installation path: ", "安装路径:")}${paths.targetPath}\n`);
265
305
  }
266
306
  return;
267
307
  }
268
- throw new Error(`Unknown skills command: ${action}`);
308
+ throw new Error(cliText(`Unknown skills command: ${action}`, `未知的 skills 命令:${action}`));
269
309
  }
270
310
 
271
311
  async function consoleCommand(input: string[]): Promise<void> {
@@ -274,7 +314,10 @@ async function consoleCommand(input: string[]): Promise<void> {
274
314
  const force = input.includes("--force");
275
315
  if (!force && await isServerReachable(endpoint)) {
276
316
  throw new Error(
277
- "The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.",
317
+ cliText(
318
+ "The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.",
319
+ "Agent Bot 服务正在运行。请先停止服务以避免并发访问任务状态,或显式使用 --force。",
320
+ ),
278
321
  );
279
322
  }
280
323
  const entry = fileURLToPath(new URL("./index.js", import.meta.url));
@@ -320,7 +363,7 @@ async function serverCommand(input: string[]): Promise<void> {
320
363
  }
321
364
  if (action === "stop") {
322
365
  ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
323
- process.stdout.write("Agent Bot server stop requested.\n");
366
+ process.stdout.write(cliText("Agent Bot server stop requested.\n", "已请求停止 Agent Bot 服务。\n"));
324
367
  return;
325
368
  }
326
369
  if (action === "restart") {
@@ -332,10 +375,12 @@ async function serverCommand(input: string[]): Promise<void> {
332
375
  mode: immediate ? "immediate" : "safe",
333
376
  reason,
334
377
  }));
335
- process.stdout.write(immediate ? "Immediate restart requested.\n" : "Safe restart requested.\n");
378
+ process.stdout.write(immediate
379
+ ? cliText("Immediate restart requested.\n", "已请求立即重启。\n")
380
+ : cliText("Safe restart requested.\n", "已请求安全重启。\n"));
336
381
  return;
337
382
  }
338
- throw new Error(`Unknown server command: ${action}`);
383
+ throw new Error(cliText(`Unknown server command: ${action}`, `未知的 server 命令:${action}`));
339
384
  }
340
385
 
341
386
  async function taskCommand(input: string[]): Promise<void> {
@@ -346,9 +391,15 @@ async function taskCommand(input: string[]): Promise<void> {
346
391
  const allSessions = store.listAllSessions();
347
392
  if (action === "prompt" || action === "send") {
348
393
  const [reference, ...promptParts] = rest;
349
- if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
394
+ if (!reference) throw new Error(cliText(
395
+ `task ${action} requires a task number or task ID.`,
396
+ `task ${action} 需要任务序号或任务 ID。`,
397
+ ));
350
398
  const text = promptParts.join(" ").trim();
351
- if (!text) throw new Error(`task ${action} requires a Prompt.`);
399
+ if (!text) throw new Error(cliText(
400
+ `task ${action} requires a Prompt.`,
401
+ `task ${action} 需要提示词。`,
402
+ ));
352
403
  const session = resolveTask(allSessions, reference);
353
404
  const endpoint = controlEndpoint(config.storage.sqlitePath);
354
405
  ensureOk(await sendControlRequest(endpoint, {
@@ -356,7 +407,7 @@ async function taskCommand(input: string[]): Promise<void> {
356
407
  localSessionId: session.localSessionId,
357
408
  text,
358
409
  }, 60_000));
359
- process.stdout.write("Prompt submitted.\n");
410
+ process.stdout.write(cliText("Prompt submitted.\n", "提示词已提交。\n"));
360
411
  return;
361
412
  }
362
413
  const sessions = filterSessions(allSessions, rest);
@@ -366,7 +417,10 @@ async function taskCommand(input: string[]): Promise<void> {
366
417
  return;
367
418
  }
368
419
  const reference = rest.find((value) => !value.startsWith("--"));
369
- if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
420
+ if (!reference) throw new Error(cliText(
421
+ `task ${action} requires a task number or task ID.`,
422
+ `task ${action} 需要任务序号或任务 ID。`,
423
+ ));
370
424
  const session = resolveTask(sessions, reference);
371
425
  if (action === "chat") {
372
426
  const route = taskChatRoute(session);
@@ -402,7 +456,7 @@ async function taskCommand(input: string[]): Promise<void> {
402
456
  const endpoint = controlEndpoint(config.storage.sqlitePath);
403
457
  if (action === "stop") {
404
458
  ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
405
- process.stdout.write("Task stop requested.\n");
459
+ process.stdout.write(cliText("Task stop requested.\n", "已请求停止任务。\n"));
406
460
  return;
407
461
  }
408
462
  if (action === "title") {
@@ -410,16 +464,19 @@ async function taskCommand(input: string[]): Promise<void> {
410
464
  const titleArgs = rest.slice(referenceIndex + 1);
411
465
  const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
412
466
  const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
413
- if (!title) throw new Error("task title requires a new title.");
467
+ if (!title) throw new Error(cliText(
468
+ "task title requires a new title.",
469
+ "task title 需要新标题。",
470
+ ));
414
471
  ensureOk(await sendControlRequest(endpoint, {
415
472
  action: "task_title",
416
473
  localSessionId: session.localSessionId,
417
474
  title,
418
475
  }));
419
- process.stdout.write("Task title updated.\n");
476
+ process.stdout.write(cliText("Task title updated.\n", "任务标题已更新。\n"));
420
477
  return;
421
478
  }
422
- throw new Error(`Unknown task command: ${action}`);
479
+ throw new Error(cliText(`Unknown task command: ${action}`, `未知的 task 命令:${action}`));
423
480
  } finally {
424
481
  store.close();
425
482
  }
@@ -437,7 +494,10 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
437
494
  if (/^\d+$/.test(reference)) {
438
495
  const index = Number(reference) - 1;
439
496
  const session = sessions[index];
440
- if (!session) throw new Error(`Task number is out of range: ${reference}`);
497
+ if (!session) throw new Error(cliText(
498
+ `Task number is out of range: ${reference}`,
499
+ `任务序号超出范围:${reference}`,
500
+ ));
441
501
  return session;
442
502
  }
443
503
  const exact = sessions.find((session) =>
@@ -448,100 +508,143 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
448
508
  || session.remoteSessionId?.startsWith(reference)
449
509
  || session.acpSessionId?.startsWith(reference));
450
510
  if (matches.length === 1) return matches[0]!;
451
- if (matches.length > 1) throw new Error(`Task ID prefix is ambiguous: ${reference}`);
452
- throw new Error(`Task not found: ${reference}`);
511
+ if (matches.length > 1) throw new Error(cliText(
512
+ `Task ID prefix is ambiguous: ${reference}`,
513
+ `任务 ID 前缀不唯一:${reference}`,
514
+ ));
515
+ throw new Error(cliText(`Task not found: ${reference}`, `未找到任务:${reference}`));
453
516
  }
454
517
 
455
518
  function printTaskList(sessions: SessionRecord[]): void {
456
519
  if (sessions.length === 0) {
457
- process.stdout.write("No tasks.\n");
520
+ process.stdout.write(cliText("No tasks.\n", "没有任务。\n"));
458
521
  return;
459
522
  }
460
523
  for (const [index, session] of sessions.entries()) {
461
524
  const id = session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId;
462
- process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "Untitled task"}\n`);
525
+ process.stdout.write(`${index + 1}. [${taskStateLabel(session.status)}/${taskStateLabel(session.lastTurnStatus)}] ${session.title ?? cliText("Untitled task", "未命名任务")}\n`);
463
526
  process.stdout.write(` ${id} · ${session.contextKey} · ${session.updatedAt}\n`);
464
527
  }
465
528
  }
466
529
 
467
530
  function printTaskStatus(session: SessionRecord, snapshot: unknown, remote?: TaskStatusControlData["remote"]): void {
468
- process.stdout.write(`Title: ${session.title ?? "Untitled task"}\n`);
469
- process.stdout.write(`Task ID: ${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
470
- process.stdout.write(`Local ID: ${session.localSessionId}\n`);
471
- process.stdout.write(`Context: ${session.contextKey}\n`);
472
- process.stdout.write(`Status: ${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
473
- process.stdout.write(`Directory: ${session.cwd}\n`);
474
- process.stdout.write(`Last turn: ${session.lastTurnId ?? "-"}\n`);
531
+ process.stdout.write(`${cliText("Title: ", "标题:")}${session.title ?? cliText("Untitled task", "未命名任务")}\n`);
532
+ process.stdout.write(`${cliText("Task ID: ", "任务 ID:")}${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
533
+ process.stdout.write(`${cliText("Local ID: ", "本地 ID:")}${session.localSessionId}\n`);
534
+ process.stdout.write(`${cliText("Context: ", "上下文:")}${session.contextKey}\n`);
535
+ process.stdout.write(`${cliText("Status: ", "状态:")}${taskStateLabel(session.status)} / ${taskStateLabel(session.lastTurnStatus)}\n`);
536
+ process.stdout.write(`${cliText("Directory: ", "目录:")}${session.cwd}\n`);
537
+ process.stdout.write(`${cliText("Last turn: ", "上一轮:")}${session.lastTurnId ?? "-"}\n`);
475
538
  let displayedFinalResponse: string | undefined;
476
539
  if (snapshot && typeof snapshot === "object") {
477
540
  const state = snapshot as Record<string, unknown>;
478
- if (typeof state.durationMs === "number") process.stdout.write(`Duration: ${Math.round(state.durationMs / 1_000)}s\n`);
479
- if (typeof state.totalTokens === "number") process.stdout.write(`Tokens:${state.totalTokens}\n`);
480
- if (typeof state.totalToolCount === "number") process.stdout.write(`Tool calls: ${state.totalToolCount}\n`);
541
+ if (typeof state.durationMs === "number") {
542
+ process.stdout.write(`${cliText("Duration: ", "耗时:")}${Math.round(state.durationMs / 1_000)}s\n`);
543
+ }
544
+ if (typeof state.totalTokens === "number") {
545
+ process.stdout.write(`${cliText("Tokens: ", "Tokens:")}${state.totalTokens}\n`);
546
+ }
547
+ if (typeof state.totalToolCount === "number") {
548
+ process.stdout.write(`${cliText("Tool calls: ", "工具调用:")}${state.totalToolCount}\n`);
549
+ }
481
550
  if (typeof state.finalResponse === "string" && state.finalResponse.trim()) displayedFinalResponse = state.finalResponse.trim();
482
551
  }
483
- if (!snapshot && remote?.lastTurnToolCount !== undefined) process.stdout.write(`Tool calls: ${remote.lastTurnToolCount}\n`);
552
+ if (!snapshot && remote?.lastTurnToolCount !== undefined) {
553
+ process.stdout.write(`${cliText("Tool calls: ", "工具调用:")}${remote.lastTurnToolCount}\n`);
554
+ }
484
555
  displayedFinalResponse = remote?.finalResponse?.trim() || displayedFinalResponse;
485
- if (displayedFinalResponse) process.stdout.write(`Final response:\n${displayedFinalResponse}\n`);
556
+ if (displayedFinalResponse) {
557
+ process.stdout.write(`${cliText("Final response:\n", "最终回答:\n")}${displayedFinalResponse}\n`);
558
+ }
486
559
  }
487
560
 
488
561
  function printSkillStatus(status: SkillRegistrationStatus): void {
489
- process.stdout.write(`Agent Bot Skill: ${status.registered ? "installed" : "not installed"}\n`);
490
- process.stdout.write(`System directory: ${status.skillsRoot}\n`);
491
- process.stdout.write(`Installation path: ${status.targetPath}\n`);
562
+ process.stdout.write(`${cliText("Agent Bot Skill: ", "Agent Bot Skill:")}${status.registered ? cliText("installed", "已安装") : cliText("not installed", "未安装")}\n`);
563
+ process.stdout.write(`${cliText("System directory: ", "系统目录:")}${status.skillsRoot}\n`);
564
+ process.stdout.write(`${cliText("Installation path: ", "安装路径:")}${status.targetPath}\n`);
492
565
  if (status.registered) {
493
- process.stdout.write(`Ownership: ${status.managed ? "managed by Agent Bot" : "external directory (will not be modified or removed)"}\n`);
494
- process.stdout.write(`Version: ${status.upToDate ? "up to date" : "update required"}\n`);
566
+ process.stdout.write(`${cliText("Ownership: ", "所有权:")}${status.managed
567
+ ? cliText("managed by Agent Bot", "由 Agent Bot 管理")
568
+ : cliText("external directory (will not be modified or removed)", "外部目录(不会修改或删除)")}\n`);
569
+ process.stdout.write(`${cliText("Version: ", "版本:")}${status.upToDate
570
+ ? cliText("up to date", "已是最新")
571
+ : cliText("update required", "需要更新")}\n`);
495
572
  }
496
573
  }
497
574
 
498
575
  function printInitializationPaths(result: InitializationResult): void {
499
- process.stdout.write("Preparing the Agent Bot user environment...\n");
576
+ process.stdout.write(cliText(
577
+ "Preparing the Agent Bot user environment...\n",
578
+ "正在准备 Agent Bot 用户环境...\n",
579
+ ));
500
580
  if (result.reset) {
501
- process.stdout.write(`Reset backup: ${result.reset.backupPath}\n`);
581
+ process.stdout.write(`${cliText("Reset backup: ", "重置备份:")}${result.reset.backupPath}\n`);
502
582
  }
503
- process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
504
- process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
505
- process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
506
- process.stdout.write(`Data directory: ${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
507
- process.stdout.write(`Log directory: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
583
+ process.stdout.write(`${cliText("Home directory: ", "主目录:")}${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
584
+ process.stdout.write(`${cliText("Config file: ", "配置文件:")}${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
585
+ process.stdout.write(`${cliText("Environment file: ", "环境文件:")}${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
586
+ process.stdout.write(`${cliText("Data directory: ", "数据目录:")}${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
587
+ process.stdout.write(`${cliText("Log directory: ", "日志目录:")}${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
508
588
  }
509
589
 
510
590
  function printInitializationResult(result: Omit<InitCommandResult, "server">): void {
511
- process.stdout.write("\nAgent Bot initialization completed.\n");
591
+ process.stdout.write(cliText(
592
+ "\nAgent Bot initialization completed.\n",
593
+ "\nAgent Bot 初始化完成。\n",
594
+ ));
512
595
  if (result.feishu.status === "created") {
513
- process.stdout.write(`Lark app: created and credentials saved (${result.feishu.appId})\n`);
596
+ process.stdout.write(cliText(
597
+ `Lark app: created and credentials saved (${result.feishu.appId})\n`,
598
+ `飞书应用:已创建并保存凭据 (${result.feishu.appId})\n`,
599
+ ));
514
600
  } else if (result.feishu.status === "existing") {
515
- process.stdout.write(`Lark app: already configured and unchanged (${result.feishu.appId})\n`);
601
+ process.stdout.write(cliText(
602
+ `Lark app: already configured and unchanged (${result.feishu.appId})\n`,
603
+ `飞书应用:已配置,未做修改 (${result.feishu.appId})\n`,
604
+ ));
516
605
  } else {
517
- process.stdout.write("Lark app: skipped; use Console mode or run init again later.\n");
606
+ process.stdout.write(cliText(
607
+ "Lark app: skipped; use Console mode or run init again later.\n",
608
+ "飞书应用:已跳过;请使用 Console 模式或稍后重新运行 init。\n",
609
+ ));
518
610
  }
519
611
  if (result.feishu.configuration?.status === "updated") {
520
612
  const added = result.feishu.configuration.added;
521
- process.stdout.write(
613
+ process.stdout.write(cliText(
522
614
  `Lark configuration: added ${added.scopes.length} scopes, ${added.events.length} events, and ${added.callbacks.length} callbacks\n`,
523
- );
615
+ `飞书配置:已新增 ${added.scopes.length} 项权限、${added.events.length} 个事件和 ${added.callbacks.length} 个回调\n`,
616
+ ));
524
617
  } else if (result.feishu.configuration?.status === "partial") {
525
618
  const configuration = result.feishu.configuration;
526
619
  const addedCount =
527
620
  configuration.added.scopes.length + configuration.added.events.length + configuration.added.callbacks.length;
528
621
  if (addedCount > 0) {
529
- process.stdout.write(
530
- `Lark configuration: core capabilities are ready; added ${configuration.added.scopes.length} scopes, ${configuration.added.events.length} events, and ${configuration.added.callbacks.length} callbacks\n`,
531
- );
622
+ process.stdout.write(cliText(
623
+ `Lark configuration: initialization continued with missing configuration; added ${configuration.added.scopes.length} scopes, ${configuration.added.events.length} events, and ${configuration.added.callbacks.length} callbacks\n`,
624
+ `飞书配置:仍有配置缺失,初始化已继续;新增 ${configuration.added.scopes.length} 项权限、${configuration.added.events.length} 个事件和 ${configuration.added.callbacks.length} 个回调\n`,
625
+ ));
532
626
  } else {
533
- process.stdout.write("Lark configuration: core capabilities are ready\n");
627
+ process.stdout.write(cliText(
628
+ "Lark configuration: initialization continued with missing configuration\n",
629
+ "飞书配置:仍有配置缺失,初始化已继续\n",
630
+ ));
534
631
  }
535
- printOptionalFeishuConfigurationWarnings(configuration.remaining);
632
+ printFeishuConfigurationWarnings(configuration.remaining);
536
633
  } else if (result.feishu.configuration?.status === "ready") {
537
- process.stdout.write("Lark configuration: scopes, events, and callbacks are ready\n");
634
+ process.stdout.write(cliText(
635
+ "Lark configuration: scopes, events, and callbacks are ready\n",
636
+ "飞书配置:权限、事件和回调均已就绪\n",
637
+ ));
538
638
  }
539
- process.stdout.write(`Config file: ${result.config.path}\n`);
639
+ process.stdout.write(`${cliText("Config file: ", "配置文件:")}${result.config.path}\n`);
540
640
  }
541
641
 
542
642
  function printInitializationServerResult(result: InitCommandResult["server"]): void {
543
643
  if (result.status === "skipped") {
544
- process.stdout.write("Agent Bot server: skipped (Lark is not configured; Console mode is available)\n");
644
+ process.stdout.write(cliText(
645
+ "Agent Bot server: skipped (Lark is not configured; Console mode is available)\n",
646
+ "Agent Bot 服务:已跳过(未配置飞书;可使用 Console 模式)\n",
647
+ ));
545
648
  return;
546
649
  }
547
650
  printServerStartResult(result);
@@ -550,8 +653,8 @@ function printInitializationServerResult(result: InitCommandResult["server"]): v
550
653
  function printServerStartResult(result: ServerStartResult): void {
551
654
  process.stdout.write(
552
655
  result.status === "already-running"
553
- ? "Agent Bot server is already running.\n"
554
- : "Agent Bot server started.\n",
656
+ ? cliText("Agent Bot server is already running.\n", "Agent Bot 服务已在运行。\n")
657
+ : cliText("Agent Bot server started.\n", "Agent Bot 服务已启动。\n"),
555
658
  );
556
659
  }
557
660
 
@@ -560,97 +663,181 @@ function printFeishuVerification(challenge: FeishuAppRegistrationChallenge, json
560
663
  {
561
664
  verificationUrl: challenge.verificationUrl,
562
665
  json,
563
- qrInstruction: "Scan this QR code with Lark to create the bot app:",
666
+ qrInstruction: cliText(
667
+ "Scan this QR code with Lark to create the bot app:",
668
+ "使用飞书扫描此二维码以创建机器人应用:",
669
+ ),
564
670
  },
565
671
  );
566
- process.stderr.write(`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`);
672
+ process.stderr.write(cliText(
673
+ `The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`,
674
+ `链接将在约 ${Math.ceil(challenge.expiresIn / 60)} 分钟后过期,正在等待确认...\n`,
675
+ ));
567
676
  }
568
677
 
569
678
  function printFeishuConfigurationVerification(
570
679
  challenge: FeishuConfigurationChallenge,
571
680
  json: boolean,
572
681
  ): void {
573
- process.stderr.write("\nThe Lark app is missing the following configuration:\n");
682
+ process.stderr.write(challenge.kind === "manual_scope"
683
+ ? cliText(
684
+ "\nThe following Lark permission must be added manually in Developer Console:\n",
685
+ "\n必须在飞书开发者后台手动添加以下权限:\n",
686
+ )
687
+ : cliText(
688
+ "\nThe Lark app is missing the following configuration:\n",
689
+ "\n飞书应用缺少以下配置:\n",
690
+ ));
574
691
  if (challenge.missing.scopes.length > 0) {
575
- process.stderr.write(`Scopes: ${challenge.missing.scopes.join(", ")}\n`);
692
+ process.stderr.write(`${cliText("Scopes: ", "权限:")}${challenge.missing.scopes.join(", ")}\n`);
576
693
  }
577
694
  if (challenge.missing.events.length > 0) {
578
- process.stderr.write(`Events: ${challenge.missing.events.join(", ")}\n`);
695
+ process.stderr.write(`${cliText("Events: ", "事件:")}${challenge.missing.events.join(", ")}\n`);
579
696
  }
580
697
  if (challenge.missing.callbacks.length > 0) {
581
- process.stderr.write(`Callbacks: ${challenge.missing.callbacks.join(", ")}\n`);
698
+ process.stderr.write(`${cliText("Callbacks: ", "回调:")}${challenge.missing.callbacks.join(", ")}\n`);
582
699
  }
583
700
  printVerificationQrAndLink(
584
701
  {
585
702
  verificationUrl: challenge.verificationUrl,
586
703
  json,
587
- qrInstruction: "Scan this QR code with Lark to complete the configuration:",
704
+ qrInstruction: challenge.kind === "manual_scope"
705
+ ? cliText(
706
+ "Scan this QR code with Lark to open the filtered permission page:",
707
+ "使用飞书扫描此二维码以打开已筛选的权限页面:",
708
+ )
709
+ : cliText(
710
+ "Scan this QR code with Lark to complete the configuration:",
711
+ "使用飞书扫描此二维码以完成配置:",
712
+ ),
713
+ linkLabel: challenge.kind === "manual_scope"
714
+ ? cliText("Permission page", "权限页面")
715
+ : undefined,
588
716
  },
589
717
  );
718
+ if (challenge.kind === "manual_scope") {
719
+ process.stderr.write(cliText(
720
+ "Add the filtered permission, publish the app version, and complete tenant approval if required.\n",
721
+ "请添加已筛选的权限、发布应用版本,并在需要时完成租户管理员审批。\n",
722
+ ));
723
+ }
590
724
  if (challenge.blocking) {
591
- process.stderr.write("Waiting for the core scopes and message event to become active...\n");
725
+ process.stderr.write(cliText(
726
+ "Waiting for the core scopes and message event to become active...\n",
727
+ "正在等待核心权限和消息事件生效...\n",
728
+ ));
592
729
  } else {
593
- process.stderr.write("Waiting up to 5 minutes for these optional items to become active...\n");
730
+ process.stderr.write(cliText(
731
+ "Waiting up to 5 minutes for these optional items to become active...\n",
732
+ "将等待最多 5 分钟,直到这些可选配置生效...\n",
733
+ ));
594
734
  }
595
735
  }
596
736
 
597
- function printOptionalFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge["missing"]): void {
598
- process.stdout.write("Warning: some optional configuration is not active, so some features may be unavailable.\n");
737
+ function printFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge["missing"]): void {
738
+ process.stdout.write(cliText(
739
+ "Warning: some Lark configuration is not active, so some features may be unavailable.\n",
740
+ "警告:部分飞书配置尚未生效,某些功能可能不可用。\n",
741
+ ));
599
742
  if (missing.scopes.length > 0) {
600
- process.stdout.write(`Inactive scopes: ${missing.scopes.join(", ")}\n`);
743
+ process.stdout.write(`${cliText("Inactive scopes: ", "未生效权限:")}${missing.scopes.join(", ")}\n`);
601
744
  }
602
745
  if (missing.events.length > 0) {
603
- process.stdout.write(`Inactive events: ${missing.events.join(", ")}\n`);
746
+ process.stdout.write(`${cliText("Inactive events: ", "未生效事件:")}${missing.events.join(", ")}\n`);
604
747
  }
605
748
  if (missing.callbacks.length > 0) {
606
- process.stdout.write(`Inactive callbacks: ${missing.callbacks.join(", ")}\n`);
749
+ process.stdout.write(`${cliText("Inactive callbacks: ", "未生效回调:")}${missing.callbacks.join(", ")}\n`);
607
750
  }
608
- for (const feature of optionalFeishuFeatureWarnings(missing)) {
609
- process.stdout.write(`Affected feature: ${feature}\n`);
751
+ for (const feature of feishuFeatureWarnings(missing)) {
752
+ process.stdout.write(`${cliText("Affected feature: ", "受影响功能:")}${feature}\n`);
610
753
  }
611
754
  }
612
755
 
613
- function optionalFeishuFeatureWarnings(missing: FeishuConfigurationChallenge["missing"]): string[] {
756
+ function feishuFeatureWarnings(missing: FeishuConfigurationChallenge["missing"]): string[] {
614
757
  const scopes = new Set(missing.scopes);
615
758
  const events = new Set(missing.events);
616
759
  const callbacks = new Set(missing.callbacks);
617
760
  const warnings: string[] = [];
761
+ if (scopes.has("im:message.group_msg")) {
762
+ warnings.push(cliText(
763
+ "responding to ordinary group messages that do not mention the bot",
764
+ "响应未 @ 机器人的普通群消息",
765
+ ));
766
+ }
618
767
  if (scopes.has("im:chat:create")) {
619
- warnings.push("creating Lark groups with /newgroup and /forkgroup");
768
+ warnings.push(cliText(
769
+ "creating Lark groups with /newgroup and /forkgroup",
770
+ "使用 /newgroup 和 /forkgroup 创建飞书群",
771
+ ));
620
772
  }
621
773
  if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
622
- warnings.push("synchronizing Agent Bot task titles after Lark group renames");
774
+ warnings.push(cliText(
775
+ "synchronizing Agent Bot task titles after Lark group renames",
776
+ "飞书群重命名后同步 Agent Bot 任务标题",
777
+ ));
623
778
  }
624
779
  if (scopes.has("im:message.reactions:write_only")) {
625
- warnings.push("showing message-processing status with reactions");
780
+ warnings.push(cliText(
781
+ "showing message-processing status with reactions",
782
+ "使用 Reaction 显示消息处理状态",
783
+ ));
626
784
  }
627
785
  if (scopes.has("im:message:readonly")) {
628
- warnings.push("reading images from user messages");
786
+ warnings.push(cliText("reading images from user messages", "读取用户消息中的图片"));
629
787
  }
630
788
  if (scopes.has("im:resource")) {
631
- warnings.push("uploading images, sending local images, or setting group avatars");
789
+ warnings.push(cliText(
790
+ "uploading images, sending local images, or setting group avatars",
791
+ "上传图片、发送本地图片或设置群头像",
792
+ ));
632
793
  }
633
794
  if (scopes.has("im:message:update")) {
634
- warnings.push("updating sent progress cards");
795
+ warnings.push(cliText("updating sent progress cards", "更新已发送的进度卡片"));
635
796
  }
636
797
  if (callbacks.has("card.action.trigger")) {
637
- warnings.push("card buttons and interactive actions");
798
+ warnings.push(cliText("card buttons and interactive actions", "卡片按钮和交互操作"));
638
799
  }
639
800
  return warnings;
640
801
  }
641
802
 
642
803
  function initializationStatusLabel(status: InitializationStatus): string {
643
- if (status === "created") return "created";
644
- if (status === "reset") return "reset from template";
645
- return "already exists; unchanged";
804
+ if (status === "created") return cliText("created", "已创建");
805
+ if (status === "reset") return cliText("reset from template", "已从模板重置");
806
+ return cliText("already exists; unchanged", "已存在,未修改");
807
+ }
808
+
809
+ function taskStateLabel(status: string | undefined): string {
810
+ if (!status) return "-";
811
+ const chinese: Record<string, string> = {
812
+ starting: "启动中",
813
+ ready: "就绪",
814
+ running: "运行中",
815
+ closed: "已关闭",
816
+ failed: "失败",
817
+ completed: "已完成",
818
+ cancelled: "已取消",
819
+ interrupted: "已中断",
820
+ inProgress: "进行中",
821
+ active: "活动中",
822
+ idle: "空闲",
823
+ not_loaded: "未加载",
824
+ error: "错误",
825
+ };
826
+ return cliLanguage === "zh" ? chinese[status] ?? status : status;
646
827
  }
647
828
 
648
829
  function ensureOk(response: ControlResponse): void {
649
830
  if (response.ok) return;
650
831
  const message = response.message?.trim();
651
- throw new Error(message && !/[一-龥]/u.test(message)
652
- ? message
653
- : "Agent Bot control operation failed. Check the server logs for details.");
832
+ const matchesLanguage = cliLanguage === "zh"
833
+ ? Boolean(message && /[一-龥]/u.test(message))
834
+ : Boolean(message && !/[一-龥]/u.test(message));
835
+ throw new Error(matchesLanguage
836
+ ? message!
837
+ : cliText(
838
+ "Agent Bot control operation failed. Check the server logs for details.",
839
+ "Agent Bot 控制操作失败,请查看服务日志了解详情。",
840
+ ));
654
841
  }
655
842
 
656
843
  function optionValue(args: string[], name: string): string | undefined {