@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/src/cli.ts
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";
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
type InitializationResult,
|
|
20
21
|
type InitializationStatus,
|
|
21
22
|
} from "./cli/Initializer.js";
|
|
23
|
+
import { parseInitCommandOptions, type InitCommandOptions } from "./cli/initOptions.js";
|
|
22
24
|
import {
|
|
23
25
|
registerFeishuApp,
|
|
24
26
|
type FeishuAppCredentials,
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
type FeishuConfigurationChallenge,
|
|
31
33
|
} from "./cli/FeishuAppConfiguration.js";
|
|
32
34
|
import { renderCliHelp } from "./cli/help.js";
|
|
35
|
+
import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
|
|
33
36
|
import { resolveSystemSkillsRoot, SkillRegistry, type SkillRegistrationStatus } from "./cli/SkillRegistry.js";
|
|
34
37
|
import { readPackageVersion } from "./cli/packageVersion.js";
|
|
35
38
|
import { applyExplicitProfile, parseGlobalOptions } from "./cli/profile.js";
|
|
@@ -46,11 +49,12 @@ import {
|
|
|
46
49
|
prepareCrashReportDirectory,
|
|
47
50
|
resolveSupervisorDiagnosticsPaths,
|
|
48
51
|
} from "./supervision/SupervisorDiagnostics.js";
|
|
52
|
+
import { agentBotHome, defaultSqlitePath } from "./config/paths.js";
|
|
49
53
|
|
|
50
54
|
const args = process.argv.slice(2);
|
|
51
55
|
|
|
52
56
|
void main(args).catch((error: unknown) => {
|
|
53
|
-
process.stderr.write(
|
|
57
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
54
58
|
process.exitCode = 1;
|
|
55
59
|
});
|
|
56
60
|
|
|
@@ -68,7 +72,7 @@ async function main(input: string[]): Promise<void> {
|
|
|
68
72
|
return;
|
|
69
73
|
}
|
|
70
74
|
if (command === "init") {
|
|
71
|
-
await initCommand(rest, parsed.configPath);
|
|
75
|
+
await initCommand(rest, parsed.configPath, Boolean(parsed.profilePath));
|
|
72
76
|
return;
|
|
73
77
|
}
|
|
74
78
|
if (command === "console") {
|
|
@@ -87,7 +91,7 @@ async function main(input: string[]): Promise<void> {
|
|
|
87
91
|
skillsCommand(rest);
|
|
88
92
|
return;
|
|
89
93
|
}
|
|
90
|
-
throw new Error(
|
|
94
|
+
throw new Error(`Unknown command: ${command}. Run agent-bot --help for usage.`);
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
type FeishuInitializationStatus = "created" | "existing" | "skipped";
|
|
@@ -101,44 +105,43 @@ interface InitCommandResult extends InitializationResult {
|
|
|
101
105
|
server: InitializationServerResult;
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
async function initCommand(
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const paths = initializeAgentBot({ configPath });
|
|
116
|
-
if (!json) printInitializationPaths(paths);
|
|
117
|
-
const initializationLock = acquireInitializationLock(paths.home.path);
|
|
118
|
-
|
|
108
|
+
async function initCommand(
|
|
109
|
+
input: string[],
|
|
110
|
+
configPath: string | undefined,
|
|
111
|
+
explicitProfile: boolean,
|
|
112
|
+
): Promise<void> {
|
|
113
|
+
const options = parseInitCommandOptions(input, explicitProfile);
|
|
114
|
+
let initializationLock = options.reset
|
|
115
|
+
? acquireInitializationLock(agentBotHome())
|
|
116
|
+
: undefined;
|
|
117
|
+
let paths: InitializationResult;
|
|
119
118
|
let initialized: Omit<InitCommandResult, "server">;
|
|
120
119
|
try {
|
|
120
|
+
if (options.reset) await assertResetProfileServerStopped(configPath);
|
|
121
|
+
paths = initializeAgentBot({ configPath, reset: options.reset });
|
|
122
|
+
if (!options.json) printInitializationPaths(paths);
|
|
123
|
+
initializationLock ??= acquireInitializationLock(paths.home.path);
|
|
121
124
|
cleanupFeishuCredentialTemporaryFiles(paths.env.path);
|
|
122
125
|
const feishu = await initializeFeishu(
|
|
123
126
|
paths,
|
|
124
|
-
|
|
127
|
+
options,
|
|
125
128
|
);
|
|
126
129
|
initialized = { ...paths, feishu };
|
|
127
130
|
} finally {
|
|
128
|
-
initializationLock
|
|
131
|
+
initializationLock?.release();
|
|
129
132
|
}
|
|
130
133
|
|
|
131
|
-
if (!json) printInitializationResult(initialized);
|
|
132
|
-
if (!json && !skipFeishu) process.stdout.write("\
|
|
133
|
-
const server = await startInitializedServer({ skipFeishu, configPath });
|
|
134
|
+
if (!options.json) printInitializationResult(initialized);
|
|
135
|
+
if (!options.json && !options.skipFeishu) process.stdout.write("\nStarting Agent Bot server...\n");
|
|
136
|
+
const server = await startInitializedServer({ skipFeishu: options.skipFeishu, configPath });
|
|
134
137
|
const result: InitCommandResult = { ...initialized, server };
|
|
135
|
-
if (json) printJson(result);
|
|
138
|
+
if (options.json) printJson(result);
|
|
136
139
|
else printInitializationServerResult(server);
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
async function initializeFeishu(
|
|
140
143
|
paths: InitializationResult,
|
|
141
|
-
options:
|
|
144
|
+
options: InitCommandOptions,
|
|
142
145
|
): Promise<InitCommandResult["feishu"]> {
|
|
143
146
|
const existing = readFeishuCredentials(paths.env.path);
|
|
144
147
|
if (options.skipFeishu) {
|
|
@@ -149,7 +152,7 @@ async function initializeFeishu(
|
|
|
149
152
|
}
|
|
150
153
|
|
|
151
154
|
const controller = new AbortController();
|
|
152
|
-
const onInterrupt = (): void => controller.abort(new Error("
|
|
155
|
+
const onInterrupt = (): void => controller.abort(new Error("Initialization was cancelled."));
|
|
153
156
|
process.once("SIGINT", onInterrupt);
|
|
154
157
|
process.once("SIGTERM", onInterrupt);
|
|
155
158
|
try {
|
|
@@ -164,7 +167,7 @@ async function initializeFeishu(
|
|
|
164
167
|
status = "existing";
|
|
165
168
|
} else {
|
|
166
169
|
if (existing.status === "incomplete" && !options.json) {
|
|
167
|
-
process.stdout.write("\n
|
|
170
|
+
process.stdout.write("\nIncomplete Lark credentials were found. A new bot will be created.\n");
|
|
168
171
|
}
|
|
169
172
|
credentials = await registerFeishuApp({
|
|
170
173
|
signal: controller.signal,
|
|
@@ -174,7 +177,7 @@ async function initializeFeishu(
|
|
|
174
177
|
status = "created";
|
|
175
178
|
}
|
|
176
179
|
|
|
177
|
-
if (!options.json) process.stdout.write("\n
|
|
180
|
+
if (!options.json) process.stdout.write("\nChecking Lark app permissions, events, and callbacks...\n");
|
|
178
181
|
const configuration = await ensureFeishuAppConfiguration(credentials, {
|
|
179
182
|
signal: controller.signal,
|
|
180
183
|
onVerification: (challenge) => printFeishuConfigurationVerification(challenge, options.json),
|
|
@@ -190,6 +193,25 @@ async function initializeFeishu(
|
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
195
|
|
|
196
|
+
async function assertResetProfileServerStopped(configPath?: string): Promise<void> {
|
|
197
|
+
const endpoints = new Set<string>([controlEndpoint(defaultSqlitePath())]);
|
|
198
|
+
const selectedConfigPath = configPath ?? process.env.AGENT_BOT_CONFIG;
|
|
199
|
+
if (selectedConfigPath && fs.existsSync(selectedConfigPath)) {
|
|
200
|
+
try {
|
|
201
|
+
endpoints.add(controlEndpoint(loadConfig(selectedConfigPath).storage.sqlitePath));
|
|
202
|
+
} catch {
|
|
203
|
+
// Reset must remain available when the existing configuration is invalid.
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const endpoint of endpoints) {
|
|
207
|
+
if (await isServerReachable(endpoint)) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
"Cannot reset a running profile. Stop it with agent-bot --profile <directory> server stop, then try again.",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
193
215
|
function skillsCommand(input: string[]): void {
|
|
194
216
|
const [action = "status", ...rest] = input;
|
|
195
217
|
const sourcePath = fileURLToPath(new URL("../skills/agent-bot/", import.meta.url));
|
|
@@ -201,16 +223,16 @@ function skillsCommand(input: string[]): void {
|
|
|
201
223
|
const result = registry.install();
|
|
202
224
|
if (json) printJson(result);
|
|
203
225
|
else process.stdout.write(result.updated
|
|
204
|
-
?
|
|
205
|
-
: `Agent Bot Skill
|
|
226
|
+
? `Installed the Agent Bot Skill: ${result.status.targetPath}\n`
|
|
227
|
+
: `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`);
|
|
206
228
|
return;
|
|
207
229
|
}
|
|
208
230
|
if (action === "uninstall" || action === "unregister") {
|
|
209
231
|
const removed = registry.uninstall();
|
|
210
232
|
if (json) printJson({ removed, targetPath: registry.targetPath });
|
|
211
233
|
else process.stdout.write(removed
|
|
212
|
-
?
|
|
213
|
-
: `Agent Bot Skill
|
|
234
|
+
? `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`
|
|
235
|
+
: `The Agent Bot Skill is not installed: ${registry.targetPath}\n`);
|
|
214
236
|
return;
|
|
215
237
|
}
|
|
216
238
|
if (action === "status") {
|
|
@@ -223,13 +245,13 @@ function skillsCommand(input: string[]): void {
|
|
|
223
245
|
const paths = { sourcePath: registry.sourcePath, skillsRoot: registry.skillsRoot, targetPath: registry.targetPath };
|
|
224
246
|
if (json) printJson(paths);
|
|
225
247
|
else {
|
|
226
|
-
process.stdout.write(
|
|
227
|
-
process.stdout.write(
|
|
228
|
-
process.stdout.write(
|
|
248
|
+
process.stdout.write(`Built-in Skill: ${paths.sourcePath}\n`);
|
|
249
|
+
process.stdout.write(`System Skills directory: ${paths.skillsRoot}\n`);
|
|
250
|
+
process.stdout.write(`Installation path: ${paths.targetPath}\n`);
|
|
229
251
|
}
|
|
230
252
|
return;
|
|
231
253
|
}
|
|
232
|
-
throw new Error(
|
|
254
|
+
throw new Error(`Unknown skills command: ${action}`);
|
|
233
255
|
}
|
|
234
256
|
|
|
235
257
|
async function consoleCommand(input: string[]): Promise<void> {
|
|
@@ -237,7 +259,9 @@ async function consoleCommand(input: string[]): Promise<void> {
|
|
|
237
259
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
238
260
|
const force = input.includes("--force");
|
|
239
261
|
if (!force && await isServerReachable(endpoint)) {
|
|
240
|
-
throw new Error(
|
|
262
|
+
throw new Error(
|
|
263
|
+
"The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.",
|
|
264
|
+
);
|
|
241
265
|
}
|
|
242
266
|
const entry = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
243
267
|
const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
|
|
@@ -262,11 +286,16 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
262
286
|
try {
|
|
263
287
|
const response = await sendControlRequest(endpoint, { action: "health" }, 1_500);
|
|
264
288
|
ensureOk(response);
|
|
265
|
-
|
|
266
|
-
|
|
289
|
+
const status = withConfiguredFeishuAppId(response.data, config.feishu.appId);
|
|
290
|
+
if (rest.includes("--json")) printJson(status);
|
|
291
|
+
else process.stdout.write(formatServerStatus(status));
|
|
267
292
|
} catch {
|
|
268
|
-
|
|
269
|
-
|
|
293
|
+
const status = {
|
|
294
|
+
running: false,
|
|
295
|
+
feishuAppId: config.feishu.appId ?? null,
|
|
296
|
+
};
|
|
297
|
+
if (rest.includes("--json")) printJson(status);
|
|
298
|
+
else process.stdout.write(formatServerStatus(status));
|
|
270
299
|
process.exitCode = 3;
|
|
271
300
|
}
|
|
272
301
|
return;
|
|
@@ -276,21 +305,23 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
276
305
|
return;
|
|
277
306
|
}
|
|
278
307
|
if (action === "stop") {
|
|
279
|
-
|
|
308
|
+
ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
|
|
309
|
+
process.stdout.write("Agent Bot server stop requested.\n");
|
|
280
310
|
return;
|
|
281
311
|
}
|
|
282
312
|
if (action === "restart") {
|
|
283
313
|
const immediate = rest.includes("--immediate") || rest.includes("--force");
|
|
284
314
|
const reason = optionValue(rest, "--reason")
|
|
285
|
-
?? (immediate ? "通过
|
|
286
|
-
|
|
315
|
+
?? (immediate ? "通过 Agent Bot CLI 立即重启" : "通过 Agent Bot CLI 安全重启");
|
|
316
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
287
317
|
action: "server_restart",
|
|
288
318
|
mode: immediate ? "immediate" : "safe",
|
|
289
319
|
reason,
|
|
290
320
|
}));
|
|
321
|
+
process.stdout.write(immediate ? "Immediate restart requested.\n" : "Safe restart requested.\n");
|
|
291
322
|
return;
|
|
292
323
|
}
|
|
293
|
-
throw new Error(
|
|
324
|
+
throw new Error(`Unknown server command: ${action}`);
|
|
294
325
|
}
|
|
295
326
|
|
|
296
327
|
async function taskCommand(input: string[]): Promise<void> {
|
|
@@ -301,16 +332,17 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
301
332
|
const allSessions = store.listAllSessions();
|
|
302
333
|
if (action === "prompt" || action === "send") {
|
|
303
334
|
const [reference, ...promptParts] = rest;
|
|
304
|
-
if (!reference) throw new Error(`task ${action}
|
|
335
|
+
if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
|
|
305
336
|
const text = promptParts.join(" ").trim();
|
|
306
|
-
if (!text) throw new Error(`task ${action}
|
|
337
|
+
if (!text) throw new Error(`task ${action} requires a Prompt.`);
|
|
307
338
|
const session = resolveTask(allSessions, reference);
|
|
308
339
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
309
|
-
|
|
340
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
310
341
|
action: "task_prompt",
|
|
311
342
|
localSessionId: session.localSessionId,
|
|
312
343
|
text,
|
|
313
344
|
}, 60_000));
|
|
345
|
+
process.stdout.write("Prompt submitted.\n");
|
|
314
346
|
return;
|
|
315
347
|
}
|
|
316
348
|
const sessions = filterSessions(allSessions, rest);
|
|
@@ -320,7 +352,7 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
320
352
|
return;
|
|
321
353
|
}
|
|
322
354
|
const reference = rest.find((value) => !value.startsWith("--"));
|
|
323
|
-
if (!reference) throw new Error(`task ${action}
|
|
355
|
+
if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
|
|
324
356
|
const session = resolveTask(sessions, reference);
|
|
325
357
|
if (action === "chat") {
|
|
326
358
|
const route = taskChatRoute(session);
|
|
@@ -355,7 +387,8 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
355
387
|
}
|
|
356
388
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
357
389
|
if (action === "stop") {
|
|
358
|
-
|
|
390
|
+
ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
|
|
391
|
+
process.stdout.write("Task stop requested.\n");
|
|
359
392
|
return;
|
|
360
393
|
}
|
|
361
394
|
if (action === "title") {
|
|
@@ -363,15 +396,16 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
363
396
|
const titleArgs = rest.slice(referenceIndex + 1);
|
|
364
397
|
const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
|
|
365
398
|
const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
|
|
366
|
-
if (!title) throw new Error("task title
|
|
367
|
-
|
|
399
|
+
if (!title) throw new Error("task title requires a new title.");
|
|
400
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
368
401
|
action: "task_title",
|
|
369
402
|
localSessionId: session.localSessionId,
|
|
370
403
|
title,
|
|
371
404
|
}));
|
|
405
|
+
process.stdout.write("Task title updated.\n");
|
|
372
406
|
return;
|
|
373
407
|
}
|
|
374
|
-
throw new Error(
|
|
408
|
+
throw new Error(`Unknown task command: ${action}`);
|
|
375
409
|
} finally {
|
|
376
410
|
store.close();
|
|
377
411
|
}
|
|
@@ -389,7 +423,7 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
|
|
|
389
423
|
if (/^\d+$/.test(reference)) {
|
|
390
424
|
const index = Number(reference) - 1;
|
|
391
425
|
const session = sessions[index];
|
|
392
|
-
if (!session) throw new Error(
|
|
426
|
+
if (!session) throw new Error(`Task number is out of range: ${reference}`);
|
|
393
427
|
return session;
|
|
394
428
|
}
|
|
395
429
|
const exact = sessions.find((session) =>
|
|
@@ -400,93 +434,78 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
|
|
|
400
434
|
|| session.remoteSessionId?.startsWith(reference)
|
|
401
435
|
|| session.acpSessionId?.startsWith(reference));
|
|
402
436
|
if (matches.length === 1) return matches[0]!;
|
|
403
|
-
if (matches.length > 1) throw new Error(
|
|
404
|
-
throw new Error(
|
|
437
|
+
if (matches.length > 1) throw new Error(`Task ID prefix is ambiguous: ${reference}`);
|
|
438
|
+
throw new Error(`Task not found: ${reference}`);
|
|
405
439
|
}
|
|
406
440
|
|
|
407
441
|
function printTaskList(sessions: SessionRecord[]): void {
|
|
408
442
|
if (sessions.length === 0) {
|
|
409
|
-
process.stdout.write("
|
|
443
|
+
process.stdout.write("No tasks.\n");
|
|
410
444
|
return;
|
|
411
445
|
}
|
|
412
446
|
for (const [index, session] of sessions.entries()) {
|
|
413
447
|
const id = session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId;
|
|
414
|
-
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "
|
|
448
|
+
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "Untitled task"}\n`);
|
|
415
449
|
process.stdout.write(` ${id} · ${session.contextKey} · ${session.updatedAt}\n`);
|
|
416
450
|
}
|
|
417
451
|
}
|
|
418
452
|
|
|
419
453
|
function printTaskStatus(session: SessionRecord, snapshot: unknown, remote?: TaskStatusControlData["remote"]): void {
|
|
420
|
-
process.stdout.write(
|
|
421
|
-
process.stdout.write(
|
|
422
|
-
process.stdout.write(
|
|
423
|
-
process.stdout.write(
|
|
424
|
-
process.stdout.write(
|
|
425
|
-
process.stdout.write(
|
|
426
|
-
process.stdout.write(
|
|
454
|
+
process.stdout.write(`Title: ${session.title ?? "Untitled task"}\n`);
|
|
455
|
+
process.stdout.write(`Task ID: ${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
|
|
456
|
+
process.stdout.write(`Local ID: ${session.localSessionId}\n`);
|
|
457
|
+
process.stdout.write(`Context: ${session.contextKey}\n`);
|
|
458
|
+
process.stdout.write(`Status: ${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
|
|
459
|
+
process.stdout.write(`Directory: ${session.cwd}\n`);
|
|
460
|
+
process.stdout.write(`Last turn: ${session.lastTurnId ?? "-"}\n`);
|
|
427
461
|
let displayedFinalResponse: string | undefined;
|
|
428
462
|
if (snapshot && typeof snapshot === "object") {
|
|
429
463
|
const state = snapshot as Record<string, unknown>;
|
|
430
|
-
if (typeof state.durationMs === "number") process.stdout.write(
|
|
464
|
+
if (typeof state.durationMs === "number") process.stdout.write(`Duration: ${Math.round(state.durationMs / 1_000)}s\n`);
|
|
431
465
|
if (typeof state.totalTokens === "number") process.stdout.write(`Tokens:${state.totalTokens}\n`);
|
|
432
|
-
if (typeof state.totalToolCount === "number") process.stdout.write(
|
|
466
|
+
if (typeof state.totalToolCount === "number") process.stdout.write(`Tool calls: ${state.totalToolCount}\n`);
|
|
433
467
|
if (typeof state.finalResponse === "string" && state.finalResponse.trim()) displayedFinalResponse = state.finalResponse.trim();
|
|
434
468
|
}
|
|
435
|
-
if (!snapshot && remote?.lastTurnToolCount !== undefined) process.stdout.write(
|
|
469
|
+
if (!snapshot && remote?.lastTurnToolCount !== undefined) process.stdout.write(`Tool calls: ${remote.lastTurnToolCount}\n`);
|
|
436
470
|
displayedFinalResponse = remote?.finalResponse?.trim() || displayedFinalResponse;
|
|
437
|
-
if (displayedFinalResponse) process.stdout.write(
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function printServerStatus(data: unknown): void {
|
|
441
|
-
const value = data && typeof data === "object" ? data as Record<string, unknown> : {};
|
|
442
|
-
const activity = value.activity && typeof value.activity === "object"
|
|
443
|
-
? value.activity as Record<string, unknown>
|
|
444
|
-
: {};
|
|
445
|
-
process.stdout.write(
|
|
446
|
-
value.ready === false
|
|
447
|
-
? "agent-bot server:启动中(正在连接飞书机器人)\n"
|
|
448
|
-
: "agent-bot server:运行中\n",
|
|
449
|
-
);
|
|
450
|
-
process.stdout.write(`PID:${value.pid ?? "-"}\n`);
|
|
451
|
-
process.stdout.write(`启动时间:${value.startedAt ?? "-"}\n`);
|
|
452
|
-
process.stdout.write(`Supervisor:${value.supervised ? "已启用" : "未启用"}\n`);
|
|
453
|
-
process.stdout.write(`运行任务:${activity.runningSessions ?? 0}\n`);
|
|
454
|
-
process.stdout.write(`待投递结果:${activity.pendingFinalDeliveries ?? 0}\n`);
|
|
455
|
-
process.stdout.write(`安全重启:${value.safeRestartScheduled ? `等待中(${value.safeRestartReason ?? "未注明原因"})` : "未安排"}\n`);
|
|
471
|
+
if (displayedFinalResponse) process.stdout.write(`Final response:\n${displayedFinalResponse}\n`);
|
|
456
472
|
}
|
|
457
473
|
|
|
458
474
|
function printSkillStatus(status: SkillRegistrationStatus): void {
|
|
459
|
-
process.stdout.write(`Agent Bot Skill
|
|
460
|
-
process.stdout.write(
|
|
461
|
-
process.stdout.write(
|
|
475
|
+
process.stdout.write(`Agent Bot Skill: ${status.registered ? "installed" : "not installed"}\n`);
|
|
476
|
+
process.stdout.write(`System directory: ${status.skillsRoot}\n`);
|
|
477
|
+
process.stdout.write(`Installation path: ${status.targetPath}\n`);
|
|
462
478
|
if (status.registered) {
|
|
463
|
-
process.stdout.write(
|
|
464
|
-
process.stdout.write(
|
|
479
|
+
process.stdout.write(`Ownership: ${status.managed ? "managed by Agent Bot" : "external directory (will not be modified or removed)"}\n`);
|
|
480
|
+
process.stdout.write(`Version: ${status.upToDate ? "up to date" : "update required"}\n`);
|
|
465
481
|
}
|
|
466
482
|
}
|
|
467
483
|
|
|
468
484
|
function printInitializationPaths(result: InitializationResult): void {
|
|
469
|
-
process.stdout.write("
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
process.stdout.write(
|
|
474
|
-
process.stdout.write(
|
|
485
|
+
process.stdout.write("Preparing the Agent Bot user environment...\n");
|
|
486
|
+
if (result.reset) {
|
|
487
|
+
process.stdout.write(`Reset backup: ${result.reset.backupPath}\n`);
|
|
488
|
+
}
|
|
489
|
+
process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
|
|
490
|
+
process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
|
|
491
|
+
process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
|
|
492
|
+
process.stdout.write(`Data directory: ${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
|
|
493
|
+
process.stdout.write(`Log directory: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
|
|
475
494
|
}
|
|
476
495
|
|
|
477
496
|
function printInitializationResult(result: Omit<InitCommandResult, "server">): void {
|
|
478
|
-
process.stdout.write("\nAgent Bot
|
|
497
|
+
process.stdout.write("\nAgent Bot initialization completed.\n");
|
|
479
498
|
if (result.feishu.status === "created") {
|
|
480
|
-
process.stdout.write(
|
|
499
|
+
process.stdout.write(`Lark app: created and credentials saved (${result.feishu.appId})\n`);
|
|
481
500
|
} else if (result.feishu.status === "existing") {
|
|
482
|
-
process.stdout.write(
|
|
501
|
+
process.stdout.write(`Lark app: already configured and unchanged (${result.feishu.appId})\n`);
|
|
483
502
|
} else {
|
|
484
|
-
process.stdout.write("
|
|
503
|
+
process.stdout.write("Lark app: skipped; use Console mode or run init again later.\n");
|
|
485
504
|
}
|
|
486
505
|
if (result.feishu.configuration?.status === "updated") {
|
|
487
506
|
const added = result.feishu.configuration.added;
|
|
488
507
|
process.stdout.write(
|
|
489
|
-
|
|
508
|
+
`Lark configuration: added ${added.scopes.length} scopes, ${added.events.length} events, and ${added.callbacks.length} callbacks\n`,
|
|
490
509
|
);
|
|
491
510
|
} else if (result.feishu.configuration?.status === "partial") {
|
|
492
511
|
const configuration = result.feishu.configuration;
|
|
@@ -494,21 +513,21 @@ function printInitializationResult(result: Omit<InitCommandResult, "server">): v
|
|
|
494
513
|
configuration.added.scopes.length + configuration.added.events.length + configuration.added.callbacks.length;
|
|
495
514
|
if (addedCount > 0) {
|
|
496
515
|
process.stdout.write(
|
|
497
|
-
|
|
516
|
+
`Lark configuration: core capabilities are ready; added ${configuration.added.scopes.length} scopes, ${configuration.added.events.length} events, and ${configuration.added.callbacks.length} callbacks\n`,
|
|
498
517
|
);
|
|
499
518
|
} else {
|
|
500
|
-
process.stdout.write("
|
|
519
|
+
process.stdout.write("Lark configuration: core capabilities are ready\n");
|
|
501
520
|
}
|
|
502
521
|
printOptionalFeishuConfigurationWarnings(configuration.remaining);
|
|
503
522
|
} else if (result.feishu.configuration?.status === "ready") {
|
|
504
|
-
process.stdout.write("
|
|
523
|
+
process.stdout.write("Lark configuration: scopes, events, and callbacks are ready\n");
|
|
505
524
|
}
|
|
506
|
-
process.stdout.write(
|
|
525
|
+
process.stdout.write(`Config file: ${result.config.path}\n`);
|
|
507
526
|
}
|
|
508
527
|
|
|
509
528
|
function printInitializationServerResult(result: InitCommandResult["server"]): void {
|
|
510
529
|
if (result.status === "skipped") {
|
|
511
|
-
process.stdout.write("Agent Bot server
|
|
530
|
+
process.stdout.write("Agent Bot server: skipped (Lark is not configured; Console mode is available)\n");
|
|
512
531
|
return;
|
|
513
532
|
}
|
|
514
533
|
printServerStartResult(result);
|
|
@@ -517,37 +536,37 @@ function printInitializationServerResult(result: InitCommandResult["server"]): v
|
|
|
517
536
|
function printServerStartResult(result: ServerStartResult): void {
|
|
518
537
|
process.stdout.write(
|
|
519
538
|
result.status === "already-running"
|
|
520
|
-
? "
|
|
521
|
-
: "
|
|
539
|
+
? "Agent Bot server is already running.\n"
|
|
540
|
+
: "Agent Bot server started.\n",
|
|
522
541
|
);
|
|
523
542
|
}
|
|
524
543
|
|
|
525
544
|
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge, json: boolean): void {
|
|
526
|
-
process.stderr.write("\n
|
|
545
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to create the bot app:\n\n");
|
|
527
546
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
528
547
|
if (!json) {
|
|
529
548
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
530
549
|
process.stderr.write(`${output}\n`);
|
|
531
550
|
});
|
|
532
551
|
}
|
|
533
|
-
process.stderr.write(
|
|
552
|
+
process.stderr.write(`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`);
|
|
534
553
|
}
|
|
535
554
|
|
|
536
555
|
function printFeishuConfigurationVerification(
|
|
537
556
|
challenge: FeishuConfigurationChallenge,
|
|
538
557
|
json: boolean,
|
|
539
558
|
): void {
|
|
540
|
-
process.stderr.write("\n
|
|
559
|
+
process.stderr.write("\nThe Lark app is missing the following configuration:\n");
|
|
541
560
|
if (challenge.missing.scopes.length > 0) {
|
|
542
|
-
process.stderr.write(
|
|
561
|
+
process.stderr.write(`Scopes: ${challenge.missing.scopes.join(", ")}\n`);
|
|
543
562
|
}
|
|
544
563
|
if (challenge.missing.events.length > 0) {
|
|
545
|
-
process.stderr.write(
|
|
564
|
+
process.stderr.write(`Events: ${challenge.missing.events.join(", ")}\n`);
|
|
546
565
|
}
|
|
547
566
|
if (challenge.missing.callbacks.length > 0) {
|
|
548
|
-
process.stderr.write(
|
|
567
|
+
process.stderr.write(`Callbacks: ${challenge.missing.callbacks.join(", ")}\n`);
|
|
549
568
|
}
|
|
550
|
-
process.stderr.write("\n
|
|
569
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to complete the configuration:\n\n");
|
|
551
570
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
552
571
|
if (!json) {
|
|
553
572
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
@@ -555,25 +574,25 @@ function printFeishuConfigurationVerification(
|
|
|
555
574
|
});
|
|
556
575
|
}
|
|
557
576
|
if (challenge.blocking) {
|
|
558
|
-
process.stderr.write("
|
|
577
|
+
process.stderr.write("Waiting for the core scopes and message event to become active...\n");
|
|
559
578
|
} else {
|
|
560
|
-
process.stderr.write("
|
|
579
|
+
process.stderr.write("These optional items do not block basic messaging. Initialization will continue; grant them later using the link.\n");
|
|
561
580
|
}
|
|
562
581
|
}
|
|
563
582
|
|
|
564
583
|
function printOptionalFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge["missing"]): void {
|
|
565
|
-
process.stdout.write("
|
|
584
|
+
process.stdout.write("Warning: some optional configuration is not active, so some features may be unavailable.\n");
|
|
566
585
|
if (missing.scopes.length > 0) {
|
|
567
|
-
process.stdout.write(
|
|
586
|
+
process.stdout.write(`Inactive scopes: ${missing.scopes.join(", ")}\n`);
|
|
568
587
|
}
|
|
569
588
|
if (missing.events.length > 0) {
|
|
570
|
-
process.stdout.write(
|
|
589
|
+
process.stdout.write(`Inactive events: ${missing.events.join(", ")}\n`);
|
|
571
590
|
}
|
|
572
591
|
if (missing.callbacks.length > 0) {
|
|
573
|
-
process.stdout.write(
|
|
592
|
+
process.stdout.write(`Inactive callbacks: ${missing.callbacks.join(", ")}\n`);
|
|
574
593
|
}
|
|
575
594
|
for (const feature of optionalFeishuFeatureWarnings(missing)) {
|
|
576
|
-
process.stdout.write(
|
|
595
|
+
process.stdout.write(`Affected feature: ${feature}\n`);
|
|
577
596
|
}
|
|
578
597
|
}
|
|
579
598
|
|
|
@@ -583,41 +602,41 @@ function optionalFeishuFeatureWarnings(missing: FeishuConfigurationChallenge["mi
|
|
|
583
602
|
const callbacks = new Set(missing.callbacks);
|
|
584
603
|
const warnings: string[] = [];
|
|
585
604
|
if (scopes.has("im:chat:create")) {
|
|
586
|
-
warnings.push("
|
|
605
|
+
warnings.push("creating Lark groups with /newgroup and /forkgroup");
|
|
587
606
|
}
|
|
588
607
|
if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
|
|
589
|
-
warnings.push("
|
|
608
|
+
warnings.push("synchronizing Agent Bot task titles after Lark group renames");
|
|
590
609
|
}
|
|
591
610
|
if (scopes.has("im:message.reactions:write_only")) {
|
|
592
|
-
warnings.push("
|
|
611
|
+
warnings.push("showing message-processing status with reactions");
|
|
593
612
|
}
|
|
594
613
|
if (scopes.has("im:message:readonly")) {
|
|
595
|
-
warnings.push("
|
|
614
|
+
warnings.push("reading images from user messages");
|
|
596
615
|
}
|
|
597
616
|
if (scopes.has("im:resource")) {
|
|
598
|
-
warnings.push("
|
|
617
|
+
warnings.push("uploading images, sending local images, or setting group avatars");
|
|
599
618
|
}
|
|
600
619
|
if (scopes.has("im:message:update")) {
|
|
601
|
-
warnings.push("
|
|
620
|
+
warnings.push("updating sent progress cards");
|
|
602
621
|
}
|
|
603
622
|
if (callbacks.has("card.action.trigger")) {
|
|
604
|
-
warnings.push("
|
|
623
|
+
warnings.push("card buttons and interactive actions");
|
|
605
624
|
}
|
|
606
625
|
return warnings;
|
|
607
626
|
}
|
|
608
627
|
|
|
609
628
|
function initializationStatusLabel(status: InitializationStatus): string {
|
|
610
|
-
if (status === "created") return "
|
|
611
|
-
return "
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
function printResponse(response: ControlResponse): void {
|
|
615
|
-
ensureOk(response);
|
|
616
|
-
process.stdout.write(`${response.message ?? "操作成功。"}\n`);
|
|
629
|
+
if (status === "created") return "created";
|
|
630
|
+
if (status === "reset") return "reset from template";
|
|
631
|
+
return "already exists; unchanged";
|
|
617
632
|
}
|
|
618
633
|
|
|
619
634
|
function ensureOk(response: ControlResponse): void {
|
|
620
|
-
if (
|
|
635
|
+
if (response.ok) return;
|
|
636
|
+
const message = response.message?.trim();
|
|
637
|
+
throw new Error(message && !/[一-龥]/u.test(message)
|
|
638
|
+
? message
|
|
639
|
+
: "Agent Bot control operation failed. Check the server logs for details.");
|
|
621
640
|
}
|
|
622
641
|
|
|
623
642
|
function optionValue(args: string[], name: string): string | undefined {
|
|
@@ -16,6 +16,7 @@ import { localCardImage } from "./LocalCardImage.js";
|
|
|
16
16
|
export interface StartupStatusView {
|
|
17
17
|
startedAt: Date;
|
|
18
18
|
restartReason: string;
|
|
19
|
+
agentBotVersion: string;
|
|
19
20
|
defaultAgentName: string;
|
|
20
21
|
defaultAgentTitle: string;
|
|
21
22
|
cwd: string;
|
|
@@ -340,6 +341,7 @@ export class CardRenderer {
|
|
|
340
341
|
`**默认 Agent**:${view.defaultAgentTitle} (${inlineCode(view.defaultAgentName)})`,
|
|
341
342
|
];
|
|
342
343
|
lines.push(
|
|
344
|
+
`**Agent Bot 版本**:${inlineCode(view.agentBotVersion)}`,
|
|
343
345
|
`**服务状态 / 启动时间**:🟢 在线 / ${formatStartupTime(view.startedAt)}`,
|
|
344
346
|
`**重启原因**:${inlineCode(view.restartReason)}`,
|
|
345
347
|
"> 发送消息即可开始对话;发送 `/new` 创建新任务;发送 `/help` 查看帮助。",
|