@keyou007/agent-bot 0.1.3 → 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/CHANGELOG.md +6 -0
- package/README.md +3 -1
- package/README.zh.md +3 -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 +4 -4
- 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 +2 -57
- 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 +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 +112 -116
- package/dist/cli.js.map +1 -1
- package/dist/feishu/transport.js +1 -1
- package/dist/feishu/transport.js.map +1 -1
- package/dist/index.js +5 -4
- package/dist/index.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +10 -10
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/docs/technical-reference.md +2 -0
- package/docs/technical-reference.zh.md +2 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/skills/agent-bot/SKILL.md +1 -1
- package/src/cli/FeishuAppConfiguration.ts +10 -10
- package/src/cli/FeishuAppRegistration.ts +9 -9
- package/src/cli/Initializer.ts +4 -4
- 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 +2 -66
- 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 +113 -119
- package/src/feishu/transport.ts +1 -1
- package/src/index.ts +5 -4
- package/src/proxy/ProxySessionController.ts +10 -10
package/src/cli.ts
CHANGED
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type FeishuConfigurationChallenge,
|
|
31
31
|
} from "./cli/FeishuAppConfiguration.js";
|
|
32
32
|
import { renderCliHelp } from "./cli/help.js";
|
|
33
|
+
import { formatServerStatus, withConfiguredFeishuAppId } from "./cli/serverStatus.js";
|
|
33
34
|
import { resolveSystemSkillsRoot, SkillRegistry, type SkillRegistrationStatus } from "./cli/SkillRegistry.js";
|
|
34
35
|
import { readPackageVersion } from "./cli/packageVersion.js";
|
|
35
36
|
import { applyExplicitProfile, parseGlobalOptions } from "./cli/profile.js";
|
|
@@ -50,7 +51,7 @@ import {
|
|
|
50
51
|
const args = process.argv.slice(2);
|
|
51
52
|
|
|
52
53
|
void main(args).catch((error: unknown) => {
|
|
53
|
-
process.stderr.write(
|
|
54
|
+
process.stderr.write(`Error: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
54
55
|
process.exitCode = 1;
|
|
55
56
|
});
|
|
56
57
|
|
|
@@ -87,7 +88,7 @@ async function main(input: string[]): Promise<void> {
|
|
|
87
88
|
skillsCommand(rest);
|
|
88
89
|
return;
|
|
89
90
|
}
|
|
90
|
-
throw new Error(
|
|
91
|
+
throw new Error(`Unknown command: ${command}. Run agent-bot --help for usage.`);
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
type FeishuInitializationStatus = "created" | "existing" | "skipped";
|
|
@@ -104,12 +105,12 @@ interface InitCommandResult extends InitializationResult {
|
|
|
104
105
|
async function initCommand(input: string[], configPath?: string): Promise<void> {
|
|
105
106
|
const supported = new Set(["--json", "--skip-feishu", "--reconfigure-feishu"]);
|
|
106
107
|
const unsupported = input.filter((value) => !supported.has(value));
|
|
107
|
-
if (unsupported.length > 0) throw new Error(`init
|
|
108
|
+
if (unsupported.length > 0) throw new Error(`Unsupported init options: ${unsupported.join(" ")}`);
|
|
108
109
|
const json = input.includes("--json");
|
|
109
110
|
const skipFeishu = input.includes("--skip-feishu");
|
|
110
111
|
const reconfigureFeishu = input.includes("--reconfigure-feishu");
|
|
111
112
|
if (skipFeishu && reconfigureFeishu) {
|
|
112
|
-
throw new Error("--skip-feishu
|
|
113
|
+
throw new Error("--skip-feishu and --reconfigure-feishu cannot be used together.");
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
const paths = initializeAgentBot({ configPath });
|
|
@@ -129,7 +130,7 @@ async function initCommand(input: string[], configPath?: string): Promise<void>
|
|
|
129
130
|
}
|
|
130
131
|
|
|
131
132
|
if (!json) printInitializationResult(initialized);
|
|
132
|
-
if (!json && !skipFeishu) process.stdout.write("\
|
|
133
|
+
if (!json && !skipFeishu) process.stdout.write("\nStarting Agent Bot server...\n");
|
|
133
134
|
const server = await startInitializedServer({ skipFeishu, configPath });
|
|
134
135
|
const result: InitCommandResult = { ...initialized, server };
|
|
135
136
|
if (json) printJson(result);
|
|
@@ -149,7 +150,7 @@ async function initializeFeishu(
|
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
const controller = new AbortController();
|
|
152
|
-
const onInterrupt = (): void => controller.abort(new Error("
|
|
153
|
+
const onInterrupt = (): void => controller.abort(new Error("Initialization was cancelled."));
|
|
153
154
|
process.once("SIGINT", onInterrupt);
|
|
154
155
|
process.once("SIGTERM", onInterrupt);
|
|
155
156
|
try {
|
|
@@ -164,7 +165,7 @@ async function initializeFeishu(
|
|
|
164
165
|
status = "existing";
|
|
165
166
|
} else {
|
|
166
167
|
if (existing.status === "incomplete" && !options.json) {
|
|
167
|
-
process.stdout.write("\n
|
|
168
|
+
process.stdout.write("\nIncomplete Lark credentials were found. A new bot will be created.\n");
|
|
168
169
|
}
|
|
169
170
|
credentials = await registerFeishuApp({
|
|
170
171
|
signal: controller.signal,
|
|
@@ -174,7 +175,7 @@ async function initializeFeishu(
|
|
|
174
175
|
status = "created";
|
|
175
176
|
}
|
|
176
177
|
|
|
177
|
-
if (!options.json) process.stdout.write("\n
|
|
178
|
+
if (!options.json) process.stdout.write("\nChecking Lark app permissions, events, and callbacks...\n");
|
|
178
179
|
const configuration = await ensureFeishuAppConfiguration(credentials, {
|
|
179
180
|
signal: controller.signal,
|
|
180
181
|
onVerification: (challenge) => printFeishuConfigurationVerification(challenge, options.json),
|
|
@@ -201,16 +202,16 @@ function skillsCommand(input: string[]): void {
|
|
|
201
202
|
const result = registry.install();
|
|
202
203
|
if (json) printJson(result);
|
|
203
204
|
else process.stdout.write(result.updated
|
|
204
|
-
?
|
|
205
|
-
: `Agent Bot Skill
|
|
205
|
+
? `Installed the Agent Bot Skill: ${result.status.targetPath}\n`
|
|
206
|
+
: `The Agent Bot Skill is already up to date: ${result.status.targetPath}\n`);
|
|
206
207
|
return;
|
|
207
208
|
}
|
|
208
209
|
if (action === "uninstall" || action === "unregister") {
|
|
209
210
|
const removed = registry.uninstall();
|
|
210
211
|
if (json) printJson({ removed, targetPath: registry.targetPath });
|
|
211
212
|
else process.stdout.write(removed
|
|
212
|
-
?
|
|
213
|
-
: `Agent Bot Skill
|
|
213
|
+
? `Uninstalled the Agent Bot Skill: ${registry.targetPath}\n`
|
|
214
|
+
: `The Agent Bot Skill is not installed: ${registry.targetPath}\n`);
|
|
214
215
|
return;
|
|
215
216
|
}
|
|
216
217
|
if (action === "status") {
|
|
@@ -223,13 +224,13 @@ function skillsCommand(input: string[]): void {
|
|
|
223
224
|
const paths = { sourcePath: registry.sourcePath, skillsRoot: registry.skillsRoot, targetPath: registry.targetPath };
|
|
224
225
|
if (json) printJson(paths);
|
|
225
226
|
else {
|
|
226
|
-
process.stdout.write(
|
|
227
|
-
process.stdout.write(
|
|
228
|
-
process.stdout.write(
|
|
227
|
+
process.stdout.write(`Built-in Skill: ${paths.sourcePath}\n`);
|
|
228
|
+
process.stdout.write(`System Skills directory: ${paths.skillsRoot}\n`);
|
|
229
|
+
process.stdout.write(`Installation path: ${paths.targetPath}\n`);
|
|
229
230
|
}
|
|
230
231
|
return;
|
|
231
232
|
}
|
|
232
|
-
throw new Error(
|
|
233
|
+
throw new Error(`Unknown skills command: ${action}`);
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
async function consoleCommand(input: string[]): Promise<void> {
|
|
@@ -237,7 +238,9 @@ async function consoleCommand(input: string[]): Promise<void> {
|
|
|
237
238
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
238
239
|
const force = input.includes("--force");
|
|
239
240
|
if (!force && await isServerReachable(endpoint)) {
|
|
240
|
-
throw new Error(
|
|
241
|
+
throw new Error(
|
|
242
|
+
"The Agent Bot server is running. Stop it first to avoid concurrent task-state access, or explicitly use --force.",
|
|
243
|
+
);
|
|
241
244
|
}
|
|
242
245
|
const entry = fileURLToPath(new URL("./index.js", import.meta.url));
|
|
243
246
|
const reportDirectory = resolveSupervisorDiagnosticsPaths(config).crashReportDirectory;
|
|
@@ -262,11 +265,16 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
262
265
|
try {
|
|
263
266
|
const response = await sendControlRequest(endpoint, { action: "health" }, 1_500);
|
|
264
267
|
ensureOk(response);
|
|
265
|
-
|
|
266
|
-
|
|
268
|
+
const status = withConfiguredFeishuAppId(response.data, config.feishu.appId);
|
|
269
|
+
if (rest.includes("--json")) printJson(status);
|
|
270
|
+
else process.stdout.write(formatServerStatus(status));
|
|
267
271
|
} catch {
|
|
268
|
-
|
|
269
|
-
|
|
272
|
+
const status = {
|
|
273
|
+
running: false,
|
|
274
|
+
feishuAppId: config.feishu.appId ?? null,
|
|
275
|
+
};
|
|
276
|
+
if (rest.includes("--json")) printJson(status);
|
|
277
|
+
else process.stdout.write(formatServerStatus(status));
|
|
270
278
|
process.exitCode = 3;
|
|
271
279
|
}
|
|
272
280
|
return;
|
|
@@ -276,21 +284,23 @@ async function serverCommand(input: string[]): Promise<void> {
|
|
|
276
284
|
return;
|
|
277
285
|
}
|
|
278
286
|
if (action === "stop") {
|
|
279
|
-
|
|
287
|
+
ensureOk(await sendControlRequest(endpoint, { action: "server_stop" }));
|
|
288
|
+
process.stdout.write("Agent Bot server stop requested.\n");
|
|
280
289
|
return;
|
|
281
290
|
}
|
|
282
291
|
if (action === "restart") {
|
|
283
292
|
const immediate = rest.includes("--immediate") || rest.includes("--force");
|
|
284
293
|
const reason = optionValue(rest, "--reason")
|
|
285
|
-
?? (immediate ? "通过
|
|
286
|
-
|
|
294
|
+
?? (immediate ? "通过 Agent Bot CLI 立即重启" : "通过 Agent Bot CLI 安全重启");
|
|
295
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
287
296
|
action: "server_restart",
|
|
288
297
|
mode: immediate ? "immediate" : "safe",
|
|
289
298
|
reason,
|
|
290
299
|
}));
|
|
300
|
+
process.stdout.write(immediate ? "Immediate restart requested.\n" : "Safe restart requested.\n");
|
|
291
301
|
return;
|
|
292
302
|
}
|
|
293
|
-
throw new Error(
|
|
303
|
+
throw new Error(`Unknown server command: ${action}`);
|
|
294
304
|
}
|
|
295
305
|
|
|
296
306
|
async function taskCommand(input: string[]): Promise<void> {
|
|
@@ -301,16 +311,17 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
301
311
|
const allSessions = store.listAllSessions();
|
|
302
312
|
if (action === "prompt" || action === "send") {
|
|
303
313
|
const [reference, ...promptParts] = rest;
|
|
304
|
-
if (!reference) throw new Error(`task ${action}
|
|
314
|
+
if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
|
|
305
315
|
const text = promptParts.join(" ").trim();
|
|
306
|
-
if (!text) throw new Error(`task ${action}
|
|
316
|
+
if (!text) throw new Error(`task ${action} requires a Prompt.`);
|
|
307
317
|
const session = resolveTask(allSessions, reference);
|
|
308
318
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
309
|
-
|
|
319
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
310
320
|
action: "task_prompt",
|
|
311
321
|
localSessionId: session.localSessionId,
|
|
312
322
|
text,
|
|
313
323
|
}, 60_000));
|
|
324
|
+
process.stdout.write("Prompt submitted.\n");
|
|
314
325
|
return;
|
|
315
326
|
}
|
|
316
327
|
const sessions = filterSessions(allSessions, rest);
|
|
@@ -320,7 +331,7 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
320
331
|
return;
|
|
321
332
|
}
|
|
322
333
|
const reference = rest.find((value) => !value.startsWith("--"));
|
|
323
|
-
if (!reference) throw new Error(`task ${action}
|
|
334
|
+
if (!reference) throw new Error(`task ${action} requires a task number or task ID.`);
|
|
324
335
|
const session = resolveTask(sessions, reference);
|
|
325
336
|
if (action === "chat") {
|
|
326
337
|
const route = taskChatRoute(session);
|
|
@@ -355,7 +366,8 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
355
366
|
}
|
|
356
367
|
const endpoint = controlEndpoint(config.storage.sqlitePath);
|
|
357
368
|
if (action === "stop") {
|
|
358
|
-
|
|
369
|
+
ensureOk(await sendControlRequest(endpoint, { action: "task_stop", localSessionId: session.localSessionId }));
|
|
370
|
+
process.stdout.write("Task stop requested.\n");
|
|
359
371
|
return;
|
|
360
372
|
}
|
|
361
373
|
if (action === "title") {
|
|
@@ -363,15 +375,16 @@ async function taskCommand(input: string[]): Promise<void> {
|
|
|
363
375
|
const titleArgs = rest.slice(referenceIndex + 1);
|
|
364
376
|
const optionIndex = titleArgs.findIndex((value) => value.startsWith("--"));
|
|
365
377
|
const title = titleArgs.slice(0, optionIndex < 0 ? undefined : optionIndex).join(" ").trim();
|
|
366
|
-
if (!title) throw new Error("task title
|
|
367
|
-
|
|
378
|
+
if (!title) throw new Error("task title requires a new title.");
|
|
379
|
+
ensureOk(await sendControlRequest(endpoint, {
|
|
368
380
|
action: "task_title",
|
|
369
381
|
localSessionId: session.localSessionId,
|
|
370
382
|
title,
|
|
371
383
|
}));
|
|
384
|
+
process.stdout.write("Task title updated.\n");
|
|
372
385
|
return;
|
|
373
386
|
}
|
|
374
|
-
throw new Error(
|
|
387
|
+
throw new Error(`Unknown task command: ${action}`);
|
|
375
388
|
} finally {
|
|
376
389
|
store.close();
|
|
377
390
|
}
|
|
@@ -389,7 +402,7 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
|
|
|
389
402
|
if (/^\d+$/.test(reference)) {
|
|
390
403
|
const index = Number(reference) - 1;
|
|
391
404
|
const session = sessions[index];
|
|
392
|
-
if (!session) throw new Error(
|
|
405
|
+
if (!session) throw new Error(`Task number is out of range: ${reference}`);
|
|
393
406
|
return session;
|
|
394
407
|
}
|
|
395
408
|
const exact = sessions.find((session) =>
|
|
@@ -400,93 +413,75 @@ function resolveTask(sessions: SessionRecord[], reference: string): SessionRecor
|
|
|
400
413
|
|| session.remoteSessionId?.startsWith(reference)
|
|
401
414
|
|| session.acpSessionId?.startsWith(reference));
|
|
402
415
|
if (matches.length === 1) return matches[0]!;
|
|
403
|
-
if (matches.length > 1) throw new Error(
|
|
404
|
-
throw new Error(
|
|
416
|
+
if (matches.length > 1) throw new Error(`Task ID prefix is ambiguous: ${reference}`);
|
|
417
|
+
throw new Error(`Task not found: ${reference}`);
|
|
405
418
|
}
|
|
406
419
|
|
|
407
420
|
function printTaskList(sessions: SessionRecord[]): void {
|
|
408
421
|
if (sessions.length === 0) {
|
|
409
|
-
process.stdout.write("
|
|
422
|
+
process.stdout.write("No tasks.\n");
|
|
410
423
|
return;
|
|
411
424
|
}
|
|
412
425
|
for (const [index, session] of sessions.entries()) {
|
|
413
426
|
const id = session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId;
|
|
414
|
-
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "
|
|
427
|
+
process.stdout.write(`${index + 1}. [${session.status}/${session.lastTurnStatus ?? "-"}] ${session.title ?? "Untitled task"}\n`);
|
|
415
428
|
process.stdout.write(` ${id} · ${session.contextKey} · ${session.updatedAt}\n`);
|
|
416
429
|
}
|
|
417
430
|
}
|
|
418
431
|
|
|
419
432
|
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(
|
|
433
|
+
process.stdout.write(`Title: ${session.title ?? "Untitled task"}\n`);
|
|
434
|
+
process.stdout.write(`Task ID: ${session.remoteSessionId ?? session.acpSessionId ?? session.localSessionId}\n`);
|
|
435
|
+
process.stdout.write(`Local ID: ${session.localSessionId}\n`);
|
|
436
|
+
process.stdout.write(`Context: ${session.contextKey}\n`);
|
|
437
|
+
process.stdout.write(`Status: ${session.status} / ${session.lastTurnStatus ?? "-"}\n`);
|
|
438
|
+
process.stdout.write(`Directory: ${session.cwd}\n`);
|
|
439
|
+
process.stdout.write(`Last turn: ${session.lastTurnId ?? "-"}\n`);
|
|
427
440
|
let displayedFinalResponse: string | undefined;
|
|
428
441
|
if (snapshot && typeof snapshot === "object") {
|
|
429
442
|
const state = snapshot as Record<string, unknown>;
|
|
430
|
-
if (typeof state.durationMs === "number") process.stdout.write(
|
|
443
|
+
if (typeof state.durationMs === "number") process.stdout.write(`Duration: ${Math.round(state.durationMs / 1_000)}s\n`);
|
|
431
444
|
if (typeof state.totalTokens === "number") process.stdout.write(`Tokens:${state.totalTokens}\n`);
|
|
432
|
-
if (typeof state.totalToolCount === "number") process.stdout.write(
|
|
445
|
+
if (typeof state.totalToolCount === "number") process.stdout.write(`Tool calls: ${state.totalToolCount}\n`);
|
|
433
446
|
if (typeof state.finalResponse === "string" && state.finalResponse.trim()) displayedFinalResponse = state.finalResponse.trim();
|
|
434
447
|
}
|
|
435
|
-
if (!snapshot && remote?.lastTurnToolCount !== undefined) process.stdout.write(
|
|
448
|
+
if (!snapshot && remote?.lastTurnToolCount !== undefined) process.stdout.write(`Tool calls: ${remote.lastTurnToolCount}\n`);
|
|
436
449
|
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`);
|
|
450
|
+
if (displayedFinalResponse) process.stdout.write(`Final response:\n${displayedFinalResponse}\n`);
|
|
456
451
|
}
|
|
457
452
|
|
|
458
453
|
function printSkillStatus(status: SkillRegistrationStatus): void {
|
|
459
|
-
process.stdout.write(`Agent Bot Skill
|
|
460
|
-
process.stdout.write(
|
|
461
|
-
process.stdout.write(
|
|
454
|
+
process.stdout.write(`Agent Bot Skill: ${status.registered ? "installed" : "not installed"}\n`);
|
|
455
|
+
process.stdout.write(`System directory: ${status.skillsRoot}\n`);
|
|
456
|
+
process.stdout.write(`Installation path: ${status.targetPath}\n`);
|
|
462
457
|
if (status.registered) {
|
|
463
|
-
process.stdout.write(
|
|
464
|
-
process.stdout.write(
|
|
458
|
+
process.stdout.write(`Ownership: ${status.managed ? "managed by Agent Bot" : "external directory (will not be modified or removed)"}\n`);
|
|
459
|
+
process.stdout.write(`Version: ${status.upToDate ? "up to date" : "update required"}\n`);
|
|
465
460
|
}
|
|
466
461
|
}
|
|
467
462
|
|
|
468
463
|
function printInitializationPaths(result: InitializationResult): void {
|
|
469
|
-
process.stdout.write("
|
|
470
|
-
process.stdout.write(
|
|
471
|
-
process.stdout.write(
|
|
472
|
-
process.stdout.write(
|
|
473
|
-
process.stdout.write(
|
|
474
|
-
process.stdout.write(
|
|
464
|
+
process.stdout.write("Preparing the Agent Bot user environment...\n");
|
|
465
|
+
process.stdout.write(`Home directory: ${result.home.path} (${initializationStatusLabel(result.home.status)})\n`);
|
|
466
|
+
process.stdout.write(`Config file: ${result.config.path} (${initializationStatusLabel(result.config.status)})\n`);
|
|
467
|
+
process.stdout.write(`Environment file: ${result.env.path} (${initializationStatusLabel(result.env.status)})\n`);
|
|
468
|
+
process.stdout.write(`Data directory: ${result.data.path} (${initializationStatusLabel(result.data.status)})\n`);
|
|
469
|
+
process.stdout.write(`Log directory: ${result.logs.path} (${initializationStatusLabel(result.logs.status)})\n`);
|
|
475
470
|
}
|
|
476
471
|
|
|
477
472
|
function printInitializationResult(result: Omit<InitCommandResult, "server">): void {
|
|
478
|
-
process.stdout.write("\nAgent Bot
|
|
473
|
+
process.stdout.write("\nAgent Bot initialization completed.\n");
|
|
479
474
|
if (result.feishu.status === "created") {
|
|
480
|
-
process.stdout.write(
|
|
475
|
+
process.stdout.write(`Lark app: created and credentials saved (${result.feishu.appId})\n`);
|
|
481
476
|
} else if (result.feishu.status === "existing") {
|
|
482
|
-
process.stdout.write(
|
|
477
|
+
process.stdout.write(`Lark app: already configured and unchanged (${result.feishu.appId})\n`);
|
|
483
478
|
} else {
|
|
484
|
-
process.stdout.write("
|
|
479
|
+
process.stdout.write("Lark app: skipped; use Console mode or run init again later.\n");
|
|
485
480
|
}
|
|
486
481
|
if (result.feishu.configuration?.status === "updated") {
|
|
487
482
|
const added = result.feishu.configuration.added;
|
|
488
483
|
process.stdout.write(
|
|
489
|
-
|
|
484
|
+
`Lark configuration: added ${added.scopes.length} scopes, ${added.events.length} events, and ${added.callbacks.length} callbacks\n`,
|
|
490
485
|
);
|
|
491
486
|
} else if (result.feishu.configuration?.status === "partial") {
|
|
492
487
|
const configuration = result.feishu.configuration;
|
|
@@ -494,21 +489,21 @@ function printInitializationResult(result: Omit<InitCommandResult, "server">): v
|
|
|
494
489
|
configuration.added.scopes.length + configuration.added.events.length + configuration.added.callbacks.length;
|
|
495
490
|
if (addedCount > 0) {
|
|
496
491
|
process.stdout.write(
|
|
497
|
-
|
|
492
|
+
`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
493
|
);
|
|
499
494
|
} else {
|
|
500
|
-
process.stdout.write("
|
|
495
|
+
process.stdout.write("Lark configuration: core capabilities are ready\n");
|
|
501
496
|
}
|
|
502
497
|
printOptionalFeishuConfigurationWarnings(configuration.remaining);
|
|
503
498
|
} else if (result.feishu.configuration?.status === "ready") {
|
|
504
|
-
process.stdout.write("
|
|
499
|
+
process.stdout.write("Lark configuration: scopes, events, and callbacks are ready\n");
|
|
505
500
|
}
|
|
506
|
-
process.stdout.write(
|
|
501
|
+
process.stdout.write(`Config file: ${result.config.path}\n`);
|
|
507
502
|
}
|
|
508
503
|
|
|
509
504
|
function printInitializationServerResult(result: InitCommandResult["server"]): void {
|
|
510
505
|
if (result.status === "skipped") {
|
|
511
|
-
process.stdout.write("Agent Bot server
|
|
506
|
+
process.stdout.write("Agent Bot server: skipped (Lark is not configured; Console mode is available)\n");
|
|
512
507
|
return;
|
|
513
508
|
}
|
|
514
509
|
printServerStartResult(result);
|
|
@@ -517,37 +512,37 @@ function printInitializationServerResult(result: InitCommandResult["server"]): v
|
|
|
517
512
|
function printServerStartResult(result: ServerStartResult): void {
|
|
518
513
|
process.stdout.write(
|
|
519
514
|
result.status === "already-running"
|
|
520
|
-
? "
|
|
521
|
-
: "
|
|
515
|
+
? "Agent Bot server is already running.\n"
|
|
516
|
+
: "Agent Bot server started.\n",
|
|
522
517
|
);
|
|
523
518
|
}
|
|
524
519
|
|
|
525
520
|
function printFeishuVerification(challenge: FeishuAppRegistrationChallenge, json: boolean): void {
|
|
526
|
-
process.stderr.write("\n
|
|
521
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to create the bot app:\n\n");
|
|
527
522
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
528
523
|
if (!json) {
|
|
529
524
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
530
525
|
process.stderr.write(`${output}\n`);
|
|
531
526
|
});
|
|
532
527
|
}
|
|
533
|
-
process.stderr.write(
|
|
528
|
+
process.stderr.write(`The link expires in about ${Math.ceil(challenge.expiresIn / 60)} minutes. Waiting for confirmation...\n`);
|
|
534
529
|
}
|
|
535
530
|
|
|
536
531
|
function printFeishuConfigurationVerification(
|
|
537
532
|
challenge: FeishuConfigurationChallenge,
|
|
538
533
|
json: boolean,
|
|
539
534
|
): void {
|
|
540
|
-
process.stderr.write("\n
|
|
535
|
+
process.stderr.write("\nThe Lark app is missing the following configuration:\n");
|
|
541
536
|
if (challenge.missing.scopes.length > 0) {
|
|
542
|
-
process.stderr.write(
|
|
537
|
+
process.stderr.write(`Scopes: ${challenge.missing.scopes.join(", ")}\n`);
|
|
543
538
|
}
|
|
544
539
|
if (challenge.missing.events.length > 0) {
|
|
545
|
-
process.stderr.write(
|
|
540
|
+
process.stderr.write(`Events: ${challenge.missing.events.join(", ")}\n`);
|
|
546
541
|
}
|
|
547
542
|
if (challenge.missing.callbacks.length > 0) {
|
|
548
|
-
process.stderr.write(
|
|
543
|
+
process.stderr.write(`Callbacks: ${challenge.missing.callbacks.join(", ")}\n`);
|
|
549
544
|
}
|
|
550
|
-
process.stderr.write("\n
|
|
545
|
+
process.stderr.write("\nScan with Lark or open the link below in a browser to complete the configuration:\n\n");
|
|
551
546
|
process.stderr.write(`${challenge.verificationUrl}\n\n`);
|
|
552
547
|
if (!json) {
|
|
553
548
|
qrcode.generate(challenge.verificationUrl, { small: true }, (output) => {
|
|
@@ -555,25 +550,25 @@ function printFeishuConfigurationVerification(
|
|
|
555
550
|
});
|
|
556
551
|
}
|
|
557
552
|
if (challenge.blocking) {
|
|
558
|
-
process.stderr.write("
|
|
553
|
+
process.stderr.write("Waiting for the core scopes and message event to become active...\n");
|
|
559
554
|
} else {
|
|
560
|
-
process.stderr.write("
|
|
555
|
+
process.stderr.write("These optional items do not block basic messaging. Initialization will continue; grant them later using the link.\n");
|
|
561
556
|
}
|
|
562
557
|
}
|
|
563
558
|
|
|
564
559
|
function printOptionalFeishuConfigurationWarnings(missing: FeishuConfigurationChallenge["missing"]): void {
|
|
565
|
-
process.stdout.write("
|
|
560
|
+
process.stdout.write("Warning: some optional configuration is not active, so some features may be unavailable.\n");
|
|
566
561
|
if (missing.scopes.length > 0) {
|
|
567
|
-
process.stdout.write(
|
|
562
|
+
process.stdout.write(`Inactive scopes: ${missing.scopes.join(", ")}\n`);
|
|
568
563
|
}
|
|
569
564
|
if (missing.events.length > 0) {
|
|
570
|
-
process.stdout.write(
|
|
565
|
+
process.stdout.write(`Inactive events: ${missing.events.join(", ")}\n`);
|
|
571
566
|
}
|
|
572
567
|
if (missing.callbacks.length > 0) {
|
|
573
|
-
process.stdout.write(
|
|
568
|
+
process.stdout.write(`Inactive callbacks: ${missing.callbacks.join(", ")}\n`);
|
|
574
569
|
}
|
|
575
570
|
for (const feature of optionalFeishuFeatureWarnings(missing)) {
|
|
576
|
-
process.stdout.write(
|
|
571
|
+
process.stdout.write(`Affected feature: ${feature}\n`);
|
|
577
572
|
}
|
|
578
573
|
}
|
|
579
574
|
|
|
@@ -583,41 +578,40 @@ function optionalFeishuFeatureWarnings(missing: FeishuConfigurationChallenge["mi
|
|
|
583
578
|
const callbacks = new Set(missing.callbacks);
|
|
584
579
|
const warnings: string[] = [];
|
|
585
580
|
if (scopes.has("im:chat:create")) {
|
|
586
|
-
warnings.push("
|
|
581
|
+
warnings.push("creating Lark groups with /newgroup and /forkgroup");
|
|
587
582
|
}
|
|
588
583
|
if (scopes.has("im:chat:read") || events.has("im.chat.updated_v1")) {
|
|
589
|
-
warnings.push("
|
|
584
|
+
warnings.push("synchronizing Agent Bot task titles after Lark group renames");
|
|
590
585
|
}
|
|
591
586
|
if (scopes.has("im:message.reactions:write_only")) {
|
|
592
|
-
warnings.push("
|
|
587
|
+
warnings.push("showing message-processing status with reactions");
|
|
593
588
|
}
|
|
594
589
|
if (scopes.has("im:message:readonly")) {
|
|
595
|
-
warnings.push("
|
|
590
|
+
warnings.push("reading images from user messages");
|
|
596
591
|
}
|
|
597
592
|
if (scopes.has("im:resource")) {
|
|
598
|
-
warnings.push("
|
|
593
|
+
warnings.push("uploading images, sending local images, or setting group avatars");
|
|
599
594
|
}
|
|
600
595
|
if (scopes.has("im:message:update")) {
|
|
601
|
-
warnings.push("
|
|
596
|
+
warnings.push("updating sent progress cards");
|
|
602
597
|
}
|
|
603
598
|
if (callbacks.has("card.action.trigger")) {
|
|
604
|
-
warnings.push("
|
|
599
|
+
warnings.push("card buttons and interactive actions");
|
|
605
600
|
}
|
|
606
601
|
return warnings;
|
|
607
602
|
}
|
|
608
603
|
|
|
609
604
|
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`);
|
|
605
|
+
if (status === "created") return "created";
|
|
606
|
+
return "already exists; unchanged";
|
|
617
607
|
}
|
|
618
608
|
|
|
619
609
|
function ensureOk(response: ControlResponse): void {
|
|
620
|
-
if (
|
|
610
|
+
if (response.ok) return;
|
|
611
|
+
const message = response.message?.trim();
|
|
612
|
+
throw new Error(message && !/[一-龥]/u.test(message)
|
|
613
|
+
? message
|
|
614
|
+
: "Agent Bot control operation failed. Check the server logs for details.");
|
|
621
615
|
}
|
|
622
616
|
|
|
623
617
|
function optionValue(args: string[], name: string): string | undefined {
|
package/src/feishu/transport.ts
CHANGED
|
@@ -8,7 +8,7 @@ type FeishuTransportConfig = Pick<
|
|
|
8
8
|
export function requireServerFeishuTransport(config: FeishuTransportConfig): "sdk" {
|
|
9
9
|
if (!config.appId || !config.appSecret) {
|
|
10
10
|
throw new Error(
|
|
11
|
-
"
|
|
11
|
+
"The Lark bot is not configured. Run agent-bot init first, or use agent-bot console for local-only testing.",
|
|
12
12
|
);
|
|
13
13
|
}
|
|
14
14
|
return "sdk";
|
package/src/index.ts
CHANGED
|
@@ -177,6 +177,7 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
177
177
|
pid: process.pid,
|
|
178
178
|
startedAt: processStartedAt.toISOString(),
|
|
179
179
|
supervised,
|
|
180
|
+
feishuAppId: config.feishu.appId ?? null,
|
|
180
181
|
safeRestartScheduled: safeRestart.scheduled,
|
|
181
182
|
safeRestartReason: safeRestart.pendingReason,
|
|
182
183
|
activity: store.getServerActivityState(),
|
|
@@ -188,16 +189,16 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
188
189
|
return {
|
|
189
190
|
ok: true,
|
|
190
191
|
message: newlyScheduled
|
|
191
|
-
? "
|
|
192
|
-
: "
|
|
192
|
+
? "Safe restart scheduled. It will run after all tasks finish and the server stays idle for 15 seconds."
|
|
193
|
+
: "A safe restart is already pending. Its reason has been updated.",
|
|
193
194
|
};
|
|
194
195
|
}
|
|
195
196
|
setTimeout(() => void initiateRestart(request.reason), 25);
|
|
196
|
-
return { ok: true, message: "
|
|
197
|
+
return { ok: true, message: "Immediate restart requested." };
|
|
197
198
|
case "server_stop":
|
|
198
199
|
safeRestart.cancel();
|
|
199
200
|
setTimeout(() => void shutdown(STOP_EXIT_CODE), 25);
|
|
200
|
-
return { ok: true, message: "
|
|
201
|
+
return { ok: true, message: "Agent Bot server stop requested." };
|
|
201
202
|
case "task_status":
|
|
202
203
|
return { ok: true, data: await controller.controlGetTaskStatus(request.localSessionId) };
|
|
203
204
|
case "task_stop":
|
|
@@ -415,9 +415,9 @@ export class ProxySessionController {
|
|
|
415
415
|
|
|
416
416
|
async controlStopTask(localSessionId: string): Promise<string> {
|
|
417
417
|
const record = this.store.getSession(localSessionId);
|
|
418
|
-
if (!record || record.status === "closed") throw new Error(
|
|
418
|
+
if (!record || record.status === "closed") throw new Error(`Task not found: ${localSessionId}`);
|
|
419
419
|
await this.cancelSession(record);
|
|
420
|
-
return
|
|
420
|
+
return `Task stop requested: ${record.title ?? record.remoteSessionId ?? record.localSessionId}`;
|
|
421
421
|
}
|
|
422
422
|
|
|
423
423
|
async controlGetTaskStatus(localSessionId: string): Promise<{
|
|
@@ -426,7 +426,7 @@ export class ProxySessionController {
|
|
|
426
426
|
remote?: RemoteSessionSummary;
|
|
427
427
|
}> {
|
|
428
428
|
const record = this.store.getSession(localSessionId);
|
|
429
|
-
if (!record) throw new Error(
|
|
429
|
+
if (!record) throw new Error(`Task not found: ${localSessionId}`);
|
|
430
430
|
let remote: RemoteSessionSummary | undefined;
|
|
431
431
|
if (record.remoteSessionId) {
|
|
432
432
|
const runtime = this.runtimes.forAgent(this.ensureAgent(record.agentName));
|
|
@@ -447,9 +447,9 @@ export class ProxySessionController {
|
|
|
447
447
|
|
|
448
448
|
async controlSetTaskTitle(localSessionId: string, title: string): Promise<string> {
|
|
449
449
|
const normalizedTitle = normalizeTaskTitle(title);
|
|
450
|
-
if (!normalizedTitle) throw new Error("
|
|
450
|
+
if (!normalizedTitle) throw new Error("The task title cannot be empty.");
|
|
451
451
|
const record = this.store.getSession(localSessionId);
|
|
452
|
-
if (!record || record.status === "closed") throw new Error(
|
|
452
|
+
if (!record || record.status === "closed") throw new Error(`Task not found: ${localSessionId}`);
|
|
453
453
|
const loaded = await this.loadSession(record);
|
|
454
454
|
if (loaded.runtime.setTitle) await loaded.runtime.setTitle(record.localSessionId, normalizedTitle);
|
|
455
455
|
else loaded.session.title = normalizedTitle;
|
|
@@ -460,14 +460,14 @@ export class ProxySessionController {
|
|
|
460
460
|
title: normalizedTitle,
|
|
461
461
|
source: "cli",
|
|
462
462
|
});
|
|
463
|
-
return
|
|
463
|
+
return `Task title changed to: ${normalizedTitle}`;
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
async controlSendTaskPrompt(localSessionId: string, text: string): Promise<string> {
|
|
467
467
|
const promptText = text.trim();
|
|
468
|
-
if (!promptText) throw new Error("Prompt
|
|
468
|
+
if (!promptText) throw new Error("The Prompt cannot be empty.");
|
|
469
469
|
const record = this.store.getSession(localSessionId);
|
|
470
|
-
if (!record || record.status === "closed") throw new Error(
|
|
470
|
+
if (!record || record.status === "closed") throw new Error(`Task not found: ${localSessionId}`);
|
|
471
471
|
const runtime = this.runtimes.forAgent(this.ensureAgent(record.agentName));
|
|
472
472
|
const activeTurnId = runtime.getSession(localSessionId)?.activeTurnId;
|
|
473
473
|
const routedContextKey = this.outbound.getSessionContextKey(localSessionId);
|
|
@@ -487,7 +487,7 @@ export class ProxySessionController {
|
|
|
487
487
|
?? lastSnapshot?.replyTarget
|
|
488
488
|
?? routedReplyTarget;
|
|
489
489
|
if (isThreadContextKey(responseContextKey) && !existingReplyTarget) {
|
|
490
|
-
throw new Error("
|
|
490
|
+
throw new Error("Could not resolve the target task's thread reply location. The Prompt was not sent.");
|
|
491
491
|
}
|
|
492
492
|
const scopedRecord = this.store.getSessionForContext(localSessionId, responseContextKey)
|
|
493
493
|
?? { ...record, contextKey: responseContextKey };
|
|
@@ -521,7 +521,7 @@ export class ProxySessionController {
|
|
|
521
521
|
} finally {
|
|
522
522
|
if (this.messageQueues.get(responseContextKey) === next) this.messageQueues.delete(responseContextKey);
|
|
523
523
|
}
|
|
524
|
-
return
|
|
524
|
+
return `The Prompt was posted to the original chat and submitted to the task: ${record.title ?? record.remoteSessionId ?? record.localSessionId}`;
|
|
525
525
|
}
|
|
526
526
|
|
|
527
527
|
private cardActionContextKey(action: CardAction): string {
|