@keyou007/agent-bot 0.1.15 → 0.1.16-alpha.1
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 +10 -0
- package/README.md +1 -1
- package/README.zh.md +1 -1
- package/dist/cli/AgentPrerequisites.js +5 -0
- package/dist/cli/AgentPrerequisites.js.map +1 -1
- package/dist/cli/FeishuAppConfiguration.js +38 -0
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/FeishuConfigurationFeatures.js +86 -0
- package/dist/cli/FeishuConfigurationFeatures.js.map +1 -0
- package/dist/cli/InitUi.js +90 -0
- package/dist/cli/InitUi.js.map +1 -0
- package/dist/cli.js +401 -271
- package/dist/cli.js.map +1 -1
- package/npm-shrinkwrap.json +61 -2
- package/package.json +6 -2
- package/src/cli/AgentPrerequisites.ts +10 -0
- package/src/cli/FeishuAppConfiguration.ts +61 -0
- package/src/cli/FeishuConfigurationFeatures.ts +110 -0
- package/src/cli/InitUi.ts +135 -0
- package/src/cli.ts +551 -403
package/src/cli.ts
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { createInterface } from "node:readline";
|
|
6
5
|
import { loadConfig, loadConfigWithoutEnvironmentMutation } from "./config/loadConfig.js";
|
|
7
6
|
import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.js";
|
|
8
7
|
import {
|
|
@@ -50,24 +49,31 @@ import {
|
|
|
50
49
|
} from "./cli/FeishuAppRegistration.js";
|
|
51
50
|
import {
|
|
52
51
|
ensureFeishuAppConfiguration,
|
|
52
|
+
ensureFeishuGroupMessagePermission,
|
|
53
53
|
type EnsureFeishuAppConfigurationResult,
|
|
54
54
|
type FeishuConfigurationChallenge,
|
|
55
55
|
} from "./cli/FeishuAppConfiguration.js";
|
|
56
|
+
import {
|
|
57
|
+
feishuAffectedFeatures,
|
|
58
|
+
formatFeishuConfigurationFeatureIntroduction,
|
|
59
|
+
} from "./cli/FeishuConfigurationFeatures.js";
|
|
56
60
|
import { renderCliHelp } from "./cli/help.js";
|
|
57
61
|
import { cliLanguage, cliText, localizeCliErrorMessage } from "./cli/i18n.js";
|
|
58
62
|
import {
|
|
59
63
|
inspectSupportedAgent,
|
|
60
64
|
inspectSupportedAgents,
|
|
61
65
|
runSupportedAgentMaintenance,
|
|
66
|
+
selectAgentMaintenanceActions,
|
|
62
67
|
type SupportedAgentInspection,
|
|
63
68
|
} from "./cli/AgentPrerequisites.js";
|
|
64
69
|
import {
|
|
65
|
-
parseMaintenanceSelection,
|
|
66
|
-
resolveAgentConfigurationChoices,
|
|
67
|
-
resolveDefaultAgentChoice,
|
|
68
70
|
selectableDefaultAgents,
|
|
69
|
-
type SelectableDefaultAgent,
|
|
70
71
|
} from "./cli/DefaultAgentSelection.js";
|
|
72
|
+
import {
|
|
73
|
+
InitializationPromptCancelledError,
|
|
74
|
+
InitUi,
|
|
75
|
+
shouldUseInteractiveInitialization,
|
|
76
|
+
} from "./cli/InitUi.js";
|
|
71
77
|
import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
|
|
72
78
|
import { resolveSystemSkillsRoot, SkillRegistry, type SkillRegistrationStatus } from "./cli/SkillRegistry.js";
|
|
73
79
|
import { readPackageVersion } from "./cli/packageVersion.js";
|
|
@@ -129,6 +135,10 @@ import {
|
|
|
129
135
|
const args = process.argv.slice(2);
|
|
130
136
|
|
|
131
137
|
void main(args).catch((error: unknown) => {
|
|
138
|
+
if (error instanceof InitializationPromptCancelledError) {
|
|
139
|
+
process.exitCode = 130;
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
132
142
|
const message = error instanceof Error ? error.message : String(error);
|
|
133
143
|
process.stderr.write(`${cliText("Error: ", "错误:")}${localizeCliErrorMessage(message)}\n`);
|
|
134
144
|
process.exitCode = 1;
|
|
@@ -291,12 +301,21 @@ interface InitializedAgent extends SupportedAgentInspection {
|
|
|
291
301
|
};
|
|
292
302
|
}
|
|
293
303
|
|
|
304
|
+
interface FeishuInitializationContext {
|
|
305
|
+
result: InitCommandResult["feishu"];
|
|
306
|
+
credentials?: FeishuAppCredentials;
|
|
307
|
+
}
|
|
308
|
+
|
|
294
309
|
async function initCommand(
|
|
295
310
|
input: string[],
|
|
296
311
|
configPath: string | undefined,
|
|
297
312
|
): Promise<void> {
|
|
298
313
|
const options = parseInitCommandOptions(input);
|
|
299
314
|
const version = readPackageVersion();
|
|
315
|
+
const ui = new InitUi({
|
|
316
|
+
interactive: shouldUseInteractiveInitialization(options.json),
|
|
317
|
+
});
|
|
318
|
+
ui.start(cliText(`AgentBot ${version} setup`, `AgentBot ${version} 初始化`));
|
|
300
319
|
const previousInitialization = readInitializationReceipt(agentBotHome());
|
|
301
320
|
let initializationLock = options.reset
|
|
302
321
|
? acquireInitializationLock(agentBotHome())
|
|
@@ -306,18 +325,14 @@ async function initCommand(
|
|
|
306
325
|
try {
|
|
307
326
|
if (options.reset) await assertResetProfileServerStopped(configPath);
|
|
308
327
|
paths = initializeAgentBot({ configPath, reset: options.reset });
|
|
309
|
-
if (!options.json) printInitializationPaths(paths);
|
|
310
|
-
const inspectedAgents = await initializeSupportedAgents(options.json);
|
|
328
|
+
if (!options.json) printInitializationPaths(paths, ui);
|
|
329
|
+
const inspectedAgents = await initializeSupportedAgents(options.json, ui);
|
|
311
330
|
const defaultAgent = await configureAgentsAndDefault(
|
|
312
331
|
paths.config.path,
|
|
313
332
|
inspectedAgents,
|
|
314
333
|
options.json,
|
|
315
334
|
shouldConfigureAgentsDuringInitialization(paths.config.status),
|
|
316
|
-
|
|
317
|
-
const groupMessages = await configureGroupMessageResponse(
|
|
318
|
-
paths.config.path,
|
|
319
|
-
options.json,
|
|
320
|
-
shouldConfigureGroupMessagesDuringInitialization(paths.config.status),
|
|
335
|
+
ui,
|
|
321
336
|
);
|
|
322
337
|
const configuredAgents = readConfiguredAgentSelection(paths.config.path).agents.map((agent) => agent.name);
|
|
323
338
|
const configuredAgentNames = new Set(configuredAgents);
|
|
@@ -327,20 +342,33 @@ async function initCommand(
|
|
|
327
342
|
}));
|
|
328
343
|
initializationLock ??= acquireInitializationLock(paths.home.path);
|
|
329
344
|
cleanupFeishuCredentialTemporaryFiles(paths.env.path);
|
|
330
|
-
const
|
|
345
|
+
const feishuContext = await initializeFeishu(
|
|
331
346
|
paths,
|
|
332
347
|
options,
|
|
333
|
-
|
|
348
|
+
ui,
|
|
349
|
+
);
|
|
350
|
+
const groupMessages = await configureGroupMessageResponse(
|
|
351
|
+
paths.config.path,
|
|
352
|
+
options.json,
|
|
353
|
+
shouldConfigureGroupMessagesDuringInitialization(paths.config.status),
|
|
354
|
+
ui,
|
|
355
|
+
);
|
|
356
|
+
const feishu = await completeFeishuGroupMessageSetup(
|
|
357
|
+
feishuContext,
|
|
358
|
+
groupMessages.mode,
|
|
359
|
+
ui,
|
|
334
360
|
);
|
|
361
|
+
ui.step(cliText("Installing the AgentBot Skill", "正在安装 AgentBot Skill"));
|
|
335
362
|
const skill = bundledSkillRegistry().install();
|
|
336
363
|
initialized = { ...paths, agents, configuredAgents, defaultAgent, groupMessages, feishu, skill };
|
|
337
364
|
} finally {
|
|
338
365
|
initializationLock?.release();
|
|
339
366
|
}
|
|
340
367
|
|
|
341
|
-
if (!options.json) printInitializationResult(initialized);
|
|
368
|
+
if (!options.json) printInitializationResult(initialized, ui);
|
|
342
369
|
if (!options.json && !options.skipFeishu) {
|
|
343
|
-
|
|
370
|
+
if (ui.interactive) ui.step(cliText("Starting AgentBot", "正在启动 AgentBot"));
|
|
371
|
+
else process.stdout.write(cliText("\nStarting Agent Bot server...\n", "\n正在启动 Agent Bot 服务...\n"));
|
|
344
372
|
}
|
|
345
373
|
const server = await startInitializedServer({ skipFeishu: options.skipFeishu, configPath });
|
|
346
374
|
const welcomeKind = resolveInitializationWelcomeKind({
|
|
@@ -379,41 +407,59 @@ async function initCommand(
|
|
|
379
407
|
const result: InitCommandResult = { ...initialized, server, welcome };
|
|
380
408
|
if (options.json) printJson(result);
|
|
381
409
|
else {
|
|
382
|
-
printInitializationServerResult(server);
|
|
383
|
-
printInitializationWelcomeResult(welcome);
|
|
410
|
+
printInitializationServerResult(server, ui);
|
|
411
|
+
printInitializationWelcomeResult(welcome, ui);
|
|
412
|
+
ui.finish(cliText("AgentBot initialization completed", "AgentBot 初始化完成"));
|
|
384
413
|
}
|
|
385
414
|
}
|
|
386
415
|
|
|
387
|
-
async function initializeSupportedAgents(json: boolean): Promise<InitializedAgent[]> {
|
|
416
|
+
async function initializeSupportedAgents(json: boolean, ui: InitUi): Promise<InitializedAgent[]> {
|
|
388
417
|
const output = json ? process.stderr : process.stdout;
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
"\nAgent 设置\n正在检查 Codex 和 TraeX...\n",
|
|
392
|
-
));
|
|
393
|
-
const inspections = await inspectSupportedAgents();
|
|
394
|
-
for (const inspection of inspections) printAgentInspection(inspection, output);
|
|
395
|
-
const actionable = inspections.filter((inspection) => inspection.action);
|
|
396
|
-
if (actionable.length === 0) return inspections;
|
|
397
|
-
|
|
398
|
-
output.write(cliText("\nAvailable actions:\n", "\n可执行的操作:\n"));
|
|
399
|
-
for (const [index, inspection] of actionable.entries()) {
|
|
418
|
+
if (ui.interactive) ui.step(cliText("Checking installed Agents", "正在检查已安装的 Agent"));
|
|
419
|
+
else {
|
|
400
420
|
output.write(cliText(
|
|
401
|
-
|
|
402
|
-
|
|
421
|
+
"\nAgent setup\nChecking Codex and TraeX...\n",
|
|
422
|
+
"\nAgent 设置\n正在检查 Codex 和 TraeX...\n",
|
|
403
423
|
));
|
|
404
424
|
}
|
|
425
|
+
const inspections = await inspectSupportedAgents();
|
|
426
|
+
for (const inspection of inspections) {
|
|
427
|
+
if (ui.interactive) printInteractiveAgentInspection(inspection, ui);
|
|
428
|
+
else printAgentInspection(inspection, output);
|
|
429
|
+
}
|
|
430
|
+
const actionable = selectAgentMaintenanceActions(inspections);
|
|
431
|
+
if (actionable.length === 0) return inspections;
|
|
405
432
|
|
|
406
433
|
let selected = new Set<number>();
|
|
407
|
-
if (
|
|
434
|
+
if (ui.interactive) {
|
|
435
|
+
selected = new Set(await ui.multiselect({
|
|
436
|
+
message: cliText("Choose Agent maintenance actions", "请选择 Agent 安装或升级操作"),
|
|
437
|
+
options: actionable.map((inspection, index) => ({
|
|
438
|
+
value: index,
|
|
439
|
+
label: cliText(
|
|
440
|
+
`${inspection.action?.kind === "install" ? "Install" : "Upgrade"} ${inspection.name}`,
|
|
441
|
+
`${inspection.action?.kind === "install" ? "安装" : "升级"} ${inspection.name}`,
|
|
442
|
+
),
|
|
443
|
+
hint: inspection.action?.command,
|
|
444
|
+
})),
|
|
445
|
+
initialValues: [],
|
|
446
|
+
required: false,
|
|
447
|
+
}));
|
|
448
|
+
if (selected.size === 0) {
|
|
449
|
+
ui.info(cliText("Agent maintenance skipped", "已跳过 Agent 安装和升级"));
|
|
450
|
+
}
|
|
451
|
+
} else {
|
|
452
|
+
output.write(cliText("\nAvailable actions:\n", "\n可执行的操作:\n"));
|
|
453
|
+
for (const [index, inspection] of actionable.entries()) {
|
|
454
|
+
output.write(cliText(
|
|
455
|
+
` ${index + 1}. ${inspection.action?.kind === "install" ? "Install" : "Upgrade"} ${inspection.name}: ${inspection.action?.command}\n`,
|
|
456
|
+
` ${index + 1}. ${inspection.action?.kind === "install" ? "安装" : "升级"} ${inspection.name}:${inspection.action?.command}\n`,
|
|
457
|
+
));
|
|
458
|
+
}
|
|
408
459
|
output.write(cliText(
|
|
409
460
|
"No interactive terminal is available. Run the commands above manually if needed.\n",
|
|
410
461
|
"当前没有交互式终端,如有需要请手动执行上面的命令。\n",
|
|
411
462
|
));
|
|
412
|
-
} else {
|
|
413
|
-
selected = new Set(await promptForAgentMaintenance(actionable.length));
|
|
414
|
-
if (selected.size === 0) {
|
|
415
|
-
output.write(cliText("No Agent changes selected.\n", "已跳过 Agent 安装和升级。\n"));
|
|
416
|
-
}
|
|
417
463
|
}
|
|
418
464
|
|
|
419
465
|
const initialized: InitializedAgent[] = [];
|
|
@@ -426,25 +472,29 @@ async function initializeSupportedAgents(json: boolean): Promise<InitializedAgen
|
|
|
426
472
|
if (!selected.has(actionIndex)) {
|
|
427
473
|
initialized.push({
|
|
428
474
|
...inspection,
|
|
429
|
-
assistance: { status:
|
|
475
|
+
assistance: { status: ui.interactive ? "skipped" : "unavailable" },
|
|
430
476
|
});
|
|
431
477
|
continue;
|
|
432
478
|
}
|
|
433
479
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
)
|
|
480
|
+
const actionMessage = cliText(
|
|
481
|
+
`Running ${inspection.name} ${inspection.action.kind}: ${inspection.action.command}`,
|
|
482
|
+
`正在${inspection.action.kind === "install" ? "安装" : "升级"} ${inspection.name}:${inspection.action.command}`,
|
|
483
|
+
);
|
|
484
|
+
if (ui.interactive) ui.step(actionMessage);
|
|
485
|
+
else output.write(`\n${actionMessage}\n`);
|
|
438
486
|
const maintenance = await runSupportedAgentMaintenance(inspection.id, inspection.action.kind);
|
|
439
487
|
if (maintenance.status !== 0 || maintenance.error) {
|
|
440
488
|
const error = maintenance.error ?? cliText(
|
|
441
489
|
`Command exited with code ${maintenance.status ?? "unknown"}.`,
|
|
442
490
|
`命令退出码为 ${maintenance.status ?? "未知"}。`,
|
|
443
491
|
);
|
|
444
|
-
|
|
445
|
-
`${inspection.name} ${inspection.action.kind} did not complete; initialization will continue
|
|
446
|
-
`${inspection.name}${inspection.action.kind === "install" ? "安装" : "升级"}
|
|
447
|
-
)
|
|
492
|
+
const warning = cliText(
|
|
493
|
+
`${inspection.name} ${inspection.action.kind} did not complete; initialization will continue.`,
|
|
494
|
+
`${inspection.name}${inspection.action.kind === "install" ? "安装" : "升级"}未完成,初始化将继续。`,
|
|
495
|
+
);
|
|
496
|
+
if (ui.interactive) ui.warn(warning);
|
|
497
|
+
else output.write(`${warning}\n`);
|
|
448
498
|
initialized.push({ ...inspection, assistance: { status: "failed", error } });
|
|
449
499
|
continue;
|
|
450
500
|
}
|
|
@@ -452,15 +502,19 @@ async function initializeSupportedAgents(json: boolean): Promise<InitializedAgen
|
|
|
452
502
|
refreshCurrentProcessPath();
|
|
453
503
|
const refreshed = await inspectSupportedAgent(inspection.id);
|
|
454
504
|
if (refreshed.installedVersion) {
|
|
455
|
-
|
|
456
|
-
`${inspection.name} is ready (${refreshed.installedVersion})
|
|
457
|
-
`${inspection.name} 已就绪(${refreshed.installedVersion}
|
|
458
|
-
)
|
|
505
|
+
const ready = cliText(
|
|
506
|
+
`${inspection.name} is ready (${refreshed.installedVersion}).`,
|
|
507
|
+
`${inspection.name} 已就绪(${refreshed.installedVersion})。`,
|
|
508
|
+
);
|
|
509
|
+
if (ui.interactive) ui.success(ready);
|
|
510
|
+
else output.write(`${ready}\n`);
|
|
459
511
|
} else {
|
|
460
|
-
|
|
461
|
-
`${inspection.name} command completed, but the CLI is not visible in this process. Open a new terminal and run agentbot init again
|
|
462
|
-
`${inspection.name} 命令已完成,但当前进程仍无法找到该 CLI。请打开新终端后重新运行 agentbot init
|
|
463
|
-
)
|
|
512
|
+
const warning = cliText(
|
|
513
|
+
`${inspection.name} command completed, but the CLI is not visible in this process. Open a new terminal and run agentbot init again.`,
|
|
514
|
+
`${inspection.name} 命令已完成,但当前进程仍无法找到该 CLI。请打开新终端后重新运行 agentbot init。`,
|
|
515
|
+
);
|
|
516
|
+
if (ui.interactive) ui.warn(warning);
|
|
517
|
+
else output.write(`${warning}\n`);
|
|
464
518
|
}
|
|
465
519
|
initialized.push({ ...refreshed, assistance: { status: "completed" } });
|
|
466
520
|
}
|
|
@@ -472,6 +526,7 @@ async function configureAgentsAndDefault(
|
|
|
472
526
|
inspections: InitializedAgent[],
|
|
473
527
|
json: boolean,
|
|
474
528
|
configureAgents: boolean,
|
|
529
|
+
ui: InitUi,
|
|
475
530
|
): Promise<InitCommandResult["defaultAgent"]> {
|
|
476
531
|
const output = json ? process.stderr : process.stdout;
|
|
477
532
|
const configured = readConfiguredAgentSelection(configPath);
|
|
@@ -486,10 +541,12 @@ async function configureAgentsAndDefault(
|
|
|
486
541
|
"现有 Profile 没有有效的默认 Agent。请在 config.yaml 中设置 defaults.agent,然后重新运行 agentbot init。",
|
|
487
542
|
));
|
|
488
543
|
}
|
|
489
|
-
|
|
490
|
-
`Default Agent: ${configured.defaultAgent} (unchanged)
|
|
491
|
-
`默认 Agent:${configured.defaultAgent}
|
|
492
|
-
)
|
|
544
|
+
const message = cliText(
|
|
545
|
+
`Default Agent: ${configured.defaultAgent} (unchanged)`,
|
|
546
|
+
`默认 Agent:${configured.defaultAgent}(保持不变)`,
|
|
547
|
+
);
|
|
548
|
+
if (ui.interactive) ui.info(message);
|
|
549
|
+
else output.write(`${message}\n`);
|
|
493
550
|
return { name: configured.defaultAgent!, status: "existing" };
|
|
494
551
|
}
|
|
495
552
|
|
|
@@ -502,34 +559,42 @@ async function configureAgentsAndDefault(
|
|
|
502
559
|
}
|
|
503
560
|
|
|
504
561
|
let selectedAgents = choices;
|
|
505
|
-
if (
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
562
|
+
if (ui.interactive && choices.length > 1) {
|
|
563
|
+
const selectedIndexes = await ui.multiselect({
|
|
564
|
+
message: cliText("Choose Agents to configure", "请选择要配置的 Agent"),
|
|
565
|
+
options: choices.map((choice, index) => ({
|
|
566
|
+
value: index,
|
|
567
|
+
label: `${choice.name} - ${choice.title}`,
|
|
568
|
+
hint: choice.installedVersion,
|
|
569
|
+
})),
|
|
570
|
+
initialValues: choices.map((_, index) => index),
|
|
571
|
+
required: true,
|
|
572
|
+
});
|
|
511
573
|
selectedAgents = selectedIndexes.flatMap((index) => choices[index] ? [choices[index]!] : []);
|
|
512
574
|
}
|
|
513
575
|
|
|
514
576
|
const selectedNames = selectedAgents.map((agent) => agent.name);
|
|
515
|
-
|
|
516
|
-
`Configured Agents: ${selectedNames.join(", ")}
|
|
517
|
-
`已配置 Agent:${selectedNames.join("、")}
|
|
518
|
-
)
|
|
577
|
+
const configuredMessage = cliText(
|
|
578
|
+
`Configured Agents: ${selectedNames.join(", ")}`,
|
|
579
|
+
`已配置 Agent:${selectedNames.join("、")}`,
|
|
580
|
+
);
|
|
581
|
+
if (ui.interactive) ui.success(configuredMessage);
|
|
582
|
+
else output.write(`${configuredMessage}\n`);
|
|
519
583
|
|
|
520
584
|
let selectedDefault = selectedAgents[0]!;
|
|
521
585
|
if (selectedAgents.length > 1) {
|
|
522
|
-
if (
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
:
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
586
|
+
if (ui.interactive) {
|
|
587
|
+
const currentIndex = selectedAgents.findIndex((choice) => choice.name === configured.defaultAgent);
|
|
588
|
+
const selectedIndex = await ui.select({
|
|
589
|
+
message: cliText("Choose the default Agent", "请选择默认 Agent"),
|
|
590
|
+
options: selectedAgents.map((choice, index) => ({
|
|
591
|
+
value: index,
|
|
592
|
+
label: `${choice.name} - ${choice.title}`,
|
|
593
|
+
hint: choice.installedVersion,
|
|
594
|
+
})),
|
|
595
|
+
initialValue: currentIndex >= 0 ? currentIndex : 0,
|
|
596
|
+
});
|
|
597
|
+
selectedDefault = selectedAgents[selectedIndex]!;
|
|
533
598
|
} else {
|
|
534
599
|
selectedDefault = selectedAgents.find((agent) => agent.name === configured.defaultAgent)
|
|
535
600
|
?? selectedAgents[0]!;
|
|
@@ -537,10 +602,12 @@ async function configureAgentsAndDefault(
|
|
|
537
602
|
}
|
|
538
603
|
|
|
539
604
|
const updated = writeConfiguredAgentSelection(configPath, selectedNames, selectedDefault.name);
|
|
540
|
-
|
|
541
|
-
`Default Agent: ${selectedDefault.name}${
|
|
542
|
-
`默认 Agent:${selectedDefault.name}${
|
|
543
|
-
)
|
|
605
|
+
const defaultMessage = cliText(
|
|
606
|
+
`Default Agent: ${selectedDefault.name}${ui.interactive ? "" : " (selected automatically)"}`,
|
|
607
|
+
`默认 Agent:${selectedDefault.name}${ui.interactive ? "" : "(已自动选择)"}`,
|
|
608
|
+
);
|
|
609
|
+
if (ui.interactive) ui.success(defaultMessage);
|
|
610
|
+
else output.write(`${defaultMessage}\n`);
|
|
544
611
|
return { name: selectedDefault.name, status: updated ? "selected" : "existing" };
|
|
545
612
|
}
|
|
546
613
|
|
|
@@ -548,19 +615,47 @@ async function configureGroupMessageResponse(
|
|
|
548
615
|
configPath: string,
|
|
549
616
|
json: boolean,
|
|
550
617
|
configure: boolean,
|
|
618
|
+
ui: InitUi,
|
|
551
619
|
): Promise<InitCommandResult["groupMessages"]> {
|
|
552
620
|
const output = json ? process.stderr : process.stdout;
|
|
553
621
|
if (!configure) {
|
|
554
|
-
|
|
622
|
+
const mode = readGroupMessageResponseMode(configPath);
|
|
623
|
+
if (ui.interactive) {
|
|
624
|
+
ui.info(cliText(
|
|
625
|
+
`Group message response: ${mode === "all" ? "receive all group messages" : "require an explicit @ mention"} (unchanged)`,
|
|
626
|
+
`群消息响应方式:${mode === "all" ? "接收所有群消息" : "必须明确 @ 机器人"}(保持不变)`,
|
|
627
|
+
));
|
|
628
|
+
}
|
|
629
|
+
return { mode, status: "existing" };
|
|
555
630
|
}
|
|
556
631
|
|
|
557
632
|
let mode: GroupMessageResponseMode = "mention-only";
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
633
|
+
if (ui.interactive) {
|
|
634
|
+
mode = await ui.select({
|
|
635
|
+
message: cliText(
|
|
636
|
+
"Choose how AgentBot responds to group messages",
|
|
637
|
+
"请选择 AgentBot 响应群消息的方式",
|
|
638
|
+
),
|
|
639
|
+
options: [
|
|
640
|
+
{
|
|
641
|
+
value: "mention-only",
|
|
642
|
+
label: cliText("Require an explicit @ mention", "必须明确 @ 机器人"),
|
|
643
|
+
hint: cliText(
|
|
644
|
+
"Recommended; no additional manual permission is required",
|
|
645
|
+
"推荐;不需要额外手动申请权限",
|
|
646
|
+
),
|
|
647
|
+
},
|
|
648
|
+
{
|
|
649
|
+
value: "all",
|
|
650
|
+
label: cliText("Receive all group messages", "接收所有群消息"),
|
|
651
|
+
hint: cliText(
|
|
652
|
+
"Respond without @; requires a final manual Lark permission step",
|
|
653
|
+
"无需 @ 即可响应;最后需要手动开通飞书权限",
|
|
654
|
+
),
|
|
655
|
+
},
|
|
656
|
+
],
|
|
657
|
+
initialValue: "mention-only",
|
|
658
|
+
});
|
|
564
659
|
} else {
|
|
565
660
|
output.write(cliText(
|
|
566
661
|
"No interactive terminal is available. Group messages will require an explicit @ mention, and the all-group-message permission will not be requested.\n",
|
|
@@ -569,169 +664,44 @@ async function configureGroupMessageResponse(
|
|
|
569
664
|
}
|
|
570
665
|
|
|
571
666
|
writeGroupMessageResponseMode(configPath, mode);
|
|
572
|
-
|
|
573
|
-
`Group message response: ${mode === "all" ? "receive all group messages" : "require an explicit @ mention"}
|
|
574
|
-
`群消息响应方式:${mode === "all" ? "接收所有群消息" : "必须明确 @ 机器人"}
|
|
575
|
-
)
|
|
667
|
+
const message = cliText(
|
|
668
|
+
`Group message response: ${mode === "all" ? "receive all group messages" : "require an explicit @ mention"}`,
|
|
669
|
+
`群消息响应方式:${mode === "all" ? "接收所有群消息" : "必须明确 @ 机器人"}`,
|
|
670
|
+
);
|
|
671
|
+
if (ui.interactive) ui.success(message);
|
|
672
|
+
else output.write(`${message}\n`);
|
|
576
673
|
return { mode, status: "selected" };
|
|
577
674
|
}
|
|
578
675
|
|
|
579
|
-
function
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
let settled = false;
|
|
583
|
-
const finish = (mode: GroupMessageResponseMode): void => {
|
|
584
|
-
settled = true;
|
|
585
|
-
readline.close();
|
|
586
|
-
resolve(mode);
|
|
587
|
-
};
|
|
588
|
-
const ask = (): void => {
|
|
589
|
-
readline.question(cliText(
|
|
590
|
-
"Select 1 or 2 [2]: ",
|
|
591
|
-
"请选择 1 或 2 [2]:",
|
|
592
|
-
), (answer) => {
|
|
593
|
-
const normalized = answer.trim().toLowerCase();
|
|
594
|
-
if (normalized === "1") {
|
|
595
|
-
finish("all");
|
|
596
|
-
return;
|
|
597
|
-
}
|
|
598
|
-
if (normalized === "" || normalized === "2") {
|
|
599
|
-
finish("mention-only");
|
|
600
|
-
return;
|
|
601
|
-
}
|
|
602
|
-
process.stderr.write(cliText(
|
|
603
|
-
"Enter 1 to receive all group messages, or 2 to require an @ mention.\n",
|
|
604
|
-
"请输入 1 接收所有群消息,或输入 2 要求明确 @ 机器人。\n",
|
|
605
|
-
));
|
|
606
|
-
ask();
|
|
607
|
-
});
|
|
608
|
-
};
|
|
609
|
-
listenForPromptCancellation(readline, () => {
|
|
610
|
-
settled = true;
|
|
611
|
-
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
612
|
-
}, () => settled);
|
|
613
|
-
ask();
|
|
614
|
-
});
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
function promptForAgentMaintenance(choiceCount: number): Promise<number[]> {
|
|
618
|
-
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
619
|
-
return new Promise((resolve, reject) => {
|
|
620
|
-
let settled = false;
|
|
621
|
-
const finish = (selection: number[]): void => {
|
|
622
|
-
settled = true;
|
|
623
|
-
readline.close();
|
|
624
|
-
resolve(selection);
|
|
625
|
-
};
|
|
626
|
-
const ask = (): void => {
|
|
627
|
-
readline.question(cliText(
|
|
628
|
-
"Enter action numbers separated by commas, type all, or press Enter to skip: ",
|
|
629
|
-
"请输入要执行的操作编号(多个用逗号分隔),输入 all 选择全部,直接回车跳过:",
|
|
630
|
-
), (answer) => {
|
|
631
|
-
const selection = parseMaintenanceSelection(answer, choiceCount);
|
|
632
|
-
if (selection) {
|
|
633
|
-
finish(selection);
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
process.stderr.write(cliText(
|
|
637
|
-
`Enter numbers from 1 to ${choiceCount}, all, or press Enter to skip.\n`,
|
|
638
|
-
`请输入 1 到 ${choiceCount} 之间的编号、all,或直接回车跳过。\n`,
|
|
639
|
-
));
|
|
640
|
-
ask();
|
|
641
|
-
});
|
|
642
|
-
};
|
|
643
|
-
listenForPromptCancellation(readline, () => {
|
|
644
|
-
settled = true;
|
|
645
|
-
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
646
|
-
}, () => settled);
|
|
647
|
-
ask();
|
|
648
|
-
});
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
function promptForConfiguredAgents(choices: SelectableDefaultAgent[]): Promise<number[]> {
|
|
652
|
-
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
653
|
-
return new Promise((resolve, reject) => {
|
|
654
|
-
let settled = false;
|
|
655
|
-
const finish = (selection: number[]): void => {
|
|
656
|
-
settled = true;
|
|
657
|
-
readline.close();
|
|
658
|
-
resolve(selection);
|
|
659
|
-
};
|
|
660
|
-
const ask = (): void => {
|
|
661
|
-
readline.question(cliText(
|
|
662
|
-
"Enter numbers or Agent names separated by commas, type all, or press Enter for all: ",
|
|
663
|
-
"请输入 Agent 编号或标准名(多个用逗号分隔),输入 all 或直接回车选择全部:",
|
|
664
|
-
), (answer) => {
|
|
665
|
-
const selection = resolveAgentConfigurationChoices(answer, choices);
|
|
666
|
-
if (selection) {
|
|
667
|
-
finish(selection);
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
process.stderr.write(cliText(
|
|
671
|
-
`Select at least one Agent using numbers from 1 to ${choices.length} or standard names.\n`,
|
|
672
|
-
`请使用 1 到 ${choices.length} 之间的编号或标准名,至少选择一个 Agent。\n`,
|
|
673
|
-
));
|
|
674
|
-
ask();
|
|
675
|
-
});
|
|
676
|
-
};
|
|
677
|
-
listenForPromptCancellation(readline, () => {
|
|
678
|
-
settled = true;
|
|
679
|
-
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
680
|
-
}, () => settled);
|
|
681
|
-
ask();
|
|
682
|
-
});
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
function promptForDefaultAgent(
|
|
686
|
-
choices: SelectableDefaultAgent[],
|
|
687
|
-
currentAgent?: string,
|
|
688
|
-
): Promise<number> {
|
|
689
|
-
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
690
|
-
return new Promise((resolve, reject) => {
|
|
691
|
-
let settled = false;
|
|
692
|
-
const finish = (index: number): void => {
|
|
693
|
-
settled = true;
|
|
694
|
-
readline.close();
|
|
695
|
-
resolve(index);
|
|
696
|
-
};
|
|
697
|
-
const ask = (): void => {
|
|
698
|
-
const currentAvailable = choices.some((choice) => choice.name === currentAgent);
|
|
699
|
-
readline.question(cliText(
|
|
700
|
-
`Enter a number or Agent name${currentAvailable ? ` [${currentAgent}]` : ""}: `,
|
|
701
|
-
`请输入编号或 Agent 标准名${currentAvailable ? ` [${currentAgent}]` : ""}:`,
|
|
702
|
-
), (answer) => {
|
|
703
|
-
const index = resolveDefaultAgentChoice(answer, choices, currentAgent);
|
|
704
|
-
if (index !== undefined) {
|
|
705
|
-
finish(index);
|
|
706
|
-
return;
|
|
707
|
-
}
|
|
708
|
-
process.stderr.write(cliText(
|
|
709
|
-
`Enter a number from 1 to ${choices.length} or an Agent standard name.\n`,
|
|
710
|
-
`请输入 1 到 ${choices.length} 之间的编号,或 Agent 标准名。\n`,
|
|
711
|
-
));
|
|
712
|
-
ask();
|
|
713
|
-
});
|
|
714
|
-
};
|
|
715
|
-
listenForPromptCancellation(readline, () => {
|
|
716
|
-
settled = true;
|
|
717
|
-
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
718
|
-
}, () => settled);
|
|
719
|
-
ask();
|
|
720
|
-
});
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
function listenForPromptCancellation(
|
|
724
|
-
readline: ReturnType<typeof createInterface>,
|
|
725
|
-
cancel: () => void,
|
|
726
|
-
isSettled: () => boolean,
|
|
676
|
+
function printInteractiveAgentInspection(
|
|
677
|
+
inspection: SupportedAgentInspection,
|
|
678
|
+
ui: InitUi,
|
|
727
679
|
): void {
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
}
|
|
680
|
+
if (inspection.state === "missing") {
|
|
681
|
+
ui.info(cliText(
|
|
682
|
+
`${inspection.name}: not installed`,
|
|
683
|
+
`${inspection.name}:未安装`,
|
|
684
|
+
));
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
if (inspection.state === "outdated") {
|
|
688
|
+
ui.warn(cliText(
|
|
689
|
+
`${inspection.name}: ${inspection.installedVersion}; update available (${inspection.latestVersion})`,
|
|
690
|
+
`${inspection.name}:${inspection.installedVersion};可升级到 ${inspection.latestVersion}`,
|
|
691
|
+
));
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (inspection.latestCheckFailed) {
|
|
695
|
+
ui.warn(cliText(
|
|
696
|
+
`${inspection.name}: ${inspection.installedVersion}; latest-version check unavailable`,
|
|
697
|
+
`${inspection.name}:${inspection.installedVersion};无法检查最新版本`,
|
|
698
|
+
));
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
ui.success(cliText(
|
|
702
|
+
`${inspection.name}: ${inspection.installedVersion} (up to date)`,
|
|
703
|
+
`${inspection.name}:${inspection.installedVersion}(已是最新)`,
|
|
704
|
+
));
|
|
735
705
|
}
|
|
736
706
|
|
|
737
707
|
function printAgentInspection(
|
|
@@ -776,30 +746,24 @@ function refreshCurrentProcessPath(): void {
|
|
|
776
746
|
async function initializeFeishu(
|
|
777
747
|
paths: InitializationResult,
|
|
778
748
|
options: InitCommandOptions,
|
|
779
|
-
|
|
780
|
-
): Promise<
|
|
749
|
+
ui: InitUi,
|
|
750
|
+
): Promise<FeishuInitializationContext> {
|
|
781
751
|
const existing = readFeishuCredentials(paths.env.path);
|
|
782
752
|
if (options.skipFeishu) {
|
|
783
753
|
return {
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
754
|
+
result: {
|
|
755
|
+
status: "skipped",
|
|
756
|
+
...(existing.appId ? { appId: existing.appId } : {}),
|
|
757
|
+
...(existing.appId && existing.appSecret
|
|
758
|
+
? { userOpenIdStatus: existing.userOpenId ? "configured" as const : "pending" as const }
|
|
759
|
+
: {}),
|
|
760
|
+
},
|
|
789
761
|
};
|
|
790
762
|
}
|
|
791
|
-
if (
|
|
792
|
-
|
|
793
|
-
}
|
|
763
|
+
if (ui.interactive) ui.step(cliText("Creating or connecting the Lark bot", "正在创建或连接飞书机器人"));
|
|
764
|
+
else if (!options.json) process.stdout.write(cliText("\nLark setup\n", "\n飞书设置\n"));
|
|
794
765
|
|
|
795
|
-
|
|
796
|
-
const onInterrupt = (): void => controller.abort(new Error(cliText(
|
|
797
|
-
"Initialization was cancelled.",
|
|
798
|
-
"初始化已取消。",
|
|
799
|
-
)));
|
|
800
|
-
process.once("SIGINT", onInterrupt);
|
|
801
|
-
process.once("SIGTERM", onInterrupt);
|
|
802
|
-
try {
|
|
766
|
+
return withInitializationCancellation(async (signal) => {
|
|
803
767
|
let credentials: FeishuAppCredentials;
|
|
804
768
|
let status: Exclude<FeishuInitializationStatus, "skipped">;
|
|
805
769
|
if (!shouldCreateFeishuApp(existing, options.reconfigureFeishu)) {
|
|
@@ -809,37 +773,134 @@ async function initializeFeishu(
|
|
|
809
773
|
userOpenId: existing.userOpenId,
|
|
810
774
|
};
|
|
811
775
|
status = "existing";
|
|
776
|
+
if (ui.interactive) {
|
|
777
|
+
ui.success(cliText(
|
|
778
|
+
`Using the existing Lark bot (${credentials.appId})`,
|
|
779
|
+
`正在使用已有飞书机器人(${credentials.appId})`,
|
|
780
|
+
));
|
|
781
|
+
}
|
|
812
782
|
} else {
|
|
813
783
|
if (existing.status === "incomplete" && !options.json) {
|
|
814
|
-
|
|
815
|
-
"
|
|
816
|
-
"
|
|
817
|
-
)
|
|
784
|
+
const message = cliText(
|
|
785
|
+
"Incomplete Lark credentials were found. A new bot will be created.",
|
|
786
|
+
"发现不完整的飞书凭据,将创建一个新机器人。",
|
|
787
|
+
);
|
|
788
|
+
if (ui.interactive) ui.warn(message);
|
|
789
|
+
else process.stdout.write(`\n${message}\n`);
|
|
818
790
|
}
|
|
819
791
|
credentials = await registerFeishuApp({
|
|
820
|
-
signal
|
|
821
|
-
onVerification: (challenge) => printFeishuVerification(challenge),
|
|
792
|
+
signal,
|
|
793
|
+
onVerification: (challenge) => printFeishuVerification(challenge, ui),
|
|
822
794
|
});
|
|
823
795
|
writeFeishuCredentials(paths.env.path, credentials);
|
|
824
796
|
status = "created";
|
|
797
|
+
if (ui.interactive) {
|
|
798
|
+
ui.success(cliText(
|
|
799
|
+
`Lark bot created (${credentials.appId})`,
|
|
800
|
+
`飞书机器人已创建(${credentials.appId})`,
|
|
801
|
+
));
|
|
802
|
+
}
|
|
825
803
|
}
|
|
826
804
|
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
805
|
+
const checking = cliText(
|
|
806
|
+
"Checking one-click Lark permissions, events, and callbacks",
|
|
807
|
+
"正在检查可一键授权的飞书权限、事件和回调",
|
|
808
|
+
);
|
|
809
|
+
if (ui.interactive) ui.step(checking);
|
|
810
|
+
else if (!options.json) process.stdout.write(`${checking}...\n`);
|
|
811
|
+
const configuration = await runFeishuConfigurationInteraction(
|
|
812
|
+
signal,
|
|
813
|
+
ui,
|
|
814
|
+
(interaction) => ensureFeishuAppConfiguration(credentials, {
|
|
815
|
+
...interaction,
|
|
816
|
+
respondToAllGroupMessages: false,
|
|
817
|
+
}),
|
|
818
|
+
);
|
|
819
|
+
if (ui.interactive) {
|
|
820
|
+
if (configuration.status === "partial") {
|
|
821
|
+
ui.warn(cliText(
|
|
822
|
+
"Some optional one-click Lark configuration was skipped or is not active yet",
|
|
823
|
+
"部分可选的一键飞书配置已跳过或尚未生效",
|
|
824
|
+
));
|
|
825
|
+
} else {
|
|
826
|
+
ui.success(cliText(
|
|
827
|
+
"One-click Lark permissions, events, and callbacks are ready",
|
|
828
|
+
"可一键授权的飞书权限、事件和回调已就绪",
|
|
829
|
+
));
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
credentials,
|
|
834
|
+
result: {
|
|
835
|
+
status,
|
|
836
|
+
appId: credentials.appId,
|
|
837
|
+
userOpenIdStatus: credentials.userOpenId ? "configured" : "pending",
|
|
838
|
+
configuration,
|
|
839
|
+
},
|
|
840
|
+
};
|
|
841
|
+
});
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
async function completeFeishuGroupMessageSetup(
|
|
845
|
+
context: FeishuInitializationContext,
|
|
846
|
+
mode: GroupMessageResponseMode,
|
|
847
|
+
ui: InitUi,
|
|
848
|
+
): Promise<InitCommandResult["feishu"]> {
|
|
849
|
+
if (mode !== "all" || !context.credentials) return context.result;
|
|
850
|
+
|
|
851
|
+
ui.step(cliText(
|
|
852
|
+
"Configuring permission for group messages without @ mentions",
|
|
853
|
+
"正在配置群消息免 @ 权限",
|
|
854
|
+
));
|
|
855
|
+
const groupConfiguration = await withInitializationCancellation((signal) =>
|
|
856
|
+
runFeishuConfigurationInteraction(
|
|
857
|
+
signal,
|
|
858
|
+
ui,
|
|
859
|
+
(interaction) => ensureFeishuGroupMessagePermission(context.credentials!, interaction),
|
|
860
|
+
)
|
|
861
|
+
);
|
|
862
|
+
if (ui.interactive) {
|
|
863
|
+
if (groupConfiguration.status === "partial") {
|
|
864
|
+
ui.warn(cliText(
|
|
865
|
+
"The all-group-message permission is not active; group messages still require an @ mention",
|
|
866
|
+
"接收所有群消息权限尚未生效;群消息仍需 @ 机器人",
|
|
867
|
+
));
|
|
868
|
+
} else {
|
|
869
|
+
ui.success(cliText(
|
|
870
|
+
"Group messages can be received without @ mentions",
|
|
871
|
+
"已可接收未 @ 机器人的群消息",
|
|
831
872
|
));
|
|
832
873
|
}
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
874
|
+
}
|
|
875
|
+
return {
|
|
876
|
+
...context.result,
|
|
877
|
+
configuration: mergeFeishuConfigurationResults(
|
|
878
|
+
context.result.configuration,
|
|
879
|
+
groupConfiguration,
|
|
880
|
+
),
|
|
881
|
+
};
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
async function runFeishuConfigurationInteraction(
|
|
885
|
+
signal: AbortSignal,
|
|
886
|
+
ui: InitUi,
|
|
887
|
+
operation: (options: {
|
|
888
|
+
signal: AbortSignal;
|
|
889
|
+
manualPermissionSkipSignal: AbortSignal;
|
|
890
|
+
optionalSkipSignal: AbortSignal;
|
|
891
|
+
onVerification: (challenge: FeishuConfigurationChallenge) => void;
|
|
892
|
+
}) => Promise<EnsureFeishuAppConfigurationResult>,
|
|
893
|
+
): Promise<EnsureFeishuAppConfigurationResult> {
|
|
894
|
+
const manualPermissionSkip = new AbortController();
|
|
895
|
+
const optionalSkip = new AbortController();
|
|
896
|
+
let skipListener: OptionalAuthorizationSkipListener | undefined;
|
|
897
|
+
try {
|
|
898
|
+
return await operation({
|
|
899
|
+
signal,
|
|
839
900
|
manualPermissionSkipSignal: manualPermissionSkip.signal,
|
|
840
901
|
optionalSkipSignal: optionalSkip.signal,
|
|
841
902
|
onVerification: (challenge) => {
|
|
842
|
-
printFeishuConfigurationVerification(challenge);
|
|
903
|
+
printFeishuConfigurationVerification(challenge, ui);
|
|
843
904
|
if (challenge.kind === "manual_scope" && challenge.blocking) {
|
|
844
905
|
skipListener?.close();
|
|
845
906
|
skipListener = listenForManualPermissionSkip(() => manualPermissionSkip.abort());
|
|
@@ -848,19 +909,58 @@ async function initializeFeishu(
|
|
|
848
909
|
skipListener = listenForOptionalAuthorizationSkip(() => optionalSkip.abort());
|
|
849
910
|
}
|
|
850
911
|
},
|
|
851
|
-
})
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
912
|
+
});
|
|
913
|
+
} finally {
|
|
914
|
+
skipListener?.close();
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
async function withInitializationCancellation<T>(
|
|
919
|
+
operation: (signal: AbortSignal) => Promise<T>,
|
|
920
|
+
): Promise<T> {
|
|
921
|
+
const controller = new AbortController();
|
|
922
|
+
const onInterrupt = (): void => controller.abort(new Error(cliText(
|
|
923
|
+
"Initialization was cancelled.",
|
|
924
|
+
"初始化已取消。",
|
|
925
|
+
)));
|
|
926
|
+
process.once("SIGINT", onInterrupt);
|
|
927
|
+
process.once("SIGTERM", onInterrupt);
|
|
928
|
+
try {
|
|
929
|
+
return await operation(controller.signal);
|
|
858
930
|
} finally {
|
|
859
931
|
process.removeListener("SIGINT", onInterrupt);
|
|
860
932
|
process.removeListener("SIGTERM", onInterrupt);
|
|
861
933
|
}
|
|
862
934
|
}
|
|
863
935
|
|
|
936
|
+
function mergeFeishuConfigurationResults(
|
|
937
|
+
first: EnsureFeishuAppConfigurationResult | undefined,
|
|
938
|
+
second: EnsureFeishuAppConfigurationResult,
|
|
939
|
+
): EnsureFeishuAppConfigurationResult {
|
|
940
|
+
if (!first) return second;
|
|
941
|
+
const added = mergeFeishuConfigurationSets(first.added, second.added);
|
|
942
|
+
const remaining = mergeFeishuConfigurationSets(first.remaining, second.remaining);
|
|
943
|
+
const addedCount = added.scopes.length + added.events.length + added.callbacks.length;
|
|
944
|
+
const remainingCount = remaining.scopes.length + remaining.events.length + remaining.callbacks.length;
|
|
945
|
+
return {
|
|
946
|
+
status: remainingCount > 0 ? "partial" : addedCount > 0 ? "updated" : "ready",
|
|
947
|
+
configuration: second.configuration,
|
|
948
|
+
added,
|
|
949
|
+
remaining,
|
|
950
|
+
};
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
function mergeFeishuConfigurationSets(
|
|
954
|
+
first: FeishuConfigurationChallenge["missing"],
|
|
955
|
+
second: FeishuConfigurationChallenge["missing"],
|
|
956
|
+
): FeishuConfigurationChallenge["missing"] {
|
|
957
|
+
return {
|
|
958
|
+
scopes: [...new Set([...first.scopes, ...second.scopes])],
|
|
959
|
+
events: [...new Set([...first.events, ...second.events])],
|
|
960
|
+
callbacks: [...new Set([...first.callbacks, ...second.callbacks])],
|
|
961
|
+
};
|
|
962
|
+
}
|
|
963
|
+
|
|
864
964
|
async function assertResetProfileServerStopped(configPath?: string): Promise<void> {
|
|
865
965
|
const endpoints = new Set<string>([controlEndpoint(defaultSqlitePath())]);
|
|
866
966
|
const selectedConfigPath = configPath ?? process.env.AGENT_BOT_CONFIG;
|
|
@@ -1850,7 +1950,21 @@ function printSkillStatus(status: SkillRegistrationStatus): void {
|
|
|
1850
1950
|
}
|
|
1851
1951
|
}
|
|
1852
1952
|
|
|
1853
|
-
function printInitializationPaths(result: InitializationResult): void {
|
|
1953
|
+
function printInitializationPaths(result: InitializationResult, ui: InitUi): void {
|
|
1954
|
+
if (ui.interactive) {
|
|
1955
|
+
const lines = [
|
|
1956
|
+
...(result.reset
|
|
1957
|
+
? [`${cliText("Reset backup", "重置备份")}: ${result.reset.backupPath}`]
|
|
1958
|
+
: []),
|
|
1959
|
+
`${cliText("Home directory", "主目录")}: ${result.home.path} (${initializationStatusLabel(result.home.status)})`,
|
|
1960
|
+
`${cliText("Config file", "配置文件")}: ${result.config.path} (${initializationStatusLabel(result.config.status)})`,
|
|
1961
|
+
`${cliText("Environment file", "环境文件")}: ${result.env.path} (${initializationStatusLabel(result.env.status)})`,
|
|
1962
|
+
`${cliText("Data directory", "数据目录")}: ${result.data.path} (${initializationStatusLabel(result.data.status)})`,
|
|
1963
|
+
`${cliText("Log directory", "日志目录")}: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})`,
|
|
1964
|
+
];
|
|
1965
|
+
ui.note(cliText("Profile", "Profile"), lines.join("\n"));
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1854
1968
|
process.stdout.write(cliText(
|
|
1855
1969
|
"Profile setup\n",
|
|
1856
1970
|
"Profile 设置\n",
|
|
@@ -1865,7 +1979,44 @@ function printInitializationPaths(result: InitializationResult): void {
|
|
|
1865
1979
|
process.stdout.write(`${cliText("Log directory: ", "日志目录:")}${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
|
|
1866
1980
|
}
|
|
1867
1981
|
|
|
1868
|
-
function printInitializationResult(
|
|
1982
|
+
function printInitializationResult(
|
|
1983
|
+
result: Omit<InitCommandResult, "server" | "welcome">,
|
|
1984
|
+
ui: InitUi,
|
|
1985
|
+
): void {
|
|
1986
|
+
if (ui.interactive) {
|
|
1987
|
+
const feishuStatus = result.feishu.status === "created"
|
|
1988
|
+
? cliText(`created (${result.feishu.appId})`, `已创建(${result.feishu.appId})`)
|
|
1989
|
+
: result.feishu.status === "existing"
|
|
1990
|
+
? cliText(`existing (${result.feishu.appId})`, `已配置(${result.feishu.appId})`)
|
|
1991
|
+
: cliText("skipped", "已跳过");
|
|
1992
|
+
const configuration = result.feishu.configuration;
|
|
1993
|
+
const configurationStatus = configuration?.status === "partial"
|
|
1994
|
+
? cliText("partially configured", "部分配置未完成")
|
|
1995
|
+
: configuration
|
|
1996
|
+
? cliText("ready", "已就绪")
|
|
1997
|
+
: cliText("not checked", "未检查");
|
|
1998
|
+
ui.note(cliText("Initialization summary", "初始化摘要"), [
|
|
1999
|
+
`${cliText("Configured Agents", "已配置 Agent")}: ${result.configuredAgents.join(", ")}`,
|
|
2000
|
+
`${cliText("Default Agent", "默认 Agent")}: ${result.defaultAgent.name}`,
|
|
2001
|
+
`${cliText("Group messages", "群消息")}: ${result.groupMessages.mode === "all"
|
|
2002
|
+
? cliText("receive all", "接收所有消息")
|
|
2003
|
+
: cliText("require @ mention", "需要 @ 机器人")}`,
|
|
2004
|
+
`${cliText("Lark bot", "飞书机器人")}: ${feishuStatus}`,
|
|
2005
|
+
`${cliText("Lark configuration", "飞书配置")}: ${configurationStatus}`,
|
|
2006
|
+
`${cliText("AgentBot Skill", "AgentBot Skill")}: ${result.skill.status.targetPath}`,
|
|
2007
|
+
`${cliText("Config file", "配置文件")}: ${result.config.path}`,
|
|
2008
|
+
].join("\n"));
|
|
2009
|
+
if (configuration?.status === "partial") {
|
|
2010
|
+
ui.warn(cliText(
|
|
2011
|
+
"Initialization continued with some unavailable Lark features.",
|
|
2012
|
+
"部分飞书功能尚不可用,但初始化已继续。",
|
|
2013
|
+
));
|
|
2014
|
+
for (const feature of feishuAffectedFeatures(configuration.remaining)) {
|
|
2015
|
+
ui.warn(`${cliText("Affected feature", "受影响功能")}: ${feature}`);
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
return;
|
|
2019
|
+
}
|
|
1869
2020
|
process.stdout.write(cliText(
|
|
1870
2021
|
"\nAgent Bot initialization completed.\n",
|
|
1871
2022
|
"\nAgent Bot 初始化完成。\n",
|
|
@@ -1932,44 +2083,60 @@ function printInitializationResult(result: Omit<InitCommandResult, "server" | "w
|
|
|
1932
2083
|
process.stdout.write(`${cliText("Config file: ", "配置文件:")}${result.config.path}\n`);
|
|
1933
2084
|
}
|
|
1934
2085
|
|
|
1935
|
-
function printInitializationServerResult(result: InitCommandResult["server"]): void {
|
|
2086
|
+
function printInitializationServerResult(result: InitCommandResult["server"], ui: InitUi): void {
|
|
1936
2087
|
if (result.status === "skipped") {
|
|
1937
|
-
|
|
1938
|
-
"Agent Bot server: skipped (Lark is not configured; Console mode is available)
|
|
1939
|
-
"Agent Bot 服务:已跳过(未配置飞书;可使用 Console
|
|
1940
|
-
)
|
|
2088
|
+
const message = cliText(
|
|
2089
|
+
"Agent Bot server: skipped (Lark is not configured; Console mode is available)",
|
|
2090
|
+
"Agent Bot 服务:已跳过(未配置飞书;可使用 Console 模式)",
|
|
2091
|
+
);
|
|
2092
|
+
if (ui.interactive) ui.warn(message);
|
|
2093
|
+
else process.stdout.write(`${message}\n`);
|
|
1941
2094
|
return;
|
|
1942
2095
|
}
|
|
1943
2096
|
if (result.status === "restart-scheduled") {
|
|
1944
|
-
|
|
1945
|
-
"Agent Bot server: safe restart scheduled to load the current version
|
|
1946
|
-
"Agent Bot
|
|
1947
|
-
)
|
|
2097
|
+
const message = cliText(
|
|
2098
|
+
"Agent Bot server: safe restart scheduled to load the current version.",
|
|
2099
|
+
"Agent Bot 服务:已安排安全重启,以加载当前版本。",
|
|
2100
|
+
);
|
|
2101
|
+
if (ui.interactive) ui.success(message);
|
|
2102
|
+
else process.stdout.write(`${message}\n`);
|
|
1948
2103
|
return;
|
|
1949
2104
|
}
|
|
1950
|
-
|
|
2105
|
+
if (ui.interactive) {
|
|
2106
|
+
ui.success(result.status === "already-running"
|
|
2107
|
+
? cliText("Agent Bot server is already running.", "Agent Bot 服务已在运行。")
|
|
2108
|
+
: cliText("Agent Bot server started.", "Agent Bot 服务已启动。"));
|
|
2109
|
+
} else {
|
|
2110
|
+
printServerStartResult(result);
|
|
2111
|
+
}
|
|
1951
2112
|
}
|
|
1952
2113
|
|
|
1953
|
-
function printInitializationWelcomeResult(result: InitializationWelcomeResult): void {
|
|
2114
|
+
function printInitializationWelcomeResult(result: InitializationWelcomeResult, ui: InitUi): void {
|
|
1954
2115
|
if (result.status === "sent") {
|
|
1955
|
-
|
|
1956
|
-
"Welcome card: sent to your Lark private chat
|
|
1957
|
-
"
|
|
1958
|
-
)
|
|
2116
|
+
const message = cliText(
|
|
2117
|
+
"Welcome card: sent to your Lark private chat.",
|
|
2118
|
+
"欢迎卡片:已发送到你的飞书私聊。",
|
|
2119
|
+
);
|
|
2120
|
+
if (ui.interactive) ui.success(message);
|
|
2121
|
+
else process.stdout.write(`${message}\n`);
|
|
1959
2122
|
return;
|
|
1960
2123
|
}
|
|
1961
2124
|
if (result.status === "failed") {
|
|
1962
|
-
|
|
1963
|
-
`
|
|
1964
|
-
|
|
1965
|
-
)
|
|
2125
|
+
const message = cliText(
|
|
2126
|
+
`The private welcome card could not be sent: ${result.message}`,
|
|
2127
|
+
`无法发送私聊欢迎卡片:${result.message}`,
|
|
2128
|
+
);
|
|
2129
|
+
if (ui.interactive) ui.warn(message);
|
|
2130
|
+
else process.stderr.write(`${cliText("Warning: ", "警告:")}${message}\n`);
|
|
1966
2131
|
return;
|
|
1967
2132
|
}
|
|
1968
2133
|
if (result.reason === "missing-user-open-id") {
|
|
1969
|
-
|
|
1970
|
-
"
|
|
1971
|
-
"
|
|
1972
|
-
)
|
|
2134
|
+
const message = cliText(
|
|
2135
|
+
"The private welcome card was skipped because FEISHU_USER_OPEN_ID is not available. Send the bot a private message, then run agentbot init again.",
|
|
2136
|
+
"尚未获得 FEISHU_USER_OPEN_ID,无法发送私聊欢迎卡片。请先私聊机器人,再次运行 agentbot init。",
|
|
2137
|
+
);
|
|
2138
|
+
if (ui.interactive) ui.warn(message);
|
|
2139
|
+
else process.stderr.write(`${cliText("Warning: ", "警告:")}${message}\n`);
|
|
1973
2140
|
}
|
|
1974
2141
|
}
|
|
1975
2142
|
|
|
@@ -1981,38 +2148,61 @@ function printServerStartResult(result: ServerStartResult): void {
|
|
|
1981
2148
|
);
|
|
1982
2149
|
}
|
|
1983
2150
|
|
|
1984
|
-
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge): void {
|
|
2151
|
+
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge, ui: InitUi): void {
|
|
2152
|
+
if (ui.interactive) {
|
|
2153
|
+
ui.note(
|
|
2154
|
+
cliText("Create Lark bot", "创建飞书机器人"),
|
|
2155
|
+
cliText(
|
|
2156
|
+
"Open the link below and confirm creation. AgentBot will save the App ID and App Secret after Lark returns them.",
|
|
2157
|
+
"请打开下方链接并确认创建。飞书返回 App ID 和 App Secret 后,AgentBot 会自动保存。",
|
|
2158
|
+
),
|
|
2159
|
+
);
|
|
2160
|
+
}
|
|
1985
2161
|
printVerificationLink(
|
|
1986
2162
|
{
|
|
1987
2163
|
verificationUrl: challenge.verificationUrl,
|
|
1988
2164
|
},
|
|
1989
2165
|
);
|
|
1990
|
-
|
|
1991
|
-
`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation
|
|
1992
|
-
`链接将在约 ${Math.ceil(challenge.expiresIn / 60)}
|
|
1993
|
-
)
|
|
2166
|
+
const waiting = cliText(
|
|
2167
|
+
`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...`,
|
|
2168
|
+
`链接将在约 ${Math.ceil(challenge.expiresIn / 60)} 分钟后过期,正在等待确认...`,
|
|
2169
|
+
);
|
|
2170
|
+
if (ui.interactive) ui.info(waiting);
|
|
2171
|
+
else process.stderr.write(`${waiting}\n`);
|
|
1994
2172
|
}
|
|
1995
2173
|
|
|
1996
2174
|
function printFeishuConfigurationVerification(
|
|
1997
2175
|
challenge: FeishuConfigurationChallenge,
|
|
2176
|
+
ui: InitUi,
|
|
1998
2177
|
): void {
|
|
1999
|
-
|
|
2000
|
-
? cliText(
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
))
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
if (
|
|
2015
|
-
|
|
2178
|
+
const title = challenge.kind === "manual_scope"
|
|
2179
|
+
? cliText("Manual Lark permission", "手动开通飞书权限")
|
|
2180
|
+
: cliText("Lark authorization required", "需要飞书授权");
|
|
2181
|
+
const details = [
|
|
2182
|
+
...(challenge.missing.scopes.length > 0
|
|
2183
|
+
? [`${cliText("Scopes", "权限")}: ${challenge.missing.scopes.join(", ")}`]
|
|
2184
|
+
: []),
|
|
2185
|
+
...(challenge.missing.events.length > 0
|
|
2186
|
+
? [`${cliText("Events", "事件")}: ${challenge.missing.events.join(", ")}`]
|
|
2187
|
+
: []),
|
|
2188
|
+
...(challenge.missing.callbacks.length > 0
|
|
2189
|
+
? [`${cliText("Callbacks", "回调")}: ${challenge.missing.callbacks.join(", ")}`]
|
|
2190
|
+
: []),
|
|
2191
|
+
formatFeishuConfigurationFeatureIntroduction(challenge.missing).trim(),
|
|
2192
|
+
].filter(Boolean).join("\n");
|
|
2193
|
+
if (ui.interactive) {
|
|
2194
|
+
ui.note(title, details);
|
|
2195
|
+
} else {
|
|
2196
|
+
process.stderr.write(challenge.kind === "manual_scope"
|
|
2197
|
+
? cliText(
|
|
2198
|
+
"\nThe following Lark permission must be added manually in Developer Console:\n",
|
|
2199
|
+
"\n必须在飞书开发者后台手动添加以下权限:\n",
|
|
2200
|
+
)
|
|
2201
|
+
: cliText(
|
|
2202
|
+
"\nThe Lark app is missing the following configuration:\n",
|
|
2203
|
+
"\n飞书应用缺少以下配置:\n",
|
|
2204
|
+
));
|
|
2205
|
+
process.stderr.write(`${details}\n`);
|
|
2016
2206
|
}
|
|
2017
2207
|
printVerificationLink(
|
|
2018
2208
|
{
|
|
@@ -2023,27 +2213,32 @@ function printFeishuConfigurationVerification(
|
|
|
2023
2213
|
},
|
|
2024
2214
|
);
|
|
2025
2215
|
if (challenge.kind === "manual_scope") {
|
|
2026
|
-
|
|
2027
|
-
"Add the filtered permission, publish the app version, and complete tenant approval if required
|
|
2028
|
-
"
|
|
2029
|
-
)
|
|
2216
|
+
const guidance = cliText(
|
|
2217
|
+
"Add the filtered permission, publish the app version, and complete tenant approval if required.",
|
|
2218
|
+
"请添加已筛选的权限、发布应用版本,并在需要时完成租户管理员审批。",
|
|
2219
|
+
);
|
|
2220
|
+
if (ui.interactive) ui.info(guidance);
|
|
2221
|
+
else process.stderr.write(`${guidance}\n`);
|
|
2030
2222
|
}
|
|
2223
|
+
let waiting: string;
|
|
2031
2224
|
if (challenge.kind === "manual_scope") {
|
|
2032
|
-
|
|
2033
|
-
"Waiting up to 5 minutes for this permission to appear in the published app version
|
|
2034
|
-
"将等待最多 5
|
|
2035
|
-
)
|
|
2225
|
+
waiting = cliText(
|
|
2226
|
+
"Waiting up to 5 minutes for this permission to appear in the published app version...",
|
|
2227
|
+
"将等待最多 5 分钟,直到该权限出现在已发布的应用版本中...",
|
|
2228
|
+
);
|
|
2036
2229
|
} else if (challenge.blocking) {
|
|
2037
|
-
|
|
2038
|
-
"Waiting for the core scopes and message event to become active
|
|
2039
|
-
"
|
|
2040
|
-
)
|
|
2230
|
+
waiting = cliText(
|
|
2231
|
+
"Waiting for the core scopes and message event to become active...",
|
|
2232
|
+
"正在等待核心权限和消息事件生效...",
|
|
2233
|
+
);
|
|
2041
2234
|
} else {
|
|
2042
|
-
|
|
2043
|
-
"Waiting up to 5 minutes for these optional items to become active
|
|
2044
|
-
"将等待最多 5
|
|
2045
|
-
)
|
|
2235
|
+
waiting = cliText(
|
|
2236
|
+
"Waiting up to 5 minutes for these optional items to become active...",
|
|
2237
|
+
"将等待最多 5 分钟,直到这些可选配置生效...",
|
|
2238
|
+
);
|
|
2046
2239
|
}
|
|
2240
|
+
if (ui.interactive) ui.info(waiting);
|
|
2241
|
+
else process.stderr.write(`${waiting}\n`);
|
|
2047
2242
|
}
|
|
2048
2243
|
|
|
2049
2244
|
function printFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge["missing"]): void {
|
|
@@ -2060,58 +2255,11 @@ function printFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge[
|
|
|
2060
2255
|
if (missing.callbacks.length > 0) {
|
|
2061
2256
|
process.stdout.write(`${cliText("Inactive callbacks: ", "未生效回调:")}${missing.callbacks.join(", ")}\n`);
|
|
2062
2257
|
}
|
|
2063
|
-
for (const feature of
|
|
2258
|
+
for (const feature of feishuAffectedFeatures(missing)) {
|
|
2064
2259
|
process.stdout.write(`${cliText("Affected feature: ", "受影响功能:")}${feature}\n`);
|
|
2065
2260
|
}
|
|
2066
2261
|
}
|
|
2067
2262
|
|
|
2068
|
-
function feishuFeatureWarnings(missing: FeishuConfigurationChallenge["missing"]): string[] {
|
|
2069
|
-
const scopes = new Set(missing.scopes);
|
|
2070
|
-
const events = new Set(missing.events);
|
|
2071
|
-
const callbacks = new Set(missing.callbacks);
|
|
2072
|
-
const warnings: string[] = [];
|
|
2073
|
-
if (scopes.has("im:message.group_msg")) {
|
|
2074
|
-
warnings.push(cliText(
|
|
2075
|
-
"responding to ordinary group messages that do not mention the bot",
|
|
2076
|
-
"响应未 @ 机器人的普通群消息",
|
|
2077
|
-
));
|
|
2078
|
-
}
|
|
2079
|
-
if (scopes.has("im:chat:create")) {
|
|
2080
|
-
warnings.push(cliText(
|
|
2081
|
-
"creating Lark groups with /newgroup and /forkgroup",
|
|
2082
|
-
"使用 /newgroup 和 /forkgroup 创建飞书群",
|
|
2083
|
-
));
|
|
2084
|
-
}
|
|
2085
|
-
if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
|
|
2086
|
-
warnings.push(cliText(
|
|
2087
|
-
"synchronizing Agent Bot task titles after Lark group renames",
|
|
2088
|
-
"飞书群重命名后同步 Agent Bot 任务标题",
|
|
2089
|
-
));
|
|
2090
|
-
}
|
|
2091
|
-
if (scopes.has("im:message.reactions:write_only")) {
|
|
2092
|
-
warnings.push(cliText(
|
|
2093
|
-
"showing message-processing status with reactions",
|
|
2094
|
-
"使用 Reaction 显示消息处理状态",
|
|
2095
|
-
));
|
|
2096
|
-
}
|
|
2097
|
-
if (scopes.has("im:message:readonly")) {
|
|
2098
|
-
warnings.push(cliText("reading images from user messages", "读取用户消息中的图片"));
|
|
2099
|
-
}
|
|
2100
|
-
if (scopes.has("im:resource")) {
|
|
2101
|
-
warnings.push(cliText(
|
|
2102
|
-
"uploading images, sending local images, or setting group avatars",
|
|
2103
|
-
"上传图片、发送本地图片或设置群头像",
|
|
2104
|
-
));
|
|
2105
|
-
}
|
|
2106
|
-
if (scopes.has("im:message:update")) {
|
|
2107
|
-
warnings.push(cliText("updating sent progress cards", "更新已发送的进度卡片"));
|
|
2108
|
-
}
|
|
2109
|
-
if (callbacks.has("card.action.trigger")) {
|
|
2110
|
-
warnings.push(cliText("card buttons and interactive actions", "卡片按钮和交互操作"));
|
|
2111
|
-
}
|
|
2112
|
-
return warnings;
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
2263
|
function initializationStatusLabel(status: InitializationStatus): string {
|
|
2116
2264
|
if (status === "created") return cliText("created", "已创建");
|
|
2117
2265
|
if (status === "updated") return cliText("updated with missing settings", "已补全缺失配置");
|