@keyou007/agent-bot 0.1.24-alpha.2 → 0.1.24-alpha.3
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 +87 -0
- package/README.md +27 -5
- package/README.zh.md +26 -5
- package/dist/acp/AcpJsonRpcConnection.js +3 -1
- package/dist/acp/AcpJsonRpcConnection.js.map +1 -1
- package/dist/cli/SelfUpdateRunner.js.map +1 -1
- package/dist/cli/SelfUpdater.js +2 -0
- package/dist/cli/SelfUpdater.js.map +1 -1
- package/dist/codex/AppServerConnection.js +3 -1
- package/dist/codex/AppServerConnection.js.map +1 -1
- package/dist/codex/CodexEventMapper.js +28 -1
- package/dist/codex/CodexEventMapper.js.map +1 -1
- package/dist/codex/CodexRuntime.js +92 -0
- package/dist/codex/CodexRuntime.js.map +1 -1
- package/dist/commands/CommandRouter.js +5 -0
- package/dist/commands/CommandRouter.js.map +1 -1
- package/dist/feishu/CardRenderer.js +45 -23
- package/dist/feishu/CardRenderer.js.map +1 -1
- package/dist/feishu/FeishuConnector.js +2 -0
- package/dist/feishu/FeishuConnector.js.map +1 -1
- package/dist/feishu/FeishuMessageClient.js +44 -0
- package/dist/feishu/FeishuMessageClient.js.map +1 -1
- package/dist/feishu/FeishuTurnPresenter.js +114 -37
- package/dist/feishu/FeishuTurnPresenter.js.map +1 -1
- package/dist/feishu/RecentMessageContext.js +70 -0
- package/dist/feishu/RecentMessageContext.js.map +1 -0
- package/dist/index.js +62 -24
- package/dist/index.js.map +1 -1
- package/dist/local-files/DiagramPreview.js +171 -0
- package/dist/local-files/DiagramPreview.js.map +1 -0
- package/dist/local-files/LocalFileViewerServer.js +141 -3
- package/dist/local-files/LocalFileViewerServer.js.map +1 -1
- package/dist/local-files/PreviewSyntaxHighlight.js +57 -0
- package/dist/local-files/PreviewSyntaxHighlight.js.map +1 -0
- package/dist/local-files/TurnPreviewPage.js +495 -60
- package/dist/local-files/TurnPreviewPage.js.map +1 -1
- package/dist/presentation/OutboundRouter.js +18 -9
- package/dist/presentation/OutboundRouter.js.map +1 -1
- package/dist/presentation/TurnStateReducer.js +155 -6
- package/dist/presentation/TurnStateReducer.js.map +1 -1
- package/dist/presentation/turnReasoning.js +23 -0
- package/dist/presentation/turnReasoning.js.map +1 -0
- package/dist/proxy/ProxySessionController.js +460 -34
- package/dist/proxy/ProxySessionController.js.map +1 -1
- package/dist/state/StateStore.js +43 -2
- package/dist/state/StateStore.js.map +1 -1
- package/dist/state/TurnPreviewJournal.js +469 -0
- package/dist/state/TurnPreviewJournal.js.map +1 -0
- package/dist/state/migrations.js +10 -0
- package/dist/state/migrations.js.map +1 -1
- package/dist/updates/AutomaticUpdatePreparer.js +16 -7
- package/dist/updates/AutomaticUpdatePreparer.js.map +1 -1
- package/dist/updates/ManualUpdateController.js +102 -0
- package/dist/updates/ManualUpdateController.js.map +1 -0
- package/dist/updates/PublishedRelease.js +42 -4
- package/dist/updates/PublishedRelease.js.map +1 -1
- package/dist/utils/ProtocolLineWarning.js +15 -0
- package/dist/utils/ProtocolLineWarning.js.map +1 -0
- package/docs/technical-reference.md +21 -1
- package/docs/technical-reference.zh.md +21 -1
- package/npm-shrinkwrap.json +1401 -10
- package/package.json +3 -1
- package/skills/agent-bot/SKILL.md +8 -1
- package/src/acp/AcpJsonRpcConnection.ts +3 -1
- package/src/cli/SelfUpdateRunner.ts +1 -0
- package/src/cli/SelfUpdater.ts +2 -0
- package/src/codex/AppServerConnection.ts +3 -1
- package/src/codex/CodexEventMapper.ts +29 -1
- package/src/codex/CodexRuntime.ts +87 -0
- package/src/commands/CommandRouter.ts +4 -0
- package/src/commands/commandTypes.ts +1 -0
- package/src/feishu/CardRenderer.ts +43 -21
- package/src/feishu/FeishuConnector.ts +2 -0
- package/src/feishu/FeishuMessageClient.ts +44 -0
- package/src/feishu/FeishuTurnPresenter.ts +107 -31
- package/src/feishu/RecentMessageContext.ts +90 -0
- package/src/feishu/types.ts +5 -0
- package/src/index.ts +62 -22
- package/src/local-files/DiagramPreview.ts +173 -0
- package/src/local-files/LocalFileViewerServer.ts +129 -2
- package/src/local-files/PreviewSyntaxHighlight.ts +58 -0
- package/src/local-files/TurnPreviewPage.ts +535 -57
- package/src/presentation/OutboundRouter.ts +25 -7
- package/src/presentation/TurnStateReducer.ts +151 -7
- package/src/presentation/turnReasoning.ts +21 -0
- package/src/presentation/turnViewTypes.ts +19 -1
- package/src/proxy/ProxySessionController.ts +452 -24
- package/src/runtime/types.ts +21 -0
- package/src/state/StateStore.ts +45 -2
- package/src/state/TurnPreviewJournal.ts +398 -0
- package/src/state/migrations.ts +10 -0
- package/src/updates/AutomaticUpdatePreparer.ts +21 -7
- package/src/updates/ManualUpdateController.ts +116 -0
- package/src/updates/PublishedRelease.ts +49 -3
- package/src/utils/ProtocolLineWarning.ts +13 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import type { Logger } from "pino";
|
|
3
|
+
import { truncateText } from "../utils/markdown.js";
|
|
4
|
+
import { compareSemver } from "../cli/SelfUpdater.js";
|
|
5
|
+
import { CardRenderer, type TaskListCardAction } from "../feishu/CardRenderer.js";
|
|
6
|
+
import type { CardAction, MessageReplyTarget } from "../feishu/types.js";
|
|
7
|
+
import type { OutboundRouter } from "../presentation/OutboundRouter.js";
|
|
8
|
+
import type { StateStore } from "../state/StateStore.js";
|
|
9
|
+
import { publishedVersionSchema, type PublishedRelease } from "./PublishedRelease.js";
|
|
10
|
+
|
|
11
|
+
interface ManualUpdateOptions {
|
|
12
|
+
store: StateStore;
|
|
13
|
+
outbound: Pick<OutboundRouter, "sendText" | "sendInteractiveCard" | "updateInteractiveCard">;
|
|
14
|
+
logger: Pick<Logger, "warn">;
|
|
15
|
+
currentVersion: string;
|
|
16
|
+
ownerOpenId(): string | undefined;
|
|
17
|
+
readReleases(): Promise<PublishedRelease[]>;
|
|
18
|
+
checkSupport(): Promise<void>;
|
|
19
|
+
hasPendingUpdate(): boolean;
|
|
20
|
+
applyUpdate(version: string, contextKey: string, replyTarget?: MessageReplyTarget): Promise<void>;
|
|
21
|
+
now?(): number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class ManualUpdateController {
|
|
25
|
+
private readonly renderer = new CardRenderer();
|
|
26
|
+
private selecting = false;
|
|
27
|
+
|
|
28
|
+
constructor(private readonly options: ManualUpdateOptions) {}
|
|
29
|
+
|
|
30
|
+
async show(contextKey: string, userId?: string): Promise<void> {
|
|
31
|
+
await this.options.outbound.sendText(contextKey, "正在检查正式版和 Alpha 版更新,请稍后。");
|
|
32
|
+
const [releases, support] = await Promise.all([
|
|
33
|
+
this.options.readReleases(),
|
|
34
|
+
this.options.checkSupport().then(() => undefined, (error: unknown) => String(error instanceof Error ? error.message : error)),
|
|
35
|
+
]);
|
|
36
|
+
const owner = this.options.ownerOpenId();
|
|
37
|
+
const notice = support
|
|
38
|
+
? `当前安装方式不支持在线更新:${truncateText(support, 600)}`
|
|
39
|
+
: !owner || userId !== owner
|
|
40
|
+
? "仅配置的机器人所有者可以执行更新。"
|
|
41
|
+
: this.selecting || this.options.hasPendingUpdate()
|
|
42
|
+
? "已有更新或重启正在进行,请稍后再检查。"
|
|
43
|
+
: undefined;
|
|
44
|
+
const now = this.options.now?.() ?? Date.now();
|
|
45
|
+
const bindings: Array<{ token: string; value: Record<string, string> }> = [];
|
|
46
|
+
const actions: TaskListCardAction[] = [];
|
|
47
|
+
const sections = [{ title: "当前版本", lines: [`${this.options.currentVersion}`] }];
|
|
48
|
+
for (const release of releases) {
|
|
49
|
+
const label = release.channel === "latest" ? "正式版" : "Alpha";
|
|
50
|
+
if (!release.version) {
|
|
51
|
+
sections.push({ title: label, lines: [`查询失败:${truncateText(release.error ?? "暂时无法获取版本", 300)}`] });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const version = publishedVersionSchema.parse(release.version);
|
|
55
|
+
const comparison = compareSemver(version, this.options.currentVersion);
|
|
56
|
+
sections.push({ title: `${label} ${version}`, lines: [
|
|
57
|
+
comparison > 0 ? "有新版本" : comparison === 0 ? "当前已安装" : "早于当前版本,不提供降级",
|
|
58
|
+
truncateText(release.notes ?? "暂无更新简介。", 2000),
|
|
59
|
+
`[完整更新日志](https://github.com/keyou/agent-bot/releases/tag/v${version})`,
|
|
60
|
+
] });
|
|
61
|
+
if (comparison > 0 && !notice) {
|
|
62
|
+
const token = randomUUID();
|
|
63
|
+
bindings.push({ token, value: { action: "agentbot_update_select", token, version, contextKey, owner: owner!, createdAt: String(now) } });
|
|
64
|
+
actions.push({ text: `Update ${version}`, value: { action: "agentbot_update_select", token, contextKey } });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const summary = notice ?? (actions.length > 0
|
|
68
|
+
? "选择一个版本开始更新;等待任务与结果投递完成后重启,失败时尝试回滚。"
|
|
69
|
+
: releases.some((release) => !release.version)
|
|
70
|
+
? "部分版本查询失败,请稍后重新发送 /update。"
|
|
71
|
+
: "暂无高于当前版本的可用更新。");
|
|
72
|
+
sections.push({ title: "说明", lines: [summary] });
|
|
73
|
+
const messageId = await this.options.outbound.sendInteractiveCard(contextKey,
|
|
74
|
+
this.renderer.renderSectionsCard("Agent Bot 更新", sections, actions));
|
|
75
|
+
if (messageId && bindings.length) this.options.store.upsertCardActionBindings(messageId, bindings);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async select(action: CardAction, replyTarget?: MessageReplyTarget): Promise<void> {
|
|
79
|
+
const token = typeof action.value.token === "string" ? action.value.token : "";
|
|
80
|
+
const binding = action.messageId && token ? this.options.store.getCardActionBinding(action.messageId, token) : undefined;
|
|
81
|
+
const now = this.options.now?.() ?? Date.now();
|
|
82
|
+
const createdAt = Number(binding?.createdAt);
|
|
83
|
+
if (!binding || binding.action !== "agentbot_update_select" || binding.contextKey !== action.contextKey
|
|
84
|
+
|| !Number.isFinite(createdAt) || now - createdAt > 30 * 60_000
|
|
85
|
+
|| now < createdAt) {
|
|
86
|
+
throw new Error("更新卡片已失效,请重新发送 /update。");
|
|
87
|
+
}
|
|
88
|
+
if (!action.userId || action.userId !== binding.owner || action.userId !== this.options.ownerOpenId()) {
|
|
89
|
+
throw new Error("仅配置的机器人所有者可以执行更新。");
|
|
90
|
+
}
|
|
91
|
+
const version = publishedVersionSchema.parse(binding.version);
|
|
92
|
+
if (compareSemver(version, this.options.currentVersion) <= 0) throw new Error("此版本无需更新,请重新发送 /update。");
|
|
93
|
+
if (this.selecting || this.options.hasPendingUpdate()) throw new Error("已有更新或重启正在进行,请勿重复操作。");
|
|
94
|
+
this.selecting = true;
|
|
95
|
+
try {
|
|
96
|
+
this.options.store.retainCardActionBindings(action.messageId!, []);
|
|
97
|
+
await this.options.outbound.sendText(action.contextKey, `正在下载并校验 Agent Bot ${version},请稍后;完成后将安排安全更新。`);
|
|
98
|
+
await this.options.applyUpdate(version, action.contextKey, replyTarget);
|
|
99
|
+
await this.showResult(action, version, "更新已就绪,等待任务与结果投递完成后自动安装并重启。");
|
|
100
|
+
} catch (error) {
|
|
101
|
+
await this.showResult(action, version, "更新未完成,当前服务继续运行。请重新发送 /update 后重试。");
|
|
102
|
+
throw error;
|
|
103
|
+
} finally {
|
|
104
|
+
this.selecting = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
private async showResult(action: CardAction, version: string, status: string): Promise<void> {
|
|
109
|
+
try {
|
|
110
|
+
await this.options.outbound.updateInteractiveCard(action.contextKey, action.messageId!,
|
|
111
|
+
this.renderer.renderSectionsCard("Agent Bot 更新", [{ title: version, lines: [status] }]));
|
|
112
|
+
} catch (error) {
|
|
113
|
+
this.options.logger.warn({ error, version }, "Failed to refresh the manual update card.");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { AGENT_BOT_PACKAGE_NAME } from "../cli/SelfUpdater.js";
|
|
3
4
|
|
|
@@ -8,8 +9,39 @@ export async function readLatestStableVersion(): Promise<string> {
|
|
|
8
9
|
return z.object({ version: stableVersionSchema }).parse(JSON.parse(text)).version;
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
export const publishedVersionSchema = z.string().regex(/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-alpha\.(0|[1-9]\d*))?$/u);
|
|
13
|
+
|
|
14
|
+
export interface PublishedRelease {
|
|
15
|
+
channel: "latest" | "alpha";
|
|
16
|
+
version?: string;
|
|
17
|
+
notes?: string;
|
|
18
|
+
error?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function readPublishedReleases(): Promise<PublishedRelease[]> {
|
|
22
|
+
return Promise.all((["latest", "alpha"] as const).map(async (channel): Promise<PublishedRelease> => {
|
|
23
|
+
let version: string;
|
|
24
|
+
try {
|
|
25
|
+
const text = await fetchText(`https://registry.npmjs.org/${encodeURIComponent(AGENT_BOT_PACKAGE_NAME)}/${channel}`, 256_000);
|
|
26
|
+
version = z.object({ version: channel === "latest" ? stableVersionSchema : publishedVersionSchema }).parse(JSON.parse(text)).version;
|
|
27
|
+
if (channel === "alpha" && !version.includes("-alpha.")) throw new Error("The alpha tag does not point to an Alpha release.");
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return { channel, error: error instanceof Error ? error.message : String(error) };
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
return { channel, version, notes: await readReleaseNotes(version) };
|
|
33
|
+
} catch {
|
|
34
|
+
return { channel, version, notes: `更新简介暂时无法读取。[查看更新日志](https://github.com/keyou/agent-bot/releases/tag/v${version})` };
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
11
39
|
export async function readReleaseNotes(version: string): Promise<string> {
|
|
12
|
-
|
|
40
|
+
publishedVersionSchema.parse(version);
|
|
41
|
+
// Published tags are immutable; bundled translations can cover older releases.
|
|
42
|
+
const bundled = await readFile(new URL("../../CHANGELOG.md", import.meta.url), "utf8").catch(() => "");
|
|
43
|
+
const chinese = chineseReleaseNotes(releaseNotesSection(bundled, version) ?? "");
|
|
44
|
+
if (chinese) return boundReleaseNotes(chinese, version);
|
|
13
45
|
const changelog = await fetchText(
|
|
14
46
|
`https://raw.githubusercontent.com/keyou/agent-bot/v${version}/CHANGELOG.md`, 2_000_000,
|
|
15
47
|
);
|
|
@@ -17,10 +49,24 @@ export async function readReleaseNotes(version: string): Promise<string> {
|
|
|
17
49
|
}
|
|
18
50
|
|
|
19
51
|
export function extractReleaseNotes(changelog: string, version: string): string {
|
|
52
|
+
const notes = releaseNotesSection(changelog, version);
|
|
53
|
+
if (!notes) throw new Error(`Release notes are unavailable for Agent Bot ${version}.`);
|
|
54
|
+
return boundReleaseNotes(chineseReleaseNotes(notes) ?? notes, version);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function releaseNotesSection(changelog: string, version: string): string | undefined {
|
|
20
58
|
const sections = changelog.split(/^## /mu).slice(1);
|
|
21
59
|
const section = sections.find((value) => value.split(/\r?\n/u, 1)[0]?.startsWith(`[${version}]`));
|
|
22
|
-
|
|
23
|
-
|
|
60
|
+
return section?.includes("\n") ? section.slice(section.indexOf("\n") + 1).trim() : undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function chineseReleaseNotes(notes: string): string | undefined {
|
|
64
|
+
const sections = notes.split(/^###\s+/mu).slice(1);
|
|
65
|
+
const section = sections.find((value) => /^(?:中文|简体中文|Chinese|zh-CN)\s*$/iu.test(value.split(/\r?\n/u, 1)[0] ?? ""));
|
|
66
|
+
return section?.includes("\n") ? section.slice(section.indexOf("\n") + 1).trim() || undefined : undefined;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function boundReleaseNotes(notes: string, version: string): string {
|
|
24
70
|
return notes.length > 4_000 ? `${notes.slice(0, 3_900)}\n\n[完整更新日志](https://github.com/keyou/agent-bot/releases/tag/v${version})` : notes;
|
|
25
71
|
}
|
|
26
72
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Logger } from "pino";
|
|
2
|
+
|
|
3
|
+
/** Invalid protocol output is diagnostic metadata, not a second full execution log. */
|
|
4
|
+
export class ProtocolLineWarning {
|
|
5
|
+
private nextWarningAt = 0;
|
|
6
|
+
private suppressed = 0;
|
|
7
|
+
warn(logger: Logger, line: string, message: string, now = Date.now()): void {
|
|
8
|
+
if (now < this.nextWarningAt) { this.suppressed++; return; }
|
|
9
|
+
logger.warn({ line: line.slice(0, 2048), characters: line.length, suppressed: this.suppressed }, message);
|
|
10
|
+
this.suppressed = 0;
|
|
11
|
+
this.nextWarningAt = now + 10_000;
|
|
12
|
+
}
|
|
13
|
+
}
|