@keyou007/agent-bot 0.1.9 → 0.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +7 -1
- package/README.zh.md +7 -1
- package/dist/cli/FeishuAppConfiguration.js +84 -22
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/FeishuAppRegistration.js +12 -9
- package/dist/cli/FeishuAppRegistration.js.map +1 -1
- package/dist/cli/Initializer.js +7 -6
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/LocalControlClient.js +3 -2
- package/dist/cli/LocalControlClient.js.map +1 -1
- package/dist/cli/LocalControlServer.js +2 -1
- package/dist/cli/LocalControlServer.js.map +1 -1
- package/dist/cli/OptionalAuthorizationSkip.js +27 -6
- package/dist/cli/OptionalAuthorizationSkip.js.map +1 -1
- package/dist/cli/ServerStarter.js +12 -3
- package/dist/cli/ServerStarter.js.map +1 -1
- package/dist/cli/SkillRegistry.js +5 -4
- package/dist/cli/SkillRegistry.js.map +1 -1
- package/dist/cli/VerificationOutput.js +2 -1
- package/dist/cli/VerificationOutput.js.map +1 -1
- package/dist/cli/help.js +48 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/i18n.js +27 -0
- package/dist/cli/i18n.js.map +1 -0
- package/dist/cli/initOptions.js +5 -4
- package/dist/cli/initOptions.js.map +1 -1
- package/dist/cli/packageVersion.js +2 -1
- package/dist/cli/packageVersion.js.map +1 -1
- package/dist/cli/profile.js +4 -3
- package/dist/cli/profile.js.map +1 -1
- package/dist/cli/serverStatus.js +15 -14
- package/dist/cli/serverStatus.js.map +1 -1
- package/dist/cli/taskChatRoute.js +3 -2
- package/dist/cli/taskChatRoute.js.map +1 -1
- package/dist/cli.js +165 -107
- package/dist/cli.js.map +1 -1
- package/dist/commands/CommandRouter.js +49 -3
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +6 -0
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/docs/technical-reference.md +13 -2
- package/docs/technical-reference.zh.md +13 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +6 -6
- package/src/cli/FeishuAppConfiguration.ts +130 -18
- package/src/cli/FeishuAppRegistration.ts +24 -9
- package/src/cli/Initializer.ts +25 -6
- package/src/cli/LocalControlClient.ts +9 -2
- package/src/cli/LocalControlServer.ts +2 -1
- package/src/cli/OptionalAuthorizationSkip.ts +51 -6
- package/src/cli/ServerStarter.ts +20 -3
- package/src/cli/SkillRegistry.ts +17 -4
- package/src/cli/VerificationOutput.ts +4 -1
- package/src/cli/help.ts +50 -1
- package/src/cli/i18n.ts +38 -0
- package/src/cli/initOptions.ts +17 -4
- package/src/cli/packageVersion.ts +5 -1
- package/src/cli/profile.ts +13 -3
- package/src/cli/serverStatus.ts +19 -14
- package/src/cli/taskChatRoute.ts +9 -2
- package/src/cli.ts +290 -103
- package/src/commands/CommandRouter.ts +58 -3
- package/src/proxy/ProxySessionController.ts +6 -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
|
-
|
|
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(
|
|
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)
|
|
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(
|
|
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(
|
|
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)
|
|
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 (
|
|
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
|
-
|
|
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
|
-
?
|
|
241
|
-
|
|
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
|
-
?
|
|
249
|
-
|
|
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(
|
|
263
|
-
process.stdout.write(
|
|
264
|
-
process.stdout.write(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
452
|
-
|
|
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
|
|
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(
|
|
469
|
-
process.stdout.write(
|
|
470
|
-
process.stdout.write(
|
|
471
|
-
process.stdout.write(
|
|
472
|
-
process.stdout.write(
|
|
473
|
-
process.stdout.write(
|
|
474
|
-
process.stdout.write(
|
|
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")
|
|
479
|
-
|
|
480
|
-
|
|
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)
|
|
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)
|
|
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(
|
|
490
|
-
process.stdout.write(
|
|
491
|
-
process.stdout.write(
|
|
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(
|
|
494
|
-
|
|
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(
|
|
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(
|
|
581
|
+
process.stdout.write(`${cliText("Reset backup: ", "重置备份:")}${result.reset.backupPath}\n`);
|
|
502
582
|
}
|
|
503
|
-
process.stdout.write(
|
|
504
|
-
process.stdout.write(
|
|
505
|
-
process.stdout.write(
|
|
506
|
-
process.stdout.write(
|
|
507
|
-
process.stdout.write(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
627
|
+
process.stdout.write(cliText(
|
|
628
|
+
"Lark configuration: initialization continued with missing configuration\n",
|
|
629
|
+
"飞书配置:仍有配置缺失,初始化已继续\n",
|
|
630
|
+
));
|
|
534
631
|
}
|
|
535
|
-
|
|
632
|
+
printFeishuConfigurationWarnings(configuration.remaining);
|
|
536
633
|
} else if (result.feishu.configuration?.status === "ready") {
|
|
537
|
-
process.stdout.write(
|
|
634
|
+
process.stdout.write(cliText(
|
|
635
|
+
"Lark configuration: scopes, events, and callbacks are ready\n",
|
|
636
|
+
"飞书配置:权限、事件和回调均已就绪\n",
|
|
637
|
+
));
|
|
538
638
|
}
|
|
539
|
-
process.stdout.write(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
|
598
|
-
process.stdout.write(
|
|
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(
|
|
743
|
+
process.stdout.write(`${cliText("Inactive scopes: ", "未生效权限:")}${missing.scopes.join(", ")}\n`);
|
|
601
744
|
}
|
|
602
745
|
if (missing.events.length > 0) {
|
|
603
|
-
process.stdout.write(
|
|
746
|
+
process.stdout.write(`${cliText("Inactive events: ", "未生效事件:")}${missing.events.join(", ")}\n`);
|
|
604
747
|
}
|
|
605
748
|
if (missing.callbacks.length > 0) {
|
|
606
|
-
process.stdout.write(
|
|
749
|
+
process.stdout.write(`${cliText("Inactive callbacks: ", "未生效回调:")}${missing.callbacks.join(", ")}\n`);
|
|
607
750
|
}
|
|
608
|
-
for (const feature of
|
|
609
|
-
process.stdout.write(
|
|
751
|
+
for (const feature of feishuFeatureWarnings(missing)) {
|
|
752
|
+
process.stdout.write(`${cliText("Affected feature: ", "受影响功能:")}${feature}\n`);
|
|
610
753
|
}
|
|
611
754
|
}
|
|
612
755
|
|
|
613
|
-
function
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
652
|
-
? message
|
|
653
|
-
:
|
|
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 {
|