@keyou007/agent-bot 0.1.2 → 0.1.4
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 +3 -1
- package/CHANGELOG.md +15 -0
- package/README.md +23 -8
- package/README.zh.md +23 -8
- package/config.example.yaml +1 -0
- package/dist/cli/FeishuAppConfiguration.js +10 -10
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/FeishuAppRegistration.js +21 -11
- package/dist/cli/FeishuAppRegistration.js.map +1 -1
- package/dist/cli/Initializer.js +14 -8
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/LocalControlClient.js +2 -2
- package/dist/cli/LocalControlClient.js.map +1 -1
- package/dist/cli/LocalControlServer.js +1 -1
- package/dist/cli/LocalControlServer.js.map +1 -1
- package/dist/cli/ServerStarter.js +69 -0
- package/dist/cli/ServerStarter.js.map +1 -0
- package/dist/cli/SkillRegistry.js +4 -4
- package/dist/cli/SkillRegistry.js.map +1 -1
- package/dist/cli/help.js +5 -56
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/packageVersion.js +1 -1
- package/dist/cli/packageVersion.js.map +1 -1
- package/dist/cli/profile.js +40 -0
- package/dist/cli/profile.js.map +1 -0
- package/dist/cli/serverStatus.js +37 -0
- package/dist/cli/serverStatus.js.map +1 -0
- package/dist/cli/taskChatRoute.js +2 -2
- package/dist/cli/taskChatRoute.js.map +1 -1
- package/dist/cli.js +148 -168
- package/dist/cli.js.map +1 -1
- package/dist/config/loadConfig.js +7 -2
- package/dist/config/loadConfig.js.map +1 -1
- package/dist/config/paths.js +1 -0
- package/dist/config/paths.js.map +1 -1
- package/dist/config/schema.js +1 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/feishu/CardRenderer.js +21 -3
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +7 -76
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/transport.js +1 -1
- package/dist/feishu/transport.js.map +1 -1
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +28 -13
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/startup/StartupNotifier.js +13 -2
- package/dist/startup/StartupNotifier.js.map +1 -1
- package/dist/startup/startFeishu.js +1 -1
- package/dist/startup/startFeishu.js.map +1 -1
- package/dist/supervision/SafeRestartNotifier.js +23 -7
- package/dist/supervision/SafeRestartNotifier.js.map +1 -1
- package/dist/supervision/SafeRestartScheduler.js +20 -2
- package/dist/supervision/SafeRestartScheduler.js.map +1 -1
- package/dist/supervision/SupervisorDiagnostics.js +209 -0
- package/dist/supervision/SupervisorDiagnostics.js.map +1 -0
- package/dist/supervisor.js +47 -12
- package/dist/supervisor.js.map +1 -1
- package/docs/technical-reference.md +25 -5
- package/docs/technical-reference.zh.md +25 -5
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +24 -4
- package/src/cli/FeishuAppConfiguration.ts +10 -10
- package/src/cli/FeishuAppRegistration.ts +23 -10
- package/src/cli/Initializer.ts +14 -7
- package/src/cli/LocalControlClient.ts +2 -2
- package/src/cli/LocalControlServer.ts +1 -1
- package/src/cli/ServerStarter.ts +106 -0
- package/src/cli/SkillRegistry.ts +4 -4
- package/src/cli/help.ts +5 -65
- package/src/cli/packageVersion.ts +1 -1
- package/src/cli/profile.ts +59 -0
- package/src/cli/serverStatus.ts +39 -0
- package/src/cli/taskChatRoute.ts +2 -2
- package/src/cli.ts +160 -167
- package/src/config/loadConfig.ts +12 -2
- package/src/config/paths.ts +1 -0
- package/src/config/schema.ts +1 -0
- package/src/feishu/CardRenderer.ts +25 -3
- package/src/feishu/FeishuConnector.ts +6 -87
- package/src/feishu/transport.ts +1 -1
- package/src/index.ts +22 -9
- package/src/proxy/ProxySessionController.ts +28 -13
- package/src/startup/StartupNotifier.ts +21 -4
- package/src/startup/startFeishu.ts +1 -1
- package/src/supervision/SafeRestartNotifier.ts +22 -7
- package/src/supervision/SafeRestartScheduler.ts +27 -3
- package/src/supervision/SupervisorDiagnostics.ts +268 -0
- package/src/supervisor.ts +46 -11
package/dist/cli.js
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import qrcode from "qrcode-terminal";
|
|
5
5
|
import { loadConfig } from "./config/loadConfig.js";
|
|
6
|
-
import { sendControlRequest, isServerReachable
|
|
6
|
+
import { sendControlRequest, isServerReachable } from "./cli/LocalControlClient.js";
|
|
7
7
|
import { controlEndpoint, } from "./cli/controlProtocol.js";
|
|
8
8
|
import { acquireInitializationLock, cleanupFeishuCredentialTemporaryFiles, initializeAgentBot, readFeishuCredentials, shouldCreateFeishuApp, writeFeishuCredentials, } from "./cli/Initializer.js";
|
|
9
9
|
import { registerFeishuApp, } from "./cli/FeishuAppRegistration.js";
|
|
10
10
|
import { ensureFeishuAppConfiguration, } from "./cli/FeishuAppConfiguration.js";
|
|
11
11
|
import { renderCliHelp } from "./cli/help.js";
|
|
12
|
-
import {
|
|
12
|
+
import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
|
|
13
13
|
import { resolveSystemSkillsRoot, SkillRegistry } from "./cli/SkillRegistry.js";
|
|
14
14
|
import { readPackageVersion } from "./cli/packageVersion.js";
|
|
15
|
+
import { applyExplicitProfile, parseGlobalOptions } from "./cli/profile.js";
|
|
16
|
+
import { startInitializedServer, startServer, } from "./cli/ServerStarter.js";
|
|
15
17
|
import { taskChatRoute } from "./cli/taskChatRoute.js";
|
|
16
18
|
import { StateStore } from "./state/StateStore.js";
|
|
19
|
+
import { nodeDiagnosticReportArguments, prepareCrashReportDirectory, resolveSupervisorDiagnosticsPaths, } from "./supervision/SupervisorDiagnostics.js";
|
|
17
20
|
const args = process.argv.slice(2);
|
|
18
21
|
void main(args).catch((error) => {
|
|
19
|
-
process.stderr.write(
|
|
22
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
20
23
|
process.exitCode = 1;
|
|
21
24
|
});
|
|
22
25
|
async function main(input) {
|
|
23
26
|
const parsed = parseGlobalOptions(input);
|
|
24
|
-
if (parsed.
|
|
27
|
+
if (parsed.profilePath)
|
|
28
|
+
applyExplicitProfile(parsed.profilePath);
|
|
29
|
+
else if (parsed.configPath)
|
|
25
30
|
process.env.AGENT_BOT_CONFIG = parsed.configPath;
|
|
26
31
|
const [command, ...rest] = parsed.args;
|
|
27
32
|
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
@@ -52,35 +57,42 @@ async function main(input) {
|
|
|
52
57
|
skillsCommand(rest);
|
|
53
58
|
return;
|
|
54
59
|
}
|
|
55
|
-
throw new Error(
|
|
60
|
+
throw new Error(`Unknown command: ${command}. Run agent-bot --help for usage.`);
|
|
56
61
|
}
|
|
57
62
|
async function initCommand(input, configPath) {
|
|
58
63
|
const supported = new Set(["--json", "--skip-feishu", "--reconfigure-feishu"]);
|
|
59
64
|
const unsupported = input.filter((value) => !supported.has(value));
|
|
60
65
|
if (unsupported.length > 0)
|
|
61
|
-
throw new Error(`init
|
|
66
|
+
throw new Error(`Unsupported init options: ${unsupported.join(" ")}`);
|
|
62
67
|
const json = input.includes("--json");
|
|
63
68
|
const skipFeishu = input.includes("--skip-feishu");
|
|
64
69
|
const reconfigureFeishu = input.includes("--reconfigure-feishu");
|
|
65
70
|
if (skipFeishu && reconfigureFeishu) {
|
|
66
|
-
throw new Error("--skip-feishu
|
|
71
|
+
throw new Error("--skip-feishu and --reconfigure-feishu cannot be used together.");
|
|
67
72
|
}
|
|
68
73
|
const paths = initializeAgentBot({ configPath });
|
|
69
74
|
if (!json)
|
|
70
75
|
printInitializationPaths(paths);
|
|
71
76
|
const initializationLock = acquireInitializationLock(paths.home.path);
|
|
77
|
+
let initialized;
|
|
72
78
|
try {
|
|
73
79
|
cleanupFeishuCredentialTemporaryFiles(paths.env.path);
|
|
74
80
|
const feishu = await initializeFeishu(paths, { json, skipFeishu, reconfigureFeishu });
|
|
75
|
-
|
|
76
|
-
if (json)
|
|
77
|
-
printJson(result);
|
|
78
|
-
else
|
|
79
|
-
printInitializationResult(result);
|
|
81
|
+
initialized = { ...paths, feishu };
|
|
80
82
|
}
|
|
81
83
|
finally {
|
|
82
84
|
initializationLock.release();
|
|
83
85
|
}
|
|
86
|
+
if (!json)
|
|
87
|
+
printInitializationResult(initialized);
|
|
88
|
+
if (!json && !skipFeishu)
|
|
89
|
+
process.stdout.write("\nStarting Agent Bot server...\n");
|
|
90
|
+
const server = await startInitializedServer({ skipFeishu, configPath });
|
|
91
|
+
const result = { ...initialized, server };
|
|
92
|
+
if (json)
|
|
93
|
+
printJson(result);
|
|
94
|
+
else
|
|
95
|
+
printInitializationServerResult(server);
|
|
84
96
|
}
|
|
85
97
|
async function initializeFeishu(paths, options) {
|
|
86
98
|
const existing = readFeishuCredentials(paths.env.path);
|
|
@@ -91,7 +103,7 @@ async function initializeFeishu(paths, options) {
|
|
|
91
103
|
};
|
|
92
104
|
}
|
|
93
105
|
const controller = new AbortController();
|
|
94
|
-
const onInterrupt = () => controller.abort(new Error("
|
|
106
|
+
const onInterrupt = () => controller.abort(new Error("Initialization was cancelled."));
|
|
95
107
|
process.once("SIGINT", onInterrupt);
|
|
96
108
|
process.once("SIGTERM", onInterrupt);
|
|
97
109
|
try {
|
|
@@ -101,12 +113,13 @@ async function initializeFeishu(paths, options) {
|
|
|
101
113
|
credentials = {
|
|
102
114
|
appId: existing.appId,
|
|
103
115
|
appSecret: existing.appSecret,
|
|
116
|
+
userOpenId: existing.userOpenId,
|
|
104
117
|
};
|
|
105
118
|
status = "existing";
|
|
106
119
|
}
|
|
107
120
|
else {
|
|
108
121
|
if (existing.status === "incomplete" && !options.json) {
|
|
109
|
-
process.stdout.write("\n
|
|
122
|
+
process.stdout.write("\nIncomplete Lark credentials were found. A new bot will be created.\n");
|
|
110
123
|
}
|
|
111
124
|
credentials = await registerFeishuApp({
|
|
112
125
|
signal: controller.signal,
|
|
@@ -116,7 +129,7 @@ async function initializeFeishu(paths, options) {
|
|
|
116
129
|
status = "created";
|
|
117
130
|
}
|
|
118
131
|
if (!options.json)
|
|
119
|
-
process.stdout.write("\n
|
|
132
|
+
process.stdout.write("\nChecking Lark app permissions, events, and callbacks...\n");
|
|
120
133
|
const configuration = await ensureFeishuAppConfiguration(credentials, {
|
|
121
134
|
signal: controller.signal,
|
|
122
135
|
onVerification: (challenge) => printFeishuConfigurationVerification(challenge, options.json),
|
|
@@ -144,8 +157,8 @@ function skillsCommand(input) {
|
|
|
144
157
|
printJson(result);
|
|
145
158
|
else
|
|
146
159
|
process.stdout.write(result.updated
|
|
147
|
-
?
|
|
148
|
-
: `Agent Bot Skill
|
|
160
|
+
? `Installed the Agent Bot Skill: ${result.status.targetPath}\n`
|
|
161
|
+
: `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`);
|
|
149
162
|
return;
|
|
150
163
|
}
|
|
151
164
|
if (action === "uninstall" || action === "unregister") {
|
|
@@ -154,8 +167,8 @@ function skillsCommand(input) {
|
|
|
154
167
|
printJson({ removed, targetPath: registry.targetPath });
|
|
155
168
|
else
|
|
156
169
|
process.stdout.write(removed
|
|
157
|
-
?
|
|
158
|
-
: `Agent Bot Skill
|
|
170
|
+
? `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`
|
|
171
|
+
: `The Agent Bot Skill is not installed: ${registry.targetPath}\n`);
|
|
159
172
|
return;
|
|
160
173
|
}
|
|
161
174
|
if (action === "status") {
|
|
@@ -171,23 +184,28 @@ function skillsCommand(input) {
|
|
|
171
184
|
if (json)
|
|
172
185
|
printJson(paths);
|
|
173
186
|
else {
|
|
174
|
-
process.stdout.write(
|
|
175
|
-
process.stdout.write(
|
|
176
|
-
process.stdout.write(
|
|
187
|
+
process.stdout.write(`Built-in Skill: ${paths.sourcePath}\n`);
|
|
188
|
+
process.stdout.write(`System Skills directory: ${paths.skillsRoot}\n`);
|
|
189
|
+
process.stdout.write(`Installation path: ${paths.targetPath}\n`);
|
|
177
190
|
}
|
|
178
191
|
return;
|
|
179
192
|
}
|
|
180
|
-
throw new Error(
|
|
193
|
+
throw new Error(`Unknown skills command: ${action}`);
|
|
181
194
|
}
|
|
182
195
|
async function consoleCommand(input) {
|
|
183
196
|
const config = loadConfig();
|
|
184
197
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
185
198
|
const force = input.includes("--force");
|
|
186
199
|
if (!force && await isServerReachable(endpoint)) {
|
|
187
|
-
throw new Error("
|
|
200
|
+
throw new Error("The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.");
|
|
188
201
|
}
|
|
189
202
|
const entry = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
190
|
-
const
|
|
203
|
+
const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
|
|
204
|
+
prepareCrashReportDirectory(reportDirectory);
|
|
205
|
+
const result = spawnSync(process.execPath, [
|
|
206
|
+
...nodeDiagnosticReportArguments(reportDirectory),
|
|
207
|
+
entry,
|
|
208
|
+
], {
|
|
191
209
|
cwd: process.cwd(),
|
|
192
210
|
stdio: "inherit",
|
|
193
211
|
env: { ...process.env, AGENT_BOT_CONSOLE_ONLY: "1" },
|
|
@@ -205,64 +223,47 @@ async function serverCommand(input) {
|
|
|
205
223
|
try {
|
|
206
224
|
const response = await sendControlRequest(endpoint, { action: "health" }, 1_500);
|
|
207
225
|
ensureOk(response);
|
|
226
|
+
const status = withConfiguredFeishuAppId(response.data, config.feishu.appId);
|
|
208
227
|
if (rest.includes("--json"))
|
|
209
|
-
printJson(
|
|
228
|
+
printJson(status);
|
|
210
229
|
else
|
|
211
|
-
|
|
230
|
+
process.stdout.write(formatServerStatus(status));
|
|
212
231
|
}
|
|
213
232
|
catch {
|
|
233
|
+
const status = {
|
|
234
|
+
running: false,
|
|
235
|
+
feishuAppId: config.feishu.appId ?? null,
|
|
236
|
+
};
|
|
214
237
|
if (rest.includes("--json"))
|
|
215
|
-
printJson(
|
|
238
|
+
printJson(status);
|
|
216
239
|
else
|
|
217
|
-
process.stdout.write(
|
|
240
|
+
process.stdout.write(formatServerStatus(status));
|
|
218
241
|
process.exitCode = 3;
|
|
219
242
|
}
|
|
220
243
|
return;
|
|
221
244
|
}
|
|
222
245
|
if (action === "start") {
|
|
223
|
-
|
|
224
|
-
if (await isServerRunning(endpoint)) {
|
|
225
|
-
process.stdout.write("agent-bot server 已在运行。\n");
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
if (await isServerReachable(endpoint)) {
|
|
229
|
-
const running = await waitForServer(endpoint, 45_000);
|
|
230
|
-
if (!running)
|
|
231
|
-
throw new Error("agent-bot server 已启动,但未能连接飞书机器人。请检查日志。");
|
|
232
|
-
process.stdout.write("agent-bot server 已启动。\n");
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
const entry = fileURLToPath(new URL("./supervisor.js", import.meta.url));
|
|
236
|
-
const child = spawn(process.execPath, [entry], {
|
|
237
|
-
cwd: process.cwd(),
|
|
238
|
-
detached: true,
|
|
239
|
-
windowsHide: true,
|
|
240
|
-
stdio: "ignore",
|
|
241
|
-
env: { ...process.env, AGENT_BOT_RESTART_REASON: "通过 agent-bot CLI 启动" },
|
|
242
|
-
});
|
|
243
|
-
child.unref();
|
|
244
|
-
const running = await waitForServer(endpoint, 45_000);
|
|
245
|
-
if (!running)
|
|
246
|
-
throw new Error("已启动 Supervisor,但 server 未在 45 秒内连接飞书机器人。请检查日志。");
|
|
247
|
-
process.stdout.write("agent-bot server 已启动。\n");
|
|
246
|
+
printServerStartResult(await startServer(config));
|
|
248
247
|
return;
|
|
249
248
|
}
|
|
250
249
|
if (action === "stop") {
|
|
251
|
-
|
|
250
|
+
ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
|
|
251
|
+
process.stdout.write("Agent Bot server stop requested.\n");
|
|
252
252
|
return;
|
|
253
253
|
}
|
|
254
254
|
if (action === "restart") {
|
|
255
255
|
const immediate = rest.includes("--immediate") || rest.includes("--force");
|
|
256
256
|
const reason = optionValue(rest, "--reason")
|
|
257
|
-
?? (immediate ? "通过
|
|
258
|
-
|
|
257
|
+
?? (immediate ? "通过 Agent Bot CLI 立即重启" : "通过 Agent Bot CLI 安全重启");
|
|
258
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
259
259
|
action: "server_restart",
|
|
260
260
|
mode: immediate ? "immediate" : "safe",
|
|
261
261
|
reason,
|
|
262
262
|
}));
|
|
263
|
+
process.stdout.write(immediate ? "Immediate restart requested.\n" : "Safe restart requested.\n");
|
|
263
264
|
return;
|
|
264
265
|
}
|
|
265
|
-
throw new Error(
|
|
266
|
+
throw new Error(`Unknown server command: ${action}`);
|
|
266
267
|
}
|
|
267
268
|
async function taskCommand(input) {
|
|
268
269
|
const [action = "list", ...rest] = input;
|
|
@@ -273,17 +274,18 @@ async function taskCommand(input) {
|
|
|
273
274
|
if (action === "prompt" || action === "send") {
|
|
274
275
|
const [reference, ...promptParts] = rest;
|
|
275
276
|
if (!reference)
|
|
276
|
-
throw new Error(`task ${action}
|
|
277
|
+
throw new Error(`task ${action} requires a task number or task ID.`);
|
|
277
278
|
const text = promptParts.join(" ").trim();
|
|
278
279
|
if (!text)
|
|
279
|
-
throw new Error(`task ${action}
|
|
280
|
+
throw new Error(`task ${action} requires a Prompt.`);
|
|
280
281
|
const session = resolveTask(allSessions, reference);
|
|
281
282
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
282
|
-
|
|
283
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
283
284
|
action: "task_prompt",
|
|
284
285
|
localSessionId: session.localSessionId,
|
|
285
286
|
text,
|
|
286
287
|
}, 60_000));
|
|
288
|
+
process.stdout.write("Prompt submitted.\n");
|
|
287
289
|
return;
|
|
288
290
|
}
|
|
289
291
|
const sessions = filterSessions(allSessions, rest);
|
|
@@ -296,7 +298,7 @@ async function taskCommand(input) {
|
|
|
296
298
|
}
|
|
297
299
|
const reference = rest.find((value) => !value.startsWith("--"));
|
|
298
300
|
if (!reference)
|
|
299
|
-
throw new Error(`task ${action}
|
|
301
|
+
throw new Error(`task ${action} requires a task number or task ID.`);
|
|
300
302
|
const session = resolveTask(sessions, reference);
|
|
301
303
|
if (action === "chat") {
|
|
302
304
|
const route = taskChatRoute(session);
|
|
@@ -336,7 +338,8 @@ async function taskCommand(input) {
|
|
|
336
338
|
}
|
|
337
339
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
338
340
|
if (action === "stop") {
|
|
339
|
-
|
|
341
|
+
ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
|
|
342
|
+
process.stdout.write("Task stop requested.\n");
|
|
340
343
|
return;
|
|
341
344
|
}
|
|
342
345
|
if (action === "title") {
|
|
@@ -345,31 +348,21 @@ async function taskCommand(input) {
|
|
|
345
348
|
const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
|
|
346
349
|
const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
|
|
347
350
|
if (!title)
|
|
348
|
-
throw new Error("task title
|
|
349
|
-
|
|
351
|
+
throw new Error("task title requires a new title.");
|
|
352
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
350
353
|
action: "task_title",
|
|
351
354
|
localSessionId: session.localSessionId,
|
|
352
355
|
title,
|
|
353
356
|
}));
|
|
357
|
+
process.stdout.write("Task title updated.\n");
|
|
354
358
|
return;
|
|
355
359
|
}
|
|
356
|
-
throw new Error(
|
|
360
|
+
throw new Error(`Unknown task command: ${action}`);
|
|
357
361
|
}
|
|
358
362
|
finally {
|
|
359
363
|
store.close();
|
|
360
364
|
}
|
|
361
365
|
}
|
|
362
|
-
function parseGlobalOptions(input) {
|
|
363
|
-
const args = [...input];
|
|
364
|
-
const configIndex = args.indexOf("--config");
|
|
365
|
-
if (configIndex < 0)
|
|
366
|
-
return { args };
|
|
367
|
-
const configPath = args[configIndex + 1];
|
|
368
|
-
if (!configPath)
|
|
369
|
-
throw new Error("--config 需要配置文件路径。");
|
|
370
|
-
args.splice(configIndex, 2);
|
|
371
|
-
return { configPath, args };
|
|
372
|
-
}
|
|
373
366
|
function filterSessions(sessions, args) {
|
|
374
367
|
const context = optionValue(args, "--context");
|
|
375
368
|
const status = optionValue(args, "--status");
|
|
@@ -381,7 +374,7 @@ function resolveTask(sessions, reference) {
|
|
|
381
374
|
const index = Number(reference) - 1;
|
|
382
375
|
const session = sessions[index];
|
|
383
376
|
if (!session)
|
|
384
|
-
throw new Error(
|
|
377
|
+
throw new Error(`Task number is out of range: ${reference}`);
|
|
385
378
|
return session;
|
|
386
379
|
}
|
|
387
380
|
const exact = sessions.find((session) => session.localSessionId === reference || session.remoteSessionId === reference || session.acpSessionId === reference);
|
|
@@ -393,132 +386,128 @@ function resolveTask(sessions, reference) {
|
|
|
393
386
|
if (matches.length === 1)
|
|
394
387
|
return matches[0];
|
|
395
388
|
if (matches.length > 1)
|
|
396
|
-
throw new Error(
|
|
397
|
-
throw new Error(
|
|
389
|
+
throw new Error(`Task ID prefix is ambiguous: ${reference}`);
|
|
390
|
+
throw new Error(`Task not found: ${reference}`);
|
|
398
391
|
}
|
|
399
392
|
function printTaskList(sessions) {
|
|
400
393
|
if (sessions.length === 0) {
|
|
401
|
-
process.stdout.write("
|
|
394
|
+
process.stdout.write("No tasks.\n");
|
|
402
395
|
return;
|
|
403
396
|
}
|
|
404
397
|
for (const [index, session] of sessions.entries()) {
|
|
405
398
|
const id = session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId;
|
|
406
|
-
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "
|
|
399
|
+
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "Untitled task"}\n`);
|
|
407
400
|
process.stdout.write(` ${id} · ${session.contextKey} · ${session.updatedAt}\n`);
|
|
408
401
|
}
|
|
409
402
|
}
|
|
410
403
|
function printTaskStatus(session, snapshot, remote) {
|
|
411
|
-
process.stdout.write(
|
|
412
|
-
process.stdout.write(
|
|
413
|
-
process.stdout.write(
|
|
414
|
-
process.stdout.write(
|
|
415
|
-
process.stdout.write(
|
|
416
|
-
process.stdout.write(
|
|
417
|
-
process.stdout.write(
|
|
404
|
+
process.stdout.write(`Title: ${session.title ?? "Untitled task"}\n`);
|
|
405
|
+
process.stdout.write(`Task ID: ${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
|
|
406
|
+
process.stdout.write(`Local ID: ${session.localSessionId}\n`);
|
|
407
|
+
process.stdout.write(`Context: ${session.contextKey}\n`);
|
|
408
|
+
process.stdout.write(`Status: ${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
|
|
409
|
+
process.stdout.write(`Directory: ${session.cwd}\n`);
|
|
410
|
+
process.stdout.write(`Last turn: ${session.lastTurnId ?? "-"}\n`);
|
|
418
411
|
let displayedFinalResponse;
|
|
419
412
|
if (snapshot && typeof snapshot === "object") {
|
|
420
413
|
const state = snapshot;
|
|
421
414
|
if (typeof state.durationMs === "number")
|
|
422
|
-
process.stdout.write(
|
|
415
|
+
process.stdout.write(`Duration: ${Math.round(state.durationMs / 1_000)}s\n`);
|
|
423
416
|
if (typeof state.totalTokens === "number")
|
|
424
417
|
process.stdout.write(`Tokens:${state.totalTokens}\n`);
|
|
425
418
|
if (typeof state.totalToolCount === "number")
|
|
426
|
-
process.stdout.write(
|
|
419
|
+
process.stdout.write(`Tool calls: ${state.totalToolCount}\n`);
|
|
427
420
|
if (typeof state.finalResponse === "string" && state.finalResponse.trim())
|
|
428
421
|
displayedFinalResponse = state.finalResponse.trim();
|
|
429
422
|
}
|
|
430
423
|
if (!snapshot && remote?.lastTurnToolCount !== undefined)
|
|
431
|
-
process.stdout.write(
|
|
424
|
+
process.stdout.write(`Tool calls: ${remote.lastTurnToolCount}\n`);
|
|
432
425
|
displayedFinalResponse = remote?.finalResponse?.trim() || displayedFinalResponse;
|
|
433
426
|
if (displayedFinalResponse)
|
|
434
|
-
process.stdout.write(
|
|
435
|
-
}
|
|
436
|
-
function printServerStatus(data) {
|
|
437
|
-
const value = data && typeof data === "object" ? data : {};
|
|
438
|
-
const activity = value.activity && typeof value.activity === "object"
|
|
439
|
-
? value.activity
|
|
440
|
-
: {};
|
|
441
|
-
process.stdout.write(value.ready === false
|
|
442
|
-
? "agent-bot server:启动中(正在连接飞书机器人)\n"
|
|
443
|
-
: "agent-bot server:运行中\n");
|
|
444
|
-
process.stdout.write(`PID:${value.pid ?? "-"}\n`);
|
|
445
|
-
process.stdout.write(`启动时间:${value.startedAt ?? "-"}\n`);
|
|
446
|
-
process.stdout.write(`Supervisor:${value.supervised ? "已启用" : "未启用"}\n`);
|
|
447
|
-
process.stdout.write(`运行任务:${activity.runningSessions ?? 0}\n`);
|
|
448
|
-
process.stdout.write(`待投递结果:${activity.pendingFinalDeliveries ?? 0}\n`);
|
|
449
|
-
process.stdout.write(`安全重启:${value.safeRestartScheduled ? `等待中(${value.safeRestartReason ?? "未注明原因"})` : "未安排"}\n`);
|
|
427
|
+
process.stdout.write(`Final response:\n${displayedFinalResponse}\n`);
|
|
450
428
|
}
|
|
451
429
|
function printSkillStatus(status) {
|
|
452
|
-
process.stdout.write(`Agent Bot Skill
|
|
453
|
-
process.stdout.write(
|
|
454
|
-
process.stdout.write(
|
|
430
|
+
process.stdout.write(`Agent Bot Skill: ${status.registered ? "installed" : "not installed"}\n`);
|
|
431
|
+
process.stdout.write(`System directory: ${status.skillsRoot}\n`);
|
|
432
|
+
process.stdout.write(`Installation path: ${status.targetPath}\n`);
|
|
455
433
|
if (status.registered) {
|
|
456
|
-
process.stdout.write(
|
|
457
|
-
process.stdout.write(
|
|
434
|
+
process.stdout.write(`Ownership: ${status.managed ? "managed by Agent Bot" : "external directory (will not be modified or removed)"}\n`);
|
|
435
|
+
process.stdout.write(`Version: ${status.upToDate ? "up to date" : "update required"}\n`);
|
|
458
436
|
}
|
|
459
437
|
}
|
|
460
438
|
function printInitializationPaths(result) {
|
|
461
|
-
process.stdout.write("
|
|
462
|
-
process.stdout.write(
|
|
463
|
-
process.stdout.write(
|
|
464
|
-
process.stdout.write(
|
|
465
|
-
process.stdout.write(
|
|
466
|
-
process.stdout.write(
|
|
439
|
+
process.stdout.write("Preparing the Agent Bot user environment...\n");
|
|
440
|
+
process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
|
|
441
|
+
process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
|
|
442
|
+
process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
|
|
443
|
+
process.stdout.write(`Data directory: ${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
|
|
444
|
+
process.stdout.write(`Log directory: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
|
|
467
445
|
}
|
|
468
446
|
function printInitializationResult(result) {
|
|
469
|
-
process.stdout.write("\nAgent Bot
|
|
447
|
+
process.stdout.write("\nAgent Bot initialization completed.\n");
|
|
470
448
|
if (result.feishu.status === "created") {
|
|
471
|
-
process.stdout.write(
|
|
449
|
+
process.stdout.write(`Lark app: created and credentials saved (${result.feishu.appId})\n`);
|
|
472
450
|
}
|
|
473
451
|
else if (result.feishu.status === "existing") {
|
|
474
|
-
process.stdout.write(
|
|
452
|
+
process.stdout.write(`Lark app: already configured and unchanged (${result.feishu.appId})\n`);
|
|
475
453
|
}
|
|
476
454
|
else {
|
|
477
|
-
process.stdout.write("
|
|
455
|
+
process.stdout.write("Lark app: skipped; use Console mode or run init again later.\n");
|
|
478
456
|
}
|
|
479
457
|
if (result.feishu.configuration?.status === "updated") {
|
|
480
458
|
const added = result.feishu.configuration.added;
|
|
481
|
-
process.stdout.write(
|
|
459
|
+
process.stdout.write(`Lark configuration: added ${added.scopes.length} scopes, ${added.events.length} events, and ${added.callbacks.length} callbacks\n`);
|
|
482
460
|
}
|
|
483
461
|
else if (result.feishu.configuration?.status === "partial") {
|
|
484
462
|
const configuration = result.feishu.configuration;
|
|
485
463
|
const addedCount = configuration.added.scopes.length + configuration.added.events.length + configuration.added.callbacks.length;
|
|
486
464
|
if (addedCount > 0) {
|
|
487
|
-
process.stdout.write(
|
|
465
|
+
process.stdout.write(`Lark configuration: core capabilities are ready; added ${configuration.added.scopes.length} scopes, ${configuration.added.events.length} events, and ${configuration.added.callbacks.length} callbacks\n`);
|
|
488
466
|
}
|
|
489
467
|
else {
|
|
490
|
-
process.stdout.write("
|
|
468
|
+
process.stdout.write("Lark configuration: core capabilities are ready\n");
|
|
491
469
|
}
|
|
492
470
|
printOptionalFeishuConfigurationWarnings(configuration.remaining);
|
|
493
471
|
}
|
|
494
472
|
else if (result.feishu.configuration?.status === "ready") {
|
|
495
|
-
process.stdout.write("
|
|
473
|
+
process.stdout.write("Lark configuration: scopes, events, and callbacks are ready\n");
|
|
474
|
+
}
|
|
475
|
+
process.stdout.write(`Config file: ${result.config.path}\n`);
|
|
476
|
+
}
|
|
477
|
+
function printInitializationServerResult(result) {
|
|
478
|
+
if (result.status === "skipped") {
|
|
479
|
+
process.stdout.write("Agent Bot server: skipped (Lark is not configured; Console mode is available)\n");
|
|
480
|
+
return;
|
|
496
481
|
}
|
|
497
|
-
|
|
498
|
-
|
|
482
|
+
printServerStartResult(result);
|
|
483
|
+
}
|
|
484
|
+
function printServerStartResult(result) {
|
|
485
|
+
process.stdout.write(result.status === "already-running"
|
|
486
|
+
? "Agent Bot server is already running.\n"
|
|
487
|
+
: "Agent Bot server started.\n");
|
|
499
488
|
}
|
|
500
489
|
function printFeishuVerification(challenge, json) {
|
|
501
|
-
process.stderr.write("\n
|
|
490
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to create the bot app:\n\n");
|
|
502
491
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
503
492
|
if (!json) {
|
|
504
493
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
505
494
|
process.stderr.write(`${output}\n`);
|
|
506
495
|
});
|
|
507
496
|
}
|
|
508
|
-
process.stderr.write(
|
|
497
|
+
process.stderr.write(`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`);
|
|
509
498
|
}
|
|
510
499
|
function printFeishuConfigurationVerification(challenge, json) {
|
|
511
|
-
process.stderr.write("\n
|
|
500
|
+
process.stderr.write("\nThe Lark app is missing the following configuration:\n");
|
|
512
501
|
if (challenge.missing.scopes.length > 0) {
|
|
513
|
-
process.stderr.write(
|
|
502
|
+
process.stderr.write(`Scopes: ${challenge.missing.scopes.join(", ")}\n`);
|
|
514
503
|
}
|
|
515
504
|
if (challenge.missing.events.length > 0) {
|
|
516
|
-
process.stderr.write(
|
|
505
|
+
process.stderr.write(`Events: ${challenge.missing.events.join(", ")}\n`);
|
|
517
506
|
}
|
|
518
507
|
if (challenge.missing.callbacks.length > 0) {
|
|
519
|
-
process.stderr.write(
|
|
508
|
+
process.stderr.write(`Callbacks: ${challenge.missing.callbacks.join(", ")}\n`);
|
|
520
509
|
}
|
|
521
|
-
process.stderr.write("\n
|
|
510
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to complete the configuration:\n\n");
|
|
522
511
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
523
512
|
if (!json) {
|
|
524
513
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
@@ -526,25 +515,25 @@ function printFeishuConfigurationVerification(challenge, json) {
|
|
|
526
515
|
});
|
|
527
516
|
}
|
|
528
517
|
if (challenge.blocking) {
|
|
529
|
-
process.stderr.write("
|
|
518
|
+
process.stderr.write("Waiting for the core scopes and message event to become active...\n");
|
|
530
519
|
}
|
|
531
520
|
else {
|
|
532
|
-
process.stderr.write("
|
|
521
|
+
process.stderr.write("These optional items do not block basic messaging. Initialization will continue; grant them later using the link.\n");
|
|
533
522
|
}
|
|
534
523
|
}
|
|
535
524
|
function printOptionalFeishuConfigurationWarnings(missing) {
|
|
536
|
-
process.stdout.write("
|
|
525
|
+
process.stdout.write("Warning: some optional configuration is not active, so some features may be unavailable.\n");
|
|
537
526
|
if (missing.scopes.length > 0) {
|
|
538
|
-
process.stdout.write(
|
|
527
|
+
process.stdout.write(`Inactive scopes: ${missing.scopes.join(", ")}\n`);
|
|
539
528
|
}
|
|
540
529
|
if (missing.events.length > 0) {
|
|
541
|
-
process.stdout.write(
|
|
530
|
+
process.stdout.write(`Inactive events: ${missing.events.join(", ")}\n`);
|
|
542
531
|
}
|
|
543
532
|
if (missing.callbacks.length > 0) {
|
|
544
|
-
process.stdout.write(
|
|
533
|
+
process.stdout.write(`Inactive callbacks: ${missing.callbacks.join(", ")}\n`);
|
|
545
534
|
}
|
|
546
535
|
for (const feature of optionalFeishuFeatureWarnings(missing)) {
|
|
547
|
-
process.stdout.write(
|
|
536
|
+
process.stdout.write(`Affected feature: ${feature}\n`);
|
|
548
537
|
}
|
|
549
538
|
}
|
|
550
539
|
function optionalFeishuFeatureWarnings(missing) {
|
|
@@ -553,54 +542,45 @@ function optionalFeishuFeatureWarnings(missing) {
|
|
|
553
542
|
const callbacks = new Set(missing.callbacks);
|
|
554
543
|
const warnings = [];
|
|
555
544
|
if (scopes.has("im:chat:create")) {
|
|
556
|
-
warnings.push("
|
|
545
|
+
warnings.push("creating Lark groups with /newgroup and /forkgroup");
|
|
557
546
|
}
|
|
558
547
|
if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
|
|
559
|
-
warnings.push("
|
|
548
|
+
warnings.push("synchronizing Agent Bot task titles after Lark group renames");
|
|
560
549
|
}
|
|
561
550
|
if (scopes.has("im:message.reactions:write_only")) {
|
|
562
|
-
warnings.push("
|
|
551
|
+
warnings.push("showing message-processing status with reactions");
|
|
563
552
|
}
|
|
564
553
|
if (scopes.has("im:message:readonly")) {
|
|
565
|
-
warnings.push("
|
|
554
|
+
warnings.push("reading images from user messages");
|
|
566
555
|
}
|
|
567
556
|
if (scopes.has("im:resource")) {
|
|
568
|
-
warnings.push("
|
|
557
|
+
warnings.push("uploading images, sending local images, or setting group avatars");
|
|
569
558
|
}
|
|
570
559
|
if (scopes.has("im:message:update")) {
|
|
571
|
-
warnings.push("
|
|
560
|
+
warnings.push("updating sent progress cards");
|
|
572
561
|
}
|
|
573
562
|
if (callbacks.has("card.action.trigger")) {
|
|
574
|
-
warnings.push("
|
|
563
|
+
warnings.push("card buttons and interactive actions");
|
|
575
564
|
}
|
|
576
565
|
return warnings;
|
|
577
566
|
}
|
|
578
567
|
function initializationStatusLabel(status) {
|
|
579
568
|
if (status === "created")
|
|
580
|
-
return "
|
|
581
|
-
return "
|
|
582
|
-
}
|
|
583
|
-
function printResponse(response) {
|
|
584
|
-
ensureOk(response);
|
|
585
|
-
process.stdout.write(`${response.message ?? "操作成功。"}\n`);
|
|
569
|
+
return "created";
|
|
570
|
+
return "already exists; unchanged";
|
|
586
571
|
}
|
|
587
572
|
function ensureOk(response) {
|
|
588
|
-
if (
|
|
589
|
-
|
|
573
|
+
if (response.ok)
|
|
574
|
+
return;
|
|
575
|
+
const message = response.message?.trim();
|
|
576
|
+
throw new Error(message && !/[一-龥]/u.test(message)
|
|
577
|
+
? message
|
|
578
|
+
: "Agent Bot control operation failed. Check the server logs for details.");
|
|
590
579
|
}
|
|
591
580
|
function optionValue(args, name) {
|
|
592
581
|
const index = args.indexOf(name);
|
|
593
582
|
return index >= 0 ? args[index + 1] : undefined;
|
|
594
583
|
}
|
|
595
|
-
async function waitForServer(endpoint, timeoutMs) {
|
|
596
|
-
const deadline = Date.now() + timeoutMs;
|
|
597
|
-
while (Date.now() < deadline) {
|
|
598
|
-
if (await isServerRunning(endpoint))
|
|
599
|
-
return true;
|
|
600
|
-
await new Promise((resolve) => setTimeout(resolve, 200));
|
|
601
|
-
}
|
|
602
|
-
return false;
|
|
603
|
-
}
|
|
604
584
|
function printJson(value) {
|
|
605
585
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
606
586
|
}
|