@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.00cf97b.20260317
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 +2337 -348
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1964 -201
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1964 -201
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2335 -346
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.13-next.00cf97b.20260317.tgz +0 -0
- package/package.json +6 -5
- package/dist/tencent-ai-cloud-agent-sdk-0.2.12.tgz +0 -0
package/dist/index.mjs
CHANGED
|
@@ -54,7 +54,6 @@ const ToolInputSchemas = {
|
|
|
54
54
|
queryString: z.string().describe("用户的实际问题或搜索查询"),
|
|
55
55
|
knowledgeBaseNames: z.string().describe("知识库名称,多个用逗号分隔")
|
|
56
56
|
}),
|
|
57
|
-
read_rules: z.object({ ruleNames: z.string().describe("要读取的规则关键词,用逗号分隔,格式:{ruleName}_{ruleId}") }),
|
|
58
57
|
mcp_get_tool_description: z.object({ toolRequests: z.string().describe("JSON 字符串,二维数组格式:[[\"server1\", \"tool1\"], [\"server2\", \"tool2\"]]") }),
|
|
59
58
|
mcp_call_tool: z.object({
|
|
60
59
|
serverName: z.string().describe("MCP 服务器名称"),
|
|
@@ -68,13 +67,6 @@ const ToolInputSchemas = {
|
|
|
68
67
|
arguments: z.record(z.unknown()).optional().describe("资源模板的参数"),
|
|
69
68
|
downloadPath: z.string().optional().describe("可选的绝对路径,用于保存资源到磁盘")
|
|
70
69
|
}),
|
|
71
|
-
create_rule: z.object({
|
|
72
|
-
ruleScope: z.string().describe("规则范围,project rule 或 user rule"),
|
|
73
|
-
ruleName: z.string().describe("规则文件名,不带扩展名"),
|
|
74
|
-
ruleType: z.string().describe("规则类型,always、manual 或 requested"),
|
|
75
|
-
ruleContent: z.string().describe("规则内容,使用 Markdown 格式"),
|
|
76
|
-
ruleDescription: z.string().optional().describe("规则描述,使用 Markdown 格式")
|
|
77
|
-
}),
|
|
78
70
|
update_memory: z.object({
|
|
79
71
|
action: z.enum([
|
|
80
72
|
"create",
|
|
@@ -133,7 +125,6 @@ const ToolInputSchemas = {
|
|
|
133
125
|
cloud_studio_fetch_log: z.object({}).passthrough(),
|
|
134
126
|
cloud_studio_execute_command: z.object({}).passthrough(),
|
|
135
127
|
cloud_studio_deploy_sandbox: z.object({}).passthrough(),
|
|
136
|
-
component_get_prompt: z.object({}).passthrough(),
|
|
137
128
|
web_fetch: z.object({
|
|
138
129
|
url: z.string().describe("要获取内容的 URL"),
|
|
139
130
|
fetchInfo: z.string().describe("用户想要获取的信息描述")
|
|
@@ -141,7 +132,9 @@ const ToolInputSchemas = {
|
|
|
141
132
|
use_skill: z.object({ command: z.string().describe("技能名称(不含参数),如 \"pdf\" 或 \"xlsx\"") }),
|
|
142
133
|
web_search: z.object({
|
|
143
134
|
explanation: z.string().describe("为什么使用此工具的一句话解释"),
|
|
144
|
-
|
|
135
|
+
query: z.string().describe("搜索关键词"),
|
|
136
|
+
max_results: z.number().optional().describe("最大返回数量"),
|
|
137
|
+
language: z.string().optional().describe("语言代码,例如 zh-CN")
|
|
145
138
|
}),
|
|
146
139
|
task: z.object({
|
|
147
140
|
subagent_name: z.string().describe("要调用的子代理名称"),
|
|
@@ -219,11 +212,6 @@ const ToolOutputSchemas = {
|
|
|
219
212
|
selectedKnowledgeBases: z.string(),
|
|
220
213
|
queryInput: z.string()
|
|
221
214
|
}),
|
|
222
|
-
read_rules: z.object({
|
|
223
|
-
type: z.literal("rule_match_result"),
|
|
224
|
-
ruleDescription: z.string(),
|
|
225
|
-
filePaths: z.array(z.string())
|
|
226
|
-
}),
|
|
227
215
|
mcp_get_tool_description: z.object({}).passthrough(),
|
|
228
216
|
mcp_call_tool: z.object({
|
|
229
217
|
type: z.literal("mcp_call_tool_result"),
|
|
@@ -260,17 +248,6 @@ const ToolOutputSchemas = {
|
|
|
260
248
|
content: z.string(),
|
|
261
249
|
downloadPath: z.string().optional()
|
|
262
250
|
}),
|
|
263
|
-
create_rule: z.object({
|
|
264
|
-
type: z.literal("rule_create_result"),
|
|
265
|
-
ruleName: z.string(),
|
|
266
|
-
createState: z.enum([
|
|
267
|
-
"success",
|
|
268
|
-
"invoke",
|
|
269
|
-
"cancelled"
|
|
270
|
-
]),
|
|
271
|
-
hint: z.string().optional(),
|
|
272
|
-
filePath: z.string().optional()
|
|
273
|
-
}),
|
|
274
251
|
update_memory: z.object({
|
|
275
252
|
type: z.literal("update_memory_result"),
|
|
276
253
|
success: z.boolean(),
|
|
@@ -284,9 +261,9 @@ const ToolOutputSchemas = {
|
|
|
284
261
|
}),
|
|
285
262
|
search_content: z.object({
|
|
286
263
|
type: z.literal("search_content_result"),
|
|
287
|
-
|
|
264
|
+
path: z.string(),
|
|
288
265
|
pattern: z.string(),
|
|
289
|
-
|
|
266
|
+
glob: z.string(),
|
|
290
267
|
matches: z.array(z.object({
|
|
291
268
|
filePath: z.string(),
|
|
292
269
|
content: z.string(),
|
|
@@ -298,7 +275,7 @@ const ToolOutputSchemas = {
|
|
|
298
275
|
totalCount: z.number(),
|
|
299
276
|
hasMore: z.boolean(),
|
|
300
277
|
offset: z.number(),
|
|
301
|
-
|
|
278
|
+
headLimit: z.number(),
|
|
302
279
|
contextBefore: z.number(),
|
|
303
280
|
contextAfter: z.number(),
|
|
304
281
|
contextAround: z.number().optional(),
|
|
@@ -484,15 +461,6 @@ const ToolOutputSchemas = {
|
|
|
484
461
|
}).optional()
|
|
485
462
|
}))
|
|
486
463
|
}),
|
|
487
|
-
component_get_prompt: z.object({
|
|
488
|
-
type: z.literal("component_get_prompt_result"),
|
|
489
|
-
componentType: z.string(),
|
|
490
|
-
webFramework: z.string(),
|
|
491
|
-
data: z.object({
|
|
492
|
-
type: z.literal("text"),
|
|
493
|
-
text: z.string()
|
|
494
|
-
})
|
|
495
|
-
}),
|
|
496
464
|
web_fetch: z.object({
|
|
497
465
|
type: z.literal("web_fetch_tool_result"),
|
|
498
466
|
message: z.string(),
|
|
@@ -508,14 +476,29 @@ const ToolOutputSchemas = {
|
|
|
508
476
|
}),
|
|
509
477
|
web_search: z.object({
|
|
510
478
|
type: z.literal("web_search_tool_result"),
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
479
|
+
query: z.string().optional(),
|
|
480
|
+
searchType: z.literal("text2text").optional(),
|
|
481
|
+
provider: z.string().optional(),
|
|
482
|
+
results: z.array(z.object({
|
|
515
483
|
title: z.string(),
|
|
516
|
-
|
|
517
|
-
|
|
484
|
+
url: z.string(),
|
|
485
|
+
snippet: z.string().optional(),
|
|
486
|
+
site: z.string().optional(),
|
|
487
|
+
highlights: z.array(z.string()).optional(),
|
|
488
|
+
content: z.string().optional(),
|
|
489
|
+
favicon: z.string().optional()
|
|
518
490
|
})),
|
|
491
|
+
images: z.array(z.object({
|
|
492
|
+
url: z.string(),
|
|
493
|
+
description: z.string().optional(),
|
|
494
|
+
sourceUrl: z.string().optional(),
|
|
495
|
+
width: z.number().optional(),
|
|
496
|
+
height: z.number().optional(),
|
|
497
|
+
title: z.string().optional(),
|
|
498
|
+
siteName: z.string().optional()
|
|
499
|
+
})),
|
|
500
|
+
totalResults: z.number().optional(),
|
|
501
|
+
responseTimeMs: z.number().optional(),
|
|
519
502
|
searchInput: z.string().optional()
|
|
520
503
|
}),
|
|
521
504
|
task: z.object({
|
|
@@ -661,7 +644,11 @@ const ExtensionMethod = {
|
|
|
661
644
|
CHECKPOINT: "_codebuddy.ai/checkpoint",
|
|
662
645
|
USAGE: "_codebuddy.ai/usage",
|
|
663
646
|
COMMAND: "_codebuddy.ai/command",
|
|
664
|
-
AUTH_URL: "_codebuddy.ai/authUrl"
|
|
647
|
+
AUTH_URL: "_codebuddy.ai/authUrl",
|
|
648
|
+
FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot",
|
|
649
|
+
DELEGATE_TOOL: "_codebuddy.ai/delegateTool",
|
|
650
|
+
DELEGATE_TOOLS_CHANGED: "_codebuddy.ai/delegateToolsChanged",
|
|
651
|
+
UI_CONTROL: "_codebuddy.ai/uiControl"
|
|
665
652
|
};
|
|
666
653
|
/**
|
|
667
654
|
* All known extension methods
|
|
@@ -672,7 +659,10 @@ const KNOWN_EXTENSIONS = [
|
|
|
672
659
|
ExtensionMethod.CHECKPOINT,
|
|
673
660
|
ExtensionMethod.USAGE,
|
|
674
661
|
ExtensionMethod.COMMAND,
|
|
675
|
-
ExtensionMethod.AUTH_URL
|
|
662
|
+
ExtensionMethod.AUTH_URL,
|
|
663
|
+
ExtensionMethod.FILE_HISTORY_SNAPSHOT,
|
|
664
|
+
ExtensionMethod.DELEGATE_TOOL,
|
|
665
|
+
ExtensionMethod.DELEGATE_TOOLS_CHANGED
|
|
676
666
|
];
|
|
677
667
|
|
|
678
668
|
//#endregion
|
|
@@ -723,7 +713,7 @@ function parseSSELine(line, currentEvent) {
|
|
|
723
713
|
};
|
|
724
714
|
}
|
|
725
715
|
function streamableHttp(options) {
|
|
726
|
-
const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, postTimeout = 3e4, backpressure = {} } = options;
|
|
716
|
+
const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, connectionTimeout = 3e4, postTimeout = 3e4, backpressure = {} } = options;
|
|
727
717
|
const { enabled: reconnectEnabled = true, initialDelay = 1e3, maxDelay = 3e4, maxRetries = Infinity, jitter: jitterEnabled = true } = reconnect;
|
|
728
718
|
const { highWaterMark = 100, lowWaterMark = 50, pauseTimeout = 5e3 } = backpressure;
|
|
729
719
|
let connectionId;
|
|
@@ -934,11 +924,21 @@ function streamableHttp(options) {
|
|
|
934
924
|
const headers = buildHeaders();
|
|
935
925
|
headers["Accept"] = "text/event-stream";
|
|
936
926
|
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
});
|
|
927
|
+
const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
|
|
928
|
+
const connectController = new AbortController();
|
|
929
|
+
const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
|
|
930
|
+
if (externalSignal) externalSignal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
931
|
+
abortController.signal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
932
|
+
let response;
|
|
933
|
+
try {
|
|
934
|
+
response = await customFetch(endpoint, {
|
|
935
|
+
method: "GET",
|
|
936
|
+
headers,
|
|
937
|
+
signal: connectController.signal
|
|
938
|
+
});
|
|
939
|
+
} finally {
|
|
940
|
+
clearTimeout(connectTimer);
|
|
941
|
+
}
|
|
942
942
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
943
943
|
const newConnectionId = response.headers.get("Acp-Connection-Id");
|
|
944
944
|
if (!newConnectionId) throw new Error("Server did not return Acp-Connection-Id header");
|
|
@@ -1874,6 +1874,9 @@ var StreamableHttpClient = class {
|
|
|
1874
1874
|
headers: this.options.headers,
|
|
1875
1875
|
reconnect: this.options.reconnect,
|
|
1876
1876
|
fetch: this.options.fetch,
|
|
1877
|
+
heartbeatTimeout: this.options.heartbeatTimeout,
|
|
1878
|
+
postTimeout: this.options.postTimeout,
|
|
1879
|
+
connectionTimeout: this.options.connectionTimeout,
|
|
1877
1880
|
onConnect: (connectionId) => {
|
|
1878
1881
|
this.options.logger?.debug(`Transport connected: ${connectionId}`);
|
|
1879
1882
|
},
|
|
@@ -1948,10 +1951,6 @@ var StreamableHttpClient = class {
|
|
|
1948
1951
|
},
|
|
1949
1952
|
requestPermission: async (params) => this.handleRequestPermission(params),
|
|
1950
1953
|
extNotification: async (method, params) => {
|
|
1951
|
-
console.log("[ACP-Client] extNotification callback invoked:", {
|
|
1952
|
-
method,
|
|
1953
|
-
paramsKeys: Object.keys(params)
|
|
1954
|
-
});
|
|
1955
1954
|
await this.handleExtNotification(method, params);
|
|
1956
1955
|
},
|
|
1957
1956
|
extMethod: async (method, params) => this.handleExtMethod(method, params)
|
|
@@ -1959,10 +1958,15 @@ var StreamableHttpClient = class {
|
|
|
1959
1958
|
}
|
|
1960
1959
|
/**
|
|
1961
1960
|
* Create a new session
|
|
1961
|
+
*
|
|
1962
|
+
* Retries on transient network errors (e.g., proxy connection reset)
|
|
1963
|
+
* since session/new is idempotent and safe to retry.
|
|
1962
1964
|
*/
|
|
1963
1965
|
async createSession(cwd) {
|
|
1964
1966
|
this.ensureInitialized("createSession");
|
|
1965
|
-
|
|
1967
|
+
const maxRetries = 2;
|
|
1968
|
+
let lastError;
|
|
1969
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
1966
1970
|
const response = await this.connection.newSession({
|
|
1967
1971
|
cwd,
|
|
1968
1972
|
mcpServers: []
|
|
@@ -1970,8 +1974,16 @@ var StreamableHttpClient = class {
|
|
|
1970
1974
|
this.options.logger?.info(`Session created: ${response.sessionId}`);
|
|
1971
1975
|
return response;
|
|
1972
1976
|
} catch (err) {
|
|
1973
|
-
|
|
1977
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
1978
|
+
if (attempt < maxRetries && isRetryableNetworkError(err)) {
|
|
1979
|
+
const delay = 500 * Math.pow(2, attempt);
|
|
1980
|
+
this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
|
|
1981
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
|
|
1974
1985
|
}
|
|
1986
|
+
throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
|
|
1975
1987
|
}
|
|
1976
1988
|
/**
|
|
1977
1989
|
* Load an existing session
|
|
@@ -2195,6 +2207,18 @@ var StreamableHttpClient = class {
|
|
|
2195
2207
|
if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
|
|
2196
2208
|
}
|
|
2197
2209
|
};
|
|
2210
|
+
/**
|
|
2211
|
+
* Check if an error is a retryable network-level error.
|
|
2212
|
+
* Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
|
|
2213
|
+
*/
|
|
2214
|
+
function isRetryableNetworkError(error) {
|
|
2215
|
+
if (error instanceof TypeError) return true;
|
|
2216
|
+
if (error instanceof Error) {
|
|
2217
|
+
const msg = error.message.toLowerCase();
|
|
2218
|
+
return msg.includes("failed to fetch") || msg.includes("fetch failed") || msg.includes("network request failed") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("socket hang up");
|
|
2219
|
+
}
|
|
2220
|
+
return false;
|
|
2221
|
+
}
|
|
2198
2222
|
|
|
2199
2223
|
//#endregion
|
|
2200
2224
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
|
|
@@ -2230,7 +2254,7 @@ var CloudAgentConnection = class {
|
|
|
2230
2254
|
fetch: config.fetch,
|
|
2231
2255
|
clientCapabilities: config.clientCapabilities,
|
|
2232
2256
|
onSessionUpdate: (update) => {
|
|
2233
|
-
if (!this._isStreaming) this.emit("sessionUpdate", update);
|
|
2257
|
+
if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
|
|
2234
2258
|
},
|
|
2235
2259
|
onArtifact: (artifact, event) => {
|
|
2236
2260
|
console.log("[CloudConnection] onArtifact callback:", {
|
|
@@ -2244,10 +2268,41 @@ var CloudAgentConnection = class {
|
|
|
2244
2268
|
},
|
|
2245
2269
|
onUsageUpdate: (usage) => {
|
|
2246
2270
|
this.emit("usageUpdate", usage);
|
|
2271
|
+
},
|
|
2272
|
+
onExtNotification: (method, params) => {
|
|
2273
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2274
|
+
method,
|
|
2275
|
+
paramsKeys: Object.keys(params)
|
|
2276
|
+
});
|
|
2277
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2278
|
+
const action = params.action;
|
|
2279
|
+
const commandParams = params.params;
|
|
2280
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2281
|
+
action,
|
|
2282
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2283
|
+
});
|
|
2284
|
+
this.emit("command", {
|
|
2285
|
+
action,
|
|
2286
|
+
params: commandParams
|
|
2287
|
+
});
|
|
2288
|
+
}
|
|
2247
2289
|
}
|
|
2248
2290
|
});
|
|
2249
2291
|
this.setupEventForwarding();
|
|
2250
2292
|
}
|
|
2293
|
+
/**
|
|
2294
|
+
* Check whether a notification belongs to this connection's own session.
|
|
2295
|
+
*
|
|
2296
|
+
* CloudConnection.createSession() overrides sessionId to agentId, so the
|
|
2297
|
+
* rest of the client stack uses agentId as the canonical session
|
|
2298
|
+
* identifier. Notifications whose sessionId differs from agentId
|
|
2299
|
+
* originate from sub-agent sessions running inside the same sandbox and
|
|
2300
|
+
* should be silently ignored at this layer — the adapter layer handles
|
|
2301
|
+
* sub-agent messages independently via parentToolUseId in _meta.
|
|
2302
|
+
*/
|
|
2303
|
+
isOwnSessionNotification(notification) {
|
|
2304
|
+
return notification.sessionId === this.agentId;
|
|
2305
|
+
}
|
|
2251
2306
|
setupEventForwarding() {
|
|
2252
2307
|
this.client.on("connecting", () => {
|
|
2253
2308
|
this.emit("connecting", void 0);
|
|
@@ -2372,7 +2427,7 @@ var CloudAgentConnection = class {
|
|
|
2372
2427
|
}
|
|
2373
2428
|
async createSession(params) {
|
|
2374
2429
|
return {
|
|
2375
|
-
...await this.client.
|
|
2430
|
+
...await this.client.createSession(this.cwd),
|
|
2376
2431
|
sessionId: this.agentId
|
|
2377
2432
|
};
|
|
2378
2433
|
}
|
|
@@ -2408,6 +2463,7 @@ var CloudAgentConnection = class {
|
|
|
2408
2463
|
let resolveUpdate = null;
|
|
2409
2464
|
let done = false;
|
|
2410
2465
|
const listener = (update) => {
|
|
2466
|
+
if (!this.isOwnSessionNotification(update)) return;
|
|
2411
2467
|
if (resolveUpdate) {
|
|
2412
2468
|
resolveUpdate(update);
|
|
2413
2469
|
resolveUpdate = null;
|
|
@@ -2490,6 +2546,16 @@ var CloudAgentConnection = class {
|
|
|
2490
2546
|
get sessionConnectionInfo() {
|
|
2491
2547
|
return this._sessionConnectionInfo;
|
|
2492
2548
|
}
|
|
2549
|
+
async reportTelemetry(eventName, payload) {
|
|
2550
|
+
try {
|
|
2551
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2552
|
+
eventName,
|
|
2553
|
+
payload
|
|
2554
|
+
});
|
|
2555
|
+
} catch (error) {
|
|
2556
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2557
|
+
}
|
|
2558
|
+
}
|
|
2493
2559
|
async extMethod(method, params) {
|
|
2494
2560
|
return this.client.extMethod(method, params);
|
|
2495
2561
|
}
|
|
@@ -3092,7 +3158,7 @@ var utils_default = {
|
|
|
3092
3158
|
*
|
|
3093
3159
|
* @returns {Error} The created error.
|
|
3094
3160
|
*/
|
|
3095
|
-
function AxiosError(message, code, config, request, response) {
|
|
3161
|
+
function AxiosError$1(message, code, config, request, response) {
|
|
3096
3162
|
Error.call(this);
|
|
3097
3163
|
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
3098
3164
|
else this.stack = (/* @__PURE__ */ new Error()).stack;
|
|
@@ -3106,7 +3172,7 @@ function AxiosError(message, code, config, request, response) {
|
|
|
3106
3172
|
this.status = response.status ? response.status : null;
|
|
3107
3173
|
}
|
|
3108
3174
|
}
|
|
3109
|
-
utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
3175
|
+
utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
|
|
3110
3176
|
return {
|
|
3111
3177
|
message: this.message,
|
|
3112
3178
|
name: this.name,
|
|
@@ -3121,7 +3187,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
|
3121
3187
|
status: this.status
|
|
3122
3188
|
};
|
|
3123
3189
|
} });
|
|
3124
|
-
const prototype$1 = AxiosError.prototype;
|
|
3190
|
+
const prototype$1 = AxiosError$1.prototype;
|
|
3125
3191
|
const descriptors = {};
|
|
3126
3192
|
[
|
|
3127
3193
|
"ERR_BAD_OPTION_VALUE",
|
|
@@ -3139,22 +3205,22 @@ const descriptors = {};
|
|
|
3139
3205
|
].forEach((code) => {
|
|
3140
3206
|
descriptors[code] = { value: code };
|
|
3141
3207
|
});
|
|
3142
|
-
Object.defineProperties(AxiosError, descriptors);
|
|
3208
|
+
Object.defineProperties(AxiosError$1, descriptors);
|
|
3143
3209
|
Object.defineProperty(prototype$1, "isAxiosError", { value: true });
|
|
3144
|
-
AxiosError.from = (error, code, config, request, response, customProps) => {
|
|
3210
|
+
AxiosError$1.from = (error, code, config, request, response, customProps) => {
|
|
3145
3211
|
const axiosError = Object.create(prototype$1);
|
|
3146
3212
|
utils_default.toFlatObject(error, axiosError, function filter(obj) {
|
|
3147
3213
|
return obj !== Error.prototype;
|
|
3148
3214
|
}, (prop) => {
|
|
3149
3215
|
return prop !== "isAxiosError";
|
|
3150
3216
|
});
|
|
3151
|
-
AxiosError.call(axiosError, error.message, code, config, request, response);
|
|
3217
|
+
AxiosError$1.call(axiosError, error.message, code, config, request, response);
|
|
3152
3218
|
axiosError.cause = error;
|
|
3153
3219
|
axiosError.name = error.name;
|
|
3154
3220
|
customProps && Object.assign(axiosError, customProps);
|
|
3155
3221
|
return axiosError;
|
|
3156
3222
|
};
|
|
3157
|
-
var AxiosError_default = AxiosError;
|
|
3223
|
+
var AxiosError_default = AxiosError$1;
|
|
3158
3224
|
|
|
3159
3225
|
//#endregion
|
|
3160
3226
|
//#region ../agent-provider/node_modules/axios/lib/helpers/null.js
|
|
@@ -3233,7 +3299,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
|
|
|
3233
3299
|
*
|
|
3234
3300
|
* @returns
|
|
3235
3301
|
*/
|
|
3236
|
-
function toFormData(obj, formData, options) {
|
|
3302
|
+
function toFormData$1(obj, formData, options) {
|
|
3237
3303
|
if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
|
|
3238
3304
|
formData = formData || new (null_default || FormData)();
|
|
3239
3305
|
options = utils_default.toFlatObject(options, {
|
|
@@ -3304,7 +3370,7 @@ function toFormData(obj, formData, options) {
|
|
|
3304
3370
|
build(obj);
|
|
3305
3371
|
return formData;
|
|
3306
3372
|
}
|
|
3307
|
-
var toFormData_default = toFormData;
|
|
3373
|
+
var toFormData_default = toFormData$1;
|
|
3308
3374
|
|
|
3309
3375
|
//#endregion
|
|
3310
3376
|
//#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
|
|
@@ -3821,7 +3887,7 @@ function buildAccessors(obj, header) {
|
|
|
3821
3887
|
});
|
|
3822
3888
|
});
|
|
3823
3889
|
}
|
|
3824
|
-
var AxiosHeaders = class {
|
|
3890
|
+
var AxiosHeaders$1 = class {
|
|
3825
3891
|
constructor(headers) {
|
|
3826
3892
|
headers && this.set(headers);
|
|
3827
3893
|
}
|
|
@@ -3959,7 +4025,7 @@ var AxiosHeaders = class {
|
|
|
3959
4025
|
return this;
|
|
3960
4026
|
}
|
|
3961
4027
|
};
|
|
3962
|
-
AxiosHeaders.accessor([
|
|
4028
|
+
AxiosHeaders$1.accessor([
|
|
3963
4029
|
"Content-Type",
|
|
3964
4030
|
"Content-Length",
|
|
3965
4031
|
"Accept",
|
|
@@ -3967,7 +4033,7 @@ AxiosHeaders.accessor([
|
|
|
3967
4033
|
"User-Agent",
|
|
3968
4034
|
"Authorization"
|
|
3969
4035
|
]);
|
|
3970
|
-
utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
4036
|
+
utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
|
|
3971
4037
|
let mapped = key[0].toUpperCase() + key.slice(1);
|
|
3972
4038
|
return {
|
|
3973
4039
|
get: () => value,
|
|
@@ -3976,8 +4042,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
|
3976
4042
|
}
|
|
3977
4043
|
};
|
|
3978
4044
|
});
|
|
3979
|
-
utils_default.freezeMethods(AxiosHeaders);
|
|
3980
|
-
var AxiosHeaders_default = AxiosHeaders;
|
|
4045
|
+
utils_default.freezeMethods(AxiosHeaders$1);
|
|
4046
|
+
var AxiosHeaders_default = AxiosHeaders$1;
|
|
3981
4047
|
|
|
3982
4048
|
//#endregion
|
|
3983
4049
|
//#region ../agent-provider/node_modules/axios/lib/core/transformData.js
|
|
@@ -4003,7 +4069,7 @@ function transformData(fns, response) {
|
|
|
4003
4069
|
|
|
4004
4070
|
//#endregion
|
|
4005
4071
|
//#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
|
|
4006
|
-
function isCancel(value) {
|
|
4072
|
+
function isCancel$1(value) {
|
|
4007
4073
|
return !!(value && value.__CANCEL__);
|
|
4008
4074
|
}
|
|
4009
4075
|
|
|
@@ -4018,12 +4084,12 @@ function isCancel(value) {
|
|
|
4018
4084
|
*
|
|
4019
4085
|
* @returns {CanceledError} The created error.
|
|
4020
4086
|
*/
|
|
4021
|
-
function CanceledError(message, config, request) {
|
|
4087
|
+
function CanceledError$1(message, config, request) {
|
|
4022
4088
|
AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
|
|
4023
4089
|
this.name = "CanceledError";
|
|
4024
4090
|
}
|
|
4025
|
-
utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
|
|
4026
|
-
var CanceledError_default = CanceledError;
|
|
4091
|
+
utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
|
|
4092
|
+
var CanceledError_default = CanceledError$1;
|
|
4027
4093
|
|
|
4028
4094
|
//#endregion
|
|
4029
4095
|
//#region ../agent-provider/node_modules/axios/lib/core/settle.js
|
|
@@ -4250,7 +4316,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
|
|
|
4250
4316
|
*
|
|
4251
4317
|
* @returns {Object} New object resulting from merging config2 to config1
|
|
4252
4318
|
*/
|
|
4253
|
-
function mergeConfig(config1, config2) {
|
|
4319
|
+
function mergeConfig$1(config1, config2) {
|
|
4254
4320
|
config2 = config2 || {};
|
|
4255
4321
|
const config = {};
|
|
4256
4322
|
function getMergedValue(target, source, prop, caseless) {
|
|
@@ -4316,7 +4382,7 @@ function mergeConfig(config1, config2) {
|
|
|
4316
4382
|
//#endregion
|
|
4317
4383
|
//#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
|
|
4318
4384
|
var resolveConfig_default = (config) => {
|
|
4319
|
-
const newConfig = mergeConfig({}, config);
|
|
4385
|
+
const newConfig = mergeConfig$1({}, config);
|
|
4320
4386
|
let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
|
|
4321
4387
|
newConfig.headers = headers = AxiosHeaders_default.from(headers);
|
|
4322
4388
|
newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
@@ -4747,7 +4813,7 @@ function dispatchRequest(config) {
|
|
|
4747
4813
|
response.headers = AxiosHeaders_default.from(response.headers);
|
|
4748
4814
|
return response;
|
|
4749
4815
|
}, function onAdapterRejection(reason) {
|
|
4750
|
-
if (!isCancel(reason)) {
|
|
4816
|
+
if (!isCancel$1(reason)) {
|
|
4751
4817
|
throwIfCancellationRequested(config);
|
|
4752
4818
|
if (reason && reason.response) {
|
|
4753
4819
|
reason.response.data = transformData.call(config, config.transformResponse, reason.response);
|
|
@@ -4760,7 +4826,7 @@ function dispatchRequest(config) {
|
|
|
4760
4826
|
|
|
4761
4827
|
//#endregion
|
|
4762
4828
|
//#region ../agent-provider/node_modules/axios/lib/env/data.js
|
|
4763
|
-
const VERSION = "1.10.0";
|
|
4829
|
+
const VERSION$1 = "1.10.0";
|
|
4764
4830
|
|
|
4765
4831
|
//#endregion
|
|
4766
4832
|
//#region ../agent-provider/node_modules/axios/lib/helpers/validator.js
|
|
@@ -4789,7 +4855,7 @@ const deprecatedWarnings = {};
|
|
|
4789
4855
|
*/
|
|
4790
4856
|
validators$1.transitional = function transitional(validator, version, message) {
|
|
4791
4857
|
function formatMessage(opt, desc) {
|
|
4792
|
-
return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
4858
|
+
return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
4793
4859
|
}
|
|
4794
4860
|
return (value, opt, opts) => {
|
|
4795
4861
|
if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
|
|
@@ -4846,7 +4912,7 @@ const validators = validator_default.validators;
|
|
|
4846
4912
|
*
|
|
4847
4913
|
* @return {Axios} A new instance of Axios
|
|
4848
4914
|
*/
|
|
4849
|
-
var Axios = class {
|
|
4915
|
+
var Axios$1 = class {
|
|
4850
4916
|
constructor(instanceConfig) {
|
|
4851
4917
|
this.defaults = instanceConfig || {};
|
|
4852
4918
|
this.interceptors = {
|
|
@@ -4883,7 +4949,7 @@ var Axios = class {
|
|
|
4883
4949
|
config = config || {};
|
|
4884
4950
|
config.url = configOrUrl;
|
|
4885
4951
|
} else config = configOrUrl || {};
|
|
4886
|
-
config = mergeConfig(this.defaults, config);
|
|
4952
|
+
config = mergeConfig$1(this.defaults, config);
|
|
4887
4953
|
const { transitional, paramsSerializer, headers } = config;
|
|
4888
4954
|
if (transitional !== void 0) validator_default.assertOptions(transitional, {
|
|
4889
4955
|
silentJSONParsing: validators.transitional(validators.boolean),
|
|
@@ -4962,7 +5028,7 @@ var Axios = class {
|
|
|
4962
5028
|
return promise;
|
|
4963
5029
|
}
|
|
4964
5030
|
getUri(config) {
|
|
4965
|
-
config = mergeConfig(this.defaults, config);
|
|
5031
|
+
config = mergeConfig$1(this.defaults, config);
|
|
4966
5032
|
return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
4967
5033
|
}
|
|
4968
5034
|
};
|
|
@@ -4972,8 +5038,8 @@ utils_default.forEach([
|
|
|
4972
5038
|
"head",
|
|
4973
5039
|
"options"
|
|
4974
5040
|
], function forEachMethodNoData(method) {
|
|
4975
|
-
Axios.prototype[method] = function(url, config) {
|
|
4976
|
-
return this.request(mergeConfig(config || {}, {
|
|
5041
|
+
Axios$1.prototype[method] = function(url, config) {
|
|
5042
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
4977
5043
|
method,
|
|
4978
5044
|
url,
|
|
4979
5045
|
data: (config || {}).data
|
|
@@ -4987,7 +5053,7 @@ utils_default.forEach([
|
|
|
4987
5053
|
], function forEachMethodWithData(method) {
|
|
4988
5054
|
function generateHTTPMethod(isForm) {
|
|
4989
5055
|
return function httpMethod(url, data, config) {
|
|
4990
|
-
return this.request(mergeConfig(config || {}, {
|
|
5056
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
4991
5057
|
method,
|
|
4992
5058
|
headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
|
|
4993
5059
|
url,
|
|
@@ -4995,10 +5061,10 @@ utils_default.forEach([
|
|
|
4995
5061
|
}));
|
|
4996
5062
|
};
|
|
4997
5063
|
}
|
|
4998
|
-
Axios.prototype[method] = generateHTTPMethod();
|
|
4999
|
-
Axios.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
5064
|
+
Axios$1.prototype[method] = generateHTTPMethod();
|
|
5065
|
+
Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
5000
5066
|
});
|
|
5001
|
-
var Axios_default = Axios;
|
|
5067
|
+
var Axios_default = Axios$1;
|
|
5002
5068
|
|
|
5003
5069
|
//#endregion
|
|
5004
5070
|
//#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
|
|
@@ -5009,7 +5075,7 @@ var Axios_default = Axios;
|
|
|
5009
5075
|
*
|
|
5010
5076
|
* @returns {CancelToken}
|
|
5011
5077
|
*/
|
|
5012
|
-
var CancelToken = class CancelToken {
|
|
5078
|
+
var CancelToken$1 = class CancelToken$1 {
|
|
5013
5079
|
constructor(executor) {
|
|
5014
5080
|
if (typeof executor !== "function") throw new TypeError("executor must be a function.");
|
|
5015
5081
|
let resolvePromise;
|
|
@@ -5081,14 +5147,14 @@ var CancelToken = class CancelToken {
|
|
|
5081
5147
|
static source() {
|
|
5082
5148
|
let cancel;
|
|
5083
5149
|
return {
|
|
5084
|
-
token: new CancelToken(function executor(c) {
|
|
5150
|
+
token: new CancelToken$1(function executor(c) {
|
|
5085
5151
|
cancel = c;
|
|
5086
5152
|
}),
|
|
5087
5153
|
cancel
|
|
5088
5154
|
};
|
|
5089
5155
|
}
|
|
5090
5156
|
};
|
|
5091
|
-
var CancelToken_default = CancelToken;
|
|
5157
|
+
var CancelToken_default = CancelToken$1;
|
|
5092
5158
|
|
|
5093
5159
|
//#endregion
|
|
5094
5160
|
//#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
|
|
@@ -5113,7 +5179,7 @@ var CancelToken_default = CancelToken;
|
|
|
5113
5179
|
*
|
|
5114
5180
|
* @returns {Function}
|
|
5115
5181
|
*/
|
|
5116
|
-
function spread(callback) {
|
|
5182
|
+
function spread$1(callback) {
|
|
5117
5183
|
return function wrap(arr) {
|
|
5118
5184
|
return callback.apply(null, arr);
|
|
5119
5185
|
};
|
|
@@ -5128,13 +5194,13 @@ function spread(callback) {
|
|
|
5128
5194
|
*
|
|
5129
5195
|
* @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
|
|
5130
5196
|
*/
|
|
5131
|
-
function isAxiosError(payload) {
|
|
5197
|
+
function isAxiosError$1(payload) {
|
|
5132
5198
|
return utils_default.isObject(payload) && payload.isAxiosError === true;
|
|
5133
5199
|
}
|
|
5134
5200
|
|
|
5135
5201
|
//#endregion
|
|
5136
5202
|
//#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
|
|
5137
|
-
const HttpStatusCode = {
|
|
5203
|
+
const HttpStatusCode$1 = {
|
|
5138
5204
|
Continue: 100,
|
|
5139
5205
|
SwitchingProtocols: 101,
|
|
5140
5206
|
Processing: 102,
|
|
@@ -5199,10 +5265,10 @@ const HttpStatusCode = {
|
|
|
5199
5265
|
NotExtended: 510,
|
|
5200
5266
|
NetworkAuthenticationRequired: 511
|
|
5201
5267
|
};
|
|
5202
|
-
Object.entries(HttpStatusCode).forEach(([key, value]) => {
|
|
5203
|
-
HttpStatusCode[value] = key;
|
|
5268
|
+
Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
|
|
5269
|
+
HttpStatusCode$1[value] = key;
|
|
5204
5270
|
});
|
|
5205
|
-
var HttpStatusCode_default = HttpStatusCode;
|
|
5271
|
+
var HttpStatusCode_default = HttpStatusCode$1;
|
|
5206
5272
|
|
|
5207
5273
|
//#endregion
|
|
5208
5274
|
//#region ../agent-provider/node_modules/axios/lib/axios.js
|
|
@@ -5219,7 +5285,7 @@ function createInstance(defaultConfig) {
|
|
|
5219
5285
|
utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
|
|
5220
5286
|
utils_default.extend(instance, context, null, { allOwnKeys: true });
|
|
5221
5287
|
instance.create = function create(instanceConfig) {
|
|
5222
|
-
return createInstance(mergeConfig(defaultConfig, instanceConfig));
|
|
5288
|
+
return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
|
|
5223
5289
|
};
|
|
5224
5290
|
return instance;
|
|
5225
5291
|
}
|
|
@@ -5227,17 +5293,17 @@ const axios = createInstance(defaults_default);
|
|
|
5227
5293
|
axios.Axios = Axios_default;
|
|
5228
5294
|
axios.CanceledError = CanceledError_default;
|
|
5229
5295
|
axios.CancelToken = CancelToken_default;
|
|
5230
|
-
axios.isCancel = isCancel;
|
|
5231
|
-
axios.VERSION = VERSION;
|
|
5296
|
+
axios.isCancel = isCancel$1;
|
|
5297
|
+
axios.VERSION = VERSION$1;
|
|
5232
5298
|
axios.toFormData = toFormData_default;
|
|
5233
5299
|
axios.AxiosError = AxiosError_default;
|
|
5234
5300
|
axios.Cancel = axios.CanceledError;
|
|
5235
5301
|
axios.all = function all(promises) {
|
|
5236
5302
|
return Promise.all(promises);
|
|
5237
5303
|
};
|
|
5238
|
-
axios.spread = spread;
|
|
5239
|
-
axios.isAxiosError = isAxiosError;
|
|
5240
|
-
axios.mergeConfig = mergeConfig;
|
|
5304
|
+
axios.spread = spread$1;
|
|
5305
|
+
axios.isAxiosError = isAxiosError$1;
|
|
5306
|
+
axios.mergeConfig = mergeConfig$1;
|
|
5241
5307
|
axios.AxiosHeaders = AxiosHeaders_default;
|
|
5242
5308
|
axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
|
|
5243
5309
|
axios.getAdapter = adapters_default.getAdapter;
|
|
@@ -5245,6 +5311,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
|
|
|
5245
5311
|
axios.default = axios;
|
|
5246
5312
|
var axios_default = axios;
|
|
5247
5313
|
|
|
5314
|
+
//#endregion
|
|
5315
|
+
//#region ../agent-provider/node_modules/axios/index.js
|
|
5316
|
+
const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
|
|
5317
|
+
|
|
5248
5318
|
//#endregion
|
|
5249
5319
|
//#region ../agent-provider/src/http/http-service.ts
|
|
5250
5320
|
/**
|
|
@@ -5253,7 +5323,7 @@ var axios_default = axios;
|
|
|
5253
5323
|
* 特性:
|
|
5254
5324
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
5255
5325
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
5256
|
-
* - 统一 401
|
|
5326
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
5257
5327
|
* - 自动携带凭证(withCredentials)
|
|
5258
5328
|
* - 类型安全
|
|
5259
5329
|
*
|
|
@@ -5293,6 +5363,8 @@ var HttpService = class HttpService {
|
|
|
5293
5363
|
*/
|
|
5294
5364
|
constructor(config = {}) {
|
|
5295
5365
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
5366
|
+
this.isRefreshing = false;
|
|
5367
|
+
this.refreshSubscribers = [];
|
|
5296
5368
|
this.config = config;
|
|
5297
5369
|
this.axiosInstance = axios_default.create({
|
|
5298
5370
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -5333,18 +5405,54 @@ var HttpService = class HttpService {
|
|
|
5333
5405
|
}, (error) => Promise.reject(error));
|
|
5334
5406
|
}
|
|
5335
5407
|
/**
|
|
5336
|
-
* 注册默认响应拦截器(处理 401
|
|
5408
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
5337
5409
|
*/
|
|
5338
5410
|
registerDefaultResponseInterceptor() {
|
|
5339
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5411
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
5412
|
+
const originalRequest = error.config;
|
|
5413
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
5414
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
5415
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
5416
|
+
return Promise.reject(error);
|
|
5417
|
+
}
|
|
5418
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
5419
|
+
originalRequest._retry = true;
|
|
5420
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
5421
|
+
this.refreshSubscribers.push((success) => {
|
|
5422
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
5423
|
+
else reject(error);
|
|
5424
|
+
});
|
|
5425
|
+
});
|
|
5426
|
+
this.isRefreshing = true;
|
|
5427
|
+
try {
|
|
5428
|
+
await this.triggerUnauthorizedCallbacks();
|
|
5429
|
+
this.onRefreshSuccess();
|
|
5430
|
+
return this.axiosInstance.request(originalRequest);
|
|
5431
|
+
} catch (refreshError) {
|
|
5432
|
+
this.onRefreshFailure();
|
|
5433
|
+
return Promise.reject(error);
|
|
5434
|
+
} finally {
|
|
5435
|
+
this.isRefreshing = false;
|
|
5436
|
+
}
|
|
5343
5437
|
}
|
|
5344
5438
|
return Promise.reject(error);
|
|
5345
5439
|
});
|
|
5346
5440
|
}
|
|
5347
5441
|
/**
|
|
5442
|
+
* token 刷新成功,通知所有等待的请求
|
|
5443
|
+
*/
|
|
5444
|
+
onRefreshSuccess() {
|
|
5445
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
5446
|
+
this.refreshSubscribers = [];
|
|
5447
|
+
}
|
|
5448
|
+
/**
|
|
5449
|
+
* token 刷新失败,通知所有等待的请求
|
|
5450
|
+
*/
|
|
5451
|
+
onRefreshFailure() {
|
|
5452
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
5453
|
+
this.refreshSubscribers = [];
|
|
5454
|
+
}
|
|
5455
|
+
/**
|
|
5348
5456
|
* 注册请求拦截器
|
|
5349
5457
|
* @param onFulfilled 请求成功拦截器
|
|
5350
5458
|
* @param onRejected 请求失败拦截器
|
|
@@ -5421,16 +5529,18 @@ var HttpService = class HttpService {
|
|
|
5421
5529
|
this.unauthorizedCallbacks.delete(callback);
|
|
5422
5530
|
}
|
|
5423
5531
|
/**
|
|
5424
|
-
* 触发所有 401
|
|
5532
|
+
* 触发所有 401 回调并等待完成
|
|
5533
|
+
* @returns Promise,等待所有回调完成
|
|
5534
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
5425
5535
|
*/
|
|
5426
|
-
triggerUnauthorizedCallbacks() {
|
|
5427
|
-
this.unauthorizedCallbacks
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
}
|
|
5536
|
+
async triggerUnauthorizedCallbacks() {
|
|
5537
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
5538
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
5539
|
+
if (failedResults.length > 0) {
|
|
5540
|
+
const errors = failedResults.map((r) => r.reason);
|
|
5541
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
5542
|
+
throw errors[0];
|
|
5543
|
+
}
|
|
5434
5544
|
}
|
|
5435
5545
|
/**
|
|
5436
5546
|
* 更新 authToken
|
|
@@ -5542,6 +5652,7 @@ var AccountService = class {
|
|
|
5542
5652
|
this.initPromise = null;
|
|
5543
5653
|
this.initResolve = null;
|
|
5544
5654
|
this.requestInterceptorId = null;
|
|
5655
|
+
this.crossTabBroadcaster = null;
|
|
5545
5656
|
this.initPromise = new Promise((resolve) => {
|
|
5546
5657
|
this.initResolve = resolve;
|
|
5547
5658
|
});
|
|
@@ -5590,15 +5701,34 @@ var AccountService = class {
|
|
|
5590
5701
|
this.initialized = true;
|
|
5591
5702
|
this.initResolve?.(account);
|
|
5592
5703
|
}
|
|
5593
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
5704
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
5705
|
+
this.notifyListeners();
|
|
5706
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
5707
|
+
}
|
|
5594
5708
|
}
|
|
5595
5709
|
/**
|
|
5596
5710
|
* 清除账号(登出)
|
|
5711
|
+
* 先广播登出消息,再清除本地账号
|
|
5597
5712
|
*/
|
|
5598
5713
|
clearAccount() {
|
|
5714
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
5599
5715
|
this.setAccount(null);
|
|
5600
5716
|
}
|
|
5601
5717
|
/**
|
|
5718
|
+
* 静默清除账号(不广播)
|
|
5719
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
5720
|
+
*/
|
|
5721
|
+
clearAccountSilently() {
|
|
5722
|
+
this.setAccount(null);
|
|
5723
|
+
}
|
|
5724
|
+
/**
|
|
5725
|
+
* 设置跨标签页认证同步广播器
|
|
5726
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
5727
|
+
*/
|
|
5728
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
5729
|
+
this.crossTabBroadcaster = broadcaster;
|
|
5730
|
+
}
|
|
5731
|
+
/**
|
|
5602
5732
|
* 订阅账号变化
|
|
5603
5733
|
* @param callback 变化时的回调函数
|
|
5604
5734
|
* @returns 取消订阅函数
|
|
@@ -5663,6 +5793,114 @@ var AccountService = class {
|
|
|
5663
5793
|
* 导出单例实例
|
|
5664
5794
|
*/
|
|
5665
5795
|
const accountService = new AccountService();
|
|
5796
|
+
/**
|
|
5797
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
5798
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
5799
|
+
*/
|
|
5800
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
5801
|
+
|
|
5802
|
+
//#endregion
|
|
5803
|
+
//#region ../agent-provider/src/common/utils/lru-cache.ts
|
|
5804
|
+
/**
|
|
5805
|
+
* LRU (Least Recently Used) Cache
|
|
5806
|
+
* 当缓存达到容量上限时,自动淘汰最久未使用的数据
|
|
5807
|
+
*
|
|
5808
|
+
* @template K - Key 类型
|
|
5809
|
+
* @template V - Value 类型
|
|
5810
|
+
*/
|
|
5811
|
+
var LRUCache = class {
|
|
5812
|
+
/**
|
|
5813
|
+
* 创建 LRU 缓存实例
|
|
5814
|
+
* @param capacity - 缓存容量上限
|
|
5815
|
+
*/
|
|
5816
|
+
constructor(capacity) {
|
|
5817
|
+
if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
|
|
5818
|
+
this.capacity = capacity;
|
|
5819
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
5820
|
+
}
|
|
5821
|
+
/**
|
|
5822
|
+
* 获取缓存值
|
|
5823
|
+
* @param key - 键
|
|
5824
|
+
* @returns 值,如果不存在返回 undefined
|
|
5825
|
+
*/
|
|
5826
|
+
get(key) {
|
|
5827
|
+
if (!this.cache.has(key)) return;
|
|
5828
|
+
const value = this.cache.get(key);
|
|
5829
|
+
this.cache.delete(key);
|
|
5830
|
+
this.cache.set(key, value);
|
|
5831
|
+
return value;
|
|
5832
|
+
}
|
|
5833
|
+
/**
|
|
5834
|
+
* 设置缓存值
|
|
5835
|
+
* @param key - 键
|
|
5836
|
+
* @param value - 值
|
|
5837
|
+
*/
|
|
5838
|
+
set(key, value) {
|
|
5839
|
+
if (this.cache.has(key)) this.cache.delete(key);
|
|
5840
|
+
else if (this.cache.size >= this.capacity) {
|
|
5841
|
+
const firstKey = this.cache.keys().next().value;
|
|
5842
|
+
this.cache.delete(firstKey);
|
|
5843
|
+
}
|
|
5844
|
+
this.cache.set(key, value);
|
|
5845
|
+
}
|
|
5846
|
+
/**
|
|
5847
|
+
* 检查 key 是否存在
|
|
5848
|
+
* @param key - 键
|
|
5849
|
+
* @returns 是否存在
|
|
5850
|
+
*/
|
|
5851
|
+
has(key) {
|
|
5852
|
+
return this.cache.has(key);
|
|
5853
|
+
}
|
|
5854
|
+
/**
|
|
5855
|
+
* 删除指定 key
|
|
5856
|
+
* @param key - 键
|
|
5857
|
+
* @returns 是否删除成功
|
|
5858
|
+
*/
|
|
5859
|
+
delete(key) {
|
|
5860
|
+
return this.cache.delete(key);
|
|
5861
|
+
}
|
|
5862
|
+
/**
|
|
5863
|
+
* 清空缓存
|
|
5864
|
+
*/
|
|
5865
|
+
clear() {
|
|
5866
|
+
this.cache.clear();
|
|
5867
|
+
}
|
|
5868
|
+
/**
|
|
5869
|
+
* 获取当前缓存大小
|
|
5870
|
+
* @returns 当前缓存的元素数量
|
|
5871
|
+
*/
|
|
5872
|
+
get size() {
|
|
5873
|
+
return this.cache.size;
|
|
5874
|
+
}
|
|
5875
|
+
/**
|
|
5876
|
+
* 获取缓存容量
|
|
5877
|
+
* @returns 缓存容量上限
|
|
5878
|
+
*/
|
|
5879
|
+
get maxSize() {
|
|
5880
|
+
return this.capacity;
|
|
5881
|
+
}
|
|
5882
|
+
/**
|
|
5883
|
+
* 获取所有 key
|
|
5884
|
+
* @returns key 数组
|
|
5885
|
+
*/
|
|
5886
|
+
keys() {
|
|
5887
|
+
return Array.from(this.cache.keys());
|
|
5888
|
+
}
|
|
5889
|
+
/**
|
|
5890
|
+
* 获取所有 value
|
|
5891
|
+
* @returns value 数组
|
|
5892
|
+
*/
|
|
5893
|
+
values() {
|
|
5894
|
+
return Array.from(this.cache.values());
|
|
5895
|
+
}
|
|
5896
|
+
/**
|
|
5897
|
+
* 遍历缓存
|
|
5898
|
+
* @param callback - 回调函数
|
|
5899
|
+
*/
|
|
5900
|
+
forEach(callback) {
|
|
5901
|
+
this.cache.forEach((value, key) => callback(value, key));
|
|
5902
|
+
}
|
|
5903
|
+
};
|
|
5666
5904
|
|
|
5667
5905
|
//#endregion
|
|
5668
5906
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -5765,20 +6003,32 @@ var CosUploadService = class {
|
|
|
5765
6003
|
* 上传单个文件到 COS
|
|
5766
6004
|
*
|
|
5767
6005
|
* @param file - 要上传的文件
|
|
6006
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
5768
6007
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
5769
6008
|
*/
|
|
5770
|
-
async uploadFile(file) {
|
|
6009
|
+
async uploadFile(file, abortSignal) {
|
|
5771
6010
|
const filename = file.name;
|
|
5772
6011
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
5773
6012
|
try {
|
|
6013
|
+
if (abortSignal?.aborted) return {
|
|
6014
|
+
success: false,
|
|
6015
|
+
error: "Upload cancelled",
|
|
6016
|
+
aborted: true
|
|
6017
|
+
};
|
|
5774
6018
|
const objectKey = this.generateObjectKey(filename);
|
|
5775
6019
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
5776
6020
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
5777
6021
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
6022
|
+
if (abortSignal?.aborted) return {
|
|
6023
|
+
success: false,
|
|
6024
|
+
error: "Upload cancelled",
|
|
6025
|
+
aborted: true
|
|
6026
|
+
};
|
|
5778
6027
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
5779
6028
|
method: "PUT",
|
|
5780
6029
|
body: file,
|
|
5781
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
6030
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
6031
|
+
signal: abortSignal
|
|
5782
6032
|
});
|
|
5783
6033
|
if (!uploadResponse.ok) {
|
|
5784
6034
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -5792,6 +6042,11 @@ var CosUploadService = class {
|
|
|
5792
6042
|
objectKey
|
|
5793
6043
|
};
|
|
5794
6044
|
} catch (error) {
|
|
6045
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
6046
|
+
success: false,
|
|
6047
|
+
error: "Upload cancelled",
|
|
6048
|
+
aborted: true
|
|
6049
|
+
};
|
|
5795
6050
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
5796
6051
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
5797
6052
|
return {
|
|
@@ -5806,14 +6061,25 @@ var CosUploadService = class {
|
|
|
5806
6061
|
* 使用并发控制,限制同时上传的文件数量
|
|
5807
6062
|
*
|
|
5808
6063
|
* @param files - 要上传的文件数组
|
|
6064
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
5809
6065
|
* @returns 所有文件的上传结果
|
|
5810
6066
|
*/
|
|
5811
|
-
async uploadFiles(files) {
|
|
6067
|
+
async uploadFiles(files, abortSignal) {
|
|
5812
6068
|
if (files.length === 0) return {
|
|
5813
6069
|
success: true,
|
|
5814
6070
|
urls: [],
|
|
5815
6071
|
results: []
|
|
5816
6072
|
};
|
|
6073
|
+
if (abortSignal?.aborted) return {
|
|
6074
|
+
success: false,
|
|
6075
|
+
error: "Upload cancelled",
|
|
6076
|
+
aborted: true,
|
|
6077
|
+
results: files.map(() => ({
|
|
6078
|
+
success: false,
|
|
6079
|
+
error: "Upload cancelled",
|
|
6080
|
+
aborted: true
|
|
6081
|
+
}))
|
|
6082
|
+
};
|
|
5817
6083
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
5818
6084
|
try {
|
|
5819
6085
|
const fileInfos = files.map((file) => ({
|
|
@@ -5825,6 +6091,12 @@ var CosUploadService = class {
|
|
|
5825
6091
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
5826
6092
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
5827
6093
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
6094
|
+
if (abortSignal?.aborted) return {
|
|
6095
|
+
success: false,
|
|
6096
|
+
error: "Upload cancelled",
|
|
6097
|
+
aborted: true,
|
|
6098
|
+
objectKey: fileInfos[index].objectKey
|
|
6099
|
+
};
|
|
5828
6100
|
const presignedItem = presignedResponse.items[index];
|
|
5829
6101
|
if (!presignedItem) return {
|
|
5830
6102
|
success: false,
|
|
@@ -5835,7 +6107,8 @@ var CosUploadService = class {
|
|
|
5835
6107
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
5836
6108
|
method: "PUT",
|
|
5837
6109
|
body: file,
|
|
5838
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
6110
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
6111
|
+
signal: abortSignal
|
|
5839
6112
|
});
|
|
5840
6113
|
if (!uploadResponse.ok) {
|
|
5841
6114
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -5852,6 +6125,12 @@ var CosUploadService = class {
|
|
|
5852
6125
|
objectKey: presignedItem.object_key
|
|
5853
6126
|
};
|
|
5854
6127
|
} catch (error) {
|
|
6128
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
6129
|
+
success: false,
|
|
6130
|
+
error: "Upload cancelled",
|
|
6131
|
+
aborted: true,
|
|
6132
|
+
objectKey: presignedItem.object_key
|
|
6133
|
+
};
|
|
5855
6134
|
return {
|
|
5856
6135
|
success: false,
|
|
5857
6136
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -5905,35 +6184,35 @@ var CosUploadService = class {
|
|
|
5905
6184
|
* E2B Filesystem Implementation
|
|
5906
6185
|
*
|
|
5907
6186
|
* Provides FilesResource implementation using E2B Sandbox SDK.
|
|
5908
|
-
*
|
|
6187
|
+
* Supports optional auto-reconnect on auth failure (401/403).
|
|
5909
6188
|
*
|
|
5910
6189
|
* @see https://e2b.dev/docs/filesystem/read-write
|
|
5911
6190
|
* @see https://e2b.dev/docs/filesystem/watch
|
|
5912
6191
|
*/
|
|
5913
6192
|
/**
|
|
5914
|
-
* E2B Filesystem
|
|
6193
|
+
* E2B Filesystem
|
|
5915
6194
|
*
|
|
5916
|
-
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource
|
|
6195
|
+
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource.
|
|
6196
|
+
* When `reconnectFn` is provided, automatically reconnects on auth errors.
|
|
5917
6197
|
*
|
|
5918
6198
|
* @example
|
|
5919
6199
|
* ```typescript
|
|
5920
|
-
*
|
|
5921
|
-
*
|
|
5922
|
-
* apiKey: 'e2b_xxx'
|
|
5923
|
-
* });
|
|
5924
|
-
*
|
|
5925
|
-
* // Read/write files
|
|
5926
|
-
* await fs.write('/test.txt', 'Hello World');
|
|
5927
|
-
* const content = await fs.read('/test.txt');
|
|
6200
|
+
* // Basic usage (no auto-reconnect)
|
|
6201
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
5928
6202
|
*
|
|
5929
|
-
* //
|
|
5930
|
-
* const
|
|
5931
|
-
*
|
|
5932
|
-
* });
|
|
6203
|
+
* // With auto-reconnect on token expiry
|
|
6204
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
6205
|
+
* fs.setReconnectFn(async () => fetchFreshConnectionInfo());
|
|
5933
6206
|
* ```
|
|
5934
6207
|
*/
|
|
5935
6208
|
var E2BFilesystem = class E2BFilesystem {
|
|
6209
|
+
static {
|
|
6210
|
+
this.MIN_RECONNECT_INTERVAL_MS = 10 * 1e3;
|
|
6211
|
+
}
|
|
5936
6212
|
constructor(sandbox) {
|
|
6213
|
+
this.isReconnecting = false;
|
|
6214
|
+
this.reconnectSubscribers = [];
|
|
6215
|
+
this.lastReconnectAt = 0;
|
|
5937
6216
|
this.sandbox = sandbox;
|
|
5938
6217
|
}
|
|
5939
6218
|
/**
|
|
@@ -5949,38 +6228,111 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
5949
6228
|
}));
|
|
5950
6229
|
}
|
|
5951
6230
|
/**
|
|
6231
|
+
* Set reconnect callback. When set, auth errors trigger automatic reconnect + retry.
|
|
6232
|
+
*/
|
|
6233
|
+
setReconnectFn(fn) {
|
|
6234
|
+
this.reconnectFn = fn;
|
|
6235
|
+
}
|
|
6236
|
+
/**
|
|
5952
6237
|
* Get the underlying E2B Sandbox instance
|
|
5953
6238
|
*/
|
|
5954
6239
|
getSandbox() {
|
|
5955
6240
|
return this.sandbox;
|
|
5956
6241
|
}
|
|
6242
|
+
isAuthError(error) {
|
|
6243
|
+
if (!error || typeof error !== "object") return false;
|
|
6244
|
+
const err = error;
|
|
6245
|
+
if (err.status === 401 || err.status === 403) return true;
|
|
6246
|
+
if (err.statusCode === 401 || err.statusCode === 403) return true;
|
|
6247
|
+
if (err.response && typeof err.response === "object") {
|
|
6248
|
+
const resp = err.response;
|
|
6249
|
+
if (resp.status === 401 || resp.status === 403) return true;
|
|
6250
|
+
}
|
|
6251
|
+
if (typeof err.message === "string") {
|
|
6252
|
+
const msg = err.message.toLowerCase();
|
|
6253
|
+
if (msg.includes("unauthorized") || msg.includes("token expired") || msg.includes("authentication")) return true;
|
|
6254
|
+
}
|
|
6255
|
+
return false;
|
|
6256
|
+
}
|
|
6257
|
+
canAttemptReconnect() {
|
|
6258
|
+
return Date.now() - this.lastReconnectAt >= E2BFilesystem.MIN_RECONNECT_INTERVAL_MS;
|
|
6259
|
+
}
|
|
6260
|
+
/**
|
|
6261
|
+
* Reconnect with fresh credentials.
|
|
6262
|
+
* Only one reconnect in-flight at a time; concurrent callers share the result.
|
|
6263
|
+
*/
|
|
6264
|
+
async reconnect() {
|
|
6265
|
+
if (this.isReconnecting) return new Promise((resolve, reject) => {
|
|
6266
|
+
this.reconnectSubscribers.push((success) => {
|
|
6267
|
+
if (success) resolve();
|
|
6268
|
+
else reject(/* @__PURE__ */ new Error("E2B sandbox reconnect failed"));
|
|
6269
|
+
});
|
|
6270
|
+
});
|
|
6271
|
+
this.isReconnecting = true;
|
|
6272
|
+
this.lastReconnectAt = Date.now();
|
|
6273
|
+
try {
|
|
6274
|
+
const info = await this.reconnectFn();
|
|
6275
|
+
this.sandbox = await Sandbox.connect(info.sandboxId, {
|
|
6276
|
+
domain: info.domain,
|
|
6277
|
+
apiUrl: info.apiUrl,
|
|
6278
|
+
requestTimeoutMs: info.requestTimeoutMs,
|
|
6279
|
+
debug: info.debug,
|
|
6280
|
+
headers: info.headers
|
|
6281
|
+
});
|
|
6282
|
+
this.reconnectSubscribers.forEach((cb) => cb(true));
|
|
6283
|
+
this.reconnectSubscribers = [];
|
|
6284
|
+
} catch (error) {
|
|
6285
|
+
this.reconnectSubscribers.forEach((cb) => cb(false));
|
|
6286
|
+
this.reconnectSubscribers = [];
|
|
6287
|
+
throw error;
|
|
6288
|
+
} finally {
|
|
6289
|
+
this.isReconnecting = false;
|
|
6290
|
+
}
|
|
6291
|
+
}
|
|
6292
|
+
/**
|
|
6293
|
+
* Execute an operation. If reconnectFn is set and an auth error occurs,
|
|
6294
|
+
* reconnect and retry once.
|
|
6295
|
+
*/
|
|
6296
|
+
async exec(operation) {
|
|
6297
|
+
try {
|
|
6298
|
+
return await operation(this.sandbox.files);
|
|
6299
|
+
} catch (error) {
|
|
6300
|
+
if (this.reconnectFn && this.isAuthError(error) && this.canAttemptReconnect()) {
|
|
6301
|
+
await this.reconnect();
|
|
6302
|
+
return operation(this.sandbox.files);
|
|
6303
|
+
}
|
|
6304
|
+
throw error;
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
5957
6307
|
read(path, opts) {
|
|
5958
|
-
return this.
|
|
6308
|
+
return this.exec((f) => f.read(path, opts));
|
|
5959
6309
|
}
|
|
5960
6310
|
write(pathOrFiles, dataOrOpts, opts) {
|
|
5961
|
-
|
|
5962
|
-
|
|
6311
|
+
return this.exec((f) => {
|
|
6312
|
+
if (Array.isArray(pathOrFiles)) return f.write(pathOrFiles, dataOrOpts);
|
|
6313
|
+
return f.write(pathOrFiles, dataOrOpts, opts);
|
|
6314
|
+
});
|
|
5963
6315
|
}
|
|
5964
6316
|
async list(path, opts) {
|
|
5965
|
-
return this.
|
|
6317
|
+
return this.exec((f) => f.list(path, opts));
|
|
5966
6318
|
}
|
|
5967
6319
|
async exists(path, opts) {
|
|
5968
|
-
return this.
|
|
6320
|
+
return this.exec((f) => f.exists(path, opts));
|
|
5969
6321
|
}
|
|
5970
6322
|
async makeDir(path, opts) {
|
|
5971
|
-
return this.
|
|
6323
|
+
return this.exec((f) => f.makeDir(path, opts));
|
|
5972
6324
|
}
|
|
5973
6325
|
async remove(path, opts) {
|
|
5974
|
-
return this.
|
|
6326
|
+
return this.exec((f) => f.remove(path, opts));
|
|
5975
6327
|
}
|
|
5976
6328
|
async rename(oldPath, newPath, opts) {
|
|
5977
|
-
return this.
|
|
6329
|
+
return this.exec((f) => f.rename(oldPath, newPath, opts));
|
|
5978
6330
|
}
|
|
5979
6331
|
async getInfo(path, opts) {
|
|
5980
|
-
return this.
|
|
6332
|
+
return this.exec((f) => f.getInfo(path, opts));
|
|
5981
6333
|
}
|
|
5982
6334
|
async watchDir(path, onEvent, opts) {
|
|
5983
|
-
return this.
|
|
6335
|
+
return this.exec((f) => f.watchDir(path, onEvent, opts));
|
|
5984
6336
|
}
|
|
5985
6337
|
};
|
|
5986
6338
|
|
|
@@ -6132,8 +6484,11 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6132
6484
|
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
6133
6485
|
this.connectionCache = /* @__PURE__ */ new Map();
|
|
6134
6486
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
6487
|
+
this.productConfigCache = null;
|
|
6135
6488
|
this.options = options;
|
|
6136
6489
|
this.logger = options.logger;
|
|
6490
|
+
this.marketplaceCache = new LRUCache(200);
|
|
6491
|
+
this.pluginCache = new LRUCache(2e3);
|
|
6137
6492
|
if (options.endpoint) httpService.setBaseURL(options.endpoint);
|
|
6138
6493
|
if (options.authToken) httpService.setAuthToken(options.authToken);
|
|
6139
6494
|
if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
|
|
@@ -6184,7 +6539,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6184
6539
|
const cached = this.filesystemCache.get(agentId);
|
|
6185
6540
|
if (cached) return cached;
|
|
6186
6541
|
const info = await this.getSandboxInfo(agentId);
|
|
6187
|
-
const
|
|
6542
|
+
const e2bFilesystem = await E2BFilesystem.connect(info);
|
|
6543
|
+
e2bFilesystem.setReconnectFn(async () => {
|
|
6544
|
+
this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
|
|
6545
|
+
const newInfo = await this.getSandboxInfo(agentId);
|
|
6546
|
+
this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
|
|
6547
|
+
return newInfo;
|
|
6548
|
+
});
|
|
6549
|
+
const filesystem = createAgentFilesystem(e2bFilesystem);
|
|
6188
6550
|
this.filesystemCache.set(agentId, filesystem);
|
|
6189
6551
|
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
6190
6552
|
return filesystem;
|
|
@@ -6252,15 +6614,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6252
6614
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
6253
6615
|
const apiResponse = await httpService.get(url);
|
|
6254
6616
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
6255
|
-
const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
|
|
6256
|
-
const pagination = apiResponse.data.pagination;
|
|
6257
|
-
console.log("[CloudAgentProvider] API response:", {
|
|
6258
|
-
agentsCount: agents.length,
|
|
6259
|
-
pagination
|
|
6260
|
-
});
|
|
6261
6617
|
return {
|
|
6262
|
-
agents,
|
|
6263
|
-
pagination
|
|
6618
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
6619
|
+
pagination: apiResponse.data.pagination
|
|
6264
6620
|
};
|
|
6265
6621
|
} catch (error) {
|
|
6266
6622
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -6270,13 +6626,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6270
6626
|
/**
|
|
6271
6627
|
* Create a new conversation
|
|
6272
6628
|
* POST {endpoint}/console/as/conversations
|
|
6629
|
+
* @param params - Session params containing cwd and optional configuration
|
|
6273
6630
|
*/
|
|
6274
|
-
async create() {
|
|
6631
|
+
async create(params) {
|
|
6275
6632
|
try {
|
|
6276
|
-
const
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
});
|
|
6633
|
+
const { options = {} } = params;
|
|
6634
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
6635
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
6636
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
6637
|
+
const createPayload = {
|
|
6638
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
6639
|
+
model: options.model || "deepseek-r1",
|
|
6640
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
6641
|
+
};
|
|
6642
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
6643
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
6280
6644
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
6281
6645
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
6282
6646
|
return apiResponse.data.id;
|
|
@@ -6326,7 +6690,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6326
6690
|
} catch (error) {
|
|
6327
6691
|
this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
|
|
6328
6692
|
}
|
|
6329
|
-
|
|
6693
|
+
const existingConnection = this.connectionCache.get(endpoint);
|
|
6694
|
+
if (existingConnection) {
|
|
6695
|
+
this.connectionCache.delete(endpoint);
|
|
6696
|
+
existingConnection.removeAllListeners();
|
|
6697
|
+
existingConnection.disconnect().catch((err) => {
|
|
6698
|
+
this.logger?.debug("Failed to disconnect old connection:", err);
|
|
6699
|
+
});
|
|
6700
|
+
}
|
|
6330
6701
|
const clientCapabilities = {
|
|
6331
6702
|
...this.options.clientCapabilities,
|
|
6332
6703
|
_meta: {
|
|
@@ -6446,6 +6817,35 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6446
6817
|
}
|
|
6447
6818
|
}
|
|
6448
6819
|
/**
|
|
6820
|
+
* Update conversation status by ID
|
|
6821
|
+
* POST {endpoint}/console/as/conversations/{agentId}
|
|
6822
|
+
*
|
|
6823
|
+
* @param agentId - Conversation ID to update
|
|
6824
|
+
* @param status - New status for the conversation
|
|
6825
|
+
* @returns PatchConversationResponse containing the updated conversation ID
|
|
6826
|
+
*
|
|
6827
|
+
* @example
|
|
6828
|
+
* ```typescript
|
|
6829
|
+
* const result = await provider.updateStatus('agent-123', 'completed');
|
|
6830
|
+
* console.log('Updated conversation status:', result.id);
|
|
6831
|
+
* ```
|
|
6832
|
+
*/
|
|
6833
|
+
async updateStatus(agentId, status) {
|
|
6834
|
+
try {
|
|
6835
|
+
const body = { status };
|
|
6836
|
+
const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
|
|
6837
|
+
if (!apiResponse.data) {
|
|
6838
|
+
this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
|
|
6839
|
+
return { id: agentId };
|
|
6840
|
+
}
|
|
6841
|
+
this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
|
|
6842
|
+
return apiResponse.data;
|
|
6843
|
+
} catch (error) {
|
|
6844
|
+
this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
|
|
6845
|
+
throw error;
|
|
6846
|
+
}
|
|
6847
|
+
}
|
|
6848
|
+
/**
|
|
6449
6849
|
* Get available models from product configuration
|
|
6450
6850
|
*
|
|
6451
6851
|
* GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
|
|
@@ -6476,9 +6876,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6476
6876
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
6477
6877
|
return [];
|
|
6478
6878
|
}
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6879
|
+
this.productConfigCache = apiResponse.data;
|
|
6880
|
+
const productConfig = apiResponse.data;
|
|
6881
|
+
const allModels = productConfig.models ?? [];
|
|
6882
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
6883
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
6884
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
6885
|
+
return filteredModels.map((model) => ({
|
|
6482
6886
|
id: model.id,
|
|
6483
6887
|
name: model.name ?? model.id,
|
|
6484
6888
|
description: model.description,
|
|
@@ -6500,6 +6904,43 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6500
6904
|
}
|
|
6501
6905
|
}
|
|
6502
6906
|
/**
|
|
6907
|
+
* 获取产品部署类型(从缓存)
|
|
6908
|
+
* 需要先调用 getModels() 初始化缓存
|
|
6909
|
+
*
|
|
6910
|
+
* @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
|
|
6911
|
+
*/
|
|
6912
|
+
getDeploymentType() {
|
|
6913
|
+
return this.productConfigCache?.deploymentType ?? "SaaS";
|
|
6914
|
+
}
|
|
6915
|
+
/**
|
|
6916
|
+
* 获取 Credit 购买引导配置(从缓存)
|
|
6917
|
+
* 需要先调用 getModels() 初始化缓存
|
|
6918
|
+
*
|
|
6919
|
+
* @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
|
|
6920
|
+
*/
|
|
6921
|
+
getCreditPurchaseActions() {
|
|
6922
|
+
return this.productConfigCache?.config?.creditPurchaseActions ?? {
|
|
6923
|
+
"14018": {
|
|
6924
|
+
labelZh: "获取 Credits",
|
|
6925
|
+
labelEn: "Get credits",
|
|
6926
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6927
|
+
showButton: true
|
|
6928
|
+
},
|
|
6929
|
+
"6004": {
|
|
6930
|
+
labelZh: "升级专业版",
|
|
6931
|
+
labelEn: "Upgrade to Pro",
|
|
6932
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6933
|
+
showButton: true
|
|
6934
|
+
},
|
|
6935
|
+
"6005": {
|
|
6936
|
+
labelZh: "升级专业版",
|
|
6937
|
+
labelEn: "Upgrade to Pro",
|
|
6938
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6939
|
+
showButton: true
|
|
6940
|
+
}
|
|
6941
|
+
};
|
|
6942
|
+
}
|
|
6943
|
+
/**
|
|
6503
6944
|
* Generate a unique request ID
|
|
6504
6945
|
*/
|
|
6505
6946
|
generateRequestId() {
|
|
@@ -6582,7 +7023,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6582
7023
|
/**
|
|
6583
7024
|
* Upload files to cloud storage via COS presigned URL
|
|
6584
7025
|
*
|
|
6585
|
-
* @param params - files array (File objects in browser)
|
|
7026
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
6586
7027
|
* @returns Response with corresponding cloud URLs
|
|
6587
7028
|
*/
|
|
6588
7029
|
async uploadFile(params) {
|
|
@@ -6592,12 +7033,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6592
7033
|
success: false,
|
|
6593
7034
|
error: "No valid File objects provided"
|
|
6594
7035
|
};
|
|
6595
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
7036
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
6596
7037
|
return {
|
|
6597
7038
|
success: result.success,
|
|
6598
7039
|
urls: result.urls,
|
|
6599
7040
|
expireSeconds: result.expireSeconds,
|
|
6600
|
-
error: result.error
|
|
7041
|
+
error: result.error,
|
|
7042
|
+
aborted: result.aborted
|
|
6601
7043
|
};
|
|
6602
7044
|
}
|
|
6603
7045
|
/**
|
|
@@ -6639,20 +7081,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6639
7081
|
}
|
|
6640
7082
|
/**
|
|
6641
7083
|
* 获取支持的场景列表
|
|
6642
|
-
* API 端点: GET /
|
|
7084
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
6643
7085
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
6644
7086
|
*
|
|
7087
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
6645
7088
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
6646
7089
|
*/
|
|
6647
|
-
async getSupportScenes() {
|
|
7090
|
+
async getSupportScenes(locale) {
|
|
6648
7091
|
try {
|
|
6649
|
-
const
|
|
7092
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
7093
|
+
const apiResponse = await httpService.get(url);
|
|
6650
7094
|
if (!apiResponse.data) {
|
|
6651
7095
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
6652
7096
|
return [];
|
|
6653
7097
|
}
|
|
6654
7098
|
const scenes = apiResponse.data.scenes || [];
|
|
6655
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
7099
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
6656
7100
|
return scenes;
|
|
6657
7101
|
} catch (error) {
|
|
6658
7102
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -6668,7 +7112,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6668
7112
|
type: "cloud",
|
|
6669
7113
|
status,
|
|
6670
7114
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
6671
|
-
capabilities: this.options.clientCapabilities
|
|
7115
|
+
capabilities: this.options.clientCapabilities,
|
|
7116
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
6672
7117
|
};
|
|
6673
7118
|
}
|
|
6674
7119
|
/**
|
|
@@ -6684,55 +7129,498 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6684
7129
|
const queryString = searchParams.toString();
|
|
6685
7130
|
return queryString ? `${path}?${queryString}` : path;
|
|
6686
7131
|
}
|
|
6687
|
-
};
|
|
6688
|
-
|
|
6689
|
-
//#endregion
|
|
6690
|
-
//#region ../agent-provider/src/common/client/session.ts
|
|
6691
|
-
/**
|
|
6692
|
-
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
6693
|
-
*
|
|
6694
|
-
* This class wraps an AgentConnection and provides the session-centric API.
|
|
6695
|
-
* It is created by SessionManager when creating or loading sessions.
|
|
6696
|
-
*
|
|
6697
|
-
* @example
|
|
6698
|
-
* ```typescript
|
|
6699
|
-
* // Created by client.sessions.new() or client.sessions.load()
|
|
6700
|
-
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
6701
|
-
*
|
|
6702
|
-
* // Access agent state
|
|
6703
|
-
* console.log(session.agentState.status);
|
|
6704
|
-
*
|
|
6705
|
-
* // Send prompt
|
|
6706
|
-
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
6707
|
-
*
|
|
6708
|
-
* // Cleanup
|
|
6709
|
-
* session.disconnect();
|
|
6710
|
-
* ```
|
|
6711
|
-
*/
|
|
6712
|
-
var ActiveSessionImpl = class {
|
|
6713
7132
|
/**
|
|
6714
|
-
*
|
|
6715
|
-
*
|
|
6716
|
-
* @param sessionId - Session ID
|
|
6717
|
-
* @param agentId - Agent ID
|
|
6718
|
-
* @param connection - Already connected AgentConnection
|
|
6719
|
-
* @param options - Additional options
|
|
7133
|
+
* 获取已安装插件列表
|
|
7134
|
+
* GET /console/as/user/plugins/installed
|
|
6720
7135
|
*/
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
7136
|
+
async getInstalledPlugins(forceRefresh) {
|
|
7137
|
+
try {
|
|
7138
|
+
const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
|
|
7139
|
+
name: p.plugin_name,
|
|
7140
|
+
marketplaceName: p.marketplace_name,
|
|
7141
|
+
status: p.enabled ? "enabled" : "disabled",
|
|
7142
|
+
description: p.description,
|
|
7143
|
+
version: p.version,
|
|
7144
|
+
installScope: p.scope === "local" ? "project" : p.scope,
|
|
7145
|
+
installedScopes: [p.scope],
|
|
7146
|
+
installId: p.id
|
|
7147
|
+
}));
|
|
7148
|
+
result.forEach((plugin) => {
|
|
7149
|
+
this.pluginCache.set(plugin.name, plugin);
|
|
7150
|
+
});
|
|
7151
|
+
return result;
|
|
7152
|
+
} catch (error) {
|
|
7153
|
+
this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
|
|
7154
|
+
throw error;
|
|
7155
|
+
}
|
|
7156
|
+
}
|
|
7157
|
+
/**
|
|
7158
|
+
* 安装插件
|
|
7159
|
+
* POST /console/as/user/plugins/install
|
|
7160
|
+
*
|
|
7161
|
+
* @param pluginNames - 插件名称数组
|
|
7162
|
+
* @param marketplaceNameOrId - 市场名称或 ID
|
|
7163
|
+
*/
|
|
7164
|
+
async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
|
|
7165
|
+
try {
|
|
7166
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7167
|
+
if (!marketplaceId) return {
|
|
7168
|
+
success: false,
|
|
7169
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7170
|
+
};
|
|
7171
|
+
const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
|
|
7172
|
+
plugin_name: pluginName,
|
|
7173
|
+
marketplace_id: marketplaceId,
|
|
7174
|
+
version: "latest"
|
|
7175
|
+
})))).filter((r) => r.status === "rejected");
|
|
7176
|
+
if (failed.length > 0) {
|
|
7177
|
+
const errors = failed.map((r) => r.reason?.message || "Unknown error");
|
|
7178
|
+
return {
|
|
7179
|
+
success: false,
|
|
7180
|
+
error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
|
|
7181
|
+
};
|
|
7182
|
+
}
|
|
7183
|
+
return { success: true };
|
|
7184
|
+
} catch (error) {
|
|
7185
|
+
return {
|
|
7186
|
+
success: false,
|
|
7187
|
+
error: this.extractErrorMessage(error)
|
|
7188
|
+
};
|
|
7189
|
+
}
|
|
7190
|
+
}
|
|
7191
|
+
/**
|
|
7192
|
+
* 卸载插件
|
|
7193
|
+
* POST /console/as/user/plugins/installed/:id/uninstall
|
|
7194
|
+
*
|
|
7195
|
+
* 完整链路:
|
|
7196
|
+
* CloudAgentProvider.uninstallPlugin()
|
|
7197
|
+
* -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
|
|
7198
|
+
* -> agentserver: PluginInstallService.Uninstall()
|
|
7199
|
+
* -> 软删除 DB + 异步同步到活跃沙箱
|
|
7200
|
+
*
|
|
7201
|
+
* @param pluginName - 插件名称
|
|
7202
|
+
* @param marketplaceName - 市场名称(用于标识唯一插件)
|
|
7203
|
+
* @param scope - 卸载范围 ('user' | 'project' | 'project-local')
|
|
7204
|
+
*/
|
|
7205
|
+
async uninstallPlugin(pluginName, marketplaceName, scope) {
|
|
7206
|
+
try {
|
|
7207
|
+
const installId = await this.findPluginInstallId(pluginName);
|
|
7208
|
+
if (!installId) return {
|
|
7209
|
+
success: false,
|
|
7210
|
+
error: `Plugin not found or not installed: ${pluginName}`
|
|
7211
|
+
};
|
|
7212
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
|
|
7213
|
+
return { success: true };
|
|
7214
|
+
} catch (error) {
|
|
7215
|
+
return {
|
|
7216
|
+
success: false,
|
|
7217
|
+
error: this.extractErrorMessage(error)
|
|
7218
|
+
};
|
|
7219
|
+
}
|
|
7220
|
+
}
|
|
7221
|
+
/**
|
|
7222
|
+
* 获取插件市场列表
|
|
7223
|
+
* GET /console/as/marketplace/sources
|
|
7224
|
+
*/
|
|
7225
|
+
async getPluginMarketplaces(forceRefresh) {
|
|
7226
|
+
try {
|
|
7227
|
+
const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
|
|
7228
|
+
id: src.id,
|
|
7229
|
+
name: src.name,
|
|
7230
|
+
type: this.mapSourceType(src.source_type),
|
|
7231
|
+
source: { url: src.url },
|
|
7232
|
+
description: src.name,
|
|
7233
|
+
isBuiltin: src.is_default
|
|
7234
|
+
}));
|
|
7235
|
+
result.forEach((m) => {
|
|
7236
|
+
const marketplaceInfo = {
|
|
7237
|
+
id: m.id,
|
|
7238
|
+
name: m.name
|
|
7239
|
+
};
|
|
7240
|
+
this.marketplaceCache.set(m.name, marketplaceInfo);
|
|
7241
|
+
this.marketplaceCache.set(m.id, marketplaceInfo);
|
|
7242
|
+
});
|
|
7243
|
+
return result;
|
|
7244
|
+
} catch (error) {
|
|
7245
|
+
this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
|
|
7246
|
+
throw error;
|
|
7247
|
+
}
|
|
7248
|
+
}
|
|
7249
|
+
/**
|
|
7250
|
+
* 获取市场下的插件列表
|
|
7251
|
+
* - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
|
|
7252
|
+
* - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
|
|
7253
|
+
*
|
|
7254
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
7255
|
+
* @param forceRefresh - 是否强制刷新
|
|
7256
|
+
* @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
|
|
7257
|
+
*/
|
|
7258
|
+
async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
|
|
7259
|
+
try {
|
|
7260
|
+
if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
|
|
7261
|
+
q: searchText,
|
|
7262
|
+
page: 1,
|
|
7263
|
+
page_size: 100
|
|
7264
|
+
} })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
7265
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7266
|
+
if (!sourceId) {
|
|
7267
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
7268
|
+
return [];
|
|
7269
|
+
}
|
|
7270
|
+
const params = {
|
|
7271
|
+
source_id: sourceId,
|
|
7272
|
+
page: 1,
|
|
7273
|
+
page_size: 100
|
|
7274
|
+
};
|
|
7275
|
+
return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
7276
|
+
} catch (error) {
|
|
7277
|
+
this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
|
|
7278
|
+
throw error;
|
|
7279
|
+
}
|
|
7280
|
+
}
|
|
7281
|
+
/**
|
|
7282
|
+
* 获取插件详情
|
|
7283
|
+
* GET /console/as/marketplace/plugins/:name/detail
|
|
7284
|
+
*
|
|
7285
|
+
* @param pluginName - 插件名称
|
|
7286
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
7287
|
+
*/
|
|
7288
|
+
async getPluginDetail(pluginName, marketplaceNameOrId) {
|
|
7289
|
+
try {
|
|
7290
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7291
|
+
if (!sourceId) {
|
|
7292
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
7293
|
+
return null;
|
|
7294
|
+
}
|
|
7295
|
+
const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
|
|
7296
|
+
if (!p) return null;
|
|
7297
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
7298
|
+
const tags = p.tags ? JSON.parse(p.tags) : [];
|
|
7299
|
+
return {
|
|
7300
|
+
name: p.name,
|
|
7301
|
+
marketplaceName: p.marketplace_name,
|
|
7302
|
+
description: p.description,
|
|
7303
|
+
version: p.version,
|
|
7304
|
+
iconUrl: p.icon_url,
|
|
7305
|
+
tags,
|
|
7306
|
+
installed: p.installed,
|
|
7307
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
7308
|
+
readme: p.readme,
|
|
7309
|
+
author: p.author,
|
|
7310
|
+
homepage: p.homepage,
|
|
7311
|
+
repositoryUrl: p.repository_url,
|
|
7312
|
+
license: p.license,
|
|
7313
|
+
...capabilities
|
|
7314
|
+
};
|
|
7315
|
+
} catch (error) {
|
|
7316
|
+
this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
|
|
7317
|
+
throw error;
|
|
7318
|
+
}
|
|
7319
|
+
}
|
|
7320
|
+
/**
|
|
7321
|
+
* 添加插件市场
|
|
7322
|
+
* POST /console/as/marketplace/sources
|
|
7323
|
+
*/
|
|
7324
|
+
async addPluginMarketplace(sourceUrl, name) {
|
|
7325
|
+
try {
|
|
7326
|
+
const body = {
|
|
7327
|
+
source_type: sourceUrl.startsWith("http") ? "url" : "github",
|
|
7328
|
+
url: sourceUrl,
|
|
7329
|
+
name: name || sourceUrl
|
|
7330
|
+
};
|
|
7331
|
+
const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
|
|
7332
|
+
if (!sourceData) throw new Error("Invalid response from server");
|
|
7333
|
+
const marketplaceInfo = {
|
|
7334
|
+
id: sourceData.id,
|
|
7335
|
+
name: sourceData.name
|
|
7336
|
+
};
|
|
7337
|
+
this.marketplaceCache.set(sourceData.name, marketplaceInfo);
|
|
7338
|
+
this.marketplaceCache.set(sourceData.id, marketplaceInfo);
|
|
7339
|
+
return {
|
|
7340
|
+
success: true,
|
|
7341
|
+
marketplace: {
|
|
7342
|
+
id: sourceData.id,
|
|
7343
|
+
name: sourceData.name,
|
|
7344
|
+
type: this.mapSourceType(sourceData.source_type),
|
|
7345
|
+
source: { url: sourceData.url },
|
|
7346
|
+
isBuiltin: sourceData.is_default
|
|
7347
|
+
}
|
|
7348
|
+
};
|
|
7349
|
+
} catch (error) {
|
|
7350
|
+
return {
|
|
7351
|
+
success: false,
|
|
7352
|
+
error: this.extractErrorMessage(error)
|
|
7353
|
+
};
|
|
7354
|
+
}
|
|
7355
|
+
}
|
|
7356
|
+
/**
|
|
7357
|
+
* 删除插件市场
|
|
7358
|
+
* POST /console/as/marketplace/sources/:id/delete
|
|
7359
|
+
*/
|
|
7360
|
+
async removePluginMarketplace(marketplaceNameOrId) {
|
|
7361
|
+
try {
|
|
7362
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7363
|
+
if (!marketplaceId) return {
|
|
7364
|
+
success: false,
|
|
7365
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7366
|
+
};
|
|
7367
|
+
await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
|
|
7368
|
+
return { success: true };
|
|
7369
|
+
} catch (error) {
|
|
7370
|
+
return {
|
|
7371
|
+
success: false,
|
|
7372
|
+
error: this.extractErrorMessage(error)
|
|
7373
|
+
};
|
|
7374
|
+
}
|
|
7375
|
+
}
|
|
7376
|
+
/**
|
|
7377
|
+
* 刷新插件市场
|
|
7378
|
+
* POST /console/as/marketplace/sources/:id/check-updates
|
|
7379
|
+
*/
|
|
7380
|
+
async refreshPluginMarketplace(marketplaceNameOrId) {
|
|
7381
|
+
try {
|
|
7382
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7383
|
+
if (!marketplaceId) return {
|
|
7384
|
+
success: false,
|
|
7385
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7386
|
+
};
|
|
7387
|
+
return {
|
|
7388
|
+
success: true,
|
|
7389
|
+
plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
|
|
7390
|
+
};
|
|
7391
|
+
} catch (error) {
|
|
7392
|
+
return {
|
|
7393
|
+
success: false,
|
|
7394
|
+
error: this.extractErrorMessage(error)
|
|
7395
|
+
};
|
|
7396
|
+
}
|
|
7397
|
+
}
|
|
7398
|
+
/**
|
|
7399
|
+
* 批量切换插件启用/禁用状态
|
|
7400
|
+
* POST /console/as/user/plugins/installed/:id/toggle
|
|
7401
|
+
*/
|
|
7402
|
+
async batchTogglePlugins(request) {
|
|
7403
|
+
try {
|
|
7404
|
+
const results = await Promise.allSettled(request.items.map(async (item) => {
|
|
7405
|
+
const installId = await this.findPluginInstallId(item.pluginName);
|
|
7406
|
+
if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
|
|
7407
|
+
const enabled = item.operation === "enable";
|
|
7408
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
|
|
7409
|
+
return item;
|
|
7410
|
+
}));
|
|
7411
|
+
const succeededPlugins = [];
|
|
7412
|
+
const failedPlugins = [];
|
|
7413
|
+
results.forEach((r, i) => {
|
|
7414
|
+
const item = request.items[i];
|
|
7415
|
+
if (r.status === "fulfilled") succeededPlugins.push(item);
|
|
7416
|
+
else failedPlugins.push({
|
|
7417
|
+
...item,
|
|
7418
|
+
error: r.reason?.message || "Unknown error"
|
|
7419
|
+
});
|
|
7420
|
+
});
|
|
7421
|
+
return {
|
|
7422
|
+
success: failedPlugins.length === 0,
|
|
7423
|
+
succeededPlugins,
|
|
7424
|
+
failedPlugins
|
|
7425
|
+
};
|
|
7426
|
+
} catch (error) {
|
|
7427
|
+
return {
|
|
7428
|
+
success: false,
|
|
7429
|
+
succeededPlugins: [],
|
|
7430
|
+
failedPlugins: request.items.map((item) => ({
|
|
7431
|
+
...item,
|
|
7432
|
+
error: this.extractErrorMessage(error)
|
|
7433
|
+
}))
|
|
7434
|
+
};
|
|
7435
|
+
}
|
|
7436
|
+
}
|
|
7437
|
+
/**
|
|
7438
|
+
* 将后端插件数据映射为前端格式
|
|
7439
|
+
*/
|
|
7440
|
+
mapPluginData(p) {
|
|
7441
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
7442
|
+
let tags = [];
|
|
7443
|
+
if (p.tags) {
|
|
7444
|
+
if (Array.isArray(p.tags)) tags = p.tags;
|
|
7445
|
+
else if (typeof p.tags === "string") try {
|
|
7446
|
+
const parsed = JSON.parse(p.tags);
|
|
7447
|
+
tags = Array.isArray(parsed) ? parsed : [parsed];
|
|
7448
|
+
} catch {
|
|
7449
|
+
tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
|
|
7450
|
+
}
|
|
7451
|
+
}
|
|
7452
|
+
return {
|
|
7453
|
+
name: p.name,
|
|
7454
|
+
marketplaceName: p.marketplace_name,
|
|
7455
|
+
description: p.description,
|
|
7456
|
+
version: p.version,
|
|
7457
|
+
iconUrl: p.icon_url,
|
|
7458
|
+
tags,
|
|
7459
|
+
installed: p.installed,
|
|
7460
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
7461
|
+
installedScopes: p.installed ? [p.installed_scope] : [],
|
|
7462
|
+
...capabilities
|
|
7463
|
+
};
|
|
7464
|
+
}
|
|
7465
|
+
/**
|
|
7466
|
+
* 从缓存中查找插件的 install_id
|
|
7467
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
7468
|
+
*
|
|
7469
|
+
* @param pluginName - 插件名称
|
|
7470
|
+
* @returns install_id 或 null
|
|
7471
|
+
*/
|
|
7472
|
+
async findPluginInstallId(pluginName) {
|
|
7473
|
+
const cached = this.pluginCache.get(pluginName);
|
|
7474
|
+
if (cached) return cached.installId;
|
|
7475
|
+
await this.getInstalledPlugins();
|
|
7476
|
+
return this.pluginCache.get(pluginName)?.installId || null;
|
|
7477
|
+
}
|
|
7478
|
+
/**
|
|
7479
|
+
* 从缓存中查找 marketplace ID
|
|
7480
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
7481
|
+
*/
|
|
7482
|
+
async findMarketplaceId(nameOrId) {
|
|
7483
|
+
const cached = this.marketplaceCache.get(nameOrId);
|
|
7484
|
+
if (cached) return cached.id;
|
|
7485
|
+
await this.getPluginMarketplaces();
|
|
7486
|
+
return this.marketplaceCache.get(nameOrId)?.id || null;
|
|
7487
|
+
}
|
|
7488
|
+
/**
|
|
7489
|
+
* 提取 API 错误信息
|
|
7490
|
+
* 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
|
|
7491
|
+
*/
|
|
7492
|
+
extractErrorMessage(error) {
|
|
7493
|
+
if (error instanceof AxiosError) {
|
|
7494
|
+
const status = error.response?.status;
|
|
7495
|
+
const apiResponse = error.response?.data;
|
|
7496
|
+
const parts = [];
|
|
7497
|
+
if (status) parts.push(`HTTP ${status}`);
|
|
7498
|
+
if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
|
|
7499
|
+
if (apiResponse?.msg) parts.push(apiResponse.msg);
|
|
7500
|
+
else if (error.message) parts.push(error.message);
|
|
7501
|
+
const errorMessage = parts.join(" - ");
|
|
7502
|
+
this.logger?.error("[CloudAgentProvider] API Error:", {
|
|
7503
|
+
status,
|
|
7504
|
+
code: apiResponse?.code,
|
|
7505
|
+
msg: apiResponse?.msg,
|
|
7506
|
+
requestId: apiResponse?.requestId,
|
|
7507
|
+
url: error.config?.url,
|
|
7508
|
+
method: error.config?.method
|
|
7509
|
+
});
|
|
7510
|
+
return errorMessage;
|
|
7511
|
+
}
|
|
7512
|
+
if (error instanceof Error) return error.message;
|
|
7513
|
+
return "Unknown error";
|
|
7514
|
+
}
|
|
7515
|
+
/**
|
|
7516
|
+
* 映射后端 source_type 到前端类型
|
|
7517
|
+
*/
|
|
7518
|
+
mapSourceType(sourceType) {
|
|
7519
|
+
switch (sourceType) {
|
|
7520
|
+
case "github": return "github";
|
|
7521
|
+
case "official": return "custom";
|
|
7522
|
+
default: return "custom";
|
|
7523
|
+
}
|
|
7524
|
+
}
|
|
7525
|
+
/**
|
|
7526
|
+
* 解析 capabilities JSON 字符串
|
|
7527
|
+
*/
|
|
7528
|
+
parseCapabilities(capabilitiesStr) {
|
|
7529
|
+
try {
|
|
7530
|
+
const cap = JSON.parse(capabilitiesStr);
|
|
7531
|
+
return {
|
|
7532
|
+
commands: cap.commands,
|
|
7533
|
+
skills: cap.skills,
|
|
7534
|
+
mcpServers: cap.mcp,
|
|
7535
|
+
agents: cap.agents,
|
|
7536
|
+
hooks: cap.hooks,
|
|
7537
|
+
rules: cap.rules
|
|
7538
|
+
};
|
|
7539
|
+
} catch {
|
|
7540
|
+
return {};
|
|
7541
|
+
}
|
|
7542
|
+
}
|
|
7543
|
+
/**
|
|
7544
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
7545
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
7546
|
+
* 注入用户信息和浏览器环境等公共字段
|
|
7547
|
+
*/
|
|
7548
|
+
async reportTelemetry(eventName, payload) {
|
|
7549
|
+
try {
|
|
7550
|
+
const account = accountService.getAccount();
|
|
7551
|
+
const commonFields = {};
|
|
7552
|
+
if (account) {
|
|
7553
|
+
commonFields.userId = account.uid;
|
|
7554
|
+
commonFields.userNickname = account.nickname;
|
|
7555
|
+
if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
|
|
7556
|
+
if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
|
|
7557
|
+
}
|
|
7558
|
+
if (typeof navigator !== "undefined") {
|
|
7559
|
+
commonFields.userAgent = navigator.userAgent;
|
|
7560
|
+
commonFields.os = navigator.platform;
|
|
7561
|
+
}
|
|
7562
|
+
const events = [{
|
|
7563
|
+
eventCode: eventName,
|
|
7564
|
+
timestamp: Date.now(),
|
|
7565
|
+
reportDelay: 0,
|
|
7566
|
+
...commonFields,
|
|
7567
|
+
...payload
|
|
7568
|
+
}];
|
|
7569
|
+
await httpService.post("/v2/report", events);
|
|
7570
|
+
} catch (error) {
|
|
7571
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
7572
|
+
}
|
|
7573
|
+
}
|
|
7574
|
+
};
|
|
7575
|
+
|
|
7576
|
+
//#endregion
|
|
7577
|
+
//#region ../agent-provider/src/common/client/session.ts
|
|
7578
|
+
/**
|
|
7579
|
+
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
7580
|
+
*
|
|
7581
|
+
* This class wraps an AgentConnection and provides the session-centric API.
|
|
7582
|
+
* It is created by SessionManager when creating or loading sessions.
|
|
7583
|
+
*
|
|
7584
|
+
* @example
|
|
7585
|
+
* ```typescript
|
|
7586
|
+
* // Created by client.sessions.new() or client.sessions.load()
|
|
7587
|
+
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
7588
|
+
*
|
|
7589
|
+
* // Access agent state
|
|
7590
|
+
* console.log(session.agentState.status);
|
|
7591
|
+
*
|
|
7592
|
+
* // Send prompt
|
|
7593
|
+
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
7594
|
+
*
|
|
7595
|
+
* // Cleanup
|
|
7596
|
+
* session.disconnect();
|
|
7597
|
+
* ```
|
|
7598
|
+
*/
|
|
7599
|
+
var ActiveSessionImpl = class {
|
|
7600
|
+
/**
|
|
7601
|
+
* Create an ActiveSessionImpl instance
|
|
7602
|
+
*
|
|
7603
|
+
* @param sessionId - Session ID
|
|
7604
|
+
* @param agentId - Agent ID
|
|
7605
|
+
* @param connection - Already connected AgentConnection
|
|
7606
|
+
* @param options - Additional options
|
|
7607
|
+
*/
|
|
7608
|
+
constructor(sessionId, agentId, connection, options = {}) {
|
|
7609
|
+
this._availableCommands = [];
|
|
7610
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
7611
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
7612
|
+
this.connectionListeners = [];
|
|
7613
|
+
this._id = sessionId;
|
|
7614
|
+
this._agentId = agentId;
|
|
7615
|
+
this.connection = connection;
|
|
7616
|
+
this.logger = options.logger;
|
|
7617
|
+
this._getFilesystem = options.getFilesystem;
|
|
7618
|
+
this._connectionInfo = options.connectionInfo;
|
|
7619
|
+
this.setupConnectionEvents(connection);
|
|
7620
|
+
this.agent = this.createAgentOperations();
|
|
7621
|
+
this.prompts = this.createPromptsResource();
|
|
7622
|
+
this.artifacts = this.createArtifactsResource();
|
|
7623
|
+
this.files = this.createFilesResource();
|
|
6736
7624
|
}
|
|
6737
7625
|
/**
|
|
6738
7626
|
* Session ID
|
|
@@ -6747,6 +7635,18 @@ var ActiveSessionImpl = class {
|
|
|
6747
7635
|
return this._agentId;
|
|
6748
7636
|
}
|
|
6749
7637
|
/**
|
|
7638
|
+
* Actual workspace path (set from newSession response _meta)
|
|
7639
|
+
*/
|
|
7640
|
+
get cwd() {
|
|
7641
|
+
return this._cwd;
|
|
7642
|
+
}
|
|
7643
|
+
/**
|
|
7644
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
7645
|
+
*/
|
|
7646
|
+
setCwd(cwd) {
|
|
7647
|
+
this._cwd = cwd;
|
|
7648
|
+
}
|
|
7649
|
+
/**
|
|
6750
7650
|
* Agent state (live connection state)
|
|
6751
7651
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
6752
7652
|
*/
|
|
@@ -6963,8 +7863,8 @@ var ActiveSessionImpl = class {
|
|
|
6963
7863
|
* await session.setMode('architect');
|
|
6964
7864
|
* ```
|
|
6965
7865
|
*/
|
|
6966
|
-
async setMode(modeId) {
|
|
6967
|
-
if (this._availableModes) {
|
|
7866
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
7867
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
6968
7868
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
6969
7869
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
6970
7870
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -6987,6 +7887,7 @@ var ActiveSessionImpl = class {
|
|
|
6987
7887
|
* ```
|
|
6988
7888
|
*/
|
|
6989
7889
|
async setSessionModel(modelId) {
|
|
7890
|
+
this._currentModelId = modelId;
|
|
6990
7891
|
await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
|
|
6991
7892
|
}
|
|
6992
7893
|
/**
|
|
@@ -7064,11 +7965,23 @@ var ActiveSessionImpl = class {
|
|
|
7064
7965
|
* Disconnect from the session/agent
|
|
7065
7966
|
*/
|
|
7066
7967
|
disconnect() {
|
|
7968
|
+
this.removeConnectionListeners();
|
|
7067
7969
|
this.connection.disconnect();
|
|
7068
7970
|
this.removeAllListeners();
|
|
7069
7971
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
7070
7972
|
}
|
|
7071
7973
|
/**
|
|
7974
|
+
* Detach the session from connection events without disconnecting the connection.
|
|
7975
|
+
* This should be called when the session is being replaced but the connection is shared.
|
|
7976
|
+
* Unlike disconnect(), this only removes event listeners without closing the connection.
|
|
7977
|
+
*/
|
|
7978
|
+
detach() {
|
|
7979
|
+
this.logger?.info(`Session ${this._id}: Detaching from connection events`);
|
|
7980
|
+
this.removeConnectionListeners();
|
|
7981
|
+
this.removeAllListeners();
|
|
7982
|
+
this.logger?.info(`Session ${this._id}: Detached successfully`);
|
|
7983
|
+
}
|
|
7984
|
+
/**
|
|
7072
7985
|
* Symbol.dispose for 'using' keyword support
|
|
7073
7986
|
* Automatically disconnects and cleans up when session goes out of scope
|
|
7074
7987
|
*
|
|
@@ -7087,60 +8000,85 @@ var ActiveSessionImpl = class {
|
|
|
7087
8000
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
7088
8001
|
return this.connection;
|
|
7089
8002
|
}
|
|
8003
|
+
/**
|
|
8004
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
8005
|
+
*/
|
|
8006
|
+
addConnectionListener(connection, event, listener) {
|
|
8007
|
+
connection.on(event, listener);
|
|
8008
|
+
this.connectionListeners.push({
|
|
8009
|
+
event,
|
|
8010
|
+
listener
|
|
8011
|
+
});
|
|
8012
|
+
}
|
|
8013
|
+
/**
|
|
8014
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
8015
|
+
*/
|
|
8016
|
+
removeConnectionListeners() {
|
|
8017
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
8018
|
+
this.connectionListeners = [];
|
|
8019
|
+
}
|
|
7090
8020
|
setupConnectionEvents(connection) {
|
|
7091
|
-
|
|
8021
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
7092
8022
|
this.emit("connected", void 0);
|
|
7093
8023
|
});
|
|
7094
|
-
|
|
8024
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
7095
8025
|
this.emit("disconnected", void 0);
|
|
7096
8026
|
});
|
|
7097
|
-
|
|
8027
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
7098
8028
|
this.emit("error", error);
|
|
7099
8029
|
});
|
|
7100
|
-
|
|
8030
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
8031
|
+
const notificationSessionId = update?.sessionId;
|
|
8032
|
+
if (notificationSessionId && notificationSessionId !== this._id) {
|
|
8033
|
+
console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
|
|
8034
|
+
return;
|
|
8035
|
+
}
|
|
7101
8036
|
this.emit("sessionUpdate", update);
|
|
7102
8037
|
});
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
artifactUri: artifact.uri,
|
|
7106
|
-
artifactType: artifact.type
|
|
7107
|
-
});
|
|
8038
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
8039
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7108
8040
|
this.emit("artifactCreated", artifact);
|
|
7109
8041
|
});
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
artifactUri: artifact.uri,
|
|
7113
|
-
artifactType: artifact.type
|
|
7114
|
-
});
|
|
8042
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
8043
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7115
8044
|
this.emit("artifactUpdated", artifact);
|
|
7116
8045
|
});
|
|
7117
|
-
|
|
7118
|
-
|
|
8046
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
8047
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7119
8048
|
this.emit("artifactDeleted", artifact);
|
|
7120
8049
|
});
|
|
7121
|
-
|
|
8050
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
7122
8051
|
this.emit("permissionRequest", request);
|
|
7123
8052
|
});
|
|
7124
|
-
|
|
8053
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
7125
8054
|
this.emit("questionRequest", request);
|
|
7126
8055
|
});
|
|
7127
|
-
|
|
8056
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
7128
8057
|
this.prompts.cancel();
|
|
7129
8058
|
});
|
|
7130
|
-
|
|
8059
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
7131
8060
|
this.emit("usageUpdate", usage);
|
|
7132
8061
|
});
|
|
7133
|
-
|
|
8062
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
8063
|
+
const originSessionId = checkpoint.__sessionId;
|
|
8064
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
7134
8065
|
this.emit("checkpointCreated", checkpoint);
|
|
7135
8066
|
});
|
|
7136
|
-
|
|
8067
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
8068
|
+
const originSessionId = checkpoint.__sessionId;
|
|
8069
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
7137
8070
|
this.emit("checkpointUpdated", checkpoint);
|
|
7138
8071
|
});
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
8072
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
8073
|
+
const originSessionId = command.__sessionId;
|
|
8074
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
8075
|
+
console.log("[Session] Command not forwarded:", {
|
|
8076
|
+
command,
|
|
8077
|
+
originSessionId,
|
|
8078
|
+
sessionId: this._id
|
|
8079
|
+
});
|
|
8080
|
+
return;
|
|
8081
|
+
}
|
|
7144
8082
|
this.emit("command", command);
|
|
7145
8083
|
});
|
|
7146
8084
|
}
|
|
@@ -7150,19 +8088,20 @@ var ActiveSessionImpl = class {
|
|
|
7150
8088
|
_meta: response._meta ?? void 0
|
|
7151
8089
|
};
|
|
7152
8090
|
}
|
|
8091
|
+
/**
|
|
8092
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
8093
|
+
* 所有类型的 artifact 都按 __sessionId 严格隔离
|
|
8094
|
+
*/
|
|
8095
|
+
shouldForwardArtifact(artifact) {
|
|
8096
|
+
const originSessionId = artifact.__sessionId;
|
|
8097
|
+
if (!originSessionId || originSessionId !== this._id) return false;
|
|
8098
|
+
return true;
|
|
8099
|
+
}
|
|
7153
8100
|
};
|
|
7154
8101
|
|
|
7155
8102
|
//#endregion
|
|
7156
8103
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
7157
8104
|
/**
|
|
7158
|
-
* SessionManager - Manages session lifecycle and connections
|
|
7159
|
-
*
|
|
7160
|
-
* Provides the core implementation for session-centric API operations:
|
|
7161
|
-
* - list() - Lists sessions (mapped from agents)
|
|
7162
|
-
* - createSession() - Creates new session (auto-creates agent)
|
|
7163
|
-
* - loadSession() - Loads existing session (finds agent by sessionId)
|
|
7164
|
-
*/
|
|
7165
|
-
/**
|
|
7166
8105
|
* SessionManager - Session lifecycle management
|
|
7167
8106
|
*
|
|
7168
8107
|
* This class manages the relationship between sessions and agents.
|
|
@@ -7212,7 +8151,8 @@ var SessionManager = class {
|
|
|
7212
8151
|
createdAt: agent.createdAt,
|
|
7213
8152
|
lastActivityAt: agent.updatedAt,
|
|
7214
8153
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
7215
|
-
isPlayground: agent.isPlayground
|
|
8154
|
+
isPlayground: agent.isPlayground,
|
|
8155
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
7216
8156
|
}));
|
|
7217
8157
|
console.log("[SessionManager] Returning sessions:", {
|
|
7218
8158
|
count: sessions.length,
|
|
@@ -7239,13 +8179,26 @@ var SessionManager = class {
|
|
|
7239
8179
|
if (this.provider.create) {
|
|
7240
8180
|
agentId = await this.provider.create(params);
|
|
7241
8181
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
8182
|
+
if (params.options?.onSessionPrepared) {
|
|
8183
|
+
const initialPrompt = params.options?.prompt;
|
|
8184
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
8185
|
+
params.options.onSessionPrepared({
|
|
8186
|
+
id: agentId,
|
|
8187
|
+
agentId,
|
|
8188
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
8189
|
+
status: "connecting",
|
|
8190
|
+
cwd: params.cwd || "",
|
|
8191
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
8192
|
+
});
|
|
8193
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
8194
|
+
}
|
|
7242
8195
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
7243
8196
|
const connection = await this.provider.connect(agentId);
|
|
7244
8197
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
7245
8198
|
const response = await connection.createSession({
|
|
7246
|
-
_meta: params._meta,
|
|
8199
|
+
_meta: params.options?._meta,
|
|
7247
8200
|
cwd: params.cwd,
|
|
7248
|
-
mcpServers: params.mcpServers
|
|
8201
|
+
mcpServers: params.options?.mcpServers
|
|
7249
8202
|
});
|
|
7250
8203
|
if (this.provider.registerSession) {
|
|
7251
8204
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -7258,14 +8211,10 @@ var SessionManager = class {
|
|
|
7258
8211
|
connectionInfo
|
|
7259
8212
|
});
|
|
7260
8213
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
description: m.description ?? void 0
|
|
7266
|
-
}));
|
|
7267
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
7268
|
-
}
|
|
8214
|
+
const availableModels = this.extractAvailableModels(response);
|
|
8215
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
8216
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
8217
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
7269
8218
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
7270
8219
|
return session;
|
|
7271
8220
|
}
|
|
@@ -7301,17 +8250,31 @@ var SessionManager = class {
|
|
|
7301
8250
|
mcpServers: params.mcpServers
|
|
7302
8251
|
});
|
|
7303
8252
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
id: m.modelId,
|
|
7307
|
-
name: m.name,
|
|
7308
|
-
description: m.description ?? void 0
|
|
7309
|
-
}));
|
|
7310
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
7311
|
-
}
|
|
8253
|
+
const availableModels = this.extractAvailableModels(response);
|
|
8254
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
7312
8255
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
7313
8256
|
return session;
|
|
7314
8257
|
}
|
|
8258
|
+
/**
|
|
8259
|
+
* 从 ACP response 中提取可用模型列表
|
|
8260
|
+
*
|
|
8261
|
+
* 优先级:
|
|
8262
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
8263
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
8264
|
+
* 3. undefined - 都没有时返回 undefined
|
|
8265
|
+
*
|
|
8266
|
+
* @param response - ACP 响应对象
|
|
8267
|
+
* @returns ModelInfo[] | undefined
|
|
8268
|
+
*/
|
|
8269
|
+
extractAvailableModels(response) {
|
|
8270
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
8271
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
8272
|
+
const availableModels = response.models?.availableModels;
|
|
8273
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
8274
|
+
...model,
|
|
8275
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
8276
|
+
}));
|
|
8277
|
+
}
|
|
7315
8278
|
};
|
|
7316
8279
|
|
|
7317
8280
|
//#endregion
|
|
@@ -7418,13 +8381,33 @@ var AgentClient = class {
|
|
|
7418
8381
|
throw error;
|
|
7419
8382
|
}
|
|
7420
8383
|
},
|
|
7421
|
-
|
|
7422
|
-
this.logger?.debug("AgentClient.sessions.
|
|
8384
|
+
updateStatus: async (sessionId, status) => {
|
|
8385
|
+
this.logger?.debug("AgentClient.sessions.updateStatus called", {
|
|
8386
|
+
sessionId,
|
|
8387
|
+
status
|
|
8388
|
+
});
|
|
7423
8389
|
try {
|
|
7424
|
-
if (this.provider.
|
|
7425
|
-
const result = await this.provider.
|
|
7426
|
-
this.logger?.info("Session
|
|
7427
|
-
|
|
8390
|
+
if (this.provider.updateStatus) {
|
|
8391
|
+
const result = await this.provider.updateStatus(sessionId, status);
|
|
8392
|
+
this.logger?.info("Session status updated successfully", {
|
|
8393
|
+
sessionId,
|
|
8394
|
+
status
|
|
8395
|
+
});
|
|
8396
|
+
return result;
|
|
8397
|
+
}
|
|
8398
|
+
throw new Error("Provider does not support updateStatus method");
|
|
8399
|
+
} catch (error) {
|
|
8400
|
+
this.logger?.error("Failed to update session status", error);
|
|
8401
|
+
throw error;
|
|
8402
|
+
}
|
|
8403
|
+
},
|
|
8404
|
+
move: async (sessionId) => {
|
|
8405
|
+
this.logger?.debug("AgentClient.sessions.move called", { sessionId });
|
|
8406
|
+
try {
|
|
8407
|
+
if (this.provider.move) {
|
|
8408
|
+
const result = await this.provider.move(sessionId);
|
|
8409
|
+
this.logger?.info("Session moved successfully", { sessionId });
|
|
8410
|
+
return result;
|
|
7428
8411
|
}
|
|
7429
8412
|
throw new Error("Provider does not support move method");
|
|
7430
8413
|
} catch (error) {
|
|
@@ -7465,6 +8448,100 @@ var AgentClient = class {
|
|
|
7465
8448
|
return [];
|
|
7466
8449
|
}
|
|
7467
8450
|
},
|
|
8451
|
+
getAutomationSnapshot: async () => {
|
|
8452
|
+
try {
|
|
8453
|
+
if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
|
|
8454
|
+
this.logger?.warn("Provider does not support getAutomationSnapshot");
|
|
8455
|
+
} catch (error) {
|
|
8456
|
+
this.logger?.error("Failed to get automation snapshot", error);
|
|
8457
|
+
}
|
|
8458
|
+
return {
|
|
8459
|
+
automations: [],
|
|
8460
|
+
inbox: [],
|
|
8461
|
+
runtimeState: {},
|
|
8462
|
+
updatedAt: Date.now()
|
|
8463
|
+
};
|
|
8464
|
+
},
|
|
8465
|
+
updateAutomation: async (payload) => {
|
|
8466
|
+
try {
|
|
8467
|
+
if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
|
|
8468
|
+
this.logger?.warn("Provider does not support updateAutomation");
|
|
8469
|
+
} catch (error) {
|
|
8470
|
+
this.logger?.error("Failed to update automation", error);
|
|
8471
|
+
return {
|
|
8472
|
+
success: false,
|
|
8473
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8474
|
+
};
|
|
8475
|
+
}
|
|
8476
|
+
return {
|
|
8477
|
+
success: false,
|
|
8478
|
+
message: "Provider does not support updateAutomation"
|
|
8479
|
+
};
|
|
8480
|
+
},
|
|
8481
|
+
deleteAutomation: async (id) => {
|
|
8482
|
+
try {
|
|
8483
|
+
if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
|
|
8484
|
+
this.logger?.warn("Provider does not support deleteAutomation");
|
|
8485
|
+
} catch (error) {
|
|
8486
|
+
this.logger?.error("Failed to delete automation", error);
|
|
8487
|
+
return {
|
|
8488
|
+
success: false,
|
|
8489
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8490
|
+
};
|
|
8491
|
+
}
|
|
8492
|
+
return {
|
|
8493
|
+
success: false,
|
|
8494
|
+
message: "Provider does not support deleteAutomation"
|
|
8495
|
+
};
|
|
8496
|
+
},
|
|
8497
|
+
archiveAutomationInboxItem: async (itemId) => {
|
|
8498
|
+
try {
|
|
8499
|
+
if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
|
|
8500
|
+
this.logger?.warn("Provider does not support archiveAutomationInboxItem");
|
|
8501
|
+
} catch (error) {
|
|
8502
|
+
this.logger?.error("Failed to archive automation inbox item", error);
|
|
8503
|
+
return {
|
|
8504
|
+
success: false,
|
|
8505
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8506
|
+
};
|
|
8507
|
+
}
|
|
8508
|
+
return {
|
|
8509
|
+
success: false,
|
|
8510
|
+
message: "Provider does not support archiveAutomationInboxItem"
|
|
8511
|
+
};
|
|
8512
|
+
},
|
|
8513
|
+
deleteAutomationInboxItem: async (itemId) => {
|
|
8514
|
+
try {
|
|
8515
|
+
if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
|
|
8516
|
+
this.logger?.warn("Provider does not support deleteAutomationInboxItem");
|
|
8517
|
+
} catch (error) {
|
|
8518
|
+
this.logger?.error("Failed to delete automation inbox item", error);
|
|
8519
|
+
return {
|
|
8520
|
+
success: false,
|
|
8521
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8522
|
+
};
|
|
8523
|
+
}
|
|
8524
|
+
return {
|
|
8525
|
+
success: false,
|
|
8526
|
+
message: "Provider does not support deleteAutomationInboxItem"
|
|
8527
|
+
};
|
|
8528
|
+
},
|
|
8529
|
+
testAutomation: async (id) => {
|
|
8530
|
+
try {
|
|
8531
|
+
if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
|
|
8532
|
+
this.logger?.warn("Provider does not support testAutomation");
|
|
8533
|
+
} catch (error) {
|
|
8534
|
+
this.logger?.error("Failed to test automation", error);
|
|
8535
|
+
return {
|
|
8536
|
+
success: false,
|
|
8537
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8538
|
+
};
|
|
8539
|
+
}
|
|
8540
|
+
return {
|
|
8541
|
+
success: false,
|
|
8542
|
+
message: "Provider does not support testAutomation"
|
|
8543
|
+
};
|
|
8544
|
+
},
|
|
7468
8545
|
on: (event, handler) => {
|
|
7469
8546
|
if (this.provider.on) this.provider.on(event, handler);
|
|
7470
8547
|
else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
|
|
@@ -7584,6 +8661,189 @@ var AgentClient = class {
|
|
|
7584
8661
|
};
|
|
7585
8662
|
}
|
|
7586
8663
|
},
|
|
8664
|
+
getSubagentList: async (params) => {
|
|
8665
|
+
try {
|
|
8666
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
8667
|
+
const result = await this.provider.getSubagentList(params);
|
|
8668
|
+
this.logger?.info("Subagent list retrieved", {
|
|
8669
|
+
resultCount: result.results.length,
|
|
8670
|
+
hasError: !!result.error
|
|
8671
|
+
});
|
|
8672
|
+
return result;
|
|
8673
|
+
}
|
|
8674
|
+
return {
|
|
8675
|
+
results: [],
|
|
8676
|
+
error: "Provider does not support getSubagentList"
|
|
8677
|
+
};
|
|
8678
|
+
} catch (error) {
|
|
8679
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
8680
|
+
return {
|
|
8681
|
+
results: [],
|
|
8682
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8683
|
+
};
|
|
8684
|
+
}
|
|
8685
|
+
},
|
|
8686
|
+
getSkillList: async (params) => {
|
|
8687
|
+
try {
|
|
8688
|
+
if (this.provider && this.provider.getSkillList) {
|
|
8689
|
+
const result = await this.provider.getSkillList(params);
|
|
8690
|
+
this.logger?.info("Skill list retrieved", {
|
|
8691
|
+
resultCount: result.results.length,
|
|
8692
|
+
hasError: !!result.error
|
|
8693
|
+
});
|
|
8694
|
+
return result;
|
|
8695
|
+
}
|
|
8696
|
+
return {
|
|
8697
|
+
results: [],
|
|
8698
|
+
error: "Provider does not support getSkillList"
|
|
8699
|
+
};
|
|
8700
|
+
} catch (error) {
|
|
8701
|
+
this.logger?.error("Failed to get skill list", error);
|
|
8702
|
+
return {
|
|
8703
|
+
results: [],
|
|
8704
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8705
|
+
};
|
|
8706
|
+
}
|
|
8707
|
+
},
|
|
8708
|
+
importSkill: async (params) => {
|
|
8709
|
+
try {
|
|
8710
|
+
if (this.provider && this.provider.importSkill) {
|
|
8711
|
+
const result = await this.provider.importSkill(params);
|
|
8712
|
+
this.logger?.info("Import skill completed", {
|
|
8713
|
+
success: result.success,
|
|
8714
|
+
hasError: !!result.error
|
|
8715
|
+
});
|
|
8716
|
+
return result;
|
|
8717
|
+
}
|
|
8718
|
+
return {
|
|
8719
|
+
success: false,
|
|
8720
|
+
error: "Provider does not support importSkill"
|
|
8721
|
+
};
|
|
8722
|
+
} catch (error) {
|
|
8723
|
+
this.logger?.error("Failed to import skill", error);
|
|
8724
|
+
return {
|
|
8725
|
+
success: false,
|
|
8726
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8727
|
+
};
|
|
8728
|
+
}
|
|
8729
|
+
},
|
|
8730
|
+
toggleSkill: async (params) => {
|
|
8731
|
+
try {
|
|
8732
|
+
if (this.provider && this.provider.toggleSkill) {
|
|
8733
|
+
const result = await this.provider.toggleSkill(params);
|
|
8734
|
+
this.logger?.info("Toggle skill completed", {
|
|
8735
|
+
success: result.success,
|
|
8736
|
+
hasError: !!result.error
|
|
8737
|
+
});
|
|
8738
|
+
return result;
|
|
8739
|
+
}
|
|
8740
|
+
return {
|
|
8741
|
+
success: false,
|
|
8742
|
+
error: "Provider does not support toggleSkill"
|
|
8743
|
+
};
|
|
8744
|
+
} catch (error) {
|
|
8745
|
+
this.logger?.error("Failed to toggle skill", error);
|
|
8746
|
+
return {
|
|
8747
|
+
success: false,
|
|
8748
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8749
|
+
};
|
|
8750
|
+
}
|
|
8751
|
+
},
|
|
8752
|
+
deleteSkill: async (params) => {
|
|
8753
|
+
try {
|
|
8754
|
+
if (this.provider && this.provider.deleteSkill) {
|
|
8755
|
+
const result = await this.provider.deleteSkill(params);
|
|
8756
|
+
this.logger?.info("Delete skill completed", {
|
|
8757
|
+
success: result.success,
|
|
8758
|
+
hasError: !!result.error
|
|
8759
|
+
});
|
|
8760
|
+
return result;
|
|
8761
|
+
}
|
|
8762
|
+
return {
|
|
8763
|
+
success: false,
|
|
8764
|
+
error: "Provider does not support deleteSkill"
|
|
8765
|
+
};
|
|
8766
|
+
} catch (error) {
|
|
8767
|
+
this.logger?.error("Failed to delete skill", error);
|
|
8768
|
+
return {
|
|
8769
|
+
success: false,
|
|
8770
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8771
|
+
};
|
|
8772
|
+
}
|
|
8773
|
+
},
|
|
8774
|
+
getSkillContent: async (params) => {
|
|
8775
|
+
try {
|
|
8776
|
+
if (this.provider && this.provider.getSkillContent) {
|
|
8777
|
+
const result = await this.provider.getSkillContent(params);
|
|
8778
|
+
this.logger?.info("Get skill content completed", { hasError: !!result.error });
|
|
8779
|
+
return result;
|
|
8780
|
+
}
|
|
8781
|
+
return {
|
|
8782
|
+
content: "",
|
|
8783
|
+
error: "Provider does not support getSkillContent"
|
|
8784
|
+
};
|
|
8785
|
+
} catch (error) {
|
|
8786
|
+
this.logger?.error("Failed to get skill content", error);
|
|
8787
|
+
return {
|
|
8788
|
+
content: "",
|
|
8789
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8790
|
+
};
|
|
8791
|
+
}
|
|
8792
|
+
},
|
|
8793
|
+
getMarketplaceSkills: async () => {
|
|
8794
|
+
try {
|
|
8795
|
+
if (this.provider && this.provider.getMarketplaceSkills) {
|
|
8796
|
+
const result = await this.provider.getMarketplaceSkills();
|
|
8797
|
+
this.logger?.info("Marketplace skills retrieved", {
|
|
8798
|
+
resultCount: result.results.length,
|
|
8799
|
+
hasError: !!result.error
|
|
8800
|
+
});
|
|
8801
|
+
return result;
|
|
8802
|
+
}
|
|
8803
|
+
return {
|
|
8804
|
+
results: [],
|
|
8805
|
+
error: "Provider does not support getMarketplaceSkills"
|
|
8806
|
+
};
|
|
8807
|
+
} catch (error) {
|
|
8808
|
+
this.logger?.error("Failed to get marketplace skills", error);
|
|
8809
|
+
return {
|
|
8810
|
+
results: [],
|
|
8811
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8812
|
+
};
|
|
8813
|
+
}
|
|
8814
|
+
},
|
|
8815
|
+
getMarketplaceSkillContent: async (params) => {
|
|
8816
|
+
try {
|
|
8817
|
+
if (this.provider && this.provider.getMarketplaceSkillContent) return await this.provider.getMarketplaceSkillContent(params);
|
|
8818
|
+
return {
|
|
8819
|
+
content: "",
|
|
8820
|
+
error: "Provider does not support getMarketplaceSkillContent"
|
|
8821
|
+
};
|
|
8822
|
+
} catch (error) {
|
|
8823
|
+
this.logger?.error("Failed to get marketplace skill content", error);
|
|
8824
|
+
return {
|
|
8825
|
+
content: "",
|
|
8826
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8827
|
+
};
|
|
8828
|
+
}
|
|
8829
|
+
},
|
|
8830
|
+
installMarketplaceSkill: async (params) => {
|
|
8831
|
+
try {
|
|
8832
|
+
if (this.provider && this.provider.installMarketplaceSkill) return await this.provider.installMarketplaceSkill(params);
|
|
8833
|
+
return {
|
|
8834
|
+
success: false,
|
|
8835
|
+
skillName: params.skillName,
|
|
8836
|
+
errorMessage: "Provider does not support installMarketplaceSkill"
|
|
8837
|
+
};
|
|
8838
|
+
} catch (error) {
|
|
8839
|
+
this.logger?.error("Failed to install marketplace skill", error);
|
|
8840
|
+
return {
|
|
8841
|
+
success: false,
|
|
8842
|
+
skillName: params.skillName,
|
|
8843
|
+
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
8844
|
+
};
|
|
8845
|
+
}
|
|
8846
|
+
},
|
|
7587
8847
|
batchTogglePlugins: async (request) => {
|
|
7588
8848
|
try {
|
|
7589
8849
|
if (this.provider && this.provider.batchTogglePlugins) {
|
|
@@ -7628,37 +8888,229 @@ var AgentClient = class {
|
|
|
7628
8888
|
return [];
|
|
7629
8889
|
}
|
|
7630
8890
|
},
|
|
7631
|
-
installPlugins: async (pluginNames, marketplaceName, installScope) => {
|
|
8891
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
8892
|
+
try {
|
|
8893
|
+
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
8894
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
8895
|
+
this.logger?.info("Install plugins", {
|
|
8896
|
+
pluginNames,
|
|
8897
|
+
marketplaceName,
|
|
8898
|
+
success: result.success
|
|
8899
|
+
});
|
|
8900
|
+
return result;
|
|
8901
|
+
}
|
|
8902
|
+
this.logger?.warn("Provider does not support installPlugins");
|
|
8903
|
+
return {
|
|
8904
|
+
success: false,
|
|
8905
|
+
error: "Provider does not support installPlugins"
|
|
8906
|
+
};
|
|
8907
|
+
} catch (error) {
|
|
8908
|
+
this.logger?.error("Failed to install plugins", error);
|
|
8909
|
+
return {
|
|
8910
|
+
success: false,
|
|
8911
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8912
|
+
};
|
|
8913
|
+
}
|
|
8914
|
+
},
|
|
8915
|
+
uninstallPlugin: async (pluginName, marketplaceName, scope) => {
|
|
8916
|
+
try {
|
|
8917
|
+
if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
|
|
8918
|
+
const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
|
|
8919
|
+
this.logger?.info("Uninstall plugin", {
|
|
8920
|
+
pluginName,
|
|
8921
|
+
marketplaceName,
|
|
8922
|
+
scope,
|
|
8923
|
+
success: result.success
|
|
8924
|
+
});
|
|
8925
|
+
return result;
|
|
8926
|
+
}
|
|
8927
|
+
this.logger?.warn("Provider does not support uninstallPlugin");
|
|
8928
|
+
return {
|
|
8929
|
+
success: false,
|
|
8930
|
+
error: "Provider does not support uninstallPlugin"
|
|
8931
|
+
};
|
|
8932
|
+
} catch (error) {
|
|
8933
|
+
this.logger?.error("Failed to uninstall plugin", error);
|
|
8934
|
+
return {
|
|
8935
|
+
success: false,
|
|
8936
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8937
|
+
};
|
|
8938
|
+
}
|
|
8939
|
+
},
|
|
8940
|
+
updatePlugin: async (pluginName, marketplaceName) => {
|
|
8941
|
+
try {
|
|
8942
|
+
if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
|
|
8943
|
+
const result = await this.provider.updatePlugin(pluginName, marketplaceName);
|
|
8944
|
+
this.logger?.info("Update plugin", {
|
|
8945
|
+
pluginName,
|
|
8946
|
+
marketplaceName,
|
|
8947
|
+
success: result.success
|
|
8948
|
+
});
|
|
8949
|
+
return result;
|
|
8950
|
+
}
|
|
8951
|
+
this.logger?.warn("Provider does not support updatePlugin");
|
|
8952
|
+
return {
|
|
8953
|
+
success: false,
|
|
8954
|
+
error: "Provider does not support updatePlugin"
|
|
8955
|
+
};
|
|
8956
|
+
} catch (error) {
|
|
8957
|
+
this.logger?.error("Failed to update plugin", error);
|
|
8958
|
+
return {
|
|
8959
|
+
success: false,
|
|
8960
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8961
|
+
};
|
|
8962
|
+
}
|
|
8963
|
+
},
|
|
8964
|
+
getPluginMarketplaces: async (forceRefresh) => {
|
|
8965
|
+
try {
|
|
8966
|
+
if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
|
|
8967
|
+
const result = await this.provider.getPluginMarketplaces(forceRefresh);
|
|
8968
|
+
this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
|
|
8969
|
+
return result;
|
|
8970
|
+
}
|
|
8971
|
+
this.logger?.warn("Provider does not support getPluginMarketplaces");
|
|
8972
|
+
return [];
|
|
8973
|
+
} catch (error) {
|
|
8974
|
+
this.logger?.error("Failed to get plugin marketplaces", error);
|
|
8975
|
+
return [];
|
|
8976
|
+
}
|
|
8977
|
+
},
|
|
8978
|
+
getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
|
|
8979
|
+
try {
|
|
8980
|
+
if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
|
|
8981
|
+
const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
|
|
8982
|
+
this.logger?.info("Got marketplace plugins", {
|
|
8983
|
+
marketplaceName,
|
|
8984
|
+
count: result?.length ?? 0
|
|
8985
|
+
});
|
|
8986
|
+
return result;
|
|
8987
|
+
}
|
|
8988
|
+
this.logger?.warn("Provider does not support getMarketplacePlugins");
|
|
8989
|
+
return [];
|
|
8990
|
+
} catch (error) {
|
|
8991
|
+
this.logger?.error("Failed to get marketplace plugins", error);
|
|
8992
|
+
return [];
|
|
8993
|
+
}
|
|
8994
|
+
},
|
|
8995
|
+
getPluginDetail: async (pluginName, marketplaceName) => {
|
|
8996
|
+
try {
|
|
8997
|
+
if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
|
|
8998
|
+
const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
|
|
8999
|
+
this.logger?.info("Got plugin detail", {
|
|
9000
|
+
pluginName,
|
|
9001
|
+
marketplaceName
|
|
9002
|
+
});
|
|
9003
|
+
return result;
|
|
9004
|
+
}
|
|
9005
|
+
this.logger?.warn("Provider does not support getPluginDetail");
|
|
9006
|
+
return null;
|
|
9007
|
+
} catch (error) {
|
|
9008
|
+
this.logger?.error("Failed to get plugin detail", error);
|
|
9009
|
+
return null;
|
|
9010
|
+
}
|
|
9011
|
+
},
|
|
9012
|
+
addPluginMarketplace: async (source, name) => {
|
|
9013
|
+
try {
|
|
9014
|
+
if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
|
|
9015
|
+
const result = await this.provider.addPluginMarketplace(source, name);
|
|
9016
|
+
this.logger?.info("Add plugin marketplace", {
|
|
9017
|
+
source,
|
|
9018
|
+
name,
|
|
9019
|
+
success: result.success
|
|
9020
|
+
});
|
|
9021
|
+
return result;
|
|
9022
|
+
}
|
|
9023
|
+
this.logger?.warn("Provider does not support addPluginMarketplace");
|
|
9024
|
+
return {
|
|
9025
|
+
success: false,
|
|
9026
|
+
error: "Provider does not support addPluginMarketplace"
|
|
9027
|
+
};
|
|
9028
|
+
} catch (error) {
|
|
9029
|
+
this.logger?.error("Failed to add plugin marketplace", error);
|
|
9030
|
+
return {
|
|
9031
|
+
success: false,
|
|
9032
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
9033
|
+
};
|
|
9034
|
+
}
|
|
9035
|
+
},
|
|
9036
|
+
removePluginMarketplace: async (marketplaceName) => {
|
|
9037
|
+
try {
|
|
9038
|
+
if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
|
|
9039
|
+
const result = await this.provider.removePluginMarketplace(marketplaceName);
|
|
9040
|
+
this.logger?.info("Remove plugin marketplace", {
|
|
9041
|
+
marketplaceName,
|
|
9042
|
+
success: result.success
|
|
9043
|
+
});
|
|
9044
|
+
return result;
|
|
9045
|
+
}
|
|
9046
|
+
this.logger?.warn("Provider does not support removePluginMarketplace");
|
|
9047
|
+
return {
|
|
9048
|
+
success: false,
|
|
9049
|
+
error: "Provider does not support removePluginMarketplace"
|
|
9050
|
+
};
|
|
9051
|
+
} catch (error) {
|
|
9052
|
+
this.logger?.error("Failed to remove plugin marketplace", error);
|
|
9053
|
+
return {
|
|
9054
|
+
success: false,
|
|
9055
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
9056
|
+
};
|
|
9057
|
+
}
|
|
9058
|
+
},
|
|
9059
|
+
refreshPluginMarketplace: async (marketplaceName) => {
|
|
7632
9060
|
try {
|
|
7633
|
-
if (this.provider && "
|
|
7634
|
-
const result = await this.provider.
|
|
7635
|
-
this.logger?.info("
|
|
7636
|
-
pluginNames,
|
|
9061
|
+
if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
|
|
9062
|
+
const result = await this.provider.refreshPluginMarketplace(marketplaceName);
|
|
9063
|
+
this.logger?.info("Refresh plugin marketplace", {
|
|
7637
9064
|
marketplaceName,
|
|
7638
9065
|
success: result.success
|
|
7639
9066
|
});
|
|
7640
9067
|
return result;
|
|
7641
9068
|
}
|
|
7642
|
-
this.logger?.warn("Provider does not support
|
|
9069
|
+
this.logger?.warn("Provider does not support refreshPluginMarketplace");
|
|
7643
9070
|
return {
|
|
7644
9071
|
success: false,
|
|
7645
|
-
error: "Provider does not support
|
|
9072
|
+
error: "Provider does not support refreshPluginMarketplace"
|
|
7646
9073
|
};
|
|
7647
9074
|
} catch (error) {
|
|
7648
|
-
this.logger?.error("Failed to
|
|
9075
|
+
this.logger?.error("Failed to refresh plugin marketplace", error);
|
|
7649
9076
|
return {
|
|
7650
9077
|
success: false,
|
|
7651
9078
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
7652
9079
|
};
|
|
7653
9080
|
}
|
|
7654
9081
|
},
|
|
7655
|
-
|
|
9082
|
+
openFolderInNewWindow: async (folderPath) => {
|
|
9083
|
+
try {
|
|
9084
|
+
if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
|
|
9085
|
+
await this.provider.openFolderInNewWindow(folderPath);
|
|
9086
|
+
this.logger?.info("Opened folder in new window", { folderPath });
|
|
9087
|
+
} else {
|
|
9088
|
+
this.logger?.warn("Provider does not support openFolderInNewWindow");
|
|
9089
|
+
throw new Error("Provider does not support openFolderInNewWindow");
|
|
9090
|
+
}
|
|
9091
|
+
} catch (error) {
|
|
9092
|
+
this.logger?.error("Failed to open folder in new window", error);
|
|
9093
|
+
throw error;
|
|
9094
|
+
}
|
|
9095
|
+
},
|
|
9096
|
+
openFolder: async (folderPath) => {
|
|
7656
9097
|
try {
|
|
7657
|
-
if (this.provider && "
|
|
7658
|
-
const result = await this.provider.
|
|
7659
|
-
this.logger?.info("
|
|
9098
|
+
if (this.provider && "openFolder" in this.provider && typeof this.provider.openFolder === "function") {
|
|
9099
|
+
const result = await this.provider.openFolder(folderPath);
|
|
9100
|
+
this.logger?.info("Opened folder in system file manager", { folderPath });
|
|
7660
9101
|
return result;
|
|
9102
|
+
} else {
|
|
9103
|
+
this.logger?.warn("Provider does not support openFolder");
|
|
9104
|
+
throw new Error("Provider does not support openFolder");
|
|
7661
9105
|
}
|
|
9106
|
+
} catch (error) {
|
|
9107
|
+
this.logger?.error("Failed to open folder", error);
|
|
9108
|
+
throw error;
|
|
9109
|
+
}
|
|
9110
|
+
},
|
|
9111
|
+
getSupportScenes: async (locale) => {
|
|
9112
|
+
try {
|
|
9113
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
7662
9114
|
this.logger?.warn("Provider does not support getSupportScenes");
|
|
7663
9115
|
return [];
|
|
7664
9116
|
} catch (error) {
|
|
@@ -7666,6 +9118,193 @@ var AgentClient = class {
|
|
|
7666
9118
|
return [];
|
|
7667
9119
|
}
|
|
7668
9120
|
},
|
|
9121
|
+
getProductScenes: async (locale) => {
|
|
9122
|
+
try {
|
|
9123
|
+
if (this.provider?.getProductScenes) {
|
|
9124
|
+
const result = await this.provider.getProductScenes(locale);
|
|
9125
|
+
this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
|
|
9126
|
+
return result;
|
|
9127
|
+
}
|
|
9128
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
9129
|
+
return [];
|
|
9130
|
+
} catch (error) {
|
|
9131
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
9132
|
+
return [];
|
|
9133
|
+
}
|
|
9134
|
+
},
|
|
9135
|
+
getAvailableCommands: async (params) => {
|
|
9136
|
+
try {
|
|
9137
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
9138
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
9139
|
+
this.logger?.info("Got available commands from provider", {
|
|
9140
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
9141
|
+
count: result?.length ?? 0
|
|
9142
|
+
});
|
|
9143
|
+
return result;
|
|
9144
|
+
}
|
|
9145
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
9146
|
+
return [];
|
|
9147
|
+
} catch (error) {
|
|
9148
|
+
this.logger?.error("Failed to get available commands", error);
|
|
9149
|
+
return [];
|
|
9150
|
+
}
|
|
9151
|
+
},
|
|
9152
|
+
reportTelemetry: async (eventName, payload) => {
|
|
9153
|
+
try {
|
|
9154
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
9155
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
9156
|
+
} catch (error) {
|
|
9157
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
9158
|
+
}
|
|
9159
|
+
},
|
|
9160
|
+
getProductConfiguration: async () => {
|
|
9161
|
+
try {
|
|
9162
|
+
if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
|
|
9163
|
+
this.logger?.warn("Provider does not support getProductConfiguration");
|
|
9164
|
+
return {};
|
|
9165
|
+
} catch (error) {
|
|
9166
|
+
this.logger?.error("Failed to get product configuration", error);
|
|
9167
|
+
return {};
|
|
9168
|
+
}
|
|
9169
|
+
},
|
|
9170
|
+
getUserInfo: async () => {
|
|
9171
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() called");
|
|
9172
|
+
try {
|
|
9173
|
+
if (this.provider?.getUserInfo) {
|
|
9174
|
+
const result = await this.provider.getUserInfo();
|
|
9175
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
|
|
9176
|
+
return result;
|
|
9177
|
+
}
|
|
9178
|
+
this.logger?.warn("Provider does not support getUserInfo");
|
|
9179
|
+
return {};
|
|
9180
|
+
} catch (error) {
|
|
9181
|
+
this.logger?.error("Failed to get user info", error);
|
|
9182
|
+
return {};
|
|
9183
|
+
}
|
|
9184
|
+
},
|
|
9185
|
+
respondToSampling: async (sessionId, response) => {
|
|
9186
|
+
try {
|
|
9187
|
+
if (this.provider?.respondToSampling) {
|
|
9188
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
9189
|
+
this.logger?.info("Responded to sampling request", {
|
|
9190
|
+
sessionId,
|
|
9191
|
+
requestId: response.id,
|
|
9192
|
+
approved: response.approved
|
|
9193
|
+
});
|
|
9194
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
9195
|
+
} catch (error) {
|
|
9196
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
9197
|
+
throw error;
|
|
9198
|
+
}
|
|
9199
|
+
},
|
|
9200
|
+
respondToRoots: async (sessionId, response) => {
|
|
9201
|
+
try {
|
|
9202
|
+
if (this.provider?.respondToRoots) {
|
|
9203
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
9204
|
+
this.logger?.info("Responded to roots request", {
|
|
9205
|
+
sessionId,
|
|
9206
|
+
requestId: response.id,
|
|
9207
|
+
approved: response.approved
|
|
9208
|
+
});
|
|
9209
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
9210
|
+
} catch (error) {
|
|
9211
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
9212
|
+
throw error;
|
|
9213
|
+
}
|
|
9214
|
+
},
|
|
9215
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
9216
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
9217
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
9218
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
9219
|
+
}
|
|
9220
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
9221
|
+
return () => {};
|
|
9222
|
+
},
|
|
9223
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
9224
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
9225
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
9226
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
9227
|
+
}
|
|
9228
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
9229
|
+
return () => {};
|
|
9230
|
+
},
|
|
9231
|
+
getMcpServers: async () => {
|
|
9232
|
+
if (this.provider?.getMcpServers) {
|
|
9233
|
+
this.logger?.info("Getting MCP servers list");
|
|
9234
|
+
return this.provider.getMcpServers();
|
|
9235
|
+
}
|
|
9236
|
+
this.logger?.warn("Provider does not support getMcpServers");
|
|
9237
|
+
return [];
|
|
9238
|
+
},
|
|
9239
|
+
toggleMcpServer: async (serverName, enabled) => {
|
|
9240
|
+
if (this.provider?.toggleMcpServer) {
|
|
9241
|
+
this.logger?.info("Toggling MCP server", {
|
|
9242
|
+
serverName,
|
|
9243
|
+
enabled
|
|
9244
|
+
});
|
|
9245
|
+
await this.provider.toggleMcpServer(serverName, enabled);
|
|
9246
|
+
} else {
|
|
9247
|
+
this.logger?.warn("Provider does not support toggleMcpServer");
|
|
9248
|
+
throw new Error("toggleMcpServer not supported by provider");
|
|
9249
|
+
}
|
|
9250
|
+
},
|
|
9251
|
+
reconnectMcpServer: async (serverName, forceHttpCallback) => {
|
|
9252
|
+
if (this.provider?.reconnectMcpServer) {
|
|
9253
|
+
this.logger?.info("Reconnecting MCP server", {
|
|
9254
|
+
serverName,
|
|
9255
|
+
forceHttpCallback
|
|
9256
|
+
});
|
|
9257
|
+
await this.provider.reconnectMcpServer(serverName, forceHttpCallback);
|
|
9258
|
+
} else {
|
|
9259
|
+
this.logger?.warn("Provider does not support reconnectMcpServer");
|
|
9260
|
+
throw new Error("reconnectMcpServer not supported by provider");
|
|
9261
|
+
}
|
|
9262
|
+
},
|
|
9263
|
+
deleteMcpServer: async (serverName) => {
|
|
9264
|
+
if (this.provider?.deleteMcpServer) {
|
|
9265
|
+
this.logger?.info("Deleting MCP server", { serverName });
|
|
9266
|
+
await this.provider.deleteMcpServer(serverName);
|
|
9267
|
+
} else {
|
|
9268
|
+
this.logger?.warn("Provider does not support deleteMcpServer");
|
|
9269
|
+
throw new Error("deleteMcpServer not supported by provider");
|
|
9270
|
+
}
|
|
9271
|
+
},
|
|
9272
|
+
openMcpConfig: async () => {
|
|
9273
|
+
if (this.provider?.openMcpConfig) {
|
|
9274
|
+
this.logger?.info("Opening MCP config");
|
|
9275
|
+
await this.provider.openMcpConfig();
|
|
9276
|
+
} else {
|
|
9277
|
+
this.logger?.warn("Provider does not support openMcpConfig");
|
|
9278
|
+
throw new Error("openMcpConfig not supported by provider");
|
|
9279
|
+
}
|
|
9280
|
+
},
|
|
9281
|
+
getMcpConfigContent: async () => {
|
|
9282
|
+
if (this.provider?.getMcpConfigContent) {
|
|
9283
|
+
this.logger?.info("Getting MCP config content");
|
|
9284
|
+
return await this.provider.getMcpConfigContent();
|
|
9285
|
+
} else {
|
|
9286
|
+
this.logger?.warn("Provider does not support getMcpConfigContent");
|
|
9287
|
+
throw new Error("getMcpConfigContent not supported by provider");
|
|
9288
|
+
}
|
|
9289
|
+
},
|
|
9290
|
+
saveMcpConfigContent: async (content) => {
|
|
9291
|
+
if (this.provider?.saveMcpConfigContent) {
|
|
9292
|
+
this.logger?.info("Saving MCP config content");
|
|
9293
|
+
await this.provider.saveMcpConfigContent(content);
|
|
9294
|
+
} else {
|
|
9295
|
+
this.logger?.warn("Provider does not support saveMcpConfigContent");
|
|
9296
|
+
throw new Error("saveMcpConfigContent not supported by provider");
|
|
9297
|
+
}
|
|
9298
|
+
},
|
|
9299
|
+
clipboardReadText: async () => {
|
|
9300
|
+
if (this.provider?.clipboardReadText) {
|
|
9301
|
+
this.logger?.info("Reading clipboard text");
|
|
9302
|
+
return await this.provider.clipboardReadText();
|
|
9303
|
+
} else {
|
|
9304
|
+
this.logger?.warn("Provider does not support clipboardReadText");
|
|
9305
|
+
throw new Error("clipboardReadText not supported by provider");
|
|
9306
|
+
}
|
|
9307
|
+
},
|
|
7669
9308
|
models: this.createModelsResource()
|
|
7670
9309
|
};
|
|
7671
9310
|
}
|
|
@@ -7732,6 +9371,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
7732
9371
|
return AccountStatus;
|
|
7733
9372
|
}({});
|
|
7734
9373
|
|
|
9374
|
+
//#endregion
|
|
9375
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
9376
|
+
/**
|
|
9377
|
+
* OAuth Repository Service
|
|
9378
|
+
*
|
|
9379
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
9380
|
+
*/
|
|
9381
|
+
/**
|
|
9382
|
+
* OAuth Repository Service
|
|
9383
|
+
*
|
|
9384
|
+
* 提供仓库和分支的查询操作
|
|
9385
|
+
*/
|
|
9386
|
+
var OAuthRepositoryService = class {
|
|
9387
|
+
/**
|
|
9388
|
+
* 获取仓库分支列表
|
|
9389
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
9390
|
+
*
|
|
9391
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9392
|
+
* @param params 平台特定的查询参数
|
|
9393
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9394
|
+
* @param perPage 每页数量,最大100
|
|
9395
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
9396
|
+
*
|
|
9397
|
+
* @example
|
|
9398
|
+
* ```typescript
|
|
9399
|
+
* // GitHub
|
|
9400
|
+
* const branches = await service.getBranches('github', {
|
|
9401
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
9402
|
+
* repo: 'CodeBuddyIDE'
|
|
9403
|
+
* });
|
|
9404
|
+
*
|
|
9405
|
+
* // Gongfeng
|
|
9406
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
9407
|
+
* project_id: '1611499'
|
|
9408
|
+
* });
|
|
9409
|
+
*
|
|
9410
|
+
* // CNB
|
|
9411
|
+
* const branches = await service.getBranches('cnb', {
|
|
9412
|
+
* repo: 'genie/genie-ide'
|
|
9413
|
+
* });
|
|
9414
|
+
* ```
|
|
9415
|
+
*/
|
|
9416
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
9417
|
+
try {
|
|
9418
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
9419
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
9420
|
+
const apiResponse = await httpService.get(url);
|
|
9421
|
+
if (!apiResponse.data) {
|
|
9422
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
9423
|
+
return [];
|
|
9424
|
+
}
|
|
9425
|
+
const branches = apiResponse.data.branches || [];
|
|
9426
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
9427
|
+
return branches;
|
|
9428
|
+
} catch (error) {
|
|
9429
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
9430
|
+
throw error;
|
|
9431
|
+
}
|
|
9432
|
+
}
|
|
9433
|
+
/**
|
|
9434
|
+
* 获取仓库列表
|
|
9435
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
9436
|
+
*
|
|
9437
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
9438
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
9439
|
+
*
|
|
9440
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9441
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9442
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
9443
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
9444
|
+
* @param perPage 每页数量,最大100
|
|
9445
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
9446
|
+
*
|
|
9447
|
+
* @example
|
|
9448
|
+
* ```typescript
|
|
9449
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
9450
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
9451
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
9452
|
+
*
|
|
9453
|
+
* // Gongfeng
|
|
9454
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
9455
|
+
* // response.gongfeng_repos 是数组
|
|
9456
|
+
*
|
|
9457
|
+
* // CNB
|
|
9458
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
9459
|
+
* // response.cnb_repos 是数组
|
|
9460
|
+
* ```
|
|
9461
|
+
*/
|
|
9462
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
9463
|
+
try {
|
|
9464
|
+
const queryParams = new URLSearchParams();
|
|
9465
|
+
queryParams.append("page", String(page));
|
|
9466
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
9467
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
9468
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
9469
|
+
const apiResponse = await httpService.get(url);
|
|
9470
|
+
if (!apiResponse.data) {
|
|
9471
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
9472
|
+
return {};
|
|
9473
|
+
}
|
|
9474
|
+
const response = apiResponse.data;
|
|
9475
|
+
this.logRepositoryCounts(response);
|
|
9476
|
+
return response;
|
|
9477
|
+
} catch (error) {
|
|
9478
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
9479
|
+
throw error;
|
|
9480
|
+
}
|
|
9481
|
+
}
|
|
9482
|
+
/**
|
|
9483
|
+
* 构建分支查询参数
|
|
9484
|
+
*/
|
|
9485
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
9486
|
+
const queryParams = new URLSearchParams();
|
|
9487
|
+
queryParams.append("page", String(page));
|
|
9488
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
9489
|
+
if (connector === "github") {
|
|
9490
|
+
const githubParams = params;
|
|
9491
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
9492
|
+
queryParams.append("owner", githubParams.owner);
|
|
9493
|
+
queryParams.append("repo", githubParams.repo);
|
|
9494
|
+
} else if (connector === "gongfeng") {
|
|
9495
|
+
const gongfengParams = params;
|
|
9496
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
9497
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
9498
|
+
} else if (connector === "cnb") {
|
|
9499
|
+
const cnbParams = params;
|
|
9500
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
9501
|
+
queryParams.append("repo", cnbParams.repo);
|
|
9502
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
9503
|
+
return queryParams;
|
|
9504
|
+
}
|
|
9505
|
+
/**
|
|
9506
|
+
* 记录仓库数量日志
|
|
9507
|
+
*/
|
|
9508
|
+
logRepositoryCounts(response) {
|
|
9509
|
+
if (response.github_repos) {
|
|
9510
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
9511
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
9512
|
+
}
|
|
9513
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
9514
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
9515
|
+
}
|
|
9516
|
+
};
|
|
9517
|
+
/**
|
|
9518
|
+
* OAuth Repository Service 单例实例
|
|
9519
|
+
*/
|
|
9520
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
9521
|
+
|
|
7735
9522
|
//#endregion
|
|
7736
9523
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
7737
9524
|
/**
|
|
@@ -7740,28 +9527,17 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
7740
9527
|
* 封装与后端 API 的 HTTP 通信
|
|
7741
9528
|
*/
|
|
7742
9529
|
/**
|
|
7743
|
-
*
|
|
7744
|
-
*
|
|
9530
|
+
* 判断当前账号是否是 SSO 账号
|
|
9531
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
7745
9532
|
*/
|
|
7746
|
-
const isSSODomain = () => {
|
|
7747
|
-
const { hostname } = window.location;
|
|
7748
|
-
return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
|
|
7749
|
-
};
|
|
7750
9533
|
/**
|
|
7751
|
-
*
|
|
7752
|
-
* - SSO
|
|
7753
|
-
* - 非 SSO
|
|
9534
|
+
* 根据路径获取完整 URL
|
|
9535
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
9536
|
+
* - 非 SSO 账号直接使用当前域名
|
|
9537
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
9538
|
+
* @returns 完整的 URL 地址
|
|
7754
9539
|
*/
|
|
7755
|
-
const
|
|
7756
|
-
const { hostname, protocol } = window.location;
|
|
7757
|
-
if (isSSODomain()) {
|
|
7758
|
-
const isCodebuddy = hostname.includes("codebuddy.cn");
|
|
7759
|
-
const isStaging = hostname.includes("staging");
|
|
7760
|
-
if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
|
|
7761
|
-
else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
|
|
7762
|
-
}
|
|
7763
|
-
return `${window.location.origin}/login`;
|
|
7764
|
-
};
|
|
9540
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
7765
9541
|
/** 获取当前域名的账号选择页面 URL */
|
|
7766
9542
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
7767
9543
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -7798,12 +9574,30 @@ var BackendProvider = class {
|
|
|
7798
9574
|
constructor(config) {
|
|
7799
9575
|
httpService.setBaseURL(config.baseUrl);
|
|
7800
9576
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
7801
|
-
httpService.onUnauthorized(() =>
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
9577
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
9578
|
+
}
|
|
9579
|
+
/**
|
|
9580
|
+
* 处理 401 未授权错误
|
|
9581
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
9582
|
+
*
|
|
9583
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
9584
|
+
*/
|
|
9585
|
+
async handleUnauthorized() {
|
|
9586
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
9587
|
+
try {
|
|
9588
|
+
if (await this.refreshToken()) {
|
|
9589
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
9590
|
+
return;
|
|
9591
|
+
}
|
|
9592
|
+
throw new Error("Token refresh returned null");
|
|
9593
|
+
} catch (error) {
|
|
9594
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
9595
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
9596
|
+
this.logout().catch((logoutError) => {
|
|
9597
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
7805
9598
|
});
|
|
7806
|
-
|
|
9599
|
+
throw error;
|
|
9600
|
+
}
|
|
7807
9601
|
}
|
|
7808
9602
|
/**
|
|
7809
9603
|
* 获取当前账号信息
|
|
@@ -7848,7 +9642,7 @@ var BackendProvider = class {
|
|
|
7848
9642
|
return account;
|
|
7849
9643
|
}
|
|
7850
9644
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
7851
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
9645
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
7852
9646
|
accountService.setAccount(null);
|
|
7853
9647
|
return null;
|
|
7854
9648
|
} catch (error) {
|
|
@@ -7871,7 +9665,8 @@ var BackendProvider = class {
|
|
|
7871
9665
|
activeStatus: connector.active_status,
|
|
7872
9666
|
displayName: connector.display_name,
|
|
7873
9667
|
oauthClientId: connector.oauth_client_id,
|
|
7874
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
9668
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
9669
|
+
oauthAppName: connector.oauth_app_name
|
|
7875
9670
|
})) };
|
|
7876
9671
|
}
|
|
7877
9672
|
throw result;
|
|
@@ -7953,7 +9748,8 @@ var BackendProvider = class {
|
|
|
7953
9748
|
connectStatus: connector.connect_status,
|
|
7954
9749
|
displayName: connector.display_name,
|
|
7955
9750
|
oauthClientId: connector.oauth_client_id,
|
|
7956
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
9751
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
9752
|
+
oauthAppName: connector.oauth_app_name
|
|
7957
9753
|
})) };
|
|
7958
9754
|
}
|
|
7959
9755
|
throw result;
|
|
@@ -8065,10 +9861,11 @@ var BackendProvider = class {
|
|
|
8065
9861
|
if (!time) return 0;
|
|
8066
9862
|
return new Date(time).getTime();
|
|
8067
9863
|
};
|
|
8068
|
-
const dailyCredits = [CommodityCode.free
|
|
9864
|
+
const dailyCredits = [CommodityCode.free];
|
|
8069
9865
|
const planResources = resources.map((r) => {
|
|
8070
9866
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
8071
9867
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
9868
|
+
const refreshAt = parseTime(r.CycleEndTime) + 1e3;
|
|
8072
9869
|
return {
|
|
8073
9870
|
id: r.ResourceId,
|
|
8074
9871
|
name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
|
|
@@ -8078,7 +9875,7 @@ var BackendProvider = class {
|
|
|
8078
9875
|
used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
|
|
8079
9876
|
left: Number(r.CycleCapacityRemainPrecise) || 0,
|
|
8080
9877
|
expireAt: parseTime(endTime),
|
|
8081
|
-
refreshAt: isDaily ? void 0 :
|
|
9878
|
+
refreshAt: isDaily ? void 0 : refreshAt
|
|
8082
9879
|
};
|
|
8083
9880
|
}).sort((a, b) => {
|
|
8084
9881
|
const getPriority = (code) => {
|
|
@@ -8086,10 +9883,11 @@ var BackendProvider = class {
|
|
|
8086
9883
|
CommodityCode.proMon,
|
|
8087
9884
|
CommodityCode.proMonPlus,
|
|
8088
9885
|
CommodityCode.proYear,
|
|
9886
|
+
CommodityCode.freeMon,
|
|
8089
9887
|
CommodityCode.extra
|
|
8090
9888
|
].includes(code)) return 1;
|
|
8091
9889
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
8092
|
-
if ([CommodityCode.free
|
|
9890
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
8093
9891
|
return 4;
|
|
8094
9892
|
};
|
|
8095
9893
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -8210,38 +10008,69 @@ var BackendProvider = class {
|
|
|
8210
10008
|
*/
|
|
8211
10009
|
async login() {
|
|
8212
10010
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
8213
|
-
window.location.href = `${
|
|
10011
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
8214
10012
|
}
|
|
8215
10013
|
/**
|
|
8216
10014
|
* 登出账号
|
|
8217
|
-
*
|
|
10015
|
+
*
|
|
10016
|
+
* 策略:
|
|
10017
|
+
* - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
|
|
10018
|
+
* - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
|
|
8218
10019
|
*/
|
|
8219
10020
|
async logout() {
|
|
8220
|
-
const
|
|
10021
|
+
const account = accountService.getAccount();
|
|
10022
|
+
if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
|
|
10023
|
+
else await this.logoutViaHttp();
|
|
10024
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
10025
|
+
accountService.clearAccount();
|
|
10026
|
+
}
|
|
10027
|
+
/**
|
|
10028
|
+
* IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
|
|
10029
|
+
* 轮询 iframe URL 变化检测完成,兜底超时 5 秒
|
|
10030
|
+
*/
|
|
10031
|
+
async logoutViaIframe() {
|
|
10032
|
+
const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
|
|
8221
10033
|
try {
|
|
8222
10034
|
await new Promise((resolve) => {
|
|
8223
10035
|
const iframe = document.createElement("iframe");
|
|
8224
10036
|
iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
|
|
8225
|
-
iframe.src =
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
10037
|
+
iframe.src = logoutUrl;
|
|
10038
|
+
let pollTimer;
|
|
10039
|
+
let settled = false;
|
|
10040
|
+
const done = () => {
|
|
10041
|
+
if (settled) return;
|
|
10042
|
+
settled = true;
|
|
10043
|
+
clearInterval(pollTimer);
|
|
8231
10044
|
clearTimeout(timeout);
|
|
8232
10045
|
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
8233
|
-
};
|
|
8234
|
-
iframe.onerror = () => {
|
|
8235
|
-
cleanup();
|
|
8236
10046
|
resolve();
|
|
8237
10047
|
};
|
|
10048
|
+
let wasRedirecting = false;
|
|
10049
|
+
pollTimer = setInterval(() => {
|
|
10050
|
+
try {
|
|
10051
|
+
const href = iframe.contentWindow?.location?.href;
|
|
10052
|
+
if (wasRedirecting && href) done();
|
|
10053
|
+
} catch {
|
|
10054
|
+
wasRedirecting = true;
|
|
10055
|
+
}
|
|
10056
|
+
}, 100);
|
|
10057
|
+
const timeout = setTimeout(done, 5e3);
|
|
10058
|
+
iframe.onerror = done;
|
|
8238
10059
|
document.body.appendChild(iframe);
|
|
8239
10060
|
});
|
|
8240
10061
|
} catch (error) {
|
|
8241
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
10062
|
+
console.error("[BackendProvider] logout via iframe failed:", error);
|
|
10063
|
+
}
|
|
10064
|
+
}
|
|
10065
|
+
/**
|
|
10066
|
+
* 非 IOA 企业登出:直接 HTTP 请求 /console/logout
|
|
10067
|
+
*/
|
|
10068
|
+
async logoutViaHttp() {
|
|
10069
|
+
try {
|
|
10070
|
+
await httpService.get("/console/logout");
|
|
10071
|
+
} catch (error) {
|
|
10072
|
+
console.error("[BackendProvider] logout via http failed:", error);
|
|
8242
10073
|
}
|
|
8243
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
8244
|
-
accountService.clearAccount();
|
|
8245
10074
|
}
|
|
8246
10075
|
/**
|
|
8247
10076
|
* 批量切换插件状态
|
|
@@ -8273,6 +10102,101 @@ var BackendProvider = class {
|
|
|
8273
10102
|
return null;
|
|
8274
10103
|
}
|
|
8275
10104
|
}
|
|
10105
|
+
/**
|
|
10106
|
+
* 刷新 Token
|
|
10107
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
10108
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
10109
|
+
*/
|
|
10110
|
+
async refreshToken() {
|
|
10111
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
10112
|
+
try {
|
|
10113
|
+
const account = await this.getAccount();
|
|
10114
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
10115
|
+
return account;
|
|
10116
|
+
} catch (error) {
|
|
10117
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
10118
|
+
return null;
|
|
10119
|
+
}
|
|
10120
|
+
}
|
|
10121
|
+
/**
|
|
10122
|
+
* 获取仓库分支列表
|
|
10123
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
10124
|
+
*
|
|
10125
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
10126
|
+
* @param params 平台特定的查询参数
|
|
10127
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
10128
|
+
* @param perPage 每页数量,最大100
|
|
10129
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
10130
|
+
*/
|
|
10131
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
10132
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
10133
|
+
}
|
|
10134
|
+
/**
|
|
10135
|
+
* 获取仓库列表
|
|
10136
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
10137
|
+
*
|
|
10138
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
10139
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
10140
|
+
*
|
|
10141
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
10142
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
10143
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
10144
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
10145
|
+
* @param perPage 每页数量,最大100
|
|
10146
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
10147
|
+
*/
|
|
10148
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
10149
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
10150
|
+
}
|
|
10151
|
+
/**
|
|
10152
|
+
* 保存待发送的输入内容到后端
|
|
10153
|
+
* API 端点: POST /api/v1/code-id
|
|
10154
|
+
*/
|
|
10155
|
+
async savePendingInput(code) {
|
|
10156
|
+
try {
|
|
10157
|
+
const result = await httpService.post("/api/v1/code-id", { code });
|
|
10158
|
+
return result?.codeId || result?.data?.codeId || null;
|
|
10159
|
+
} catch (e) {
|
|
10160
|
+
console.warn("[BackendProvider] savePendingInput failed:", e);
|
|
10161
|
+
return null;
|
|
10162
|
+
}
|
|
10163
|
+
}
|
|
10164
|
+
/**
|
|
10165
|
+
* 从后端加载待发送的输入内容
|
|
10166
|
+
* API 端点: GET /api/v1/code?id=xxx
|
|
10167
|
+
*/
|
|
10168
|
+
async loadPendingInput(codeId) {
|
|
10169
|
+
try {
|
|
10170
|
+
const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
|
|
10171
|
+
return result?.code || result?.data?.code || null;
|
|
10172
|
+
} catch (e) {
|
|
10173
|
+
console.warn("[BackendProvider] loadPendingInput failed:", e);
|
|
10174
|
+
return null;
|
|
10175
|
+
}
|
|
10176
|
+
}
|
|
10177
|
+
/**
|
|
10178
|
+
* 获取每日签到状态
|
|
10179
|
+
* API 端点: POST /billing/meter/checkin-status
|
|
10180
|
+
*/
|
|
10181
|
+
async getCheckinStatus() {
|
|
10182
|
+
try {
|
|
10183
|
+
const result = await httpService.post("/billing/meter/checkin-status", {});
|
|
10184
|
+
if (result?.code === 0 && result?.data) return result.data;
|
|
10185
|
+
return null;
|
|
10186
|
+
} catch (error) {
|
|
10187
|
+
console.error("[BackendProvider] getCheckinStatus failed:", error);
|
|
10188
|
+
return null;
|
|
10189
|
+
}
|
|
10190
|
+
}
|
|
10191
|
+
/**
|
|
10192
|
+
* 执行每日签到
|
|
10193
|
+
* API 端点: POST /billing/meter/daily-checkin
|
|
10194
|
+
*/
|
|
10195
|
+
async claimDailyCheckin() {
|
|
10196
|
+
const result = await httpService.post("/billing/meter/daily-checkin", {});
|
|
10197
|
+
if (result?.code === 0 && result?.data) return result.data;
|
|
10198
|
+
throw new Error(result?.msg || "Checkin failed");
|
|
10199
|
+
}
|
|
8276
10200
|
};
|
|
8277
10201
|
/**
|
|
8278
10202
|
* 创建 BackendProvider 实例
|
|
@@ -8312,8 +10236,12 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
8312
10236
|
GET_FILE: "backend:get-file",
|
|
8313
10237
|
RELOAD_WINDOW: "backend:reload-window",
|
|
8314
10238
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
10239
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
8315
10240
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
8316
|
-
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
10241
|
+
GET_SUPPORT_SCENES: "backend:get-support-scenes",
|
|
10242
|
+
GET_ACCOUNT_USAGE: "backend:get-account-usage",
|
|
10243
|
+
GET_CHECKIN_STATUS: "backend:get-checkin-status",
|
|
10244
|
+
CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin"
|
|
8317
10245
|
};
|
|
8318
10246
|
/**
|
|
8319
10247
|
* 生成唯一请求 ID
|
|
@@ -8360,12 +10288,14 @@ var IPCBackendProvider = class {
|
|
|
8360
10288
|
*/
|
|
8361
10289
|
async getAccount() {
|
|
8362
10290
|
this.log("Getting account via IPC");
|
|
10291
|
+
const startTime = performance.now();
|
|
8363
10292
|
try {
|
|
8364
10293
|
const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
10294
|
+
this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
|
|
8365
10295
|
accountService.setAccount(account);
|
|
8366
10296
|
return account;
|
|
8367
10297
|
} catch (error) {
|
|
8368
|
-
this.log(
|
|
10298
|
+
this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
|
|
8369
10299
|
accountService.setAccount(null);
|
|
8370
10300
|
return null;
|
|
8371
10301
|
}
|
|
@@ -8607,6 +10537,20 @@ var IPCBackendProvider = class {
|
|
|
8607
10537
|
}
|
|
8608
10538
|
}
|
|
8609
10539
|
/**
|
|
10540
|
+
* 在外部浏览器中打开链接
|
|
10541
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
10542
|
+
* @param url 要打开的 URL
|
|
10543
|
+
*/
|
|
10544
|
+
async openExternal(url) {
|
|
10545
|
+
this.log("Opening external URL via IPC:", url);
|
|
10546
|
+
try {
|
|
10547
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
10548
|
+
} catch (error) {
|
|
10549
|
+
this.log("Open external request failed:", error);
|
|
10550
|
+
throw error;
|
|
10551
|
+
}
|
|
10552
|
+
}
|
|
10553
|
+
/**
|
|
8610
10554
|
* 批量切换插件状态
|
|
8611
10555
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
8612
10556
|
*/
|
|
@@ -8640,6 +10584,51 @@ var IPCBackendProvider = class {
|
|
|
8640
10584
|
}
|
|
8641
10585
|
}
|
|
8642
10586
|
/**
|
|
10587
|
+
* 获取账号用量信息(积分/Credits)
|
|
10588
|
+
* IDE 环境: 通过 IPC 实时获取用量信息,每次打开菜单时调用
|
|
10589
|
+
*
|
|
10590
|
+
* 调用链:
|
|
10591
|
+
* 1. agent-ui: IPCBackendProvider.getAccountUsage()
|
|
10592
|
+
* 2. Agent Manager renderer: BackendService.getAccountUsage()
|
|
10593
|
+
* 3. Main Process: codebuddy:getAccountUsage IPC handler
|
|
10594
|
+
* 4. 返回 { usageLeft, usageTotal, editionType, refreshAt } 等字段
|
|
10595
|
+
*/
|
|
10596
|
+
async getAccountUsage() {
|
|
10597
|
+
this.log("Getting account usage via IPC");
|
|
10598
|
+
try {
|
|
10599
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT_USAGE);
|
|
10600
|
+
} catch (error) {
|
|
10601
|
+
this.log("Get account usage failed:", error);
|
|
10602
|
+
return null;
|
|
10603
|
+
}
|
|
10604
|
+
}
|
|
10605
|
+
/**
|
|
10606
|
+
* 获取每日签到状态
|
|
10607
|
+
* IDE 环境: 通过 IPC 获取签到状态
|
|
10608
|
+
*/
|
|
10609
|
+
async getCheckinStatus() {
|
|
10610
|
+
this.log("Getting checkin status via IPC");
|
|
10611
|
+
try {
|
|
10612
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_CHECKIN_STATUS);
|
|
10613
|
+
} catch (error) {
|
|
10614
|
+
this.log("Get checkin status failed:", error);
|
|
10615
|
+
return null;
|
|
10616
|
+
}
|
|
10617
|
+
}
|
|
10618
|
+
/**
|
|
10619
|
+
* 执行每日签到
|
|
10620
|
+
* IDE 环境: 通过 IPC 执行签到
|
|
10621
|
+
*/
|
|
10622
|
+
async claimDailyCheckin() {
|
|
10623
|
+
this.log("Claiming daily checkin via IPC");
|
|
10624
|
+
try {
|
|
10625
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.CLAIM_DAILY_CHECKIN);
|
|
10626
|
+
} catch (error) {
|
|
10627
|
+
this.log("Claim daily checkin failed:", error);
|
|
10628
|
+
throw error;
|
|
10629
|
+
}
|
|
10630
|
+
}
|
|
10631
|
+
/**
|
|
8643
10632
|
* 调试日志
|
|
8644
10633
|
*/
|
|
8645
10634
|
log(...args) {
|