@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.2bf8b02.20260209
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 +714 -140
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +719 -100
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +719 -100
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +714 -140
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.13-next.2bf8b02.20260209.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
|
|
@@ -2283,6 +2285,24 @@ var CloudAgentConnection = class {
|
|
|
2283
2285
|
},
|
|
2284
2286
|
onUsageUpdate: (usage) => {
|
|
2285
2287
|
this.emit("usageUpdate", usage);
|
|
2288
|
+
},
|
|
2289
|
+
onExtNotification: (method, params) => {
|
|
2290
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2291
|
+
method,
|
|
2292
|
+
paramsKeys: Object.keys(params)
|
|
2293
|
+
});
|
|
2294
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2295
|
+
const action = params.action;
|
|
2296
|
+
const commandParams = params.params;
|
|
2297
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2298
|
+
action,
|
|
2299
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2300
|
+
});
|
|
2301
|
+
this.emit("command", {
|
|
2302
|
+
action,
|
|
2303
|
+
params: commandParams
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2286
2306
|
}
|
|
2287
2307
|
});
|
|
2288
2308
|
this.setupEventForwarding();
|
|
@@ -2411,7 +2431,7 @@ var CloudAgentConnection = class {
|
|
|
2411
2431
|
}
|
|
2412
2432
|
async createSession(params) {
|
|
2413
2433
|
return {
|
|
2414
|
-
...await this.client.
|
|
2434
|
+
...await this.client.createSession(this.cwd),
|
|
2415
2435
|
sessionId: this.agentId
|
|
2416
2436
|
};
|
|
2417
2437
|
}
|
|
@@ -2529,6 +2549,16 @@ var CloudAgentConnection = class {
|
|
|
2529
2549
|
get sessionConnectionInfo() {
|
|
2530
2550
|
return this._sessionConnectionInfo;
|
|
2531
2551
|
}
|
|
2552
|
+
async reportTelemetry(eventName, payload) {
|
|
2553
|
+
try {
|
|
2554
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2555
|
+
eventName,
|
|
2556
|
+
payload
|
|
2557
|
+
});
|
|
2558
|
+
} catch (error) {
|
|
2559
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2532
2562
|
async extMethod(method, params) {
|
|
2533
2563
|
return this.client.extMethod(method, params);
|
|
2534
2564
|
}
|
|
@@ -15923,7 +15953,7 @@ var axios_default = axios;
|
|
|
15923
15953
|
* 特性:
|
|
15924
15954
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
15925
15955
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
15926
|
-
* - 统一 401
|
|
15956
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
15927
15957
|
* - 自动携带凭证(withCredentials)
|
|
15928
15958
|
* - 类型安全
|
|
15929
15959
|
*
|
|
@@ -15963,6 +15993,8 @@ var HttpService = class HttpService {
|
|
|
15963
15993
|
*/
|
|
15964
15994
|
constructor(config = {}) {
|
|
15965
15995
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
15996
|
+
this.isRefreshing = false;
|
|
15997
|
+
this.refreshSubscribers = [];
|
|
15966
15998
|
this.config = config;
|
|
15967
15999
|
this.axiosInstance = axios_default.create({
|
|
15968
16000
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -16003,18 +16035,54 @@ var HttpService = class HttpService {
|
|
|
16003
16035
|
}, (error) => Promise.reject(error));
|
|
16004
16036
|
}
|
|
16005
16037
|
/**
|
|
16006
|
-
* 注册默认响应拦截器(处理 401
|
|
16038
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
16007
16039
|
*/
|
|
16008
16040
|
registerDefaultResponseInterceptor() {
|
|
16009
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16041
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
16042
|
+
const originalRequest = error.config;
|
|
16043
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
16044
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
16045
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
16046
|
+
return Promise.reject(error);
|
|
16047
|
+
}
|
|
16048
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
16049
|
+
originalRequest._retry = true;
|
|
16050
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
16051
|
+
this.refreshSubscribers.push((success) => {
|
|
16052
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
16053
|
+
else reject(error);
|
|
16054
|
+
});
|
|
16055
|
+
});
|
|
16056
|
+
this.isRefreshing = true;
|
|
16057
|
+
try {
|
|
16058
|
+
await this.triggerUnauthorizedCallbacks();
|
|
16059
|
+
this.onRefreshSuccess();
|
|
16060
|
+
return this.axiosInstance.request(originalRequest);
|
|
16061
|
+
} catch (refreshError) {
|
|
16062
|
+
this.onRefreshFailure();
|
|
16063
|
+
return Promise.reject(error);
|
|
16064
|
+
} finally {
|
|
16065
|
+
this.isRefreshing = false;
|
|
16066
|
+
}
|
|
16013
16067
|
}
|
|
16014
16068
|
return Promise.reject(error);
|
|
16015
16069
|
});
|
|
16016
16070
|
}
|
|
16017
16071
|
/**
|
|
16072
|
+
* token 刷新成功,通知所有等待的请求
|
|
16073
|
+
*/
|
|
16074
|
+
onRefreshSuccess() {
|
|
16075
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
16076
|
+
this.refreshSubscribers = [];
|
|
16077
|
+
}
|
|
16078
|
+
/**
|
|
16079
|
+
* token 刷新失败,通知所有等待的请求
|
|
16080
|
+
*/
|
|
16081
|
+
onRefreshFailure() {
|
|
16082
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
16083
|
+
this.refreshSubscribers = [];
|
|
16084
|
+
}
|
|
16085
|
+
/**
|
|
16018
16086
|
* 注册请求拦截器
|
|
16019
16087
|
* @param onFulfilled 请求成功拦截器
|
|
16020
16088
|
* @param onRejected 请求失败拦截器
|
|
@@ -16091,16 +16159,18 @@ var HttpService = class HttpService {
|
|
|
16091
16159
|
this.unauthorizedCallbacks.delete(callback);
|
|
16092
16160
|
}
|
|
16093
16161
|
/**
|
|
16094
|
-
* 触发所有 401
|
|
16162
|
+
* 触发所有 401 回调并等待完成
|
|
16163
|
+
* @returns Promise,等待所有回调完成
|
|
16164
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
16095
16165
|
*/
|
|
16096
|
-
triggerUnauthorizedCallbacks() {
|
|
16097
|
-
this.unauthorizedCallbacks
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
|
|
16103
|
-
}
|
|
16166
|
+
async triggerUnauthorizedCallbacks() {
|
|
16167
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
16168
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
16169
|
+
if (failedResults.length > 0) {
|
|
16170
|
+
const errors = failedResults.map((r) => r.reason);
|
|
16171
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
16172
|
+
throw errors[0];
|
|
16173
|
+
}
|
|
16104
16174
|
}
|
|
16105
16175
|
/**
|
|
16106
16176
|
* 更新 authToken
|
|
@@ -16212,6 +16282,7 @@ var AccountService = class {
|
|
|
16212
16282
|
this.initPromise = null;
|
|
16213
16283
|
this.initResolve = null;
|
|
16214
16284
|
this.requestInterceptorId = null;
|
|
16285
|
+
this.crossTabBroadcaster = null;
|
|
16215
16286
|
this.initPromise = new Promise((resolve) => {
|
|
16216
16287
|
this.initResolve = resolve;
|
|
16217
16288
|
});
|
|
@@ -16260,15 +16331,34 @@ var AccountService = class {
|
|
|
16260
16331
|
this.initialized = true;
|
|
16261
16332
|
this.initResolve?.(account);
|
|
16262
16333
|
}
|
|
16263
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
16334
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
16335
|
+
this.notifyListeners();
|
|
16336
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
16337
|
+
}
|
|
16264
16338
|
}
|
|
16265
16339
|
/**
|
|
16266
16340
|
* 清除账号(登出)
|
|
16341
|
+
* 先广播登出消息,再清除本地账号
|
|
16267
16342
|
*/
|
|
16268
16343
|
clearAccount() {
|
|
16344
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
16345
|
+
this.setAccount(null);
|
|
16346
|
+
}
|
|
16347
|
+
/**
|
|
16348
|
+
* 静默清除账号(不广播)
|
|
16349
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
16350
|
+
*/
|
|
16351
|
+
clearAccountSilently() {
|
|
16269
16352
|
this.setAccount(null);
|
|
16270
16353
|
}
|
|
16271
16354
|
/**
|
|
16355
|
+
* 设置跨标签页认证同步广播器
|
|
16356
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
16357
|
+
*/
|
|
16358
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
16359
|
+
this.crossTabBroadcaster = broadcaster;
|
|
16360
|
+
}
|
|
16361
|
+
/**
|
|
16272
16362
|
* 订阅账号变化
|
|
16273
16363
|
* @param callback 变化时的回调函数
|
|
16274
16364
|
* @returns 取消订阅函数
|
|
@@ -16333,6 +16423,11 @@ var AccountService = class {
|
|
|
16333
16423
|
* 导出单例实例
|
|
16334
16424
|
*/
|
|
16335
16425
|
const accountService = new AccountService();
|
|
16426
|
+
/**
|
|
16427
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
16428
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
16429
|
+
*/
|
|
16430
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
16336
16431
|
|
|
16337
16432
|
//#endregion
|
|
16338
16433
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -16435,20 +16530,32 @@ var CosUploadService = class {
|
|
|
16435
16530
|
* 上传单个文件到 COS
|
|
16436
16531
|
*
|
|
16437
16532
|
* @param file - 要上传的文件
|
|
16533
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16438
16534
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
16439
16535
|
*/
|
|
16440
|
-
async uploadFile(file) {
|
|
16536
|
+
async uploadFile(file, abortSignal) {
|
|
16441
16537
|
const filename = file.name;
|
|
16442
16538
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
16443
16539
|
try {
|
|
16540
|
+
if (abortSignal?.aborted) return {
|
|
16541
|
+
success: false,
|
|
16542
|
+
error: "Upload cancelled",
|
|
16543
|
+
aborted: true
|
|
16544
|
+
};
|
|
16444
16545
|
const objectKey = this.generateObjectKey(filename);
|
|
16445
16546
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
16446
16547
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
16447
16548
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
16549
|
+
if (abortSignal?.aborted) return {
|
|
16550
|
+
success: false,
|
|
16551
|
+
error: "Upload cancelled",
|
|
16552
|
+
aborted: true
|
|
16553
|
+
};
|
|
16448
16554
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16449
16555
|
method: "PUT",
|
|
16450
16556
|
body: file,
|
|
16451
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16557
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16558
|
+
signal: abortSignal
|
|
16452
16559
|
});
|
|
16453
16560
|
if (!uploadResponse.ok) {
|
|
16454
16561
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16462,6 +16569,11 @@ var CosUploadService = class {
|
|
|
16462
16569
|
objectKey
|
|
16463
16570
|
};
|
|
16464
16571
|
} catch (error) {
|
|
16572
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16573
|
+
success: false,
|
|
16574
|
+
error: "Upload cancelled",
|
|
16575
|
+
aborted: true
|
|
16576
|
+
};
|
|
16465
16577
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
16466
16578
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
16467
16579
|
return {
|
|
@@ -16476,14 +16588,25 @@ var CosUploadService = class {
|
|
|
16476
16588
|
* 使用并发控制,限制同时上传的文件数量
|
|
16477
16589
|
*
|
|
16478
16590
|
* @param files - 要上传的文件数组
|
|
16591
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16479
16592
|
* @returns 所有文件的上传结果
|
|
16480
16593
|
*/
|
|
16481
|
-
async uploadFiles(files) {
|
|
16594
|
+
async uploadFiles(files, abortSignal) {
|
|
16482
16595
|
if (files.length === 0) return {
|
|
16483
16596
|
success: true,
|
|
16484
16597
|
urls: [],
|
|
16485
16598
|
results: []
|
|
16486
16599
|
};
|
|
16600
|
+
if (abortSignal?.aborted) return {
|
|
16601
|
+
success: false,
|
|
16602
|
+
error: "Upload cancelled",
|
|
16603
|
+
aborted: true,
|
|
16604
|
+
results: files.map(() => ({
|
|
16605
|
+
success: false,
|
|
16606
|
+
error: "Upload cancelled",
|
|
16607
|
+
aborted: true
|
|
16608
|
+
}))
|
|
16609
|
+
};
|
|
16487
16610
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
16488
16611
|
try {
|
|
16489
16612
|
const fileInfos = files.map((file) => ({
|
|
@@ -16495,6 +16618,12 @@ var CosUploadService = class {
|
|
|
16495
16618
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
16496
16619
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
16497
16620
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
16621
|
+
if (abortSignal?.aborted) return {
|
|
16622
|
+
success: false,
|
|
16623
|
+
error: "Upload cancelled",
|
|
16624
|
+
aborted: true,
|
|
16625
|
+
objectKey: fileInfos[index].objectKey
|
|
16626
|
+
};
|
|
16498
16627
|
const presignedItem = presignedResponse.items[index];
|
|
16499
16628
|
if (!presignedItem) return {
|
|
16500
16629
|
success: false,
|
|
@@ -16505,7 +16634,8 @@ var CosUploadService = class {
|
|
|
16505
16634
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16506
16635
|
method: "PUT",
|
|
16507
16636
|
body: file,
|
|
16508
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16637
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16638
|
+
signal: abortSignal
|
|
16509
16639
|
});
|
|
16510
16640
|
if (!uploadResponse.ok) {
|
|
16511
16641
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16522,6 +16652,12 @@ var CosUploadService = class {
|
|
|
16522
16652
|
objectKey: presignedItem.object_key
|
|
16523
16653
|
};
|
|
16524
16654
|
} catch (error) {
|
|
16655
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16656
|
+
success: false,
|
|
16657
|
+
error: "Upload cancelled",
|
|
16658
|
+
aborted: true,
|
|
16659
|
+
objectKey: presignedItem.object_key
|
|
16660
|
+
};
|
|
16525
16661
|
return {
|
|
16526
16662
|
success: false,
|
|
16527
16663
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -16922,15 +17058,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16922
17058
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
16923
17059
|
const apiResponse = await httpService.get(url);
|
|
16924
17060
|
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
17061
|
return {
|
|
16932
|
-
agents,
|
|
16933
|
-
pagination
|
|
17062
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
17063
|
+
pagination: apiResponse.data.pagination
|
|
16934
17064
|
};
|
|
16935
17065
|
} catch (error) {
|
|
16936
17066
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -16940,13 +17070,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16940
17070
|
/**
|
|
16941
17071
|
* Create a new conversation
|
|
16942
17072
|
* POST {endpoint}/console/as/conversations
|
|
17073
|
+
* @param params - Session params containing cwd and optional configuration
|
|
16943
17074
|
*/
|
|
16944
|
-
async create() {
|
|
17075
|
+
async create(params) {
|
|
16945
17076
|
try {
|
|
16946
|
-
const
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
});
|
|
17077
|
+
const { options = {} } = params;
|
|
17078
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
17079
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
17080
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
17081
|
+
const createPayload = {
|
|
17082
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
17083
|
+
model: options.model || "deepseek-r1",
|
|
17084
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
17085
|
+
};
|
|
17086
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
17087
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
16950
17088
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
16951
17089
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
16952
17090
|
return apiResponse.data.id;
|
|
@@ -17146,9 +17284,12 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17146
17284
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
17147
17285
|
return [];
|
|
17148
17286
|
}
|
|
17149
|
-
const
|
|
17150
|
-
|
|
17151
|
-
|
|
17287
|
+
const productConfig = apiResponse.data;
|
|
17288
|
+
const allModels = productConfig.models ?? [];
|
|
17289
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
17290
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
17291
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
17292
|
+
return filteredModels.map((model) => ({
|
|
17152
17293
|
id: model.id,
|
|
17153
17294
|
name: model.name ?? model.id,
|
|
17154
17295
|
description: model.description,
|
|
@@ -17252,7 +17393,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17252
17393
|
/**
|
|
17253
17394
|
* Upload files to cloud storage via COS presigned URL
|
|
17254
17395
|
*
|
|
17255
|
-
* @param params - files array (File objects in browser)
|
|
17396
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
17256
17397
|
* @returns Response with corresponding cloud URLs
|
|
17257
17398
|
*/
|
|
17258
17399
|
async uploadFile(params) {
|
|
@@ -17262,12 +17403,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17262
17403
|
success: false,
|
|
17263
17404
|
error: "No valid File objects provided"
|
|
17264
17405
|
};
|
|
17265
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
17406
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
17266
17407
|
return {
|
|
17267
17408
|
success: result.success,
|
|
17268
17409
|
urls: result.urls,
|
|
17269
17410
|
expireSeconds: result.expireSeconds,
|
|
17270
|
-
error: result.error
|
|
17411
|
+
error: result.error,
|
|
17412
|
+
aborted: result.aborted
|
|
17271
17413
|
};
|
|
17272
17414
|
}
|
|
17273
17415
|
/**
|
|
@@ -17309,20 +17451,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17309
17451
|
}
|
|
17310
17452
|
/**
|
|
17311
17453
|
* 获取支持的场景列表
|
|
17312
|
-
* API 端点: GET /
|
|
17454
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
17313
17455
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
17314
17456
|
*
|
|
17457
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
17315
17458
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
17316
17459
|
*/
|
|
17317
|
-
async getSupportScenes() {
|
|
17460
|
+
async getSupportScenes(locale) {
|
|
17318
17461
|
try {
|
|
17319
|
-
const
|
|
17462
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
17463
|
+
const apiResponse = await httpService.get(url);
|
|
17320
17464
|
if (!apiResponse.data) {
|
|
17321
17465
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
17322
17466
|
return [];
|
|
17323
17467
|
}
|
|
17324
17468
|
const scenes = apiResponse.data.scenes || [];
|
|
17325
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
17469
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
17326
17470
|
return scenes;
|
|
17327
17471
|
} catch (error) {
|
|
17328
17472
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -17338,7 +17482,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17338
17482
|
type: "cloud",
|
|
17339
17483
|
status,
|
|
17340
17484
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
17341
|
-
capabilities: this.options.clientCapabilities
|
|
17485
|
+
capabilities: this.options.clientCapabilities,
|
|
17486
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
17342
17487
|
};
|
|
17343
17488
|
}
|
|
17344
17489
|
/**
|
|
@@ -17354,6 +17499,23 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17354
17499
|
const queryString = searchParams.toString();
|
|
17355
17500
|
return queryString ? `${path}?${queryString}` : path;
|
|
17356
17501
|
}
|
|
17502
|
+
/**
|
|
17503
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
17504
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
17505
|
+
*/
|
|
17506
|
+
async reportTelemetry(eventName, payload) {
|
|
17507
|
+
try {
|
|
17508
|
+
const events = [{
|
|
17509
|
+
eventCode: eventName,
|
|
17510
|
+
timestamp: Date.now(),
|
|
17511
|
+
reportDelay: 0,
|
|
17512
|
+
...payload
|
|
17513
|
+
}];
|
|
17514
|
+
await httpService.post("/v2/report", events);
|
|
17515
|
+
} catch (error) {
|
|
17516
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
17517
|
+
}
|
|
17518
|
+
}
|
|
17357
17519
|
};
|
|
17358
17520
|
|
|
17359
17521
|
//#endregion
|
|
@@ -17402,6 +17564,7 @@ var ActiveSessionImpl = class {
|
|
|
17402
17564
|
this._availableCommands = [];
|
|
17403
17565
|
this.listeners = /* @__PURE__ */ new Map();
|
|
17404
17566
|
this.onceListeners = /* @__PURE__ */ new Map();
|
|
17567
|
+
this.connectionListeners = [];
|
|
17405
17568
|
this._id = sessionId;
|
|
17406
17569
|
this._agentId = agentId;
|
|
17407
17570
|
this.connection = connection;
|
|
@@ -17427,6 +17590,18 @@ var ActiveSessionImpl = class {
|
|
|
17427
17590
|
return this._agentId;
|
|
17428
17591
|
}
|
|
17429
17592
|
/**
|
|
17593
|
+
* Actual workspace path (set from newSession response _meta)
|
|
17594
|
+
*/
|
|
17595
|
+
get cwd() {
|
|
17596
|
+
return this._cwd;
|
|
17597
|
+
}
|
|
17598
|
+
/**
|
|
17599
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
17600
|
+
*/
|
|
17601
|
+
setCwd(cwd) {
|
|
17602
|
+
this._cwd = cwd;
|
|
17603
|
+
}
|
|
17604
|
+
/**
|
|
17430
17605
|
* Agent state (live connection state)
|
|
17431
17606
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
17432
17607
|
*/
|
|
@@ -17643,8 +17818,8 @@ var ActiveSessionImpl = class {
|
|
|
17643
17818
|
* await session.setMode('architect');
|
|
17644
17819
|
* ```
|
|
17645
17820
|
*/
|
|
17646
|
-
async setMode(modeId) {
|
|
17647
|
-
if (this._availableModes) {
|
|
17821
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
17822
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
17648
17823
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
17649
17824
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
17650
17825
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -17744,6 +17919,7 @@ var ActiveSessionImpl = class {
|
|
|
17744
17919
|
* Disconnect from the session/agent
|
|
17745
17920
|
*/
|
|
17746
17921
|
disconnect() {
|
|
17922
|
+
this.removeConnectionListeners();
|
|
17747
17923
|
this.connection.disconnect();
|
|
17748
17924
|
this.removeAllListeners();
|
|
17749
17925
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
@@ -17767,60 +17943,80 @@ var ActiveSessionImpl = class {
|
|
|
17767
17943
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
17768
17944
|
return this.connection;
|
|
17769
17945
|
}
|
|
17946
|
+
/**
|
|
17947
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
17948
|
+
*/
|
|
17949
|
+
addConnectionListener(connection, event, listener) {
|
|
17950
|
+
connection.on(event, listener);
|
|
17951
|
+
this.connectionListeners.push({
|
|
17952
|
+
event,
|
|
17953
|
+
listener
|
|
17954
|
+
});
|
|
17955
|
+
}
|
|
17956
|
+
/**
|
|
17957
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
17958
|
+
*/
|
|
17959
|
+
removeConnectionListeners() {
|
|
17960
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
17961
|
+
this.connectionListeners = [];
|
|
17962
|
+
}
|
|
17770
17963
|
setupConnectionEvents(connection) {
|
|
17771
|
-
|
|
17964
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
17772
17965
|
this.emit("connected", void 0);
|
|
17773
17966
|
});
|
|
17774
|
-
|
|
17967
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
17775
17968
|
this.emit("disconnected", void 0);
|
|
17776
17969
|
});
|
|
17777
|
-
|
|
17970
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
17778
17971
|
this.emit("error", error);
|
|
17779
17972
|
});
|
|
17780
|
-
|
|
17973
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
17781
17974
|
this.emit("sessionUpdate", update);
|
|
17782
17975
|
});
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
artifactUri: artifact.uri,
|
|
17786
|
-
artifactType: artifact.type
|
|
17787
|
-
});
|
|
17976
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
17977
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17788
17978
|
this.emit("artifactCreated", artifact);
|
|
17789
17979
|
});
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
artifactUri: artifact.uri,
|
|
17793
|
-
artifactType: artifact.type
|
|
17794
|
-
});
|
|
17980
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
17981
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17795
17982
|
this.emit("artifactUpdated", artifact);
|
|
17796
17983
|
});
|
|
17797
|
-
|
|
17798
|
-
|
|
17984
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
17985
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17799
17986
|
this.emit("artifactDeleted", artifact);
|
|
17800
17987
|
});
|
|
17801
|
-
|
|
17988
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
17802
17989
|
this.emit("permissionRequest", request);
|
|
17803
17990
|
});
|
|
17804
|
-
|
|
17991
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
17805
17992
|
this.emit("questionRequest", request);
|
|
17806
17993
|
});
|
|
17807
|
-
|
|
17994
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
17808
17995
|
this.prompts.cancel();
|
|
17809
17996
|
});
|
|
17810
|
-
|
|
17997
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
17811
17998
|
this.emit("usageUpdate", usage);
|
|
17812
17999
|
});
|
|
17813
|
-
|
|
18000
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
18001
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18002
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17814
18003
|
this.emit("checkpointCreated", checkpoint);
|
|
17815
18004
|
});
|
|
17816
|
-
|
|
18005
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
18006
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18007
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17817
18008
|
this.emit("checkpointUpdated", checkpoint);
|
|
17818
18009
|
});
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
18010
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
18011
|
+
const originSessionId = command.__sessionId;
|
|
18012
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
18013
|
+
console.log("[Session] Command not forwarded:", {
|
|
18014
|
+
command,
|
|
18015
|
+
originSessionId,
|
|
18016
|
+
sessionId: this._id
|
|
18017
|
+
});
|
|
18018
|
+
return;
|
|
18019
|
+
}
|
|
17824
18020
|
this.emit("command", command);
|
|
17825
18021
|
});
|
|
17826
18022
|
}
|
|
@@ -17830,19 +18026,38 @@ var ActiveSessionImpl = class {
|
|
|
17830
18026
|
_meta: response._meta ?? void 0
|
|
17831
18027
|
};
|
|
17832
18028
|
}
|
|
18029
|
+
/**
|
|
18030
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
18031
|
+
* - media 类型:按 cwd 路径隔离(同 cwd 下不同 session 可共享 media)
|
|
18032
|
+
* - 其余类型:按 __sessionId 严格隔离
|
|
18033
|
+
*/
|
|
18034
|
+
shouldForwardArtifact(artifact) {
|
|
18035
|
+
const originSessionId = artifact.__sessionId;
|
|
18036
|
+
console.log("[Session] shouldForwardArtifact:", {
|
|
18037
|
+
artifact,
|
|
18038
|
+
originSessionId,
|
|
18039
|
+
sessionId: this._id,
|
|
18040
|
+
cwd: this.connection?.cwd
|
|
18041
|
+
});
|
|
18042
|
+
if (artifact.type === "media") {
|
|
18043
|
+
const cwd = this.connection?.cwd;
|
|
18044
|
+
const uri = artifact?.uri;
|
|
18045
|
+
if (cwd && uri) {
|
|
18046
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
18047
|
+
const uriPath = toPosix(uri.replace(/^(?:file|agent):\/\//, ""));
|
|
18048
|
+
const posixCwd = toPosix(cwd);
|
|
18049
|
+
const normalizedCwd = posixCwd.endsWith("/") ? posixCwd : posixCwd + "/";
|
|
18050
|
+
return uriPath.startsWith(normalizedCwd);
|
|
18051
|
+
}
|
|
18052
|
+
}
|
|
18053
|
+
if (originSessionId && originSessionId !== this._id) return false;
|
|
18054
|
+
return true;
|
|
18055
|
+
}
|
|
17833
18056
|
};
|
|
17834
18057
|
|
|
17835
18058
|
//#endregion
|
|
17836
18059
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
17837
18060
|
/**
|
|
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
18061
|
* SessionManager - Session lifecycle management
|
|
17847
18062
|
*
|
|
17848
18063
|
* This class manages the relationship between sessions and agents.
|
|
@@ -17892,7 +18107,8 @@ var SessionManager = class {
|
|
|
17892
18107
|
createdAt: agent.createdAt,
|
|
17893
18108
|
lastActivityAt: agent.updatedAt,
|
|
17894
18109
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
17895
|
-
isPlayground: agent.isPlayground
|
|
18110
|
+
isPlayground: agent.isPlayground,
|
|
18111
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
17896
18112
|
}));
|
|
17897
18113
|
console.log("[SessionManager] Returning sessions:", {
|
|
17898
18114
|
count: sessions.length,
|
|
@@ -17919,13 +18135,26 @@ var SessionManager = class {
|
|
|
17919
18135
|
if (this.provider.create) {
|
|
17920
18136
|
agentId = await this.provider.create(params);
|
|
17921
18137
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
18138
|
+
if (params.options?.onSessionPrepared) {
|
|
18139
|
+
const initialPrompt = params.options?.prompt;
|
|
18140
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
18141
|
+
params.options.onSessionPrepared({
|
|
18142
|
+
id: agentId,
|
|
18143
|
+
agentId,
|
|
18144
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
18145
|
+
status: "connecting",
|
|
18146
|
+
cwd: params.cwd || "",
|
|
18147
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
18148
|
+
});
|
|
18149
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
18150
|
+
}
|
|
17922
18151
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
17923
18152
|
const connection = await this.provider.connect(agentId);
|
|
17924
18153
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
17925
18154
|
const response = await connection.createSession({
|
|
17926
|
-
_meta: params._meta,
|
|
18155
|
+
_meta: params.options?._meta,
|
|
17927
18156
|
cwd: params.cwd,
|
|
17928
|
-
mcpServers: params.mcpServers
|
|
18157
|
+
mcpServers: params.options?.mcpServers
|
|
17929
18158
|
});
|
|
17930
18159
|
if (this.provider.registerSession) {
|
|
17931
18160
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -17938,14 +18167,10 @@ var SessionManager = class {
|
|
|
17938
18167
|
connectionInfo
|
|
17939
18168
|
});
|
|
17940
18169
|
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
|
-
}
|
|
18170
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18171
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
18172
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
18173
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
17949
18174
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
17950
18175
|
return session;
|
|
17951
18176
|
}
|
|
@@ -17981,17 +18206,31 @@ var SessionManager = class {
|
|
|
17981
18206
|
mcpServers: params.mcpServers
|
|
17982
18207
|
});
|
|
17983
18208
|
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
|
-
}
|
|
18209
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18210
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
17992
18211
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
17993
18212
|
return session;
|
|
17994
18213
|
}
|
|
18214
|
+
/**
|
|
18215
|
+
* 从 ACP response 中提取可用模型列表
|
|
18216
|
+
*
|
|
18217
|
+
* 优先级:
|
|
18218
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
18219
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
18220
|
+
* 3. undefined - 都没有时返回 undefined
|
|
18221
|
+
*
|
|
18222
|
+
* @param response - ACP 响应对象
|
|
18223
|
+
* @returns ModelInfo[] | undefined
|
|
18224
|
+
*/
|
|
18225
|
+
extractAvailableModels(response) {
|
|
18226
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
18227
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
18228
|
+
const availableModels = response.models?.availableModels;
|
|
18229
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
18230
|
+
...model,
|
|
18231
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
18232
|
+
}));
|
|
18233
|
+
}
|
|
17995
18234
|
};
|
|
17996
18235
|
|
|
17997
18236
|
//#endregion
|
|
@@ -18264,6 +18503,28 @@ var AgentClient = class {
|
|
|
18264
18503
|
};
|
|
18265
18504
|
}
|
|
18266
18505
|
},
|
|
18506
|
+
getSubagentList: async (params) => {
|
|
18507
|
+
try {
|
|
18508
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
18509
|
+
const result = await this.provider.getSubagentList(params);
|
|
18510
|
+
this.logger?.info("Subagent list retrieved", {
|
|
18511
|
+
resultCount: result.results.length,
|
|
18512
|
+
hasError: !!result.error
|
|
18513
|
+
});
|
|
18514
|
+
return result;
|
|
18515
|
+
}
|
|
18516
|
+
return {
|
|
18517
|
+
results: [],
|
|
18518
|
+
error: "Provider does not support getSubagentList"
|
|
18519
|
+
};
|
|
18520
|
+
} catch (error) {
|
|
18521
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
18522
|
+
return {
|
|
18523
|
+
results: [],
|
|
18524
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18525
|
+
};
|
|
18526
|
+
}
|
|
18527
|
+
},
|
|
18267
18528
|
batchTogglePlugins: async (request) => {
|
|
18268
18529
|
try {
|
|
18269
18530
|
if (this.provider && this.provider.batchTogglePlugins) {
|
|
@@ -18308,10 +18569,10 @@ var AgentClient = class {
|
|
|
18308
18569
|
return [];
|
|
18309
18570
|
}
|
|
18310
18571
|
},
|
|
18311
|
-
installPlugins: async (pluginNames, marketplaceName, installScope) => {
|
|
18572
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
18312
18573
|
try {
|
|
18313
18574
|
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
18314
|
-
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
|
|
18575
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
18315
18576
|
this.logger?.info("Install plugins", {
|
|
18316
18577
|
pluginNames,
|
|
18317
18578
|
marketplaceName,
|
|
@@ -18332,13 +18593,9 @@ var AgentClient = class {
|
|
|
18332
18593
|
};
|
|
18333
18594
|
}
|
|
18334
18595
|
},
|
|
18335
|
-
getSupportScenes: async () => {
|
|
18596
|
+
getSupportScenes: async (locale) => {
|
|
18336
18597
|
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
|
-
}
|
|
18598
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
18342
18599
|
this.logger?.warn("Provider does not support getSupportScenes");
|
|
18343
18600
|
return [];
|
|
18344
18601
|
} catch (error) {
|
|
@@ -18346,6 +18603,94 @@ var AgentClient = class {
|
|
|
18346
18603
|
return [];
|
|
18347
18604
|
}
|
|
18348
18605
|
},
|
|
18606
|
+
getProductScenes: async (locale) => {
|
|
18607
|
+
try {
|
|
18608
|
+
const hasMethod = this.provider && "getProductScenes" in this.provider;
|
|
18609
|
+
const isFunction = hasMethod && typeof this.provider.getProductScenes === "function";
|
|
18610
|
+
this.logger?.warn(`[getProductScenes] provider=${!!this.provider}, hasMethod=${hasMethod}, isFunction=${isFunction}, providerType=${this.provider?.constructor?.name}`);
|
|
18611
|
+
if (isFunction) {
|
|
18612
|
+
const result = await this.provider.getProductScenes(locale);
|
|
18613
|
+
this.logger?.warn(`[getProductScenes] got ${result?.length ?? 0} scenes`);
|
|
18614
|
+
return result;
|
|
18615
|
+
}
|
|
18616
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
18617
|
+
return [];
|
|
18618
|
+
} catch (error) {
|
|
18619
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
18620
|
+
return [];
|
|
18621
|
+
}
|
|
18622
|
+
},
|
|
18623
|
+
getAvailableCommands: async (params) => {
|
|
18624
|
+
try {
|
|
18625
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
18626
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
18627
|
+
this.logger?.info("Got available commands from provider", {
|
|
18628
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
18629
|
+
count: result?.length ?? 0
|
|
18630
|
+
});
|
|
18631
|
+
return result;
|
|
18632
|
+
}
|
|
18633
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
18634
|
+
return [];
|
|
18635
|
+
} catch (error) {
|
|
18636
|
+
this.logger?.error("Failed to get available commands", error);
|
|
18637
|
+
return [];
|
|
18638
|
+
}
|
|
18639
|
+
},
|
|
18640
|
+
reportTelemetry: async (eventName, payload) => {
|
|
18641
|
+
try {
|
|
18642
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
18643
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
18644
|
+
} catch (error) {
|
|
18645
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
18646
|
+
}
|
|
18647
|
+
},
|
|
18648
|
+
respondToSampling: async (sessionId, response) => {
|
|
18649
|
+
try {
|
|
18650
|
+
if (this.provider?.respondToSampling) {
|
|
18651
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
18652
|
+
this.logger?.info("Responded to sampling request", {
|
|
18653
|
+
sessionId,
|
|
18654
|
+
requestId: response.id,
|
|
18655
|
+
approved: response.approved
|
|
18656
|
+
});
|
|
18657
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
18658
|
+
} catch (error) {
|
|
18659
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
18660
|
+
throw error;
|
|
18661
|
+
}
|
|
18662
|
+
},
|
|
18663
|
+
respondToRoots: async (sessionId, response) => {
|
|
18664
|
+
try {
|
|
18665
|
+
if (this.provider?.respondToRoots) {
|
|
18666
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
18667
|
+
this.logger?.info("Responded to roots request", {
|
|
18668
|
+
sessionId,
|
|
18669
|
+
requestId: response.id,
|
|
18670
|
+
approved: response.approved
|
|
18671
|
+
});
|
|
18672
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
18673
|
+
} catch (error) {
|
|
18674
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
18675
|
+
throw error;
|
|
18676
|
+
}
|
|
18677
|
+
},
|
|
18678
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
18679
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
18680
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
18681
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
18682
|
+
}
|
|
18683
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
18684
|
+
return () => {};
|
|
18685
|
+
},
|
|
18686
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
18687
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
18688
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
18689
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
18690
|
+
}
|
|
18691
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
18692
|
+
return () => {};
|
|
18693
|
+
},
|
|
18349
18694
|
models: this.createModelsResource()
|
|
18350
18695
|
};
|
|
18351
18696
|
}
|
|
@@ -18412,6 +18757,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18412
18757
|
return AccountStatus;
|
|
18413
18758
|
}({});
|
|
18414
18759
|
|
|
18760
|
+
//#endregion
|
|
18761
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
18762
|
+
/**
|
|
18763
|
+
* OAuth Repository Service
|
|
18764
|
+
*
|
|
18765
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
18766
|
+
*/
|
|
18767
|
+
/**
|
|
18768
|
+
* OAuth Repository Service
|
|
18769
|
+
*
|
|
18770
|
+
* 提供仓库和分支的查询操作
|
|
18771
|
+
*/
|
|
18772
|
+
var OAuthRepositoryService = class {
|
|
18773
|
+
/**
|
|
18774
|
+
* 获取仓库分支列表
|
|
18775
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
18776
|
+
*
|
|
18777
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18778
|
+
* @param params 平台特定的查询参数
|
|
18779
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18780
|
+
* @param perPage 每页数量,最大100
|
|
18781
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
18782
|
+
*
|
|
18783
|
+
* @example
|
|
18784
|
+
* ```typescript
|
|
18785
|
+
* // GitHub
|
|
18786
|
+
* const branches = await service.getBranches('github', {
|
|
18787
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
18788
|
+
* repo: 'CodeBuddyIDE'
|
|
18789
|
+
* });
|
|
18790
|
+
*
|
|
18791
|
+
* // Gongfeng
|
|
18792
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
18793
|
+
* project_id: '1611499'
|
|
18794
|
+
* });
|
|
18795
|
+
*
|
|
18796
|
+
* // CNB
|
|
18797
|
+
* const branches = await service.getBranches('cnb', {
|
|
18798
|
+
* repo: 'genie/genie-ide'
|
|
18799
|
+
* });
|
|
18800
|
+
* ```
|
|
18801
|
+
*/
|
|
18802
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
18803
|
+
try {
|
|
18804
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
18805
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18806
|
+
const apiResponse = await httpService.get(url);
|
|
18807
|
+
if (!apiResponse.data) {
|
|
18808
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
18809
|
+
return [];
|
|
18810
|
+
}
|
|
18811
|
+
const branches = apiResponse.data.branches || [];
|
|
18812
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
18813
|
+
return branches;
|
|
18814
|
+
} catch (error) {
|
|
18815
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
18816
|
+
throw error;
|
|
18817
|
+
}
|
|
18818
|
+
}
|
|
18819
|
+
/**
|
|
18820
|
+
* 获取仓库列表
|
|
18821
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
18822
|
+
*
|
|
18823
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
18824
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
18825
|
+
*
|
|
18826
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
18827
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
18828
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
18829
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
18830
|
+
* @param perPage 每页数量,最大100
|
|
18831
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
18832
|
+
*
|
|
18833
|
+
* @example
|
|
18834
|
+
* ```typescript
|
|
18835
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
18836
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
18837
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
18838
|
+
*
|
|
18839
|
+
* // Gongfeng
|
|
18840
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
18841
|
+
* // response.gongfeng_repos 是数组
|
|
18842
|
+
*
|
|
18843
|
+
* // CNB
|
|
18844
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
18845
|
+
* // response.cnb_repos 是数组
|
|
18846
|
+
* ```
|
|
18847
|
+
*/
|
|
18848
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
18849
|
+
try {
|
|
18850
|
+
const queryParams = new URLSearchParams();
|
|
18851
|
+
queryParams.append("page", String(page));
|
|
18852
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
18853
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
18854
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
18855
|
+
const apiResponse = await httpService.get(url);
|
|
18856
|
+
if (!apiResponse.data) {
|
|
18857
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
18858
|
+
return {};
|
|
18859
|
+
}
|
|
18860
|
+
const response = apiResponse.data;
|
|
18861
|
+
this.logRepositoryCounts(response);
|
|
18862
|
+
return response;
|
|
18863
|
+
} catch (error) {
|
|
18864
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
18865
|
+
throw error;
|
|
18866
|
+
}
|
|
18867
|
+
}
|
|
18868
|
+
/**
|
|
18869
|
+
* 构建分支查询参数
|
|
18870
|
+
*/
|
|
18871
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
18872
|
+
const queryParams = new URLSearchParams();
|
|
18873
|
+
queryParams.append("page", String(page));
|
|
18874
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
18875
|
+
if (connector === "github") {
|
|
18876
|
+
const githubParams = params;
|
|
18877
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
18878
|
+
queryParams.append("owner", githubParams.owner);
|
|
18879
|
+
queryParams.append("repo", githubParams.repo);
|
|
18880
|
+
} else if (connector === "gongfeng") {
|
|
18881
|
+
const gongfengParams = params;
|
|
18882
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
18883
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
18884
|
+
} else if (connector === "cnb") {
|
|
18885
|
+
const cnbParams = params;
|
|
18886
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
18887
|
+
queryParams.append("repo", cnbParams.repo);
|
|
18888
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
18889
|
+
return queryParams;
|
|
18890
|
+
}
|
|
18891
|
+
/**
|
|
18892
|
+
* 记录仓库数量日志
|
|
18893
|
+
*/
|
|
18894
|
+
logRepositoryCounts(response) {
|
|
18895
|
+
if (response.github_repos) {
|
|
18896
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
18897
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
18898
|
+
}
|
|
18899
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
18900
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
18901
|
+
}
|
|
18902
|
+
};
|
|
18903
|
+
/**
|
|
18904
|
+
* OAuth Repository Service 单例实例
|
|
18905
|
+
*/
|
|
18906
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
18907
|
+
|
|
18415
18908
|
//#endregion
|
|
18416
18909
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
18417
18910
|
/**
|
|
@@ -18420,28 +18913,24 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18420
18913
|
* 封装与后端 API 的 HTTP 通信
|
|
18421
18914
|
*/
|
|
18422
18915
|
/**
|
|
18423
|
-
*
|
|
18424
|
-
*
|
|
18916
|
+
* 判断当前账号是否是 SSO 账号
|
|
18917
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
18425
18918
|
*/
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
18919
|
+
const safeParseJSON = (jsonString) => {
|
|
18920
|
+
try {
|
|
18921
|
+
return JSON.parse(jsonString);
|
|
18922
|
+
} catch (error) {
|
|
18923
|
+
return {};
|
|
18924
|
+
}
|
|
18429
18925
|
};
|
|
18430
18926
|
/**
|
|
18431
|
-
*
|
|
18432
|
-
* - SSO
|
|
18433
|
-
* - 非 SSO
|
|
18927
|
+
* 根据路径获取完整 URL
|
|
18928
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
18929
|
+
* - 非 SSO 账号直接使用当前域名
|
|
18930
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
18931
|
+
* @returns 完整的 URL 地址
|
|
18434
18932
|
*/
|
|
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
|
-
};
|
|
18933
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
18445
18934
|
/** 获取当前域名的账号选择页面 URL */
|
|
18446
18935
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
18447
18936
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -18478,12 +18967,30 @@ var BackendProvider = class {
|
|
|
18478
18967
|
constructor(config) {
|
|
18479
18968
|
httpService.setBaseURL(config.baseUrl);
|
|
18480
18969
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
18481
|
-
httpService.onUnauthorized(() =>
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
18970
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
18971
|
+
}
|
|
18972
|
+
/**
|
|
18973
|
+
* 处理 401 未授权错误
|
|
18974
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
18975
|
+
*
|
|
18976
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
18977
|
+
*/
|
|
18978
|
+
async handleUnauthorized() {
|
|
18979
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
18980
|
+
try {
|
|
18981
|
+
if (await this.refreshToken()) {
|
|
18982
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
18983
|
+
return;
|
|
18984
|
+
}
|
|
18985
|
+
throw new Error("Token refresh returned null");
|
|
18986
|
+
} catch (error) {
|
|
18987
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
18988
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
18989
|
+
this.logout().catch((logoutError) => {
|
|
18990
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
18485
18991
|
});
|
|
18486
|
-
|
|
18992
|
+
throw error;
|
|
18993
|
+
}
|
|
18487
18994
|
}
|
|
18488
18995
|
/**
|
|
18489
18996
|
* 获取当前账号信息
|
|
@@ -18528,7 +19035,7 @@ var BackendProvider = class {
|
|
|
18528
19035
|
return account;
|
|
18529
19036
|
}
|
|
18530
19037
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18531
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
19038
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18532
19039
|
accountService.setAccount(null);
|
|
18533
19040
|
return null;
|
|
18534
19041
|
} catch (error) {
|
|
@@ -18551,7 +19058,8 @@ var BackendProvider = class {
|
|
|
18551
19058
|
activeStatus: connector.active_status,
|
|
18552
19059
|
displayName: connector.display_name,
|
|
18553
19060
|
oauthClientId: connector.oauth_client_id,
|
|
18554
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19061
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19062
|
+
oauthAppName: connector.oauth_app_name
|
|
18555
19063
|
})) };
|
|
18556
19064
|
}
|
|
18557
19065
|
throw result;
|
|
@@ -18633,7 +19141,8 @@ var BackendProvider = class {
|
|
|
18633
19141
|
connectStatus: connector.connect_status,
|
|
18634
19142
|
displayName: connector.display_name,
|
|
18635
19143
|
oauthClientId: connector.oauth_client_id,
|
|
18636
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
19144
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
19145
|
+
oauthAppName: connector.oauth_app_name
|
|
18637
19146
|
})) };
|
|
18638
19147
|
}
|
|
18639
19148
|
throw result;
|
|
@@ -18724,6 +19233,9 @@ var BackendProvider = class {
|
|
|
18724
19233
|
PackageCode: void 0,
|
|
18725
19234
|
name: ""
|
|
18726
19235
|
};
|
|
19236
|
+
const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
|
|
19237
|
+
console.log("[PRODUCT_FEATURES]", productFeatures);
|
|
19238
|
+
if (!productFeatures.billing) return defaultPlan;
|
|
18727
19239
|
try {
|
|
18728
19240
|
const now = /* @__PURE__ */ new Date();
|
|
18729
19241
|
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
@@ -18745,7 +19257,7 @@ var BackendProvider = class {
|
|
|
18745
19257
|
if (!time) return 0;
|
|
18746
19258
|
return new Date(time).getTime();
|
|
18747
19259
|
};
|
|
18748
|
-
const dailyCredits = [CommodityCode.free
|
|
19260
|
+
const dailyCredits = [CommodityCode.free];
|
|
18749
19261
|
const planResources = resources.map((r) => {
|
|
18750
19262
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
18751
19263
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
@@ -18766,10 +19278,11 @@ var BackendProvider = class {
|
|
|
18766
19278
|
CommodityCode.proMon,
|
|
18767
19279
|
CommodityCode.proMonPlus,
|
|
18768
19280
|
CommodityCode.proYear,
|
|
19281
|
+
CommodityCode.freeMon,
|
|
18769
19282
|
CommodityCode.extra
|
|
18770
19283
|
].includes(code)) return 1;
|
|
18771
19284
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
18772
|
-
if ([CommodityCode.free
|
|
19285
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
18773
19286
|
return 4;
|
|
18774
19287
|
};
|
|
18775
19288
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -18890,7 +19403,7 @@ var BackendProvider = class {
|
|
|
18890
19403
|
*/
|
|
18891
19404
|
async login() {
|
|
18892
19405
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18893
|
-
window.location.href = `${
|
|
19406
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18894
19407
|
}
|
|
18895
19408
|
/**
|
|
18896
19409
|
* 登出账号
|
|
@@ -18953,6 +19466,52 @@ var BackendProvider = class {
|
|
|
18953
19466
|
return null;
|
|
18954
19467
|
}
|
|
18955
19468
|
}
|
|
19469
|
+
/**
|
|
19470
|
+
* 刷新 Token
|
|
19471
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
19472
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
19473
|
+
*/
|
|
19474
|
+
async refreshToken() {
|
|
19475
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
19476
|
+
try {
|
|
19477
|
+
const account = await this.getAccount();
|
|
19478
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
19479
|
+
return account;
|
|
19480
|
+
} catch (error) {
|
|
19481
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
19482
|
+
return null;
|
|
19483
|
+
}
|
|
19484
|
+
}
|
|
19485
|
+
/**
|
|
19486
|
+
* 获取仓库分支列表
|
|
19487
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
19488
|
+
*
|
|
19489
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19490
|
+
* @param params 平台特定的查询参数
|
|
19491
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19492
|
+
* @param perPage 每页数量,最大100
|
|
19493
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
19494
|
+
*/
|
|
19495
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
19496
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
19497
|
+
}
|
|
19498
|
+
/**
|
|
19499
|
+
* 获取仓库列表
|
|
19500
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
19501
|
+
*
|
|
19502
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
19503
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
19504
|
+
*
|
|
19505
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19506
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19507
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
19508
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
19509
|
+
* @param perPage 每页数量,最大100
|
|
19510
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
19511
|
+
*/
|
|
19512
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
19513
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
19514
|
+
}
|
|
18956
19515
|
};
|
|
18957
19516
|
/**
|
|
18958
19517
|
* 创建 BackendProvider 实例
|
|
@@ -18992,6 +19551,7 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
18992
19551
|
GET_FILE: "backend:get-file",
|
|
18993
19552
|
RELOAD_WINDOW: "backend:reload-window",
|
|
18994
19553
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
19554
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
18995
19555
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
18996
19556
|
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
18997
19557
|
};
|
|
@@ -19287,6 +19847,20 @@ var IPCBackendProvider = class {
|
|
|
19287
19847
|
}
|
|
19288
19848
|
}
|
|
19289
19849
|
/**
|
|
19850
|
+
* 在外部浏览器中打开链接
|
|
19851
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
19852
|
+
* @param url 要打开的 URL
|
|
19853
|
+
*/
|
|
19854
|
+
async openExternal(url) {
|
|
19855
|
+
this.log("Opening external URL via IPC:", url);
|
|
19856
|
+
try {
|
|
19857
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
19858
|
+
} catch (error) {
|
|
19859
|
+
this.log("Open external request failed:", error);
|
|
19860
|
+
throw error;
|
|
19861
|
+
}
|
|
19862
|
+
}
|
|
19863
|
+
/**
|
|
19290
19864
|
* 批量切换插件状态
|
|
19291
19865
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
19292
19866
|
*/
|