@keyou007/agent-bot 0.1.14-alpha.1 → 0.1.14-alpha.2
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 +14 -1
- package/README.md +42 -12
- package/README.zh.md +42 -12
- package/config.example.yaml +1 -1
- package/dist/cli/AutostartManager.js +431 -0
- package/dist/cli/AutostartManager.js.map +1 -0
- package/dist/cli/FeishuAppConfiguration.js +17 -13
- package/dist/cli/FeishuAppConfiguration.js.map +1 -1
- package/dist/cli/Initializer.js +35 -0
- package/dist/cli/Initializer.js.map +1 -1
- package/dist/cli/SelfUpdateRunner.js +552 -0
- package/dist/cli/SelfUpdateRunner.js.map +1 -0
- package/dist/cli/SelfUpdater.js +431 -0
- package/dist/cli/SelfUpdater.js.map +1 -0
- package/dist/cli/autostartOutput.js +63 -0
- package/dist/cli/autostartOutput.js.map +1 -0
- package/dist/cli/controlProtocol.js.map +1 -1
- package/dist/cli/help.js +92 -18
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/taskGroupOptions.js +14 -11
- package/dist/cli/taskGroupOptions.js.map +1 -1
- package/dist/cli/taskListOutput.js +3 -0
- package/dist/cli/taskListOutput.js.map +1 -1
- package/dist/cli/taskTarget.js +99 -0
- package/dist/cli/taskTarget.js.map +1 -0
- package/dist/cli.js +643 -58
- package/dist/cli.js.map +1 -1
- package/dist/codex/CodexRuntime.js +8 -2
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +7 -1
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +172 -23
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuMarkdown.js +25 -1
- package/dist/feishu/FeishuMarkdown.js.map +1 -1
- package/dist/index.js +134 -2
- package/dist/index.js.map +1 -1
- package/dist/proxy/ProxySessionController.js +436 -12
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +18 -0
- package/dist/state/StateStore.js.map +1 -1
- package/docs/technical-reference.md +17 -7
- package/docs/technical-reference.zh.md +17 -7
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -2
- package/skills/agent-bot/SKILL.md +107 -188
- package/src/cli/AutostartManager.ts +579 -0
- package/src/cli/FeishuAppConfiguration.ts +22 -7
- package/src/cli/Initializer.ts +57 -0
- package/src/cli/SelfUpdateRunner.ts +817 -0
- package/src/cli/SelfUpdater.ts +688 -0
- package/src/cli/autostartOutput.ts +75 -0
- package/src/cli/controlProtocol.ts +111 -1
- package/src/cli/help.ts +92 -18
- package/src/cli/taskGroupOptions.ts +27 -17
- package/src/cli/taskListOutput.ts +10 -0
- package/src/cli/taskTarget.ts +168 -0
- package/src/cli.ts +781 -65
- package/src/codex/CodexRuntime.ts +8 -2
- package/src/commands/CommandRouter.ts +6 -1
- package/src/commands/commandTypes.ts +1 -0
- package/src/feishu/CardRenderer.ts +199 -23
- package/src/feishu/FeishuMarkdown.ts +27 -1
- package/src/index.ts +172 -2
- package/src/proxy/ProxySessionController.ts +532 -15
- package/src/runtime/types.ts +1 -0
- package/src/state/StateStore.ts +19 -0
package/src/index.ts
CHANGED
|
@@ -48,6 +48,12 @@ import {
|
|
|
48
48
|
resolveSupervisorDiagnosticsPaths,
|
|
49
49
|
} from "./supervision/SupervisorDiagnostics.js";
|
|
50
50
|
import { refreshedSystemEnvironment } from "./supervision/systemEnvironment.js";
|
|
51
|
+
import {
|
|
52
|
+
assertSelfUpdatePlanPath,
|
|
53
|
+
launchSelfUpdateRunner,
|
|
54
|
+
readPendingSelfUpdate,
|
|
55
|
+
releaseSelfUpdatePlan,
|
|
56
|
+
} from "./cli/SelfUpdater.js";
|
|
51
57
|
|
|
52
58
|
const processStartedAt = new Date();
|
|
53
59
|
const agentBotVersion = readPackageVersion();
|
|
@@ -119,16 +125,26 @@ if (routes.length === 0) throw new Error("Neither Feishu nor console input is en
|
|
|
119
125
|
const outbound = new OutboundRouter(routes);
|
|
120
126
|
let shuttingDown = false;
|
|
121
127
|
let restartRequested = false;
|
|
128
|
+
let pendingSelfUpdatePlanPath: string | undefined;
|
|
122
129
|
const safeRestart = new SafeRestartScheduler({
|
|
123
130
|
readActivity: () => store.getServerActivityState(),
|
|
124
|
-
onReady: (reason, notificationTargets) =>
|
|
131
|
+
onReady: (reason, notificationTargets) => pendingSelfUpdatePlanPath
|
|
132
|
+
? initiateSelfUpdate(reason, notificationTargets)
|
|
133
|
+
: initiateRestart(reason, notificationTargets),
|
|
125
134
|
onStatus: (status) => safeRestartNotifier?.update(status),
|
|
126
135
|
onStatusError: (error) => logger.warn({ error }, "Failed to publish safe restart status."),
|
|
127
136
|
});
|
|
128
137
|
const controller = new ProxySessionController(config, store, runtimes, outbound, logger, {
|
|
129
138
|
supervised,
|
|
130
139
|
restart: requestRestart,
|
|
131
|
-
cancelSafeRestart: (scheduleId) =>
|
|
140
|
+
cancelSafeRestart: async (scheduleId) => {
|
|
141
|
+
const cancelled = await safeRestart.cancelScheduled(scheduleId);
|
|
142
|
+
if (cancelled && pendingSelfUpdatePlanPath) {
|
|
143
|
+
releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
144
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
145
|
+
}
|
|
146
|
+
return cancelled;
|
|
147
|
+
},
|
|
132
148
|
rememberFeishuUserOpenId: (userOpenId) => {
|
|
133
149
|
if (config.feishu.userOpenId?.startsWith("ou_")) return;
|
|
134
150
|
const persisted = persistFeishuUserOpenIdIfMissing(defaultDotEnvPath(), userOpenId);
|
|
@@ -185,6 +201,27 @@ await controller.recoverInterruptedTasks().catch((error: unknown) => {
|
|
|
185
201
|
logger.error({ error }, "Startup task recovery failed; the server will remain available.");
|
|
186
202
|
});
|
|
187
203
|
consoleConnector?.start();
|
|
204
|
+
recoverPendingSelfUpdate();
|
|
205
|
+
|
|
206
|
+
function recoverPendingSelfUpdate(): void {
|
|
207
|
+
const pending = readPendingSelfUpdate(agentBotHome());
|
|
208
|
+
if (!pending || pendingSelfUpdatePlanPath || restartRequested || safeRestart.scheduled) return;
|
|
209
|
+
let notificationTarget: RestartNotificationTarget | undefined;
|
|
210
|
+
try {
|
|
211
|
+
notificationTarget = inferControlRestartTarget(pending.plan.notificationSessionId);
|
|
212
|
+
} catch (error) {
|
|
213
|
+
logger.warn({ error }, "Could not restore the self-update notification target; update recovery will continue.");
|
|
214
|
+
}
|
|
215
|
+
pendingSelfUpdatePlanPath = pending.planPath;
|
|
216
|
+
safeRestart.schedule(
|
|
217
|
+
pending.plan.reason ?? `Resume Agent Bot update to ${pending.plan.toVersion}`,
|
|
218
|
+
notificationTarget,
|
|
219
|
+
);
|
|
220
|
+
logger.warn(
|
|
221
|
+
{ planPath: pending.planPath, toVersion: pending.plan.toVersion },
|
|
222
|
+
"Recovered a prepared Agent Bot self-update after Worker restart.",
|
|
223
|
+
);
|
|
224
|
+
}
|
|
188
225
|
|
|
189
226
|
async function requestRestart(
|
|
190
227
|
contextKey: string,
|
|
@@ -199,6 +236,10 @@ async function requestRestart(
|
|
|
199
236
|
await outbound.sendText(contextKey, "Agent Bot 已在重启中,请稍候。").catch(() => undefined);
|
|
200
237
|
return;
|
|
201
238
|
}
|
|
239
|
+
if (pendingSelfUpdatePlanPath) {
|
|
240
|
+
await outbound.sendText(contextKey, "Agent Bot 更新已在等待中;更新完成后服务会自动恢复。").catch(() => undefined);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
202
243
|
if (force) {
|
|
203
244
|
await initiateRestart("用户执行 /restart --force 命令", [notificationTarget]);
|
|
204
245
|
return;
|
|
@@ -223,6 +264,7 @@ async function initiateRestart(
|
|
|
223
264
|
return;
|
|
224
265
|
}
|
|
225
266
|
restartRequested = true;
|
|
267
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
226
268
|
await safeRestart.cancelCurrent();
|
|
227
269
|
await safeRestartNotifier?.flush();
|
|
228
270
|
const allNotificationTargets = mergeRestartNotificationTargets(
|
|
@@ -240,6 +282,55 @@ async function initiateRestart(
|
|
|
240
282
|
), 100);
|
|
241
283
|
}
|
|
242
284
|
|
|
285
|
+
async function initiateSelfUpdate(
|
|
286
|
+
reason: string,
|
|
287
|
+
notificationTargets: RestartNotificationTarget[] = [],
|
|
288
|
+
): Promise<void> {
|
|
289
|
+
if (restartRequested) {
|
|
290
|
+
await sendRestartTexts(notificationTargets, "Agent Bot 已在重启或更新中,请稍候。");
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
const planPath = pendingSelfUpdatePlanPath;
|
|
294
|
+
if (!planPath) return;
|
|
295
|
+
restartRequested = true;
|
|
296
|
+
await safeRestartNotifier?.flush();
|
|
297
|
+
const allNotificationTargets = mergeRestartNotificationTargets(
|
|
298
|
+
notificationTargets,
|
|
299
|
+
safeRestartNotifier?.getNotificationTargets() ?? [],
|
|
300
|
+
);
|
|
301
|
+
await sendRestartTexts(
|
|
302
|
+
allNotificationTargets,
|
|
303
|
+
"Agent Bot 正在更新,完成或回滚后会自动恢复服务并发送启动状态通知。",
|
|
304
|
+
);
|
|
305
|
+
const environmentRefresh = refreshedSystemEnvironment();
|
|
306
|
+
if (environmentRefresh.error) {
|
|
307
|
+
logger.warn({ error: environmentRefresh.error }, "Failed to refresh the environment for Agent Bot update.");
|
|
308
|
+
}
|
|
309
|
+
try {
|
|
310
|
+
assertSelfUpdatePlanPath(planPath, agentBotHome());
|
|
311
|
+
launchSelfUpdateRunner(planPath, {
|
|
312
|
+
waitPids: [process.pid, ...(supervised ? [process.ppid] : [])],
|
|
313
|
+
environment: replacementSupervisorEnvironment(
|
|
314
|
+
reason,
|
|
315
|
+
environmentRefresh.environment,
|
|
316
|
+
allNotificationTargets,
|
|
317
|
+
),
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
restartRequested = false;
|
|
321
|
+
releaseSelfUpdatePlan(planPath);
|
|
322
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
323
|
+
logger.error({ error, planPath }, "Failed to launch Agent Bot self-update; keeping the current service online.");
|
|
324
|
+
await sendRestartTexts(
|
|
325
|
+
allNotificationTargets,
|
|
326
|
+
`Agent Bot 更新未启动,当前服务保持在线:${error instanceof Error ? error.message : String(error)}`,
|
|
327
|
+
);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
331
|
+
setTimeout(() => void shutdown(STOP_EXIT_CODE), 100);
|
|
332
|
+
}
|
|
333
|
+
|
|
243
334
|
async function handleControlRequest(request: ControlRequest): Promise<ControlResponse> {
|
|
244
335
|
switch (request.action) {
|
|
245
336
|
case "health":
|
|
@@ -268,6 +359,9 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
268
359
|
},
|
|
269
360
|
};
|
|
270
361
|
case "server_restart": {
|
|
362
|
+
if (pendingSelfUpdatePlanPath) {
|
|
363
|
+
return { ok: false, message: "An Agent Bot update is already pending." };
|
|
364
|
+
}
|
|
271
365
|
const notificationTarget = inferControlRestartTarget(request.notificationSessionId);
|
|
272
366
|
if (request.mode === "safe") {
|
|
273
367
|
const newlyScheduled = safeRestart.schedule(request.reason, notificationTarget);
|
|
@@ -284,18 +378,90 @@ async function handleControlRequest(request: ControlRequest): Promise<ControlRes
|
|
|
284
378
|
), 25);
|
|
285
379
|
return { ok: true, message: "Immediate restart requested." };
|
|
286
380
|
}
|
|
381
|
+
case "server_update": {
|
|
382
|
+
if (restartRequested || pendingSelfUpdatePlanPath || safeRestart.scheduled) {
|
|
383
|
+
return { ok: false, message: "A restart or update is already pending." };
|
|
384
|
+
}
|
|
385
|
+
assertSelfUpdatePlanPath(request.planPath, agentBotHome());
|
|
386
|
+
const notificationTarget = inferControlRestartTarget(request.notificationSessionId);
|
|
387
|
+
pendingSelfUpdatePlanPath = request.planPath;
|
|
388
|
+
safeRestart.schedule(request.reason, notificationTarget);
|
|
389
|
+
return {
|
|
390
|
+
ok: true,
|
|
391
|
+
message: "Agent Bot update scheduled after active tasks finish and the server becomes idle.",
|
|
392
|
+
};
|
|
393
|
+
}
|
|
287
394
|
case "server_stop":
|
|
288
395
|
safeRestart.cancel();
|
|
396
|
+
if (pendingSelfUpdatePlanPath) releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
397
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
289
398
|
setTimeout(() => void shutdown(STOP_EXIT_CODE), 25);
|
|
290
399
|
return { ok: true, message: "Agent Bot server stop requested." };
|
|
291
400
|
case "task_status":
|
|
292
401
|
return { ok: true, data: await controller.controlGetTaskStatus(request.localSessionId) };
|
|
293
402
|
case "task_stop":
|
|
294
403
|
return { ok: true, message: await controller.controlStopTask(request.localSessionId) };
|
|
404
|
+
case "task_archive":
|
|
405
|
+
return { ok: true, data: await controller.controlArchiveTask(request.localSessionId) };
|
|
295
406
|
case "task_title":
|
|
296
407
|
return { ok: true, message: await controller.controlSetTaskTitle(request.localSessionId, request.title) };
|
|
297
408
|
case "task_prompt":
|
|
298
409
|
return { ok: true, message: await controller.controlSendTaskPrompt(request.localSessionId, request.text) };
|
|
410
|
+
case "task_new":
|
|
411
|
+
return {
|
|
412
|
+
ok: true,
|
|
413
|
+
data: await controller.controlCreateTask(
|
|
414
|
+
request.localSessionId,
|
|
415
|
+
request.title,
|
|
416
|
+
request.cwd,
|
|
417
|
+
request.projectless === true,
|
|
418
|
+
request.agentName,
|
|
419
|
+
),
|
|
420
|
+
};
|
|
421
|
+
case "task_fork":
|
|
422
|
+
return { ok: true, data: await controller.controlForkTask(request.localSessionId) };
|
|
423
|
+
case "task_switch":
|
|
424
|
+
return {
|
|
425
|
+
ok: true,
|
|
426
|
+
data: await controller.controlSwitchTask(
|
|
427
|
+
request.localSessionId,
|
|
428
|
+
request.targetLocalSessionId,
|
|
429
|
+
request.previous === true,
|
|
430
|
+
),
|
|
431
|
+
};
|
|
432
|
+
case "task_queue":
|
|
433
|
+
return { ok: true, data: await controller.controlQueueTaskPrompt(request.localSessionId, request.text) };
|
|
434
|
+
case "task_agent":
|
|
435
|
+
return { ok: true, data: controller.controlTaskAgent(request.localSessionId, request.agentName) };
|
|
436
|
+
case "task_settings":
|
|
437
|
+
return {
|
|
438
|
+
ok: true,
|
|
439
|
+
data: await controller.controlTaskSettings(request.localSessionId, request.setting, request.value),
|
|
440
|
+
};
|
|
441
|
+
case "task_goal":
|
|
442
|
+
return {
|
|
443
|
+
ok: true,
|
|
444
|
+
data: await controller.controlTaskGoal(
|
|
445
|
+
request.localSessionId,
|
|
446
|
+
request.goalAction,
|
|
447
|
+
request.objective,
|
|
448
|
+
),
|
|
449
|
+
};
|
|
450
|
+
case "task_turns":
|
|
451
|
+
return { ok: true, data: controller.controlListTaskTurns(request.localSessionId) };
|
|
452
|
+
case "task_reset":
|
|
453
|
+
return { ok: true, data: await controller.controlResetTaskToTurn(request.localSessionId, request.turnId) };
|
|
454
|
+
case "task_mute":
|
|
455
|
+
return { ok: true, data: controller.controlTaskMute(request.localSessionId, request.enabled) };
|
|
456
|
+
case "task_shell":
|
|
457
|
+
return { ok: true, data: await controller.controlRunTaskShell(request.localSessionId, request.command) };
|
|
458
|
+
case "task_directory":
|
|
459
|
+
return {
|
|
460
|
+
ok: true,
|
|
461
|
+
data: await controller.controlListTaskDirectory(request.localSessionId, request.directory, request.page),
|
|
462
|
+
};
|
|
463
|
+
case "task_send_file":
|
|
464
|
+
return { ok: true, data: { messageId: await controller.controlSendTaskFile(request.localSessionId, request.filePath) } };
|
|
299
465
|
case "task_new_group":
|
|
300
466
|
return {
|
|
301
467
|
ok: true,
|
|
@@ -327,6 +493,10 @@ async function shutdown(
|
|
|
327
493
|
): Promise<void> {
|
|
328
494
|
if (shuttingDown) return;
|
|
329
495
|
shuttingDown = true;
|
|
496
|
+
if (pendingSelfUpdatePlanPath) {
|
|
497
|
+
releaseSelfUpdatePlan(pendingSelfUpdatePlanPath);
|
|
498
|
+
pendingSelfUpdatePlanPath = undefined;
|
|
499
|
+
}
|
|
330
500
|
logger.info({ exitCode }, "Shutting down Agent Bot.");
|
|
331
501
|
safeRestart.cancel();
|
|
332
502
|
feishuConnector?.stop();
|