@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.1828052.20260211
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 +863 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +860 -101
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +860 -101
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +863 -158
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.13-next.1828052.20260211.tgz +0 -0
- package/package.json +4 -3
- 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)
|
|
@@ -2283,6 +2281,24 @@ var CloudAgentConnection = class {
|
|
|
2283
2281
|
},
|
|
2284
2282
|
onUsageUpdate: (usage) => {
|
|
2285
2283
|
this.emit("usageUpdate", usage);
|
|
2284
|
+
},
|
|
2285
|
+
onExtNotification: (method, params) => {
|
|
2286
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2287
|
+
method,
|
|
2288
|
+
paramsKeys: Object.keys(params)
|
|
2289
|
+
});
|
|
2290
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2291
|
+
const action = params.action;
|
|
2292
|
+
const commandParams = params.params;
|
|
2293
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2294
|
+
action,
|
|
2295
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2296
|
+
});
|
|
2297
|
+
this.emit("command", {
|
|
2298
|
+
action,
|
|
2299
|
+
params: commandParams
|
|
2300
|
+
});
|
|
2301
|
+
}
|
|
2286
2302
|
}
|
|
2287
2303
|
});
|
|
2288
2304
|
this.setupEventForwarding();
|
|
@@ -2411,7 +2427,7 @@ var CloudAgentConnection = class {
|
|
|
2411
2427
|
}
|
|
2412
2428
|
async createSession(params) {
|
|
2413
2429
|
return {
|
|
2414
|
-
...await this.client.
|
|
2430
|
+
...await this.client.createSession(this.cwd),
|
|
2415
2431
|
sessionId: this.agentId
|
|
2416
2432
|
};
|
|
2417
2433
|
}
|
|
@@ -2529,6 +2545,16 @@ var CloudAgentConnection = class {
|
|
|
2529
2545
|
get sessionConnectionInfo() {
|
|
2530
2546
|
return this._sessionConnectionInfo;
|
|
2531
2547
|
}
|
|
2548
|
+
async reportTelemetry(eventName, payload) {
|
|
2549
|
+
try {
|
|
2550
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2551
|
+
eventName,
|
|
2552
|
+
payload
|
|
2553
|
+
});
|
|
2554
|
+
} catch (error) {
|
|
2555
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2556
|
+
}
|
|
2557
|
+
}
|
|
2532
2558
|
async extMethod(method, params) {
|
|
2533
2559
|
return this.client.extMethod(method, params);
|
|
2534
2560
|
}
|
|
@@ -15923,7 +15949,7 @@ var axios_default = axios;
|
|
|
15923
15949
|
* 特性:
|
|
15924
15950
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
15925
15951
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
15926
|
-
* - 统一 401
|
|
15952
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
15927
15953
|
* - 自动携带凭证(withCredentials)
|
|
15928
15954
|
* - 类型安全
|
|
15929
15955
|
*
|
|
@@ -15963,6 +15989,8 @@ var HttpService = class HttpService {
|
|
|
15963
15989
|
*/
|
|
15964
15990
|
constructor(config = {}) {
|
|
15965
15991
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
15992
|
+
this.isRefreshing = false;
|
|
15993
|
+
this.refreshSubscribers = [];
|
|
15966
15994
|
this.config = config;
|
|
15967
15995
|
this.axiosInstance = axios_default.create({
|
|
15968
15996
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -16003,18 +16031,54 @@ var HttpService = class HttpService {
|
|
|
16003
16031
|
}, (error) => Promise.reject(error));
|
|
16004
16032
|
}
|
|
16005
16033
|
/**
|
|
16006
|
-
* 注册默认响应拦截器(处理 401
|
|
16034
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
16007
16035
|
*/
|
|
16008
16036
|
registerDefaultResponseInterceptor() {
|
|
16009
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16037
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
16038
|
+
const originalRequest = error.config;
|
|
16039
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
16040
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
16041
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
16042
|
+
return Promise.reject(error);
|
|
16043
|
+
}
|
|
16044
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
16045
|
+
originalRequest._retry = true;
|
|
16046
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
16047
|
+
this.refreshSubscribers.push((success) => {
|
|
16048
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
16049
|
+
else reject(error);
|
|
16050
|
+
});
|
|
16051
|
+
});
|
|
16052
|
+
this.isRefreshing = true;
|
|
16053
|
+
try {
|
|
16054
|
+
await this.triggerUnauthorizedCallbacks();
|
|
16055
|
+
this.onRefreshSuccess();
|
|
16056
|
+
return this.axiosInstance.request(originalRequest);
|
|
16057
|
+
} catch (refreshError) {
|
|
16058
|
+
this.onRefreshFailure();
|
|
16059
|
+
return Promise.reject(error);
|
|
16060
|
+
} finally {
|
|
16061
|
+
this.isRefreshing = false;
|
|
16062
|
+
}
|
|
16013
16063
|
}
|
|
16014
16064
|
return Promise.reject(error);
|
|
16015
16065
|
});
|
|
16016
16066
|
}
|
|
16017
16067
|
/**
|
|
16068
|
+
* token 刷新成功,通知所有等待的请求
|
|
16069
|
+
*/
|
|
16070
|
+
onRefreshSuccess() {
|
|
16071
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
16072
|
+
this.refreshSubscribers = [];
|
|
16073
|
+
}
|
|
16074
|
+
/**
|
|
16075
|
+
* token 刷新失败,通知所有等待的请求
|
|
16076
|
+
*/
|
|
16077
|
+
onRefreshFailure() {
|
|
16078
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
16079
|
+
this.refreshSubscribers = [];
|
|
16080
|
+
}
|
|
16081
|
+
/**
|
|
16018
16082
|
* 注册请求拦截器
|
|
16019
16083
|
* @param onFulfilled 请求成功拦截器
|
|
16020
16084
|
* @param onRejected 请求失败拦截器
|
|
@@ -16091,16 +16155,18 @@ var HttpService = class HttpService {
|
|
|
16091
16155
|
this.unauthorizedCallbacks.delete(callback);
|
|
16092
16156
|
}
|
|
16093
16157
|
/**
|
|
16094
|
-
* 触发所有 401
|
|
16158
|
+
* 触发所有 401 回调并等待完成
|
|
16159
|
+
* @returns Promise,等待所有回调完成
|
|
16160
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
16095
16161
|
*/
|
|
16096
|
-
triggerUnauthorizedCallbacks() {
|
|
16097
|
-
this.unauthorizedCallbacks
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
|
|
16103
|
-
}
|
|
16162
|
+
async triggerUnauthorizedCallbacks() {
|
|
16163
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
16164
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
16165
|
+
if (failedResults.length > 0) {
|
|
16166
|
+
const errors = failedResults.map((r) => r.reason);
|
|
16167
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
16168
|
+
throw errors[0];
|
|
16169
|
+
}
|
|
16104
16170
|
}
|
|
16105
16171
|
/**
|
|
16106
16172
|
* 更新 authToken
|
|
@@ -16212,6 +16278,7 @@ var AccountService = class {
|
|
|
16212
16278
|
this.initPromise = null;
|
|
16213
16279
|
this.initResolve = null;
|
|
16214
16280
|
this.requestInterceptorId = null;
|
|
16281
|
+
this.crossTabBroadcaster = null;
|
|
16215
16282
|
this.initPromise = new Promise((resolve) => {
|
|
16216
16283
|
this.initResolve = resolve;
|
|
16217
16284
|
});
|
|
@@ -16260,15 +16327,34 @@ var AccountService = class {
|
|
|
16260
16327
|
this.initialized = true;
|
|
16261
16328
|
this.initResolve?.(account);
|
|
16262
16329
|
}
|
|
16263
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
16330
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
16331
|
+
this.notifyListeners();
|
|
16332
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
16333
|
+
}
|
|
16264
16334
|
}
|
|
16265
16335
|
/**
|
|
16266
16336
|
* 清除账号(登出)
|
|
16337
|
+
* 先广播登出消息,再清除本地账号
|
|
16267
16338
|
*/
|
|
16268
16339
|
clearAccount() {
|
|
16340
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
16269
16341
|
this.setAccount(null);
|
|
16270
16342
|
}
|
|
16271
16343
|
/**
|
|
16344
|
+
* 静默清除账号(不广播)
|
|
16345
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
16346
|
+
*/
|
|
16347
|
+
clearAccountSilently() {
|
|
16348
|
+
this.setAccount(null);
|
|
16349
|
+
}
|
|
16350
|
+
/**
|
|
16351
|
+
* 设置跨标签页认证同步广播器
|
|
16352
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
16353
|
+
*/
|
|
16354
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
16355
|
+
this.crossTabBroadcaster = broadcaster;
|
|
16356
|
+
}
|
|
16357
|
+
/**
|
|
16272
16358
|
* 订阅账号变化
|
|
16273
16359
|
* @param callback 变化时的回调函数
|
|
16274
16360
|
* @returns 取消订阅函数
|
|
@@ -16333,6 +16419,11 @@ var AccountService = class {
|
|
|
16333
16419
|
* 导出单例实例
|
|
16334
16420
|
*/
|
|
16335
16421
|
const accountService = new AccountService();
|
|
16422
|
+
/**
|
|
16423
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
16424
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
16425
|
+
*/
|
|
16426
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
16336
16427
|
|
|
16337
16428
|
//#endregion
|
|
16338
16429
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -16435,20 +16526,32 @@ var CosUploadService = class {
|
|
|
16435
16526
|
* 上传单个文件到 COS
|
|
16436
16527
|
*
|
|
16437
16528
|
* @param file - 要上传的文件
|
|
16529
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16438
16530
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
16439
16531
|
*/
|
|
16440
|
-
async uploadFile(file) {
|
|
16532
|
+
async uploadFile(file, abortSignal) {
|
|
16441
16533
|
const filename = file.name;
|
|
16442
16534
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
16443
16535
|
try {
|
|
16536
|
+
if (abortSignal?.aborted) return {
|
|
16537
|
+
success: false,
|
|
16538
|
+
error: "Upload cancelled",
|
|
16539
|
+
aborted: true
|
|
16540
|
+
};
|
|
16444
16541
|
const objectKey = this.generateObjectKey(filename);
|
|
16445
16542
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
16446
16543
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
16447
16544
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
16545
|
+
if (abortSignal?.aborted) return {
|
|
16546
|
+
success: false,
|
|
16547
|
+
error: "Upload cancelled",
|
|
16548
|
+
aborted: true
|
|
16549
|
+
};
|
|
16448
16550
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16449
16551
|
method: "PUT",
|
|
16450
16552
|
body: file,
|
|
16451
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16553
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16554
|
+
signal: abortSignal
|
|
16452
16555
|
});
|
|
16453
16556
|
if (!uploadResponse.ok) {
|
|
16454
16557
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16462,6 +16565,11 @@ var CosUploadService = class {
|
|
|
16462
16565
|
objectKey
|
|
16463
16566
|
};
|
|
16464
16567
|
} catch (error) {
|
|
16568
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16569
|
+
success: false,
|
|
16570
|
+
error: "Upload cancelled",
|
|
16571
|
+
aborted: true
|
|
16572
|
+
};
|
|
16465
16573
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
16466
16574
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
16467
16575
|
return {
|
|
@@ -16476,14 +16584,25 @@ var CosUploadService = class {
|
|
|
16476
16584
|
* 使用并发控制,限制同时上传的文件数量
|
|
16477
16585
|
*
|
|
16478
16586
|
* @param files - 要上传的文件数组
|
|
16587
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16479
16588
|
* @returns 所有文件的上传结果
|
|
16480
16589
|
*/
|
|
16481
|
-
async uploadFiles(files) {
|
|
16590
|
+
async uploadFiles(files, abortSignal) {
|
|
16482
16591
|
if (files.length === 0) return {
|
|
16483
16592
|
success: true,
|
|
16484
16593
|
urls: [],
|
|
16485
16594
|
results: []
|
|
16486
16595
|
};
|
|
16596
|
+
if (abortSignal?.aborted) return {
|
|
16597
|
+
success: false,
|
|
16598
|
+
error: "Upload cancelled",
|
|
16599
|
+
aborted: true,
|
|
16600
|
+
results: files.map(() => ({
|
|
16601
|
+
success: false,
|
|
16602
|
+
error: "Upload cancelled",
|
|
16603
|
+
aborted: true
|
|
16604
|
+
}))
|
|
16605
|
+
};
|
|
16487
16606
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
16488
16607
|
try {
|
|
16489
16608
|
const fileInfos = files.map((file) => ({
|
|
@@ -16495,6 +16614,12 @@ var CosUploadService = class {
|
|
|
16495
16614
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
16496
16615
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
16497
16616
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
16617
|
+
if (abortSignal?.aborted) return {
|
|
16618
|
+
success: false,
|
|
16619
|
+
error: "Upload cancelled",
|
|
16620
|
+
aborted: true,
|
|
16621
|
+
objectKey: fileInfos[index].objectKey
|
|
16622
|
+
};
|
|
16498
16623
|
const presignedItem = presignedResponse.items[index];
|
|
16499
16624
|
if (!presignedItem) return {
|
|
16500
16625
|
success: false,
|
|
@@ -16505,7 +16630,8 @@ var CosUploadService = class {
|
|
|
16505
16630
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16506
16631
|
method: "PUT",
|
|
16507
16632
|
body: file,
|
|
16508
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16633
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16634
|
+
signal: abortSignal
|
|
16509
16635
|
});
|
|
16510
16636
|
if (!uploadResponse.ok) {
|
|
16511
16637
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16522,6 +16648,12 @@ var CosUploadService = class {
|
|
|
16522
16648
|
objectKey: presignedItem.object_key
|
|
16523
16649
|
};
|
|
16524
16650
|
} catch (error) {
|
|
16651
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16652
|
+
success: false,
|
|
16653
|
+
error: "Upload cancelled",
|
|
16654
|
+
aborted: true,
|
|
16655
|
+
objectKey: presignedItem.object_key
|
|
16656
|
+
};
|
|
16525
16657
|
return {
|
|
16526
16658
|
success: false,
|
|
16527
16659
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -16922,15 +17054,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16922
17054
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
16923
17055
|
const apiResponse = await httpService.get(url);
|
|
16924
17056
|
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
17057
|
return {
|
|
16932
|
-
agents,
|
|
16933
|
-
pagination
|
|
17058
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
17059
|
+
pagination: apiResponse.data.pagination
|
|
16934
17060
|
};
|
|
16935
17061
|
} catch (error) {
|
|
16936
17062
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -16940,13 +17066,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16940
17066
|
/**
|
|
16941
17067
|
* Create a new conversation
|
|
16942
17068
|
* POST {endpoint}/console/as/conversations
|
|
17069
|
+
* @param params - Session params containing cwd and optional configuration
|
|
16943
17070
|
*/
|
|
16944
|
-
async create() {
|
|
17071
|
+
async create(params) {
|
|
16945
17072
|
try {
|
|
16946
|
-
const
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
});
|
|
17073
|
+
const { options = {} } = params;
|
|
17074
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
17075
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
17076
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
17077
|
+
const createPayload = {
|
|
17078
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
17079
|
+
model: options.model || "deepseek-r1",
|
|
17080
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
17081
|
+
};
|
|
17082
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
17083
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
16950
17084
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
16951
17085
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
16952
17086
|
return apiResponse.data.id;
|
|
@@ -17116,6 +17250,35 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17116
17250
|
}
|
|
17117
17251
|
}
|
|
17118
17252
|
/**
|
|
17253
|
+
* Update conversation status by ID
|
|
17254
|
+
* POST {endpoint}/console/as/conversations/{agentId}
|
|
17255
|
+
*
|
|
17256
|
+
* @param agentId - Conversation ID to update
|
|
17257
|
+
* @param status - New status for the conversation
|
|
17258
|
+
* @returns PatchConversationResponse containing the updated conversation ID
|
|
17259
|
+
*
|
|
17260
|
+
* @example
|
|
17261
|
+
* ```typescript
|
|
17262
|
+
* const result = await provider.updateStatus('agent-123', 'completed');
|
|
17263
|
+
* console.log('Updated conversation status:', result.id);
|
|
17264
|
+
* ```
|
|
17265
|
+
*/
|
|
17266
|
+
async updateStatus(agentId, status) {
|
|
17267
|
+
try {
|
|
17268
|
+
const body = { status };
|
|
17269
|
+
const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
|
|
17270
|
+
if (!apiResponse.data) {
|
|
17271
|
+
this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
|
|
17272
|
+
return { id: agentId };
|
|
17273
|
+
}
|
|
17274
|
+
this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
|
|
17275
|
+
return apiResponse.data;
|
|
17276
|
+
} catch (error) {
|
|
17277
|
+
this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
|
|
17278
|
+
throw error;
|
|
17279
|
+
}
|
|
17280
|
+
}
|
|
17281
|
+
/**
|
|
17119
17282
|
* Get available models from product configuration
|
|
17120
17283
|
*
|
|
17121
17284
|
* GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
|
|
@@ -17146,9 +17309,12 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17146
17309
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
17147
17310
|
return [];
|
|
17148
17311
|
}
|
|
17149
|
-
const
|
|
17150
|
-
|
|
17151
|
-
|
|
17312
|
+
const productConfig = apiResponse.data;
|
|
17313
|
+
const allModels = productConfig.models ?? [];
|
|
17314
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
17315
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
17316
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
17317
|
+
return filteredModels.map((model) => ({
|
|
17152
17318
|
id: model.id,
|
|
17153
17319
|
name: model.name ?? model.id,
|
|
17154
17320
|
description: model.description,
|
|
@@ -17252,7 +17418,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17252
17418
|
/**
|
|
17253
17419
|
* Upload files to cloud storage via COS presigned URL
|
|
17254
17420
|
*
|
|
17255
|
-
* @param params - files array (File objects in browser)
|
|
17421
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
17256
17422
|
* @returns Response with corresponding cloud URLs
|
|
17257
17423
|
*/
|
|
17258
17424
|
async uploadFile(params) {
|
|
@@ -17262,12 +17428,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17262
17428
|
success: false,
|
|
17263
17429
|
error: "No valid File objects provided"
|
|
17264
17430
|
};
|
|
17265
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
17431
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
17266
17432
|
return {
|
|
17267
17433
|
success: result.success,
|
|
17268
17434
|
urls: result.urls,
|
|
17269
17435
|
expireSeconds: result.expireSeconds,
|
|
17270
|
-
error: result.error
|
|
17436
|
+
error: result.error,
|
|
17437
|
+
aborted: result.aborted
|
|
17271
17438
|
};
|
|
17272
17439
|
}
|
|
17273
17440
|
/**
|
|
@@ -17309,20 +17476,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17309
17476
|
}
|
|
17310
17477
|
/**
|
|
17311
17478
|
* 获取支持的场景列表
|
|
17312
|
-
* API 端点: GET /
|
|
17479
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
17313
17480
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
17314
17481
|
*
|
|
17482
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
17315
17483
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
17316
17484
|
*/
|
|
17317
|
-
async getSupportScenes() {
|
|
17485
|
+
async getSupportScenes(locale) {
|
|
17318
17486
|
try {
|
|
17319
|
-
const
|
|
17487
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
17488
|
+
const apiResponse = await httpService.get(url);
|
|
17320
17489
|
if (!apiResponse.data) {
|
|
17321
17490
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
17322
17491
|
return [];
|
|
17323
17492
|
}
|
|
17324
17493
|
const scenes = apiResponse.data.scenes || [];
|
|
17325
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
17494
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
17326
17495
|
return scenes;
|
|
17327
17496
|
} catch (error) {
|
|
17328
17497
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -17338,7 +17507,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17338
17507
|
type: "cloud",
|
|
17339
17508
|
status,
|
|
17340
17509
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
17341
|
-
capabilities: this.options.clientCapabilities
|
|
17510
|
+
capabilities: this.options.clientCapabilities,
|
|
17511
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
17342
17512
|
};
|
|
17343
17513
|
}
|
|
17344
17514
|
/**
|
|
@@ -17354,6 +17524,37 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17354
17524
|
const queryString = searchParams.toString();
|
|
17355
17525
|
return queryString ? `${path}?${queryString}` : path;
|
|
17356
17526
|
}
|
|
17527
|
+
/**
|
|
17528
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
17529
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
17530
|
+
* 注入用户信息和浏览器环境等公共字段
|
|
17531
|
+
*/
|
|
17532
|
+
async reportTelemetry(eventName, payload) {
|
|
17533
|
+
try {
|
|
17534
|
+
const account = accountService.getAccount();
|
|
17535
|
+
const commonFields = {};
|
|
17536
|
+
if (account) {
|
|
17537
|
+
commonFields.userId = account.uid;
|
|
17538
|
+
commonFields.userNickname = account.nickname;
|
|
17539
|
+
if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
|
|
17540
|
+
if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
|
|
17541
|
+
}
|
|
17542
|
+
if (typeof navigator !== "undefined") {
|
|
17543
|
+
commonFields.userAgent = navigator.userAgent;
|
|
17544
|
+
commonFields.os = navigator.platform;
|
|
17545
|
+
}
|
|
17546
|
+
const events = [{
|
|
17547
|
+
eventCode: eventName,
|
|
17548
|
+
timestamp: Date.now(),
|
|
17549
|
+
reportDelay: 0,
|
|
17550
|
+
...commonFields,
|
|
17551
|
+
...payload
|
|
17552
|
+
}];
|
|
17553
|
+
await httpService.post("/v2/report", events);
|
|
17554
|
+
} catch (error) {
|
|
17555
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
17556
|
+
}
|
|
17557
|
+
}
|
|
17357
17558
|
};
|
|
17358
17559
|
|
|
17359
17560
|
//#endregion
|
|
@@ -17402,6 +17603,7 @@ var ActiveSessionImpl = class {
|
|
|
17402
17603
|
this._availableCommands = [];
|
|
17403
17604
|
this.listeners = /* @__PURE__ */ new Map();
|
|
17404
17605
|
this.onceListeners = /* @__PURE__ */ new Map();
|
|
17606
|
+
this.connectionListeners = [];
|
|
17405
17607
|
this._id = sessionId;
|
|
17406
17608
|
this._agentId = agentId;
|
|
17407
17609
|
this.connection = connection;
|
|
@@ -17427,6 +17629,18 @@ var ActiveSessionImpl = class {
|
|
|
17427
17629
|
return this._agentId;
|
|
17428
17630
|
}
|
|
17429
17631
|
/**
|
|
17632
|
+
* Actual workspace path (set from newSession response _meta)
|
|
17633
|
+
*/
|
|
17634
|
+
get cwd() {
|
|
17635
|
+
return this._cwd;
|
|
17636
|
+
}
|
|
17637
|
+
/**
|
|
17638
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
17639
|
+
*/
|
|
17640
|
+
setCwd(cwd) {
|
|
17641
|
+
this._cwd = cwd;
|
|
17642
|
+
}
|
|
17643
|
+
/**
|
|
17430
17644
|
* Agent state (live connection state)
|
|
17431
17645
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
17432
17646
|
*/
|
|
@@ -17643,8 +17857,8 @@ var ActiveSessionImpl = class {
|
|
|
17643
17857
|
* await session.setMode('architect');
|
|
17644
17858
|
* ```
|
|
17645
17859
|
*/
|
|
17646
|
-
async setMode(modeId) {
|
|
17647
|
-
if (this._availableModes) {
|
|
17860
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
17861
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
17648
17862
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
17649
17863
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
17650
17864
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -17744,6 +17958,7 @@ var ActiveSessionImpl = class {
|
|
|
17744
17958
|
* Disconnect from the session/agent
|
|
17745
17959
|
*/
|
|
17746
17960
|
disconnect() {
|
|
17961
|
+
this.removeConnectionListeners();
|
|
17747
17962
|
this.connection.disconnect();
|
|
17748
17963
|
this.removeAllListeners();
|
|
17749
17964
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
@@ -17767,60 +17982,80 @@ var ActiveSessionImpl = class {
|
|
|
17767
17982
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
17768
17983
|
return this.connection;
|
|
17769
17984
|
}
|
|
17985
|
+
/**
|
|
17986
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
17987
|
+
*/
|
|
17988
|
+
addConnectionListener(connection, event, listener) {
|
|
17989
|
+
connection.on(event, listener);
|
|
17990
|
+
this.connectionListeners.push({
|
|
17991
|
+
event,
|
|
17992
|
+
listener
|
|
17993
|
+
});
|
|
17994
|
+
}
|
|
17995
|
+
/**
|
|
17996
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
17997
|
+
*/
|
|
17998
|
+
removeConnectionListeners() {
|
|
17999
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
18000
|
+
this.connectionListeners = [];
|
|
18001
|
+
}
|
|
17770
18002
|
setupConnectionEvents(connection) {
|
|
17771
|
-
|
|
18003
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
17772
18004
|
this.emit("connected", void 0);
|
|
17773
18005
|
});
|
|
17774
|
-
|
|
18006
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
17775
18007
|
this.emit("disconnected", void 0);
|
|
17776
18008
|
});
|
|
17777
|
-
|
|
18009
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
17778
18010
|
this.emit("error", error);
|
|
17779
18011
|
});
|
|
17780
|
-
|
|
18012
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
17781
18013
|
this.emit("sessionUpdate", update);
|
|
17782
18014
|
});
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
artifactUri: artifact.uri,
|
|
17786
|
-
artifactType: artifact.type
|
|
17787
|
-
});
|
|
18015
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
18016
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17788
18017
|
this.emit("artifactCreated", artifact);
|
|
17789
18018
|
});
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
artifactUri: artifact.uri,
|
|
17793
|
-
artifactType: artifact.type
|
|
17794
|
-
});
|
|
18019
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
18020
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17795
18021
|
this.emit("artifactUpdated", artifact);
|
|
17796
18022
|
});
|
|
17797
|
-
|
|
17798
|
-
|
|
18023
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
18024
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17799
18025
|
this.emit("artifactDeleted", artifact);
|
|
17800
18026
|
});
|
|
17801
|
-
|
|
18027
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
17802
18028
|
this.emit("permissionRequest", request);
|
|
17803
18029
|
});
|
|
17804
|
-
|
|
18030
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
17805
18031
|
this.emit("questionRequest", request);
|
|
17806
18032
|
});
|
|
17807
|
-
|
|
18033
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
17808
18034
|
this.prompts.cancel();
|
|
17809
18035
|
});
|
|
17810
|
-
|
|
18036
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
17811
18037
|
this.emit("usageUpdate", usage);
|
|
17812
18038
|
});
|
|
17813
|
-
|
|
18039
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
18040
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18041
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17814
18042
|
this.emit("checkpointCreated", checkpoint);
|
|
17815
18043
|
});
|
|
17816
|
-
|
|
18044
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
18045
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18046
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17817
18047
|
this.emit("checkpointUpdated", checkpoint);
|
|
17818
18048
|
});
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
18049
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
18050
|
+
const originSessionId = command.__sessionId;
|
|
18051
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
18052
|
+
console.log("[Session] Command not forwarded:", {
|
|
18053
|
+
command,
|
|
18054
|
+
originSessionId,
|
|
18055
|
+
sessionId: this._id
|
|
18056
|
+
});
|
|
18057
|
+
return;
|
|
18058
|
+
}
|
|
17824
18059
|
this.emit("command", command);
|
|
17825
18060
|
});
|
|
17826
18061
|
}
|
|
@@ -17830,19 +18065,38 @@ var ActiveSessionImpl = class {
|
|
|
17830
18065
|
_meta: response._meta ?? void 0
|
|
17831
18066
|
};
|
|
17832
18067
|
}
|
|
18068
|
+
/**
|
|
18069
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
18070
|
+
* - media 类型:按 cwd 路径隔离(同 cwd 下不同 session 可共享 media)
|
|
18071
|
+
* - 其余类型:按 __sessionId 严格隔离
|
|
18072
|
+
*/
|
|
18073
|
+
shouldForwardArtifact(artifact) {
|
|
18074
|
+
const originSessionId = artifact.__sessionId;
|
|
18075
|
+
console.log("[Session] shouldForwardArtifact:", {
|
|
18076
|
+
artifact,
|
|
18077
|
+
originSessionId,
|
|
18078
|
+
sessionId: this._id,
|
|
18079
|
+
cwd: this.connection?.cwd
|
|
18080
|
+
});
|
|
18081
|
+
if (artifact.type === "media") {
|
|
18082
|
+
const cwd = this.connection?.cwd;
|
|
18083
|
+
const uri = artifact?.uri;
|
|
18084
|
+
if (cwd && uri) {
|
|
18085
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
18086
|
+
const uriPath = toPosix(uri.replace(/^(?:file|agent):\/\//, ""));
|
|
18087
|
+
const posixCwd = toPosix(cwd);
|
|
18088
|
+
const normalizedCwd = posixCwd.endsWith("/") ? posixCwd : posixCwd + "/";
|
|
18089
|
+
return uriPath.startsWith(normalizedCwd);
|
|
18090
|
+
}
|
|
18091
|
+
}
|
|
18092
|
+
if (originSessionId && originSessionId !== this._id) return false;
|
|
18093
|
+
return true;
|
|
18094
|
+
}
|
|
17833
18095
|
};
|
|
17834
18096
|
|
|
17835
18097
|
//#endregion
|
|
17836
18098
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
17837
18099
|
/**
|
|
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
18100
|
* SessionManager - Session lifecycle management
|
|
17847
18101
|
*
|
|
17848
18102
|
* This class manages the relationship between sessions and agents.
|
|
@@ -17892,7 +18146,8 @@ var SessionManager = class {
|
|
|
17892
18146
|
createdAt: agent.createdAt,
|
|
17893
18147
|
lastActivityAt: agent.updatedAt,
|
|
17894
18148
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
17895
|
-
isPlayground: agent.isPlayground
|
|
18149
|
+
isPlayground: agent.isPlayground,
|
|
18150
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
17896
18151
|
}));
|
|
17897
18152
|
console.log("[SessionManager] Returning sessions:", {
|
|
17898
18153
|
count: sessions.length,
|
|
@@ -17919,13 +18174,26 @@ var SessionManager = class {
|
|
|
17919
18174
|
if (this.provider.create) {
|
|
17920
18175
|
agentId = await this.provider.create(params);
|
|
17921
18176
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
18177
|
+
if (params.options?.onSessionPrepared) {
|
|
18178
|
+
const initialPrompt = params.options?.prompt;
|
|
18179
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
18180
|
+
params.options.onSessionPrepared({
|
|
18181
|
+
id: agentId,
|
|
18182
|
+
agentId,
|
|
18183
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
18184
|
+
status: "connecting",
|
|
18185
|
+
cwd: params.cwd || "",
|
|
18186
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
18187
|
+
});
|
|
18188
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
18189
|
+
}
|
|
17922
18190
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
17923
18191
|
const connection = await this.provider.connect(agentId);
|
|
17924
18192
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
17925
18193
|
const response = await connection.createSession({
|
|
17926
|
-
_meta: params._meta,
|
|
18194
|
+
_meta: params.options?._meta,
|
|
17927
18195
|
cwd: params.cwd,
|
|
17928
|
-
mcpServers: params.mcpServers
|
|
18196
|
+
mcpServers: params.options?.mcpServers
|
|
17929
18197
|
});
|
|
17930
18198
|
if (this.provider.registerSession) {
|
|
17931
18199
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -17938,14 +18206,10 @@ var SessionManager = class {
|
|
|
17938
18206
|
connectionInfo
|
|
17939
18207
|
});
|
|
17940
18208
|
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
|
-
}
|
|
18209
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18210
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
18211
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
18212
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
17949
18213
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
17950
18214
|
return session;
|
|
17951
18215
|
}
|
|
@@ -17981,17 +18245,31 @@ var SessionManager = class {
|
|
|
17981
18245
|
mcpServers: params.mcpServers
|
|
17982
18246
|
});
|
|
17983
18247
|
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
|
-
}
|
|
18248
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18249
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
17992
18250
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
17993
18251
|
return session;
|
|
17994
18252
|
}
|
|
18253
|
+
/**
|
|
18254
|
+
* 从 ACP response 中提取可用模型列表
|
|
18255
|
+
*
|
|
18256
|
+
* 优先级:
|
|
18257
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
18258
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
18259
|
+
* 3. undefined - 都没有时返回 undefined
|
|
18260
|
+
*
|
|
18261
|
+
* @param response - ACP 响应对象
|
|
18262
|
+
* @returns ModelInfo[] | undefined
|
|
18263
|
+
*/
|
|
18264
|
+
extractAvailableModels(response) {
|
|
18265
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
18266
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
18267
|
+
const availableModels = response.models?.availableModels;
|
|
18268
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
18269
|
+
...model,
|
|
18270
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
18271
|
+
}));
|
|
18272
|
+
}
|
|
17995
18273
|
};
|
|
17996
18274
|
|
|
17997
18275
|
//#endregion
|
|
@@ -18098,6 +18376,26 @@ var AgentClient = class {
|
|
|
18098
18376
|
throw error;
|
|
18099
18377
|
}
|
|
18100
18378
|
},
|
|
18379
|
+
updateStatus: async (sessionId, status) => {
|
|
18380
|
+
this.logger?.debug("AgentClient.sessions.updateStatus called", {
|
|
18381
|
+
sessionId,
|
|
18382
|
+
status
|
|
18383
|
+
});
|
|
18384
|
+
try {
|
|
18385
|
+
if (this.provider.updateStatus) {
|
|
18386
|
+
const result = await this.provider.updateStatus(sessionId, status);
|
|
18387
|
+
this.logger?.info("Session status updated successfully", {
|
|
18388
|
+
sessionId,
|
|
18389
|
+
status
|
|
18390
|
+
});
|
|
18391
|
+
return result;
|
|
18392
|
+
}
|
|
18393
|
+
throw new Error("Provider does not support updateStatus method");
|
|
18394
|
+
} catch (error) {
|
|
18395
|
+
this.logger?.error("Failed to update session status", error);
|
|
18396
|
+
throw error;
|
|
18397
|
+
}
|
|
18398
|
+
},
|
|
18101
18399
|
move: async (sessionId) => {
|
|
18102
18400
|
this.logger?.debug("AgentClient.sessions.move called", { sessionId });
|
|
18103
18401
|
try {
|
|
@@ -18264,6 +18562,72 @@ var AgentClient = class {
|
|
|
18264
18562
|
};
|
|
18265
18563
|
}
|
|
18266
18564
|
},
|
|
18565
|
+
getSubagentList: async (params) => {
|
|
18566
|
+
try {
|
|
18567
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
18568
|
+
const result = await this.provider.getSubagentList(params);
|
|
18569
|
+
this.logger?.info("Subagent list retrieved", {
|
|
18570
|
+
resultCount: result.results.length,
|
|
18571
|
+
hasError: !!result.error
|
|
18572
|
+
});
|
|
18573
|
+
return result;
|
|
18574
|
+
}
|
|
18575
|
+
return {
|
|
18576
|
+
results: [],
|
|
18577
|
+
error: "Provider does not support getSubagentList"
|
|
18578
|
+
};
|
|
18579
|
+
} catch (error) {
|
|
18580
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
18581
|
+
return {
|
|
18582
|
+
results: [],
|
|
18583
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18584
|
+
};
|
|
18585
|
+
}
|
|
18586
|
+
},
|
|
18587
|
+
getSkillList: async (params) => {
|
|
18588
|
+
try {
|
|
18589
|
+
if (this.provider && this.provider.getSkillList) {
|
|
18590
|
+
const result = await this.provider.getSkillList(params);
|
|
18591
|
+
this.logger?.info("Skill list retrieved", {
|
|
18592
|
+
resultCount: result.results.length,
|
|
18593
|
+
hasError: !!result.error
|
|
18594
|
+
});
|
|
18595
|
+
return result;
|
|
18596
|
+
}
|
|
18597
|
+
return {
|
|
18598
|
+
results: [],
|
|
18599
|
+
error: "Provider does not support getSkillList"
|
|
18600
|
+
};
|
|
18601
|
+
} catch (error) {
|
|
18602
|
+
this.logger?.error("Failed to get skill list", error);
|
|
18603
|
+
return {
|
|
18604
|
+
results: [],
|
|
18605
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18606
|
+
};
|
|
18607
|
+
}
|
|
18608
|
+
},
|
|
18609
|
+
importSkill: async (params) => {
|
|
18610
|
+
try {
|
|
18611
|
+
if (this.provider && this.provider.importSkill) {
|
|
18612
|
+
const result = await this.provider.importSkill(params);
|
|
18613
|
+
this.logger?.info("Import skill completed", {
|
|
18614
|
+
success: result.success,
|
|
18615
|
+
hasError: !!result.error
|
|
18616
|
+
});
|
|
18617
|
+
return result;
|
|
18618
|
+
}
|
|
18619
|
+
return {
|
|
18620
|
+
success: false,
|
|
18621
|
+
error: "Provider does not support importSkill"
|
|
18622
|
+
};
|
|
18623
|
+
} catch (error) {
|
|
18624
|
+
this.logger?.error("Failed to import skill", error);
|
|
18625
|
+
return {
|
|
18626
|
+
success: false,
|
|
18627
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18628
|
+
};
|
|
18629
|
+
}
|
|
18630
|
+
},
|
|
18267
18631
|
batchTogglePlugins: async (request) => {
|
|
18268
18632
|
try {
|
|
18269
18633
|
if (this.provider && this.provider.batchTogglePlugins) {
|
|
@@ -18308,10 +18672,10 @@ var AgentClient = class {
|
|
|
18308
18672
|
return [];
|
|
18309
18673
|
}
|
|
18310
18674
|
},
|
|
18311
|
-
installPlugins: async (pluginNames, marketplaceName, installScope) => {
|
|
18675
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
18312
18676
|
try {
|
|
18313
18677
|
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
18314
|
-
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
|
|
18678
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
18315
18679
|
this.logger?.info("Install plugins", {
|
|
18316
18680
|
pluginNames,
|
|
18317
18681
|
marketplaceName,
|
|
@@ -18332,13 +18696,9 @@ var AgentClient = class {
|
|
|
18332
18696
|
};
|
|
18333
18697
|
}
|
|
18334
18698
|
},
|
|
18335
|
-
getSupportScenes: async () => {
|
|
18699
|
+
getSupportScenes: async (locale) => {
|
|
18336
18700
|
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
|
-
}
|
|
18701
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
18342
18702
|
this.logger?.warn("Provider does not support getSupportScenes");
|
|
18343
18703
|
return [];
|
|
18344
18704
|
} catch (error) {
|
|
@@ -18346,6 +18706,91 @@ var AgentClient = class {
|
|
|
18346
18706
|
return [];
|
|
18347
18707
|
}
|
|
18348
18708
|
},
|
|
18709
|
+
getProductScenes: async (locale) => {
|
|
18710
|
+
try {
|
|
18711
|
+
if (this.provider?.getProductScenes) {
|
|
18712
|
+
const result = await this.provider.getProductScenes(locale);
|
|
18713
|
+
this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
|
|
18714
|
+
return result;
|
|
18715
|
+
}
|
|
18716
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
18717
|
+
return [];
|
|
18718
|
+
} catch (error) {
|
|
18719
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
18720
|
+
return [];
|
|
18721
|
+
}
|
|
18722
|
+
},
|
|
18723
|
+
getAvailableCommands: async (params) => {
|
|
18724
|
+
try {
|
|
18725
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
18726
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
18727
|
+
this.logger?.info("Got available commands from provider", {
|
|
18728
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
18729
|
+
count: result?.length ?? 0
|
|
18730
|
+
});
|
|
18731
|
+
return result;
|
|
18732
|
+
}
|
|
18733
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
18734
|
+
return [];
|
|
18735
|
+
} catch (error) {
|
|
18736
|
+
this.logger?.error("Failed to get available commands", error);
|
|
18737
|
+
return [];
|
|
18738
|
+
}
|
|
18739
|
+
},
|
|
18740
|
+
reportTelemetry: async (eventName, payload) => {
|
|
18741
|
+
try {
|
|
18742
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
18743
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
18744
|
+
} catch (error) {
|
|
18745
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
18746
|
+
}
|
|
18747
|
+
},
|
|
18748
|
+
respondToSampling: async (sessionId, response) => {
|
|
18749
|
+
try {
|
|
18750
|
+
if (this.provider?.respondToSampling) {
|
|
18751
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
18752
|
+
this.logger?.info("Responded to sampling request", {
|
|
18753
|
+
sessionId,
|
|
18754
|
+
requestId: response.id,
|
|
18755
|
+
approved: response.approved
|
|
18756
|
+
});
|
|
18757
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
18758
|
+
} catch (error) {
|
|
18759
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
18760
|
+
throw error;
|
|
18761
|
+
}
|
|
18762
|
+
},
|
|
18763
|
+
respondToRoots: async (sessionId, response) => {
|
|
18764
|
+
try {
|
|
18765
|
+
if (this.provider?.respondToRoots) {
|
|
18766
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
18767
|
+
this.logger?.info("Responded to roots request", {
|
|
18768
|
+
sessionId,
|
|
18769
|
+
requestId: response.id,
|
|
18770
|
+
approved: response.approved
|
|
18771
|
+
});
|
|
18772
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
18773
|
+
} catch (error) {
|
|
18774
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
18775
|
+
throw error;
|
|
18776
|
+
}
|
|
18777
|
+
},
|
|
18778
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
18779
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
18780
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
18781
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
18782
|
+
}
|
|
18783
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
18784
|
+
return () => {};
|
|
18785
|
+
},
|
|
18786
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
18787
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
18788
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
18789
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
18790
|
+
}
|
|
18791
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
18792
|
+
return () => {};
|
|
18793
|
+
},
|
|
18349
18794
|
models: this.createModelsResource()
|
|
18350
18795
|
};
|
|
18351
18796
|
}
|
|
@@ -18412,6 +18857,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18412
18857
|
return AccountStatus;
|
|
18413
18858
|
}({});
|
|
18414
18859
|
|
|
18860
|
+
//#endregion
|
|
18861
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
18862
|
+
/**
|
|
18863
|
+
* OAuth Repository Service
|
|
18864
|
+
*
|
|
18865
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
18866
|
+
*/
|
|
18867
|
+
/**
|
|
18868
|
+
* OAuth Repository Service
|
|
18869
|
+
*
|
|
18870
|
+
* 提供仓库和分支的查询操作
|
|
18871
|
+
*/
|
|
18872
|
+
var OAuthRepositoryService = class {
|
|
18873
|
+
/**
|
|
18874
|
+
* 获取仓库分支列表
|
|
18875
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
18876
|
+
*
|
|
18877
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18878
|
+
* @param params 平台特定的查询参数
|
|
18879
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18880
|
+
* @param perPage 每页数量,最大100
|
|
18881
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
18882
|
+
*
|
|
18883
|
+
* @example
|
|
18884
|
+
* ```typescript
|
|
18885
|
+
* // GitHub
|
|
18886
|
+
* const branches = await service.getBranches('github', {
|
|
18887
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
18888
|
+
* repo: 'CodeBuddyIDE'
|
|
18889
|
+
* });
|
|
18890
|
+
*
|
|
18891
|
+
* // Gongfeng
|
|
18892
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
18893
|
+
* project_id: '1611499'
|
|
18894
|
+
* });
|
|
18895
|
+
*
|
|
18896
|
+
* // CNB
|
|
18897
|
+
* const branches = await service.getBranches('cnb', {
|
|
18898
|
+
* repo: 'genie/genie-ide'
|
|
18899
|
+
* });
|
|
18900
|
+
* ```
|
|
18901
|
+
*/
|
|
18902
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
18903
|
+
try {
|
|
18904
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
18905
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18906
|
+
const apiResponse = await httpService.get(url);
|
|
18907
|
+
if (!apiResponse.data) {
|
|
18908
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
18909
|
+
return [];
|
|
18910
|
+
}
|
|
18911
|
+
const branches = apiResponse.data.branches || [];
|
|
18912
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
18913
|
+
return branches;
|
|
18914
|
+
} catch (error) {
|
|
18915
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
18916
|
+
throw error;
|
|
18917
|
+
}
|
|
18918
|
+
}
|
|
18919
|
+
/**
|
|
18920
|
+
* 获取仓库列表
|
|
18921
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
18922
|
+
*
|
|
18923
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
18924
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
18925
|
+
*
|
|
18926
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18927
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18928
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
18929
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
18930
|
+
* @param perPage 每页数量,最大100
|
|
18931
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
18932
|
+
*
|
|
18933
|
+
* @example
|
|
18934
|
+
* ```typescript
|
|
18935
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
18936
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
18937
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
18938
|
+
*
|
|
18939
|
+
* // Gongfeng
|
|
18940
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
18941
|
+
* // response.gongfeng_repos 是数组
|
|
18942
|
+
*
|
|
18943
|
+
* // CNB
|
|
18944
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
18945
|
+
* // response.cnb_repos 是数组
|
|
18946
|
+
* ```
|
|
18947
|
+
*/
|
|
18948
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
18949
|
+
try {
|
|
18950
|
+
const queryParams = new URLSearchParams();
|
|
18951
|
+
queryParams.append("page", String(page));
|
|
18952
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
18953
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
18954
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18955
|
+
const apiResponse = await httpService.get(url);
|
|
18956
|
+
if (!apiResponse.data) {
|
|
18957
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
18958
|
+
return {};
|
|
18959
|
+
}
|
|
18960
|
+
const response = apiResponse.data;
|
|
18961
|
+
this.logRepositoryCounts(response);
|
|
18962
|
+
return response;
|
|
18963
|
+
} catch (error) {
|
|
18964
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
18965
|
+
throw error;
|
|
18966
|
+
}
|
|
18967
|
+
}
|
|
18968
|
+
/**
|
|
18969
|
+
* 构建分支查询参数
|
|
18970
|
+
*/
|
|
18971
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
18972
|
+
const queryParams = new URLSearchParams();
|
|
18973
|
+
queryParams.append("page", String(page));
|
|
18974
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
18975
|
+
if (connector === "github") {
|
|
18976
|
+
const githubParams = params;
|
|
18977
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
18978
|
+
queryParams.append("owner", githubParams.owner);
|
|
18979
|
+
queryParams.append("repo", githubParams.repo);
|
|
18980
|
+
} else if (connector === "gongfeng") {
|
|
18981
|
+
const gongfengParams = params;
|
|
18982
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
18983
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
18984
|
+
} else if (connector === "cnb") {
|
|
18985
|
+
const cnbParams = params;
|
|
18986
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
18987
|
+
queryParams.append("repo", cnbParams.repo);
|
|
18988
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
18989
|
+
return queryParams;
|
|
18990
|
+
}
|
|
18991
|
+
/**
|
|
18992
|
+
* 记录仓库数量日志
|
|
18993
|
+
*/
|
|
18994
|
+
logRepositoryCounts(response) {
|
|
18995
|
+
if (response.github_repos) {
|
|
18996
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
18997
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
18998
|
+
}
|
|
18999
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
19000
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
19001
|
+
}
|
|
19002
|
+
};
|
|
19003
|
+
/**
|
|
19004
|
+
* OAuth Repository Service 单例实例
|
|
19005
|
+
*/
|
|
19006
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
19007
|
+
|
|
18415
19008
|
//#endregion
|
|
18416
19009
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
18417
19010
|
/**
|
|
@@ -18420,28 +19013,24 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18420
19013
|
* 封装与后端 API 的 HTTP 通信
|
|
18421
19014
|
*/
|
|
18422
19015
|
/**
|
|
18423
|
-
*
|
|
18424
|
-
*
|
|
19016
|
+
* 判断当前账号是否是 SSO 账号
|
|
19017
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
18425
19018
|
*/
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
19019
|
+
const safeParseJSON = (jsonString) => {
|
|
19020
|
+
try {
|
|
19021
|
+
return JSON.parse(jsonString);
|
|
19022
|
+
} catch (error) {
|
|
19023
|
+
return {};
|
|
19024
|
+
}
|
|
18429
19025
|
};
|
|
18430
19026
|
/**
|
|
18431
|
-
*
|
|
18432
|
-
* - SSO
|
|
18433
|
-
* - 非 SSO
|
|
19027
|
+
* 根据路径获取完整 URL
|
|
19028
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
19029
|
+
* - 非 SSO 账号直接使用当前域名
|
|
19030
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
19031
|
+
* @returns 完整的 URL 地址
|
|
18434
19032
|
*/
|
|
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
|
-
};
|
|
19033
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
18445
19034
|
/** 获取当前域名的账号选择页面 URL */
|
|
18446
19035
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
18447
19036
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -18478,12 +19067,30 @@ var BackendProvider = class {
|
|
|
18478
19067
|
constructor(config) {
|
|
18479
19068
|
httpService.setBaseURL(config.baseUrl);
|
|
18480
19069
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
18481
|
-
httpService.onUnauthorized(() =>
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
19070
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
19071
|
+
}
|
|
19072
|
+
/**
|
|
19073
|
+
* 处理 401 未授权错误
|
|
19074
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
19075
|
+
*
|
|
19076
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
19077
|
+
*/
|
|
19078
|
+
async handleUnauthorized() {
|
|
19079
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
19080
|
+
try {
|
|
19081
|
+
if (await this.refreshToken()) {
|
|
19082
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
19083
|
+
return;
|
|
19084
|
+
}
|
|
19085
|
+
throw new Error("Token refresh returned null");
|
|
19086
|
+
} catch (error) {
|
|
19087
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
19088
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
19089
|
+
this.logout().catch((logoutError) => {
|
|
19090
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
18485
19091
|
});
|
|
18486
|
-
|
|
19092
|
+
throw error;
|
|
19093
|
+
}
|
|
18487
19094
|
}
|
|
18488
19095
|
/**
|
|
18489
19096
|
* 获取当前账号信息
|
|
@@ -18528,7 +19135,7 @@ var BackendProvider = class {
|
|
|
18528
19135
|
return account;
|
|
18529
19136
|
}
|
|
18530
19137
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18531
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
19138
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18532
19139
|
accountService.setAccount(null);
|
|
18533
19140
|
return null;
|
|
18534
19141
|
} catch (error) {
|
|
@@ -18551,7 +19158,8 @@ var BackendProvider = class {
|
|
|
18551
19158
|
activeStatus: connector.active_status,
|
|
18552
19159
|
displayName: connector.display_name,
|
|
18553
19160
|
oauthClientId: connector.oauth_client_id,
|
|
18554
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19161
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19162
|
+
oauthAppName: connector.oauth_app_name
|
|
18555
19163
|
})) };
|
|
18556
19164
|
}
|
|
18557
19165
|
throw result;
|
|
@@ -18633,7 +19241,8 @@ var BackendProvider = class {
|
|
|
18633
19241
|
connectStatus: connector.connect_status,
|
|
18634
19242
|
displayName: connector.display_name,
|
|
18635
19243
|
oauthClientId: connector.oauth_client_id,
|
|
18636
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19244
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19245
|
+
oauthAppName: connector.oauth_app_name
|
|
18637
19246
|
})) };
|
|
18638
19247
|
}
|
|
18639
19248
|
throw result;
|
|
@@ -18724,6 +19333,9 @@ var BackendProvider = class {
|
|
|
18724
19333
|
PackageCode: void 0,
|
|
18725
19334
|
name: ""
|
|
18726
19335
|
};
|
|
19336
|
+
const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
|
|
19337
|
+
console.log("[PRODUCT_FEATURES]", productFeatures);
|
|
19338
|
+
if (!productFeatures.billing) return defaultPlan;
|
|
18727
19339
|
try {
|
|
18728
19340
|
const now = /* @__PURE__ */ new Date();
|
|
18729
19341
|
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
@@ -18745,7 +19357,7 @@ var BackendProvider = class {
|
|
|
18745
19357
|
if (!time) return 0;
|
|
18746
19358
|
return new Date(time).getTime();
|
|
18747
19359
|
};
|
|
18748
|
-
const dailyCredits = [CommodityCode.free
|
|
19360
|
+
const dailyCredits = [CommodityCode.free];
|
|
18749
19361
|
const planResources = resources.map((r) => {
|
|
18750
19362
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
18751
19363
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
@@ -18766,10 +19378,11 @@ var BackendProvider = class {
|
|
|
18766
19378
|
CommodityCode.proMon,
|
|
18767
19379
|
CommodityCode.proMonPlus,
|
|
18768
19380
|
CommodityCode.proYear,
|
|
19381
|
+
CommodityCode.freeMon,
|
|
18769
19382
|
CommodityCode.extra
|
|
18770
19383
|
].includes(code)) return 1;
|
|
18771
19384
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
18772
|
-
if ([CommodityCode.free
|
|
19385
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
18773
19386
|
return 4;
|
|
18774
19387
|
};
|
|
18775
19388
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -18890,38 +19503,69 @@ var BackendProvider = class {
|
|
|
18890
19503
|
*/
|
|
18891
19504
|
async login() {
|
|
18892
19505
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18893
|
-
window.location.href = `${
|
|
19506
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18894
19507
|
}
|
|
18895
19508
|
/**
|
|
18896
19509
|
* 登出账号
|
|
18897
|
-
*
|
|
19510
|
+
*
|
|
19511
|
+
* 策略:
|
|
19512
|
+
* - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
|
|
19513
|
+
* - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
|
|
18898
19514
|
*/
|
|
18899
19515
|
async logout() {
|
|
18900
|
-
const
|
|
19516
|
+
const account = accountService.getAccount();
|
|
19517
|
+
if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
|
|
19518
|
+
else await this.logoutViaHttp();
|
|
19519
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
19520
|
+
accountService.clearAccount();
|
|
19521
|
+
}
|
|
19522
|
+
/**
|
|
19523
|
+
* IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
|
|
19524
|
+
* 轮询 iframe URL 变化检测完成,兜底超时 5 秒
|
|
19525
|
+
*/
|
|
19526
|
+
async logoutViaIframe() {
|
|
19527
|
+
const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
|
|
18901
19528
|
try {
|
|
18902
19529
|
await new Promise((resolve) => {
|
|
18903
19530
|
const iframe = document.createElement("iframe");
|
|
18904
19531
|
iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
|
|
18905
|
-
iframe.src =
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
19532
|
+
iframe.src = logoutUrl;
|
|
19533
|
+
let pollTimer;
|
|
19534
|
+
let settled = false;
|
|
19535
|
+
const done = () => {
|
|
19536
|
+
if (settled) return;
|
|
19537
|
+
settled = true;
|
|
19538
|
+
clearInterval(pollTimer);
|
|
18911
19539
|
clearTimeout(timeout);
|
|
18912
19540
|
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
18913
|
-
};
|
|
18914
|
-
iframe.onerror = () => {
|
|
18915
|
-
cleanup();
|
|
18916
19541
|
resolve();
|
|
18917
19542
|
};
|
|
19543
|
+
let wasRedirecting = false;
|
|
19544
|
+
pollTimer = setInterval(() => {
|
|
19545
|
+
try {
|
|
19546
|
+
const href = iframe.contentWindow?.location?.href;
|
|
19547
|
+
if (wasRedirecting && href) done();
|
|
19548
|
+
} catch {
|
|
19549
|
+
wasRedirecting = true;
|
|
19550
|
+
}
|
|
19551
|
+
}, 100);
|
|
19552
|
+
const timeout = setTimeout(done, 5e3);
|
|
19553
|
+
iframe.onerror = done;
|
|
18918
19554
|
document.body.appendChild(iframe);
|
|
18919
19555
|
});
|
|
18920
19556
|
} catch (error) {
|
|
18921
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
19557
|
+
console.error("[BackendProvider] logout via iframe failed:", error);
|
|
19558
|
+
}
|
|
19559
|
+
}
|
|
19560
|
+
/**
|
|
19561
|
+
* 非 IOA 企业登出:直接 HTTP 请求 /console/logout
|
|
19562
|
+
*/
|
|
19563
|
+
async logoutViaHttp() {
|
|
19564
|
+
try {
|
|
19565
|
+
await httpService.get("/console/logout");
|
|
19566
|
+
} catch (error) {
|
|
19567
|
+
console.error("[BackendProvider] logout via http failed:", error);
|
|
18922
19568
|
}
|
|
18923
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
18924
|
-
accountService.clearAccount();
|
|
18925
19569
|
}
|
|
18926
19570
|
/**
|
|
18927
19571
|
* 批量切换插件状态
|
|
@@ -18953,6 +19597,52 @@ var BackendProvider = class {
|
|
|
18953
19597
|
return null;
|
|
18954
19598
|
}
|
|
18955
19599
|
}
|
|
19600
|
+
/**
|
|
19601
|
+
* 刷新 Token
|
|
19602
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
19603
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
19604
|
+
*/
|
|
19605
|
+
async refreshToken() {
|
|
19606
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
19607
|
+
try {
|
|
19608
|
+
const account = await this.getAccount();
|
|
19609
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
19610
|
+
return account;
|
|
19611
|
+
} catch (error) {
|
|
19612
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
19613
|
+
return null;
|
|
19614
|
+
}
|
|
19615
|
+
}
|
|
19616
|
+
/**
|
|
19617
|
+
* 获取仓库分支列表
|
|
19618
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
19619
|
+
*
|
|
19620
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19621
|
+
* @param params 平台特定的查询参数
|
|
19622
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19623
|
+
* @param perPage 每页数量,最大100
|
|
19624
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
19625
|
+
*/
|
|
19626
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
19627
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
19628
|
+
}
|
|
19629
|
+
/**
|
|
19630
|
+
* 获取仓库列表
|
|
19631
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
19632
|
+
*
|
|
19633
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
19634
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
19635
|
+
*
|
|
19636
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19637
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19638
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
19639
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
19640
|
+
* @param perPage 每页数量,最大100
|
|
19641
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
19642
|
+
*/
|
|
19643
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
19644
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
19645
|
+
}
|
|
18956
19646
|
};
|
|
18957
19647
|
/**
|
|
18958
19648
|
* 创建 BackendProvider 实例
|
|
@@ -18992,6 +19682,7 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
18992
19682
|
GET_FILE: "backend:get-file",
|
|
18993
19683
|
RELOAD_WINDOW: "backend:reload-window",
|
|
18994
19684
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
19685
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
18995
19686
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
18996
19687
|
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
18997
19688
|
};
|
|
@@ -19287,6 +19978,20 @@ var IPCBackendProvider = class {
|
|
|
19287
19978
|
}
|
|
19288
19979
|
}
|
|
19289
19980
|
/**
|
|
19981
|
+
* 在外部浏览器中打开链接
|
|
19982
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
19983
|
+
* @param url 要打开的 URL
|
|
19984
|
+
*/
|
|
19985
|
+
async openExternal(url) {
|
|
19986
|
+
this.log("Opening external URL via IPC:", url);
|
|
19987
|
+
try {
|
|
19988
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
19989
|
+
} catch (error) {
|
|
19990
|
+
this.log("Open external request failed:", error);
|
|
19991
|
+
throw error;
|
|
19992
|
+
}
|
|
19993
|
+
}
|
|
19994
|
+
/**
|
|
19290
19995
|
* 批量切换插件状态
|
|
19291
19996
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
19292
19997
|
*/
|