@xiaohhhh1/canvas-agent 0.2.2 → 0.3.0
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/README.md +9 -1
- package/agent-instructions.md +21 -0
- package/dist/agent/claude.d.ts +3 -0
- package/dist/agent/claude.js +46 -0
- package/dist/agent/codex-client.d.ts +73 -0
- package/dist/agent/codex-client.js +438 -0
- package/dist/agent/codex-history.d.ts +25 -0
- package/dist/agent/codex-history.js +405 -0
- package/dist/agent/codex-protocol.d.ts +208 -0
- package/dist/{agents.d.ts → agent/codex.d.ts} +34 -31
- package/dist/agent/codex.js +210 -0
- package/dist/agent/types.d.ts +14 -0
- package/dist/agent/types.js +1 -0
- package/dist/canvas/operations.d.ts +13 -0
- package/dist/canvas/operations.js +161 -0
- package/dist/{schemas.d.ts → canvas/schemas.d.ts} +21 -20
- package/dist/{schemas.js → canvas/schemas.js} +1 -0
- package/dist/{canvas-session.d.ts → canvas/session.d.ts} +21 -1
- package/dist/canvas/session.js +256 -0
- package/dist/{tools.d.ts → canvas/tools.d.ts} +13 -8
- package/dist/{tools.js → canvas/tools.js} +5 -0
- package/dist/{types.d.ts → canvas/types.d.ts} +1 -10
- package/dist/canvas/types.js +1 -0
- package/dist/config.d.ts +5 -1
- package/dist/config.js +22 -4
- package/dist/index.js +2 -2
- package/dist/server/http.d.ts +2 -0
- package/dist/{http-server.js → server/http.js} +100 -16
- package/dist/server/mcp.d.ts +2 -0
- package/dist/{mcp-server.js → server/mcp.js} +5 -2
- package/dist/utils/date.d.ts +2 -0
- package/dist/utils/date.js +7 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.js +83 -0
- package/dist/utils/value.d.ts +5 -0
- package/dist/utils/value.js +8 -0
- package/package.json +7 -4
- package/dist/agents.js +0 -557
- package/dist/canvas-session.js +0 -391
- package/dist/http-server.d.ts +0 -1
- package/dist/mcp-server.d.ts +0 -1
- /package/dist/{types.js → agent/codex-protocol.js} +0 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { field } from "../utils/value.js";
|
|
2
|
+
/** 将 Codex 线程转换为列表展示所需的摘要。 */
|
|
3
|
+
export function summarizeCodexThread(thread) {
|
|
4
|
+
return {
|
|
5
|
+
id: String(field(thread, "id") || ""),
|
|
6
|
+
sessionId: String(field(thread, "sessionId") || ""),
|
|
7
|
+
preview: displayUserText(String(field(thread, "preview") || "")),
|
|
8
|
+
name: stringOrNull(field(thread, "name")),
|
|
9
|
+
cwd: String(field(thread, "cwd") || ""),
|
|
10
|
+
status: String(field(thread, "status") || ""),
|
|
11
|
+
source: field(thread, "source"),
|
|
12
|
+
threadSource: field(thread, "threadSource"),
|
|
13
|
+
createdAt: Number(field(thread, "createdAt") || 0),
|
|
14
|
+
updatedAt: Number(field(thread, "updatedAt") || 0),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/** 将 Codex turn items 转换为网页聊天历史。 */
|
|
18
|
+
export function threadMessages(thread, planUpdates = []) {
|
|
19
|
+
const turns = arrayValue(field(thread, "turns"));
|
|
20
|
+
const plansByTurn = new Map(planUpdates.map((item) => [item.turnId, item]));
|
|
21
|
+
const messages = [];
|
|
22
|
+
turns.forEach((turn, turnIndex) => {
|
|
23
|
+
const turnId = String(field(turn, "id") || turnIndex);
|
|
24
|
+
const turnError = String(field(field(turn, "error"), "message") || "").trim();
|
|
25
|
+
const planMessage = structuredPlanMessage(plansByTurn.get(turnId) || { threadId: "", turnId, explanation: stringOrNull(field(turn, "explanation")), plan: arrayValue(field(turn, "plan")), turnStatus: String(field(turn, "status") || "") });
|
|
26
|
+
let planAdded = false;
|
|
27
|
+
arrayValue(field(turn, "items")).forEach((item, itemIndex) => {
|
|
28
|
+
const type = String(field(item, "type") || "");
|
|
29
|
+
const id = String(field(item, "id") || `${turnIndex}-${itemIndex}`);
|
|
30
|
+
if (type === "userMessage") {
|
|
31
|
+
const text = displayUserText(userInputText(field(item, "content")));
|
|
32
|
+
if (text)
|
|
33
|
+
messages.push({ id, role: "user", text });
|
|
34
|
+
if (planMessage && !planAdded) {
|
|
35
|
+
messages.push(planMessage);
|
|
36
|
+
planAdded = true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (type === "agentMessage") {
|
|
40
|
+
const text = String(field(item, "text") || "").trim();
|
|
41
|
+
if (text)
|
|
42
|
+
messages.push({ id, role: "assistant", title: "Codex", text });
|
|
43
|
+
}
|
|
44
|
+
if (type === "mcpToolCall") {
|
|
45
|
+
const tool = String(field(item, "tool") || "工具调用");
|
|
46
|
+
const error = String(field(field(item, "error"), "message") || "");
|
|
47
|
+
const input = toolArguments(field(item, "arguments"));
|
|
48
|
+
messages.push({ id, role: "tool", title: toolName(tool), text: error || toolHistorySummary(tool, item, input), detail: toolHistoryDetail(tool, item, input, error) });
|
|
49
|
+
}
|
|
50
|
+
if (type === "commandExecution") {
|
|
51
|
+
const command = String(field(item, "command") || "").trim();
|
|
52
|
+
if (command)
|
|
53
|
+
messages.push({ id, role: "tool", title: "执行命令", text: command, detail: commandDetail(item) });
|
|
54
|
+
}
|
|
55
|
+
if (type === "fileChange") {
|
|
56
|
+
const changes = arrayValue(field(item, "changes"));
|
|
57
|
+
messages.push({ id, role: "tool", title: "修改文件", text: fileChangeSummary(changes), detail: { kind: "file", status: field(item, "status"), files: changes.map((change) => ({ path: String(field(change, "path") || "未知文件"), action: changeKind(field(change, "kind")) })) } });
|
|
58
|
+
}
|
|
59
|
+
if (type === "reasoning") {
|
|
60
|
+
const text = readableText(field(item, "summary"));
|
|
61
|
+
if (text)
|
|
62
|
+
messages.push({ id, role: "tool", title: "思考摘要", text, detail: { kind: "reasoning", status: "completed" } });
|
|
63
|
+
}
|
|
64
|
+
if (type === "plan") {
|
|
65
|
+
const text = String(field(item, "text") || "").trim();
|
|
66
|
+
if (text)
|
|
67
|
+
messages.push({ id, role: "tool", title: "执行计划", text, detail: { kind: "plan", status: "completed" } });
|
|
68
|
+
}
|
|
69
|
+
if (type === "webSearch")
|
|
70
|
+
messages.push({ id, role: "tool", title: "搜索资料", text: webSearchSummary(item), detail: { kind: "search", status: "completed", rows: webSearchRows(item) } });
|
|
71
|
+
if (type === "imageView")
|
|
72
|
+
messages.push({ id, role: "tool", title: "查看图片", text: String(field(item, "path") || "已查看图片"), detail: { kind: "image", status: "completed" } });
|
|
73
|
+
if (type === "imageGeneration")
|
|
74
|
+
messages.push({ id, role: "tool", title: "内置生图", text: String(field(item, "savedPath") || "图片生成完成"), detail: { kind: "image", status: field(item, "status"), savedPath: field(item, "savedPath") } });
|
|
75
|
+
if (type === "contextCompaction")
|
|
76
|
+
messages.push({ id, role: "tool", title: "整理上下文", text: "已整理当前对话,继续处理任务", detail: { kind: "context", status: "completed" } });
|
|
77
|
+
if (type === "dynamicToolCall") {
|
|
78
|
+
const tool = String(field(item, "tool") || "");
|
|
79
|
+
const title = toolName(tool);
|
|
80
|
+
const error = String(field(field(item, "error"), "message") || "");
|
|
81
|
+
const status = String(field(item, "status") || "");
|
|
82
|
+
const failed = Boolean(error) || field(item, "success") === false || status === "failed" || status === "error";
|
|
83
|
+
messages.push({ id, role: "tool", title, text: error || readableText(field(item, "contentItems")) || `${title}${failed ? "失败" : "完成"}`, detail: { kind: "tool", status: failed ? "failed" : status } });
|
|
84
|
+
}
|
|
85
|
+
if (type === "collabToolCall")
|
|
86
|
+
messages.push({ id, role: "tool", title: "协作处理", text: "已完成协作任务", detail: { kind: "tool", status: field(item, "status") } });
|
|
87
|
+
});
|
|
88
|
+
if (planMessage && !planAdded)
|
|
89
|
+
messages.push(planMessage);
|
|
90
|
+
if (turnError) {
|
|
91
|
+
const error = userFacingCodexError(turnError);
|
|
92
|
+
messages.push({ id: `error-${turnId}`, role: "error", title: error.title, text: error.text });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return messages.filter((item) => item.text).slice(-120);
|
|
96
|
+
}
|
|
97
|
+
/** 将结构化任务计划转换为聊天进度卡片。 */
|
|
98
|
+
function structuredPlanMessage(update) {
|
|
99
|
+
const tasks = arrayValue(update.plan).flatMap((item) => {
|
|
100
|
+
const step = String(field(item, "step") || "").trim();
|
|
101
|
+
return step ? [{ step, status: String(field(item, "status") || "pending") }] : [];
|
|
102
|
+
});
|
|
103
|
+
if (!tasks.length)
|
|
104
|
+
return null;
|
|
105
|
+
const completed = tasks.filter((item) => item.status === "completed").length;
|
|
106
|
+
return {
|
|
107
|
+
id: `plan-${update.turnId}`,
|
|
108
|
+
role: "tool",
|
|
109
|
+
title: "任务进度",
|
|
110
|
+
text: `已完成 ${completed}/${tasks.length} 项`,
|
|
111
|
+
detail: { kind: "todo", status: planStatus(tasks, update.turnStatus), tasks, explanation: update.explanation || "" },
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
/** 根据步骤和 turn 状态生成任务卡片状态。 */
|
|
115
|
+
function planStatus(tasks, turnStatus) {
|
|
116
|
+
if (turnStatus === "failed")
|
|
117
|
+
return "failed";
|
|
118
|
+
if (turnStatus === "interrupted")
|
|
119
|
+
return "interrupted";
|
|
120
|
+
if (tasks.every((item) => item.status === "completed"))
|
|
121
|
+
return "completed";
|
|
122
|
+
return turnStatus === "completed" ? "finished" : "inProgress";
|
|
123
|
+
}
|
|
124
|
+
/** 将常见 Codex 错误转换为普通用户可理解的提示。 */
|
|
125
|
+
function userFacingCodexError(message) {
|
|
126
|
+
if (/selected model is at capacity/i.test(message))
|
|
127
|
+
return { title: "模型暂时繁忙", text: "当前选择的模型请求量过大,暂时无法处理。请稍后重试,或切换其他模型后再试。" };
|
|
128
|
+
return { title: "任务失败", text: message || "Codex 未能完成本次任务,请稍后重试。" };
|
|
129
|
+
}
|
|
130
|
+
/** 提取用户输入条目中的文本与附件占位信息。 */
|
|
131
|
+
function userInputText(content) {
|
|
132
|
+
return arrayValue(content)
|
|
133
|
+
.map((item) => {
|
|
134
|
+
const type = String(field(item, "type") || "");
|
|
135
|
+
if (type === "text")
|
|
136
|
+
return String(field(item, "text") || "");
|
|
137
|
+
if (type === "image" || type === "localImage")
|
|
138
|
+
return "图片附件";
|
|
139
|
+
if (type === "mention")
|
|
140
|
+
return `@${String(field(item, "name") || "文件")}`;
|
|
141
|
+
return "";
|
|
142
|
+
})
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.join("\n");
|
|
145
|
+
}
|
|
146
|
+
/** 移除用户消息中由旧流程拼接的 Agent 前置提示词。 */
|
|
147
|
+
function displayUserText(text) {
|
|
148
|
+
const value = text.trim();
|
|
149
|
+
const marker = "用户请求:";
|
|
150
|
+
const index = value.lastIndexOf(marker);
|
|
151
|
+
const prompt = index >= 0 ? value.slice(index + marker.length) : value;
|
|
152
|
+
return prompt.split("\n\n本轮可用图片附件(顺序与图片输入一致):", 1)[0].trim();
|
|
153
|
+
}
|
|
154
|
+
/** 将未知值转换为数组。 */
|
|
155
|
+
function arrayValue(value) {
|
|
156
|
+
return Array.isArray(value) ? value : [];
|
|
157
|
+
}
|
|
158
|
+
/** 将非空字符串保留为字符串,否则返回 null。 */
|
|
159
|
+
function stringOrNull(value) {
|
|
160
|
+
return typeof value === "string" && value.trim() ? value : null;
|
|
161
|
+
}
|
|
162
|
+
/** 生成命令执行的用户可读详情。 */
|
|
163
|
+
function commandDetail(item) {
|
|
164
|
+
const rows = [
|
|
165
|
+
textRow("工作目录", field(item, "cwd")),
|
|
166
|
+
textRow("退出状态", field(item, "exitCode")),
|
|
167
|
+
durationRow(field(item, "durationMs")),
|
|
168
|
+
].filter(Boolean);
|
|
169
|
+
return { kind: "command", status: field(item, "status"), rows, output: String(field(item, "aggregatedOutput") || "").trim() };
|
|
170
|
+
}
|
|
171
|
+
/** 生成 MCP 工具的用户可读详情。 */
|
|
172
|
+
function toolHistoryDetail(tool, item, input, error) {
|
|
173
|
+
return { kind: "tool", status: error ? "failed" : field(item, "status"), rows: toolInputRows(tool, input), ...(error ? { output: error } : {}) };
|
|
174
|
+
}
|
|
175
|
+
/** 生成 MCP 工具在对话中的结果摘要。 */
|
|
176
|
+
function toolHistorySummary(tool, item, input) {
|
|
177
|
+
if (tool === "site_navigate")
|
|
178
|
+
return `已打开${routeName(String(field(input, "path") || "/"))}`;
|
|
179
|
+
if (tool === "canvas_list_projects")
|
|
180
|
+
return "已读取画布列表";
|
|
181
|
+
if (tool === "canvas_get_state") {
|
|
182
|
+
const result = parseToolResult(field(item, "result"));
|
|
183
|
+
const nodes = arrayValue(field(result, "nodes"));
|
|
184
|
+
const connections = arrayValue(field(result, "connections"));
|
|
185
|
+
return nodes.length || connections.length || result ? canvasContentSummary(nodes, connections.length) : "已读取当前画布内容";
|
|
186
|
+
}
|
|
187
|
+
if (tool === "canvas_get_selection")
|
|
188
|
+
return "已读取当前选中内容";
|
|
189
|
+
if (tool === "prompts_search")
|
|
190
|
+
return `已搜索提示词“${String(field(input, "query") || "") || "全部"}”`;
|
|
191
|
+
if (tool === "assets_list")
|
|
192
|
+
return "已读取我的素材";
|
|
193
|
+
if (tool === "generation_get_status")
|
|
194
|
+
return "已检查生成任务状态";
|
|
195
|
+
return `${toolName(tool)}已完成`;
|
|
196
|
+
}
|
|
197
|
+
/** 按节点类型生成人类可读的画布内容概览。 */
|
|
198
|
+
function canvasContentSummary(nodes, connections) {
|
|
199
|
+
const counts = nodes.reduce((result, node) => {
|
|
200
|
+
const type = String(field(node, "type") || "other");
|
|
201
|
+
result[type] = (result[type] || 0) + 1;
|
|
202
|
+
return result;
|
|
203
|
+
}, {});
|
|
204
|
+
const known = new Set(["text", "image", "config", "video", "audio", "group"]);
|
|
205
|
+
const other = Object.entries(counts).reduce((total, [type, count]) => total + (known.has(type) ? 0 : count), 0);
|
|
206
|
+
const parts = [
|
|
207
|
+
counts.text ? `${counts.text} 个文本` : "",
|
|
208
|
+
counts.image ? `${counts.image} 张图片` : "",
|
|
209
|
+
counts.config ? `${counts.config} 个配置` : "",
|
|
210
|
+
counts.video ? `${counts.video} 个视频` : "",
|
|
211
|
+
counts.audio ? `${counts.audio} 个音频` : "",
|
|
212
|
+
counts.group ? `${counts.group} 个分组` : "",
|
|
213
|
+
other ? `${other} 个其他节点` : "",
|
|
214
|
+
connections ? `${connections} 条连线` : "",
|
|
215
|
+
].filter(Boolean);
|
|
216
|
+
return parts.length ? parts.join("、") : "当前画布为空";
|
|
217
|
+
}
|
|
218
|
+
/** 从 MCP 历史结果中还原工具返回的数据。 */
|
|
219
|
+
function parseToolResult(result) {
|
|
220
|
+
const content = field(result, "content");
|
|
221
|
+
const text = arrayValue(content)
|
|
222
|
+
.map((item) => field(item, "text"))
|
|
223
|
+
.filter((item) => typeof item === "string")
|
|
224
|
+
.join("\n");
|
|
225
|
+
try {
|
|
226
|
+
return text ? JSON.parse(text) : result;
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return text || result;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
/** 提取工具参数中适合普通用户查看的信息。 */
|
|
233
|
+
function toolInputRows(tool, input) {
|
|
234
|
+
if (tool === "site_navigate")
|
|
235
|
+
return [textRow("目标页面", routeName(String(field(input, "path") || "/")))].filter(Boolean);
|
|
236
|
+
if (tool === "prompts_search")
|
|
237
|
+
return [textRow("搜索内容", field(input, "query"))].filter(Boolean);
|
|
238
|
+
if (tool === "canvas_create_text_node")
|
|
239
|
+
return [textRow("文本内容", field(input, "text"))].filter(Boolean);
|
|
240
|
+
if (tool === "canvas_apply_ops")
|
|
241
|
+
return [textRow("操作数量", arrayValue(field(input, "ops")).length)].filter(Boolean);
|
|
242
|
+
return [];
|
|
243
|
+
}
|
|
244
|
+
/** 生成人类可读的文件变更摘要。 */
|
|
245
|
+
function fileChangeSummary(changes) {
|
|
246
|
+
if (!changes.length)
|
|
247
|
+
return "已完成文件修改";
|
|
248
|
+
const names = changes.slice(0, 3).map((change) => String(field(change, "path") || "未知文件"));
|
|
249
|
+
if (changes.length === 1)
|
|
250
|
+
return `${changeKind(field(changes[0], "kind"))}${names[0]}`;
|
|
251
|
+
return `涉及 ${changes.length} 个文件:${names.join("、")}${changes.length > names.length ? " 等" : ""}`;
|
|
252
|
+
}
|
|
253
|
+
/** 生成网页搜索摘要。 */
|
|
254
|
+
function webSearchSummary(item) {
|
|
255
|
+
const action = field(item, "action");
|
|
256
|
+
const type = String(field(action, "type") || "");
|
|
257
|
+
if (type === "openPage")
|
|
258
|
+
return `打开网页:${String(field(action, "url") || "")}`;
|
|
259
|
+
if (type === "findInPage")
|
|
260
|
+
return `在网页中查找“${String(field(action, "pattern") || "内容")}”`;
|
|
261
|
+
return `搜索:${String(field(item, "query") || field(action, "query") || "相关资料")}`;
|
|
262
|
+
}
|
|
263
|
+
/** 生成网页搜索详情行。 */
|
|
264
|
+
function webSearchRows(item) {
|
|
265
|
+
const action = field(item, "action");
|
|
266
|
+
return [textRow("关键词", field(item, "query") || field(action, "query")), textRow("网页", field(action, "url"))].filter(Boolean);
|
|
267
|
+
}
|
|
268
|
+
/** 从 reasoning 结构中提取可读文本。 */
|
|
269
|
+
function readableText(value) {
|
|
270
|
+
if (typeof value === "string")
|
|
271
|
+
return value.trim();
|
|
272
|
+
if (Array.isArray(value))
|
|
273
|
+
return value.map(readableText).filter(Boolean).join("\n");
|
|
274
|
+
return readableText(field(value, "text"));
|
|
275
|
+
}
|
|
276
|
+
/** 将历史工具参数解析为对象。 */
|
|
277
|
+
function toolArguments(value) {
|
|
278
|
+
if (typeof value !== "string")
|
|
279
|
+
return value;
|
|
280
|
+
try {
|
|
281
|
+
return JSON.parse(value);
|
|
282
|
+
}
|
|
283
|
+
catch {
|
|
284
|
+
return {};
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** 创建非空详情行。 */
|
|
288
|
+
function textRow(label, value) {
|
|
289
|
+
return value === undefined || value === null || value === "" ? null : { label, value: String(value) };
|
|
290
|
+
}
|
|
291
|
+
/** 创建命令耗时详情行。 */
|
|
292
|
+
function durationRow(value) {
|
|
293
|
+
const duration = Number(value || 0);
|
|
294
|
+
return duration > 0 ? { label: "耗时", value: `${(duration / 1000).toFixed(1)} 秒` } : null;
|
|
295
|
+
}
|
|
296
|
+
/** 将文件变更类型转换为中文。 */
|
|
297
|
+
function changeKind(value) {
|
|
298
|
+
if (value === "add")
|
|
299
|
+
return "新增";
|
|
300
|
+
if (value === "delete")
|
|
301
|
+
return "删除";
|
|
302
|
+
return "修改";
|
|
303
|
+
}
|
|
304
|
+
/** 将站点路由转换为中文页面名称。 */
|
|
305
|
+
function routeName(path) {
|
|
306
|
+
if (path === "/")
|
|
307
|
+
return "首页";
|
|
308
|
+
if (path === "/canvas")
|
|
309
|
+
return "画布页面";
|
|
310
|
+
if (path.startsWith("/canvas/"))
|
|
311
|
+
return "指定画布";
|
|
312
|
+
if (path.startsWith("/image"))
|
|
313
|
+
return "生图工作台";
|
|
314
|
+
if (path.startsWith("/video"))
|
|
315
|
+
return "视频工作台";
|
|
316
|
+
if (path.startsWith("/prompts"))
|
|
317
|
+
return "提示词中心";
|
|
318
|
+
if (path.startsWith("/assets"))
|
|
319
|
+
return "我的素材";
|
|
320
|
+
if (path.startsWith("/config"))
|
|
321
|
+
return "配置页面";
|
|
322
|
+
return path;
|
|
323
|
+
}
|
|
324
|
+
/** 将 MCP 工具名称转换为聊天记录中的中文标题。 */
|
|
325
|
+
function toolName(name) {
|
|
326
|
+
if (name === "imagegen" || name.endsWith("__imagegen"))
|
|
327
|
+
return "生成图片";
|
|
328
|
+
if (name === "view_image" || name.endsWith("__view_image"))
|
|
329
|
+
return "查看图片";
|
|
330
|
+
if (name === "exec" || name === "exec_command" || name.endsWith("__exec_command"))
|
|
331
|
+
return "执行命令";
|
|
332
|
+
if (name === "apply_patch" || name.endsWith("__apply_patch"))
|
|
333
|
+
return "修改文件";
|
|
334
|
+
if (name === "web__run" || name.endsWith("__web__run"))
|
|
335
|
+
return "搜索资料";
|
|
336
|
+
if (name === "site_navigate")
|
|
337
|
+
return "打开页面";
|
|
338
|
+
if (name === "canvas_list_projects")
|
|
339
|
+
return "查看画布列表";
|
|
340
|
+
if (name === "canvas_apply_ops")
|
|
341
|
+
return "画布操作";
|
|
342
|
+
if (name === "canvas_get_state")
|
|
343
|
+
return "读取画布";
|
|
344
|
+
if (name === "canvas_get_selection")
|
|
345
|
+
return "读取选区";
|
|
346
|
+
if (name === "canvas_export_snapshot")
|
|
347
|
+
return "导出快照";
|
|
348
|
+
if (name === "canvas_create_node")
|
|
349
|
+
return "创建节点";
|
|
350
|
+
if (name === "canvas_create_attachment_nodes")
|
|
351
|
+
return "添加附件图片";
|
|
352
|
+
if (name === "canvas_create_text_node")
|
|
353
|
+
return "创建文本";
|
|
354
|
+
if (name === "canvas_create_text_nodes")
|
|
355
|
+
return "批量创建文本";
|
|
356
|
+
if (name === "canvas_create_config_node")
|
|
357
|
+
return "创建生成配置";
|
|
358
|
+
if (name === "canvas_create_image_prompt_flow")
|
|
359
|
+
return "创建生图流程";
|
|
360
|
+
if (name === "canvas_create_generation_flow")
|
|
361
|
+
return "创建生成流程";
|
|
362
|
+
if (name === "canvas_generate_text")
|
|
363
|
+
return "生成文本";
|
|
364
|
+
if (name === "canvas_generate_image")
|
|
365
|
+
return "生成图片";
|
|
366
|
+
if (name === "canvas_generate_video")
|
|
367
|
+
return "生成视频";
|
|
368
|
+
if (name === "canvas_generate_audio")
|
|
369
|
+
return "生成音频";
|
|
370
|
+
if (name === "canvas_update_node")
|
|
371
|
+
return "更新节点";
|
|
372
|
+
if (name === "canvas_update_node_text")
|
|
373
|
+
return "更新文本";
|
|
374
|
+
if (name === "canvas_move_nodes")
|
|
375
|
+
return "移动节点";
|
|
376
|
+
if (name === "canvas_resize_node")
|
|
377
|
+
return "调整节点尺寸";
|
|
378
|
+
if (name === "canvas_delete_nodes")
|
|
379
|
+
return "删除节点";
|
|
380
|
+
if (name === "canvas_connect_nodes")
|
|
381
|
+
return "连接节点";
|
|
382
|
+
if (name === "canvas_select_nodes")
|
|
383
|
+
return "选择节点";
|
|
384
|
+
if (name === "canvas_set_viewport")
|
|
385
|
+
return "调整视口";
|
|
386
|
+
if (name === "canvas_run_generation")
|
|
387
|
+
return "触发生成";
|
|
388
|
+
if (name === "workbench_image_get_config")
|
|
389
|
+
return "读取生图设置";
|
|
390
|
+
if (name === "workbench_image_generate")
|
|
391
|
+
return "在生图工作台生成";
|
|
392
|
+
if (name === "workbench_video_get_config")
|
|
393
|
+
return "读取视频设置";
|
|
394
|
+
if (name === "workbench_video_generate")
|
|
395
|
+
return "在视频工作台生成";
|
|
396
|
+
if (name === "prompts_search")
|
|
397
|
+
return "搜索提示词";
|
|
398
|
+
if (name === "assets_list")
|
|
399
|
+
return "查看我的素材";
|
|
400
|
+
if (name === "assets_add")
|
|
401
|
+
return "添加到我的素材";
|
|
402
|
+
if (name === "generation_get_status")
|
|
403
|
+
return "查看生成状态";
|
|
404
|
+
return name ? `调用工具:${name}` : "工具操作";
|
|
405
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type { JsonRecord } from "../utils/value.js";
|
|
2
|
+
export type CodexThread = JsonRecord & {
|
|
3
|
+
id: string;
|
|
4
|
+
cwd: string;
|
|
5
|
+
turns?: CodexTurn[];
|
|
6
|
+
};
|
|
7
|
+
export type CodexTurn = JsonRecord & {
|
|
8
|
+
id: string;
|
|
9
|
+
error?: CodexTurnError | null;
|
|
10
|
+
durationMs?: number | null;
|
|
11
|
+
};
|
|
12
|
+
export type CodexTurnError = JsonRecord & {
|
|
13
|
+
message: string;
|
|
14
|
+
};
|
|
15
|
+
export type CodexItem = JsonRecord & {
|
|
16
|
+
id: string;
|
|
17
|
+
type: string;
|
|
18
|
+
text?: string;
|
|
19
|
+
};
|
|
20
|
+
export type CodexPlanStep = {
|
|
21
|
+
step: string;
|
|
22
|
+
status: "pending" | "inProgress" | "completed";
|
|
23
|
+
};
|
|
24
|
+
export type CodexPlanUpdate = {
|
|
25
|
+
threadId: string;
|
|
26
|
+
turnId: string;
|
|
27
|
+
explanation?: string | null;
|
|
28
|
+
plan: CodexPlanStep[];
|
|
29
|
+
turnStatus?: string;
|
|
30
|
+
};
|
|
31
|
+
export type CodexTurnInput = {
|
|
32
|
+
type: "text";
|
|
33
|
+
text: string;
|
|
34
|
+
text_elements: [];
|
|
35
|
+
} | {
|
|
36
|
+
type: "localImage";
|
|
37
|
+
path: string;
|
|
38
|
+
};
|
|
39
|
+
type ThreadOptions = {
|
|
40
|
+
approvalPolicy: "never" | "on-request";
|
|
41
|
+
sandbox: "workspace-write" | "danger-full-access";
|
|
42
|
+
config: JsonRecord;
|
|
43
|
+
cwd?: string;
|
|
44
|
+
};
|
|
45
|
+
type CodexRequestSpec = {
|
|
46
|
+
initialize: {
|
|
47
|
+
params: {
|
|
48
|
+
clientInfo: {
|
|
49
|
+
name: string;
|
|
50
|
+
title: string;
|
|
51
|
+
version: string;
|
|
52
|
+
};
|
|
53
|
+
capabilities: {
|
|
54
|
+
experimentalApi: boolean;
|
|
55
|
+
requestAttestation: boolean;
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
result: JsonRecord;
|
|
59
|
+
};
|
|
60
|
+
"thread/start": {
|
|
61
|
+
params: ThreadOptions & {
|
|
62
|
+
threadSource: "user";
|
|
63
|
+
};
|
|
64
|
+
result: {
|
|
65
|
+
thread: CodexThread;
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
"thread/resume": {
|
|
69
|
+
params: ThreadOptions & {
|
|
70
|
+
threadId: string;
|
|
71
|
+
};
|
|
72
|
+
result: {
|
|
73
|
+
thread: CodexThread;
|
|
74
|
+
};
|
|
75
|
+
};
|
|
76
|
+
"thread/list": {
|
|
77
|
+
params: {
|
|
78
|
+
limit: number;
|
|
79
|
+
sortKey: "updated_at";
|
|
80
|
+
sortDirection: "desc";
|
|
81
|
+
sourceKinds: Array<"cli" | "vscode" | "appServer" | "exec">;
|
|
82
|
+
cwd: string;
|
|
83
|
+
searchTerm?: string;
|
|
84
|
+
};
|
|
85
|
+
result: {
|
|
86
|
+
data: CodexThread[];
|
|
87
|
+
nextCursor: string | null;
|
|
88
|
+
backwardsCursor: string | null;
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
"thread/read": {
|
|
92
|
+
params: {
|
|
93
|
+
threadId: string;
|
|
94
|
+
includeTurns: boolean;
|
|
95
|
+
};
|
|
96
|
+
result: {
|
|
97
|
+
thread: CodexThread;
|
|
98
|
+
};
|
|
99
|
+
};
|
|
100
|
+
"thread/archive": {
|
|
101
|
+
params: {
|
|
102
|
+
threadId: string;
|
|
103
|
+
};
|
|
104
|
+
result: Record<string, never>;
|
|
105
|
+
};
|
|
106
|
+
"turn/start": {
|
|
107
|
+
params: {
|
|
108
|
+
threadId: string;
|
|
109
|
+
input: CodexTurnInput[];
|
|
110
|
+
approvalPolicy: "never" | "on-request";
|
|
111
|
+
sandboxPolicy: {
|
|
112
|
+
type: "workspaceWrite";
|
|
113
|
+
networkAccess: boolean;
|
|
114
|
+
} | {
|
|
115
|
+
type: "dangerFullAccess";
|
|
116
|
+
};
|
|
117
|
+
};
|
|
118
|
+
result: {
|
|
119
|
+
turn: CodexTurn;
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
"turn/interrupt": {
|
|
123
|
+
params: {
|
|
124
|
+
threadId: string;
|
|
125
|
+
turnId: string;
|
|
126
|
+
};
|
|
127
|
+
result: Record<string, never>;
|
|
128
|
+
};
|
|
129
|
+
};
|
|
130
|
+
export type CodexRequestMethod = keyof CodexRequestSpec;
|
|
131
|
+
export type CodexRequestParams<Method extends CodexRequestMethod> = CodexRequestSpec[Method]["params"];
|
|
132
|
+
export type CodexRequestResult<Method extends CodexRequestMethod> = CodexRequestSpec[Method]["result"];
|
|
133
|
+
type TokenUsageBreakdown = {
|
|
134
|
+
inputTokens: number;
|
|
135
|
+
cachedInputTokens: number;
|
|
136
|
+
outputTokens: number;
|
|
137
|
+
reasoningOutputTokens: number;
|
|
138
|
+
};
|
|
139
|
+
type CodexNotificationSpec = {
|
|
140
|
+
"thread/started": {
|
|
141
|
+
thread: CodexThread;
|
|
142
|
+
};
|
|
143
|
+
"turn/started": {
|
|
144
|
+
threadId?: string;
|
|
145
|
+
turn: CodexTurn;
|
|
146
|
+
};
|
|
147
|
+
"turn/completed": {
|
|
148
|
+
threadId?: string;
|
|
149
|
+
turn: CodexTurn;
|
|
150
|
+
};
|
|
151
|
+
"turn/plan/updated": {
|
|
152
|
+
threadId?: string;
|
|
153
|
+
turnId: string;
|
|
154
|
+
explanation?: string | null;
|
|
155
|
+
plan: CodexPlanStep[];
|
|
156
|
+
};
|
|
157
|
+
"item/started": {
|
|
158
|
+
threadId: string;
|
|
159
|
+
turnId: string;
|
|
160
|
+
item: CodexItem;
|
|
161
|
+
};
|
|
162
|
+
"item/completed": {
|
|
163
|
+
threadId: string;
|
|
164
|
+
turnId: string;
|
|
165
|
+
item: CodexItem;
|
|
166
|
+
};
|
|
167
|
+
"item/agentMessage/delta": {
|
|
168
|
+
threadId: string;
|
|
169
|
+
turnId: string;
|
|
170
|
+
itemId: string;
|
|
171
|
+
delta: string;
|
|
172
|
+
};
|
|
173
|
+
"item/plan/delta": {
|
|
174
|
+
threadId: string;
|
|
175
|
+
turnId: string;
|
|
176
|
+
itemId: string;
|
|
177
|
+
delta: string;
|
|
178
|
+
};
|
|
179
|
+
"item/reasoning/summaryTextDelta": {
|
|
180
|
+
threadId: string;
|
|
181
|
+
turnId: string;
|
|
182
|
+
itemId: string;
|
|
183
|
+
delta: string;
|
|
184
|
+
summaryIndex: number;
|
|
185
|
+
};
|
|
186
|
+
"item/commandExecution/outputDelta": {
|
|
187
|
+
threadId: string;
|
|
188
|
+
turnId: string;
|
|
189
|
+
itemId: string;
|
|
190
|
+
delta: string;
|
|
191
|
+
};
|
|
192
|
+
"thread/tokenUsage/updated": {
|
|
193
|
+
threadId: string;
|
|
194
|
+
turnId: string;
|
|
195
|
+
tokenUsage: {
|
|
196
|
+
last: TokenUsageBreakdown;
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
error: {
|
|
200
|
+
threadId: string;
|
|
201
|
+
turnId: string;
|
|
202
|
+
error: CodexTurnError;
|
|
203
|
+
willRetry: boolean;
|
|
204
|
+
};
|
|
205
|
+
};
|
|
206
|
+
export type CodexNotificationMethod = keyof CodexNotificationSpec;
|
|
207
|
+
export type CodexNotificationParams<Method extends CodexNotificationMethod> = CodexNotificationSpec[Method];
|
|
208
|
+
export {};
|