@keyou007/agent-bot 0.1.11 → 0.1.13-alpha.0
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/.env.example +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +63 -44
- package/README.zh.md +63 -44
- package/config.example.yaml +8 -22
- package/dist/cli/AgentPrerequisites.js +262 -0
- package/dist/cli/AgentPrerequisites.js.map +1 -0
- package/dist/cli/DefaultAgentSelection.js +47 -0
- package/dist/cli/DefaultAgentSelection.js.map +1 -0
- package/dist/cli/FeishuAppConfiguration.js +1 -1
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/FeishuAppRegistration.js +2 -2
- package/dist/cli/FeishuAppRegistration.js.map +1 -1
- package/dist/cli/Initializer.js +166 -3
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/ServerStarter.js +7 -2
- package/dist/cli/ServerStarter.js.map +1 -1
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +24 -20
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +95 -0
- package/dist/cli/taskGroupOptions.js.map +1 -0
- package/dist/cli.js +268 -6
- package/dist/cli.js.map +1 -1
- package/dist/codex/AppServerConnection.js +3 -3
- package/dist/codex/AppServerConnection.js.map +1 -1
- package/dist/codex/CodexProcessManager.js +4 -4
- package/dist/codex/CodexProcessManager.js.map +1 -1
- package/dist/codex/CodexRuntime.js +63 -25
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/config/loadConfig.js +8 -22
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/schema.js +6 -1
- package/dist/config/schema.js.map +1 -1
- package/dist/console/ConsoleConnector.js +3 -3
- package/dist/console/ConsoleConnector.js.map +1 -1
- package/dist/console/ConsoleTurnPresenter.js +14 -7
- package/dist/console/ConsoleTurnPresenter.js.map +1 -1
- package/dist/deprecated-cli.js +5 -0
- package/dist/deprecated-cli.js.map +1 -0
- package/dist/feishu/CardRenderer.js +13 -12
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +9 -5
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/feishu/transport.js +1 -1
- package/dist/feishu/transport.js.map +1 -1
- package/dist/index.js +21 -3
- package/dist/index.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +2 -2
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +2 -1
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +128 -72
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/runtime/AcpRuntimeAdapter.js +3 -2
- package/dist/runtime/AcpRuntimeAdapter.js.map +1 -1
- package/dist/runtime/createAgentRuntimeRegistry.js +1 -1
- package/dist/runtime/createAgentRuntimeRegistry.js.map +1 -1
- package/dist/supervision/systemEnvironment.js +129 -0
- package/dist/supervision/systemEnvironment.js.map +1 -0
- package/dist/supervisor.js +13 -1
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +53 -34
- package/docs/technical-reference.zh.md +53 -34
- package/npm-shrinkwrap.json +4 -3
- package/package.json +9 -4
- package/skills/agent-bot/SKILL.md +72 -33
- package/src/cli/AgentPrerequisites.ts +377 -0
- package/src/cli/DefaultAgentSelection.ts +56 -0
- package/src/cli/FeishuAppConfiguration.ts +2 -2
- package/src/cli/FeishuAppRegistration.ts +4 -4
- package/src/cli/Initializer.ts +206 -5
- package/src/cli/ServerStarter.ts +8 -3
- package/src/cli/controlProtocol.ts +21 -1
- package/src/cli/help.ts +24 -20
- package/src/cli/taskGroupOptions.ts +149 -0
- package/src/cli.ts +391 -9
- package/src/codex/AppServerConnection.ts +3 -3
- package/src/codex/CodexProcessManager.ts +4 -4
- package/src/codex/CodexRuntime.ts +84 -31
- package/src/config/loadConfig.ts +8 -22
- package/src/config/schema.ts +7 -1
- package/src/console/ConsoleConnector.ts +3 -3
- package/src/console/ConsoleTurnPresenter.ts +20 -7
- package/src/deprecated-cli.ts +9 -0
- package/src/feishu/CardRenderer.ts +13 -12
- package/src/feishu/FeishuTurnPresenter.ts +15 -1
- package/src/feishu/transport.ts +1 -1
- package/src/index.ts +34 -3
- package/src/presentation/OutboundRouter.ts +15 -3
- package/src/presentation/TurnStateReducer.ts +2 -0
- package/src/presentation/turnViewTypes.ts +1 -0
- package/src/proxy/ProxySessionController.ts +217 -73
- package/src/runtime/AcpRuntimeAdapter.ts +3 -1
- package/src/runtime/createAgentRuntimeRegistry.ts +1 -1
- package/src/supervision/systemEnvironment.ts +174 -0
- package/src/supervisor.ts +12 -1
package/src/cli.ts
CHANGED
|
@@ -2,19 +2,23 @@
|
|
|
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";
|
|
5
6
|
import { loadConfig, loadConfigWithoutEnvironmentMutation } from "./config/loadConfig.js";
|
|
6
7
|
import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.js";
|
|
7
8
|
import {
|
|
8
9
|
controlEndpoint,
|
|
9
10
|
type ControlResponse,
|
|
11
|
+
type TaskGroupControlData,
|
|
10
12
|
type TaskStatusControlData,
|
|
11
13
|
} from "./cli/controlProtocol.js";
|
|
12
14
|
import {
|
|
13
15
|
acquireInitializationLock,
|
|
14
16
|
cleanupFeishuCredentialTemporaryFiles,
|
|
15
17
|
initializeAgentBot,
|
|
18
|
+
readConfiguredAgentSelection,
|
|
16
19
|
readFeishuCredentials,
|
|
17
20
|
shouldCreateFeishuApp,
|
|
21
|
+
writeDefaultAgent,
|
|
18
22
|
writeFeishuCredentials,
|
|
19
23
|
type InitializationResult,
|
|
20
24
|
type InitializationStatus,
|
|
@@ -37,6 +41,18 @@ import {
|
|
|
37
41
|
} from "./cli/FeishuAppConfiguration.js";
|
|
38
42
|
import { renderCliHelp } from "./cli/help.js";
|
|
39
43
|
import { cliLanguage, cliText, localizeCliErrorMessage } from "./cli/i18n.js";
|
|
44
|
+
import {
|
|
45
|
+
inspectSupportedAgent,
|
|
46
|
+
inspectSupportedAgents,
|
|
47
|
+
runSupportedAgentMaintenance,
|
|
48
|
+
type SupportedAgentInspection,
|
|
49
|
+
} from "./cli/AgentPrerequisites.js";
|
|
50
|
+
import {
|
|
51
|
+
parseMaintenanceSelection,
|
|
52
|
+
resolveDefaultAgentChoice,
|
|
53
|
+
selectableDefaultAgents,
|
|
54
|
+
type SelectableDefaultAgent,
|
|
55
|
+
} from "./cli/DefaultAgentSelection.js";
|
|
40
56
|
import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
|
|
41
57
|
import { resolveSystemSkillsRoot, SkillRegistry, type SkillRegistrationStatus } from "./cli/SkillRegistry.js";
|
|
42
58
|
import { readPackageVersion } from "./cli/packageVersion.js";
|
|
@@ -49,6 +65,11 @@ import {
|
|
|
49
65
|
type ServerStartResult,
|
|
50
66
|
} from "./cli/ServerStarter.js";
|
|
51
67
|
import { taskChatRoute } from "./cli/taskChatRoute.js";
|
|
68
|
+
import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
|
|
69
|
+
import {
|
|
70
|
+
parseTaskForkGroupOptions,
|
|
71
|
+
parseTaskNewGroupOptions,
|
|
72
|
+
} from "./cli/taskGroupOptions.js";
|
|
52
73
|
import { StateStore, type SessionRecord } from "./state/StateStore.js";
|
|
53
74
|
import {
|
|
54
75
|
nodeDiagnosticReportArguments,
|
|
@@ -99,14 +120,19 @@ async function main(input: string[]): Promise<void> {
|
|
|
99
120
|
return;
|
|
100
121
|
}
|
|
101
122
|
throw new Error(cliText(
|
|
102
|
-
`Unknown command: ${command}. Run
|
|
103
|
-
`未知命令:${command}。运行
|
|
123
|
+
`Unknown command: ${command}. Run agentbot --help for usage.`,
|
|
124
|
+
`未知命令:${command}。运行 agentbot --help 查看用法。`,
|
|
104
125
|
));
|
|
105
126
|
}
|
|
106
127
|
|
|
107
128
|
type FeishuInitializationStatus = "created" | "existing" | "skipped";
|
|
108
129
|
|
|
109
130
|
interface InitCommandResult extends InitializationResult {
|
|
131
|
+
agents: InitializedAgent[];
|
|
132
|
+
defaultAgent: {
|
|
133
|
+
name: string;
|
|
134
|
+
status: "selected" | "existing";
|
|
135
|
+
};
|
|
110
136
|
feishu: {
|
|
111
137
|
status: FeishuInitializationStatus;
|
|
112
138
|
appId?: string;
|
|
@@ -115,6 +141,13 @@ interface InitCommandResult extends InitializationResult {
|
|
|
115
141
|
server: InitializationServerResult;
|
|
116
142
|
}
|
|
117
143
|
|
|
144
|
+
interface InitializedAgent extends SupportedAgentInspection {
|
|
145
|
+
assistance?: {
|
|
146
|
+
status: "completed" | "skipped" | "unavailable" | "failed";
|
|
147
|
+
error?: string;
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
118
151
|
async function initCommand(
|
|
119
152
|
input: string[],
|
|
120
153
|
configPath: string | undefined,
|
|
@@ -130,13 +163,15 @@ async function initCommand(
|
|
|
130
163
|
if (options.reset) await assertResetProfileServerStopped(configPath);
|
|
131
164
|
paths = initializeAgentBot({ configPath, reset: options.reset });
|
|
132
165
|
if (!options.json) printInitializationPaths(paths);
|
|
166
|
+
const agents = await initializeSupportedAgents(options.json);
|
|
167
|
+
const defaultAgent = await selectDefaultAgent(paths.config.path, agents, options.json);
|
|
133
168
|
initializationLock ??= acquireInitializationLock(paths.home.path);
|
|
134
169
|
cleanupFeishuCredentialTemporaryFiles(paths.env.path);
|
|
135
170
|
const feishu = await initializeFeishu(
|
|
136
171
|
paths,
|
|
137
172
|
options,
|
|
138
173
|
);
|
|
139
|
-
initialized = { ...paths, feishu };
|
|
174
|
+
initialized = { ...paths, agents, defaultAgent, feishu };
|
|
140
175
|
} finally {
|
|
141
176
|
initializationLock?.release();
|
|
142
177
|
}
|
|
@@ -151,6 +186,259 @@ async function initCommand(
|
|
|
151
186
|
else printInitializationServerResult(server);
|
|
152
187
|
}
|
|
153
188
|
|
|
189
|
+
async function initializeSupportedAgents(json: boolean): Promise<InitializedAgent[]> {
|
|
190
|
+
const output = json ? process.stderr : process.stdout;
|
|
191
|
+
output.write(cliText(
|
|
192
|
+
"\nAgent setup\nChecking Codex and TraeX...\n",
|
|
193
|
+
"\nAgent 设置\n正在检查 Codex 和 TraeX...\n",
|
|
194
|
+
));
|
|
195
|
+
const inspections = await inspectSupportedAgents();
|
|
196
|
+
for (const inspection of inspections) printAgentInspection(inspection, output);
|
|
197
|
+
const actionable = inspections.filter((inspection) => inspection.action);
|
|
198
|
+
if (actionable.length === 0) return inspections;
|
|
199
|
+
|
|
200
|
+
output.write(cliText("\nAvailable actions:\n", "\n可执行的操作:\n"));
|
|
201
|
+
for (const [index, inspection] of actionable.entries()) {
|
|
202
|
+
output.write(cliText(
|
|
203
|
+
` ${index + 1}. ${inspection.action?.kind === "install" ? "Install" : "Upgrade"} ${inspection.name}: ${inspection.action?.command}\n`,
|
|
204
|
+
` ${index + 1}. ${inspection.action?.kind === "install" ? "安装" : "升级"} ${inspection.name}:${inspection.action?.command}\n`,
|
|
205
|
+
));
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
let selected = new Set<number>();
|
|
209
|
+
if (!process.stdin.isTTY) {
|
|
210
|
+
output.write(cliText(
|
|
211
|
+
"No interactive terminal is available. Run the commands above manually if needed.\n",
|
|
212
|
+
"当前没有交互式终端,如有需要请手动执行上面的命令。\n",
|
|
213
|
+
));
|
|
214
|
+
} else {
|
|
215
|
+
selected = new Set(await promptForAgentMaintenance(actionable.length));
|
|
216
|
+
if (selected.size === 0) {
|
|
217
|
+
output.write(cliText("No Agent changes selected.\n", "已跳过 Agent 安装和升级。\n"));
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const initialized: InitializedAgent[] = [];
|
|
222
|
+
for (const inspection of inspections) {
|
|
223
|
+
if (!inspection.action) {
|
|
224
|
+
initialized.push(inspection);
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const actionIndex = actionable.indexOf(inspection);
|
|
228
|
+
if (!selected.has(actionIndex)) {
|
|
229
|
+
initialized.push({
|
|
230
|
+
...inspection,
|
|
231
|
+
assistance: { status: process.stdin.isTTY ? "skipped" : "unavailable" },
|
|
232
|
+
});
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
output.write(cliText(
|
|
237
|
+
`\nRunning ${inspection.name} ${inspection.action.kind}: ${inspection.action.command}\n`,
|
|
238
|
+
`\n正在${inspection.action.kind === "install" ? "安装" : "升级"} ${inspection.name}:${inspection.action.command}\n`,
|
|
239
|
+
));
|
|
240
|
+
const maintenance = await runSupportedAgentMaintenance(inspection.id, inspection.action.kind);
|
|
241
|
+
if (maintenance.status !== 0 || maintenance.error) {
|
|
242
|
+
const error = maintenance.error ?? cliText(
|
|
243
|
+
`Command exited with code ${maintenance.status ?? "unknown"}.`,
|
|
244
|
+
`命令退出码为 ${maintenance.status ?? "未知"}。`,
|
|
245
|
+
);
|
|
246
|
+
output.write(cliText(
|
|
247
|
+
`${inspection.name} ${inspection.action.kind} did not complete; initialization will continue.\n`,
|
|
248
|
+
`${inspection.name}${inspection.action.kind === "install" ? "安装" : "升级"}未完成,初始化将继续。\n`,
|
|
249
|
+
));
|
|
250
|
+
initialized.push({ ...inspection, assistance: { status: "failed", error } });
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
refreshCurrentProcessPath();
|
|
255
|
+
const refreshed = await inspectSupportedAgent(inspection.id);
|
|
256
|
+
if (refreshed.installedVersion) {
|
|
257
|
+
output.write(cliText(
|
|
258
|
+
`${inspection.name} is ready (${refreshed.installedVersion}).\n`,
|
|
259
|
+
`${inspection.name} 已就绪(${refreshed.installedVersion})。\n`,
|
|
260
|
+
));
|
|
261
|
+
} else {
|
|
262
|
+
output.write(cliText(
|
|
263
|
+
`${inspection.name} command completed, but the CLI is not visible in this process. Open a new terminal and run agentbot init again.\n`,
|
|
264
|
+
`${inspection.name} 命令已完成,但当前进程仍无法找到该 CLI。请打开新终端后重新运行 agentbot init。\n`,
|
|
265
|
+
));
|
|
266
|
+
}
|
|
267
|
+
initialized.push({ ...refreshed, assistance: { status: "completed" } });
|
|
268
|
+
}
|
|
269
|
+
return initialized;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function selectDefaultAgent(
|
|
273
|
+
configPath: string,
|
|
274
|
+
inspections: InitializedAgent[],
|
|
275
|
+
json: boolean,
|
|
276
|
+
): Promise<InitCommandResult["defaultAgent"]> {
|
|
277
|
+
const output = json ? process.stderr : process.stdout;
|
|
278
|
+
const configured = readConfiguredAgentSelection(configPath);
|
|
279
|
+
const choices = selectableDefaultAgents(configured.agents, inspections);
|
|
280
|
+
if (!process.stdin.isTTY) {
|
|
281
|
+
if (!configured.defaultAgent || !configured.agents.some((agent) => agent.name === configured.defaultAgent)) {
|
|
282
|
+
throw new Error(cliText(
|
|
283
|
+
"A default Agent is not configured. Run agentbot init in an interactive terminal and select one.",
|
|
284
|
+
"尚未配置默认 Agent。请在交互式终端中运行 agentbot init 并选择一个 Agent。",
|
|
285
|
+
));
|
|
286
|
+
}
|
|
287
|
+
output.write(cliText(
|
|
288
|
+
`Default Agent: ${configured.defaultAgent} (kept because no interactive terminal is available)\n`,
|
|
289
|
+
`默认 Agent:${configured.defaultAgent}(当前没有交互式终端,已保留现有设置)\n`,
|
|
290
|
+
));
|
|
291
|
+
return { name: configured.defaultAgent, status: "existing" };
|
|
292
|
+
}
|
|
293
|
+
if (choices.length === 0) {
|
|
294
|
+
throw new Error(cliText(
|
|
295
|
+
"No available configured Agent can be selected. Install Codex or TraeX, then run agentbot init again.",
|
|
296
|
+
"没有可选择的已配置 Agent。请安装 Codex 或 TraeX,然后重新运行 agentbot init。",
|
|
297
|
+
));
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
output.write(cliText("\nSelect the default Agent:\n", "\n请选择默认 Agent:\n"));
|
|
301
|
+
for (const [index, choice] of choices.entries()) {
|
|
302
|
+
const version = choice.installedVersion ? ` (${choice.installedVersion})` : "";
|
|
303
|
+
const current = choice.name === configured.defaultAgent
|
|
304
|
+
? cliText(" [current]", " [当前]")
|
|
305
|
+
: "";
|
|
306
|
+
output.write(` ${index + 1}. ${choice.name} - ${choice.title}${version}${current}\n`);
|
|
307
|
+
}
|
|
308
|
+
const selected = choices[await promptForDefaultAgent(choices, configured.defaultAgent)];
|
|
309
|
+
if (!selected) throw new Error(cliText("Default Agent selection failed.", "默认 Agent 选择失败。"));
|
|
310
|
+
const updated = writeDefaultAgent(configPath, selected.name);
|
|
311
|
+
output.write(updated
|
|
312
|
+
? cliText(`Saved default Agent: ${selected.name}\n`, `已保存默认 Agent:${selected.name}\n`)
|
|
313
|
+
: cliText(`Default Agent unchanged: ${selected.name}\n`, `默认 Agent 保持不变:${selected.name}\n`));
|
|
314
|
+
return { name: selected.name, status: updated ? "selected" : "existing" };
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function promptForAgentMaintenance(choiceCount: number): Promise<number[]> {
|
|
318
|
+
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
319
|
+
return new Promise((resolve, reject) => {
|
|
320
|
+
let settled = false;
|
|
321
|
+
const finish = (selection: number[]): void => {
|
|
322
|
+
settled = true;
|
|
323
|
+
readline.close();
|
|
324
|
+
resolve(selection);
|
|
325
|
+
};
|
|
326
|
+
const ask = (): void => {
|
|
327
|
+
readline.question(cliText(
|
|
328
|
+
"Enter action numbers separated by commas, type all, or press Enter to skip: ",
|
|
329
|
+
"请输入要执行的操作编号(多个用逗号分隔),输入 all 选择全部,直接回车跳过:",
|
|
330
|
+
), (answer) => {
|
|
331
|
+
const selection = parseMaintenanceSelection(answer, choiceCount);
|
|
332
|
+
if (selection) {
|
|
333
|
+
finish(selection);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
process.stderr.write(cliText(
|
|
337
|
+
`Enter numbers from 1 to ${choiceCount}, all, or press Enter to skip.\n`,
|
|
338
|
+
`请输入 1 到 ${choiceCount} 之间的编号、all,或直接回车跳过。\n`,
|
|
339
|
+
));
|
|
340
|
+
ask();
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
listenForPromptCancellation(readline, () => {
|
|
344
|
+
settled = true;
|
|
345
|
+
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
346
|
+
}, () => settled);
|
|
347
|
+
ask();
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function promptForDefaultAgent(
|
|
352
|
+
choices: SelectableDefaultAgent[],
|
|
353
|
+
currentAgent?: string,
|
|
354
|
+
): Promise<number> {
|
|
355
|
+
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
356
|
+
return new Promise((resolve, reject) => {
|
|
357
|
+
let settled = false;
|
|
358
|
+
const finish = (index: number): void => {
|
|
359
|
+
settled = true;
|
|
360
|
+
readline.close();
|
|
361
|
+
resolve(index);
|
|
362
|
+
};
|
|
363
|
+
const ask = (): void => {
|
|
364
|
+
const currentAvailable = choices.some((choice) => choice.name === currentAgent);
|
|
365
|
+
readline.question(cliText(
|
|
366
|
+
`Enter a number or Agent name${currentAvailable ? ` [${currentAgent}]` : ""}: `,
|
|
367
|
+
`请输入编号或 Agent 标准名${currentAvailable ? ` [${currentAgent}]` : ""}:`,
|
|
368
|
+
), (answer) => {
|
|
369
|
+
const index = resolveDefaultAgentChoice(answer, choices, currentAgent);
|
|
370
|
+
if (index !== undefined) {
|
|
371
|
+
finish(index);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
process.stderr.write(cliText(
|
|
375
|
+
`Enter a number from 1 to ${choices.length} or an Agent standard name.\n`,
|
|
376
|
+
`请输入 1 到 ${choices.length} 之间的编号,或 Agent 标准名。\n`,
|
|
377
|
+
));
|
|
378
|
+
ask();
|
|
379
|
+
});
|
|
380
|
+
};
|
|
381
|
+
listenForPromptCancellation(readline, () => {
|
|
382
|
+
settled = true;
|
|
383
|
+
reject(new Error(cliText("Initialization was cancelled.", "初始化已取消。")));
|
|
384
|
+
}, () => settled);
|
|
385
|
+
ask();
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function listenForPromptCancellation(
|
|
390
|
+
readline: ReturnType<typeof createInterface>,
|
|
391
|
+
cancel: () => void,
|
|
392
|
+
isSettled: () => boolean,
|
|
393
|
+
): void {
|
|
394
|
+
readline.once("SIGINT", () => {
|
|
395
|
+
if (!isSettled()) cancel();
|
|
396
|
+
readline.close();
|
|
397
|
+
});
|
|
398
|
+
readline.once("close", () => {
|
|
399
|
+
if (!isSettled()) cancel();
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function printAgentInspection(
|
|
404
|
+
inspection: SupportedAgentInspection,
|
|
405
|
+
output: NodeJS.WriteStream,
|
|
406
|
+
): void {
|
|
407
|
+
if (inspection.state === "missing") {
|
|
408
|
+
output.write(cliText(
|
|
409
|
+
` ${inspection.name}: not installed\n`,
|
|
410
|
+
` ${inspection.name}:未安装\n`,
|
|
411
|
+
));
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
if (inspection.state === "outdated") {
|
|
415
|
+
output.write(cliText(
|
|
416
|
+
` ${inspection.name}: ${inspection.installedVersion}; update available (${inspection.latestVersion})\n`,
|
|
417
|
+
` ${inspection.name}:${inspection.installedVersion};可升级到 ${inspection.latestVersion}\n`,
|
|
418
|
+
));
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
if (inspection.latestCheckFailed) {
|
|
422
|
+
output.write(cliText(
|
|
423
|
+
` ${inspection.name}: ${inspection.installedVersion}; latest-version check unavailable\n`,
|
|
424
|
+
` ${inspection.name}:${inspection.installedVersion};无法检查最新版本\n`,
|
|
425
|
+
));
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
output.write(cliText(
|
|
429
|
+
` ${inspection.name}: ${inspection.installedVersion} (up to date)\n`,
|
|
430
|
+
` ${inspection.name}:${inspection.installedVersion}(已是最新)\n`,
|
|
431
|
+
));
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function refreshCurrentProcessPath(): void {
|
|
435
|
+
const refreshed = refreshedSystemEnvironment();
|
|
436
|
+
if (!refreshed.refreshed) return;
|
|
437
|
+
const pathEntry = Object.entries(refreshed.environment)
|
|
438
|
+
.find(([name]) => name.toLowerCase() === "path");
|
|
439
|
+
if (pathEntry?.[1]) process.env.PATH = pathEntry[1];
|
|
440
|
+
}
|
|
441
|
+
|
|
154
442
|
async function initializeFeishu(
|
|
155
443
|
paths: InitializationResult,
|
|
156
444
|
options: InitCommandOptions,
|
|
@@ -162,6 +450,9 @@ async function initializeFeishu(
|
|
|
162
450
|
...(existing.appId ? { appId: existing.appId } : {}),
|
|
163
451
|
};
|
|
164
452
|
}
|
|
453
|
+
if (!options.json) {
|
|
454
|
+
process.stdout.write(cliText("\nLark setup\n", "\n飞书设置\n"));
|
|
455
|
+
}
|
|
165
456
|
|
|
166
457
|
const controller = new AbortController();
|
|
167
458
|
const onInterrupt = (): void => controller.abort(new Error(cliText(
|
|
@@ -197,8 +488,8 @@ async function initializeFeishu(
|
|
|
197
488
|
|
|
198
489
|
if (!options.json) {
|
|
199
490
|
process.stdout.write(cliText(
|
|
200
|
-
"
|
|
201
|
-
"
|
|
491
|
+
"Checking app permissions, events, and callbacks...\n",
|
|
492
|
+
"正在检查应用权限、事件和回调...\n",
|
|
202
493
|
));
|
|
203
494
|
}
|
|
204
495
|
const manualPermissionSkip = new AbortController();
|
|
@@ -246,8 +537,8 @@ async function assertResetProfileServerStopped(configPath?: string): Promise<voi
|
|
|
246
537
|
if (await isServerReachable(endpoint)) {
|
|
247
538
|
throw new Error(
|
|
248
539
|
cliText(
|
|
249
|
-
"Cannot reset a running profile. Stop it with
|
|
250
|
-
"无法重置正在运行的 Profile。请先执行
|
|
540
|
+
"Cannot reset a running profile. Stop it with agentbot --profile <directory> server stop, then try again.",
|
|
541
|
+
"无法重置正在运行的 Profile。请先执行 agentbot --profile <目录> server stop,然后重试。",
|
|
251
542
|
),
|
|
252
543
|
);
|
|
253
544
|
}
|
|
@@ -410,6 +701,51 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
410
701
|
process.stdout.write(cliText("Prompt submitted.\n", "提示词已提交。\n"));
|
|
411
702
|
return;
|
|
412
703
|
}
|
|
704
|
+
if (action === "newgroup") {
|
|
705
|
+
const options = parseTaskNewGroupOptions(rest);
|
|
706
|
+
const session = resolveTask(allSessions, options.reference);
|
|
707
|
+
const targetAgentName = options.agentName ?? session.agentName;
|
|
708
|
+
const targetAgent = config.agents[targetAgentName];
|
|
709
|
+
if (!targetAgent) {
|
|
710
|
+
throw new Error(cliText(
|
|
711
|
+
`Unknown Agent standard name: ${targetAgentName}.`,
|
|
712
|
+
`未知的 Agent 标准名:${targetAgentName}。`,
|
|
713
|
+
));
|
|
714
|
+
}
|
|
715
|
+
if (options.projectless && targetAgent.kind !== "app-server") {
|
|
716
|
+
throw new Error(cliText(
|
|
717
|
+
"task newgroup --nodir is only available for App Server agents.",
|
|
718
|
+
"task newgroup --nodir 仅适用于 App Server Agent。",
|
|
719
|
+
));
|
|
720
|
+
}
|
|
721
|
+
requireCliGroupUser(config.feishu.userOpenId);
|
|
722
|
+
const response = await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
723
|
+
action: "task_new_group",
|
|
724
|
+
localSessionId: session.localSessionId,
|
|
725
|
+
...(options.title ? { title: options.title } : {}),
|
|
726
|
+
...(options.cwd ? { cwd: options.cwd } : {}),
|
|
727
|
+
...(options.agentName ? { agentName: options.agentName } : {}),
|
|
728
|
+
...(options.projectless ? { projectless: true } : {}),
|
|
729
|
+
}, 120_000);
|
|
730
|
+
const result = taskGroupControlData(response);
|
|
731
|
+
if (options.json) printJson(result);
|
|
732
|
+
else printTaskGroupResult(result, "newgroup");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
if (action === "forkgroup") {
|
|
736
|
+
const options = parseTaskForkGroupOptions(rest);
|
|
737
|
+
const session = resolveTask(allSessions, options.reference);
|
|
738
|
+
requireCliGroupUser(config.feishu.userOpenId);
|
|
739
|
+
const response = await sendControlRequest(controlEndpoint(config.storage.sqlitePath), {
|
|
740
|
+
action: "task_fork_group",
|
|
741
|
+
localSessionId: session.localSessionId,
|
|
742
|
+
...(options.title ? { title: options.title } : {}),
|
|
743
|
+
}, 120_000);
|
|
744
|
+
const result = taskGroupControlData(response);
|
|
745
|
+
if (options.json) printJson(result);
|
|
746
|
+
else printTaskGroupResult(result, "forkgroup");
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
413
749
|
const sessions = filterSessions(allSessions, rest);
|
|
414
750
|
if (action === "list") {
|
|
415
751
|
if (rest.includes("--json")) printJson(sessions);
|
|
@@ -574,8 +910,8 @@ function printSkillStatus(status: SkillRegistrationStatus): void {
|
|
|
574
910
|
|
|
575
911
|
function printInitializationPaths(result: InitializationResult): void {
|
|
576
912
|
process.stdout.write(cliText(
|
|
577
|
-
"
|
|
578
|
-
"
|
|
913
|
+
"Profile setup\n",
|
|
914
|
+
"Profile 设置\n",
|
|
579
915
|
));
|
|
580
916
|
if (result.reset) {
|
|
581
917
|
process.stdout.write(`${cliText("Reset backup: ", "重置备份:")}${result.reset.backupPath}\n`);
|
|
@@ -592,6 +928,7 @@ function printInitializationResult(result: Omit<InitCommandResult, "server">): v
|
|
|
592
928
|
"\nAgent Bot initialization completed.\n",
|
|
593
929
|
"\nAgent Bot 初始化完成。\n",
|
|
594
930
|
));
|
|
931
|
+
process.stdout.write(`${cliText("Default Agent: ", "默认 Agent:")}${result.defaultAgent.name}\n`);
|
|
595
932
|
if (result.feishu.status === "created") {
|
|
596
933
|
process.stdout.write(cliText(
|
|
597
934
|
`Lark app: created and credentials saved (${result.feishu.appId})\n`,
|
|
@@ -802,6 +1139,7 @@ function feishuFeatureWarnings(missing: FeishuConfigurationChallenge["missing"])
|
|
|
802
1139
|
|
|
803
1140
|
function initializationStatusLabel(status: InitializationStatus): string {
|
|
804
1141
|
if (status === "created") return cliText("created", "已创建");
|
|
1142
|
+
if (status === "updated") return cliText("updated with missing settings", "已补全缺失配置");
|
|
805
1143
|
if (status === "reset") return cliText("reset from template", "已从模板重置");
|
|
806
1144
|
return cliText("already exists; unchanged", "已存在,未修改");
|
|
807
1145
|
}
|
|
@@ -840,6 +1178,50 @@ function ensureOk(response: ControlResponse): void {
|
|
|
840
1178
|
));
|
|
841
1179
|
}
|
|
842
1180
|
|
|
1181
|
+
function requireCliGroupUser(userOpenId: string | undefined): asserts userOpenId is string {
|
|
1182
|
+
if (userOpenId?.startsWith("ou_")) return;
|
|
1183
|
+
throw new Error(cliText(
|
|
1184
|
+
"The profile has no authorizing Lark user to invite. Run agentbot init again or configure FEISHU_USER_OPEN_ID.",
|
|
1185
|
+
"当前 Profile 没有可邀请的飞书授权用户。请重新运行 agentbot init,或配置 FEISHU_USER_OPEN_ID。",
|
|
1186
|
+
));
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
function taskGroupControlData(response: ControlResponse): TaskGroupControlData {
|
|
1190
|
+
ensureOk(response);
|
|
1191
|
+
const data = response.data as Partial<TaskGroupControlData> | undefined;
|
|
1192
|
+
if (
|
|
1193
|
+
!data
|
|
1194
|
+
|| typeof data.sourceLocalSessionId !== "string"
|
|
1195
|
+
|| !data.group
|
|
1196
|
+
|| typeof data.group.chatId !== "string"
|
|
1197
|
+
|| typeof data.group.contextKey !== "string"
|
|
1198
|
+
|| typeof data.group.name !== "string"
|
|
1199
|
+
|| !data.task
|
|
1200
|
+
|| typeof data.task.localSessionId !== "string"
|
|
1201
|
+
) {
|
|
1202
|
+
throw new Error(cliText(
|
|
1203
|
+
"Agent Bot returned an invalid task-group result.",
|
|
1204
|
+
"Agent Bot 返回了无效的任务群结果。",
|
|
1205
|
+
));
|
|
1206
|
+
}
|
|
1207
|
+
return data as TaskGroupControlData;
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
function printTaskGroupResult(
|
|
1211
|
+
result: TaskGroupControlData,
|
|
1212
|
+
action: "newgroup" | "forkgroup",
|
|
1213
|
+
): void {
|
|
1214
|
+
process.stdout.write(cliText(
|
|
1215
|
+
`${action === "forkgroup" ? "Fork group" : "Lark group"} created: ${result.group.name}\n`,
|
|
1216
|
+
`${action === "forkgroup" ? "Fork 群" : "飞书群"}已创建:${result.group.name}\n`,
|
|
1217
|
+
));
|
|
1218
|
+
process.stdout.write(`${cliText("Chat ID: ", "群 ID:")}${result.group.chatId}\n`);
|
|
1219
|
+
process.stdout.write(`${cliText("Task ID: ", "任务 ID:")}${result.task.localSessionId}\n`);
|
|
1220
|
+
if (result.task.remoteSessionId) {
|
|
1221
|
+
process.stdout.write(`${cliText("Agent task ID: ", "Agent 任务 ID:")}${result.task.remoteSessionId}\n`);
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
|
|
843
1225
|
function optionValue(args: string[], name: string): string | undefined {
|
|
844
1226
|
const index = args.indexOf(name);
|
|
845
1227
|
return index >= 0 ? args[index + 1] : undefined;
|
|
@@ -108,7 +108,7 @@ export class AppServerConnection {
|
|
|
108
108
|
try {
|
|
109
109
|
message = JSON.parse(line) as AppServerMessage;
|
|
110
110
|
} catch (error) {
|
|
111
|
-
this.logger.warn({ line, error }, "Ignoring non-JSON
|
|
111
|
+
this.logger.warn({ line, error }, "Ignoring non-JSON App Server stdout line.");
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
114
|
this.handleMessage(message);
|
|
@@ -128,7 +128,7 @@ export class AppServerConnection {
|
|
|
128
128
|
for (const listener of this.notificationListeners) listener(message.method, message.params);
|
|
129
129
|
return;
|
|
130
130
|
}
|
|
131
|
-
this.logger.warn({ message }, "Ignoring unknown
|
|
131
|
+
this.logger.warn({ message }, "Ignoring unknown App Server message shape.");
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
private handleResponse(message: AppServerResponse): void {
|
|
@@ -139,7 +139,7 @@ export class AppServerConnection {
|
|
|
139
139
|
responseId: message.id,
|
|
140
140
|
responseKind: "error" in message ? "error" : "result",
|
|
141
141
|
},
|
|
142
|
-
"Received
|
|
142
|
+
"Received App Server response for unknown request.",
|
|
143
143
|
);
|
|
144
144
|
return;
|
|
145
145
|
}
|
|
@@ -27,15 +27,15 @@ export class CodexProcessManager implements AppServerClientProvider {
|
|
|
27
27
|
const client = new AppServerConnection(child, this.logger.child({ component: "codex-app-server" }));
|
|
28
28
|
this.client = client;
|
|
29
29
|
readline.createInterface({ input: child.stderr, crlfDelay: Infinity }).on("line", (line) => {
|
|
30
|
-
this.logger.debug({ line }, "
|
|
30
|
+
this.logger.debug({ line }, "App Server stderr.");
|
|
31
31
|
});
|
|
32
|
-
child.once("error", (error) => this.logger.error({ error }, "
|
|
32
|
+
child.once("error", (error) => this.logger.error({ error }, "App Server process error."));
|
|
33
33
|
child.once("exit", (code, signal) => {
|
|
34
|
-
this.logger.warn({ code, signal }, "
|
|
34
|
+
this.logger.warn({ code, signal }, "App Server process exited.");
|
|
35
35
|
client.close();
|
|
36
36
|
if (this.client === client) this.client = undefined;
|
|
37
37
|
if (this.child === child) this.child = undefined;
|
|
38
|
-
const error = new Error(`
|
|
38
|
+
const error = new Error(`App Server exited (code=${code ?? "null"}, signal=${signal ?? "null"}).`);
|
|
39
39
|
for (const listener of this.disconnectListeners) listener(error);
|
|
40
40
|
});
|
|
41
41
|
await client.request("initialize", {
|