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