@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.136ad71.20260213
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 +890 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +861 -102
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +861 -102
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +890 -160
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.13-next.136ad71.20260213.tgz +0 -0
- package/package.json +6 -5
- package/dist/tencent-ai-cloud-agent-sdk-0.2.12.tgz +0 -0
package/dist/index.cjs
CHANGED
|
@@ -700,7 +700,8 @@ const ExtensionMethod = {
|
|
|
700
700
|
CHECKPOINT: "_codebuddy.ai/checkpoint",
|
|
701
701
|
USAGE: "_codebuddy.ai/usage",
|
|
702
702
|
COMMAND: "_codebuddy.ai/command",
|
|
703
|
-
AUTH_URL: "_codebuddy.ai/authUrl"
|
|
703
|
+
AUTH_URL: "_codebuddy.ai/authUrl",
|
|
704
|
+
FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot"
|
|
704
705
|
};
|
|
705
706
|
/**
|
|
706
707
|
* All known extension methods
|
|
@@ -711,7 +712,8 @@ const KNOWN_EXTENSIONS = [
|
|
|
711
712
|
ExtensionMethod.CHECKPOINT,
|
|
712
713
|
ExtensionMethod.USAGE,
|
|
713
714
|
ExtensionMethod.COMMAND,
|
|
714
|
-
ExtensionMethod.AUTH_URL
|
|
715
|
+
ExtensionMethod.AUTH_URL,
|
|
716
|
+
ExtensionMethod.FILE_HISTORY_SNAPSHOT
|
|
715
717
|
];
|
|
716
718
|
|
|
717
719
|
//#endregion
|
|
@@ -1987,10 +1989,6 @@ var StreamableHttpClient = class {
|
|
|
1987
1989
|
},
|
|
1988
1990
|
requestPermission: async (params) => this.handleRequestPermission(params),
|
|
1989
1991
|
extNotification: async (method, params) => {
|
|
1990
|
-
console.log("[ACP-Client] extNotification callback invoked:", {
|
|
1991
|
-
method,
|
|
1992
|
-
paramsKeys: Object.keys(params)
|
|
1993
|
-
});
|
|
1994
1992
|
await this.handleExtNotification(method, params);
|
|
1995
1993
|
},
|
|
1996
1994
|
extMethod: async (method, params) => this.handleExtMethod(method, params)
|
|
@@ -1998,10 +1996,15 @@ var StreamableHttpClient = class {
|
|
|
1998
1996
|
}
|
|
1999
1997
|
/**
|
|
2000
1998
|
* Create a new session
|
|
1999
|
+
*
|
|
2000
|
+
* Retries on transient network errors (e.g., proxy connection reset)
|
|
2001
|
+
* since session/new is idempotent and safe to retry.
|
|
2001
2002
|
*/
|
|
2002
2003
|
async createSession(cwd) {
|
|
2003
2004
|
this.ensureInitialized("createSession");
|
|
2004
|
-
|
|
2005
|
+
const maxRetries = 2;
|
|
2006
|
+
let lastError;
|
|
2007
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
2005
2008
|
const response = await this.connection.newSession({
|
|
2006
2009
|
cwd,
|
|
2007
2010
|
mcpServers: []
|
|
@@ -2009,8 +2012,16 @@ var StreamableHttpClient = class {
|
|
|
2009
2012
|
this.options.logger?.info(`Session created: ${response.sessionId}`);
|
|
2010
2013
|
return response;
|
|
2011
2014
|
} catch (err) {
|
|
2012
|
-
|
|
2015
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
2016
|
+
if (attempt < maxRetries && isRetryableNetworkError(err)) {
|
|
2017
|
+
const delay = 500 * Math.pow(2, attempt);
|
|
2018
|
+
this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
|
|
2019
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2020
|
+
continue;
|
|
2021
|
+
}
|
|
2022
|
+
throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
|
|
2013
2023
|
}
|
|
2024
|
+
throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
|
|
2014
2025
|
}
|
|
2015
2026
|
/**
|
|
2016
2027
|
* Load an existing session
|
|
@@ -2234,6 +2245,18 @@ var StreamableHttpClient = class {
|
|
|
2234
2245
|
if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
|
|
2235
2246
|
}
|
|
2236
2247
|
};
|
|
2248
|
+
/**
|
|
2249
|
+
* Check if an error is a retryable network-level error.
|
|
2250
|
+
* Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
|
|
2251
|
+
*/
|
|
2252
|
+
function isRetryableNetworkError(error) {
|
|
2253
|
+
if (error instanceof TypeError) return true;
|
|
2254
|
+
if (error instanceof Error) {
|
|
2255
|
+
const msg = error.message.toLowerCase();
|
|
2256
|
+
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");
|
|
2257
|
+
}
|
|
2258
|
+
return false;
|
|
2259
|
+
}
|
|
2237
2260
|
|
|
2238
2261
|
//#endregion
|
|
2239
2262
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
|
|
@@ -2283,6 +2306,24 @@ var CloudAgentConnection = class {
|
|
|
2283
2306
|
},
|
|
2284
2307
|
onUsageUpdate: (usage) => {
|
|
2285
2308
|
this.emit("usageUpdate", usage);
|
|
2309
|
+
},
|
|
2310
|
+
onExtNotification: (method, params) => {
|
|
2311
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2312
|
+
method,
|
|
2313
|
+
paramsKeys: Object.keys(params)
|
|
2314
|
+
});
|
|
2315
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2316
|
+
const action = params.action;
|
|
2317
|
+
const commandParams = params.params;
|
|
2318
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2319
|
+
action,
|
|
2320
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2321
|
+
});
|
|
2322
|
+
this.emit("command", {
|
|
2323
|
+
action,
|
|
2324
|
+
params: commandParams
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2286
2327
|
}
|
|
2287
2328
|
});
|
|
2288
2329
|
this.setupEventForwarding();
|
|
@@ -2411,7 +2452,7 @@ var CloudAgentConnection = class {
|
|
|
2411
2452
|
}
|
|
2412
2453
|
async createSession(params) {
|
|
2413
2454
|
return {
|
|
2414
|
-
...await this.client.
|
|
2455
|
+
...await this.client.createSession(this.cwd),
|
|
2415
2456
|
sessionId: this.agentId
|
|
2416
2457
|
};
|
|
2417
2458
|
}
|
|
@@ -2529,6 +2570,16 @@ var CloudAgentConnection = class {
|
|
|
2529
2570
|
get sessionConnectionInfo() {
|
|
2530
2571
|
return this._sessionConnectionInfo;
|
|
2531
2572
|
}
|
|
2573
|
+
async reportTelemetry(eventName, payload) {
|
|
2574
|
+
try {
|
|
2575
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2576
|
+
eventName,
|
|
2577
|
+
payload
|
|
2578
|
+
});
|
|
2579
|
+
} catch (error) {
|
|
2580
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2532
2583
|
async extMethod(method, params) {
|
|
2533
2584
|
return this.client.extMethod(method, params);
|
|
2534
2585
|
}
|
|
@@ -15923,7 +15974,7 @@ var axios_default = axios;
|
|
|
15923
15974
|
* 特性:
|
|
15924
15975
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
15925
15976
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
15926
|
-
* - 统一 401
|
|
15977
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
15927
15978
|
* - 自动携带凭证(withCredentials)
|
|
15928
15979
|
* - 类型安全
|
|
15929
15980
|
*
|
|
@@ -15963,6 +16014,8 @@ var HttpService = class HttpService {
|
|
|
15963
16014
|
*/
|
|
15964
16015
|
constructor(config = {}) {
|
|
15965
16016
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
16017
|
+
this.isRefreshing = false;
|
|
16018
|
+
this.refreshSubscribers = [];
|
|
15966
16019
|
this.config = config;
|
|
15967
16020
|
this.axiosInstance = axios_default.create({
|
|
15968
16021
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -16003,18 +16056,54 @@ var HttpService = class HttpService {
|
|
|
16003
16056
|
}, (error) => Promise.reject(error));
|
|
16004
16057
|
}
|
|
16005
16058
|
/**
|
|
16006
|
-
* 注册默认响应拦截器(处理 401
|
|
16059
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
16007
16060
|
*/
|
|
16008
16061
|
registerDefaultResponseInterceptor() {
|
|
16009
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16062
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
16063
|
+
const originalRequest = error.config;
|
|
16064
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
16065
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
16066
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
16067
|
+
return Promise.reject(error);
|
|
16068
|
+
}
|
|
16069
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
16070
|
+
originalRequest._retry = true;
|
|
16071
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
16072
|
+
this.refreshSubscribers.push((success) => {
|
|
16073
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
16074
|
+
else reject(error);
|
|
16075
|
+
});
|
|
16076
|
+
});
|
|
16077
|
+
this.isRefreshing = true;
|
|
16078
|
+
try {
|
|
16079
|
+
await this.triggerUnauthorizedCallbacks();
|
|
16080
|
+
this.onRefreshSuccess();
|
|
16081
|
+
return this.axiosInstance.request(originalRequest);
|
|
16082
|
+
} catch (refreshError) {
|
|
16083
|
+
this.onRefreshFailure();
|
|
16084
|
+
return Promise.reject(error);
|
|
16085
|
+
} finally {
|
|
16086
|
+
this.isRefreshing = false;
|
|
16087
|
+
}
|
|
16013
16088
|
}
|
|
16014
16089
|
return Promise.reject(error);
|
|
16015
16090
|
});
|
|
16016
16091
|
}
|
|
16017
16092
|
/**
|
|
16093
|
+
* token 刷新成功,通知所有等待的请求
|
|
16094
|
+
*/
|
|
16095
|
+
onRefreshSuccess() {
|
|
16096
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
16097
|
+
this.refreshSubscribers = [];
|
|
16098
|
+
}
|
|
16099
|
+
/**
|
|
16100
|
+
* token 刷新失败,通知所有等待的请求
|
|
16101
|
+
*/
|
|
16102
|
+
onRefreshFailure() {
|
|
16103
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
16104
|
+
this.refreshSubscribers = [];
|
|
16105
|
+
}
|
|
16106
|
+
/**
|
|
16018
16107
|
* 注册请求拦截器
|
|
16019
16108
|
* @param onFulfilled 请求成功拦截器
|
|
16020
16109
|
* @param onRejected 请求失败拦截器
|
|
@@ -16091,16 +16180,18 @@ var HttpService = class HttpService {
|
|
|
16091
16180
|
this.unauthorizedCallbacks.delete(callback);
|
|
16092
16181
|
}
|
|
16093
16182
|
/**
|
|
16094
|
-
* 触发所有 401
|
|
16183
|
+
* 触发所有 401 回调并等待完成
|
|
16184
|
+
* @returns Promise,等待所有回调完成
|
|
16185
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
16095
16186
|
*/
|
|
16096
|
-
triggerUnauthorizedCallbacks() {
|
|
16097
|
-
this.unauthorizedCallbacks
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
|
|
16103
|
-
}
|
|
16187
|
+
async triggerUnauthorizedCallbacks() {
|
|
16188
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
16189
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
16190
|
+
if (failedResults.length > 0) {
|
|
16191
|
+
const errors = failedResults.map((r) => r.reason);
|
|
16192
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
16193
|
+
throw errors[0];
|
|
16194
|
+
}
|
|
16104
16195
|
}
|
|
16105
16196
|
/**
|
|
16106
16197
|
* 更新 authToken
|
|
@@ -16212,6 +16303,7 @@ var AccountService = class {
|
|
|
16212
16303
|
this.initPromise = null;
|
|
16213
16304
|
this.initResolve = null;
|
|
16214
16305
|
this.requestInterceptorId = null;
|
|
16306
|
+
this.crossTabBroadcaster = null;
|
|
16215
16307
|
this.initPromise = new Promise((resolve) => {
|
|
16216
16308
|
this.initResolve = resolve;
|
|
16217
16309
|
});
|
|
@@ -16260,15 +16352,34 @@ var AccountService = class {
|
|
|
16260
16352
|
this.initialized = true;
|
|
16261
16353
|
this.initResolve?.(account);
|
|
16262
16354
|
}
|
|
16263
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
16355
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
16356
|
+
this.notifyListeners();
|
|
16357
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
16358
|
+
}
|
|
16264
16359
|
}
|
|
16265
16360
|
/**
|
|
16266
16361
|
* 清除账号(登出)
|
|
16362
|
+
* 先广播登出消息,再清除本地账号
|
|
16267
16363
|
*/
|
|
16268
16364
|
clearAccount() {
|
|
16365
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
16269
16366
|
this.setAccount(null);
|
|
16270
16367
|
}
|
|
16271
16368
|
/**
|
|
16369
|
+
* 静默清除账号(不广播)
|
|
16370
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
16371
|
+
*/
|
|
16372
|
+
clearAccountSilently() {
|
|
16373
|
+
this.setAccount(null);
|
|
16374
|
+
}
|
|
16375
|
+
/**
|
|
16376
|
+
* 设置跨标签页认证同步广播器
|
|
16377
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
16378
|
+
*/
|
|
16379
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
16380
|
+
this.crossTabBroadcaster = broadcaster;
|
|
16381
|
+
}
|
|
16382
|
+
/**
|
|
16272
16383
|
* 订阅账号变化
|
|
16273
16384
|
* @param callback 变化时的回调函数
|
|
16274
16385
|
* @returns 取消订阅函数
|
|
@@ -16333,6 +16444,11 @@ var AccountService = class {
|
|
|
16333
16444
|
* 导出单例实例
|
|
16334
16445
|
*/
|
|
16335
16446
|
const accountService = new AccountService();
|
|
16447
|
+
/**
|
|
16448
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
16449
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
16450
|
+
*/
|
|
16451
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
16336
16452
|
|
|
16337
16453
|
//#endregion
|
|
16338
16454
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -16435,20 +16551,32 @@ var CosUploadService = class {
|
|
|
16435
16551
|
* 上传单个文件到 COS
|
|
16436
16552
|
*
|
|
16437
16553
|
* @param file - 要上传的文件
|
|
16554
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16438
16555
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
16439
16556
|
*/
|
|
16440
|
-
async uploadFile(file) {
|
|
16557
|
+
async uploadFile(file, abortSignal) {
|
|
16441
16558
|
const filename = file.name;
|
|
16442
16559
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
16443
16560
|
try {
|
|
16561
|
+
if (abortSignal?.aborted) return {
|
|
16562
|
+
success: false,
|
|
16563
|
+
error: "Upload cancelled",
|
|
16564
|
+
aborted: true
|
|
16565
|
+
};
|
|
16444
16566
|
const objectKey = this.generateObjectKey(filename);
|
|
16445
16567
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
16446
16568
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
16447
16569
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
16570
|
+
if (abortSignal?.aborted) return {
|
|
16571
|
+
success: false,
|
|
16572
|
+
error: "Upload cancelled",
|
|
16573
|
+
aborted: true
|
|
16574
|
+
};
|
|
16448
16575
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16449
16576
|
method: "PUT",
|
|
16450
16577
|
body: file,
|
|
16451
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16578
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16579
|
+
signal: abortSignal
|
|
16452
16580
|
});
|
|
16453
16581
|
if (!uploadResponse.ok) {
|
|
16454
16582
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16462,6 +16590,11 @@ var CosUploadService = class {
|
|
|
16462
16590
|
objectKey
|
|
16463
16591
|
};
|
|
16464
16592
|
} catch (error) {
|
|
16593
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16594
|
+
success: false,
|
|
16595
|
+
error: "Upload cancelled",
|
|
16596
|
+
aborted: true
|
|
16597
|
+
};
|
|
16465
16598
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
16466
16599
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
16467
16600
|
return {
|
|
@@ -16476,14 +16609,25 @@ var CosUploadService = class {
|
|
|
16476
16609
|
* 使用并发控制,限制同时上传的文件数量
|
|
16477
16610
|
*
|
|
16478
16611
|
* @param files - 要上传的文件数组
|
|
16612
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16479
16613
|
* @returns 所有文件的上传结果
|
|
16480
16614
|
*/
|
|
16481
|
-
async uploadFiles(files) {
|
|
16615
|
+
async uploadFiles(files, abortSignal) {
|
|
16482
16616
|
if (files.length === 0) return {
|
|
16483
16617
|
success: true,
|
|
16484
16618
|
urls: [],
|
|
16485
16619
|
results: []
|
|
16486
16620
|
};
|
|
16621
|
+
if (abortSignal?.aborted) return {
|
|
16622
|
+
success: false,
|
|
16623
|
+
error: "Upload cancelled",
|
|
16624
|
+
aborted: true,
|
|
16625
|
+
results: files.map(() => ({
|
|
16626
|
+
success: false,
|
|
16627
|
+
error: "Upload cancelled",
|
|
16628
|
+
aborted: true
|
|
16629
|
+
}))
|
|
16630
|
+
};
|
|
16487
16631
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
16488
16632
|
try {
|
|
16489
16633
|
const fileInfos = files.map((file) => ({
|
|
@@ -16495,6 +16639,12 @@ var CosUploadService = class {
|
|
|
16495
16639
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
16496
16640
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
16497
16641
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
16642
|
+
if (abortSignal?.aborted) return {
|
|
16643
|
+
success: false,
|
|
16644
|
+
error: "Upload cancelled",
|
|
16645
|
+
aborted: true,
|
|
16646
|
+
objectKey: fileInfos[index].objectKey
|
|
16647
|
+
};
|
|
16498
16648
|
const presignedItem = presignedResponse.items[index];
|
|
16499
16649
|
if (!presignedItem) return {
|
|
16500
16650
|
success: false,
|
|
@@ -16505,7 +16655,8 @@ var CosUploadService = class {
|
|
|
16505
16655
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16506
16656
|
method: "PUT",
|
|
16507
16657
|
body: file,
|
|
16508
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16658
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16659
|
+
signal: abortSignal
|
|
16509
16660
|
});
|
|
16510
16661
|
if (!uploadResponse.ok) {
|
|
16511
16662
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16522,6 +16673,12 @@ var CosUploadService = class {
|
|
|
16522
16673
|
objectKey: presignedItem.object_key
|
|
16523
16674
|
};
|
|
16524
16675
|
} catch (error) {
|
|
16676
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16677
|
+
success: false,
|
|
16678
|
+
error: "Upload cancelled",
|
|
16679
|
+
aborted: true,
|
|
16680
|
+
objectKey: presignedItem.object_key
|
|
16681
|
+
};
|
|
16525
16682
|
return {
|
|
16526
16683
|
success: false,
|
|
16527
16684
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -16922,15 +17079,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16922
17079
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
16923
17080
|
const apiResponse = await httpService.get(url);
|
|
16924
17081
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
16925
|
-
const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
|
|
16926
|
-
const pagination = apiResponse.data.pagination;
|
|
16927
|
-
console.log("[CloudAgentProvider] API response:", {
|
|
16928
|
-
agentsCount: agents.length,
|
|
16929
|
-
pagination
|
|
16930
|
-
});
|
|
16931
17082
|
return {
|
|
16932
|
-
agents,
|
|
16933
|
-
pagination
|
|
17083
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
17084
|
+
pagination: apiResponse.data.pagination
|
|
16934
17085
|
};
|
|
16935
17086
|
} catch (error) {
|
|
16936
17087
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -16940,13 +17091,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16940
17091
|
/**
|
|
16941
17092
|
* Create a new conversation
|
|
16942
17093
|
* POST {endpoint}/console/as/conversations
|
|
17094
|
+
* @param params - Session params containing cwd and optional configuration
|
|
16943
17095
|
*/
|
|
16944
|
-
async create() {
|
|
17096
|
+
async create(params) {
|
|
16945
17097
|
try {
|
|
16946
|
-
const
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
});
|
|
17098
|
+
const { options = {} } = params;
|
|
17099
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
17100
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
17101
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
17102
|
+
const createPayload = {
|
|
17103
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
17104
|
+
model: options.model || "deepseek-r1",
|
|
17105
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
17106
|
+
};
|
|
17107
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
17108
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
16950
17109
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
16951
17110
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
16952
17111
|
return apiResponse.data.id;
|
|
@@ -17116,6 +17275,35 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17116
17275
|
}
|
|
17117
17276
|
}
|
|
17118
17277
|
/**
|
|
17278
|
+
* Update conversation status by ID
|
|
17279
|
+
* POST {endpoint}/console/as/conversations/{agentId}
|
|
17280
|
+
*
|
|
17281
|
+
* @param agentId - Conversation ID to update
|
|
17282
|
+
* @param status - New status for the conversation
|
|
17283
|
+
* @returns PatchConversationResponse containing the updated conversation ID
|
|
17284
|
+
*
|
|
17285
|
+
* @example
|
|
17286
|
+
* ```typescript
|
|
17287
|
+
* const result = await provider.updateStatus('agent-123', 'completed');
|
|
17288
|
+
* console.log('Updated conversation status:', result.id);
|
|
17289
|
+
* ```
|
|
17290
|
+
*/
|
|
17291
|
+
async updateStatus(agentId, status) {
|
|
17292
|
+
try {
|
|
17293
|
+
const body = { status };
|
|
17294
|
+
const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
|
|
17295
|
+
if (!apiResponse.data) {
|
|
17296
|
+
this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
|
|
17297
|
+
return { id: agentId };
|
|
17298
|
+
}
|
|
17299
|
+
this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
|
|
17300
|
+
return apiResponse.data;
|
|
17301
|
+
} catch (error) {
|
|
17302
|
+
this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
|
|
17303
|
+
throw error;
|
|
17304
|
+
}
|
|
17305
|
+
}
|
|
17306
|
+
/**
|
|
17119
17307
|
* Get available models from product configuration
|
|
17120
17308
|
*
|
|
17121
17309
|
* GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
|
|
@@ -17146,9 +17334,12 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17146
17334
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
17147
17335
|
return [];
|
|
17148
17336
|
}
|
|
17149
|
-
const
|
|
17150
|
-
|
|
17151
|
-
|
|
17337
|
+
const productConfig = apiResponse.data;
|
|
17338
|
+
const allModels = productConfig.models ?? [];
|
|
17339
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
17340
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
17341
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
17342
|
+
return filteredModels.map((model) => ({
|
|
17152
17343
|
id: model.id,
|
|
17153
17344
|
name: model.name ?? model.id,
|
|
17154
17345
|
description: model.description,
|
|
@@ -17252,7 +17443,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17252
17443
|
/**
|
|
17253
17444
|
* Upload files to cloud storage via COS presigned URL
|
|
17254
17445
|
*
|
|
17255
|
-
* @param params - files array (File objects in browser)
|
|
17446
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
17256
17447
|
* @returns Response with corresponding cloud URLs
|
|
17257
17448
|
*/
|
|
17258
17449
|
async uploadFile(params) {
|
|
@@ -17262,12 +17453,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17262
17453
|
success: false,
|
|
17263
17454
|
error: "No valid File objects provided"
|
|
17264
17455
|
};
|
|
17265
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
17456
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
17266
17457
|
return {
|
|
17267
17458
|
success: result.success,
|
|
17268
17459
|
urls: result.urls,
|
|
17269
17460
|
expireSeconds: result.expireSeconds,
|
|
17270
|
-
error: result.error
|
|
17461
|
+
error: result.error,
|
|
17462
|
+
aborted: result.aborted
|
|
17271
17463
|
};
|
|
17272
17464
|
}
|
|
17273
17465
|
/**
|
|
@@ -17309,20 +17501,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17309
17501
|
}
|
|
17310
17502
|
/**
|
|
17311
17503
|
* 获取支持的场景列表
|
|
17312
|
-
* API 端点: GET /
|
|
17504
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
17313
17505
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
17314
17506
|
*
|
|
17507
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
17315
17508
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
17316
17509
|
*/
|
|
17317
|
-
async getSupportScenes() {
|
|
17510
|
+
async getSupportScenes(locale) {
|
|
17318
17511
|
try {
|
|
17319
|
-
const
|
|
17512
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
17513
|
+
const apiResponse = await httpService.get(url);
|
|
17320
17514
|
if (!apiResponse.data) {
|
|
17321
17515
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
17322
17516
|
return [];
|
|
17323
17517
|
}
|
|
17324
17518
|
const scenes = apiResponse.data.scenes || [];
|
|
17325
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
17519
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
17326
17520
|
return scenes;
|
|
17327
17521
|
} catch (error) {
|
|
17328
17522
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -17338,7 +17532,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17338
17532
|
type: "cloud",
|
|
17339
17533
|
status,
|
|
17340
17534
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
17341
|
-
capabilities: this.options.clientCapabilities
|
|
17535
|
+
capabilities: this.options.clientCapabilities,
|
|
17536
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
17342
17537
|
};
|
|
17343
17538
|
}
|
|
17344
17539
|
/**
|
|
@@ -17354,6 +17549,37 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17354
17549
|
const queryString = searchParams.toString();
|
|
17355
17550
|
return queryString ? `${path}?${queryString}` : path;
|
|
17356
17551
|
}
|
|
17552
|
+
/**
|
|
17553
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
17554
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
17555
|
+
* 注入用户信息和浏览器环境等公共字段
|
|
17556
|
+
*/
|
|
17557
|
+
async reportTelemetry(eventName, payload) {
|
|
17558
|
+
try {
|
|
17559
|
+
const account = accountService.getAccount();
|
|
17560
|
+
const commonFields = {};
|
|
17561
|
+
if (account) {
|
|
17562
|
+
commonFields.userId = account.uid;
|
|
17563
|
+
commonFields.userNickname = account.nickname;
|
|
17564
|
+
if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
|
|
17565
|
+
if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
|
|
17566
|
+
}
|
|
17567
|
+
if (typeof navigator !== "undefined") {
|
|
17568
|
+
commonFields.userAgent = navigator.userAgent;
|
|
17569
|
+
commonFields.os = navigator.platform;
|
|
17570
|
+
}
|
|
17571
|
+
const events = [{
|
|
17572
|
+
eventCode: eventName,
|
|
17573
|
+
timestamp: Date.now(),
|
|
17574
|
+
reportDelay: 0,
|
|
17575
|
+
...commonFields,
|
|
17576
|
+
...payload
|
|
17577
|
+
}];
|
|
17578
|
+
await httpService.post("/v2/report", events);
|
|
17579
|
+
} catch (error) {
|
|
17580
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
17581
|
+
}
|
|
17582
|
+
}
|
|
17357
17583
|
};
|
|
17358
17584
|
|
|
17359
17585
|
//#endregion
|
|
@@ -17402,6 +17628,7 @@ var ActiveSessionImpl = class {
|
|
|
17402
17628
|
this._availableCommands = [];
|
|
17403
17629
|
this.listeners = /* @__PURE__ */ new Map();
|
|
17404
17630
|
this.onceListeners = /* @__PURE__ */ new Map();
|
|
17631
|
+
this.connectionListeners = [];
|
|
17405
17632
|
this._id = sessionId;
|
|
17406
17633
|
this._agentId = agentId;
|
|
17407
17634
|
this.connection = connection;
|
|
@@ -17427,6 +17654,18 @@ var ActiveSessionImpl = class {
|
|
|
17427
17654
|
return this._agentId;
|
|
17428
17655
|
}
|
|
17429
17656
|
/**
|
|
17657
|
+
* Actual workspace path (set from newSession response _meta)
|
|
17658
|
+
*/
|
|
17659
|
+
get cwd() {
|
|
17660
|
+
return this._cwd;
|
|
17661
|
+
}
|
|
17662
|
+
/**
|
|
17663
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
17664
|
+
*/
|
|
17665
|
+
setCwd(cwd) {
|
|
17666
|
+
this._cwd = cwd;
|
|
17667
|
+
}
|
|
17668
|
+
/**
|
|
17430
17669
|
* Agent state (live connection state)
|
|
17431
17670
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
17432
17671
|
*/
|
|
@@ -17643,8 +17882,8 @@ var ActiveSessionImpl = class {
|
|
|
17643
17882
|
* await session.setMode('architect');
|
|
17644
17883
|
* ```
|
|
17645
17884
|
*/
|
|
17646
|
-
async setMode(modeId) {
|
|
17647
|
-
if (this._availableModes) {
|
|
17885
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
17886
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
17648
17887
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
17649
17888
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
17650
17889
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -17744,6 +17983,7 @@ var ActiveSessionImpl = class {
|
|
|
17744
17983
|
* Disconnect from the session/agent
|
|
17745
17984
|
*/
|
|
17746
17985
|
disconnect() {
|
|
17986
|
+
this.removeConnectionListeners();
|
|
17747
17987
|
this.connection.disconnect();
|
|
17748
17988
|
this.removeAllListeners();
|
|
17749
17989
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
@@ -17767,60 +18007,80 @@ var ActiveSessionImpl = class {
|
|
|
17767
18007
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
17768
18008
|
return this.connection;
|
|
17769
18009
|
}
|
|
18010
|
+
/**
|
|
18011
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
18012
|
+
*/
|
|
18013
|
+
addConnectionListener(connection, event, listener) {
|
|
18014
|
+
connection.on(event, listener);
|
|
18015
|
+
this.connectionListeners.push({
|
|
18016
|
+
event,
|
|
18017
|
+
listener
|
|
18018
|
+
});
|
|
18019
|
+
}
|
|
18020
|
+
/**
|
|
18021
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
18022
|
+
*/
|
|
18023
|
+
removeConnectionListeners() {
|
|
18024
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
18025
|
+
this.connectionListeners = [];
|
|
18026
|
+
}
|
|
17770
18027
|
setupConnectionEvents(connection) {
|
|
17771
|
-
|
|
18028
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
17772
18029
|
this.emit("connected", void 0);
|
|
17773
18030
|
});
|
|
17774
|
-
|
|
18031
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
17775
18032
|
this.emit("disconnected", void 0);
|
|
17776
18033
|
});
|
|
17777
|
-
|
|
18034
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
17778
18035
|
this.emit("error", error);
|
|
17779
18036
|
});
|
|
17780
|
-
|
|
18037
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
17781
18038
|
this.emit("sessionUpdate", update);
|
|
17782
18039
|
});
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
artifactUri: artifact.uri,
|
|
17786
|
-
artifactType: artifact.type
|
|
17787
|
-
});
|
|
18040
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
18041
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17788
18042
|
this.emit("artifactCreated", artifact);
|
|
17789
18043
|
});
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
artifactUri: artifact.uri,
|
|
17793
|
-
artifactType: artifact.type
|
|
17794
|
-
});
|
|
18044
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
18045
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17795
18046
|
this.emit("artifactUpdated", artifact);
|
|
17796
18047
|
});
|
|
17797
|
-
|
|
17798
|
-
|
|
18048
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
18049
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17799
18050
|
this.emit("artifactDeleted", artifact);
|
|
17800
18051
|
});
|
|
17801
|
-
|
|
18052
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
17802
18053
|
this.emit("permissionRequest", request);
|
|
17803
18054
|
});
|
|
17804
|
-
|
|
18055
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
17805
18056
|
this.emit("questionRequest", request);
|
|
17806
18057
|
});
|
|
17807
|
-
|
|
18058
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
17808
18059
|
this.prompts.cancel();
|
|
17809
18060
|
});
|
|
17810
|
-
|
|
18061
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
17811
18062
|
this.emit("usageUpdate", usage);
|
|
17812
18063
|
});
|
|
17813
|
-
|
|
18064
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
18065
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18066
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17814
18067
|
this.emit("checkpointCreated", checkpoint);
|
|
17815
18068
|
});
|
|
17816
|
-
|
|
18069
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
18070
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18071
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17817
18072
|
this.emit("checkpointUpdated", checkpoint);
|
|
17818
18073
|
});
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
18074
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
18075
|
+
const originSessionId = command.__sessionId;
|
|
18076
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
18077
|
+
console.log("[Session] Command not forwarded:", {
|
|
18078
|
+
command,
|
|
18079
|
+
originSessionId,
|
|
18080
|
+
sessionId: this._id
|
|
18081
|
+
});
|
|
18082
|
+
return;
|
|
18083
|
+
}
|
|
17824
18084
|
this.emit("command", command);
|
|
17825
18085
|
});
|
|
17826
18086
|
}
|
|
@@ -17830,19 +18090,38 @@ var ActiveSessionImpl = class {
|
|
|
17830
18090
|
_meta: response._meta ?? void 0
|
|
17831
18091
|
};
|
|
17832
18092
|
}
|
|
18093
|
+
/**
|
|
18094
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
18095
|
+
* - media 类型:按 cwd 路径隔离(同 cwd 下不同 session 可共享 media)
|
|
18096
|
+
* - 其余类型:按 __sessionId 严格隔离
|
|
18097
|
+
*/
|
|
18098
|
+
shouldForwardArtifact(artifact) {
|
|
18099
|
+
const originSessionId = artifact.__sessionId;
|
|
18100
|
+
console.log("[Session] shouldForwardArtifact:", {
|
|
18101
|
+
artifact,
|
|
18102
|
+
originSessionId,
|
|
18103
|
+
sessionId: this._id,
|
|
18104
|
+
cwd: this.connection?.cwd
|
|
18105
|
+
});
|
|
18106
|
+
if (artifact.type === "media") {
|
|
18107
|
+
const cwd = this.connection?.cwd;
|
|
18108
|
+
const uri = artifact?.uri;
|
|
18109
|
+
if (cwd && uri) {
|
|
18110
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
18111
|
+
const uriPath = toPosix(uri.replace(/^(?:file|agent):\/\//, ""));
|
|
18112
|
+
const posixCwd = toPosix(cwd);
|
|
18113
|
+
const normalizedCwd = posixCwd.endsWith("/") ? posixCwd : posixCwd + "/";
|
|
18114
|
+
return uriPath.startsWith(normalizedCwd);
|
|
18115
|
+
}
|
|
18116
|
+
}
|
|
18117
|
+
if (originSessionId && originSessionId !== this._id) return false;
|
|
18118
|
+
return true;
|
|
18119
|
+
}
|
|
17833
18120
|
};
|
|
17834
18121
|
|
|
17835
18122
|
//#endregion
|
|
17836
18123
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
17837
18124
|
/**
|
|
17838
|
-
* SessionManager - Manages session lifecycle and connections
|
|
17839
|
-
*
|
|
17840
|
-
* Provides the core implementation for session-centric API operations:
|
|
17841
|
-
* - list() - Lists sessions (mapped from agents)
|
|
17842
|
-
* - createSession() - Creates new session (auto-creates agent)
|
|
17843
|
-
* - loadSession() - Loads existing session (finds agent by sessionId)
|
|
17844
|
-
*/
|
|
17845
|
-
/**
|
|
17846
18125
|
* SessionManager - Session lifecycle management
|
|
17847
18126
|
*
|
|
17848
18127
|
* This class manages the relationship between sessions and agents.
|
|
@@ -17892,7 +18171,8 @@ var SessionManager = class {
|
|
|
17892
18171
|
createdAt: agent.createdAt,
|
|
17893
18172
|
lastActivityAt: agent.updatedAt,
|
|
17894
18173
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
17895
|
-
isPlayground: agent.isPlayground
|
|
18174
|
+
isPlayground: agent.isPlayground,
|
|
18175
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
17896
18176
|
}));
|
|
17897
18177
|
console.log("[SessionManager] Returning sessions:", {
|
|
17898
18178
|
count: sessions.length,
|
|
@@ -17919,13 +18199,26 @@ var SessionManager = class {
|
|
|
17919
18199
|
if (this.provider.create) {
|
|
17920
18200
|
agentId = await this.provider.create(params);
|
|
17921
18201
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
18202
|
+
if (params.options?.onSessionPrepared) {
|
|
18203
|
+
const initialPrompt = params.options?.prompt;
|
|
18204
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
18205
|
+
params.options.onSessionPrepared({
|
|
18206
|
+
id: agentId,
|
|
18207
|
+
agentId,
|
|
18208
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
18209
|
+
status: "connecting",
|
|
18210
|
+
cwd: params.cwd || "",
|
|
18211
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
18212
|
+
});
|
|
18213
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
18214
|
+
}
|
|
17922
18215
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
17923
18216
|
const connection = await this.provider.connect(agentId);
|
|
17924
18217
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
17925
18218
|
const response = await connection.createSession({
|
|
17926
|
-
_meta: params._meta,
|
|
18219
|
+
_meta: params.options?._meta,
|
|
17927
18220
|
cwd: params.cwd,
|
|
17928
|
-
mcpServers: params.mcpServers
|
|
18221
|
+
mcpServers: params.options?.mcpServers
|
|
17929
18222
|
});
|
|
17930
18223
|
if (this.provider.registerSession) {
|
|
17931
18224
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -17938,14 +18231,10 @@ var SessionManager = class {
|
|
|
17938
18231
|
connectionInfo
|
|
17939
18232
|
});
|
|
17940
18233
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
17941
|
-
|
|
17942
|
-
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
description: m.description ?? void 0
|
|
17946
|
-
}));
|
|
17947
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
17948
|
-
}
|
|
18234
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18235
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
18236
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
18237
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
17949
18238
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
17950
18239
|
return session;
|
|
17951
18240
|
}
|
|
@@ -17981,17 +18270,31 @@ var SessionManager = class {
|
|
|
17981
18270
|
mcpServers: params.mcpServers
|
|
17982
18271
|
});
|
|
17983
18272
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
id: m.modelId,
|
|
17987
|
-
name: m.name,
|
|
17988
|
-
description: m.description ?? void 0
|
|
17989
|
-
}));
|
|
17990
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
17991
|
-
}
|
|
18273
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18274
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
17992
18275
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
17993
18276
|
return session;
|
|
17994
18277
|
}
|
|
18278
|
+
/**
|
|
18279
|
+
* 从 ACP response 中提取可用模型列表
|
|
18280
|
+
*
|
|
18281
|
+
* 优先级:
|
|
18282
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
18283
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
18284
|
+
* 3. undefined - 都没有时返回 undefined
|
|
18285
|
+
*
|
|
18286
|
+
* @param response - ACP 响应对象
|
|
18287
|
+
* @returns ModelInfo[] | undefined
|
|
18288
|
+
*/
|
|
18289
|
+
extractAvailableModels(response) {
|
|
18290
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
18291
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
18292
|
+
const availableModels = response.models?.availableModels;
|
|
18293
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
18294
|
+
...model,
|
|
18295
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
18296
|
+
}));
|
|
18297
|
+
}
|
|
17995
18298
|
};
|
|
17996
18299
|
|
|
17997
18300
|
//#endregion
|
|
@@ -18098,6 +18401,26 @@ var AgentClient = class {
|
|
|
18098
18401
|
throw error;
|
|
18099
18402
|
}
|
|
18100
18403
|
},
|
|
18404
|
+
updateStatus: async (sessionId, status) => {
|
|
18405
|
+
this.logger?.debug("AgentClient.sessions.updateStatus called", {
|
|
18406
|
+
sessionId,
|
|
18407
|
+
status
|
|
18408
|
+
});
|
|
18409
|
+
try {
|
|
18410
|
+
if (this.provider.updateStatus) {
|
|
18411
|
+
const result = await this.provider.updateStatus(sessionId, status);
|
|
18412
|
+
this.logger?.info("Session status updated successfully", {
|
|
18413
|
+
sessionId,
|
|
18414
|
+
status
|
|
18415
|
+
});
|
|
18416
|
+
return result;
|
|
18417
|
+
}
|
|
18418
|
+
throw new Error("Provider does not support updateStatus method");
|
|
18419
|
+
} catch (error) {
|
|
18420
|
+
this.logger?.error("Failed to update session status", error);
|
|
18421
|
+
throw error;
|
|
18422
|
+
}
|
|
18423
|
+
},
|
|
18101
18424
|
move: async (sessionId) => {
|
|
18102
18425
|
this.logger?.debug("AgentClient.sessions.move called", { sessionId });
|
|
18103
18426
|
try {
|
|
@@ -18264,6 +18587,72 @@ var AgentClient = class {
|
|
|
18264
18587
|
};
|
|
18265
18588
|
}
|
|
18266
18589
|
},
|
|
18590
|
+
getSubagentList: async (params) => {
|
|
18591
|
+
try {
|
|
18592
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
18593
|
+
const result = await this.provider.getSubagentList(params);
|
|
18594
|
+
this.logger?.info("Subagent list retrieved", {
|
|
18595
|
+
resultCount: result.results.length,
|
|
18596
|
+
hasError: !!result.error
|
|
18597
|
+
});
|
|
18598
|
+
return result;
|
|
18599
|
+
}
|
|
18600
|
+
return {
|
|
18601
|
+
results: [],
|
|
18602
|
+
error: "Provider does not support getSubagentList"
|
|
18603
|
+
};
|
|
18604
|
+
} catch (error) {
|
|
18605
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
18606
|
+
return {
|
|
18607
|
+
results: [],
|
|
18608
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18609
|
+
};
|
|
18610
|
+
}
|
|
18611
|
+
},
|
|
18612
|
+
getSkillList: async (params) => {
|
|
18613
|
+
try {
|
|
18614
|
+
if (this.provider && this.provider.getSkillList) {
|
|
18615
|
+
const result = await this.provider.getSkillList(params);
|
|
18616
|
+
this.logger?.info("Skill list retrieved", {
|
|
18617
|
+
resultCount: result.results.length,
|
|
18618
|
+
hasError: !!result.error
|
|
18619
|
+
});
|
|
18620
|
+
return result;
|
|
18621
|
+
}
|
|
18622
|
+
return {
|
|
18623
|
+
results: [],
|
|
18624
|
+
error: "Provider does not support getSkillList"
|
|
18625
|
+
};
|
|
18626
|
+
} catch (error) {
|
|
18627
|
+
this.logger?.error("Failed to get skill list", error);
|
|
18628
|
+
return {
|
|
18629
|
+
results: [],
|
|
18630
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18631
|
+
};
|
|
18632
|
+
}
|
|
18633
|
+
},
|
|
18634
|
+
importSkill: async (params) => {
|
|
18635
|
+
try {
|
|
18636
|
+
if (this.provider && this.provider.importSkill) {
|
|
18637
|
+
const result = await this.provider.importSkill(params);
|
|
18638
|
+
this.logger?.info("Import skill completed", {
|
|
18639
|
+
success: result.success,
|
|
18640
|
+
hasError: !!result.error
|
|
18641
|
+
});
|
|
18642
|
+
return result;
|
|
18643
|
+
}
|
|
18644
|
+
return {
|
|
18645
|
+
success: false,
|
|
18646
|
+
error: "Provider does not support importSkill"
|
|
18647
|
+
};
|
|
18648
|
+
} catch (error) {
|
|
18649
|
+
this.logger?.error("Failed to import skill", error);
|
|
18650
|
+
return {
|
|
18651
|
+
success: false,
|
|
18652
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18653
|
+
};
|
|
18654
|
+
}
|
|
18655
|
+
},
|
|
18267
18656
|
batchTogglePlugins: async (request) => {
|
|
18268
18657
|
try {
|
|
18269
18658
|
if (this.provider && this.provider.batchTogglePlugins) {
|
|
@@ -18308,10 +18697,10 @@ var AgentClient = class {
|
|
|
18308
18697
|
return [];
|
|
18309
18698
|
}
|
|
18310
18699
|
},
|
|
18311
|
-
installPlugins: async (pluginNames, marketplaceName, installScope) => {
|
|
18700
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
18312
18701
|
try {
|
|
18313
18702
|
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
18314
|
-
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
|
|
18703
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
18315
18704
|
this.logger?.info("Install plugins", {
|
|
18316
18705
|
pluginNames,
|
|
18317
18706
|
marketplaceName,
|
|
@@ -18332,13 +18721,9 @@ var AgentClient = class {
|
|
|
18332
18721
|
};
|
|
18333
18722
|
}
|
|
18334
18723
|
},
|
|
18335
|
-
getSupportScenes: async () => {
|
|
18724
|
+
getSupportScenes: async (locale) => {
|
|
18336
18725
|
try {
|
|
18337
|
-
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function")
|
|
18338
|
-
const result = await this.provider.getSupportScenes();
|
|
18339
|
-
this.logger?.info("Got support scenes", { count: result?.length ?? 0 });
|
|
18340
|
-
return result;
|
|
18341
|
-
}
|
|
18726
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
18342
18727
|
this.logger?.warn("Provider does not support getSupportScenes");
|
|
18343
18728
|
return [];
|
|
18344
18729
|
} catch (error) {
|
|
@@ -18346,6 +18731,91 @@ var AgentClient = class {
|
|
|
18346
18731
|
return [];
|
|
18347
18732
|
}
|
|
18348
18733
|
},
|
|
18734
|
+
getProductScenes: async (locale) => {
|
|
18735
|
+
try {
|
|
18736
|
+
if (this.provider?.getProductScenes) {
|
|
18737
|
+
const result = await this.provider.getProductScenes(locale);
|
|
18738
|
+
this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
|
|
18739
|
+
return result;
|
|
18740
|
+
}
|
|
18741
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
18742
|
+
return [];
|
|
18743
|
+
} catch (error) {
|
|
18744
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
18745
|
+
return [];
|
|
18746
|
+
}
|
|
18747
|
+
},
|
|
18748
|
+
getAvailableCommands: async (params) => {
|
|
18749
|
+
try {
|
|
18750
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
18751
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
18752
|
+
this.logger?.info("Got available commands from provider", {
|
|
18753
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
18754
|
+
count: result?.length ?? 0
|
|
18755
|
+
});
|
|
18756
|
+
return result;
|
|
18757
|
+
}
|
|
18758
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
18759
|
+
return [];
|
|
18760
|
+
} catch (error) {
|
|
18761
|
+
this.logger?.error("Failed to get available commands", error);
|
|
18762
|
+
return [];
|
|
18763
|
+
}
|
|
18764
|
+
},
|
|
18765
|
+
reportTelemetry: async (eventName, payload) => {
|
|
18766
|
+
try {
|
|
18767
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
18768
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
18769
|
+
} catch (error) {
|
|
18770
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
18771
|
+
}
|
|
18772
|
+
},
|
|
18773
|
+
respondToSampling: async (sessionId, response) => {
|
|
18774
|
+
try {
|
|
18775
|
+
if (this.provider?.respondToSampling) {
|
|
18776
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
18777
|
+
this.logger?.info("Responded to sampling request", {
|
|
18778
|
+
sessionId,
|
|
18779
|
+
requestId: response.id,
|
|
18780
|
+
approved: response.approved
|
|
18781
|
+
});
|
|
18782
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
18783
|
+
} catch (error) {
|
|
18784
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
18785
|
+
throw error;
|
|
18786
|
+
}
|
|
18787
|
+
},
|
|
18788
|
+
respondToRoots: async (sessionId, response) => {
|
|
18789
|
+
try {
|
|
18790
|
+
if (this.provider?.respondToRoots) {
|
|
18791
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
18792
|
+
this.logger?.info("Responded to roots request", {
|
|
18793
|
+
sessionId,
|
|
18794
|
+
requestId: response.id,
|
|
18795
|
+
approved: response.approved
|
|
18796
|
+
});
|
|
18797
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
18798
|
+
} catch (error) {
|
|
18799
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
18800
|
+
throw error;
|
|
18801
|
+
}
|
|
18802
|
+
},
|
|
18803
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
18804
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
18805
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
18806
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
18807
|
+
}
|
|
18808
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
18809
|
+
return () => {};
|
|
18810
|
+
},
|
|
18811
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
18812
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
18813
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
18814
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
18815
|
+
}
|
|
18816
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
18817
|
+
return () => {};
|
|
18818
|
+
},
|
|
18349
18819
|
models: this.createModelsResource()
|
|
18350
18820
|
};
|
|
18351
18821
|
}
|
|
@@ -18412,6 +18882,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18412
18882
|
return AccountStatus;
|
|
18413
18883
|
}({});
|
|
18414
18884
|
|
|
18885
|
+
//#endregion
|
|
18886
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
18887
|
+
/**
|
|
18888
|
+
* OAuth Repository Service
|
|
18889
|
+
*
|
|
18890
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
18891
|
+
*/
|
|
18892
|
+
/**
|
|
18893
|
+
* OAuth Repository Service
|
|
18894
|
+
*
|
|
18895
|
+
* 提供仓库和分支的查询操作
|
|
18896
|
+
*/
|
|
18897
|
+
var OAuthRepositoryService = class {
|
|
18898
|
+
/**
|
|
18899
|
+
* 获取仓库分支列表
|
|
18900
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
18901
|
+
*
|
|
18902
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18903
|
+
* @param params 平台特定的查询参数
|
|
18904
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18905
|
+
* @param perPage 每页数量,最大100
|
|
18906
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
18907
|
+
*
|
|
18908
|
+
* @example
|
|
18909
|
+
* ```typescript
|
|
18910
|
+
* // GitHub
|
|
18911
|
+
* const branches = await service.getBranches('github', {
|
|
18912
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
18913
|
+
* repo: 'CodeBuddyIDE'
|
|
18914
|
+
* });
|
|
18915
|
+
*
|
|
18916
|
+
* // Gongfeng
|
|
18917
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
18918
|
+
* project_id: '1611499'
|
|
18919
|
+
* });
|
|
18920
|
+
*
|
|
18921
|
+
* // CNB
|
|
18922
|
+
* const branches = await service.getBranches('cnb', {
|
|
18923
|
+
* repo: 'genie/genie-ide'
|
|
18924
|
+
* });
|
|
18925
|
+
* ```
|
|
18926
|
+
*/
|
|
18927
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
18928
|
+
try {
|
|
18929
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
18930
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18931
|
+
const apiResponse = await httpService.get(url);
|
|
18932
|
+
if (!apiResponse.data) {
|
|
18933
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
18934
|
+
return [];
|
|
18935
|
+
}
|
|
18936
|
+
const branches = apiResponse.data.branches || [];
|
|
18937
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
18938
|
+
return branches;
|
|
18939
|
+
} catch (error) {
|
|
18940
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
18941
|
+
throw error;
|
|
18942
|
+
}
|
|
18943
|
+
}
|
|
18944
|
+
/**
|
|
18945
|
+
* 获取仓库列表
|
|
18946
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
18947
|
+
*
|
|
18948
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
18949
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
18950
|
+
*
|
|
18951
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18952
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18953
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
18954
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
18955
|
+
* @param perPage 每页数量,最大100
|
|
18956
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
18957
|
+
*
|
|
18958
|
+
* @example
|
|
18959
|
+
* ```typescript
|
|
18960
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
18961
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
18962
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
18963
|
+
*
|
|
18964
|
+
* // Gongfeng
|
|
18965
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
18966
|
+
* // response.gongfeng_repos 是数组
|
|
18967
|
+
*
|
|
18968
|
+
* // CNB
|
|
18969
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
18970
|
+
* // response.cnb_repos 是数组
|
|
18971
|
+
* ```
|
|
18972
|
+
*/
|
|
18973
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
18974
|
+
try {
|
|
18975
|
+
const queryParams = new URLSearchParams();
|
|
18976
|
+
queryParams.append("page", String(page));
|
|
18977
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
18978
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
18979
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18980
|
+
const apiResponse = await httpService.get(url);
|
|
18981
|
+
if (!apiResponse.data) {
|
|
18982
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
18983
|
+
return {};
|
|
18984
|
+
}
|
|
18985
|
+
const response = apiResponse.data;
|
|
18986
|
+
this.logRepositoryCounts(response);
|
|
18987
|
+
return response;
|
|
18988
|
+
} catch (error) {
|
|
18989
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
18990
|
+
throw error;
|
|
18991
|
+
}
|
|
18992
|
+
}
|
|
18993
|
+
/**
|
|
18994
|
+
* 构建分支查询参数
|
|
18995
|
+
*/
|
|
18996
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
18997
|
+
const queryParams = new URLSearchParams();
|
|
18998
|
+
queryParams.append("page", String(page));
|
|
18999
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
19000
|
+
if (connector === "github") {
|
|
19001
|
+
const githubParams = params;
|
|
19002
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
19003
|
+
queryParams.append("owner", githubParams.owner);
|
|
19004
|
+
queryParams.append("repo", githubParams.repo);
|
|
19005
|
+
} else if (connector === "gongfeng") {
|
|
19006
|
+
const gongfengParams = params;
|
|
19007
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
19008
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
19009
|
+
} else if (connector === "cnb") {
|
|
19010
|
+
const cnbParams = params;
|
|
19011
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
19012
|
+
queryParams.append("repo", cnbParams.repo);
|
|
19013
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
19014
|
+
return queryParams;
|
|
19015
|
+
}
|
|
19016
|
+
/**
|
|
19017
|
+
* 记录仓库数量日志
|
|
19018
|
+
*/
|
|
19019
|
+
logRepositoryCounts(response) {
|
|
19020
|
+
if (response.github_repos) {
|
|
19021
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
19022
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
19023
|
+
}
|
|
19024
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
19025
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
19026
|
+
}
|
|
19027
|
+
};
|
|
19028
|
+
/**
|
|
19029
|
+
* OAuth Repository Service 单例实例
|
|
19030
|
+
*/
|
|
19031
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
19032
|
+
|
|
18415
19033
|
//#endregion
|
|
18416
19034
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
18417
19035
|
/**
|
|
@@ -18420,28 +19038,24 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18420
19038
|
* 封装与后端 API 的 HTTP 通信
|
|
18421
19039
|
*/
|
|
18422
19040
|
/**
|
|
18423
|
-
*
|
|
18424
|
-
*
|
|
19041
|
+
* 判断当前账号是否是 SSO 账号
|
|
19042
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
18425
19043
|
*/
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
19044
|
+
const safeParseJSON = (jsonString) => {
|
|
19045
|
+
try {
|
|
19046
|
+
return JSON.parse(jsonString);
|
|
19047
|
+
} catch (error) {
|
|
19048
|
+
return {};
|
|
19049
|
+
}
|
|
18429
19050
|
};
|
|
18430
19051
|
/**
|
|
18431
|
-
*
|
|
18432
|
-
* - SSO
|
|
18433
|
-
* - 非 SSO
|
|
19052
|
+
* 根据路径获取完整 URL
|
|
19053
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
19054
|
+
* - 非 SSO 账号直接使用当前域名
|
|
19055
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
19056
|
+
* @returns 完整的 URL 地址
|
|
18434
19057
|
*/
|
|
18435
|
-
const
|
|
18436
|
-
const { hostname, protocol } = window.location;
|
|
18437
|
-
if (isSSODomain()) {
|
|
18438
|
-
const isCodebuddy = hostname.includes("codebuddy.cn");
|
|
18439
|
-
const isStaging = hostname.includes("staging");
|
|
18440
|
-
if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
|
|
18441
|
-
else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
|
|
18442
|
-
}
|
|
18443
|
-
return `${window.location.origin}/login`;
|
|
18444
|
-
};
|
|
19058
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
18445
19059
|
/** 获取当前域名的账号选择页面 URL */
|
|
18446
19060
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
18447
19061
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -18478,12 +19092,30 @@ var BackendProvider = class {
|
|
|
18478
19092
|
constructor(config) {
|
|
18479
19093
|
httpService.setBaseURL(config.baseUrl);
|
|
18480
19094
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
18481
|
-
httpService.onUnauthorized(() =>
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
19095
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
19096
|
+
}
|
|
19097
|
+
/**
|
|
19098
|
+
* 处理 401 未授权错误
|
|
19099
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
19100
|
+
*
|
|
19101
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
19102
|
+
*/
|
|
19103
|
+
async handleUnauthorized() {
|
|
19104
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
19105
|
+
try {
|
|
19106
|
+
if (await this.refreshToken()) {
|
|
19107
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
19108
|
+
return;
|
|
19109
|
+
}
|
|
19110
|
+
throw new Error("Token refresh returned null");
|
|
19111
|
+
} catch (error) {
|
|
19112
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
19113
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
19114
|
+
this.logout().catch((logoutError) => {
|
|
19115
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
18485
19116
|
});
|
|
18486
|
-
|
|
19117
|
+
throw error;
|
|
19118
|
+
}
|
|
18487
19119
|
}
|
|
18488
19120
|
/**
|
|
18489
19121
|
* 获取当前账号信息
|
|
@@ -18528,7 +19160,7 @@ var BackendProvider = class {
|
|
|
18528
19160
|
return account;
|
|
18529
19161
|
}
|
|
18530
19162
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18531
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
19163
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18532
19164
|
accountService.setAccount(null);
|
|
18533
19165
|
return null;
|
|
18534
19166
|
} catch (error) {
|
|
@@ -18551,7 +19183,8 @@ var BackendProvider = class {
|
|
|
18551
19183
|
activeStatus: connector.active_status,
|
|
18552
19184
|
displayName: connector.display_name,
|
|
18553
19185
|
oauthClientId: connector.oauth_client_id,
|
|
18554
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19186
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19187
|
+
oauthAppName: connector.oauth_app_name
|
|
18555
19188
|
})) };
|
|
18556
19189
|
}
|
|
18557
19190
|
throw result;
|
|
@@ -18633,7 +19266,8 @@ var BackendProvider = class {
|
|
|
18633
19266
|
connectStatus: connector.connect_status,
|
|
18634
19267
|
displayName: connector.display_name,
|
|
18635
19268
|
oauthClientId: connector.oauth_client_id,
|
|
18636
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19269
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19270
|
+
oauthAppName: connector.oauth_app_name
|
|
18637
19271
|
})) };
|
|
18638
19272
|
}
|
|
18639
19273
|
throw result;
|
|
@@ -18724,6 +19358,9 @@ var BackendProvider = class {
|
|
|
18724
19358
|
PackageCode: void 0,
|
|
18725
19359
|
name: ""
|
|
18726
19360
|
};
|
|
19361
|
+
const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
|
|
19362
|
+
console.log("[PRODUCT_FEATURES]", productFeatures);
|
|
19363
|
+
if (!productFeatures.billing) return defaultPlan;
|
|
18727
19364
|
try {
|
|
18728
19365
|
const now = /* @__PURE__ */ new Date();
|
|
18729
19366
|
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
@@ -18745,7 +19382,7 @@ var BackendProvider = class {
|
|
|
18745
19382
|
if (!time) return 0;
|
|
18746
19383
|
return new Date(time).getTime();
|
|
18747
19384
|
};
|
|
18748
|
-
const dailyCredits = [CommodityCode.free
|
|
19385
|
+
const dailyCredits = [CommodityCode.free];
|
|
18749
19386
|
const planResources = resources.map((r) => {
|
|
18750
19387
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
18751
19388
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
@@ -18766,10 +19403,11 @@ var BackendProvider = class {
|
|
|
18766
19403
|
CommodityCode.proMon,
|
|
18767
19404
|
CommodityCode.proMonPlus,
|
|
18768
19405
|
CommodityCode.proYear,
|
|
19406
|
+
CommodityCode.freeMon,
|
|
18769
19407
|
CommodityCode.extra
|
|
18770
19408
|
].includes(code)) return 1;
|
|
18771
19409
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
18772
|
-
if ([CommodityCode.free
|
|
19410
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
18773
19411
|
return 4;
|
|
18774
19412
|
};
|
|
18775
19413
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -18890,38 +19528,69 @@ var BackendProvider = class {
|
|
|
18890
19528
|
*/
|
|
18891
19529
|
async login() {
|
|
18892
19530
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18893
|
-
window.location.href = `${
|
|
19531
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18894
19532
|
}
|
|
18895
19533
|
/**
|
|
18896
19534
|
* 登出账号
|
|
18897
|
-
*
|
|
19535
|
+
*
|
|
19536
|
+
* 策略:
|
|
19537
|
+
* - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
|
|
19538
|
+
* - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
|
|
18898
19539
|
*/
|
|
18899
19540
|
async logout() {
|
|
18900
|
-
const
|
|
19541
|
+
const account = accountService.getAccount();
|
|
19542
|
+
if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
|
|
19543
|
+
else await this.logoutViaHttp();
|
|
19544
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
19545
|
+
accountService.clearAccount();
|
|
19546
|
+
}
|
|
19547
|
+
/**
|
|
19548
|
+
* IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
|
|
19549
|
+
* 轮询 iframe URL 变化检测完成,兜底超时 5 秒
|
|
19550
|
+
*/
|
|
19551
|
+
async logoutViaIframe() {
|
|
19552
|
+
const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
|
|
18901
19553
|
try {
|
|
18902
19554
|
await new Promise((resolve) => {
|
|
18903
19555
|
const iframe = document.createElement("iframe");
|
|
18904
19556
|
iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
|
|
18905
|
-
iframe.src =
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
19557
|
+
iframe.src = logoutUrl;
|
|
19558
|
+
let pollTimer;
|
|
19559
|
+
let settled = false;
|
|
19560
|
+
const done = () => {
|
|
19561
|
+
if (settled) return;
|
|
19562
|
+
settled = true;
|
|
19563
|
+
clearInterval(pollTimer);
|
|
18911
19564
|
clearTimeout(timeout);
|
|
18912
19565
|
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
18913
|
-
};
|
|
18914
|
-
iframe.onerror = () => {
|
|
18915
|
-
cleanup();
|
|
18916
19566
|
resolve();
|
|
18917
19567
|
};
|
|
19568
|
+
let wasRedirecting = false;
|
|
19569
|
+
pollTimer = setInterval(() => {
|
|
19570
|
+
try {
|
|
19571
|
+
const href = iframe.contentWindow?.location?.href;
|
|
19572
|
+
if (wasRedirecting && href) done();
|
|
19573
|
+
} catch {
|
|
19574
|
+
wasRedirecting = true;
|
|
19575
|
+
}
|
|
19576
|
+
}, 100);
|
|
19577
|
+
const timeout = setTimeout(done, 5e3);
|
|
19578
|
+
iframe.onerror = done;
|
|
18918
19579
|
document.body.appendChild(iframe);
|
|
18919
19580
|
});
|
|
18920
19581
|
} catch (error) {
|
|
18921
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
19582
|
+
console.error("[BackendProvider] logout via iframe failed:", error);
|
|
19583
|
+
}
|
|
19584
|
+
}
|
|
19585
|
+
/**
|
|
19586
|
+
* 非 IOA 企业登出:直接 HTTP 请求 /console/logout
|
|
19587
|
+
*/
|
|
19588
|
+
async logoutViaHttp() {
|
|
19589
|
+
try {
|
|
19590
|
+
await httpService.get("/console/logout");
|
|
19591
|
+
} catch (error) {
|
|
19592
|
+
console.error("[BackendProvider] logout via http failed:", error);
|
|
18922
19593
|
}
|
|
18923
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
18924
|
-
accountService.clearAccount();
|
|
18925
19594
|
}
|
|
18926
19595
|
/**
|
|
18927
19596
|
* 批量切换插件状态
|
|
@@ -18953,6 +19622,52 @@ var BackendProvider = class {
|
|
|
18953
19622
|
return null;
|
|
18954
19623
|
}
|
|
18955
19624
|
}
|
|
19625
|
+
/**
|
|
19626
|
+
* 刷新 Token
|
|
19627
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
19628
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
19629
|
+
*/
|
|
19630
|
+
async refreshToken() {
|
|
19631
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
19632
|
+
try {
|
|
19633
|
+
const account = await this.getAccount();
|
|
19634
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
19635
|
+
return account;
|
|
19636
|
+
} catch (error) {
|
|
19637
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
19638
|
+
return null;
|
|
19639
|
+
}
|
|
19640
|
+
}
|
|
19641
|
+
/**
|
|
19642
|
+
* 获取仓库分支列表
|
|
19643
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
19644
|
+
*
|
|
19645
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19646
|
+
* @param params 平台特定的查询参数
|
|
19647
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19648
|
+
* @param perPage 每页数量,最大100
|
|
19649
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
19650
|
+
*/
|
|
19651
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
19652
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
19653
|
+
}
|
|
19654
|
+
/**
|
|
19655
|
+
* 获取仓库列表
|
|
19656
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
19657
|
+
*
|
|
19658
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
19659
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
19660
|
+
*
|
|
19661
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19662
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19663
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
19664
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
19665
|
+
* @param perPage 每页数量,最大100
|
|
19666
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
19667
|
+
*/
|
|
19668
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
19669
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
19670
|
+
}
|
|
18956
19671
|
};
|
|
18957
19672
|
/**
|
|
18958
19673
|
* 创建 BackendProvider 实例
|
|
@@ -18992,6 +19707,7 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
18992
19707
|
GET_FILE: "backend:get-file",
|
|
18993
19708
|
RELOAD_WINDOW: "backend:reload-window",
|
|
18994
19709
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
19710
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
18995
19711
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
18996
19712
|
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
18997
19713
|
};
|
|
@@ -19287,6 +20003,20 @@ var IPCBackendProvider = class {
|
|
|
19287
20003
|
}
|
|
19288
20004
|
}
|
|
19289
20005
|
/**
|
|
20006
|
+
* 在外部浏览器中打开链接
|
|
20007
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
20008
|
+
* @param url 要打开的 URL
|
|
20009
|
+
*/
|
|
20010
|
+
async openExternal(url) {
|
|
20011
|
+
this.log("Opening external URL via IPC:", url);
|
|
20012
|
+
try {
|
|
20013
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
20014
|
+
} catch (error) {
|
|
20015
|
+
this.log("Open external request failed:", error);
|
|
20016
|
+
throw error;
|
|
20017
|
+
}
|
|
20018
|
+
}
|
|
20019
|
+
/**
|
|
19290
20020
|
* 批量切换插件状态
|
|
19291
20021
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
19292
20022
|
*/
|