@tencent-ai/cloud-agent-sdk 0.2.5 → 0.2.6-next.8dd93c4.20260127
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/dist/index.cjs +2072 -1119
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -194
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +90 -194
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2080 -1127
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.6-next.8dd93c4.20260127.tgz +0 -0
- package/package.json +4 -3
- package/dist/MockAgentProvider-4e4oOusg.cjs +0 -3
- package/dist/MockAgentProvider-D-basTXz.cjs +0 -3219
- package/dist/MockAgentProvider-D-basTXz.cjs.map +0 -1
- package/dist/MockAgentProvider-TNsV559x.mjs +0 -3202
- package/dist/MockAgentProvider-TNsV559x.mjs.map +0 -1
- package/dist/MockAgentProvider-tNdtAJCv.mjs +0 -3
- package/dist/tencent-ai-cloud-agent-sdk-0.2.5.tgz +0 -0
package/dist/index.cjs
CHANGED
|
@@ -1,10 +1,633 @@
|
|
|
1
|
-
require(
|
|
1
|
+
let zod = require("zod");
|
|
2
2
|
let _agentclientprotocol_sdk = require("@agentclientprotocol/sdk");
|
|
3
3
|
require("@connectrpc/connect");
|
|
4
4
|
require("@connectrpc/connect-web");
|
|
5
5
|
require("@bufbuild/protobuf");
|
|
6
6
|
let e2b = require("e2b");
|
|
7
7
|
|
|
8
|
+
//#region ../agent-provider/src/common/_legacy/tool-schemas.ts
|
|
9
|
+
/**
|
|
10
|
+
* ACP Tool Input/Output Schema 定义
|
|
11
|
+
*
|
|
12
|
+
* 用于约束 ACP 协议中 ToolCallUpdate 的 rawInput 和 rawOutput 字段
|
|
13
|
+
* 基于 getCraftToolProvider 使用的工具定义
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* 工具输入 Schema 定义
|
|
17
|
+
* 用于验证和约束 rawInput
|
|
18
|
+
*/
|
|
19
|
+
const ToolInputSchemas = {
|
|
20
|
+
list_dir: zod.z.object({
|
|
21
|
+
target_directory: zod.z.string().describe("要列出内容的目录路径"),
|
|
22
|
+
ignore_globs: zod.z.string().optional().describe("可选的 glob 模式数组,用于忽略特定文件")
|
|
23
|
+
}),
|
|
24
|
+
search_file: zod.z.object({
|
|
25
|
+
target_directory: zod.z.string().describe("搜索的目录绝对路径"),
|
|
26
|
+
pattern: zod.z.string().describe("文件模式(如 \"*.js\"),支持通配符"),
|
|
27
|
+
recursive: zod.z.boolean().describe("是否递归搜索子目录"),
|
|
28
|
+
caseSensitive: zod.z.boolean().describe("是否区分大小写")
|
|
29
|
+
}),
|
|
30
|
+
read_file: zod.z.object({
|
|
31
|
+
filePath: zod.z.string().describe("要读取的文件的绝对路径"),
|
|
32
|
+
offset: zod.z.number().optional().describe("开始读取的行号"),
|
|
33
|
+
limit: zod.z.number().optional().describe("要读取的行数")
|
|
34
|
+
}),
|
|
35
|
+
read_lints: zod.z.object({ paths: zod.z.string().optional().describe("要读取 lint 错误的文件或目录路径") }),
|
|
36
|
+
rag_search: zod.z.object({
|
|
37
|
+
queryString: zod.z.string().describe("用户的实际问题或搜索查询"),
|
|
38
|
+
knowledgeBaseNames: zod.z.string().describe("知识库名称,多个用逗号分隔")
|
|
39
|
+
}),
|
|
40
|
+
read_rules: zod.z.object({ ruleNames: zod.z.string().describe("要读取的规则关键词,用逗号分隔,格式:{ruleName}_{ruleId}") }),
|
|
41
|
+
mcp_get_tool_description: zod.z.object({ toolRequests: zod.z.string().describe("JSON 字符串,二维数组格式:[[\"server1\", \"tool1\"], [\"server2\", \"tool2\"]]") }),
|
|
42
|
+
mcp_call_tool: zod.z.object({
|
|
43
|
+
serverName: zod.z.string().describe("MCP 服务器名称"),
|
|
44
|
+
toolName: zod.z.string().describe("要调用的工具名称"),
|
|
45
|
+
arguments: zod.z.string().describe("目标 MCP 工具的参数,JSON 格式字符串"),
|
|
46
|
+
maxOutputLength: zod.z.number().optional().describe("控制工具输出的最大长度,默认 200000")
|
|
47
|
+
}),
|
|
48
|
+
fetch_mcp_resource: zod.z.object({
|
|
49
|
+
server: zod.z.string().describe("MCP 服务器标识符"),
|
|
50
|
+
uri: zod.z.string().describe("要读取的资源 URI"),
|
|
51
|
+
arguments: zod.z.record(zod.z.unknown()).optional().describe("资源模板的参数"),
|
|
52
|
+
downloadPath: zod.z.string().optional().describe("可选的绝对路径,用于保存资源到磁盘")
|
|
53
|
+
}),
|
|
54
|
+
create_rule: zod.z.object({
|
|
55
|
+
ruleScope: zod.z.string().describe("规则范围,project rule 或 user rule"),
|
|
56
|
+
ruleName: zod.z.string().describe("规则文件名,不带扩展名"),
|
|
57
|
+
ruleType: zod.z.string().describe("规则类型,always、manual 或 requested"),
|
|
58
|
+
ruleContent: zod.z.string().describe("规则内容,使用 Markdown 格式"),
|
|
59
|
+
ruleDescription: zod.z.string().optional().describe("规则描述,使用 Markdown 格式")
|
|
60
|
+
}),
|
|
61
|
+
update_memory: zod.z.object({
|
|
62
|
+
action: zod.z.enum([
|
|
63
|
+
"create",
|
|
64
|
+
"update",
|
|
65
|
+
"delete"
|
|
66
|
+
]).optional().describe("执行的操作"),
|
|
67
|
+
existing_knowledge_id: zod.z.string().optional().describe("更新或删除时必需,现有记忆的 ID"),
|
|
68
|
+
knowledge_to_store: zod.z.string().optional().describe("要存储的特定记忆"),
|
|
69
|
+
title: zod.z.string().optional().describe("记忆的标题")
|
|
70
|
+
}),
|
|
71
|
+
search_content: zod.z.object({
|
|
72
|
+
pattern: zod.z.string().describe("要搜索的关键字或正则表达式模式"),
|
|
73
|
+
directory: zod.z.string().describe("要搜索的目录的绝对路径"),
|
|
74
|
+
fileTypes: zod.z.string().optional().describe("可选的逗号分隔文件扩展名"),
|
|
75
|
+
contextBefore: zod.z.number().optional().describe("每个匹配前显示的行数"),
|
|
76
|
+
contextAfter: zod.z.number().optional().describe("每个匹配后显示的行数"),
|
|
77
|
+
contextAround: zod.z.number().optional().describe("每个匹配前后显示的行数"),
|
|
78
|
+
outputMode: zod.z.string().optional().describe("输出模式:content、files_with_matches 或 count"),
|
|
79
|
+
caseSensitive: zod.z.boolean().optional().describe("是否区分大小写")
|
|
80
|
+
}),
|
|
81
|
+
write_to_file: zod.z.object({
|
|
82
|
+
filePath: zod.z.string().describe("目标文件的绝对路径"),
|
|
83
|
+
content: zod.z.string().describe("要写入的内容")
|
|
84
|
+
}),
|
|
85
|
+
replace_in_file: zod.z.object({
|
|
86
|
+
filePath: zod.z.string().describe("要修改的文件的绝对路径"),
|
|
87
|
+
old_str: zod.z.string().describe("要替换的文本"),
|
|
88
|
+
new_str: zod.z.string().describe("替换后的文本")
|
|
89
|
+
}),
|
|
90
|
+
delete_file: zod.z.object({
|
|
91
|
+
target_file: zod.z.string().describe("要删除的文件的绝对路径"),
|
|
92
|
+
explanation: zod.z.string().optional().describe("为什么使用此工具的一句话解释")
|
|
93
|
+
}),
|
|
94
|
+
execute_command: zod.z.object({
|
|
95
|
+
command: zod.z.string().describe("要执行的 CLI 命令"),
|
|
96
|
+
requires_approval: zod.z.boolean().describe("命令是否需要用户批准")
|
|
97
|
+
}),
|
|
98
|
+
preview_url: zod.z.object({ url: zod.z.string().describe("要打开的完整、有效的 HTTP/HTTPS URL") }),
|
|
99
|
+
ask_followup_question: zod.z.object({ questions: zod.z.array(zod.z.object({
|
|
100
|
+
question: zod.z.string(),
|
|
101
|
+
header: zod.z.string().max(12),
|
|
102
|
+
options: zod.z.array(zod.z.object({
|
|
103
|
+
label: zod.z.string().max(50),
|
|
104
|
+
description: zod.z.string()
|
|
105
|
+
})).min(2).max(4),
|
|
106
|
+
multiSelect: zod.z.boolean().optional()
|
|
107
|
+
})).min(1).max(4) }),
|
|
108
|
+
invoke_integration: zod.z.object({}).passthrough(),
|
|
109
|
+
call_integration: zod.z.object({}).passthrough(),
|
|
110
|
+
search_integration_tool: zod.z.object({}).passthrough(),
|
|
111
|
+
supabase_get_logs: zod.z.object({}).passthrough(),
|
|
112
|
+
supabase_execute_sql: zod.z.object({}).passthrough(),
|
|
113
|
+
supabase_apply_migration: zod.z.object({}).passthrough(),
|
|
114
|
+
supabase_list_migration: zod.z.object({}).passthrough(),
|
|
115
|
+
supabase_list_tables: zod.z.object({}).passthrough(),
|
|
116
|
+
cloud_studio_fetch_log: zod.z.object({}).passthrough(),
|
|
117
|
+
cloud_studio_execute_command: zod.z.object({}).passthrough(),
|
|
118
|
+
cloud_studio_deploy_sandbox: zod.z.object({}).passthrough(),
|
|
119
|
+
component_get_prompt: zod.z.object({}).passthrough(),
|
|
120
|
+
web_fetch: zod.z.object({
|
|
121
|
+
url: zod.z.string().describe("要获取内容的 URL"),
|
|
122
|
+
fetchInfo: zod.z.string().describe("用户想要获取的信息描述")
|
|
123
|
+
}),
|
|
124
|
+
use_skill: zod.z.object({ command: zod.z.string().describe("技能名称(不含参数),如 \"pdf\" 或 \"xlsx\"") }),
|
|
125
|
+
web_search: zod.z.object({
|
|
126
|
+
explanation: zod.z.string().describe("为什么使用此工具的一句话解释"),
|
|
127
|
+
searchTerm: zod.z.string().describe("要在网络上搜索的搜索词")
|
|
128
|
+
}),
|
|
129
|
+
task: zod.z.object({
|
|
130
|
+
subagent_name: zod.z.string().describe("要调用的子代理名称"),
|
|
131
|
+
description: zod.z.string().describe("任务的简短描述(3-5 个词)"),
|
|
132
|
+
prompt: zod.z.string().describe("子代理要执行的任务"),
|
|
133
|
+
subagent_path: zod.z.string().optional().describe("子代理定义文件的路径")
|
|
134
|
+
}),
|
|
135
|
+
codebase_search: zod.z.object({
|
|
136
|
+
query: zod.z.string().describe("关于你想理解的内容的完整问题"),
|
|
137
|
+
path: zod.z.string().describe("限制搜索范围的目录路径前缀"),
|
|
138
|
+
limit: zod.z.number().max(100).optional().describe("返回的最大结果数,默认 10")
|
|
139
|
+
}),
|
|
140
|
+
lsp: zod.z.object({}).passthrough(),
|
|
141
|
+
spec_create: zod.z.object({
|
|
142
|
+
name: zod.z.string().describe("Plan 名称,用作稳定标识符/文件名"),
|
|
143
|
+
overview: zod.z.string().describe("用一两句话精确概括本次 plan 的主要内容"),
|
|
144
|
+
relative_history: zod.z.string().describe("准备阶段的上下文,包含用户需求、代码位置、额外上下文等")
|
|
145
|
+
}),
|
|
146
|
+
spec_update: zod.z.object({ status: zod.z.enum([
|
|
147
|
+
"prepare",
|
|
148
|
+
"ready",
|
|
149
|
+
"building",
|
|
150
|
+
"finished"
|
|
151
|
+
]).optional().describe("Plan 状态") })
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* 工具输出 Schema 定义
|
|
155
|
+
* 用于验证和约束 rawOutput
|
|
156
|
+
*/
|
|
157
|
+
const ToolOutputSchemas = {
|
|
158
|
+
list_dir: zod.z.object({
|
|
159
|
+
type: zod.z.literal("list_files_result"),
|
|
160
|
+
files: zod.z.array(zod.z.object({
|
|
161
|
+
filePath: zod.z.string(),
|
|
162
|
+
size: zod.z.string(),
|
|
163
|
+
modifyTime: zod.z.string()
|
|
164
|
+
})),
|
|
165
|
+
root: zod.z.string(),
|
|
166
|
+
listing: zod.z.string().optional()
|
|
167
|
+
}),
|
|
168
|
+
search_file: zod.z.object({
|
|
169
|
+
type: zod.z.literal("search_file_result"),
|
|
170
|
+
path: zod.z.string(),
|
|
171
|
+
pattern: zod.z.string(),
|
|
172
|
+
recursive: zod.z.boolean().optional(),
|
|
173
|
+
caseSensitive: zod.z.boolean().optional(),
|
|
174
|
+
results: zod.z.array(zod.z.object({
|
|
175
|
+
filePath: zod.z.string(),
|
|
176
|
+
size: zod.z.string(),
|
|
177
|
+
modifyTime: zod.z.string()
|
|
178
|
+
}))
|
|
179
|
+
}),
|
|
180
|
+
read_file: zod.z.object({
|
|
181
|
+
type: zod.z.literal("read_file_result"),
|
|
182
|
+
path: zod.z.string(),
|
|
183
|
+
content: zod.z.string(),
|
|
184
|
+
totalLineCount: zod.z.number(),
|
|
185
|
+
hasMore: zod.z.boolean(),
|
|
186
|
+
diagnostic: zod.z.string().optional(),
|
|
187
|
+
hint: zod.z.string().optional(),
|
|
188
|
+
image: zod.z.object({
|
|
189
|
+
data: zod.z.string(),
|
|
190
|
+
mimeType: zod.z.string()
|
|
191
|
+
}).optional()
|
|
192
|
+
}),
|
|
193
|
+
read_lints: zod.z.object({
|
|
194
|
+
type: zod.z.literal("read_lints_result"),
|
|
195
|
+
diagnostics: zod.z.array(zod.z.string()),
|
|
196
|
+
totalCount: zod.z.number().optional(),
|
|
197
|
+
hint: zod.z.string().optional(),
|
|
198
|
+
isTruncated: zod.z.boolean().optional()
|
|
199
|
+
}),
|
|
200
|
+
rag_search: zod.z.object({
|
|
201
|
+
type: zod.z.literal("knowledge_base_result"),
|
|
202
|
+
selectedKnowledgeBases: zod.z.string(),
|
|
203
|
+
queryInput: zod.z.string()
|
|
204
|
+
}),
|
|
205
|
+
read_rules: zod.z.object({
|
|
206
|
+
type: zod.z.literal("rule_match_result"),
|
|
207
|
+
ruleDescription: zod.z.string(),
|
|
208
|
+
filePaths: zod.z.array(zod.z.string())
|
|
209
|
+
}),
|
|
210
|
+
mcp_get_tool_description: zod.z.object({}).passthrough(),
|
|
211
|
+
mcp_call_tool: zod.z.object({
|
|
212
|
+
type: zod.z.literal("mcp_call_tool_result"),
|
|
213
|
+
serverName: zod.z.string(),
|
|
214
|
+
toolName: zod.z.string(),
|
|
215
|
+
data: zod.z.array(zod.z.union([
|
|
216
|
+
zod.z.object({
|
|
217
|
+
type: zod.z.literal("text"),
|
|
218
|
+
text: zod.z.string()
|
|
219
|
+
}),
|
|
220
|
+
zod.z.object({
|
|
221
|
+
type: zod.z.literal("image"),
|
|
222
|
+
data: zod.z.string(),
|
|
223
|
+
mimeType: zod.z.string()
|
|
224
|
+
}),
|
|
225
|
+
zod.z.object({
|
|
226
|
+
type: zod.z.literal("resource"),
|
|
227
|
+
resource: zod.z.object({
|
|
228
|
+
uri: zod.z.string(),
|
|
229
|
+
mimeType: zod.z.string().optional(),
|
|
230
|
+
text: zod.z.string().optional(),
|
|
231
|
+
blob: zod.z.string().optional()
|
|
232
|
+
})
|
|
233
|
+
})
|
|
234
|
+
])),
|
|
235
|
+
isError: zod.z.boolean().optional(),
|
|
236
|
+
error: zod.z.unknown().optional(),
|
|
237
|
+
hint: zod.z.string().optional()
|
|
238
|
+
}),
|
|
239
|
+
fetch_mcp_resource: zod.z.object({
|
|
240
|
+
type: zod.z.literal("fetch_mcp_resource_result"),
|
|
241
|
+
server: zod.z.string(),
|
|
242
|
+
uri: zod.z.string(),
|
|
243
|
+
content: zod.z.string(),
|
|
244
|
+
downloadPath: zod.z.string().optional()
|
|
245
|
+
}),
|
|
246
|
+
create_rule: zod.z.object({
|
|
247
|
+
type: zod.z.literal("rule_create_result"),
|
|
248
|
+
ruleName: zod.z.string(),
|
|
249
|
+
createState: zod.z.enum([
|
|
250
|
+
"success",
|
|
251
|
+
"invoke",
|
|
252
|
+
"cancelled"
|
|
253
|
+
]),
|
|
254
|
+
hint: zod.z.string().optional(),
|
|
255
|
+
filePath: zod.z.string().optional()
|
|
256
|
+
}),
|
|
257
|
+
update_memory: zod.z.object({
|
|
258
|
+
type: zod.z.literal("update_memory_result"),
|
|
259
|
+
success: zod.z.boolean(),
|
|
260
|
+
message: zod.z.string(),
|
|
261
|
+
action: zod.z.enum([
|
|
262
|
+
"create",
|
|
263
|
+
"update",
|
|
264
|
+
"delete"
|
|
265
|
+
]),
|
|
266
|
+
knowledge_id: zod.z.string().optional()
|
|
267
|
+
}),
|
|
268
|
+
search_content: zod.z.object({
|
|
269
|
+
type: zod.z.literal("search_content_result"),
|
|
270
|
+
directory: zod.z.string(),
|
|
271
|
+
pattern: zod.z.string(),
|
|
272
|
+
fileTypes: zod.z.string(),
|
|
273
|
+
matches: zod.z.array(zod.z.object({
|
|
274
|
+
filePath: zod.z.string(),
|
|
275
|
+
content: zod.z.string(),
|
|
276
|
+
startLine: zod.z.number(),
|
|
277
|
+
endLine: zod.z.number(),
|
|
278
|
+
size: zod.z.string(),
|
|
279
|
+
modifyTime: zod.z.string()
|
|
280
|
+
})),
|
|
281
|
+
totalCount: zod.z.number(),
|
|
282
|
+
hasMore: zod.z.boolean(),
|
|
283
|
+
offset: zod.z.number(),
|
|
284
|
+
limit: zod.z.number(),
|
|
285
|
+
contextBefore: zod.z.number(),
|
|
286
|
+
contextAfter: zod.z.number(),
|
|
287
|
+
contextAround: zod.z.number().optional(),
|
|
288
|
+
outputMode: zod.z.string(),
|
|
289
|
+
caseSensitive: zod.z.boolean(),
|
|
290
|
+
hint: zod.z.string().optional()
|
|
291
|
+
}),
|
|
292
|
+
write_to_file: zod.z.object({
|
|
293
|
+
type: zod.z.literal("write_to_file_result"),
|
|
294
|
+
path: zod.z.string(),
|
|
295
|
+
addLineCount: zod.z.number(),
|
|
296
|
+
removedLines: zod.z.number(),
|
|
297
|
+
addedChars: zod.z.number().optional(),
|
|
298
|
+
removedChars: zod.z.number().optional(),
|
|
299
|
+
bytesWritten: zod.z.number(),
|
|
300
|
+
isNewFile: zod.z.boolean(),
|
|
301
|
+
oldContent: zod.z.string().optional(),
|
|
302
|
+
diagnostic: zod.z.string().optional()
|
|
303
|
+
}),
|
|
304
|
+
replace_in_file: zod.z.object({
|
|
305
|
+
type: zod.z.literal("replace_in_file_result"),
|
|
306
|
+
path: zod.z.string(),
|
|
307
|
+
addLineCount: zod.z.number().optional(),
|
|
308
|
+
removedLines: zod.z.number().optional(),
|
|
309
|
+
addedChars: zod.z.number().optional(),
|
|
310
|
+
removedChars: zod.z.number().optional(),
|
|
311
|
+
matchCount: zod.z.number().optional(),
|
|
312
|
+
hint: zod.z.string().optional(),
|
|
313
|
+
diagnosticChange: zod.z.object({
|
|
314
|
+
added: zod.z.string(),
|
|
315
|
+
removed: zod.z.string(),
|
|
316
|
+
unchanged: zod.z.string()
|
|
317
|
+
}).optional()
|
|
318
|
+
}),
|
|
319
|
+
delete_file: zod.z.object({
|
|
320
|
+
type: zod.z.literal("delete_file_result"),
|
|
321
|
+
path: zod.z.string(),
|
|
322
|
+
recursive: zod.z.boolean(),
|
|
323
|
+
hint: zod.z.string().optional()
|
|
324
|
+
}),
|
|
325
|
+
execute_command: zod.z.object({
|
|
326
|
+
type: zod.z.literal("execute_command_result"),
|
|
327
|
+
stdout: zod.z.string(),
|
|
328
|
+
stderr: zod.z.string(),
|
|
329
|
+
exitCode: zod.z.number(),
|
|
330
|
+
hint: zod.z.string().optional(),
|
|
331
|
+
serviceInfo: zod.z.object({
|
|
332
|
+
isWatchCommand: zod.z.boolean(),
|
|
333
|
+
isServiceOutput: zod.z.boolean(),
|
|
334
|
+
serviceReady: zod.z.boolean(),
|
|
335
|
+
message: zod.z.string()
|
|
336
|
+
}).optional(),
|
|
337
|
+
use_standalone_terminal: zod.z.boolean().optional()
|
|
338
|
+
}),
|
|
339
|
+
preview_url: zod.z.object({
|
|
340
|
+
type: zod.z.literal("preview_tool_result"),
|
|
341
|
+
url: zod.z.string(),
|
|
342
|
+
message: zod.z.string()
|
|
343
|
+
}),
|
|
344
|
+
ask_followup_question: zod.z.object({
|
|
345
|
+
type: zod.z.literal("multi_question_result"),
|
|
346
|
+
questions: zod.z.array(zod.z.object({
|
|
347
|
+
id: zod.z.string(),
|
|
348
|
+
question: zod.z.string(),
|
|
349
|
+
options: zod.z.array(zod.z.string()),
|
|
350
|
+
multiSelect: zod.z.boolean().optional(),
|
|
351
|
+
title: zod.z.string().optional()
|
|
352
|
+
})),
|
|
353
|
+
answers: zod.z.record(zod.z.union([zod.z.string(), zod.z.array(zod.z.string())])),
|
|
354
|
+
message: zod.z.string()
|
|
355
|
+
}),
|
|
356
|
+
invoke_integration: zod.z.object({
|
|
357
|
+
type: zod.z.literal("invoke_integration_tool_result"),
|
|
358
|
+
recommend: zod.z.object({
|
|
359
|
+
id: zod.z.string(),
|
|
360
|
+
type: zod.z.string(),
|
|
361
|
+
status: zod.z.enum(["connected", "disconnected"])
|
|
362
|
+
}),
|
|
363
|
+
message: zod.z.string()
|
|
364
|
+
}),
|
|
365
|
+
call_integration: zod.z.object({
|
|
366
|
+
type: zod.z.literal("call_integration_tool_result"),
|
|
367
|
+
integrationId: zod.z.string(),
|
|
368
|
+
toolName: zod.z.string(),
|
|
369
|
+
data: zod.z.object({
|
|
370
|
+
type: zod.z.literal("text"),
|
|
371
|
+
text: zod.z.string()
|
|
372
|
+
}),
|
|
373
|
+
isError: zod.z.boolean().optional(),
|
|
374
|
+
error: zod.z.unknown().optional()
|
|
375
|
+
}),
|
|
376
|
+
search_integration_tool: zod.z.object({
|
|
377
|
+
type: zod.z.literal("search_integration_tool_result"),
|
|
378
|
+
data: zod.z.array(zod.z.object({
|
|
379
|
+
integrationId: zod.z.string(),
|
|
380
|
+
integrationName: zod.z.string(),
|
|
381
|
+
toolName: zod.z.string(),
|
|
382
|
+
description: zod.z.string(),
|
|
383
|
+
inputSchema: zod.z.record(zod.z.unknown())
|
|
384
|
+
})),
|
|
385
|
+
hint: zod.z.string().optional()
|
|
386
|
+
}),
|
|
387
|
+
supabase_get_logs: zod.z.object({
|
|
388
|
+
type: zod.z.enum([
|
|
389
|
+
"supabase_get_logs_result",
|
|
390
|
+
"supabase_execute_sql_result",
|
|
391
|
+
"supabase_apply_migration_result",
|
|
392
|
+
"supabase_list_migration_result",
|
|
393
|
+
"supabase_list_tables_result"
|
|
394
|
+
]),
|
|
395
|
+
message: zod.z.string()
|
|
396
|
+
}),
|
|
397
|
+
supabase_execute_sql: zod.z.object({
|
|
398
|
+
type: zod.z.enum([
|
|
399
|
+
"supabase_get_logs_result",
|
|
400
|
+
"supabase_execute_sql_result",
|
|
401
|
+
"supabase_apply_migration_result",
|
|
402
|
+
"supabase_list_migration_result",
|
|
403
|
+
"supabase_list_tables_result"
|
|
404
|
+
]),
|
|
405
|
+
message: zod.z.string()
|
|
406
|
+
}),
|
|
407
|
+
supabase_apply_migration: zod.z.object({
|
|
408
|
+
type: zod.z.enum([
|
|
409
|
+
"supabase_get_logs_result",
|
|
410
|
+
"supabase_execute_sql_result",
|
|
411
|
+
"supabase_apply_migration_result",
|
|
412
|
+
"supabase_list_migration_result",
|
|
413
|
+
"supabase_list_tables_result"
|
|
414
|
+
]),
|
|
415
|
+
message: zod.z.string()
|
|
416
|
+
}),
|
|
417
|
+
supabase_list_migration: zod.z.object({
|
|
418
|
+
type: zod.z.enum([
|
|
419
|
+
"supabase_get_logs_result",
|
|
420
|
+
"supabase_execute_sql_result",
|
|
421
|
+
"supabase_apply_migration_result",
|
|
422
|
+
"supabase_list_migration_result",
|
|
423
|
+
"supabase_list_tables_result"
|
|
424
|
+
]),
|
|
425
|
+
message: zod.z.string()
|
|
426
|
+
}),
|
|
427
|
+
supabase_list_tables: zod.z.object({
|
|
428
|
+
type: zod.z.enum([
|
|
429
|
+
"supabase_get_logs_result",
|
|
430
|
+
"supabase_execute_sql_result",
|
|
431
|
+
"supabase_apply_migration_result",
|
|
432
|
+
"supabase_list_migration_result",
|
|
433
|
+
"supabase_list_tables_result"
|
|
434
|
+
]),
|
|
435
|
+
message: zod.z.string()
|
|
436
|
+
}),
|
|
437
|
+
cloud_studio_fetch_log: zod.z.object({
|
|
438
|
+
type: zod.z.literal("cloud_studio_fetch_log_result"),
|
|
439
|
+
success: zod.z.boolean(),
|
|
440
|
+
logs: zod.z.record(zod.z.string())
|
|
441
|
+
}),
|
|
442
|
+
cloud_studio_execute_command: zod.z.object({
|
|
443
|
+
type: zod.z.literal("cloud_studio_execute_command_result"),
|
|
444
|
+
success: zod.z.boolean(),
|
|
445
|
+
message: zod.z.string()
|
|
446
|
+
}),
|
|
447
|
+
cloud_studio_deploy_sandbox: zod.z.object({
|
|
448
|
+
type: zod.z.literal("cloud_studio_integration_result"),
|
|
449
|
+
previewUrl: zod.z.string().optional(),
|
|
450
|
+
steps: zod.z.array(zod.z.object({
|
|
451
|
+
status: zod.z.enum([
|
|
452
|
+
"idle",
|
|
453
|
+
"success",
|
|
454
|
+
"running",
|
|
455
|
+
"error"
|
|
456
|
+
]),
|
|
457
|
+
name: zod.z.enum([
|
|
458
|
+
"createSandbox",
|
|
459
|
+
"uploadProject",
|
|
460
|
+
"installDependencies",
|
|
461
|
+
"startService",
|
|
462
|
+
"preview"
|
|
463
|
+
]),
|
|
464
|
+
error: zod.z.object({
|
|
465
|
+
code: zod.z.number().optional(),
|
|
466
|
+
message: zod.z.string().optional()
|
|
467
|
+
}).optional()
|
|
468
|
+
}))
|
|
469
|
+
}),
|
|
470
|
+
component_get_prompt: zod.z.object({
|
|
471
|
+
type: zod.z.literal("component_get_prompt_result"),
|
|
472
|
+
componentType: zod.z.string(),
|
|
473
|
+
webFramework: zod.z.string(),
|
|
474
|
+
data: zod.z.object({
|
|
475
|
+
type: zod.z.literal("text"),
|
|
476
|
+
text: zod.z.string()
|
|
477
|
+
})
|
|
478
|
+
}),
|
|
479
|
+
web_fetch: zod.z.object({
|
|
480
|
+
type: zod.z.literal("web_fetch_tool_result"),
|
|
481
|
+
message: zod.z.string(),
|
|
482
|
+
data: zod.z.string(),
|
|
483
|
+
loading: zod.z.string().optional(),
|
|
484
|
+
title: zod.z.string().optional(),
|
|
485
|
+
favicon: zod.z.string().optional()
|
|
486
|
+
}),
|
|
487
|
+
use_skill: zod.z.object({
|
|
488
|
+
type: zod.z.literal("use_skill_tool_result"),
|
|
489
|
+
commandMessage: zod.z.string(),
|
|
490
|
+
message: zod.z.string()
|
|
491
|
+
}),
|
|
492
|
+
web_search: zod.z.object({
|
|
493
|
+
type: zod.z.literal("web_search_tool_result"),
|
|
494
|
+
data: zod.z.array(zod.z.object({
|
|
495
|
+
passage: zod.z.string(),
|
|
496
|
+
uri: zod.z.string(),
|
|
497
|
+
site: zod.z.string(),
|
|
498
|
+
title: zod.z.string(),
|
|
499
|
+
snippets: zod.z.array(zod.z.string()).optional(),
|
|
500
|
+
content: zod.z.string().optional()
|
|
501
|
+
})),
|
|
502
|
+
searchInput: zod.z.string().optional()
|
|
503
|
+
}),
|
|
504
|
+
task: zod.z.object({
|
|
505
|
+
type: zod.z.literal("task_tool_result"),
|
|
506
|
+
toolInfo: zod.z.array(zod.z.object({
|
|
507
|
+
name: zod.z.string(),
|
|
508
|
+
info: zod.z.string(),
|
|
509
|
+
needApprove: zod.z.boolean().optional(),
|
|
510
|
+
toolCallId: zod.z.string().optional(),
|
|
511
|
+
executeStatus: zod.z.enum([
|
|
512
|
+
"ing",
|
|
513
|
+
"completed",
|
|
514
|
+
"cancel",
|
|
515
|
+
"fail"
|
|
516
|
+
]).optional()
|
|
517
|
+
})).optional(),
|
|
518
|
+
startCallTool: zod.z.boolean().optional(),
|
|
519
|
+
finalResult: zod.z.string().optional(),
|
|
520
|
+
toolCallBrief: zod.z.string().optional()
|
|
521
|
+
}),
|
|
522
|
+
codebase_search: zod.z.object({
|
|
523
|
+
type: zod.z.literal("codebase_search_result"),
|
|
524
|
+
query: zod.z.string(),
|
|
525
|
+
path: zod.z.string(),
|
|
526
|
+
content: zod.z.string()
|
|
527
|
+
}),
|
|
528
|
+
lsp: zod.z.object({
|
|
529
|
+
type: zod.z.literal("lsp_tool_result"),
|
|
530
|
+
operation: zod.z.string(),
|
|
531
|
+
result: zod.z.string(),
|
|
532
|
+
resultCount: zod.z.number().optional(),
|
|
533
|
+
fileCount: zod.z.number().optional(),
|
|
534
|
+
character: zod.z.number().optional()
|
|
535
|
+
}),
|
|
536
|
+
spec_create: zod.z.object({
|
|
537
|
+
type: zod.z.literal("plan_create_tool_result"),
|
|
538
|
+
message: zod.z.string(),
|
|
539
|
+
data: zod.z.string()
|
|
540
|
+
}),
|
|
541
|
+
spec_update: zod.z.object({
|
|
542
|
+
type: zod.z.literal("plan_update_tool_result"),
|
|
543
|
+
status: zod.z.enum([
|
|
544
|
+
"prepare",
|
|
545
|
+
"ready",
|
|
546
|
+
"building",
|
|
547
|
+
"finished"
|
|
548
|
+
]),
|
|
549
|
+
data: zod.z.string(),
|
|
550
|
+
reminder: zod.z.string()
|
|
551
|
+
})
|
|
552
|
+
};
|
|
553
|
+
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region ../agent-provider/src/common/_legacy/MockAgentProvider.ts
|
|
556
|
+
/**
|
|
557
|
+
* Mock 会话数据存储
|
|
558
|
+
* 每个会话都有对应的模拟历史消息
|
|
559
|
+
*/
|
|
560
|
+
const mockSessionHistories = /* @__PURE__ */ new Map();
|
|
561
|
+
mockSessionHistories.set("1", [
|
|
562
|
+
{
|
|
563
|
+
type: "user",
|
|
564
|
+
content: "帮我开发一个五子棋游戏,需要包含以下功能:\n1. 双人对战模式\n2. 悔棋功能\n3. 计时器",
|
|
565
|
+
timestamp: Date.now() - 18e5 - 6e4
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
type: "assistant",
|
|
569
|
+
content: "好的,我来帮你开发一个五子棋游戏。我会创建以下文件结构:\n\n- `index.html` - 主页面\n- `game.js` - 游戏逻辑\n- `style.css` - 样式文件\n\n让我开始创建这些文件...",
|
|
570
|
+
timestamp: Date.now() - 18e5 - 5e4
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
type: "assistant",
|
|
574
|
+
content: "我已经完成了所有文件的创建和修改。游戏支持双人对战、悔棋和计时功能。你可以直接在浏览器中打开 index.html 开始游戏。",
|
|
575
|
+
timestamp: Date.now() - 18e5
|
|
576
|
+
}
|
|
577
|
+
]);
|
|
578
|
+
mockSessionHistories.set("2", [
|
|
579
|
+
{
|
|
580
|
+
type: "user",
|
|
581
|
+
content: "登录页面的样式有问题,按钮颜色不对齐,帮我修复一下",
|
|
582
|
+
timestamp: Date.now() - 72e5 - 12e4
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
type: "assistant",
|
|
586
|
+
content: "我来检查登录页面的样式。让我先看看相关的 CSS 文件...",
|
|
587
|
+
timestamp: Date.now() - 72e5 - 1e5
|
|
588
|
+
},
|
|
589
|
+
{
|
|
590
|
+
type: "assistant",
|
|
591
|
+
content: "样式问题已修复,请查看效果。主要修改了按钮的 flex 布局和颜色变量。",
|
|
592
|
+
timestamp: Date.now() - 72e5
|
|
593
|
+
}
|
|
594
|
+
]);
|
|
595
|
+
mockSessionHistories.set("3", [
|
|
596
|
+
{
|
|
597
|
+
type: "user",
|
|
598
|
+
content: "API 接口响应太慢了,需要添加缓存和错误重试机制",
|
|
599
|
+
timestamp: Date.now() - 144e5 - 18e4
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
type: "assistant",
|
|
603
|
+
content: "好的,我来优化 API 接口。我会实现:\n1. Redis 缓存层\n2. 指数退避重试策略\n3. 请求去重",
|
|
604
|
+
timestamp: Date.now() - 144e5 - 15e4
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
type: "assistant",
|
|
608
|
+
content: "已添加缓存和错误重试机制。性能提升了约 60%。",
|
|
609
|
+
timestamp: Date.now() - 144e5
|
|
610
|
+
}
|
|
611
|
+
]);
|
|
612
|
+
mockSessionHistories.set("4", [
|
|
613
|
+
{
|
|
614
|
+
type: "user",
|
|
615
|
+
content: "帮我为核心模块添加单元测试,覆盖率要达到 80% 以上",
|
|
616
|
+
timestamp: Date.now() - 864e5 - 3e5
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
type: "assistant",
|
|
620
|
+
content: "好的,我会使用 Jest 来编写单元测试。让我先看看核心模块的代码结构...",
|
|
621
|
+
timestamp: Date.now() - 864e5 - 25e4
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
type: "assistant",
|
|
625
|
+
content: "测试覆盖率已达到 85%,超过了目标。主要测试了:\n- 用户认证逻辑\n- 数据验证\n- 错误处理",
|
|
626
|
+
timestamp: Date.now() - 864e5
|
|
627
|
+
}
|
|
628
|
+
]);
|
|
629
|
+
|
|
630
|
+
//#endregion
|
|
8
631
|
//#region ../agent-client-protocol/src/common/types.ts
|
|
9
632
|
/**
|
|
10
633
|
* Protocol Extension Types for Agent Client Protocol
|
|
@@ -1526,12 +2149,13 @@ var StreamableHttpClient = class {
|
|
|
1526
2149
|
* - Emit 'disconnected' event when connection becomes unhealthy so provider can clean up cache
|
|
1527
2150
|
*/
|
|
1528
2151
|
var CloudAgentConnection = class {
|
|
1529
|
-
constructor(agentId, config) {
|
|
2152
|
+
constructor(agentId, config, cwd = "/workspace") {
|
|
1530
2153
|
this.listeners = /* @__PURE__ */ new Map();
|
|
1531
2154
|
this.onceListeners = /* @__PURE__ */ new Map();
|
|
1532
2155
|
this._isStreaming = false;
|
|
1533
2156
|
this.transport = "cloud";
|
|
1534
2157
|
this.agentId = agentId;
|
|
2158
|
+
this.cwd = cwd;
|
|
1535
2159
|
this.client = new StreamableHttpClient({
|
|
1536
2160
|
endpoint: config.endpoint,
|
|
1537
2161
|
authToken: config.authToken,
|
|
@@ -1687,13 +2311,13 @@ var CloudAgentConnection = class {
|
|
|
1687
2311
|
}
|
|
1688
2312
|
async createSession(params) {
|
|
1689
2313
|
return {
|
|
1690
|
-
...await this.client.loadSession(this.agentId,
|
|
2314
|
+
...await this.client.loadSession(this.agentId, this.cwd),
|
|
1691
2315
|
sessionId: this.agentId
|
|
1692
2316
|
};
|
|
1693
2317
|
}
|
|
1694
2318
|
async loadSession(params) {
|
|
1695
2319
|
if (!params.sessionId) throw new Error("sessionId is required for loadSession");
|
|
1696
|
-
return this.client.loadSession(params.sessionId,
|
|
2320
|
+
return this.client.loadSession(params.sessionId, this.cwd);
|
|
1697
2321
|
}
|
|
1698
2322
|
async setSessionMode(sessionId, modeId) {
|
|
1699
2323
|
return this.client.setSessionMode({
|
|
@@ -1852,7 +2476,6 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1852
2476
|
*/
|
|
1853
2477
|
static async connect(info) {
|
|
1854
2478
|
return new E2BFilesystem(await e2b.Sandbox.connect(info.sandboxId, {
|
|
1855
|
-
apiKey: info.apiKey,
|
|
1856
2479
|
domain: info.domain,
|
|
1857
2480
|
apiUrl: info.apiUrl,
|
|
1858
2481
|
requestTimeoutMs: info.requestTimeoutMs,
|
|
@@ -1896,6 +2519,49 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1896
2519
|
}
|
|
1897
2520
|
};
|
|
1898
2521
|
|
|
2522
|
+
//#endregion
|
|
2523
|
+
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
2524
|
+
/**
|
|
2525
|
+
* 并发执行任务,返回包含成功/失败状态的结果
|
|
2526
|
+
*
|
|
2527
|
+
* @param tasks - 任务函数数组
|
|
2528
|
+
* @param concurrency - 最大并发数,默认 5
|
|
2529
|
+
* @returns 所有任务的结果数组,包含状态信息
|
|
2530
|
+
*
|
|
2531
|
+
* @example
|
|
2532
|
+
* ```typescript
|
|
2533
|
+
* const results = await runWithConcurrencySettled(tasks, 3);
|
|
2534
|
+
* const successes = results.filter(r => r.status === 'fulfilled');
|
|
2535
|
+
* const failures = results.filter(r => r.status === 'rejected');
|
|
2536
|
+
* ```
|
|
2537
|
+
*/
|
|
2538
|
+
async function runWithConcurrencySettled(tasks, concurrency = 5) {
|
|
2539
|
+
if (tasks.length === 0) return [];
|
|
2540
|
+
const limit = Math.max(1, concurrency);
|
|
2541
|
+
const results = new Array(tasks.length);
|
|
2542
|
+
let currentIndex = 0;
|
|
2543
|
+
async function runNext() {
|
|
2544
|
+
while (currentIndex < tasks.length) {
|
|
2545
|
+
const index = currentIndex++;
|
|
2546
|
+
const task = tasks[index];
|
|
2547
|
+
try {
|
|
2548
|
+
results[index] = {
|
|
2549
|
+
status: "fulfilled",
|
|
2550
|
+
value: await task()
|
|
2551
|
+
};
|
|
2552
|
+
} catch (reason) {
|
|
2553
|
+
results[index] = {
|
|
2554
|
+
status: "rejected",
|
|
2555
|
+
reason
|
|
2556
|
+
};
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
const workers = Array(Math.min(limit, tasks.length)).fill(null).map(() => runNext());
|
|
2561
|
+
await Promise.all(workers);
|
|
2562
|
+
return results;
|
|
2563
|
+
}
|
|
2564
|
+
|
|
1899
2565
|
//#endregion
|
|
1900
2566
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cos-upload-service.ts
|
|
1901
2567
|
/**
|
|
@@ -1906,6 +2572,7 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
1906
2572
|
* const service = new CosUploadService({
|
|
1907
2573
|
* request: (method, path, body) => cloudProvider.request(method, path, body),
|
|
1908
2574
|
* logger: console,
|
|
2575
|
+
* uploadConcurrency: 3,
|
|
1909
2576
|
* });
|
|
1910
2577
|
*
|
|
1911
2578
|
* const result = await service.uploadFile(file);
|
|
@@ -1919,6 +2586,7 @@ var CosUploadService = class {
|
|
|
1919
2586
|
this.request = options.request;
|
|
1920
2587
|
this.logger = options.logger;
|
|
1921
2588
|
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2589
|
+
this.uploadConcurrency = options.uploadConcurrency ?? 3;
|
|
1922
2590
|
}
|
|
1923
2591
|
/**
|
|
1924
2592
|
* 生成唯一的 objectKey
|
|
@@ -1929,13 +2597,13 @@ var CosUploadService = class {
|
|
|
1929
2597
|
return `uploads/${Date.now()}-${Math.random().toString(36).substring(2, 10)}-${encodeURIComponent(filename)}`;
|
|
1930
2598
|
}
|
|
1931
2599
|
/**
|
|
1932
|
-
*
|
|
2600
|
+
* 批量获取预签名 URL
|
|
1933
2601
|
*
|
|
1934
|
-
* POST /
|
|
2602
|
+
* POST /conversations/presigned_url
|
|
1935
2603
|
*/
|
|
1936
|
-
async
|
|
1937
|
-
const response = await this.request("POST", "/
|
|
1938
|
-
if (!response.ok) throw new Error(`Failed to get presigned
|
|
2604
|
+
async getPresignedUrls(objectKeys) {
|
|
2605
|
+
const response = await this.request("POST", "/conversations/presigned_url", { object_keys: objectKeys });
|
|
2606
|
+
if (!response.ok) throw new Error(`Failed to get presigned URLs: ${response.statusText}`);
|
|
1939
2607
|
const apiResponse = await response.json();
|
|
1940
2608
|
if (!apiResponse.data) throw new Error("No data in presigned URL response");
|
|
1941
2609
|
return apiResponse.data;
|
|
@@ -1944,22 +2612,17 @@ var CosUploadService = class {
|
|
|
1944
2612
|
* 上传单个文件到 COS
|
|
1945
2613
|
*
|
|
1946
2614
|
* @param file - 要上传的文件
|
|
1947
|
-
* @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
|
|
1948
2615
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
1949
2616
|
*/
|
|
1950
|
-
async uploadFile(file
|
|
2617
|
+
async uploadFile(file) {
|
|
1951
2618
|
const filename = file.name;
|
|
1952
2619
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
1953
2620
|
try {
|
|
1954
2621
|
const objectKey = this.generateObjectKey(filename);
|
|
1955
2622
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
1956
|
-
const
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
expire_seconds: expireSeconds
|
|
1960
|
-
});
|
|
1961
|
-
this.logger?.debug(`[CosUploadService] Got PUT presigned URL`);
|
|
1962
|
-
const uploadResponse = await this.fetchImpl(putPresigned.url, {
|
|
2623
|
+
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
2624
|
+
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
2625
|
+
const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
|
|
1963
2626
|
method: "PUT",
|
|
1964
2627
|
body: file,
|
|
1965
2628
|
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
@@ -1969,15 +2632,11 @@ var CosUploadService = class {
|
|
|
1969
2632
|
throw new Error(`COS upload failed: ${uploadResponse.status} ${errorText}`);
|
|
1970
2633
|
}
|
|
1971
2634
|
this.logger?.debug(`[CosUploadService] File uploaded to COS`);
|
|
1972
|
-
const getPresigned = await this.getPresignedUrl({
|
|
1973
|
-
object_key: objectKey,
|
|
1974
|
-
method: "GET",
|
|
1975
|
-
expire_seconds: expireSeconds
|
|
1976
|
-
});
|
|
1977
2635
|
this.logger?.info(`[CosUploadService] Upload success: ${filename}`);
|
|
1978
2636
|
return {
|
|
1979
2637
|
success: true,
|
|
1980
|
-
url:
|
|
2638
|
+
url: presignedItem.download_url,
|
|
2639
|
+
objectKey
|
|
1981
2640
|
};
|
|
1982
2641
|
} catch (error) {
|
|
1983
2642
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
@@ -1991,1260 +2650,1164 @@ var CosUploadService = class {
|
|
|
1991
2650
|
/**
|
|
1992
2651
|
* 批量上传文件到 COS
|
|
1993
2652
|
*
|
|
2653
|
+
* 使用并发控制,限制同时上传的文件数量
|
|
2654
|
+
*
|
|
1994
2655
|
* @param files - 要上传的文件数组
|
|
1995
|
-
* @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
|
|
1996
2656
|
* @returns 所有文件的上传结果
|
|
1997
2657
|
*/
|
|
1998
|
-
async uploadFiles(files
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2658
|
+
async uploadFiles(files) {
|
|
2659
|
+
if (files.length === 0) return {
|
|
2660
|
+
success: true,
|
|
2661
|
+
urls: [],
|
|
2662
|
+
results: []
|
|
2663
|
+
};
|
|
2664
|
+
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
2665
|
+
try {
|
|
2666
|
+
const fileInfos = files.map((file) => ({
|
|
2667
|
+
file,
|
|
2668
|
+
objectKey: this.generateObjectKey(file.name)
|
|
2669
|
+
}));
|
|
2670
|
+
const objectKeys = fileInfos.map((info) => info.objectKey);
|
|
2671
|
+
const presignedResponse = await this.getPresignedUrls(objectKeys);
|
|
2672
|
+
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
2673
|
+
const presignedMap = /* @__PURE__ */ new Map();
|
|
2674
|
+
for (const item of presignedResponse.items) presignedMap.set(item.object_key, item);
|
|
2675
|
+
const results = (await runWithConcurrencySettled(fileInfos.map(({ file, objectKey }) => async () => {
|
|
2676
|
+
const presignedItem = presignedMap.get(objectKey);
|
|
2677
|
+
if (!presignedItem) return {
|
|
2678
|
+
success: false,
|
|
2679
|
+
error: `No presigned URL for ${file.name}`,
|
|
2680
|
+
objectKey
|
|
2681
|
+
};
|
|
2682
|
+
try {
|
|
2683
|
+
const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
|
|
2684
|
+
method: "PUT",
|
|
2685
|
+
body: file,
|
|
2686
|
+
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
2687
|
+
});
|
|
2688
|
+
if (!uploadResponse.ok) {
|
|
2689
|
+
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
2690
|
+
return {
|
|
2691
|
+
success: false,
|
|
2692
|
+
error: `COS upload failed: ${uploadResponse.status} ${errorText}`,
|
|
2693
|
+
objectKey
|
|
2694
|
+
};
|
|
2695
|
+
}
|
|
2696
|
+
this.logger?.debug(`[CosUploadService] Uploaded: ${file.name}`);
|
|
2697
|
+
return {
|
|
2698
|
+
success: true,
|
|
2699
|
+
url: presignedItem.download_url,
|
|
2700
|
+
objectKey
|
|
2701
|
+
};
|
|
2702
|
+
} catch (error) {
|
|
2703
|
+
return {
|
|
2704
|
+
success: false,
|
|
2705
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
2706
|
+
objectKey
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
}), this.uploadConcurrency)).map((result, index) => {
|
|
2710
|
+
if (result.status === "fulfilled") return result.value;
|
|
2711
|
+
return {
|
|
2712
|
+
success: false,
|
|
2713
|
+
error: result.reason instanceof Error ? result.reason.message : "Unknown error",
|
|
2714
|
+
objectKey: fileInfos[index].objectKey
|
|
2715
|
+
};
|
|
2716
|
+
});
|
|
2717
|
+
const urls = results.filter((r) => r.success && r.url).map((r) => r.url);
|
|
2718
|
+
const failedResults = results.filter((r) => !r.success);
|
|
2719
|
+
if (failedResults.length > 0) {
|
|
2720
|
+
const failedErrors = failedResults.map((r) => r.error).join("; ");
|
|
2721
|
+
return {
|
|
2722
|
+
success: false,
|
|
2723
|
+
error: `${failedResults.length} file(s) failed: ${failedErrors}`,
|
|
2724
|
+
expireSeconds: presignedResponse.expire,
|
|
2725
|
+
results
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
|
|
2010
2729
|
return {
|
|
2011
|
-
success:
|
|
2012
|
-
|
|
2730
|
+
success: true,
|
|
2731
|
+
urls,
|
|
2732
|
+
expireSeconds: presignedResponse.expire,
|
|
2013
2733
|
results
|
|
2014
2734
|
};
|
|
2735
|
+
} catch (error) {
|
|
2736
|
+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
2737
|
+
this.logger?.error(`[CosUploadService] Batch upload failed`, error);
|
|
2738
|
+
return {
|
|
2739
|
+
success: false,
|
|
2740
|
+
error: errorMessage,
|
|
2741
|
+
results: files.map(() => ({
|
|
2742
|
+
success: false,
|
|
2743
|
+
error: errorMessage
|
|
2744
|
+
}))
|
|
2745
|
+
};
|
|
2015
2746
|
}
|
|
2016
|
-
this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
|
|
2017
|
-
return {
|
|
2018
|
-
success: true,
|
|
2019
|
-
urls,
|
|
2020
|
-
results
|
|
2021
|
-
};
|
|
2022
2747
|
}
|
|
2023
2748
|
};
|
|
2024
2749
|
|
|
2025
2750
|
//#endregion
|
|
2026
|
-
//#region ../agent-provider/src/
|
|
2751
|
+
//#region ../agent-provider/src/account/account-service.ts
|
|
2027
2752
|
/**
|
|
2028
|
-
*
|
|
2753
|
+
* AccountService 类
|
|
2754
|
+
*
|
|
2755
|
+
* 单例模式,管理全局账号状态
|
|
2029
2756
|
*/
|
|
2757
|
+
var AccountService = class {
|
|
2758
|
+
constructor() {
|
|
2759
|
+
this.account = null;
|
|
2760
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
2761
|
+
this.initialized = false;
|
|
2762
|
+
this.initPromise = null;
|
|
2763
|
+
this.initResolve = null;
|
|
2764
|
+
this.initPromise = new Promise((resolve) => {
|
|
2765
|
+
this.initResolve = resolve;
|
|
2766
|
+
});
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* 获取当前账号
|
|
2770
|
+
* @returns 当前账号,未登录或未加载时返回 null
|
|
2771
|
+
*/
|
|
2772
|
+
getAccount() {
|
|
2773
|
+
return this.account;
|
|
2774
|
+
}
|
|
2775
|
+
/**
|
|
2776
|
+
* 设置账号
|
|
2777
|
+
* @param account 账号信息,登出时传 null
|
|
2778
|
+
*/
|
|
2779
|
+
setAccount(account) {
|
|
2780
|
+
const prev = this.account;
|
|
2781
|
+
this.account = account;
|
|
2782
|
+
if (!this.initialized) {
|
|
2783
|
+
this.initialized = true;
|
|
2784
|
+
this.initResolve?.(account);
|
|
2785
|
+
}
|
|
2786
|
+
if (prev?.uid !== account?.uid) this.notifyListeners();
|
|
2787
|
+
}
|
|
2788
|
+
/**
|
|
2789
|
+
* 清除账号(登出)
|
|
2790
|
+
*/
|
|
2791
|
+
clearAccount() {
|
|
2792
|
+
this.setAccount(null);
|
|
2793
|
+
}
|
|
2794
|
+
/**
|
|
2795
|
+
* 订阅账号变化
|
|
2796
|
+
* @param callback 变化时的回调函数
|
|
2797
|
+
* @returns 取消订阅函数
|
|
2798
|
+
*/
|
|
2799
|
+
subscribe(callback) {
|
|
2800
|
+
this.listeners.add(callback);
|
|
2801
|
+
return () => {
|
|
2802
|
+
this.listeners.delete(callback);
|
|
2803
|
+
};
|
|
2804
|
+
}
|
|
2805
|
+
/**
|
|
2806
|
+
* 等待首次账号加载完成
|
|
2807
|
+
* @returns Promise<Account | null>
|
|
2808
|
+
*/
|
|
2809
|
+
waitForInit() {
|
|
2810
|
+
if (this.initialized) return Promise.resolve(this.account);
|
|
2811
|
+
return this.initPromise;
|
|
2812
|
+
}
|
|
2813
|
+
/**
|
|
2814
|
+
* 是否已初始化
|
|
2815
|
+
*/
|
|
2816
|
+
isInitialized() {
|
|
2817
|
+
return this.initialized;
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* 是否已登录
|
|
2821
|
+
*/
|
|
2822
|
+
isLoggedIn() {
|
|
2823
|
+
return this.account !== null;
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* 通知所有订阅者
|
|
2827
|
+
*/
|
|
2828
|
+
notifyListeners() {
|
|
2829
|
+
this.listeners.forEach((callback) => {
|
|
2830
|
+
try {
|
|
2831
|
+
callback(this.account);
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
console.error("[AccountService] Listener error:", error);
|
|
2834
|
+
}
|
|
2835
|
+
});
|
|
2836
|
+
}
|
|
2837
|
+
/**
|
|
2838
|
+
* 重置服务状态(仅用于测试)
|
|
2839
|
+
*/
|
|
2840
|
+
_reset() {
|
|
2841
|
+
this.account = null;
|
|
2842
|
+
this.listeners.clear();
|
|
2843
|
+
this.initialized = false;
|
|
2844
|
+
this.initPromise = new Promise((resolve) => {
|
|
2845
|
+
this.initResolve = resolve;
|
|
2846
|
+
});
|
|
2847
|
+
}
|
|
2848
|
+
};
|
|
2030
2849
|
/**
|
|
2031
|
-
*
|
|
2032
|
-
* TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
|
|
2033
|
-
* TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
|
|
2034
|
-
* TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
|
|
2035
|
-
* TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
|
|
2036
|
-
* TCACA_code_008_cfWoLwvjU4 赠送月包
|
|
2850
|
+
* 导出单例实例
|
|
2037
2851
|
*/
|
|
2038
|
-
|
|
2039
|
-
CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
|
|
2040
|
-
CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
|
|
2041
|
-
CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
|
|
2042
|
-
CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
|
|
2043
|
-
CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
|
|
2044
|
-
CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
|
|
2045
|
-
CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
|
|
2046
|
-
return CommodityCode;
|
|
2047
|
-
}({});
|
|
2852
|
+
const accountService = new AccountService();
|
|
2048
2853
|
|
|
2049
2854
|
//#endregion
|
|
2050
|
-
//#region ../agent-provider/src/
|
|
2855
|
+
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
|
|
2051
2856
|
/**
|
|
2052
|
-
*
|
|
2857
|
+
* Normalize a path by resolving `.` and `..` segments
|
|
2858
|
+
* This is a simplified version that works in browser environment
|
|
2859
|
+
*/
|
|
2860
|
+
function normalizePath(path) {
|
|
2861
|
+
const segments = path.split("/");
|
|
2862
|
+
const result = [];
|
|
2863
|
+
for (const segment of segments) if (segment === "..") {
|
|
2864
|
+
if (result.length > 0 && result[result.length - 1] !== "") result.pop();
|
|
2865
|
+
} else if (segment !== "." && segment !== "") result.push(segment);
|
|
2866
|
+
return (path.startsWith("/") ? "/" : "") + result.join("/");
|
|
2867
|
+
}
|
|
2868
|
+
/**
|
|
2869
|
+
* Resolve agent:/// URI to filesystem path
|
|
2053
2870
|
*
|
|
2054
|
-
*
|
|
2871
|
+
* 只处理 agent:// 协议的 URI,raw path 直接透传不做任何处理
|
|
2872
|
+
*
|
|
2873
|
+
* Supported formats:
|
|
2874
|
+
* - `agent:///workspace/{path}` → `/workspace/{path}`
|
|
2875
|
+
* - `agent:///plans/{path}` → `/root/.codebuddy/plans/{path}`
|
|
2876
|
+
* - `agent:///{path}` → `/{path}`
|
|
2877
|
+
* - Raw paths (e.g. `/foo/bar`) → 直接透传,不处理
|
|
2878
|
+
*
|
|
2879
|
+
* Security: Path traversal attacks are prevented by normalizing paths
|
|
2880
|
+
* and verifying they stay within expected boundaries.
|
|
2055
2881
|
*/
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2882
|
+
function resolveAgentUri(input) {
|
|
2883
|
+
if (!input.startsWith("agent://")) return input;
|
|
2884
|
+
const path = input.slice(8);
|
|
2885
|
+
if (!path.startsWith("/")) return input;
|
|
2886
|
+
const normalizedPath = normalizePath(path);
|
|
2887
|
+
if (normalizedPath.startsWith("/plans/") || normalizedPath === "/plans") {
|
|
2888
|
+
const finalPath = normalizePath("/root/.codebuddy" + normalizedPath);
|
|
2889
|
+
if (!finalPath.startsWith("/root/.codebuddy/plans")) throw new Error(`Invalid path: path traversal detected in ${input}`);
|
|
2890
|
+
return finalPath;
|
|
2891
|
+
}
|
|
2892
|
+
return normalizedPath;
|
|
2893
|
+
}
|
|
2062
2894
|
/**
|
|
2063
|
-
*
|
|
2895
|
+
* Create a FilesResource wrapper that resolves agent:/// URIs
|
|
2064
2896
|
*/
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2897
|
+
function createAgentFilesystem(fs) {
|
|
2898
|
+
return {
|
|
2899
|
+
read: (path, opts) => fs.read(resolveAgentUri(path), opts),
|
|
2900
|
+
write: (pathOrFiles, dataOrOpts, opts) => {
|
|
2901
|
+
if (Array.isArray(pathOrFiles)) {
|
|
2902
|
+
const resolved = pathOrFiles.map((f) => ({
|
|
2903
|
+
...f,
|
|
2904
|
+
path: resolveAgentUri(f.path)
|
|
2905
|
+
}));
|
|
2906
|
+
return fs.write(resolved, dataOrOpts);
|
|
2907
|
+
}
|
|
2908
|
+
return fs.write(resolveAgentUri(pathOrFiles), dataOrOpts, opts);
|
|
2909
|
+
},
|
|
2910
|
+
list: (path, opts) => fs.list(resolveAgentUri(path), opts),
|
|
2911
|
+
exists: (path, opts) => fs.exists(resolveAgentUri(path), opts),
|
|
2912
|
+
makeDir: (path, opts) => fs.makeDir(resolveAgentUri(path), opts),
|
|
2913
|
+
remove: (path, opts) => fs.remove(resolveAgentUri(path), opts),
|
|
2914
|
+
rename: (oldPath, newPath, opts) => fs.rename(resolveAgentUri(oldPath), resolveAgentUri(newPath), opts),
|
|
2915
|
+
getInfo: (path, opts) => fs.getInfo(resolveAgentUri(path), opts),
|
|
2916
|
+
watchDir: (path, onEvent, opts) => fs.watchDir(resolveAgentUri(path), onEvent, opts)
|
|
2917
|
+
};
|
|
2918
|
+
}
|
|
2919
|
+
/**
|
|
2920
|
+
* CloudAgentProvider - Manages cloud-hosted agents via REST API
|
|
2921
|
+
*
|
|
2922
|
+
* API Endpoints:
|
|
2923
|
+
* - POST {endpoint}/console/cloudagent/agentmgmt/agents - Create new agent
|
|
2924
|
+
* - GET {endpoint}/console/cloudagent/agentmgmt/agents/{id} - Get agent data
|
|
2925
|
+
* - GET {endpoint}/console/cloudagent/agentmgmt/agents - List all agents
|
|
2926
|
+
* - POST {endpoint}/console/cloudagent/agentmgmt/agents/{id}/delete - Delete agent
|
|
2927
|
+
* - GET {endpoint}/console/cloudagent/agentmgmt/agents/{id}/session - Get agent session (includes sandboxId)
|
|
2928
|
+
* - GET {endpoint}/console/cloudagent/agentmgmt/models - Get available models
|
|
2929
|
+
*
|
|
2930
|
+
* The provider stores agent endpoint configurations in the cloud backend.
|
|
2931
|
+
* When connect() is called, it creates a CloudAgentConnection to the agent's
|
|
2932
|
+
* endpoint and returns an Agent instance.
|
|
2933
|
+
*
|
|
2934
|
+
* @example
|
|
2935
|
+
* ```typescript
|
|
2936
|
+
* const provider = new CloudAgentProvider({
|
|
2937
|
+
* endpoint: 'https://staging-copilot.tencent.com',
|
|
2938
|
+
* authToken: 'token'
|
|
2939
|
+
* });
|
|
2940
|
+
*
|
|
2941
|
+
* // List all agents (uses default pagination and sorting)
|
|
2942
|
+
* const allAgents = await provider.list();
|
|
2943
|
+
*
|
|
2944
|
+
* // List agents with custom pagination
|
|
2945
|
+
* const page2 = await provider.list({
|
|
2946
|
+
* page: 2,
|
|
2947
|
+
* size: 50
|
|
2948
|
+
* });
|
|
2949
|
+
*
|
|
2950
|
+
* // List agents with filtering
|
|
2951
|
+
* const runningAgents = await provider.list({
|
|
2952
|
+
* filters: [
|
|
2953
|
+
* { field: 'status', value: 'running' }
|
|
2954
|
+
* ]
|
|
2955
|
+
* });
|
|
2956
|
+
*
|
|
2957
|
+
* // List agents with custom sorting
|
|
2958
|
+
* const sortedAgents = await provider.list({
|
|
2959
|
+
* sort: {
|
|
2960
|
+
* orderBy: 'createdAt',
|
|
2961
|
+
* order: 'desc'
|
|
2962
|
+
* }
|
|
2963
|
+
* });
|
|
2964
|
+
*
|
|
2965
|
+
* // List agents created in last 14 days with multiple filters
|
|
2966
|
+
* const recentAgents = await provider.list({
|
|
2967
|
+
* dayRange: 14,
|
|
2968
|
+
* filters: [
|
|
2969
|
+
* { field: 'status', value: 'running,stopped' }
|
|
2970
|
+
* ],
|
|
2971
|
+
* page: 1,
|
|
2972
|
+
* size: 20
|
|
2973
|
+
* });
|
|
2974
|
+
*
|
|
2975
|
+
* // Get agent state
|
|
2976
|
+
* const state = await provider.get('agent-id');
|
|
2977
|
+
*
|
|
2978
|
+
* // Connect to agent
|
|
2979
|
+
* const agent = await provider.connect('agent-id');
|
|
2980
|
+
*
|
|
2981
|
+
* // Use agent
|
|
2982
|
+
* const session = await agent.sessions.create({ cwd: '/workspace' });
|
|
2983
|
+
*
|
|
2984
|
+
* // Get available models
|
|
2985
|
+
* const models = await provider.getModels('my-repo');
|
|
2986
|
+
* ```
|
|
2987
|
+
*/
|
|
2988
|
+
var CloudAgentProvider = class CloudAgentProvider {
|
|
2989
|
+
constructor(options) {
|
|
2990
|
+
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
2991
|
+
this.connectionCache = /* @__PURE__ */ new Map();
|
|
2992
|
+
this.eventListeners = /* @__PURE__ */ new Map();
|
|
2993
|
+
this.options = options;
|
|
2994
|
+
this.logger = options.logger;
|
|
2995
|
+
this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
|
|
2996
|
+
this.cosUploadService = new CosUploadService({
|
|
2997
|
+
request: (method, path, body) => this.request(method, path, body),
|
|
2998
|
+
logger: this.logger,
|
|
2999
|
+
fetch: this.fetchImpl
|
|
3000
|
+
});
|
|
2069
3001
|
}
|
|
2070
3002
|
/**
|
|
2071
|
-
*
|
|
2072
|
-
* API 端点: GET /v2/cloudagent/agentmgmt/agents
|
|
3003
|
+
* Dispose the provider and clean up resources
|
|
2073
3004
|
*/
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
const mockTitles = {
|
|
2078
|
-
"1": "开发五子棋游戏",
|
|
2079
|
-
"2": "修复登录页面样式",
|
|
2080
|
-
"3": "API 接口优化"
|
|
2081
|
-
};
|
|
2082
|
-
const agents = sessions.map((session, index) => ({
|
|
2083
|
-
id: session.sessionId,
|
|
2084
|
-
name: mockTitles[session.sessionId] || `Agent ${session.sessionId}`,
|
|
2085
|
-
status: "RUNNING",
|
|
2086
|
-
visibility: "PRIVATE",
|
|
2087
|
-
createdAt: new Date(session.createdAt).toISOString(),
|
|
2088
|
-
summary: `Session created at ${new Date(session.createdAt).toLocaleString()}`,
|
|
2089
|
-
source: {
|
|
2090
|
-
provider: "github",
|
|
2091
|
-
ref: "refs/heads/main",
|
|
2092
|
-
repository: session.cwd
|
|
2093
|
-
},
|
|
2094
|
-
target: {
|
|
2095
|
-
autoCreatePr: false,
|
|
2096
|
-
branchName: "feature/mock",
|
|
2097
|
-
prUrl: void 0,
|
|
2098
|
-
url: void 0
|
|
2099
|
-
}
|
|
2100
|
-
}));
|
|
2101
|
-
return {
|
|
2102
|
-
agents,
|
|
2103
|
-
pagination: {
|
|
2104
|
-
hasNext: false,
|
|
2105
|
-
hasPrev: false,
|
|
2106
|
-
page: 1,
|
|
2107
|
-
size: agents.length,
|
|
2108
|
-
total: agents.length,
|
|
2109
|
-
totalPages: 1
|
|
2110
|
-
}
|
|
2111
|
-
};
|
|
3005
|
+
dispose() {
|
|
3006
|
+
this.filesystemCache.clear();
|
|
3007
|
+
this.connectionCache.clear();
|
|
2112
3008
|
}
|
|
2113
3009
|
/**
|
|
2114
|
-
*
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
*/
|
|
2119
|
-
async getModels(request) {
|
|
2120
|
-
const mockModels = [{
|
|
2121
|
-
id: "glm-4.7",
|
|
2122
|
-
name: "GLM-4.7",
|
|
2123
|
-
vendor: "f",
|
|
2124
|
-
maxOutputTokens: 48e3,
|
|
2125
|
-
maxInputTokens: 2e5,
|
|
2126
|
-
supportsToolCall: true,
|
|
2127
|
-
supportsImages: false,
|
|
2128
|
-
disabledMultimodal: true,
|
|
2129
|
-
maxAllowedSize: 2e5,
|
|
2130
|
-
supportsReasoning: true,
|
|
2131
|
-
onlyReasoning: true,
|
|
2132
|
-
temperature: 1,
|
|
2133
|
-
reasoning: {
|
|
2134
|
-
effort: "medium",
|
|
2135
|
-
summary: "auto"
|
|
2136
|
-
},
|
|
2137
|
-
descriptionEn: "GLM-4.7 model, Well-rounded model for everyday use",
|
|
2138
|
-
descriptionZh: "GLM-4.7 大模型,能力均衡,适合日常使用"
|
|
2139
|
-
}, {
|
|
2140
|
-
id: "glm-4.7-flash",
|
|
2141
|
-
name: "GLM-4.7 Flash",
|
|
2142
|
-
vendor: "f",
|
|
2143
|
-
maxOutputTokens: 4e4,
|
|
2144
|
-
maxInputTokens: 128e3,
|
|
2145
|
-
supportsToolCall: true,
|
|
2146
|
-
supportsImages: false,
|
|
2147
|
-
disabledMultimodal: false,
|
|
2148
|
-
maxAllowedSize: 128e3,
|
|
2149
|
-
supportsReasoning: false,
|
|
2150
|
-
onlyReasoning: false,
|
|
2151
|
-
temperature: .7,
|
|
2152
|
-
reasoning: {
|
|
2153
|
-
effort: "low",
|
|
2154
|
-
summary: "never"
|
|
2155
|
-
},
|
|
2156
|
-
descriptionEn: "GLM-4.7 Flash, Fast and efficient model",
|
|
2157
|
-
descriptionZh: "GLM-4.7 Flash,快速高效的模型"
|
|
2158
|
-
}];
|
|
2159
|
-
console.log("[BackendProvider] getModels called for repository:", request.repository);
|
|
2160
|
-
return { models: mockModels };
|
|
3010
|
+
* Get the filesystem provider (returns self)
|
|
3011
|
+
*/
|
|
3012
|
+
get filesystem() {
|
|
3013
|
+
return this;
|
|
2161
3014
|
}
|
|
2162
3015
|
/**
|
|
2163
|
-
*
|
|
2164
|
-
*
|
|
2165
|
-
*
|
|
2166
|
-
*
|
|
2167
|
-
*
|
|
2168
|
-
*
|
|
2169
|
-
*
|
|
2170
|
-
*
|
|
2171
|
-
*
|
|
2172
|
-
*
|
|
3016
|
+
* Get filesystem resource for an agent
|
|
3017
|
+
*
|
|
3018
|
+
* Creates or returns cached filesystem instance for the agent's sandbox.
|
|
3019
|
+
* The filesystem supports both `agent:///` URIs and raw paths.
|
|
3020
|
+
*
|
|
3021
|
+
* @param agentId - Agent ID to get filesystem for
|
|
3022
|
+
* @returns FilesResource instance for the agent's sandbox (with URI support)
|
|
3023
|
+
*
|
|
3024
|
+
* @example
|
|
3025
|
+
* ```typescript
|
|
3026
|
+
* const fs = await provider.getFilesystem(agentId);
|
|
3027
|
+
*
|
|
3028
|
+
* // Use agent:/// URIs
|
|
3029
|
+
* const content = await fs.read('agent:///files/src/app.ts');
|
|
3030
|
+
* await fs.write('agent:///artifacts/output.txt', 'Hello');
|
|
3031
|
+
*
|
|
3032
|
+
* // Raw paths still work (backward compatible)
|
|
3033
|
+
* const content2 = await fs.read('/src/app.ts');
|
|
3034
|
+
* ```
|
|
2173
3035
|
*/
|
|
2174
|
-
async
|
|
2175
|
-
const
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
3036
|
+
async getFilesystem(agentId) {
|
|
3037
|
+
const cached = this.filesystemCache.get(agentId);
|
|
3038
|
+
if (cached) return cached;
|
|
3039
|
+
const info = await this.getSandboxInfo(agentId);
|
|
3040
|
+
const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
|
|
3041
|
+
this.filesystemCache.set(agentId, filesystem);
|
|
3042
|
+
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
3043
|
+
return filesystem;
|
|
3044
|
+
}
|
|
3045
|
+
/**
|
|
3046
|
+
* Get sandbox information from backend
|
|
3047
|
+
*
|
|
3048
|
+
* Uses GET {endpoint}/console/cloudagent/agentmgmt/agents/{agentId}/session
|
|
3049
|
+
* to retrieve sandbox information. Extracts sandboxId from the session response
|
|
3050
|
+
* and constructs the apiUrl for E2B proxy.
|
|
3051
|
+
*
|
|
3052
|
+
* @param agentId - Agent ID
|
|
3053
|
+
* @returns E2B Sandbox connection information with sandboxId and apiUrl
|
|
3054
|
+
*/
|
|
3055
|
+
async getSandboxInfo(agentId) {
|
|
3056
|
+
const response = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
|
|
3057
|
+
if (!response.ok) throw new Error(`Failed to get sandbox info: ${response.statusText}`);
|
|
3058
|
+
const apiResponse = await response.json();
|
|
3059
|
+
if (!apiResponse.data) throw new Error("No data in API response");
|
|
3060
|
+
const apiUrl = new URL(`/console/cloudagent/e2bproxy/agents/${agentId}`, this.options.endpoint).toString();
|
|
3061
|
+
const currentEnterpriseId = localStorage.getItem("currentEnterpriseId");
|
|
3062
|
+
return {
|
|
3063
|
+
sandboxId: apiResponse.data.sandboxId,
|
|
3064
|
+
apiUrl,
|
|
3065
|
+
accessToken: apiResponse.data.token,
|
|
3066
|
+
headers: { ...currentEnterpriseId && { "X-Enterprise-Id": currentEnterpriseId } }
|
|
2179
3067
|
};
|
|
2180
|
-
|
|
3068
|
+
}
|
|
3069
|
+
/**
|
|
3070
|
+
* Get agent state by ID
|
|
3071
|
+
*/
|
|
3072
|
+
async get(agentId) {
|
|
2181
3073
|
try {
|
|
2182
|
-
const response = await
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
if (response.status === 401 || response.status === 403) return null;
|
|
2189
|
-
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
2190
|
-
throw new Error(error.message || `HTTP ${response.status}`);
|
|
2191
|
-
}
|
|
2192
|
-
if (!(response.headers.get("Content-Type") || "").includes("application/json")) return null;
|
|
2193
|
-
const { data = {} } = await response.json();
|
|
2194
|
-
const accounts = data?.accounts || [];
|
|
2195
|
-
if (!accounts || accounts.length === 0) return null;
|
|
2196
|
-
const selectedAccountId = localStorage.getItem(SELECTED_ACCOUNT_KEY);
|
|
2197
|
-
let selectedAccount;
|
|
2198
|
-
if (selectedAccountId) {
|
|
2199
|
-
selectedAccount = accounts.find((account) => {
|
|
2200
|
-
if (account.type === "personal") return account.uid === selectedAccountId;
|
|
2201
|
-
return account.enterpriseId === selectedAccountId;
|
|
2202
|
-
});
|
|
2203
|
-
if (selectedAccount) try {
|
|
2204
|
-
const plan = await this.getCurrentPlan();
|
|
2205
|
-
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
2206
|
-
console.log("account", {
|
|
2207
|
-
...selectedAccount,
|
|
2208
|
-
...plan,
|
|
2209
|
-
editionType
|
|
2210
|
-
});
|
|
2211
|
-
return {
|
|
2212
|
-
...selectedAccount,
|
|
2213
|
-
...plan,
|
|
2214
|
-
editionType
|
|
2215
|
-
};
|
|
2216
|
-
} catch (error) {
|
|
2217
|
-
return { ...selectedAccount };
|
|
2218
|
-
}
|
|
2219
|
-
}
|
|
2220
|
-
if (accounts.length === 1) {
|
|
2221
|
-
selectedAccount = accounts[0];
|
|
2222
|
-
const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
|
|
2223
|
-
if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
|
|
2224
|
-
const plan = await this.getCurrentPlan();
|
|
2225
|
-
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
2226
|
-
console.log("account (auto-selected)", {
|
|
2227
|
-
...selectedAccount,
|
|
2228
|
-
...plan,
|
|
2229
|
-
editionType
|
|
2230
|
-
});
|
|
2231
|
-
return {
|
|
2232
|
-
...selectedAccount,
|
|
2233
|
-
...plan,
|
|
2234
|
-
editionType
|
|
2235
|
-
};
|
|
2236
|
-
}
|
|
2237
|
-
const redirectUrl = encodeURIComponent(window.location.href);
|
|
2238
|
-
window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
2239
|
-
return null;
|
|
3074
|
+
const response = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}`);
|
|
3075
|
+
if (response.status === 404) return;
|
|
3076
|
+
if (!response.ok) throw new Error(`Failed to get agent: ${response.statusText}`);
|
|
3077
|
+
const apiResponse = await response.json();
|
|
3078
|
+
if (!apiResponse.data) throw new Error("No data in API response");
|
|
3079
|
+
return this.toAgentState(apiResponse.data);
|
|
2240
3080
|
} catch (error) {
|
|
2241
|
-
|
|
2242
|
-
|
|
3081
|
+
this.logger?.error(`Failed to get agent ${agentId}:`, error);
|
|
3082
|
+
throw error;
|
|
2243
3083
|
}
|
|
2244
3084
|
}
|
|
2245
3085
|
/**
|
|
2246
|
-
*
|
|
2247
|
-
*
|
|
2248
|
-
*
|
|
3086
|
+
* List all agent states with pagination information
|
|
3087
|
+
*
|
|
3088
|
+
* @param options - Optional query parameters for filtering, sorting, and pagination
|
|
3089
|
+
* @returns Object containing agents array and pagination info
|
|
2249
3090
|
*/
|
|
2250
|
-
async
|
|
2251
|
-
const defaultPlan = {
|
|
2252
|
-
isPro: false,
|
|
2253
|
-
expireAt: 0,
|
|
2254
|
-
renewFlag: 0,
|
|
2255
|
-
PackageCode: void 0,
|
|
2256
|
-
name: ""
|
|
2257
|
-
};
|
|
3091
|
+
async list(options) {
|
|
2258
3092
|
try {
|
|
2259
|
-
|
|
2260
|
-
const
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
Status: [0, 3],
|
|
2276
|
-
PackageEndTimeRangeBegin: formatDate(now),
|
|
2277
|
-
PackageEndTimeRangeEnd: formatDate(futureDate)
|
|
3093
|
+
console.log("[CloudAgentProvider] list called with options:", JSON.stringify(options, null, 2));
|
|
3094
|
+
const params = {
|
|
3095
|
+
page: 1,
|
|
3096
|
+
size: 30,
|
|
3097
|
+
sort: {
|
|
3098
|
+
order: "desc",
|
|
3099
|
+
orderBy: "status"
|
|
3100
|
+
},
|
|
3101
|
+
...options && {
|
|
3102
|
+
...options.dayRange !== void 0 && { dayRange: options.dayRange },
|
|
3103
|
+
...options.page !== void 0 && { page: options.page },
|
|
3104
|
+
...options.size !== void 0 && { size: options.size },
|
|
3105
|
+
...options.sort !== void 0 && { sort: options.sort },
|
|
3106
|
+
...options.filters !== void 0 && { filters: options.filters },
|
|
3107
|
+
...options.title !== void 0 && { title: options.title }
|
|
3108
|
+
}
|
|
2278
3109
|
};
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
3110
|
+
console.log("[CloudAgentProvider] API request params:", JSON.stringify(params, null, 2));
|
|
3111
|
+
const response = await this.request("GET", "/console/cloudagent/agentmgmt/agents", params);
|
|
3112
|
+
if (!response.ok) throw new Error(`Failed to list agents: ${response.statusText}`);
|
|
3113
|
+
const apiResponse = await response.json();
|
|
3114
|
+
if (!apiResponse.data) throw new Error("No data in API response");
|
|
3115
|
+
const agents = apiResponse.data.agents.map((a) => this.toAgentState(a));
|
|
3116
|
+
const pagination = apiResponse.data.pagination;
|
|
3117
|
+
console.log("[CloudAgentProvider] API response:", {
|
|
3118
|
+
agentsCount: agents.length,
|
|
3119
|
+
pagination
|
|
2284
3120
|
});
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
}
|
|
2289
|
-
const result = await response.json();
|
|
2290
|
-
const resources = result?.data?.Response?.Data?.Accounts || result?.data?.Accounts || [];
|
|
2291
|
-
if (!resources || resources.length === 0) return defaultPlan;
|
|
2292
|
-
const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon);
|
|
2293
|
-
const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift);
|
|
2294
|
-
const activePlan = proPlan || trialPlan;
|
|
2295
|
-
if (activePlan) return {
|
|
2296
|
-
isPro: !!proPlan,
|
|
2297
|
-
expireAt: activePlan.ExpiredTime || activePlan.CycleEndTime || 0,
|
|
2298
|
-
renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
|
|
2299
|
-
PackageCode: activePlan.PackageCode,
|
|
2300
|
-
name: activePlan.PackageName || ""
|
|
3121
|
+
return {
|
|
3122
|
+
agents,
|
|
3123
|
+
pagination
|
|
2301
3124
|
};
|
|
2302
|
-
return defaultPlan;
|
|
2303
3125
|
} catch (error) {
|
|
2304
|
-
|
|
2305
|
-
|
|
3126
|
+
this.logger?.error("Failed to list agents:", error);
|
|
3127
|
+
throw error;
|
|
2306
3128
|
}
|
|
2307
3129
|
}
|
|
2308
3130
|
/**
|
|
2309
|
-
*
|
|
2310
|
-
*
|
|
2311
|
-
* - personal + !isPro = 'free'
|
|
2312
|
-
* - ultimate = 'ultimate' (旗舰版/团队版)
|
|
2313
|
-
* - exclusive = 'exclusive' (专享版/企业版)
|
|
2314
|
-
*/
|
|
2315
|
-
getEditionDisplayType(type, isPro) {
|
|
2316
|
-
if (type === "personal") return isPro ? "pro" : "free";
|
|
2317
|
-
if (type === "ultimate") return "ultimate";
|
|
2318
|
-
if (type === "exclusive") return "exclusive";
|
|
2319
|
-
return "free";
|
|
2320
|
-
}
|
|
2321
|
-
/**
|
|
2322
|
-
* 触发登录流程
|
|
2323
|
-
* Web 环境: 跳转到登录页面
|
|
2324
|
-
*/
|
|
2325
|
-
async login() {
|
|
2326
|
-
const redirectUrl = encodeURIComponent(window.location.href);
|
|
2327
|
-
window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
2328
|
-
}
|
|
2329
|
-
/**
|
|
2330
|
-
* 登出账号
|
|
2331
|
-
* Web 环境: 跳转到登出页面或清除 cookie
|
|
3131
|
+
* Create a new agent
|
|
3132
|
+
* POST {endpoint}/console/cloudagent/agentmgmt/agents
|
|
2332
3133
|
*/
|
|
2333
|
-
async
|
|
2334
|
-
const url = `${this.baseUrl}/console/logout`;
|
|
3134
|
+
async create() {
|
|
2335
3135
|
try {
|
|
2336
|
-
await
|
|
2337
|
-
|
|
2338
|
-
|
|
3136
|
+
const response = await this.request("POST", "/console/cloudagent/agentmgmt/agents", {
|
|
3137
|
+
prompt: "",
|
|
3138
|
+
model: "deepseek-r1"
|
|
2339
3139
|
});
|
|
3140
|
+
if (!response.ok) throw new Error(`Failed to create agent: ${response.statusText}`);
|
|
3141
|
+
const apiResponse = await response.json();
|
|
3142
|
+
if (!apiResponse.data) throw new Error("No data in API response");
|
|
3143
|
+
this.logger?.info(`Created agent: ${apiResponse.data.id}`);
|
|
3144
|
+
return apiResponse.data.id;
|
|
2340
3145
|
} catch (error) {
|
|
2341
|
-
|
|
3146
|
+
this.logger?.error("Failed to create agent:", error);
|
|
3147
|
+
throw error;
|
|
2342
3148
|
}
|
|
2343
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
2344
|
-
window.location.reload();
|
|
2345
|
-
}
|
|
2346
|
-
};
|
|
2347
|
-
/**
|
|
2348
|
-
* 创建 BackendProvider 实例
|
|
2349
|
-
*/
|
|
2350
|
-
function createBackendProvider(config) {
|
|
2351
|
-
return new BackendProvider(config);
|
|
2352
|
-
}
|
|
2353
|
-
|
|
2354
|
-
//#endregion
|
|
2355
|
-
//#region ../agent-provider/src/backend/ipc-backend-provider.ts
|
|
2356
|
-
/**
|
|
2357
|
-
* Backend 请求类型常量
|
|
2358
|
-
*/
|
|
2359
|
-
const BACKEND_REQUEST_TYPES = {
|
|
2360
|
-
GET_AGENTS: "backend:get-agents-request",
|
|
2361
|
-
GET_MODELS: "backend:get-models",
|
|
2362
|
-
LOGIN: "backend:login",
|
|
2363
|
-
LOGOUT: "backend:logout",
|
|
2364
|
-
GET_ACCOUNT: "backend:get-account"
|
|
2365
|
-
};
|
|
2366
|
-
/**
|
|
2367
|
-
* 生成唯一请求 ID
|
|
2368
|
-
*/
|
|
2369
|
-
function generateRequestId() {
|
|
2370
|
-
return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
2371
|
-
}
|
|
2372
|
-
/**
|
|
2373
|
-
* IPC Backend Provider 实现类
|
|
2374
|
-
*
|
|
2375
|
-
* 通过 IWidgetChannel 与后端通信获取 Agent 列表
|
|
2376
|
-
*/
|
|
2377
|
-
var IPCBackendProvider = class {
|
|
2378
|
-
constructor(config) {
|
|
2379
|
-
this.channel = config.channel;
|
|
2380
|
-
this.debug = config.debug ?? false;
|
|
2381
|
-
this.timeoutMs = config.timeoutMs ?? 3e4;
|
|
2382
|
-
this.log("Initialized with IWidgetChannel");
|
|
2383
3149
|
}
|
|
2384
3150
|
/**
|
|
2385
|
-
*
|
|
2386
|
-
*
|
|
2387
|
-
*
|
|
2388
|
-
*
|
|
3151
|
+
* Connect to an agent and return the connection
|
|
3152
|
+
*
|
|
3153
|
+
* This method:
|
|
3154
|
+
* 1. Fetches the agent configuration from the backend
|
|
3155
|
+
* 2. Checks if an existing connection can be reused (based on endpoint link)
|
|
3156
|
+
* 3. Creates a CloudAgentConnection to the agent's endpoint if not cached
|
|
3157
|
+
* 4. Saves session connection info to the connection
|
|
3158
|
+
* 5. Connects and initializes the connection
|
|
3159
|
+
* 6. Returns the connected CloudAgentConnection
|
|
3160
|
+
*
|
|
3161
|
+
* Connection caching:
|
|
3162
|
+
* - Connections are cached by endpoint link to enable reuse
|
|
3163
|
+
* - CloudAgentConnection is responsible for handling connection health checks
|
|
3164
|
+
* and token expiration internally
|
|
2389
3165
|
*/
|
|
2390
|
-
async
|
|
2391
|
-
const
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
3166
|
+
async connect(agentId) {
|
|
3167
|
+
const sessionResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
|
|
3168
|
+
if (sessionResponse.status === 404) throw new Error(`Agent not found: ${agentId}`);
|
|
3169
|
+
if (!sessionResponse.ok) throw new Error(`Failed to get agent for connection: ${sessionResponse.statusText}`);
|
|
3170
|
+
const sessionApiResponse = await sessionResponse.json();
|
|
3171
|
+
if (!sessionApiResponse.data) throw new Error("No data in API response");
|
|
3172
|
+
const sessionData = sessionApiResponse.data;
|
|
3173
|
+
const endpoint = sessionData.link.replace(/^http:\/\//, "https://");
|
|
3174
|
+
const cwd = sessionApiResponse.data.cwd || "/workspace";
|
|
3175
|
+
const agentResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}`);
|
|
3176
|
+
let agentName;
|
|
3177
|
+
let agentCreatedAt;
|
|
3178
|
+
let agentStatus;
|
|
3179
|
+
if (agentResponse.ok) {
|
|
3180
|
+
const agentApiResponse = await agentResponse.json();
|
|
3181
|
+
if (agentApiResponse.data) {
|
|
3182
|
+
agentName = agentApiResponse.data.name;
|
|
3183
|
+
agentCreatedAt = agentApiResponse.data.createdAt ? new Date(agentApiResponse.data.createdAt) : void 0;
|
|
3184
|
+
agentStatus = agentApiResponse.data.sessionStatus || agentApiResponse.data.status;
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
const existingConnection = this.connectionCache.get(endpoint);
|
|
3188
|
+
if (existingConnection?.isInitialized) {
|
|
3189
|
+
this.logger?.info(`Reusing existing connection for agent: ${agentId}`);
|
|
3190
|
+
return existingConnection;
|
|
3191
|
+
}
|
|
3192
|
+
const clientCapabilities = {
|
|
3193
|
+
...this.options.clientCapabilities,
|
|
3194
|
+
_meta: {
|
|
3195
|
+
...this.options.clientCapabilities?._meta,
|
|
3196
|
+
"codebuddy.ai": {
|
|
3197
|
+
...this.options.clientCapabilities?._meta?.["codebuddy.ai"],
|
|
3198
|
+
cwd
|
|
3199
|
+
}
|
|
2397
3200
|
}
|
|
2398
3201
|
};
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
3202
|
+
const connection = new CloudAgentConnection(agentId, {
|
|
3203
|
+
endpoint,
|
|
3204
|
+
authToken: sessionData.token,
|
|
3205
|
+
logger: this.logger,
|
|
3206
|
+
clientCapabilities
|
|
3207
|
+
}, cwd);
|
|
3208
|
+
connection.setSessionConnectionInfo({
|
|
3209
|
+
sessionId: sessionData.sessionId,
|
|
3210
|
+
agentId: sessionData.id,
|
|
3211
|
+
link: sessionData.link,
|
|
3212
|
+
token: sessionData.token,
|
|
3213
|
+
sandboxId: sessionData.sandboxId,
|
|
3214
|
+
expireAt: sessionData.expireAt,
|
|
3215
|
+
cwd
|
|
3216
|
+
});
|
|
2411
3217
|
try {
|
|
2412
|
-
|
|
3218
|
+
await connection.connect();
|
|
2413
3219
|
} catch (error) {
|
|
2414
|
-
this.
|
|
3220
|
+
this.logger?.error(`Failed to connect to agent ${agentId}:`, error);
|
|
2415
3221
|
throw error;
|
|
2416
3222
|
}
|
|
3223
|
+
this.connectionCache.set(endpoint, connection);
|
|
3224
|
+
connection.once("disconnected", () => {
|
|
3225
|
+
this.connectionCache.delete(endpoint);
|
|
3226
|
+
this.logger?.debug(`Connection removed from cache: ${endpoint}`);
|
|
3227
|
+
});
|
|
3228
|
+
this.logger?.info(`Connected to agent: ${agentId}`);
|
|
3229
|
+
this.emitEvent("sessionCreated", {
|
|
3230
|
+
id: agentId,
|
|
3231
|
+
agentId,
|
|
3232
|
+
name: agentName,
|
|
3233
|
+
status: agentStatus,
|
|
3234
|
+
cwd,
|
|
3235
|
+
createdAt: agentCreatedAt
|
|
3236
|
+
});
|
|
3237
|
+
return connection;
|
|
2417
3238
|
}
|
|
2418
3239
|
/**
|
|
2419
|
-
*
|
|
2420
|
-
*
|
|
3240
|
+
* Delete an agent by ID
|
|
3241
|
+
* POST {endpoint}/console/cloudagent/agentmgmt/agents/{agentId}/delete
|
|
2421
3242
|
*/
|
|
2422
|
-
async
|
|
2423
|
-
this.log("Getting models with request:", request);
|
|
3243
|
+
async delete(agentId) {
|
|
2424
3244
|
try {
|
|
2425
|
-
|
|
3245
|
+
const requestBody = { id: agentId };
|
|
3246
|
+
const response = await this.request("POST", `/console/cloudagent/agentmgmt/agents/${agentId}/delete`, requestBody);
|
|
3247
|
+
if (response.status === 404) return false;
|
|
3248
|
+
if (!response.ok) throw new Error(`Failed to delete agent: ${response.statusText}`);
|
|
3249
|
+
return true;
|
|
2426
3250
|
} catch (error) {
|
|
2427
|
-
this.
|
|
3251
|
+
this.logger?.error(`Failed to delete agent ${agentId}:`, error);
|
|
2428
3252
|
throw error;
|
|
2429
3253
|
}
|
|
2430
3254
|
}
|
|
2431
3255
|
/**
|
|
2432
|
-
*
|
|
2433
|
-
*
|
|
3256
|
+
* Archive an agent by ID
|
|
3257
|
+
* POST {endpoint}/console/cloudagent/agentmgmt/agents/{agentId}/archive
|
|
3258
|
+
*
|
|
3259
|
+
* @param agentId - Agent ID to archive
|
|
3260
|
+
* @returns ArchiveAgentResponse containing the archived agent ID
|
|
3261
|
+
*
|
|
3262
|
+
* @example
|
|
3263
|
+
* ```typescript
|
|
3264
|
+
* const result = await provider.archive('agent-123');
|
|
3265
|
+
* console.log('Archived agent:', result.id);
|
|
3266
|
+
* ```
|
|
2434
3267
|
*/
|
|
2435
|
-
async
|
|
2436
|
-
this.log("Getting account via IPC");
|
|
3268
|
+
async archive(agentId) {
|
|
2437
3269
|
try {
|
|
2438
|
-
|
|
3270
|
+
const response = await this.request("POST", `/console/cloudagent/agentmgmt/agents/${agentId}/archive`);
|
|
3271
|
+
if (!response.ok) throw new Error(`Failed to archive agent: ${response.statusText}`);
|
|
3272
|
+
const apiResponse = await response.json();
|
|
3273
|
+
if (!apiResponse.data) {
|
|
3274
|
+
this.logger?.info(`Archived agent: ${agentId}`);
|
|
3275
|
+
return { id: agentId };
|
|
3276
|
+
}
|
|
3277
|
+
this.logger?.info(`Archived agent: ${apiResponse.data.id}`);
|
|
3278
|
+
return apiResponse.data;
|
|
2439
3279
|
} catch (error) {
|
|
2440
|
-
this.
|
|
2441
|
-
|
|
3280
|
+
this.logger?.error(`Failed to archive agent ${agentId}:`, error);
|
|
3281
|
+
throw error;
|
|
2442
3282
|
}
|
|
2443
3283
|
}
|
|
2444
3284
|
/**
|
|
2445
|
-
*
|
|
2446
|
-
*
|
|
3285
|
+
* Rename an agent by ID
|
|
3286
|
+
* PATCH {endpoint}/v2/cloudagent/agentmgmt/agents/{agentId}
|
|
3287
|
+
*
|
|
3288
|
+
* @param agentId - Agent ID to rename
|
|
3289
|
+
* @param title - New title for the agent
|
|
3290
|
+
* @returns RenameAgentResponse containing the renamed agent ID
|
|
3291
|
+
*
|
|
3292
|
+
* @example
|
|
3293
|
+
* ```typescript
|
|
3294
|
+
* const result = await provider.rename('agent-123', 'New Title');
|
|
3295
|
+
* console.log('Renamed agent:', result.id);
|
|
3296
|
+
* ```
|
|
2447
3297
|
*/
|
|
2448
|
-
async
|
|
2449
|
-
this.log("Triggering login via IPC");
|
|
3298
|
+
async rename(agentId, title) {
|
|
2450
3299
|
try {
|
|
2451
|
-
|
|
3300
|
+
const body = { title };
|
|
3301
|
+
const response = await this.request("POST", `/console/cloudagent/agentmgmt/agents/${agentId}`, body);
|
|
3302
|
+
if (!response.ok) throw new Error(`Failed to rename agent: ${response.statusText}`);
|
|
3303
|
+
const apiResponse = await response.json();
|
|
3304
|
+
if (!apiResponse.data) {
|
|
3305
|
+
this.logger?.info(`Renamed agent: ${agentId} to "${title}"`);
|
|
3306
|
+
return { id: agentId };
|
|
3307
|
+
}
|
|
3308
|
+
this.logger?.info(`Renamed agent: ${apiResponse.data.id} to "${title}"`);
|
|
3309
|
+
return apiResponse.data;
|
|
2452
3310
|
} catch (error) {
|
|
2453
|
-
this.
|
|
3311
|
+
this.logger?.error(`Failed to rename agent ${agentId}:`, error);
|
|
2454
3312
|
throw error;
|
|
2455
3313
|
}
|
|
2456
3314
|
}
|
|
2457
3315
|
/**
|
|
2458
|
-
*
|
|
2459
|
-
*
|
|
3316
|
+
* Get available models from product configuration
|
|
3317
|
+
*
|
|
3318
|
+
* GET {endpoint}/v3/config?repos[]={repo}
|
|
3319
|
+
*
|
|
3320
|
+
* This method fetches the product configuration from /v3/config API
|
|
3321
|
+
* and extracts the models array from the response.
|
|
3322
|
+
*
|
|
3323
|
+
* @param repo - Optional repository URL for context-specific config
|
|
3324
|
+
* @returns Array of ModelInfo with full model details
|
|
2460
3325
|
*/
|
|
2461
|
-
async
|
|
2462
|
-
this.log("Triggering logout via IPC");
|
|
3326
|
+
async getModels(repo) {
|
|
2463
3327
|
try {
|
|
2464
|
-
|
|
3328
|
+
let url = `${this.options.endpoint}/v3/config`;
|
|
3329
|
+
if (repo) url += `?repos[]=${encodeURIComponent(repo)}`;
|
|
3330
|
+
const headers = {
|
|
3331
|
+
"Accept": "application/json, text/plain, */*",
|
|
3332
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
3333
|
+
...this.options.headers
|
|
3334
|
+
};
|
|
3335
|
+
if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
3336
|
+
const account = accountService.getAccount();
|
|
3337
|
+
if (account?.uid) headers["X-User-Id"] = account.uid;
|
|
3338
|
+
if (account?.enterpriseId) {
|
|
3339
|
+
headers["X-Enterprise-Id"] = account.enterpriseId;
|
|
3340
|
+
headers["X-Tenant-Id"] = account.enterpriseId;
|
|
3341
|
+
}
|
|
3342
|
+
headers["X-Product"] = "SaaS";
|
|
3343
|
+
headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
|
|
3344
|
+
headers["X-Request-ID"] = this.generateRequestId();
|
|
3345
|
+
this.logger?.debug(`[CloudAgentProvider] GET ${url}`);
|
|
3346
|
+
const response = await this.fetchImpl(url, {
|
|
3347
|
+
method: "GET",
|
|
3348
|
+
headers,
|
|
3349
|
+
credentials: "include"
|
|
3350
|
+
});
|
|
3351
|
+
if (!response.ok) throw new Error(`Failed to get config: ${response.statusText}`);
|
|
3352
|
+
const apiResponse = await response.json();
|
|
3353
|
+
if (!apiResponse.data) {
|
|
3354
|
+
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
3355
|
+
return [];
|
|
3356
|
+
}
|
|
3357
|
+
const models = apiResponse.data.models ?? [];
|
|
3358
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /v3/config`);
|
|
3359
|
+
return models.map((model) => ({
|
|
3360
|
+
id: model.id,
|
|
3361
|
+
name: model.name ?? model.id,
|
|
3362
|
+
description: model.description,
|
|
3363
|
+
credits: model.credits,
|
|
3364
|
+
configurable: model.configurable,
|
|
3365
|
+
configured: model.configured,
|
|
3366
|
+
isDefault: model.isDefault,
|
|
3367
|
+
supportsImages: model.supportsImages,
|
|
3368
|
+
supportsReasoning: model.supportsReasoning,
|
|
3369
|
+
onlyReasoning: model.onlyReasoning,
|
|
3370
|
+
disabledMultimodal: model.disabledMultimodal,
|
|
3371
|
+
disabled: model.disabled,
|
|
3372
|
+
disabledReason: model.disabledReason,
|
|
3373
|
+
disabledAction: model.disabledAction
|
|
3374
|
+
}));
|
|
2465
3375
|
} catch (error) {
|
|
2466
|
-
this.
|
|
3376
|
+
this.logger?.error(`[CloudAgentProvider] Failed to get models:`, error);
|
|
2467
3377
|
throw error;
|
|
2468
3378
|
}
|
|
2469
3379
|
}
|
|
2470
3380
|
/**
|
|
2471
|
-
*
|
|
2472
|
-
* 用于监听账户变化等事件
|
|
3381
|
+
* Generate a unique request ID
|
|
2473
3382
|
*/
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
3383
|
+
generateRequestId() {
|
|
3384
|
+
return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/x/g, () => Math.floor(Math.random() * 16).toString(16));
|
|
3385
|
+
}
|
|
3386
|
+
static {
|
|
3387
|
+
this.IMAGE_MIME_TYPES = [
|
|
3388
|
+
"image/png",
|
|
3389
|
+
"image/jpeg",
|
|
3390
|
+
"image/jpg",
|
|
3391
|
+
"image/gif",
|
|
3392
|
+
"image/webp",
|
|
3393
|
+
"image/svg+xml",
|
|
3394
|
+
"image/bmp"
|
|
3395
|
+
];
|
|
2480
3396
|
}
|
|
2481
3397
|
/**
|
|
2482
|
-
*
|
|
3398
|
+
* Pick files using browser's native file input
|
|
3399
|
+
*
|
|
3400
|
+
* @param params - File picker parameters
|
|
3401
|
+
* @returns Response with selected file paths (filenames in browser)
|
|
2483
3402
|
*/
|
|
2484
|
-
|
|
2485
|
-
|
|
3403
|
+
async pickFile(params) {
|
|
3404
|
+
return new Promise((resolve) => {
|
|
3405
|
+
const input = document.createElement("input");
|
|
3406
|
+
input.type = "file";
|
|
3407
|
+
input.style.display = "none";
|
|
3408
|
+
if (params?.filters && params.filters.length > 0) {
|
|
3409
|
+
const acceptTypes = [];
|
|
3410
|
+
for (const filter of params.filters) for (const ext of filter.extensions) {
|
|
3411
|
+
const mimeType = this.extensionToMimeType(ext);
|
|
3412
|
+
acceptTypes.push(mimeType || `.${ext}`);
|
|
3413
|
+
}
|
|
3414
|
+
input.accept = acceptTypes.join(",");
|
|
3415
|
+
} else input.accept = CloudAgentProvider.IMAGE_MIME_TYPES.join(",");
|
|
3416
|
+
input.multiple = params?.canSelectMany ?? false;
|
|
3417
|
+
input.onchange = () => {
|
|
3418
|
+
const files = input.files;
|
|
3419
|
+
if (!files || files.length === 0) resolve({
|
|
3420
|
+
files: [],
|
|
3421
|
+
canceled: true
|
|
3422
|
+
});
|
|
3423
|
+
else {
|
|
3424
|
+
const fileArray = Array.from(files);
|
|
3425
|
+
this.logger?.info(`Picked ${fileArray.length} file(s)`);
|
|
3426
|
+
resolve({
|
|
3427
|
+
files: fileArray,
|
|
3428
|
+
canceled: false
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
document.body.removeChild(input);
|
|
3432
|
+
};
|
|
3433
|
+
input.oncancel = () => {
|
|
3434
|
+
resolve({
|
|
3435
|
+
files: [],
|
|
3436
|
+
canceled: true
|
|
3437
|
+
});
|
|
3438
|
+
document.body.removeChild(input);
|
|
3439
|
+
};
|
|
3440
|
+
document.body.appendChild(input);
|
|
3441
|
+
input.click();
|
|
3442
|
+
});
|
|
2486
3443
|
}
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
*
|
|
2505
|
-
*
|
|
2506
|
-
*
|
|
2507
|
-
*
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
*
|
|
2526
|
-
*
|
|
2527
|
-
*
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
3444
|
+
/**
|
|
3445
|
+
* Convert file extension to MIME type
|
|
3446
|
+
*/
|
|
3447
|
+
extensionToMimeType(ext) {
|
|
3448
|
+
const extLower = ext.toLowerCase().replace(/^\./, "");
|
|
3449
|
+
return {
|
|
3450
|
+
"png": "image/png",
|
|
3451
|
+
"jpg": "image/jpeg",
|
|
3452
|
+
"jpeg": "image/jpeg",
|
|
3453
|
+
"gif": "image/gif",
|
|
3454
|
+
"webp": "image/webp",
|
|
3455
|
+
"svg": "image/svg+xml",
|
|
3456
|
+
"bmp": "image/bmp",
|
|
3457
|
+
"ico": "image/x-icon"
|
|
3458
|
+
}[extLower] || null;
|
|
3459
|
+
}
|
|
3460
|
+
/**
|
|
3461
|
+
* Upload files to cloud storage via COS presigned URL
|
|
3462
|
+
*
|
|
3463
|
+
* @param params - files array (File objects in browser)
|
|
3464
|
+
* @returns Response with corresponding cloud URLs
|
|
3465
|
+
*/
|
|
3466
|
+
async uploadFile(params) {
|
|
3467
|
+
this.logger?.info(`[CloudAgentProvider] uploadFile called for ${params.files.length} file(s)`);
|
|
3468
|
+
const files = params.files.filter((f) => typeof f !== "string");
|
|
3469
|
+
if (files.length === 0) return {
|
|
3470
|
+
success: false,
|
|
3471
|
+
error: "No valid File objects provided"
|
|
3472
|
+
};
|
|
3473
|
+
const result = await this.cosUploadService.uploadFiles(files);
|
|
3474
|
+
return {
|
|
3475
|
+
success: result.success,
|
|
3476
|
+
urls: result.urls,
|
|
3477
|
+
expireSeconds: result.expireSeconds,
|
|
3478
|
+
error: result.error
|
|
3479
|
+
};
|
|
3480
|
+
}
|
|
3481
|
+
/**
|
|
3482
|
+
* Register event listener
|
|
3483
|
+
* @param event - Event name
|
|
3484
|
+
* @param handler - Event handler function
|
|
3485
|
+
*/
|
|
3486
|
+
on(event, handler) {
|
|
3487
|
+
if (!this.eventListeners.has(event)) this.eventListeners.set(event, /* @__PURE__ */ new Set());
|
|
3488
|
+
this.eventListeners.get(event).add(handler);
|
|
3489
|
+
return () => {
|
|
3490
|
+
this.off(event, handler);
|
|
3491
|
+
};
|
|
3492
|
+
}
|
|
3493
|
+
/**
|
|
3494
|
+
* Unregister event listener
|
|
3495
|
+
* @param event - Event name
|
|
3496
|
+
* @param handler - Event handler function
|
|
3497
|
+
*/
|
|
3498
|
+
off(event, handler) {
|
|
3499
|
+
const listeners = this.eventListeners.get(event);
|
|
3500
|
+
if (listeners) listeners.delete(handler);
|
|
3501
|
+
}
|
|
3502
|
+
/**
|
|
3503
|
+
* Emit event to all registered listeners
|
|
3504
|
+
* @param event - Event name
|
|
3505
|
+
* @param args - Event arguments
|
|
3506
|
+
*/
|
|
3507
|
+
emitEvent(event, ...args) {
|
|
3508
|
+
const listeners = this.eventListeners.get(event);
|
|
3509
|
+
if (listeners && listeners.size > 0) {
|
|
3510
|
+
this.logger?.debug(`Emitting event: ${event}`, args);
|
|
3511
|
+
for (const handler of listeners) try {
|
|
3512
|
+
handler(...args);
|
|
3513
|
+
} catch (error) {
|
|
3514
|
+
this.logger?.error(`Error in event handler for ${event}:`, error);
|
|
3515
|
+
}
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
toAgentState(data) {
|
|
3519
|
+
const status = data.sessionStatus || data.status;
|
|
3520
|
+
return {
|
|
3521
|
+
id: data.id,
|
|
3522
|
+
name: data.name,
|
|
3523
|
+
description: data.summary,
|
|
3524
|
+
type: "cloud",
|
|
3525
|
+
status,
|
|
3526
|
+
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
3527
|
+
capabilities: this.options.clientCapabilities
|
|
3528
|
+
};
|
|
3529
|
+
}
|
|
3530
|
+
async request(method, path, body) {
|
|
3531
|
+
let url = `${this.options.endpoint}${path}`;
|
|
3532
|
+
const headers = {
|
|
3533
|
+
"Content-Type": "application/json",
|
|
3534
|
+
...this.options.headers
|
|
3535
|
+
};
|
|
3536
|
+
if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
3537
|
+
const account = accountService.getAccount();
|
|
3538
|
+
if (account?.uid) headers["X-User-Id"] = account.uid;
|
|
3539
|
+
if (account?.enterpriseId) {
|
|
3540
|
+
headers["X-Enterprise-Id"] = account.enterpriseId;
|
|
3541
|
+
headers["X-Tenant-Id"] = account.enterpriseId;
|
|
3542
|
+
}
|
|
3543
|
+
headers["X-Product"] = "SaaS";
|
|
3544
|
+
headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
|
|
3545
|
+
const init = {
|
|
3546
|
+
method,
|
|
3547
|
+
headers
|
|
3548
|
+
};
|
|
3549
|
+
if (method === "GET" && body !== void 0) {
|
|
3550
|
+
const params = new URLSearchParams();
|
|
3551
|
+
for (const [key, value] of Object.entries(body)) if (value !== void 0 && value !== null) {
|
|
3552
|
+
const stringValue = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
3553
|
+
params.append(key, stringValue);
|
|
3554
|
+
}
|
|
3555
|
+
const queryString = params.toString();
|
|
3556
|
+
if (queryString) url += `?${queryString}`;
|
|
3557
|
+
} else if (body !== void 0) init.body = JSON.stringify(body);
|
|
3558
|
+
this.logger?.debug(`${method} ${url}`);
|
|
3559
|
+
return this.fetchImpl(url, init);
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
|
|
3563
|
+
//#endregion
|
|
3564
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
|
|
3565
|
+
/**
|
|
3566
|
+
* Local Agent Connection
|
|
3567
|
+
* Wraps AcpJsonRpcClient to implement AgentConnection interface
|
|
2534
3568
|
*
|
|
2535
|
-
*
|
|
2536
|
-
*
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
3569
|
+
* Uses IWidgetChannel for IPC communication with ExtensionHost
|
|
3570
|
+
* Migrated from ipc-agent-provider for unified local agent access
|
|
3571
|
+
*/
|
|
3572
|
+
|
|
3573
|
+
//#endregion
|
|
3574
|
+
//#region ../agent-provider/src/common/client/session.ts
|
|
3575
|
+
/**
|
|
3576
|
+
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
2542
3577
|
*
|
|
2543
|
-
*
|
|
2544
|
-
*
|
|
2545
|
-
* dayRange: 14,
|
|
2546
|
-
* filters: [
|
|
2547
|
-
* { field: 'status', value: 'running,stopped' }
|
|
2548
|
-
* ],
|
|
2549
|
-
* page: 1,
|
|
2550
|
-
* size: 20
|
|
2551
|
-
* });
|
|
3578
|
+
* This class wraps an AgentConnection and provides the session-centric API.
|
|
3579
|
+
* It is created by SessionManager when creating or loading sessions.
|
|
2552
3580
|
*
|
|
2553
|
-
*
|
|
2554
|
-
*
|
|
3581
|
+
* @example
|
|
3582
|
+
* ```typescript
|
|
3583
|
+
* // Created by client.sessions.new() or client.sessions.load()
|
|
3584
|
+
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
2555
3585
|
*
|
|
2556
|
-
* //
|
|
2557
|
-
*
|
|
3586
|
+
* // Access agent state
|
|
3587
|
+
* console.log(session.agentState.status);
|
|
2558
3588
|
*
|
|
2559
|
-
* //
|
|
2560
|
-
* const
|
|
3589
|
+
* // Send prompt
|
|
3590
|
+
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
2561
3591
|
*
|
|
2562
|
-
* //
|
|
2563
|
-
*
|
|
3592
|
+
* // Cleanup
|
|
3593
|
+
* session.disconnect();
|
|
2564
3594
|
* ```
|
|
2565
3595
|
*/
|
|
2566
|
-
var
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
3596
|
+
var ActiveSessionImpl = class {
|
|
3597
|
+
/**
|
|
3598
|
+
* Create an ActiveSessionImpl instance
|
|
3599
|
+
*
|
|
3600
|
+
* @param sessionId - Session ID
|
|
3601
|
+
* @param agentId - Agent ID
|
|
3602
|
+
* @param connection - Already connected AgentConnection
|
|
3603
|
+
* @param options - Additional options
|
|
3604
|
+
*/
|
|
3605
|
+
constructor(sessionId, agentId, connection, options = {}) {
|
|
3606
|
+
this._availableCommands = [];
|
|
3607
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
3608
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
3609
|
+
this._id = sessionId;
|
|
3610
|
+
this._agentId = agentId;
|
|
3611
|
+
this.connection = connection;
|
|
2571
3612
|
this.logger = options.logger;
|
|
2572
|
-
this.
|
|
2573
|
-
this.
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
3613
|
+
this._getFilesystem = options.getFilesystem;
|
|
3614
|
+
this._connectionInfo = options.connectionInfo;
|
|
3615
|
+
this.setupConnectionEvents(connection);
|
|
3616
|
+
this.agent = this.createAgentOperations();
|
|
3617
|
+
this.prompts = this.createPromptsResource();
|
|
3618
|
+
this.artifacts = this.createArtifactsResource();
|
|
3619
|
+
this.files = this.createFilesResource();
|
|
2578
3620
|
}
|
|
2579
3621
|
/**
|
|
2580
|
-
*
|
|
3622
|
+
* Session ID
|
|
2581
3623
|
*/
|
|
2582
|
-
get
|
|
2583
|
-
return this;
|
|
3624
|
+
get id() {
|
|
3625
|
+
return this._id;
|
|
2584
3626
|
}
|
|
2585
3627
|
/**
|
|
2586
|
-
*
|
|
2587
|
-
*
|
|
2588
|
-
* Creates or returns cached E2BFilesystem instance for the agent's sandbox.
|
|
2589
|
-
*
|
|
2590
|
-
* @param agentId - Agent ID to get filesystem for
|
|
2591
|
-
* @returns FilesResource instance for the agent's sandbox
|
|
3628
|
+
* Agent ID
|
|
2592
3629
|
*/
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
if (cached) return cached;
|
|
2596
|
-
const info = await this.getSandboxInfo(agentId);
|
|
2597
|
-
const filesystem = await E2BFilesystem.connect(info);
|
|
2598
|
-
this.filesystemCache.set(agentId, filesystem);
|
|
2599
|
-
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
2600
|
-
return filesystem;
|
|
3630
|
+
get agentId() {
|
|
3631
|
+
return this._agentId;
|
|
2601
3632
|
}
|
|
2602
3633
|
/**
|
|
2603
|
-
*
|
|
2604
|
-
*
|
|
2605
|
-
* Uses GET {endpoint}/console/cloudagent/agentmgmt/agents/{agentId}/session
|
|
2606
|
-
* to retrieve sandbox information. Extracts sandboxId from the session response
|
|
2607
|
-
* and constructs the apiUrl for E2B proxy.
|
|
2608
|
-
*
|
|
2609
|
-
* @param agentId - Agent ID
|
|
2610
|
-
* @returns E2B Sandbox connection information with sandboxId and apiUrl
|
|
3634
|
+
* Agent state (live connection state)
|
|
3635
|
+
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
2611
3636
|
*/
|
|
2612
|
-
|
|
2613
|
-
const response = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
|
|
2614
|
-
if (!response.ok) throw new Error(`Failed to get sandbox info: ${response.statusText}`);
|
|
2615
|
-
const apiResponse = await response.json();
|
|
2616
|
-
if (!apiResponse.data) throw new Error("No data in API response");
|
|
2617
|
-
const apiUrl = new URL(`/console/cloudagent/e2bproxy/agents/${agentId}`, this.options.endpoint).toString();
|
|
2618
|
-
const currentEnterpriseId = localStorage.getItem("currentEnterpriseId");
|
|
3637
|
+
get agentState() {
|
|
2619
3638
|
return {
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
3639
|
+
id: this._agentId,
|
|
3640
|
+
status: this.connection.state,
|
|
3641
|
+
capabilities: this.connection.capabilities,
|
|
3642
|
+
type: this.connection.transport,
|
|
3643
|
+
cwd: this.connection.cwd || ""
|
|
2625
3644
|
};
|
|
2626
3645
|
}
|
|
2627
3646
|
/**
|
|
2628
|
-
* Get agent
|
|
3647
|
+
* Get agent capabilities (available after connection)
|
|
2629
3648
|
*/
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
const response = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}`);
|
|
2633
|
-
if (response.status === 404) return;
|
|
2634
|
-
if (!response.ok) throw new Error(`Failed to get agent: ${response.statusText}`);
|
|
2635
|
-
const apiResponse = await response.json();
|
|
2636
|
-
if (!apiResponse.data) throw new Error("No data in API response");
|
|
2637
|
-
return this.toAgentState(apiResponse.data);
|
|
2638
|
-
} catch (error) {
|
|
2639
|
-
this.logger?.error(`Failed to get agent ${agentId}:`, error);
|
|
2640
|
-
throw error;
|
|
2641
|
-
}
|
|
3649
|
+
get capabilities() {
|
|
3650
|
+
return this.connection.capabilities;
|
|
2642
3651
|
}
|
|
2643
3652
|
/**
|
|
2644
|
-
*
|
|
2645
|
-
*
|
|
2646
|
-
* @param options - Optional query parameters for filtering, sorting, and pagination
|
|
2647
|
-
* @returns Object containing agents array and pagination info
|
|
3653
|
+
* Available session modes
|
|
2648
3654
|
*/
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
console.log("[CloudAgentProvider] list called with options:", JSON.stringify(options, null, 2));
|
|
2652
|
-
const params = {
|
|
2653
|
-
page: 1,
|
|
2654
|
-
size: 30,
|
|
2655
|
-
sort: {
|
|
2656
|
-
order: "desc",
|
|
2657
|
-
orderBy: "status"
|
|
2658
|
-
},
|
|
2659
|
-
...options && {
|
|
2660
|
-
...options.dayRange !== void 0 && { dayRange: options.dayRange },
|
|
2661
|
-
...options.page !== void 0 && { page: options.page },
|
|
2662
|
-
...options.size !== void 0 && { size: options.size },
|
|
2663
|
-
...options.sort !== void 0 && { sort: options.sort },
|
|
2664
|
-
...options.filters !== void 0 && { filters: options.filters },
|
|
2665
|
-
...options.title !== void 0 && { title: options.title }
|
|
2666
|
-
}
|
|
2667
|
-
};
|
|
2668
|
-
console.log("[CloudAgentProvider] API request params:", JSON.stringify(params, null, 2));
|
|
2669
|
-
const response = await this.request("GET", "/console/cloudagent/agentmgmt/agents", params);
|
|
2670
|
-
if (!response.ok) throw new Error(`Failed to list agents: ${response.statusText}`);
|
|
2671
|
-
const apiResponse = await response.json();
|
|
2672
|
-
if (!apiResponse.data) throw new Error("No data in API response");
|
|
2673
|
-
const agents = apiResponse.data.agents.map((a) => this.toAgentState(a));
|
|
2674
|
-
const pagination = apiResponse.data.pagination;
|
|
2675
|
-
console.log("[CloudAgentProvider] API response:", {
|
|
2676
|
-
agentsCount: agents.length,
|
|
2677
|
-
pagination
|
|
2678
|
-
});
|
|
2679
|
-
return {
|
|
2680
|
-
agents,
|
|
2681
|
-
pagination
|
|
2682
|
-
};
|
|
2683
|
-
} catch (error) {
|
|
2684
|
-
this.logger?.error("Failed to list agents:", error);
|
|
2685
|
-
throw error;
|
|
2686
|
-
}
|
|
3655
|
+
get availableModes() {
|
|
3656
|
+
return this._availableModes;
|
|
2687
3657
|
}
|
|
2688
3658
|
/**
|
|
2689
|
-
*
|
|
2690
|
-
* POST {endpoint}/console/cloudagent/agentmgmt/agents
|
|
3659
|
+
* Current session mode
|
|
2691
3660
|
*/
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
const response = await this.request("POST", "/console/cloudagent/agentmgmt/agents", {
|
|
2695
|
-
prompt: "",
|
|
2696
|
-
model: "deepseek-r1"
|
|
2697
|
-
});
|
|
2698
|
-
if (!response.ok) throw new Error(`Failed to create agent: ${response.statusText}`);
|
|
2699
|
-
const apiResponse = await response.json();
|
|
2700
|
-
if (!apiResponse.data) throw new Error("No data in API response");
|
|
2701
|
-
this.logger?.info(`Created agent: ${apiResponse.data.id}`);
|
|
2702
|
-
return apiResponse.data.id;
|
|
2703
|
-
} catch (error) {
|
|
2704
|
-
this.logger?.error("Failed to create agent:", error);
|
|
2705
|
-
throw error;
|
|
2706
|
-
}
|
|
3661
|
+
get currentMode() {
|
|
3662
|
+
return this._currentMode;
|
|
2707
3663
|
}
|
|
2708
3664
|
/**
|
|
2709
|
-
*
|
|
2710
|
-
*
|
|
2711
|
-
* This method:
|
|
2712
|
-
* 1. Fetches the agent configuration from the backend
|
|
2713
|
-
* 2. Checks if an existing connection can be reused (based on endpoint link)
|
|
2714
|
-
* 3. Creates a CloudAgentConnection to the agent's endpoint if not cached
|
|
2715
|
-
* 4. Saves session connection info to the connection
|
|
2716
|
-
* 5. Connects and initializes the connection
|
|
2717
|
-
* 6. Returns the connected CloudAgentConnection
|
|
3665
|
+
* Available slash commands
|
|
2718
3666
|
*
|
|
2719
|
-
*
|
|
2720
|
-
*
|
|
2721
|
-
* - CloudAgentConnection is responsible for handling connection health checks
|
|
2722
|
-
* and token expiration internally
|
|
3667
|
+
* When Agent sends available_commands_update, this list is automatically updated.
|
|
3668
|
+
* Commands can be accessed directly without waiting for events.
|
|
2723
3669
|
*/
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
if (response.status === 404) throw new Error(`Agent not found: ${agentId}`);
|
|
2727
|
-
if (!response.ok) throw new Error(`Failed to get agent for connection: ${response.statusText}`);
|
|
2728
|
-
const apiResponse = await response.json();
|
|
2729
|
-
if (!apiResponse.data) throw new Error("No data in API response");
|
|
2730
|
-
const sessionData = apiResponse.data;
|
|
2731
|
-
const endpoint = sessionData.link.replace(/^http:\/\//, "https://");
|
|
2732
|
-
const existingConnection = this.connectionCache.get(endpoint);
|
|
2733
|
-
if (existingConnection?.isInitialized) {
|
|
2734
|
-
this.logger?.info(`Reusing existing connection for agent: ${agentId}`);
|
|
2735
|
-
return existingConnection;
|
|
2736
|
-
}
|
|
2737
|
-
const connection = new CloudAgentConnection(agentId, {
|
|
2738
|
-
endpoint,
|
|
2739
|
-
authToken: sessionData.token,
|
|
2740
|
-
logger: this.logger,
|
|
2741
|
-
clientCapabilities: this.options.clientCapabilities
|
|
2742
|
-
});
|
|
2743
|
-
connection.setSessionConnectionInfo({
|
|
2744
|
-
sessionId: sessionData.sessionId,
|
|
2745
|
-
agentId: sessionData.id,
|
|
2746
|
-
link: sessionData.link,
|
|
2747
|
-
token: sessionData.token,
|
|
2748
|
-
sandboxId: sessionData.sandboxId,
|
|
2749
|
-
expireAt: sessionData.expireAt
|
|
2750
|
-
});
|
|
2751
|
-
try {
|
|
2752
|
-
await connection.connect();
|
|
2753
|
-
} catch (error) {
|
|
2754
|
-
this.logger?.error(`Failed to connect to agent ${agentId}:`, error);
|
|
2755
|
-
throw error;
|
|
2756
|
-
}
|
|
2757
|
-
this.connectionCache.set(endpoint, connection);
|
|
2758
|
-
connection.once("disconnected", () => {
|
|
2759
|
-
this.connectionCache.delete(endpoint);
|
|
2760
|
-
this.logger?.debug(`Connection removed from cache: ${endpoint}`);
|
|
2761
|
-
});
|
|
2762
|
-
this.logger?.info(`Connected to agent: ${agentId}`);
|
|
2763
|
-
return connection;
|
|
3670
|
+
get availableCommands() {
|
|
3671
|
+
return this._availableCommands;
|
|
2764
3672
|
}
|
|
2765
3673
|
/**
|
|
2766
|
-
*
|
|
2767
|
-
* POST {endpoint}/console/cloudagent/agentmgmt/agents/{agentId}/delete
|
|
3674
|
+
* Check if the session is active
|
|
2768
3675
|
*/
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
const requestBody = { id: agentId };
|
|
2772
|
-
const response = await this.request("POST", `/console/cloudagent/agentmgmt/agents/${agentId}/delete`, requestBody);
|
|
2773
|
-
if (response.status === 404) return false;
|
|
2774
|
-
if (!response.ok) throw new Error(`Failed to delete agent: ${response.statusText}`);
|
|
2775
|
-
return true;
|
|
2776
|
-
} catch (error) {
|
|
2777
|
-
this.logger?.error(`Failed to delete agent ${agentId}:`, error);
|
|
2778
|
-
throw error;
|
|
2779
|
-
}
|
|
3676
|
+
get isActive() {
|
|
3677
|
+
return this.connection.isInitialized;
|
|
2780
3678
|
}
|
|
2781
3679
|
/**
|
|
2782
|
-
*
|
|
2783
|
-
*
|
|
2784
|
-
*
|
|
2785
|
-
* @param agentId - Agent ID to archive
|
|
2786
|
-
* @returns ArchiveAgentResponse containing the archived agent ID
|
|
2787
|
-
*
|
|
2788
|
-
* @example
|
|
2789
|
-
* ```typescript
|
|
2790
|
-
* const result = await provider.archive('agent-123');
|
|
2791
|
-
* console.log('Archived agent:', result.id);
|
|
2792
|
-
* ```
|
|
3680
|
+
* Session connection information (only available for cloud sessions)
|
|
3681
|
+
* 会话连接信息,包括sandboxId、link、token等
|
|
2793
3682
|
*/
|
|
2794
|
-
|
|
2795
|
-
|
|
2796
|
-
const response = await this.request("POST", `/console/cloudagent/agentmgmt/agents/${agentId}/archive`);
|
|
2797
|
-
if (!response.ok) throw new Error(`Failed to archive agent: ${response.statusText}`);
|
|
2798
|
-
const apiResponse = await response.json();
|
|
2799
|
-
if (!apiResponse.data) {
|
|
2800
|
-
this.logger?.info(`Archived agent: ${agentId}`);
|
|
2801
|
-
return { id: agentId };
|
|
2802
|
-
}
|
|
2803
|
-
this.logger?.info(`Archived agent: ${apiResponse.data.id}`);
|
|
2804
|
-
return apiResponse.data;
|
|
2805
|
-
} catch (error) {
|
|
2806
|
-
this.logger?.error(`Failed to archive agent ${agentId}:`, error);
|
|
2807
|
-
throw error;
|
|
2808
|
-
}
|
|
3683
|
+
get connectionInfo() {
|
|
3684
|
+
return this._connectionInfo;
|
|
2809
3685
|
}
|
|
2810
3686
|
/**
|
|
2811
|
-
*
|
|
2812
|
-
* PATCH {endpoint}/v2/cloudagent/agentmgmt/agents/{agentId}
|
|
2813
|
-
*
|
|
2814
|
-
* @param agentId - Agent ID to rename
|
|
2815
|
-
* @param title - New title for the agent
|
|
2816
|
-
* @returns RenameAgentResponse containing the renamed agent ID
|
|
2817
|
-
*
|
|
2818
|
-
* @example
|
|
2819
|
-
* ```typescript
|
|
2820
|
-
* const result = await provider.rename('agent-123', 'New Title');
|
|
2821
|
-
* console.log('Renamed agent:', result.id);
|
|
2822
|
-
* ```
|
|
3687
|
+
* Set session modes (called after create/load)
|
|
2823
3688
|
*/
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
return
|
|
3689
|
+
setModes(availableModes, currentMode) {
|
|
3690
|
+
this._availableModes = availableModes;
|
|
3691
|
+
this._currentMode = currentMode;
|
|
3692
|
+
}
|
|
3693
|
+
createAgentOperations() {
|
|
3694
|
+
const self = this;
|
|
3695
|
+
return {
|
|
3696
|
+
get id() {
|
|
3697
|
+
return self._agentId;
|
|
3698
|
+
},
|
|
3699
|
+
get state() {
|
|
3700
|
+
return self.agentState;
|
|
3701
|
+
},
|
|
3702
|
+
get isConnected() {
|
|
3703
|
+
return self.connection.isInitialized;
|
|
3704
|
+
},
|
|
3705
|
+
get capabilities() {
|
|
3706
|
+
return self.connection.capabilities;
|
|
2833
3707
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
3708
|
+
};
|
|
3709
|
+
}
|
|
3710
|
+
createPromptsResource() {
|
|
3711
|
+
return {
|
|
3712
|
+
send: async (params) => {
|
|
3713
|
+
const response = await this.getConnectionOrThrow().prompt(this._id, params);
|
|
3714
|
+
return this.mapPromptResponse(response);
|
|
3715
|
+
},
|
|
3716
|
+
stream: (params) => {
|
|
3717
|
+
return this.getConnectionOrThrow().promptStream(this._id, params);
|
|
3718
|
+
},
|
|
3719
|
+
cancel: async () => {
|
|
3720
|
+
await this.getConnectionOrThrow().cancel(this._id);
|
|
3721
|
+
}
|
|
3722
|
+
};
|
|
3723
|
+
}
|
|
3724
|
+
createArtifactsResource() {
|
|
3725
|
+
const notSupported = () => {
|
|
3726
|
+
throw new Error("Artifact management is no longer supported through this API");
|
|
3727
|
+
};
|
|
3728
|
+
return {
|
|
3729
|
+
list: async (_params) => {
|
|
3730
|
+
notSupported();
|
|
3731
|
+
return [];
|
|
3732
|
+
},
|
|
3733
|
+
retrieve: async (_artifactId) => {
|
|
3734
|
+
notSupported();
|
|
3735
|
+
},
|
|
3736
|
+
content: async (_artifactId) => {
|
|
3737
|
+
notSupported();
|
|
3738
|
+
return "";
|
|
3739
|
+
}
|
|
3740
|
+
};
|
|
2840
3741
|
}
|
|
2841
3742
|
/**
|
|
2842
|
-
*
|
|
2843
|
-
*
|
|
2844
|
-
* GET {endpoint}/console/cloudagent/agentmgmt/models?repo={repo}
|
|
3743
|
+
* Create files resource with lazy-loaded filesystem
|
|
2845
3744
|
*
|
|
2846
|
-
*
|
|
2847
|
-
*
|
|
3745
|
+
* The filesystem is lazily loaded on first use to avoid unnecessary
|
|
3746
|
+
* connections to the sandbox. The actual filesystem instance is obtained
|
|
3747
|
+
* via the getter function provided by SessionManager.
|
|
2848
3748
|
*/
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
name: "",
|
|
2860
|
-
description: ""
|
|
2861
|
-
}));
|
|
2862
|
-
} catch (error) {
|
|
2863
|
-
this.logger?.error(`Failed to get models for repo ${repo}:`, error);
|
|
2864
|
-
throw error;
|
|
2865
|
-
}
|
|
2866
|
-
}
|
|
2867
|
-
static {
|
|
2868
|
-
this.IMAGE_MIME_TYPES = [
|
|
2869
|
-
"image/png",
|
|
2870
|
-
"image/jpeg",
|
|
2871
|
-
"image/jpg",
|
|
2872
|
-
"image/gif",
|
|
2873
|
-
"image/webp",
|
|
2874
|
-
"image/svg+xml",
|
|
2875
|
-
"image/bmp"
|
|
2876
|
-
];
|
|
2877
|
-
}
|
|
2878
|
-
/**
|
|
2879
|
-
* Pick files using browser's native file input
|
|
2880
|
-
*
|
|
2881
|
-
* @param params - File picker parameters
|
|
2882
|
-
* @returns Response with selected file paths (filenames in browser)
|
|
2883
|
-
*/
|
|
2884
|
-
async pickFile(params) {
|
|
2885
|
-
return new Promise((resolve) => {
|
|
2886
|
-
const input = document.createElement("input");
|
|
2887
|
-
input.type = "file";
|
|
2888
|
-
input.style.display = "none";
|
|
2889
|
-
if (params?.filters && params.filters.length > 0) {
|
|
2890
|
-
const acceptTypes = [];
|
|
2891
|
-
for (const filter of params.filters) for (const ext of filter.extensions) {
|
|
2892
|
-
const mimeType = this.extensionToMimeType(ext);
|
|
2893
|
-
acceptTypes.push(mimeType || `.${ext}`);
|
|
2894
|
-
}
|
|
2895
|
-
input.accept = acceptTypes.join(",");
|
|
2896
|
-
} else input.accept = CloudAgentProvider.IMAGE_MIME_TYPES.join(",");
|
|
2897
|
-
input.multiple = params?.canSelectMany ?? false;
|
|
2898
|
-
input.onchange = () => {
|
|
2899
|
-
const files = input.files;
|
|
2900
|
-
if (!files || files.length === 0) resolve({
|
|
2901
|
-
files: [],
|
|
2902
|
-
canceled: true
|
|
2903
|
-
});
|
|
2904
|
-
else {
|
|
2905
|
-
const fileArray = Array.from(files);
|
|
2906
|
-
this.logger?.info(`Picked ${fileArray.length} file(s)`);
|
|
2907
|
-
resolve({
|
|
2908
|
-
files: fileArray,
|
|
2909
|
-
canceled: false
|
|
2910
|
-
});
|
|
2911
|
-
}
|
|
2912
|
-
document.body.removeChild(input);
|
|
2913
|
-
};
|
|
2914
|
-
input.oncancel = () => {
|
|
2915
|
-
resolve({
|
|
2916
|
-
files: [],
|
|
2917
|
-
canceled: true
|
|
2918
|
-
});
|
|
2919
|
-
document.body.removeChild(input);
|
|
2920
|
-
};
|
|
2921
|
-
document.body.appendChild(input);
|
|
2922
|
-
input.click();
|
|
2923
|
-
});
|
|
2924
|
-
}
|
|
2925
|
-
/**
|
|
2926
|
-
* Convert file extension to MIME type
|
|
2927
|
-
*/
|
|
2928
|
-
extensionToMimeType(ext) {
|
|
2929
|
-
const extLower = ext.toLowerCase().replace(/^\./, "");
|
|
2930
|
-
return {
|
|
2931
|
-
"png": "image/png",
|
|
2932
|
-
"jpg": "image/jpeg",
|
|
2933
|
-
"jpeg": "image/jpeg",
|
|
2934
|
-
"gif": "image/gif",
|
|
2935
|
-
"webp": "image/webp",
|
|
2936
|
-
"svg": "image/svg+xml",
|
|
2937
|
-
"bmp": "image/bmp",
|
|
2938
|
-
"ico": "image/x-icon"
|
|
2939
|
-
}[extLower] || null;
|
|
2940
|
-
}
|
|
2941
|
-
/**
|
|
2942
|
-
* Upload files to cloud storage via COS presigned URL
|
|
2943
|
-
*
|
|
2944
|
-
* @param params - files array (File objects in browser)
|
|
2945
|
-
* @returns Response with corresponding cloud URLs
|
|
2946
|
-
*/
|
|
2947
|
-
async uploadFile(params) {
|
|
2948
|
-
this.logger?.info(`[CloudAgentProvider] uploadFile called for ${params.files.length} file(s)`);
|
|
2949
|
-
const files = params.files.filter((f) => typeof f !== "string");
|
|
2950
|
-
if (files.length === 0) return {
|
|
2951
|
-
success: false,
|
|
2952
|
-
error: "No valid File objects provided"
|
|
2953
|
-
};
|
|
2954
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
2955
|
-
return {
|
|
2956
|
-
success: result.success,
|
|
2957
|
-
urls: result.urls,
|
|
2958
|
-
error: result.error
|
|
3749
|
+
createFilesResource() {
|
|
3750
|
+
const self = this;
|
|
3751
|
+
let filesPromise = null;
|
|
3752
|
+
/**
|
|
3753
|
+
* Get or create the filesystem instance
|
|
3754
|
+
*/
|
|
3755
|
+
const getFs = async () => {
|
|
3756
|
+
if (!self._getFilesystem) throw new Error("Filesystem not available: provider does not support filesystem operations");
|
|
3757
|
+
if (!filesPromise) filesPromise = self._getFilesystem();
|
|
3758
|
+
return filesPromise;
|
|
2959
3759
|
};
|
|
2960
|
-
}
|
|
2961
|
-
toAgentState(data) {
|
|
2962
|
-
const status = data.sessionStatus || data.status;
|
|
2963
3760
|
return {
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
...this.options.headers
|
|
2978
|
-
};
|
|
2979
|
-
if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
|
|
2980
|
-
const enterpriseId = typeof localStorage !== "undefined" ? localStorage.getItem(SELECTED_ACCOUNT_KEY) : null;
|
|
2981
|
-
if (enterpriseId) headers["X-Enterprise-Id"] = enterpriseId;
|
|
2982
|
-
const init = {
|
|
2983
|
-
method,
|
|
2984
|
-
headers
|
|
3761
|
+
read: (async (path, opts) => (await getFs()).read(path, opts)),
|
|
3762
|
+
write: (async (pathOrFiles, dataOrOpts, opts) => {
|
|
3763
|
+
const fs = await getFs();
|
|
3764
|
+
if (Array.isArray(pathOrFiles)) return fs.write(pathOrFiles, dataOrOpts);
|
|
3765
|
+
return fs.write(pathOrFiles, dataOrOpts, opts);
|
|
3766
|
+
}),
|
|
3767
|
+
list: async (path, opts) => (await getFs()).list(path, opts),
|
|
3768
|
+
exists: async (path, opts) => (await getFs()).exists(path, opts),
|
|
3769
|
+
makeDir: async (path, opts) => (await getFs()).makeDir(path, opts),
|
|
3770
|
+
remove: async (path, opts) => (await getFs()).remove(path, opts),
|
|
3771
|
+
rename: async (oldPath, newPath, opts) => (await getFs()).rename(oldPath, newPath, opts),
|
|
3772
|
+
getInfo: async (path, opts) => (await getFs()).getInfo(path, opts),
|
|
3773
|
+
watchDir: async (path, onEvent, opts) => (await getFs()).watchDir(path, onEvent, opts)
|
|
2985
3774
|
};
|
|
2986
|
-
if (method === "GET" && body !== void 0) {
|
|
2987
|
-
const params = new URLSearchParams();
|
|
2988
|
-
for (const [key, value] of Object.entries(body)) if (value !== void 0 && value !== null) {
|
|
2989
|
-
const stringValue = typeof value === "object" ? JSON.stringify(value) : String(value);
|
|
2990
|
-
params.append(key, stringValue);
|
|
2991
|
-
}
|
|
2992
|
-
const queryString = params.toString();
|
|
2993
|
-
if (queryString) url += `?${queryString}`;
|
|
2994
|
-
} else if (body !== void 0) init.body = JSON.stringify(body);
|
|
2995
|
-
this.logger?.debug(`${method} ${url}`);
|
|
2996
|
-
return this.fetchImpl(url, init);
|
|
2997
|
-
}
|
|
2998
|
-
};
|
|
2999
|
-
|
|
3000
|
-
//#endregion
|
|
3001
|
-
//#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
|
|
3002
|
-
/**
|
|
3003
|
-
* Local Agent Connection
|
|
3004
|
-
* Wraps AcpJsonRpcClient to implement AgentConnection interface
|
|
3005
|
-
*
|
|
3006
|
-
* Uses IWidgetChannel for IPC communication with ExtensionHost
|
|
3007
|
-
* Migrated from ipc-agent-provider for unified local agent access
|
|
3008
|
-
*/
|
|
3009
|
-
|
|
3010
|
-
//#endregion
|
|
3011
|
-
//#region ../agent-provider/src/common/client/session.ts
|
|
3012
|
-
/**
|
|
3013
|
-
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
3014
|
-
*
|
|
3015
|
-
* This class wraps an AgentConnection and provides the session-centric API.
|
|
3016
|
-
* It is created by SessionManager when creating or loading sessions.
|
|
3017
|
-
*
|
|
3018
|
-
* @example
|
|
3019
|
-
* ```typescript
|
|
3020
|
-
* // Created by client.sessions.new() or client.sessions.load()
|
|
3021
|
-
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
3022
|
-
*
|
|
3023
|
-
* // Access agent state
|
|
3024
|
-
* console.log(session.agentState.status);
|
|
3025
|
-
*
|
|
3026
|
-
* // Send prompt
|
|
3027
|
-
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
3028
|
-
*
|
|
3029
|
-
* // Cleanup
|
|
3030
|
-
* session.disconnect();
|
|
3031
|
-
* ```
|
|
3032
|
-
*/
|
|
3033
|
-
var ActiveSessionImpl = class {
|
|
3034
|
-
/**
|
|
3035
|
-
* Create an ActiveSessionImpl instance
|
|
3036
|
-
*
|
|
3037
|
-
* @param sessionId - Session ID
|
|
3038
|
-
* @param agentId - Agent ID
|
|
3039
|
-
* @param connection - Already connected AgentConnection
|
|
3040
|
-
* @param options - Additional options
|
|
3041
|
-
*/
|
|
3042
|
-
constructor(sessionId, agentId, connection, options = {}) {
|
|
3043
|
-
this._availableCommands = [];
|
|
3044
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
3045
|
-
this.onceListeners = /* @__PURE__ */ new Map();
|
|
3046
|
-
this._id = sessionId;
|
|
3047
|
-
this._agentId = agentId;
|
|
3048
|
-
this.connection = connection;
|
|
3049
|
-
this.logger = options.logger;
|
|
3050
|
-
this._getFilesystem = options.getFilesystem;
|
|
3051
|
-
this._connectionInfo = options.connectionInfo;
|
|
3052
|
-
this.setupConnectionEvents(connection);
|
|
3053
|
-
this.agent = this.createAgentOperations();
|
|
3054
|
-
this.prompts = this.createPromptsResource();
|
|
3055
|
-
this.artifacts = this.createArtifactsResource();
|
|
3056
|
-
this.files = this.createFilesResource();
|
|
3057
3775
|
}
|
|
3058
3776
|
/**
|
|
3059
|
-
*
|
|
3060
|
-
*/
|
|
3061
|
-
get id() {
|
|
3062
|
-
return this._id;
|
|
3063
|
-
}
|
|
3064
|
-
/**
|
|
3065
|
-
* Agent ID
|
|
3777
|
+
* Resolve a permission request
|
|
3066
3778
|
*/
|
|
3067
|
-
|
|
3068
|
-
return this.
|
|
3779
|
+
resolvePermission(requestId, optionId) {
|
|
3780
|
+
return this.connection.resolvePermission(requestId, optionId);
|
|
3069
3781
|
}
|
|
3070
3782
|
/**
|
|
3071
|
-
*
|
|
3072
|
-
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
3783
|
+
* Reject a permission request
|
|
3073
3784
|
*/
|
|
3074
|
-
|
|
3075
|
-
return
|
|
3076
|
-
id: this._agentId,
|
|
3077
|
-
status: this.connection.state,
|
|
3078
|
-
capabilities: this.connection.capabilities,
|
|
3079
|
-
type: "local",
|
|
3080
|
-
cwd: ""
|
|
3081
|
-
};
|
|
3785
|
+
rejectPermission(requestId, reason) {
|
|
3786
|
+
return this.connection.rejectPermission(requestId, reason);
|
|
3082
3787
|
}
|
|
3083
3788
|
/**
|
|
3084
|
-
*
|
|
3789
|
+
* Answer a question request with user's selections
|
|
3085
3790
|
*/
|
|
3086
|
-
|
|
3087
|
-
return this.connection.
|
|
3791
|
+
answerQuestion(toolCallId, answers) {
|
|
3792
|
+
return this.connection.answerQuestion(toolCallId, answers);
|
|
3088
3793
|
}
|
|
3089
3794
|
/**
|
|
3090
|
-
*
|
|
3795
|
+
* Cancel a question request
|
|
3091
3796
|
*/
|
|
3092
|
-
|
|
3093
|
-
return this.
|
|
3797
|
+
cancelQuestion(toolCallId, reason) {
|
|
3798
|
+
return this.connection.cancelQuestion(toolCallId, reason);
|
|
3094
3799
|
}
|
|
3095
3800
|
/**
|
|
3096
|
-
*
|
|
3801
|
+
* Callback for tool operations (skip or cancel)
|
|
3802
|
+
* @param toolCallId Tool call ID
|
|
3803
|
+
* @param toolName Tool name
|
|
3804
|
+
* @param action Action to perform ('skip' or 'cancel')
|
|
3097
3805
|
*/
|
|
3098
|
-
|
|
3099
|
-
return this.
|
|
3806
|
+
async toolCallback(toolCallId, toolName, action) {
|
|
3807
|
+
return await this.getConnectionOrThrow().toolCallback(this._id, toolCallId, toolName, action);
|
|
3100
3808
|
}
|
|
3101
3809
|
/**
|
|
3102
|
-
*
|
|
3103
|
-
*
|
|
3104
|
-
* When Agent sends available_commands_update, this list is automatically updated.
|
|
3105
|
-
* Commands can be accessed directly without waiting for events.
|
|
3106
|
-
*/
|
|
3107
|
-
get availableCommands() {
|
|
3108
|
-
return this._availableCommands;
|
|
3109
|
-
}
|
|
3110
|
-
/**
|
|
3111
|
-
* Check if the session is active
|
|
3112
|
-
*/
|
|
3113
|
-
get isActive() {
|
|
3114
|
-
return this.connection.isInitialized;
|
|
3115
|
-
}
|
|
3116
|
-
/**
|
|
3117
|
-
* Session connection information (only available for cloud sessions)
|
|
3118
|
-
* 会话连接信息,包括sandboxId、link、token等
|
|
3119
|
-
*/
|
|
3120
|
-
get connectionInfo() {
|
|
3121
|
-
return this._connectionInfo;
|
|
3122
|
-
}
|
|
3123
|
-
/**
|
|
3124
|
-
* Set session modes (called after create/load)
|
|
3125
|
-
*/
|
|
3126
|
-
setModes(availableModes, currentMode) {
|
|
3127
|
-
this._availableModes = availableModes;
|
|
3128
|
-
this._currentMode = currentMode;
|
|
3129
|
-
}
|
|
3130
|
-
createAgentOperations() {
|
|
3131
|
-
const self = this;
|
|
3132
|
-
return {
|
|
3133
|
-
get id() {
|
|
3134
|
-
return self._agentId;
|
|
3135
|
-
},
|
|
3136
|
-
get state() {
|
|
3137
|
-
return self.agentState;
|
|
3138
|
-
},
|
|
3139
|
-
get isConnected() {
|
|
3140
|
-
return self.connection.isInitialized;
|
|
3141
|
-
},
|
|
3142
|
-
get capabilities() {
|
|
3143
|
-
return self.connection.capabilities;
|
|
3144
|
-
}
|
|
3145
|
-
};
|
|
3146
|
-
}
|
|
3147
|
-
createPromptsResource() {
|
|
3148
|
-
return {
|
|
3149
|
-
send: async (params) => {
|
|
3150
|
-
const response = await this.getConnectionOrThrow().prompt(this._id, params);
|
|
3151
|
-
return this.mapPromptResponse(response);
|
|
3152
|
-
},
|
|
3153
|
-
stream: (params) => {
|
|
3154
|
-
return this.getConnectionOrThrow().promptStream(this._id, params);
|
|
3155
|
-
},
|
|
3156
|
-
cancel: async () => {
|
|
3157
|
-
await this.getConnectionOrThrow().cancel(this._id);
|
|
3158
|
-
}
|
|
3159
|
-
};
|
|
3160
|
-
}
|
|
3161
|
-
createArtifactsResource() {
|
|
3162
|
-
const notSupported = () => {
|
|
3163
|
-
throw new Error("Artifact management is no longer supported through this API");
|
|
3164
|
-
};
|
|
3165
|
-
return {
|
|
3166
|
-
list: async (_params) => {
|
|
3167
|
-
notSupported();
|
|
3168
|
-
return [];
|
|
3169
|
-
},
|
|
3170
|
-
retrieve: async (_artifactId) => {
|
|
3171
|
-
notSupported();
|
|
3172
|
-
},
|
|
3173
|
-
content: async (_artifactId) => {
|
|
3174
|
-
notSupported();
|
|
3175
|
-
return "";
|
|
3176
|
-
}
|
|
3177
|
-
};
|
|
3178
|
-
}
|
|
3179
|
-
/**
|
|
3180
|
-
* Create files resource with lazy-loaded filesystem
|
|
3181
|
-
*
|
|
3182
|
-
* The filesystem is lazily loaded on first use to avoid unnecessary
|
|
3183
|
-
* connections to the sandbox. The actual filesystem instance is obtained
|
|
3184
|
-
* via the getter function provided by SessionManager.
|
|
3185
|
-
*/
|
|
3186
|
-
createFilesResource() {
|
|
3187
|
-
const self = this;
|
|
3188
|
-
let filesPromise = null;
|
|
3189
|
-
/**
|
|
3190
|
-
* Get or create the filesystem instance
|
|
3191
|
-
*/
|
|
3192
|
-
const getFs = async () => {
|
|
3193
|
-
if (!self._getFilesystem) throw new Error("Filesystem not available: provider does not support filesystem operations");
|
|
3194
|
-
if (!filesPromise) filesPromise = self._getFilesystem();
|
|
3195
|
-
return filesPromise;
|
|
3196
|
-
};
|
|
3197
|
-
return {
|
|
3198
|
-
read: (async (path, opts) => (await getFs()).read(path, opts)),
|
|
3199
|
-
write: (async (pathOrFiles, dataOrOpts, opts) => {
|
|
3200
|
-
const fs = await getFs();
|
|
3201
|
-
if (Array.isArray(pathOrFiles)) return fs.write(pathOrFiles, dataOrOpts);
|
|
3202
|
-
return fs.write(pathOrFiles, dataOrOpts, opts);
|
|
3203
|
-
}),
|
|
3204
|
-
list: async (path, opts) => (await getFs()).list(path, opts),
|
|
3205
|
-
exists: async (path, opts) => (await getFs()).exists(path, opts),
|
|
3206
|
-
makeDir: async (path, opts) => (await getFs()).makeDir(path, opts),
|
|
3207
|
-
remove: async (path, opts) => (await getFs()).remove(path, opts),
|
|
3208
|
-
rename: async (oldPath, newPath, opts) => (await getFs()).rename(oldPath, newPath, opts),
|
|
3209
|
-
getInfo: async (path, opts) => (await getFs()).getInfo(path, opts),
|
|
3210
|
-
watchDir: async (path, onEvent, opts) => (await getFs()).watchDir(path, onEvent, opts)
|
|
3211
|
-
};
|
|
3212
|
-
}
|
|
3213
|
-
/**
|
|
3214
|
-
* Resolve a permission request
|
|
3215
|
-
*/
|
|
3216
|
-
resolvePermission(requestId, optionId) {
|
|
3217
|
-
return this.connection.resolvePermission(requestId, optionId);
|
|
3218
|
-
}
|
|
3219
|
-
/**
|
|
3220
|
-
* Reject a permission request
|
|
3221
|
-
*/
|
|
3222
|
-
rejectPermission(requestId, reason) {
|
|
3223
|
-
return this.connection.rejectPermission(requestId, reason);
|
|
3224
|
-
}
|
|
3225
|
-
/**
|
|
3226
|
-
* Answer a question request with user's selections
|
|
3227
|
-
*/
|
|
3228
|
-
answerQuestion(toolCallId, answers) {
|
|
3229
|
-
return this.connection.answerQuestion(toolCallId, answers);
|
|
3230
|
-
}
|
|
3231
|
-
/**
|
|
3232
|
-
* Cancel a question request
|
|
3233
|
-
*/
|
|
3234
|
-
cancelQuestion(toolCallId, reason) {
|
|
3235
|
-
return this.connection.cancelQuestion(toolCallId, reason);
|
|
3236
|
-
}
|
|
3237
|
-
/**
|
|
3238
|
-
* Callback for tool operations (skip or cancel)
|
|
3239
|
-
* @param toolCallId Tool call ID
|
|
3240
|
-
* @param toolName Tool name
|
|
3241
|
-
* @param action Action to perform ('skip' or 'cancel')
|
|
3242
|
-
*/
|
|
3243
|
-
async toolCallback(toolCallId, toolName, action) {
|
|
3244
|
-
return await this.getConnectionOrThrow().toolCallback(this._id, toolCallId, toolName, action);
|
|
3245
|
-
}
|
|
3246
|
-
/**
|
|
3247
|
-
* Set the current session mode
|
|
3810
|
+
* Set the current session mode
|
|
3248
3811
|
*
|
|
3249
3812
|
* @param modeId - The mode ID to switch to (must be in availableModes)
|
|
3250
3813
|
* @throws Error if modeId is not in availableModes or connection fails
|
|
@@ -3864,6 +4427,396 @@ function isCloudAgentState(state) {
|
|
|
3864
4427
|
return state.type === "cloud";
|
|
3865
4428
|
}
|
|
3866
4429
|
|
|
4430
|
+
//#endregion
|
|
4431
|
+
//#region ../agent-provider/src/backend/types.ts
|
|
4432
|
+
/**
|
|
4433
|
+
* 套餐代码
|
|
4434
|
+
*/
|
|
4435
|
+
/**
|
|
4436
|
+
* TCACA_code_001_PqouKr6QWV CodeBuddy海外版免费包
|
|
4437
|
+
* TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
|
|
4438
|
+
* TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
|
|
4439
|
+
* TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
|
|
4440
|
+
* TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
|
|
4441
|
+
* TCACA_code_008_cfWoLwvjU4 赠送月包
|
|
4442
|
+
*/
|
|
4443
|
+
let CommodityCode = /* @__PURE__ */ function(CommodityCode) {
|
|
4444
|
+
CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
|
|
4445
|
+
CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
|
|
4446
|
+
CommodityCode["proMonPlus"] = "TCACA_code_005_maRGyrHhw1";
|
|
4447
|
+
CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
|
|
4448
|
+
CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
|
|
4449
|
+
CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
|
|
4450
|
+
CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
|
|
4451
|
+
CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
|
|
4452
|
+
return CommodityCode;
|
|
4453
|
+
}({});
|
|
4454
|
+
/** 账户状态 */
|
|
4455
|
+
let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
4456
|
+
/** 有效 */
|
|
4457
|
+
AccountStatus[AccountStatus["valid"] = 0] = "valid";
|
|
4458
|
+
/** 已退款 */
|
|
4459
|
+
AccountStatus[AccountStatus["refund"] = 1] = "refund";
|
|
4460
|
+
/** 已过期 */
|
|
4461
|
+
AccountStatus[AccountStatus["expired"] = 2] = "expired";
|
|
4462
|
+
/** 已用完 */
|
|
4463
|
+
AccountStatus[AccountStatus["usedUp"] = 3] = "usedUp";
|
|
4464
|
+
return AccountStatus;
|
|
4465
|
+
}({});
|
|
4466
|
+
|
|
4467
|
+
//#endregion
|
|
4468
|
+
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
4469
|
+
/**
|
|
4470
|
+
* Backend Provider 实现
|
|
4471
|
+
*
|
|
4472
|
+
* 封装与后端 API 的 HTTP 通信
|
|
4473
|
+
*/
|
|
4474
|
+
/** 获取当前域名的登录页面 URL */
|
|
4475
|
+
const getLoginUrl = () => `${window.location.origin}/login`;
|
|
4476
|
+
/** 获取当前域名的账号选择页面 URL */
|
|
4477
|
+
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
4478
|
+
/** localStorage 中存储选中账号 ID 的 key */
|
|
4479
|
+
const SELECTED_ACCOUNT_KEY = "CODEBUDDY_IDE_SELECTED_ACCOUNT_ID";
|
|
4480
|
+
/**
|
|
4481
|
+
* Backend Provider 实现类
|
|
4482
|
+
*
|
|
4483
|
+
* 职责:
|
|
4484
|
+
* - 触发登录/登出流程
|
|
4485
|
+
* - 获取 account 后自动同步到 accountService
|
|
4486
|
+
*
|
|
4487
|
+
* 注意:getAgents 和 getModels 方法已废弃并移除,
|
|
4488
|
+
* 请使用 IAgentAdapter 中的对应方法
|
|
4489
|
+
*/
|
|
4490
|
+
var BackendProvider = class {
|
|
4491
|
+
constructor(config) {
|
|
4492
|
+
this.baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
4493
|
+
this.authToken = config.authToken;
|
|
4494
|
+
}
|
|
4495
|
+
/**
|
|
4496
|
+
* 获取当前账号信息
|
|
4497
|
+
* API 端点: GET /console/accounts (返回账号列表)
|
|
4498
|
+
*
|
|
4499
|
+
* 逻辑:
|
|
4500
|
+
* 1. 从 localStorage 读取 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID
|
|
4501
|
+
* 2. 根据 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID 找到对应账号
|
|
4502
|
+
* - personal 类型: 用 uid 匹配
|
|
4503
|
+
* - 其他类型: 用 enterpriseId 匹配
|
|
4504
|
+
* 3. 如果没有选中的账号,跳转到账号选择页面
|
|
4505
|
+
* 4. 获取套餐信息并合并到账号中
|
|
4506
|
+
* 5. 同步到 accountService
|
|
4507
|
+
*/
|
|
4508
|
+
async getAccount() {
|
|
4509
|
+
const url = `${this.baseUrl}/console/accounts`;
|
|
4510
|
+
const headers = {
|
|
4511
|
+
"Content-Type": "application/json",
|
|
4512
|
+
"Accept": "application/json"
|
|
4513
|
+
};
|
|
4514
|
+
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
4515
|
+
try {
|
|
4516
|
+
const response = await fetch(url, {
|
|
4517
|
+
method: "GET",
|
|
4518
|
+
headers,
|
|
4519
|
+
credentials: "include"
|
|
4520
|
+
});
|
|
4521
|
+
if (!response.ok) {
|
|
4522
|
+
if (response.status === 401 || response.status === 403) {
|
|
4523
|
+
accountService.setAccount(null);
|
|
4524
|
+
return null;
|
|
4525
|
+
}
|
|
4526
|
+
const error = await response.json().catch(() => ({ message: response.statusText }));
|
|
4527
|
+
throw new Error(error.message || `HTTP ${response.status}`);
|
|
4528
|
+
}
|
|
4529
|
+
if (!(response.headers.get("Content-Type") || "").includes("application/json")) {
|
|
4530
|
+
accountService.setAccount(null);
|
|
4531
|
+
return null;
|
|
4532
|
+
}
|
|
4533
|
+
const { data = {} } = await response.json();
|
|
4534
|
+
const accounts = data?.accounts || [];
|
|
4535
|
+
if (!accounts || accounts.length === 0) {
|
|
4536
|
+
accountService.setAccount(null);
|
|
4537
|
+
return null;
|
|
4538
|
+
}
|
|
4539
|
+
const selectedAccountId = localStorage.getItem(SELECTED_ACCOUNT_KEY);
|
|
4540
|
+
let selectedAccount;
|
|
4541
|
+
if (selectedAccountId) {
|
|
4542
|
+
selectedAccount = accounts.find((account) => {
|
|
4543
|
+
if (account.type === "personal") return account.uid === selectedAccountId;
|
|
4544
|
+
return account.enterpriseId === selectedAccountId;
|
|
4545
|
+
});
|
|
4546
|
+
if (selectedAccount) try {
|
|
4547
|
+
const plan = await this.getCurrentPlan();
|
|
4548
|
+
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
4549
|
+
console.log("account", {
|
|
4550
|
+
...selectedAccount,
|
|
4551
|
+
...plan,
|
|
4552
|
+
editionType
|
|
4553
|
+
});
|
|
4554
|
+
const account = {
|
|
4555
|
+
...selectedAccount,
|
|
4556
|
+
...plan,
|
|
4557
|
+
editionType
|
|
4558
|
+
};
|
|
4559
|
+
accountService.setAccount(account);
|
|
4560
|
+
return account;
|
|
4561
|
+
} catch (error) {
|
|
4562
|
+
accountService.setAccount(selectedAccount);
|
|
4563
|
+
return { ...selectedAccount };
|
|
4564
|
+
}
|
|
4565
|
+
}
|
|
4566
|
+
if (accounts.length === 1) {
|
|
4567
|
+
selectedAccount = accounts[0];
|
|
4568
|
+
const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
|
|
4569
|
+
if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
|
|
4570
|
+
const plan = await this.getCurrentPlan();
|
|
4571
|
+
const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
|
|
4572
|
+
console.log("account (auto-selected)", {
|
|
4573
|
+
...selectedAccount,
|
|
4574
|
+
...plan,
|
|
4575
|
+
editionType
|
|
4576
|
+
});
|
|
4577
|
+
const account = {
|
|
4578
|
+
...selectedAccount,
|
|
4579
|
+
...plan,
|
|
4580
|
+
editionType
|
|
4581
|
+
};
|
|
4582
|
+
accountService.setAccount(account);
|
|
4583
|
+
return account;
|
|
4584
|
+
}
|
|
4585
|
+
const redirectUrl = encodeURIComponent(window.location.href);
|
|
4586
|
+
window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
4587
|
+
accountService.setAccount(null);
|
|
4588
|
+
return null;
|
|
4589
|
+
} catch (error) {
|
|
4590
|
+
console.error("[BackendProvider] getAccount failed:", error);
|
|
4591
|
+
accountService.setAccount(null);
|
|
4592
|
+
return null;
|
|
4593
|
+
}
|
|
4594
|
+
}
|
|
4595
|
+
/**
|
|
4596
|
+
* 获取当前套餐信息
|
|
4597
|
+
* 从计量计费接口获取用户的套餐信息
|
|
4598
|
+
* API: POST /billing/meter/get-user-resource
|
|
4599
|
+
*/
|
|
4600
|
+
async getCurrentPlan() {
|
|
4601
|
+
const defaultPlan = {
|
|
4602
|
+
isPro: false,
|
|
4603
|
+
expireAt: 0,
|
|
4604
|
+
renewFlag: 0,
|
|
4605
|
+
PackageCode: void 0,
|
|
4606
|
+
name: ""
|
|
4607
|
+
};
|
|
4608
|
+
try {
|
|
4609
|
+
const url = `${this.baseUrl}/billing/meter/get-user-resource`;
|
|
4610
|
+
const headers = {
|
|
4611
|
+
"Content-Type": "application/json",
|
|
4612
|
+
"Accept": "application/json"
|
|
4613
|
+
};
|
|
4614
|
+
if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
4615
|
+
const now = /* @__PURE__ */ new Date();
|
|
4616
|
+
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
4617
|
+
const formatDate = (d) => {
|
|
4618
|
+
const pad = (n) => n.toString().padStart(2, "0");
|
|
4619
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
4620
|
+
};
|
|
4621
|
+
const body = {
|
|
4622
|
+
PageNumber: 1,
|
|
4623
|
+
PageSize: 100,
|
|
4624
|
+
ProductCode: "p_tcaca",
|
|
4625
|
+
Status: [AccountStatus.valid, AccountStatus.usedUp],
|
|
4626
|
+
PackageEndTimeRangeBegin: formatDate(now),
|
|
4627
|
+
PackageEndTimeRangeEnd: formatDate(futureDate)
|
|
4628
|
+
};
|
|
4629
|
+
const response = await fetch(url, {
|
|
4630
|
+
method: "POST",
|
|
4631
|
+
headers,
|
|
4632
|
+
credentials: "include",
|
|
4633
|
+
body: JSON.stringify(body)
|
|
4634
|
+
});
|
|
4635
|
+
if (!response.ok) {
|
|
4636
|
+
console.warn("[BackendProvider] getCurrentPlan failed:", response.status);
|
|
4637
|
+
return defaultPlan;
|
|
4638
|
+
}
|
|
4639
|
+
const resources = (await response.json())?.data?.Response?.Data?.Accounts || [];
|
|
4640
|
+
if (!resources || resources.length === 0) return defaultPlan;
|
|
4641
|
+
const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon || r.PackageCode === CommodityCode.proMonPlus);
|
|
4642
|
+
const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift || r.PackageCode === CommodityCode.freeMon);
|
|
4643
|
+
const activePlan = proPlan || trialPlan;
|
|
4644
|
+
if (activePlan) {
|
|
4645
|
+
const parseTime = (time) => {
|
|
4646
|
+
if (!time) return 0;
|
|
4647
|
+
return new Date(time).getTime();
|
|
4648
|
+
};
|
|
4649
|
+
return {
|
|
4650
|
+
isPro: !!proPlan,
|
|
4651
|
+
isTria: [AccountStatus.valid, AccountStatus.usedUp].includes(trialPlan?.Status),
|
|
4652
|
+
expireAt: parseTime(activePlan.DeductionEndTime || activePlan.ExpiredTime || activePlan.CycleEndTime),
|
|
4653
|
+
refreshAt: parseTime(activePlan.CycleEndTime),
|
|
4654
|
+
renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
|
|
4655
|
+
PackageCode: activePlan.PackageCode,
|
|
4656
|
+
name: activePlan.PackageName || "",
|
|
4657
|
+
usageTotal: activePlan.CycleCapacitySizePrecise,
|
|
4658
|
+
usageUsed: activePlan.CapacityUsedPrecise,
|
|
4659
|
+
usageLeft: activePlan.CycleCapacityRemainPrecise
|
|
4660
|
+
};
|
|
4661
|
+
}
|
|
4662
|
+
return defaultPlan;
|
|
4663
|
+
} catch (error) {
|
|
4664
|
+
console.error("[BackendProvider] getCurrentPlan error:", error);
|
|
4665
|
+
return defaultPlan;
|
|
4666
|
+
}
|
|
4667
|
+
}
|
|
4668
|
+
/**
|
|
4669
|
+
* 根据账号类型和 Pro 状态计算版本展示类型
|
|
4670
|
+
* - personal + isPro = 'pro'
|
|
4671
|
+
* - personal + !isPro = 'free'
|
|
4672
|
+
* - ultimate = 'ultimate' (旗舰版/团队版)
|
|
4673
|
+
* - exclusive = 'exclusive' (专享版/企业版)
|
|
4674
|
+
*/
|
|
4675
|
+
getEditionDisplayType(type, isPro) {
|
|
4676
|
+
if (type === "personal") return isPro ? "pro" : "free";
|
|
4677
|
+
if (type === "ultimate") return "ultimate";
|
|
4678
|
+
if (type === "exclusive") return "exclusive";
|
|
4679
|
+
return "free";
|
|
4680
|
+
}
|
|
4681
|
+
/**
|
|
4682
|
+
* 触发登录流程
|
|
4683
|
+
* Web 环境: 跳转到登录页面
|
|
4684
|
+
*/
|
|
4685
|
+
async login() {
|
|
4686
|
+
const redirectUrl = encodeURIComponent(window.location.href);
|
|
4687
|
+
window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
|
|
4688
|
+
}
|
|
4689
|
+
/**
|
|
4690
|
+
* 登出账号
|
|
4691
|
+
* Web 环境: 调用登出接口并清除本地状态
|
|
4692
|
+
*/
|
|
4693
|
+
async logout() {
|
|
4694
|
+
const url = `${this.baseUrl}/console/logout`;
|
|
4695
|
+
try {
|
|
4696
|
+
await fetch(url, {
|
|
4697
|
+
method: "POST",
|
|
4698
|
+
credentials: "include"
|
|
4699
|
+
});
|
|
4700
|
+
} catch (error) {
|
|
4701
|
+
console.error("[BackendProvider] logout failed:", error);
|
|
4702
|
+
}
|
|
4703
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
4704
|
+
accountService.clearAccount();
|
|
4705
|
+
}
|
|
4706
|
+
};
|
|
4707
|
+
/**
|
|
4708
|
+
* 创建 BackendProvider 实例
|
|
4709
|
+
*/
|
|
4710
|
+
function createBackendProvider(config) {
|
|
4711
|
+
return new BackendProvider(config);
|
|
4712
|
+
}
|
|
4713
|
+
|
|
4714
|
+
//#endregion
|
|
4715
|
+
//#region ../agent-provider/src/backend/ipc-backend-provider.ts
|
|
4716
|
+
/**
|
|
4717
|
+
* Backend 请求类型常量
|
|
4718
|
+
*/
|
|
4719
|
+
const BACKEND_REQUEST_TYPES = {
|
|
4720
|
+
LOGIN: "backend:login",
|
|
4721
|
+
LOGOUT: "backend:logout",
|
|
4722
|
+
GET_ACCOUNT: "backend:get-account"
|
|
4723
|
+
};
|
|
4724
|
+
/**
|
|
4725
|
+
* 生成唯一请求 ID
|
|
4726
|
+
*/
|
|
4727
|
+
function generateRequestId() {
|
|
4728
|
+
return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
4729
|
+
}
|
|
4730
|
+
/**
|
|
4731
|
+
* IPC Backend Provider 实现类
|
|
4732
|
+
*
|
|
4733
|
+
* 通过 IWidgetChannel 与后端通信
|
|
4734
|
+
*/
|
|
4735
|
+
var IPCBackendProvider = class {
|
|
4736
|
+
constructor(config) {
|
|
4737
|
+
this.channel = config.channel;
|
|
4738
|
+
this.debug = config.debug ?? false;
|
|
4739
|
+
this.timeoutMs = config.timeoutMs ?? 3e4;
|
|
4740
|
+
this.log("Initialized with IWidgetChannel");
|
|
4741
|
+
}
|
|
4742
|
+
/**
|
|
4743
|
+
* 发送统一格式的后端请求
|
|
4744
|
+
* @param requestType 请求类型
|
|
4745
|
+
* @param params 请求参数
|
|
4746
|
+
* @returns 响应数据
|
|
4747
|
+
*/
|
|
4748
|
+
async sendBackendRequest(requestType, params) {
|
|
4749
|
+
const message = {
|
|
4750
|
+
type: "backend",
|
|
4751
|
+
requestId: generateRequestId(),
|
|
4752
|
+
params: {
|
|
4753
|
+
type: requestType,
|
|
4754
|
+
params
|
|
4755
|
+
}
|
|
4756
|
+
};
|
|
4757
|
+
this.log("Sending backend request:", message);
|
|
4758
|
+
const response = await this.channel.callMethod("__backend__", message, this.timeoutMs);
|
|
4759
|
+
this.log("Received response:", response);
|
|
4760
|
+
if (response?.error) throw new Error(response.error);
|
|
4761
|
+
return response?.data !== void 0 ? response.data : response;
|
|
4762
|
+
}
|
|
4763
|
+
/**
|
|
4764
|
+
* 获取当前账号信息
|
|
4765
|
+
* IDE 环境: 通过 IPC 获取账号信息,并同步到 accountService
|
|
4766
|
+
*/
|
|
4767
|
+
async getAccount() {
|
|
4768
|
+
this.log("Getting account via IPC");
|
|
4769
|
+
try {
|
|
4770
|
+
const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
4771
|
+
accountService.setAccount(account);
|
|
4772
|
+
return account;
|
|
4773
|
+
} catch (error) {
|
|
4774
|
+
this.log("Get account failed:", error);
|
|
4775
|
+
accountService.setAccount(null);
|
|
4776
|
+
return null;
|
|
4777
|
+
}
|
|
4778
|
+
}
|
|
4779
|
+
/**
|
|
4780
|
+
* 触发登录流程
|
|
4781
|
+
* IDE 环境: 通过 IPC 通知 IDE 打开登录流程
|
|
4782
|
+
*/
|
|
4783
|
+
async login() {
|
|
4784
|
+
this.log("Triggering login via IPC");
|
|
4785
|
+
try {
|
|
4786
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGIN);
|
|
4787
|
+
} catch (error) {
|
|
4788
|
+
this.log("Login request failed:", error);
|
|
4789
|
+
throw error;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
/**
|
|
4793
|
+
* 登出账号
|
|
4794
|
+
* IDE 环境: 通过 IPC 通知 IDE 登出
|
|
4795
|
+
*/
|
|
4796
|
+
async logout() {
|
|
4797
|
+
this.log("Triggering logout via IPC");
|
|
4798
|
+
try {
|
|
4799
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGOUT);
|
|
4800
|
+
accountService.clearAccount();
|
|
4801
|
+
} catch (error) {
|
|
4802
|
+
this.log("Logout request failed:", error);
|
|
4803
|
+
throw error;
|
|
4804
|
+
}
|
|
4805
|
+
}
|
|
4806
|
+
/**
|
|
4807
|
+
* 调试日志
|
|
4808
|
+
*/
|
|
4809
|
+
log(...args) {
|
|
4810
|
+
if (this.debug) console.log("[IPCBackendProvider]", ...args);
|
|
4811
|
+
}
|
|
4812
|
+
};
|
|
4813
|
+
/**
|
|
4814
|
+
* 创建 IPCBackendProvider 实例
|
|
4815
|
+
*/
|
|
4816
|
+
function createIPCBackendProvider(config) {
|
|
4817
|
+
return new IPCBackendProvider(config);
|
|
4818
|
+
}
|
|
4819
|
+
|
|
3867
4820
|
//#endregion
|
|
3868
4821
|
exports.ActiveSessionImpl = ActiveSessionImpl;
|
|
3869
4822
|
exports.AgentClient = AgentClient;
|