@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/Initializer.ts
CHANGED
|
@@ -5,6 +5,7 @@ import path from "node:path";
|
|
|
5
5
|
import { cliText } from "./i18n.js";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { parse as parseDotEnv } from "dotenv";
|
|
8
|
+
import { isMap, isScalar, parseDocument, type YAMLMap } from "yaml";
|
|
8
9
|
import {
|
|
9
10
|
agentBotHome,
|
|
10
11
|
defaultConfigPath,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
resolveUserPath,
|
|
13
14
|
} from "../config/paths.js";
|
|
14
15
|
|
|
15
|
-
export type InitializationStatus = "created" | "existing" | "reset";
|
|
16
|
+
export type InitializationStatus = "created" | "existing" | "updated" | "reset";
|
|
16
17
|
|
|
17
18
|
export interface InitializedPath {
|
|
18
19
|
path: string;
|
|
@@ -38,6 +39,16 @@ export interface InitializationOptions {
|
|
|
38
39
|
reset?: boolean;
|
|
39
40
|
}
|
|
40
41
|
|
|
42
|
+
export interface ConfiguredAgentOption {
|
|
43
|
+
name: string;
|
|
44
|
+
title: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface ConfiguredAgentSelection {
|
|
48
|
+
agents: ConfiguredAgentOption[];
|
|
49
|
+
defaultAgent?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
41
52
|
export type FeishuCredentialStatus = "configured" | "missing" | "incomplete";
|
|
42
53
|
|
|
43
54
|
export interface FeishuCredentialState {
|
|
@@ -82,8 +93,8 @@ export function initializeAgentBot(options: InitializationOptions = {}): Initial
|
|
|
82
93
|
? resetProfileContents(homePath, configPath, envPath, configDirectory)
|
|
83
94
|
: undefined;
|
|
84
95
|
|
|
85
|
-
const config =
|
|
86
|
-
const envFile =
|
|
96
|
+
const config = initializeConfig(configTemplatePath, configPath);
|
|
97
|
+
const envFile = initializeEnv(envTemplatePath, envPath);
|
|
87
98
|
restrictFilePermissions(envFile.path);
|
|
88
99
|
const data = ensureDirectory(path.join(configDirectory, "data"));
|
|
89
100
|
const logs = ensureDirectory(path.join(configDirectory, "logs"));
|
|
@@ -98,6 +109,68 @@ export function initializeAgentBot(options: InitializationOptions = {}): Initial
|
|
|
98
109
|
};
|
|
99
110
|
}
|
|
100
111
|
|
|
112
|
+
export function readConfiguredAgentSelection(configPath: string): ConfiguredAgentSelection {
|
|
113
|
+
const document = parseConfigurationDocument(fs.readFileSync(configPath, "utf8"), configPath);
|
|
114
|
+
if (!isMap(document.contents)) {
|
|
115
|
+
throw new Error(cliText(
|
|
116
|
+
`Configuration file must contain a YAML mapping: ${configPath}`,
|
|
117
|
+
`配置文件必须包含 YAML 映射:${configPath}`,
|
|
118
|
+
));
|
|
119
|
+
}
|
|
120
|
+
const configuredAgents = document.contents.get("agents", true);
|
|
121
|
+
if (!isMap(configuredAgents)) {
|
|
122
|
+
throw new Error(cliText(
|
|
123
|
+
`Configuration file must contain an agents mapping: ${configPath}`,
|
|
124
|
+
`配置文件必须包含 agents 映射:${configPath}`,
|
|
125
|
+
));
|
|
126
|
+
}
|
|
127
|
+
const agents = configuredAgents.items.flatMap((pair) => {
|
|
128
|
+
const name = yamlMappingKey(pair.key);
|
|
129
|
+
if (!name) return [];
|
|
130
|
+
const titleNode = isMap(pair.value) ? pair.value.get("title", true) : undefined;
|
|
131
|
+
const title = isScalar(titleNode) && typeof titleNode.value === "string"
|
|
132
|
+
? titleNode.value
|
|
133
|
+
: name;
|
|
134
|
+
return [{ name, title }];
|
|
135
|
+
});
|
|
136
|
+
const defaultAgent = document.getIn(["defaults", "agent"]);
|
|
137
|
+
return {
|
|
138
|
+
agents,
|
|
139
|
+
...(typeof defaultAgent === "string" && defaultAgent.trim() ? { defaultAgent } : {}),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function writeDefaultAgent(configPath: string, agentName: string): boolean {
|
|
144
|
+
const original = fs.readFileSync(configPath, "utf8");
|
|
145
|
+
const document = parseConfigurationDocument(original, configPath);
|
|
146
|
+
if (!isMap(document.contents)) {
|
|
147
|
+
throw new Error(cliText(
|
|
148
|
+
`Configuration file must contain a YAML mapping: ${configPath}`,
|
|
149
|
+
`配置文件必须包含 YAML 映射:${configPath}`,
|
|
150
|
+
));
|
|
151
|
+
}
|
|
152
|
+
const configuredAgents = document.contents.get("agents", true);
|
|
153
|
+
if (!isMap(configuredAgents) || !configuredAgents.has(agentName)) {
|
|
154
|
+
throw new Error(cliText(
|
|
155
|
+
`Cannot select an Agent that is not configured: ${agentName}`,
|
|
156
|
+
`不能选择尚未配置的 Agent:${agentName}`,
|
|
157
|
+
));
|
|
158
|
+
}
|
|
159
|
+
const defaults = document.contents.get("defaults", true);
|
|
160
|
+
if (defaults !== undefined && !isMap(defaults)) {
|
|
161
|
+
throw new Error(cliText(
|
|
162
|
+
`Configuration defaults must contain a YAML mapping: ${configPath}`,
|
|
163
|
+
`配置文件的 defaults 必须是 YAML 映射:${configPath}`,
|
|
164
|
+
));
|
|
165
|
+
}
|
|
166
|
+
const currentAgent = document.getIn(["defaults", "agent"]);
|
|
167
|
+
if (currentAgent === agentName) return false;
|
|
168
|
+
if (isMap(defaults)) defaults.set("agent", agentName);
|
|
169
|
+
else document.setIn(["defaults"], { agent: agentName });
|
|
170
|
+
writeFileAtomically(configPath, document.toString());
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
|
|
101
174
|
export function readFeishuCredentials(envPath: string, env: NodeJS.ProcessEnv = process.env): FeishuCredentialState {
|
|
102
175
|
const fileValues = fs.existsSync(envPath) ? parseDotEnv(fs.readFileSync(envPath, "utf8")) : {};
|
|
103
176
|
const appId = firstNonBlank(env.FEISHU_APP_ID, fileValues.FEISHU_APP_ID);
|
|
@@ -188,8 +261,8 @@ export function acquireInitializationLock(lockDirectory: string): Initialization
|
|
|
188
261
|
if (!isFileExistsError(error)) throw error;
|
|
189
262
|
if (!isStaleInitializationLock(lockPath)) {
|
|
190
263
|
throw new Error(cliText(
|
|
191
|
-
`Another
|
|
192
|
-
`另一个
|
|
264
|
+
`Another agentbot init process is running (lock file: ${lockPath}).`,
|
|
265
|
+
`另一个 agentbot init 进程正在运行(锁文件:${lockPath})。`,
|
|
193
266
|
));
|
|
194
267
|
}
|
|
195
268
|
fs.rmSync(lockPath, { force: true });
|
|
@@ -244,6 +317,134 @@ function copyIfMissing(sourcePath: string, targetPath: string): InitializedPath
|
|
|
244
317
|
}
|
|
245
318
|
}
|
|
246
319
|
|
|
320
|
+
function initializeConfig(templatePath: string, configPath: string): InitializedPath {
|
|
321
|
+
if (!fs.existsSync(configPath)) {
|
|
322
|
+
const copied = copyIfMissing(templatePath, configPath);
|
|
323
|
+
if (copied.status === "created") return copied;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const original = fs.readFileSync(configPath, "utf8");
|
|
327
|
+
const template = fs.readFileSync(templatePath, "utf8");
|
|
328
|
+
const configDocument = parseConfigurationDocument(original, configPath);
|
|
329
|
+
const templateDocument = parseConfigurationDocument(template, templatePath);
|
|
330
|
+
if (!isMap(configDocument.contents) || !isMap(templateDocument.contents)) {
|
|
331
|
+
throw new Error(cliText(
|
|
332
|
+
`Configuration files must contain a YAML mapping: ${configPath}`,
|
|
333
|
+
`配置文件必须包含 YAML 映射:${configPath}`,
|
|
334
|
+
));
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const updated = mergeMissingConfiguration(
|
|
338
|
+
configDocument.contents,
|
|
339
|
+
templateDocument.contents,
|
|
340
|
+
[],
|
|
341
|
+
);
|
|
342
|
+
if (!updated) return { path: configPath, status: "existing" };
|
|
343
|
+
writeFileAtomically(configPath, configDocument.toString());
|
|
344
|
+
return { path: configPath, status: "updated" };
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function initializeEnv(templatePath: string, envPath: string): InitializedPath {
|
|
348
|
+
if (!fs.existsSync(envPath)) {
|
|
349
|
+
const copied = copyIfMissing(templatePath, envPath);
|
|
350
|
+
if (copied.status === "created") return copied;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const original = fs.readFileSync(envPath, "utf8");
|
|
354
|
+
const template = fs.readFileSync(templatePath, "utf8");
|
|
355
|
+
const existingKeys = dotEnvKeys(original);
|
|
356
|
+
const missingLines = template
|
|
357
|
+
.split(/\r?\n/)
|
|
358
|
+
.flatMap((line) => {
|
|
359
|
+
const key = dotEnvAssignmentKey(line);
|
|
360
|
+
if (!key || existingKeys.has(key)) return [];
|
|
361
|
+
existingKeys.add(key);
|
|
362
|
+
return [line];
|
|
363
|
+
});
|
|
364
|
+
if (missingLines.length === 0) return { path: envPath, status: "existing" };
|
|
365
|
+
|
|
366
|
+
const newline = original.includes("\r\n") ? "\r\n" : "\n";
|
|
367
|
+
const separator = original.length === 0 || /\r?\n$/u.test(original) ? "" : newline;
|
|
368
|
+
writeFileAtomically(envPath, `${original}${separator}${missingLines.join(newline)}${newline}`);
|
|
369
|
+
return { path: envPath, status: "updated" };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function parseConfigurationDocument(contents: string, filePath: string) {
|
|
373
|
+
const document = parseDocument(contents);
|
|
374
|
+
if (document.errors.length === 0) return document;
|
|
375
|
+
throw new Error(cliText(
|
|
376
|
+
`Could not upgrade configuration file ${filePath}: ${document.errors[0]?.message ?? "invalid YAML"}`,
|
|
377
|
+
`无法升级配置文件 ${filePath}:${document.errors[0]?.message ?? "YAML 无效"}`,
|
|
378
|
+
));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function mergeMissingConfiguration(
|
|
382
|
+
config: YAMLMap,
|
|
383
|
+
template: YAMLMap,
|
|
384
|
+
parentPath: string[],
|
|
385
|
+
): boolean {
|
|
386
|
+
let updated = false;
|
|
387
|
+
for (const templatePair of template.items) {
|
|
388
|
+
const key = yamlMappingKey(templatePair.key);
|
|
389
|
+
if (!key) continue;
|
|
390
|
+
if (!config.has(key)) {
|
|
391
|
+
if (shouldPreserveMissingConfiguration(parentPath, key)) continue;
|
|
392
|
+
const clonedPair = templatePair.clone();
|
|
393
|
+
if (parentPath.length === 0 && key === "defaults" && isMap(clonedPair.value)) {
|
|
394
|
+
clonedPair.value.delete("agent");
|
|
395
|
+
if (clonedPair.value.items.length === 0) continue;
|
|
396
|
+
}
|
|
397
|
+
config.add(clonedPair);
|
|
398
|
+
updated = true;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const configValue = config.get(key, true);
|
|
403
|
+
if (isMap(configValue) && isMap(templatePair.value)) {
|
|
404
|
+
updated = mergeMissingConfiguration(configValue, templatePair.value, [...parentPath, key]) || updated;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
return updated;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function shouldPreserveMissingConfiguration(parentPath: string[], key: string): boolean {
|
|
411
|
+
if (parentPath.length === 1 && parentPath[0] === "agents") return true;
|
|
412
|
+
return parentPath.length === 1 && parentPath[0] === "defaults" && key === "agent";
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function yamlMappingKey(value: unknown): string | undefined {
|
|
416
|
+
if (typeof value === "string") return value;
|
|
417
|
+
return isScalar(value) && typeof value.value === "string" ? value.value : undefined;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function dotEnvKeys(contents: string): Set<string> {
|
|
421
|
+
return new Set(contents.split(/\r?\n/).flatMap((line) => {
|
|
422
|
+
const key = dotEnvAssignmentKey(line);
|
|
423
|
+
return key ? [key] : [];
|
|
424
|
+
}));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function dotEnvAssignmentKey(line: string): string | undefined {
|
|
428
|
+
return /^\s*(?:export\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*=/u.exec(line)?.[1];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function writeFileAtomically(filePath: string, contents: string): void {
|
|
432
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
433
|
+
let temporaryFile: number | undefined;
|
|
434
|
+
try {
|
|
435
|
+
temporaryFile = fs.openSync(temporaryPath, "wx", fs.statSync(filePath).mode);
|
|
436
|
+
fs.writeFileSync(temporaryFile, contents, "utf8");
|
|
437
|
+
fs.fsyncSync(temporaryFile);
|
|
438
|
+
fs.closeSync(temporaryFile);
|
|
439
|
+
temporaryFile = undefined;
|
|
440
|
+
fs.renameSync(temporaryPath, filePath);
|
|
441
|
+
syncDirectory(path.dirname(filePath));
|
|
442
|
+
} finally {
|
|
443
|
+
if (temporaryFile !== undefined) fs.closeSync(temporaryFile);
|
|
444
|
+
fs.rmSync(temporaryPath, { force: true });
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
247
448
|
function resetProfileContents(
|
|
248
449
|
homePath: string,
|
|
249
450
|
configPath: string,
|
package/src/cli/ServerStarter.ts
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
prepareCrashReportDirectory,
|
|
9
9
|
resolveSupervisorDiagnosticsPaths,
|
|
10
10
|
} from "../supervision/SupervisorDiagnostics.js";
|
|
11
|
+
import { refreshedSystemEnvironment } from "../supervision/systemEnvironment.js";
|
|
11
12
|
import { controlEndpoint } from "./controlProtocol.js";
|
|
12
13
|
import { cliText } from "./i18n.js";
|
|
13
14
|
import { isServerReachable, isServerRunning } from "./LocalControlClient.js";
|
|
@@ -53,8 +54,8 @@ export async function startServer(
|
|
|
53
54
|
} catch (error) {
|
|
54
55
|
if (error instanceof Error && /Lark bot is not configured/.test(error.message)) {
|
|
55
56
|
throw new Error(cliText(
|
|
56
|
-
"The Lark bot is not configured. Run
|
|
57
|
-
"尚未配置飞书机器人。请先运行
|
|
57
|
+
"The Lark bot is not configured. Run agentbot init first, or use agentbot console for local-only testing.",
|
|
58
|
+
"尚未配置飞书机器人。请先运行 agentbot init,或使用 agentbot console 进行纯本地测试。",
|
|
58
59
|
));
|
|
59
60
|
}
|
|
60
61
|
throw error;
|
|
@@ -94,6 +95,7 @@ const defaultDependencies: ServerStarterDependencies = {
|
|
|
94
95
|
const entry = fileURLToPath(new URL("../supervisor.js", import.meta.url));
|
|
95
96
|
const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
|
|
96
97
|
prepareCrashReportDirectory(reportDirectory);
|
|
98
|
+
const environmentRefresh = refreshedSystemEnvironment();
|
|
97
99
|
const child = spawn(process.execPath, [
|
|
98
100
|
...nodeDiagnosticReportArguments(reportDirectory),
|
|
99
101
|
entry,
|
|
@@ -102,7 +104,10 @@ const defaultDependencies: ServerStarterDependencies = {
|
|
|
102
104
|
detached: true,
|
|
103
105
|
windowsHide: true,
|
|
104
106
|
stdio: "ignore",
|
|
105
|
-
env: {
|
|
107
|
+
env: {
|
|
108
|
+
...environmentRefresh.environment,
|
|
109
|
+
AGENT_BOT_RESTART_REASON: "通过 Agent Bot CLI 启动",
|
|
110
|
+
},
|
|
106
111
|
});
|
|
107
112
|
child.unref();
|
|
108
113
|
},
|
|
@@ -11,7 +11,16 @@ export type ControlRequest =
|
|
|
11
11
|
| { action: "task_status"; localSessionId: string }
|
|
12
12
|
| { action: "task_stop"; localSessionId: string }
|
|
13
13
|
| { action: "task_title"; localSessionId: string; title: string }
|
|
14
|
-
| { action: "task_prompt"; localSessionId: string; text: string }
|
|
14
|
+
| { action: "task_prompt"; localSessionId: string; text: string }
|
|
15
|
+
| {
|
|
16
|
+
action: "task_new_group";
|
|
17
|
+
localSessionId: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
cwd?: string;
|
|
20
|
+
agentName?: string;
|
|
21
|
+
projectless?: boolean;
|
|
22
|
+
}
|
|
23
|
+
| { action: "task_fork_group"; localSessionId: string; title?: string };
|
|
15
24
|
|
|
16
25
|
export interface ControlResponse {
|
|
17
26
|
ok: boolean;
|
|
@@ -25,6 +34,17 @@ export interface TaskStatusControlData {
|
|
|
25
34
|
remote?: RemoteSessionSummary;
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
export interface TaskGroupControlData {
|
|
38
|
+
sourceLocalSessionId: string;
|
|
39
|
+
sourceTurnId?: string;
|
|
40
|
+
group: {
|
|
41
|
+
chatId: string;
|
|
42
|
+
contextKey: string;
|
|
43
|
+
name: string;
|
|
44
|
+
};
|
|
45
|
+
task: SessionRecord;
|
|
46
|
+
}
|
|
47
|
+
|
|
28
48
|
export function controlEndpoint(sqlitePath: string): string {
|
|
29
49
|
const resolvedPath = path.resolve(sqlitePath);
|
|
30
50
|
const key = createHash("sha256").update(resolvedPath.toLowerCase()).digest("hex").slice(0, 16);
|
package/src/cli/help.ts
CHANGED
|
@@ -3,13 +3,13 @@ import { cliLanguage, type CliLanguage } from "./i18n.js";
|
|
|
3
3
|
export function renderCliHelp(version: string, language: CliLanguage = cliLanguage): string {
|
|
4
4
|
if (language === "zh") {
|
|
5
5
|
return `Agent Bot ${version}
|
|
6
|
-
通过飞书使用本地 Codex
|
|
6
|
+
通过飞书使用本地 Codex、TraeX 和兼容 ACP 的 Agent。
|
|
7
7
|
|
|
8
8
|
用法:
|
|
9
|
-
|
|
9
|
+
agentbot [选项] <命令>
|
|
10
10
|
|
|
11
11
|
常用命令:
|
|
12
|
-
init
|
|
12
|
+
init 检查并选择 Agent,初始化飞书应用和服务
|
|
13
13
|
init --reset 重置并重新配置显式指定的 Profile
|
|
14
14
|
console 打开本地控制台
|
|
15
15
|
server start 在后台启动 Agent Bot
|
|
@@ -20,6 +20,8 @@ export function renderCliHelp(version: string, language: CliLanguage = cliLangua
|
|
|
20
20
|
task status <任务> 显示任务状态
|
|
21
21
|
task chat <任务> 显示任务绑定的飞书会话
|
|
22
22
|
task prompt <任务> <提示词> 向任务发送提示词
|
|
23
|
+
task newgroup <任务> [标题] 创建新群;支持 --agent、--dir、--nodir
|
|
24
|
+
task forkgroup <任务> [标题] 从任务 Fork 新群
|
|
23
25
|
task stop <任务> 停止任务
|
|
24
26
|
task title <任务> <标题> 重命名任务
|
|
25
27
|
skills status 显示内置 Skill 状态
|
|
@@ -38,24 +40,24 @@ export function renderCliHelp(version: string, language: CliLanguage = cliLangua
|
|
|
38
40
|
--reconfigure-feishu 仅替换现有飞书凭据
|
|
39
41
|
|
|
40
42
|
示例:
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
agentbot init
|
|
44
|
+
agentbot --profile ~/.agent-bot init --reset
|
|
45
|
+
agentbot --profile ~/.agent-bot-rescue init
|
|
46
|
+
agentbot server start
|
|
47
|
+
agentbot server status
|
|
48
|
+
agentbot task list
|
|
49
|
+
agentbot server restart --reason "更新 Agent Bot"
|
|
48
50
|
`;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
return `Agent Bot ${version}
|
|
52
|
-
Use
|
|
54
|
+
Use Codex, TraeX, and compatible ACP agents from Lark/Feishu.
|
|
53
55
|
|
|
54
56
|
Usage:
|
|
55
|
-
|
|
57
|
+
agentbot [options] <command>
|
|
56
58
|
|
|
57
59
|
Common commands:
|
|
58
|
-
init
|
|
60
|
+
init Check and select an Agent; initialize the Lark app and service
|
|
59
61
|
init --reset Reset and reconfigure an explicit profile
|
|
60
62
|
console Open the local console
|
|
61
63
|
server start Start Agent Bot in the background
|
|
@@ -66,6 +68,8 @@ Common commands:
|
|
|
66
68
|
task status <task> Show task status
|
|
67
69
|
task chat <task> Show the task's Lark chat binding
|
|
68
70
|
task prompt <task> <prompt> Send a Prompt to a task
|
|
71
|
+
task newgroup <task> [title] Create a group; supports --agent, --dir, --nodir
|
|
72
|
+
task forkgroup <task> [title] Fork a task into a new group
|
|
69
73
|
task stop <task> Stop a task
|
|
70
74
|
task title <task> <title> Rename a task
|
|
71
75
|
skills status Show the built-in Skill status
|
|
@@ -84,12 +88,12 @@ Init options:
|
|
|
84
88
|
--reconfigure-feishu Replace only the existing Lark credentials
|
|
85
89
|
|
|
86
90
|
Examples:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
agentbot init
|
|
92
|
+
agentbot --profile ~/.agent-bot init --reset
|
|
93
|
+
agentbot --profile ~/.agent-bot-rescue init
|
|
94
|
+
agentbot server start
|
|
95
|
+
agentbot server status
|
|
96
|
+
agentbot task list
|
|
97
|
+
agentbot server restart --reason "Update Agent Bot"
|
|
94
98
|
`;
|
|
95
99
|
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { cliLanguage, cliText, type CliLanguage } from "./i18n.js";
|
|
2
|
+
|
|
3
|
+
export interface TaskNewGroupOptions {
|
|
4
|
+
reference: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
cwd?: string;
|
|
7
|
+
agentName?: string;
|
|
8
|
+
projectless: boolean;
|
|
9
|
+
json: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TaskForkGroupOptions {
|
|
13
|
+
reference: string;
|
|
14
|
+
title?: string;
|
|
15
|
+
json: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function parseTaskNewGroupOptions(
|
|
19
|
+
input: string[],
|
|
20
|
+
language: CliLanguage = cliLanguage,
|
|
21
|
+
): TaskNewGroupOptions {
|
|
22
|
+
const positionals: string[] = [];
|
|
23
|
+
let cwd: string | undefined;
|
|
24
|
+
let agentName: string | undefined;
|
|
25
|
+
let projectless = false;
|
|
26
|
+
let json = false;
|
|
27
|
+
for (let index = 0; index < input.length; index += 1) {
|
|
28
|
+
const argument = input[index]!;
|
|
29
|
+
if (argument === "--json") {
|
|
30
|
+
json = true;
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (argument === "--nodir") {
|
|
34
|
+
if (projectless) throw optionError("newgroup", "--nodir", language);
|
|
35
|
+
if (cwd !== undefined) throw conflictingProjectOptions(language);
|
|
36
|
+
projectless = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (argument === "--dir") {
|
|
40
|
+
if (projectless) throw conflictingProjectOptions(language);
|
|
41
|
+
if (cwd !== undefined) throw optionError("newgroup", "--dir", language);
|
|
42
|
+
const directory = input[index + 1];
|
|
43
|
+
if (!directory || directory.startsWith("--")) {
|
|
44
|
+
throw new Error(cliText(
|
|
45
|
+
"task newgroup requires a project directory after --dir.",
|
|
46
|
+
"task newgroup 需要在 --dir 后指定项目目录。",
|
|
47
|
+
language,
|
|
48
|
+
));
|
|
49
|
+
}
|
|
50
|
+
cwd = directory;
|
|
51
|
+
index += 1;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (argument === "--agent") {
|
|
55
|
+
if (agentName !== undefined) throw optionError("newgroup", "--agent", language);
|
|
56
|
+
const name = input[index + 1];
|
|
57
|
+
if (!name?.trim() || name.startsWith("--")) {
|
|
58
|
+
throw new Error(cliText(
|
|
59
|
+
"task newgroup requires an Agent standard name after --agent.",
|
|
60
|
+
"task newgroup 需要在 --agent 后指定 Agent 标准名。",
|
|
61
|
+
language,
|
|
62
|
+
));
|
|
63
|
+
}
|
|
64
|
+
agentName = name.trim();
|
|
65
|
+
index += 1;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (argument.startsWith("--")) throw unsupportedOption("newgroup", argument, language);
|
|
69
|
+
positionals.push(argument);
|
|
70
|
+
}
|
|
71
|
+
const [reference, ...titleParts] = positionals;
|
|
72
|
+
requireTaskReference("newgroup", reference, language);
|
|
73
|
+
return {
|
|
74
|
+
reference,
|
|
75
|
+
title: titleParts.join(" ").trim() || undefined,
|
|
76
|
+
cwd,
|
|
77
|
+
...(agentName ? { agentName } : {}),
|
|
78
|
+
projectless,
|
|
79
|
+
json,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function parseTaskForkGroupOptions(
|
|
84
|
+
input: string[],
|
|
85
|
+
language: CliLanguage = cliLanguage,
|
|
86
|
+
): TaskForkGroupOptions {
|
|
87
|
+
const positionals: string[] = [];
|
|
88
|
+
let json = false;
|
|
89
|
+
for (const argument of input) {
|
|
90
|
+
if (argument === "--json") {
|
|
91
|
+
json = true;
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
if (argument.startsWith("--")) throw unsupportedOption("forkgroup", argument, language);
|
|
95
|
+
positionals.push(argument);
|
|
96
|
+
}
|
|
97
|
+
const [reference, ...titleParts] = positionals;
|
|
98
|
+
requireTaskReference("forkgroup", reference, language);
|
|
99
|
+
return {
|
|
100
|
+
reference,
|
|
101
|
+
title: titleParts.join(" ").trim() || undefined,
|
|
102
|
+
json,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function requireTaskReference(
|
|
107
|
+
action: "newgroup" | "forkgroup",
|
|
108
|
+
reference: string | undefined,
|
|
109
|
+
language: CliLanguage,
|
|
110
|
+
): asserts reference is string {
|
|
111
|
+
if (reference && !reference.startsWith("--")) return;
|
|
112
|
+
throw new Error(cliText(
|
|
113
|
+
`task ${action} requires a source task number or task ID.`,
|
|
114
|
+
`task ${action} 需要源任务序号或任务 ID。`,
|
|
115
|
+
language,
|
|
116
|
+
));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function optionError(
|
|
120
|
+
action: "newgroup",
|
|
121
|
+
option: "--agent" | "--dir" | "--nodir",
|
|
122
|
+
language: CliLanguage,
|
|
123
|
+
): Error {
|
|
124
|
+
return new Error(cliText(
|
|
125
|
+
`task ${action} accepts ${option} only once.`,
|
|
126
|
+
`task ${action} 只能指定一次 ${option}。`,
|
|
127
|
+
language,
|
|
128
|
+
));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function conflictingProjectOptions(language: CliLanguage): Error {
|
|
132
|
+
return new Error(cliText(
|
|
133
|
+
"task newgroup cannot combine --dir and --nodir.",
|
|
134
|
+
"task newgroup 的 --dir 和 --nodir 不能同时使用。",
|
|
135
|
+
language,
|
|
136
|
+
));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function unsupportedOption(
|
|
140
|
+
action: "newgroup" | "forkgroup",
|
|
141
|
+
option: string,
|
|
142
|
+
language: CliLanguage,
|
|
143
|
+
): Error {
|
|
144
|
+
return new Error(cliText(
|
|
145
|
+
`task ${action} does not support option: ${option}.`,
|
|
146
|
+
`task ${action} 不支持参数:${option}。`,
|
|
147
|
+
language,
|
|
148
|
+
));
|
|
149
|
+
}
|