@tencent-ai/cloud-agent-sdk 0.2.5 → 0.2.6

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 CHANGED
@@ -1526,12 +1526,13 @@ var StreamableHttpClient = class {
1526
1526
  * - Emit 'disconnected' event when connection becomes unhealthy so provider can clean up cache
1527
1527
  */
1528
1528
  var CloudAgentConnection = class {
1529
- constructor(agentId, config) {
1529
+ constructor(agentId, config, cwd = "/workspace") {
1530
1530
  this.listeners = /* @__PURE__ */ new Map();
1531
1531
  this.onceListeners = /* @__PURE__ */ new Map();
1532
1532
  this._isStreaming = false;
1533
1533
  this.transport = "cloud";
1534
1534
  this.agentId = agentId;
1535
+ this.cwd = cwd;
1535
1536
  this.client = new StreamableHttpClient({
1536
1537
  endpoint: config.endpoint,
1537
1538
  authToken: config.authToken,
@@ -1687,13 +1688,13 @@ var CloudAgentConnection = class {
1687
1688
  }
1688
1689
  async createSession(params) {
1689
1690
  return {
1690
- ...await this.client.loadSession(this.agentId, params.cwd),
1691
+ ...await this.client.loadSession(this.agentId, this.cwd),
1691
1692
  sessionId: this.agentId
1692
1693
  };
1693
1694
  }
1694
1695
  async loadSession(params) {
1695
1696
  if (!params.sessionId) throw new Error("sessionId is required for loadSession");
1696
- return this.client.loadSession(params.sessionId, params.cwd);
1697
+ return this.client.loadSession(params.sessionId, this.cwd);
1697
1698
  }
1698
1699
  async setSessionMode(sessionId, modeId) {
1699
1700
  return this.client.setSessionMode({
@@ -1852,7 +1853,6 @@ var E2BFilesystem = class E2BFilesystem {
1852
1853
  */
1853
1854
  static async connect(info) {
1854
1855
  return new E2BFilesystem(await e2b.Sandbox.connect(info.sandboxId, {
1855
- apiKey: info.apiKey,
1856
1856
  domain: info.domain,
1857
1857
  apiUrl: info.apiUrl,
1858
1858
  requestTimeoutMs: info.requestTimeoutMs,
@@ -1896,6 +1896,49 @@ var E2BFilesystem = class E2BFilesystem {
1896
1896
  }
1897
1897
  };
1898
1898
 
1899
+ //#endregion
1900
+ //#region ../agent-provider/src/common/utils/concurrency.ts
1901
+ /**
1902
+ * 并发执行任务,返回包含成功/失败状态的结果
1903
+ *
1904
+ * @param tasks - 任务函数数组
1905
+ * @param concurrency - 最大并发数,默认 5
1906
+ * @returns 所有任务的结果数组,包含状态信息
1907
+ *
1908
+ * @example
1909
+ * ```typescript
1910
+ * const results = await runWithConcurrencySettled(tasks, 3);
1911
+ * const successes = results.filter(r => r.status === 'fulfilled');
1912
+ * const failures = results.filter(r => r.status === 'rejected');
1913
+ * ```
1914
+ */
1915
+ async function runWithConcurrencySettled(tasks, concurrency = 5) {
1916
+ if (tasks.length === 0) return [];
1917
+ const limit = Math.max(1, concurrency);
1918
+ const results = new Array(tasks.length);
1919
+ let currentIndex = 0;
1920
+ async function runNext() {
1921
+ while (currentIndex < tasks.length) {
1922
+ const index = currentIndex++;
1923
+ const task = tasks[index];
1924
+ try {
1925
+ results[index] = {
1926
+ status: "fulfilled",
1927
+ value: await task()
1928
+ };
1929
+ } catch (reason) {
1930
+ results[index] = {
1931
+ status: "rejected",
1932
+ reason
1933
+ };
1934
+ }
1935
+ }
1936
+ }
1937
+ const workers = Array(Math.min(limit, tasks.length)).fill(null).map(() => runNext());
1938
+ await Promise.all(workers);
1939
+ return results;
1940
+ }
1941
+
1899
1942
  //#endregion
1900
1943
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cos-upload-service.ts
1901
1944
  /**
@@ -1906,6 +1949,7 @@ var E2BFilesystem = class E2BFilesystem {
1906
1949
  * const service = new CosUploadService({
1907
1950
  * request: (method, path, body) => cloudProvider.request(method, path, body),
1908
1951
  * logger: console,
1952
+ * uploadConcurrency: 3,
1909
1953
  * });
1910
1954
  *
1911
1955
  * const result = await service.uploadFile(file);
@@ -1919,6 +1963,7 @@ var CosUploadService = class {
1919
1963
  this.request = options.request;
1920
1964
  this.logger = options.logger;
1921
1965
  this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
1966
+ this.uploadConcurrency = options.uploadConcurrency ?? 3;
1922
1967
  }
1923
1968
  /**
1924
1969
  * 生成唯一的 objectKey
@@ -1929,13 +1974,13 @@ var CosUploadService = class {
1929
1974
  return `uploads/${Date.now()}-${Math.random().toString(36).substring(2, 10)}-${encodeURIComponent(filename)}`;
1930
1975
  }
1931
1976
  /**
1932
- * 获取预签名 URL
1977
+ * 批量获取预签名 URL
1933
1978
  *
1934
- * POST /conversation/artifacts/presigned_url
1979
+ * POST /conversations/presigned_url
1935
1980
  */
1936
- async getPresignedUrl(params) {
1937
- const response = await this.request("POST", "/conversation/artifacts/presigned_url", params);
1938
- if (!response.ok) throw new Error(`Failed to get presigned URL: ${response.statusText}`);
1981
+ async getPresignedUrls(objectKeys) {
1982
+ const response = await this.request("POST", "/conversations/presigned_url", { object_keys: objectKeys });
1983
+ if (!response.ok) throw new Error(`Failed to get presigned URLs: ${response.statusText}`);
1939
1984
  const apiResponse = await response.json();
1940
1985
  if (!apiResponse.data) throw new Error("No data in presigned URL response");
1941
1986
  return apiResponse.data;
@@ -1944,22 +1989,17 @@ var CosUploadService = class {
1944
1989
  * 上传单个文件到 COS
1945
1990
  *
1946
1991
  * @param file - 要上传的文件
1947
- * @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
1948
1992
  * @returns 上传结果,包含访问 URL 或错误信息
1949
1993
  */
1950
- async uploadFile(file, expireSeconds = 3600) {
1994
+ async uploadFile(file) {
1951
1995
  const filename = file.name;
1952
1996
  this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
1953
1997
  try {
1954
1998
  const objectKey = this.generateObjectKey(filename);
1955
1999
  this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
1956
- const putPresigned = await this.getPresignedUrl({
1957
- object_key: objectKey,
1958
- method: "PUT",
1959
- expire_seconds: expireSeconds
1960
- });
1961
- this.logger?.debug(`[CosUploadService] Got PUT presigned URL`);
1962
- const uploadResponse = await this.fetchImpl(putPresigned.url, {
2000
+ const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
2001
+ if (!presignedItem) throw new Error("No presigned URL item returned");
2002
+ const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
1963
2003
  method: "PUT",
1964
2004
  body: file,
1965
2005
  headers: { "Content-Type": file.type || "application/octet-stream" }
@@ -1969,15 +2009,11 @@ var CosUploadService = class {
1969
2009
  throw new Error(`COS upload failed: ${uploadResponse.status} ${errorText}`);
1970
2010
  }
1971
2011
  this.logger?.debug(`[CosUploadService] File uploaded to COS`);
1972
- const getPresigned = await this.getPresignedUrl({
1973
- object_key: objectKey,
1974
- method: "GET",
1975
- expire_seconds: expireSeconds
1976
- });
1977
2012
  this.logger?.info(`[CosUploadService] Upload success: ${filename}`);
1978
2013
  return {
1979
2014
  success: true,
1980
- url: getPresigned.url
2015
+ url: presignedItem.download_url,
2016
+ objectKey
1981
2017
  };
1982
2018
  } catch (error) {
1983
2019
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
@@ -1991,509 +2027,272 @@ var CosUploadService = class {
1991
2027
  /**
1992
2028
  * 批量上传文件到 COS
1993
2029
  *
2030
+ * 使用并发控制,限制同时上传的文件数量
2031
+ *
1994
2032
  * @param files - 要上传的文件数组
1995
- * @param expireSeconds - 预签名 URL 过期时间(秒),默认 3600
1996
2033
  * @returns 所有文件的上传结果
1997
2034
  */
1998
- async uploadFiles(files, expireSeconds = 3600) {
1999
- this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s)`);
2000
- const results = [];
2001
- const urls = [];
2002
- for (const file of files) {
2003
- const result = await this.uploadFile(file, expireSeconds);
2004
- results.push(result);
2005
- if (result.success && result.url) urls.push(result.url);
2006
- }
2007
- const failedResults = results.filter((r) => !r.success);
2008
- if (failedResults.length > 0) {
2009
- const failedErrors = failedResults.map((r) => r.error).join("; ");
2035
+ async uploadFiles(files) {
2036
+ if (files.length === 0) return {
2037
+ success: true,
2038
+ urls: [],
2039
+ results: []
2040
+ };
2041
+ this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
2042
+ try {
2043
+ const fileInfos = files.map((file) => ({
2044
+ file,
2045
+ objectKey: this.generateObjectKey(file.name)
2046
+ }));
2047
+ const objectKeys = fileInfos.map((info) => info.objectKey);
2048
+ const presignedResponse = await this.getPresignedUrls(objectKeys);
2049
+ this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
2050
+ const presignedMap = /* @__PURE__ */ new Map();
2051
+ for (const item of presignedResponse.items) presignedMap.set(item.object_key, item);
2052
+ const results = (await runWithConcurrencySettled(fileInfos.map(({ file, objectKey }) => async () => {
2053
+ const presignedItem = presignedMap.get(objectKey);
2054
+ if (!presignedItem) return {
2055
+ success: false,
2056
+ error: `No presigned URL for ${file.name}`,
2057
+ objectKey
2058
+ };
2059
+ try {
2060
+ const uploadResponse = await this.fetchImpl(presignedItem.upload_url, {
2061
+ method: "PUT",
2062
+ body: file,
2063
+ headers: { "Content-Type": file.type || "application/octet-stream" }
2064
+ });
2065
+ if (!uploadResponse.ok) {
2066
+ const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
2067
+ return {
2068
+ success: false,
2069
+ error: `COS upload failed: ${uploadResponse.status} ${errorText}`,
2070
+ objectKey
2071
+ };
2072
+ }
2073
+ this.logger?.debug(`[CosUploadService] Uploaded: ${file.name}`);
2074
+ return {
2075
+ success: true,
2076
+ url: presignedItem.download_url,
2077
+ objectKey
2078
+ };
2079
+ } catch (error) {
2080
+ return {
2081
+ success: false,
2082
+ error: error instanceof Error ? error.message : "Unknown error",
2083
+ objectKey
2084
+ };
2085
+ }
2086
+ }), this.uploadConcurrency)).map((result, index) => {
2087
+ if (result.status === "fulfilled") return result.value;
2088
+ return {
2089
+ success: false,
2090
+ error: result.reason instanceof Error ? result.reason.message : "Unknown error",
2091
+ objectKey: fileInfos[index].objectKey
2092
+ };
2093
+ });
2094
+ const urls = results.filter((r) => r.success && r.url).map((r) => r.url);
2095
+ const failedResults = results.filter((r) => !r.success);
2096
+ if (failedResults.length > 0) {
2097
+ const failedErrors = failedResults.map((r) => r.error).join("; ");
2098
+ return {
2099
+ success: false,
2100
+ error: `${failedResults.length} file(s) failed: ${failedErrors}`,
2101
+ expireSeconds: presignedResponse.expire,
2102
+ results
2103
+ };
2104
+ }
2105
+ this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
2010
2106
  return {
2011
- success: false,
2012
- error: `${failedResults.length} file(s) failed: ${failedErrors}`,
2107
+ success: true,
2108
+ urls,
2109
+ expireSeconds: presignedResponse.expire,
2013
2110
  results
2014
2111
  };
2112
+ } catch (error) {
2113
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
2114
+ this.logger?.error(`[CosUploadService] Batch upload failed`, error);
2115
+ return {
2116
+ success: false,
2117
+ error: errorMessage,
2118
+ results: files.map(() => ({
2119
+ success: false,
2120
+ error: errorMessage
2121
+ }))
2122
+ };
2015
2123
  }
2016
- this.logger?.info(`[CosUploadService] All ${files.length} file(s) uploaded successfully`);
2017
- return {
2018
- success: true,
2019
- urls,
2020
- results
2021
- };
2022
2124
  }
2023
2125
  };
2024
2126
 
2025
2127
  //#endregion
2026
- //#region ../agent-provider/src/backend/types.ts
2027
- /**
2028
- * 套餐代码
2029
- */
2030
- /**
2031
- * TCACA_code_001_PqouKr6QWV CodeBuddy海外版免费包
2032
- * TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
2033
- * TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
2034
- * TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
2035
- * TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
2036
- * TCACA_code_008_cfWoLwvjU4 赠送月包
2037
- */
2038
- let CommodityCode = /* @__PURE__ */ function(CommodityCode) {
2039
- CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
2040
- CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
2041
- CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
2042
- CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
2043
- CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
2044
- CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
2045
- CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
2046
- return CommodityCode;
2047
- }({});
2048
-
2049
- //#endregion
2050
- //#region ../agent-provider/src/backend/backend-provider.ts
2128
+ //#region ../agent-provider/src/account/account-service.ts
2051
2129
  /**
2052
- * Backend Provider 实现
2130
+ * AccountService
2053
2131
  *
2054
- * 封装与后端 API 的 HTTP 通信
2055
- */
2056
- /** 获取当前域名的登录页面 URL */
2057
- const getLoginUrl = () => `${window.location.origin}/login`;
2058
- /** 获取当前域名的账号选择页面 URL */
2059
- const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
2060
- /** localStorage 中存储选中账号 ID 的 key */
2061
- const SELECTED_ACCOUNT_KEY = "CODEBUDDY_IDE_SELECTED_ACCOUNT_ID";
2062
- /**
2063
- * Backend Provider 实现类
2132
+ * 单例模式,管理全局账号状态
2064
2133
  */
2065
- var BackendProvider = class {
2066
- constructor(config) {
2067
- this.baseUrl = config.baseUrl.replace(/\/$/, "");
2068
- this.authToken = config.authToken;
2134
+ var AccountService = class {
2135
+ constructor() {
2136
+ this.account = null;
2137
+ this.listeners = /* @__PURE__ */ new Set();
2138
+ this.initialized = false;
2139
+ this.initPromise = null;
2140
+ this.initResolve = null;
2141
+ this.initPromise = new Promise((resolve) => {
2142
+ this.initResolve = resolve;
2143
+ });
2069
2144
  }
2070
2145
  /**
2071
- * 获取 Agent 列表
2072
- * API 端点: GET /v2/cloudagent/agentmgmt/agents
2146
+ * 获取当前账号
2147
+ * @returns 当前账号,未登录或未加载时返回 null
2073
2148
  */
2074
- async getAgents(request = {}) {
2075
- const { MockAgentProvider } = await Promise.resolve().then(() => require("./MockAgentProvider-4e4oOusg.cjs"));
2076
- const sessions = new MockAgentProvider().getAllSessions();
2077
- const mockTitles = {
2078
- "1": "开发五子棋游戏",
2079
- "2": "修复登录页面样式",
2080
- "3": "API 接口优化"
2081
- };
2082
- const agents = sessions.map((session, index) => ({
2083
- id: session.sessionId,
2084
- name: mockTitles[session.sessionId] || `Agent ${session.sessionId}`,
2085
- status: "RUNNING",
2086
- visibility: "PRIVATE",
2087
- createdAt: new Date(session.createdAt).toISOString(),
2088
- summary: `Session created at ${new Date(session.createdAt).toLocaleString()}`,
2089
- source: {
2090
- provider: "github",
2091
- ref: "refs/heads/main",
2092
- repository: session.cwd
2093
- },
2094
- target: {
2095
- autoCreatePr: false,
2096
- branchName: "feature/mock",
2097
- prUrl: void 0,
2098
- url: void 0
2099
- }
2100
- }));
2101
- return {
2102
- agents,
2103
- pagination: {
2104
- hasNext: false,
2105
- hasPrev: false,
2106
- page: 1,
2107
- size: agents.length,
2108
- total: agents.length,
2109
- totalPages: 1
2110
- }
2111
- };
2149
+ getAccount() {
2150
+ return this.account;
2112
2151
  }
2113
2152
  /**
2114
- * 获取可用模型列表
2115
- * API 端点: GET /v2/cloudagent/models (假设)
2116
- *
2117
- * 当前实现: 返回 Mock 数据
2153
+ * 设置账号
2154
+ * @param account 账号信息,登出时传 null
2118
2155
  */
2119
- async getModels(request) {
2120
- const mockModels = [{
2121
- id: "glm-4.7",
2122
- name: "GLM-4.7",
2123
- vendor: "f",
2124
- maxOutputTokens: 48e3,
2125
- maxInputTokens: 2e5,
2126
- supportsToolCall: true,
2127
- supportsImages: false,
2128
- disabledMultimodal: true,
2129
- maxAllowedSize: 2e5,
2130
- supportsReasoning: true,
2131
- onlyReasoning: true,
2132
- temperature: 1,
2133
- reasoning: {
2134
- effort: "medium",
2135
- summary: "auto"
2136
- },
2137
- descriptionEn: "GLM-4.7 model, Well-rounded model for everyday use",
2138
- descriptionZh: "GLM-4.7 大模型,能力均衡,适合日常使用"
2139
- }, {
2140
- id: "glm-4.7-flash",
2141
- name: "GLM-4.7 Flash",
2142
- vendor: "f",
2143
- maxOutputTokens: 4e4,
2144
- maxInputTokens: 128e3,
2145
- supportsToolCall: true,
2146
- supportsImages: false,
2147
- disabledMultimodal: false,
2148
- maxAllowedSize: 128e3,
2149
- supportsReasoning: false,
2150
- onlyReasoning: false,
2151
- temperature: .7,
2152
- reasoning: {
2153
- effort: "low",
2154
- summary: "never"
2155
- },
2156
- descriptionEn: "GLM-4.7 Flash, Fast and efficient model",
2157
- descriptionZh: "GLM-4.7 Flash,快速高效的模型"
2158
- }];
2159
- console.log("[BackendProvider] getModels called for repository:", request.repository);
2160
- return { models: mockModels };
2156
+ setAccount(account) {
2157
+ const prev = this.account;
2158
+ this.account = account;
2159
+ if (!this.initialized) {
2160
+ this.initialized = true;
2161
+ this.initResolve?.(account);
2162
+ }
2163
+ if (prev?.uid !== account?.uid) this.notifyListeners();
2161
2164
  }
2162
2165
  /**
2163
- * 获取当前账号信息
2164
- * API 端点: GET /console/accounts (返回账号列表)
2165
- *
2166
- * 逻辑:
2167
- * 1. 从 localStorage 读取 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID
2168
- * 2. 根据 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID 找到对应账号
2169
- * - personal 类型: 用 uid 匹配
2170
- * - 其他类型: 用 enterpriseId 匹配
2171
- * 3. 如果没有选中的账号,跳转到账号选择页面
2172
- * 4. 获取套餐信息并合并到账号中
2166
+ * 清除账号(登出)
2173
2167
  */
2174
- async getAccount() {
2175
- const url = `${this.baseUrl}/console/accounts`;
2176
- const headers = {
2177
- "Content-Type": "application/json",
2178
- "Accept": "application/json"
2168
+ clearAccount() {
2169
+ this.setAccount(null);
2170
+ }
2171
+ /**
2172
+ * 订阅账号变化
2173
+ * @param callback 变化时的回调函数
2174
+ * @returns 取消订阅函数
2175
+ */
2176
+ subscribe(callback) {
2177
+ this.listeners.add(callback);
2178
+ return () => {
2179
+ this.listeners.delete(callback);
2179
2180
  };
2180
- if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
2181
- try {
2182
- const response = await fetch(url, {
2183
- method: "GET",
2184
- headers,
2185
- credentials: "include"
2186
- });
2187
- if (!response.ok) {
2188
- if (response.status === 401 || response.status === 403) return null;
2189
- const error = await response.json().catch(() => ({ message: response.statusText }));
2190
- throw new Error(error.message || `HTTP ${response.status}`);
2191
- }
2192
- if (!(response.headers.get("Content-Type") || "").includes("application/json")) return null;
2193
- const { data = {} } = await response.json();
2194
- const accounts = data?.accounts || [];
2195
- if (!accounts || accounts.length === 0) return null;
2196
- const selectedAccountId = localStorage.getItem(SELECTED_ACCOUNT_KEY);
2197
- let selectedAccount;
2198
- if (selectedAccountId) {
2199
- selectedAccount = accounts.find((account) => {
2200
- if (account.type === "personal") return account.uid === selectedAccountId;
2201
- return account.enterpriseId === selectedAccountId;
2202
- });
2203
- if (selectedAccount) try {
2204
- const plan = await this.getCurrentPlan();
2205
- const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
2206
- console.log("account", {
2207
- ...selectedAccount,
2208
- ...plan,
2209
- editionType
2210
- });
2211
- return {
2212
- ...selectedAccount,
2213
- ...plan,
2214
- editionType
2215
- };
2216
- } catch (error) {
2217
- return { ...selectedAccount };
2218
- }
2181
+ }
2182
+ /**
2183
+ * 等待首次账号加载完成
2184
+ * @returns Promise<Account | null>
2185
+ */
2186
+ waitForInit() {
2187
+ if (this.initialized) return Promise.resolve(this.account);
2188
+ return this.initPromise;
2189
+ }
2190
+ /**
2191
+ * 是否已初始化
2192
+ */
2193
+ isInitialized() {
2194
+ return this.initialized;
2195
+ }
2196
+ /**
2197
+ * 是否已登录
2198
+ */
2199
+ isLoggedIn() {
2200
+ return this.account !== null;
2201
+ }
2202
+ /**
2203
+ * 通知所有订阅者
2204
+ */
2205
+ notifyListeners() {
2206
+ this.listeners.forEach((callback) => {
2207
+ try {
2208
+ callback(this.account);
2209
+ } catch (error) {
2210
+ console.error("[AccountService] Listener error:", error);
2219
2211
  }
2220
- if (accounts.length === 1) {
2221
- selectedAccount = accounts[0];
2222
- const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
2223
- if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
2224
- const plan = await this.getCurrentPlan();
2225
- const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
2226
- console.log("account (auto-selected)", {
2227
- ...selectedAccount,
2228
- ...plan,
2229
- editionType
2230
- });
2231
- return {
2232
- ...selectedAccount,
2233
- ...plan,
2234
- editionType
2235
- };
2236
- }
2237
- const redirectUrl = encodeURIComponent(window.location.href);
2238
- window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
2239
- return null;
2240
- } catch (error) {
2241
- console.error("[BackendProvider] getAccount failed:", error);
2242
- return null;
2243
- }
2244
- }
2245
- /**
2246
- * 获取当前套餐信息
2247
- * 从计量计费接口获取用户的套餐信息
2248
- * API: POST /billing/meter/get-user-resource
2249
- */
2250
- async getCurrentPlan() {
2251
- const defaultPlan = {
2252
- isPro: false,
2253
- expireAt: 0,
2254
- renewFlag: 0,
2255
- PackageCode: void 0,
2256
- name: ""
2257
- };
2258
- try {
2259
- const url = `${this.baseUrl}/billing/meter/get-user-resource`;
2260
- const headers = {
2261
- "Content-Type": "application/json",
2262
- "Accept": "application/json"
2263
- };
2264
- if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
2265
- const now = /* @__PURE__ */ new Date();
2266
- const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
2267
- const formatDate = (d) => {
2268
- const pad = (n) => n.toString().padStart(2, "0");
2269
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
2270
- };
2271
- const body = {
2272
- PageNumber: 1,
2273
- PageSize: 100,
2274
- ProductCode: "p_tcaca",
2275
- Status: [0, 3],
2276
- PackageEndTimeRangeBegin: formatDate(now),
2277
- PackageEndTimeRangeEnd: formatDate(futureDate)
2278
- };
2279
- const response = await fetch(url, {
2280
- method: "POST",
2281
- headers,
2282
- credentials: "include",
2283
- body: JSON.stringify(body)
2284
- });
2285
- if (!response.ok) {
2286
- console.warn("[BackendProvider] getCurrentPlan failed:", response.status);
2287
- return defaultPlan;
2288
- }
2289
- const result = await response.json();
2290
- const resources = result?.data?.Response?.Data?.Accounts || result?.data?.Accounts || [];
2291
- if (!resources || resources.length === 0) return defaultPlan;
2292
- const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon);
2293
- const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift);
2294
- const activePlan = proPlan || trialPlan;
2295
- if (activePlan) return {
2296
- isPro: !!proPlan,
2297
- expireAt: activePlan.ExpiredTime || activePlan.CycleEndTime || 0,
2298
- renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
2299
- PackageCode: activePlan.PackageCode,
2300
- name: activePlan.PackageName || ""
2301
- };
2302
- return defaultPlan;
2303
- } catch (error) {
2304
- console.error("[BackendProvider] getCurrentPlan error:", error);
2305
- return defaultPlan;
2306
- }
2307
- }
2308
- /**
2309
- * 根据账号类型和 Pro 状态计算版本展示类型
2310
- * - personal + isPro = 'pro'
2311
- * - personal + !isPro = 'free'
2312
- * - ultimate = 'ultimate' (旗舰版/团队版)
2313
- * - exclusive = 'exclusive' (专享版/企业版)
2314
- */
2315
- getEditionDisplayType(type, isPro) {
2316
- if (type === "personal") return isPro ? "pro" : "free";
2317
- if (type === "ultimate") return "ultimate";
2318
- if (type === "exclusive") return "exclusive";
2319
- return "free";
2320
- }
2321
- /**
2322
- * 触发登录流程
2323
- * Web 环境: 跳转到登录页面
2324
- */
2325
- async login() {
2326
- const redirectUrl = encodeURIComponent(window.location.href);
2327
- window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
2212
+ });
2328
2213
  }
2329
2214
  /**
2330
- * 登出账号
2331
- * Web 环境: 跳转到登出页面或清除 cookie
2215
+ * 重置服务状态(仅用于测试)
2332
2216
  */
2333
- async logout() {
2334
- const url = `${this.baseUrl}/console/logout`;
2335
- try {
2336
- await fetch(url, {
2337
- method: "POST",
2338
- credentials: "include"
2339
- });
2340
- } catch (error) {
2341
- console.error("[BackendProvider] logout failed:", error);
2342
- }
2343
- localStorage.removeItem(SELECTED_ACCOUNT_KEY);
2344
- window.location.reload();
2217
+ _reset() {
2218
+ this.account = null;
2219
+ this.listeners.clear();
2220
+ this.initialized = false;
2221
+ this.initPromise = new Promise((resolve) => {
2222
+ this.initResolve = resolve;
2223
+ });
2345
2224
  }
2346
2225
  };
2347
2226
  /**
2348
- * 创建 BackendProvider 实例
2227
+ * 导出单例实例
2349
2228
  */
2350
- function createBackendProvider(config) {
2351
- return new BackendProvider(config);
2352
- }
2229
+ const accountService = new AccountService();
2353
2230
 
2354
2231
  //#endregion
2355
- //#region ../agent-provider/src/backend/ipc-backend-provider.ts
2356
- /**
2357
- * Backend 请求类型常量
2358
- */
2359
- const BACKEND_REQUEST_TYPES = {
2360
- GET_AGENTS: "backend:get-agents-request",
2361
- GET_MODELS: "backend:get-models",
2362
- LOGIN: "backend:login",
2363
- LOGOUT: "backend:logout",
2364
- GET_ACCOUNT: "backend:get-account"
2365
- };
2232
+ //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
2366
2233
  /**
2367
- * 生成唯一请求 ID
2234
+ * Normalize a path by resolving `.` and `..` segments
2235
+ * This is a simplified version that works in browser environment
2368
2236
  */
2369
- function generateRequestId() {
2370
- return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
2237
+ function normalizePath(path) {
2238
+ const segments = path.split("/");
2239
+ const result = [];
2240
+ for (const segment of segments) if (segment === "..") {
2241
+ if (result.length > 0 && result[result.length - 1] !== "") result.pop();
2242
+ } else if (segment !== "." && segment !== "") result.push(segment);
2243
+ return (path.startsWith("/") ? "/" : "") + result.join("/");
2371
2244
  }
2372
2245
  /**
2373
- * IPC Backend Provider 实现类
2246
+ * Resolve agent:/// URI to filesystem path
2374
2247
  *
2375
- * 通过 IWidgetChannel 与后端通信获取 Agent 列表
2248
+ * 只处理 agent:// 协议的 URI,raw path 直接透传不做任何处理
2249
+ *
2250
+ * Supported formats:
2251
+ * - `agent:///workspace/{path}` → `/workspace/{path}`
2252
+ * - `agent:///plans/{path}` → `/root/.codebuddy/plans/{path}`
2253
+ * - `agent:///{path}` → `/{path}`
2254
+ * - Raw paths (e.g. `/foo/bar`) → 直接透传,不处理
2255
+ *
2256
+ * Security: Path traversal attacks are prevented by normalizing paths
2257
+ * and verifying they stay within expected boundaries.
2376
2258
  */
2377
- var IPCBackendProvider = class {
2378
- constructor(config) {
2379
- this.channel = config.channel;
2380
- this.debug = config.debug ?? false;
2381
- this.timeoutMs = config.timeoutMs ?? 3e4;
2382
- this.log("Initialized with IWidgetChannel");
2383
- }
2384
- /**
2385
- * 发送统一格式的后端请求
2386
- * @param requestType 请求类型
2387
- * @param params 请求参数
2388
- * @returns 响应数据
2389
- */
2390
- async sendBackendRequest(requestType, params) {
2391
- const message = {
2392
- type: "backend",
2393
- requestId: generateRequestId(),
2394
- params: {
2395
- type: requestType,
2396
- params
2397
- }
2398
- };
2399
- this.log("Sending backend request:", message);
2400
- const response = await this.channel.callMethod("__backend__", message, this.timeoutMs);
2401
- this.log("Received response:", response);
2402
- if (response?.error) throw new Error(response.error);
2403
- return response?.data !== void 0 ? response.data : response;
2404
- }
2405
- /**
2406
- * 获取 Agent 列表
2407
- * 通过 IWidgetChannel 发送请求到后端
2408
- */
2409
- async getAgents(request = {}) {
2410
- this.log("Getting agents with request:", request);
2411
- try {
2412
- return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_AGENTS, request);
2413
- } catch (error) {
2414
- this.log("Get agents failed:", error);
2415
- throw error;
2416
- }
2417
- }
2418
- /**
2419
- * 获取可用模型列表
2420
- * 通过 IWidgetChannel 发送请求到后端
2421
- */
2422
- async getModels(request) {
2423
- this.log("Getting models with request:", request);
2424
- try {
2425
- return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_MODELS, request);
2426
- } catch (error) {
2427
- this.log("Get models failed:", error);
2428
- throw error;
2429
- }
2430
- }
2431
- /**
2432
- * 获取当前账号信息
2433
- * IDE 环境: 通过 IPC 获取账号信息
2434
- */
2435
- async getAccount() {
2436
- this.log("Getting account via IPC");
2437
- try {
2438
- return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
2439
- } catch (error) {
2440
- this.log("Get account failed:", error);
2441
- return null;
2442
- }
2443
- }
2444
- /**
2445
- * 触发登录流程
2446
- * IDE 环境: 通过 IPC 通知 IDE 打开登录流程
2447
- */
2448
- async login() {
2449
- this.log("Triggering login via IPC");
2450
- try {
2451
- await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGIN);
2452
- } catch (error) {
2453
- this.log("Login request failed:", error);
2454
- throw error;
2455
- }
2456
- }
2457
- /**
2458
- * 登出账号
2459
- * IDE 环境: 通过 IPC 通知 IDE 登出
2460
- */
2461
- async logout() {
2462
- this.log("Triggering logout via IPC");
2463
- try {
2464
- await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGOUT);
2465
- } catch (error) {
2466
- this.log("Logout request failed:", error);
2467
- throw error;
2468
- }
2469
- }
2470
- /**
2471
- * 监听 channel 事件
2472
- * 用于监听账户变化等事件
2473
- */
2474
- on(event, callback) {
2475
- this.log("Registering event listener:", event);
2476
- this.channel.on(event, callback);
2477
- return () => {
2478
- this.channel.off(event, callback);
2479
- };
2480
- }
2481
- /**
2482
- * 调试日志
2483
- */
2484
- log(...args) {
2485
- if (this.debug) console.log("[IPCBackendProvider]", ...args);
2486
- }
2487
- };
2259
+ function resolveAgentUri(input) {
2260
+ if (!input.startsWith("agent://")) return input;
2261
+ const path = input.slice(8);
2262
+ if (!path.startsWith("/")) return input;
2263
+ const normalizedPath = normalizePath(path);
2264
+ if (normalizedPath.startsWith("/plans/") || normalizedPath === "/plans") {
2265
+ const finalPath = normalizePath("/root/.codebuddy" + normalizedPath);
2266
+ if (!finalPath.startsWith("/root/.codebuddy/plans")) throw new Error(`Invalid path: path traversal detected in ${input}`);
2267
+ return finalPath;
2268
+ }
2269
+ return normalizedPath;
2270
+ }
2488
2271
  /**
2489
- * 创建 IPCBackendProvider 实例
2272
+ * Create a FilesResource wrapper that resolves agent:/// URIs
2490
2273
  */
2491
- function createIPCBackendProvider(config) {
2492
- return new IPCBackendProvider(config);
2274
+ function createAgentFilesystem(fs) {
2275
+ return {
2276
+ read: (path, opts) => fs.read(resolveAgentUri(path), opts),
2277
+ write: (pathOrFiles, dataOrOpts, opts) => {
2278
+ if (Array.isArray(pathOrFiles)) {
2279
+ const resolved = pathOrFiles.map((f) => ({
2280
+ ...f,
2281
+ path: resolveAgentUri(f.path)
2282
+ }));
2283
+ return fs.write(resolved, dataOrOpts);
2284
+ }
2285
+ return fs.write(resolveAgentUri(pathOrFiles), dataOrOpts, opts);
2286
+ },
2287
+ list: (path, opts) => fs.list(resolveAgentUri(path), opts),
2288
+ exists: (path, opts) => fs.exists(resolveAgentUri(path), opts),
2289
+ makeDir: (path, opts) => fs.makeDir(resolveAgentUri(path), opts),
2290
+ remove: (path, opts) => fs.remove(resolveAgentUri(path), opts),
2291
+ rename: (oldPath, newPath, opts) => fs.rename(resolveAgentUri(oldPath), resolveAgentUri(newPath), opts),
2292
+ getInfo: (path, opts) => fs.getInfo(resolveAgentUri(path), opts),
2293
+ watchDir: (path, onEvent, opts) => fs.watchDir(resolveAgentUri(path), onEvent, opts)
2294
+ };
2493
2295
  }
2494
-
2495
- //#endregion
2496
- //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-provider.ts
2497
2296
  /**
2498
2297
  * CloudAgentProvider - Manages cloud-hosted agents via REST API
2499
2298
  *
@@ -2567,6 +2366,7 @@ var CloudAgentProvider = class CloudAgentProvider {
2567
2366
  constructor(options) {
2568
2367
  this.filesystemCache = /* @__PURE__ */ new Map();
2569
2368
  this.connectionCache = /* @__PURE__ */ new Map();
2369
+ this.eventListeners = /* @__PURE__ */ new Map();
2570
2370
  this.options = options;
2571
2371
  this.logger = options.logger;
2572
2372
  this.fetchImpl = options.fetch ?? globalThis.fetch.bind(globalThis);
@@ -2577,6 +2377,13 @@ var CloudAgentProvider = class CloudAgentProvider {
2577
2377
  });
2578
2378
  }
2579
2379
  /**
2380
+ * Dispose the provider and clean up resources
2381
+ */
2382
+ dispose() {
2383
+ this.filesystemCache.clear();
2384
+ this.connectionCache.clear();
2385
+ }
2386
+ /**
2580
2387
  * Get the filesystem provider (returns self)
2581
2388
  */
2582
2389
  get filesystem() {
@@ -2585,16 +2392,29 @@ var CloudAgentProvider = class CloudAgentProvider {
2585
2392
  /**
2586
2393
  * Get filesystem resource for an agent
2587
2394
  *
2588
- * Creates or returns cached E2BFilesystem instance for the agent's sandbox.
2395
+ * Creates or returns cached filesystem instance for the agent's sandbox.
2396
+ * The filesystem supports both `agent:///` URIs and raw paths.
2589
2397
  *
2590
2398
  * @param agentId - Agent ID to get filesystem for
2591
- * @returns FilesResource instance for the agent's sandbox
2399
+ * @returns FilesResource instance for the agent's sandbox (with URI support)
2400
+ *
2401
+ * @example
2402
+ * ```typescript
2403
+ * const fs = await provider.getFilesystem(agentId);
2404
+ *
2405
+ * // Use agent:/// URIs
2406
+ * const content = await fs.read('agent:///files/src/app.ts');
2407
+ * await fs.write('agent:///artifacts/output.txt', 'Hello');
2408
+ *
2409
+ * // Raw paths still work (backward compatible)
2410
+ * const content2 = await fs.read('/src/app.ts');
2411
+ * ```
2592
2412
  */
2593
2413
  async getFilesystem(agentId) {
2594
2414
  const cached = this.filesystemCache.get(agentId);
2595
2415
  if (cached) return cached;
2596
2416
  const info = await this.getSandboxInfo(agentId);
2597
- const filesystem = await E2BFilesystem.connect(info);
2417
+ const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
2598
2418
  this.filesystemCache.set(agentId, filesystem);
2599
2419
  this.logger?.debug(`Created filesystem for agent: ${agentId}`);
2600
2420
  return filesystem;
@@ -2619,7 +2439,6 @@ var CloudAgentProvider = class CloudAgentProvider {
2619
2439
  return {
2620
2440
  sandboxId: apiResponse.data.sandboxId,
2621
2441
  apiUrl,
2622
- apiKey: "backend-not-used",
2623
2442
  accessToken: apiResponse.data.token,
2624
2443
  headers: { ...currentEnterpriseId && { "X-Enterprise-Id": currentEnterpriseId } }
2625
2444
  };
@@ -2722,31 +2541,55 @@ var CloudAgentProvider = class CloudAgentProvider {
2722
2541
  * and token expiration internally
2723
2542
  */
2724
2543
  async connect(agentId) {
2725
- const response = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
2726
- if (response.status === 404) throw new Error(`Agent not found: ${agentId}`);
2727
- if (!response.ok) throw new Error(`Failed to get agent for connection: ${response.statusText}`);
2728
- const apiResponse = await response.json();
2729
- if (!apiResponse.data) throw new Error("No data in API response");
2730
- const sessionData = apiResponse.data;
2544
+ const sessionResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}/session`);
2545
+ if (sessionResponse.status === 404) throw new Error(`Agent not found: ${agentId}`);
2546
+ if (!sessionResponse.ok) throw new Error(`Failed to get agent for connection: ${sessionResponse.statusText}`);
2547
+ const sessionApiResponse = await sessionResponse.json();
2548
+ if (!sessionApiResponse.data) throw new Error("No data in API response");
2549
+ const sessionData = sessionApiResponse.data;
2731
2550
  const endpoint = sessionData.link.replace(/^http:\/\//, "https://");
2551
+ const cwd = sessionApiResponse.data.cwd || "/workspace";
2552
+ const agentResponse = await this.request("GET", `/console/cloudagent/agentmgmt/agents/${agentId}`);
2553
+ let agentName;
2554
+ let agentCreatedAt;
2555
+ let agentStatus;
2556
+ if (agentResponse.ok) {
2557
+ const agentApiResponse = await agentResponse.json();
2558
+ if (agentApiResponse.data) {
2559
+ agentName = agentApiResponse.data.name;
2560
+ agentCreatedAt = agentApiResponse.data.createdAt ? new Date(agentApiResponse.data.createdAt) : void 0;
2561
+ agentStatus = agentApiResponse.data.sessionStatus || agentApiResponse.data.status;
2562
+ }
2563
+ }
2732
2564
  const existingConnection = this.connectionCache.get(endpoint);
2733
2565
  if (existingConnection?.isInitialized) {
2734
2566
  this.logger?.info(`Reusing existing connection for agent: ${agentId}`);
2735
2567
  return existingConnection;
2736
2568
  }
2569
+ const clientCapabilities = {
2570
+ ...this.options.clientCapabilities,
2571
+ _meta: {
2572
+ ...this.options.clientCapabilities?._meta,
2573
+ "codebuddy.ai": {
2574
+ ...this.options.clientCapabilities?._meta?.["codebuddy.ai"],
2575
+ cwd
2576
+ }
2577
+ }
2578
+ };
2737
2579
  const connection = new CloudAgentConnection(agentId, {
2738
2580
  endpoint,
2739
2581
  authToken: sessionData.token,
2740
2582
  logger: this.logger,
2741
- clientCapabilities: this.options.clientCapabilities
2742
- });
2583
+ clientCapabilities
2584
+ }, cwd);
2743
2585
  connection.setSessionConnectionInfo({
2744
2586
  sessionId: sessionData.sessionId,
2745
2587
  agentId: sessionData.id,
2746
2588
  link: sessionData.link,
2747
2589
  token: sessionData.token,
2748
2590
  sandboxId: sessionData.sandboxId,
2749
- expireAt: sessionData.expireAt
2591
+ expireAt: sessionData.expireAt,
2592
+ cwd
2750
2593
  });
2751
2594
  try {
2752
2595
  await connection.connect();
@@ -2760,6 +2603,14 @@ var CloudAgentProvider = class CloudAgentProvider {
2760
2603
  this.logger?.debug(`Connection removed from cache: ${endpoint}`);
2761
2604
  });
2762
2605
  this.logger?.info(`Connected to agent: ${agentId}`);
2606
+ this.emitEvent("sessionCreated", {
2607
+ id: agentId,
2608
+ agentId,
2609
+ name: agentName,
2610
+ status: agentStatus,
2611
+ cwd,
2612
+ createdAt: agentCreatedAt
2613
+ });
2763
2614
  return connection;
2764
2615
  }
2765
2616
  /**
@@ -2839,31 +2690,76 @@ var CloudAgentProvider = class CloudAgentProvider {
2839
2690
  }
2840
2691
  }
2841
2692
  /**
2842
- * Get available models for a repository
2693
+ * Get available models from product configuration
2694
+ *
2695
+ * GET {endpoint}/v3/config?repos[]={repo}
2843
2696
  *
2844
- * GET {endpoint}/console/cloudagent/agentmgmt/models?repo={repo}
2697
+ * This method fetches the product configuration from /v3/config API
2698
+ * and extracts the models array from the response.
2845
2699
  *
2846
- * @param repo - Repository identifier
2847
- * @returns Array of model names (backend only provides names, not full ModelInfo)
2700
+ * @param repo - Optional repository URL for context-specific config
2701
+ * @returns Array of ModelInfo with full model details
2848
2702
  */
2849
2703
  async getModels(repo) {
2850
2704
  try {
2851
- const response = await this.request("GET", `/console/cloudagent/agentmgmt/models?repo=${encodeURIComponent(repo)}`);
2852
- if (!response.ok) throw new Error(`Failed to get models: ${response.statusText}`);
2705
+ let url = `${this.options.endpoint}/v3/config`;
2706
+ if (repo) url += `?repos[]=${encodeURIComponent(repo)}`;
2707
+ const headers = {
2708
+ "Accept": "application/json, text/plain, */*",
2709
+ "X-Requested-With": "XMLHttpRequest",
2710
+ ...this.options.headers
2711
+ };
2712
+ if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
2713
+ const account = accountService.getAccount();
2714
+ if (account?.uid) headers["X-User-Id"] = account.uid;
2715
+ if (account?.enterpriseId) {
2716
+ headers["X-Enterprise-Id"] = account.enterpriseId;
2717
+ headers["X-Tenant-Id"] = account.enterpriseId;
2718
+ }
2719
+ headers["X-Product"] = "SaaS";
2720
+ headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
2721
+ headers["X-Request-ID"] = this.generateRequestId();
2722
+ this.logger?.debug(`[CloudAgentProvider] GET ${url}`);
2723
+ const response = await this.fetchImpl(url, {
2724
+ method: "GET",
2725
+ headers,
2726
+ credentials: "include"
2727
+ });
2728
+ if (!response.ok) throw new Error(`Failed to get config: ${response.statusText}`);
2853
2729
  const apiResponse = await response.json();
2854
- if (!apiResponse.data) throw new Error("No data in API response");
2855
- const models = apiResponse.data.models;
2856
- this.logger?.info(`Retrieved ${models.length} models for repo: ${repo}`);
2857
- return models.map((m) => ({
2858
- id: m,
2859
- name: "",
2860
- description: ""
2730
+ if (!apiResponse.data) {
2731
+ this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
2732
+ return [];
2733
+ }
2734
+ const models = apiResponse.data.models ?? [];
2735
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /v3/config`);
2736
+ return models.map((model) => ({
2737
+ id: model.id,
2738
+ name: model.name ?? model.id,
2739
+ description: model.description,
2740
+ credits: model.credits,
2741
+ configurable: model.configurable,
2742
+ configured: model.configured,
2743
+ isDefault: model.isDefault,
2744
+ supportsImages: model.supportsImages,
2745
+ supportsReasoning: model.supportsReasoning,
2746
+ onlyReasoning: model.onlyReasoning,
2747
+ disabledMultimodal: model.disabledMultimodal,
2748
+ disabled: model.disabled,
2749
+ disabledReason: model.disabledReason,
2750
+ disabledAction: model.disabledAction
2861
2751
  }));
2862
2752
  } catch (error) {
2863
- this.logger?.error(`Failed to get models for repo ${repo}:`, error);
2753
+ this.logger?.error(`[CloudAgentProvider] Failed to get models:`, error);
2864
2754
  throw error;
2865
2755
  }
2866
2756
  }
2757
+ /**
2758
+ * Generate a unique request ID
2759
+ */
2760
+ generateRequestId() {
2761
+ return "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx".replace(/x/g, () => Math.floor(Math.random() * 16).toString(16));
2762
+ }
2867
2763
  static {
2868
2764
  this.IMAGE_MIME_TYPES = [
2869
2765
  "image/png",
@@ -2955,9 +2851,47 @@ var CloudAgentProvider = class CloudAgentProvider {
2955
2851
  return {
2956
2852
  success: result.success,
2957
2853
  urls: result.urls,
2854
+ expireSeconds: result.expireSeconds,
2958
2855
  error: result.error
2959
2856
  };
2960
2857
  }
2858
+ /**
2859
+ * Register event listener
2860
+ * @param event - Event name
2861
+ * @param handler - Event handler function
2862
+ */
2863
+ on(event, handler) {
2864
+ if (!this.eventListeners.has(event)) this.eventListeners.set(event, /* @__PURE__ */ new Set());
2865
+ this.eventListeners.get(event).add(handler);
2866
+ return () => {
2867
+ this.off(event, handler);
2868
+ };
2869
+ }
2870
+ /**
2871
+ * Unregister event listener
2872
+ * @param event - Event name
2873
+ * @param handler - Event handler function
2874
+ */
2875
+ off(event, handler) {
2876
+ const listeners = this.eventListeners.get(event);
2877
+ if (listeners) listeners.delete(handler);
2878
+ }
2879
+ /**
2880
+ * Emit event to all registered listeners
2881
+ * @param event - Event name
2882
+ * @param args - Event arguments
2883
+ */
2884
+ emitEvent(event, ...args) {
2885
+ const listeners = this.eventListeners.get(event);
2886
+ if (listeners && listeners.size > 0) {
2887
+ this.logger?.debug(`Emitting event: ${event}`, args);
2888
+ for (const handler of listeners) try {
2889
+ handler(...args);
2890
+ } catch (error) {
2891
+ this.logger?.error(`Error in event handler for ${event}:`, error);
2892
+ }
2893
+ }
2894
+ }
2961
2895
  toAgentState(data) {
2962
2896
  const status = data.sessionStatus || data.status;
2963
2897
  return {
@@ -2977,8 +2911,14 @@ var CloudAgentProvider = class CloudAgentProvider {
2977
2911
  ...this.options.headers
2978
2912
  };
2979
2913
  if (this.options.authToken) headers["Authorization"] = `Bearer ${this.options.authToken}`;
2980
- const enterpriseId = typeof localStorage !== "undefined" ? localStorage.getItem(SELECTED_ACCOUNT_KEY) : null;
2981
- if (enterpriseId) headers["X-Enterprise-Id"] = enterpriseId;
2914
+ const account = accountService.getAccount();
2915
+ if (account?.uid) headers["X-User-Id"] = account.uid;
2916
+ if (account?.enterpriseId) {
2917
+ headers["X-Enterprise-Id"] = account.enterpriseId;
2918
+ headers["X-Tenant-Id"] = account.enterpriseId;
2919
+ }
2920
+ headers["X-Product"] = "SaaS";
2921
+ headers["X-User-Agent"] = "CLI/2.38.0 CodeBuddy/2.38.0";
2982
2922
  const init = {
2983
2923
  method,
2984
2924
  headers
@@ -3076,8 +3016,8 @@ var ActiveSessionImpl = class {
3076
3016
  id: this._agentId,
3077
3017
  status: this.connection.state,
3078
3018
  capabilities: this.connection.capabilities,
3079
- type: "local",
3080
- cwd: ""
3019
+ type: this.connection.transport,
3020
+ cwd: this.connection.cwd || ""
3081
3021
  };
3082
3022
  }
3083
3023
  /**
@@ -3864,6 +3804,514 @@ function isCloudAgentState(state) {
3864
3804
  return state.type === "cloud";
3865
3805
  }
3866
3806
 
3807
+ //#endregion
3808
+ //#region ../agent-provider/src/backend/types.ts
3809
+ /**
3810
+ * 套餐代码
3811
+ */
3812
+ /**
3813
+ * TCACA_code_001_PqouKr6QWV CodeBuddy海外版免费包
3814
+ * TCACA_code_002_AkiJS3ZHF5 CodeBuddy海外版Pro版本包-包月/CodeBuddy Pro Plan - Monthly:
3815
+ * TCACA_code_006_DbXS0lrypC CodeBuddy海外版一次性免费赠送2周的Pro版本包/CodeBuddy One-time Free 2-Week Pro Plan Trial
3816
+ * TCACA_code_007_nzdH5h4Nl0 CodeBuddy海外版运营裂变包/CodeBuddy Growth Plan
3817
+ * TCACA_code_003_FAnt7lcmRT CodeBuddy海外版Pro版本包-包年/CodeBuddy Pro Plan - Yearly
3818
+ * TCACA_code_008_cfWoLwvjU4 赠送月包
3819
+ */
3820
+ let CommodityCode = /* @__PURE__ */ function(CommodityCode) {
3821
+ CommodityCode["free"] = "TCACA_code_001_PqouKr6QWV";
3822
+ CommodityCode["proMon"] = "TCACA_code_002_AkiJS3ZHF5";
3823
+ CommodityCode["proMonPlus"] = "TCACA_code_005_maRGyrHhw1";
3824
+ CommodityCode["gift"] = "TCACA_code_006_DbXS0lrypC";
3825
+ CommodityCode["activity"] = "TCACA_code_007_nzdH5h4Nl0";
3826
+ CommodityCode["proYear"] = "TCACA_code_003_FAnt7lcmRT";
3827
+ CommodityCode["freeMon"] = "TCACA_code_008_cfWoLwvjU4";
3828
+ CommodityCode["extra"] = "TCACA_code_009_0XmEQc2xOf";
3829
+ return CommodityCode;
3830
+ }({});
3831
+ /** 账户状态 */
3832
+ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
3833
+ /** 有效 */
3834
+ AccountStatus[AccountStatus["valid"] = 0] = "valid";
3835
+ /** 已退款 */
3836
+ AccountStatus[AccountStatus["refund"] = 1] = "refund";
3837
+ /** 已过期 */
3838
+ AccountStatus[AccountStatus["expired"] = 2] = "expired";
3839
+ /** 已用完 */
3840
+ AccountStatus[AccountStatus["usedUp"] = 3] = "usedUp";
3841
+ return AccountStatus;
3842
+ }({});
3843
+
3844
+ //#endregion
3845
+ //#region ../agent-provider/src/backend/backend-provider.ts
3846
+ /**
3847
+ * Backend Provider 实现
3848
+ *
3849
+ * 封装与后端 API 的 HTTP 通信
3850
+ */
3851
+ /** 获取当前域名的登录页面 URL */
3852
+ const getLoginUrl = () => `${window.location.origin}/login`;
3853
+ /** 获取当前域名的账号选择页面 URL */
3854
+ const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
3855
+ /** localStorage 中存储选中账号 ID 的 key */
3856
+ const SELECTED_ACCOUNT_KEY = "CODEBUDDY_IDE_SELECTED_ACCOUNT_ID";
3857
+ /**
3858
+ * Backend Provider 实现类
3859
+ *
3860
+ * 职责:
3861
+ * - 与后端 API 通信(getAgents, getModels, getAccount 等)
3862
+ * - 触发登录/登出流程
3863
+ * - 获取 account 后自动同步到 accountService
3864
+ */
3865
+ var BackendProvider = class {
3866
+ constructor(config) {
3867
+ this.baseUrl = config.baseUrl.replace(/\/$/, "");
3868
+ this.authToken = config.authToken;
3869
+ }
3870
+ /**
3871
+ * 获取 Agent 列表
3872
+ * API 端点: GET /v2/cloudagent/agentmgmt/agents
3873
+ */
3874
+ async getAgents(request = {}) {
3875
+ const { MockAgentProvider } = await Promise.resolve().then(() => require("./MockAgentProvider-4e4oOusg.cjs"));
3876
+ const sessions = new MockAgentProvider().getAllSessions();
3877
+ const mockTitles = {
3878
+ "1": "开发五子棋游戏",
3879
+ "2": "修复登录页面样式",
3880
+ "3": "API 接口优化"
3881
+ };
3882
+ const agents = sessions.map((session, index) => ({
3883
+ id: session.sessionId,
3884
+ name: mockTitles[session.sessionId] || `Agent ${session.sessionId}`,
3885
+ status: "RUNNING",
3886
+ visibility: "PRIVATE",
3887
+ createdAt: new Date(session.createdAt).toISOString(),
3888
+ summary: `Session created at ${new Date(session.createdAt).toLocaleString()}`,
3889
+ source: {
3890
+ provider: "github",
3891
+ ref: "refs/heads/main",
3892
+ repository: session.cwd
3893
+ },
3894
+ target: {
3895
+ autoCreatePr: false,
3896
+ branchName: "feature/mock",
3897
+ prUrl: void 0,
3898
+ url: void 0
3899
+ }
3900
+ }));
3901
+ return {
3902
+ agents,
3903
+ pagination: {
3904
+ hasNext: false,
3905
+ hasPrev: false,
3906
+ page: 1,
3907
+ size: agents.length,
3908
+ total: agents.length,
3909
+ totalPages: 1
3910
+ }
3911
+ };
3912
+ }
3913
+ /**
3914
+ * 获取可用模型列表
3915
+ * API 端点: GET /v2/cloudagent/models (假设)
3916
+ *
3917
+ * 当前实现: 返回 Mock 数据
3918
+ */
3919
+ async getModels(request) {
3920
+ const mockModels = [{
3921
+ id: "glm-4.7",
3922
+ name: "GLM-4.7",
3923
+ vendor: "f",
3924
+ maxOutputTokens: 48e3,
3925
+ maxInputTokens: 2e5,
3926
+ supportsToolCall: true,
3927
+ supportsImages: false,
3928
+ disabledMultimodal: true,
3929
+ maxAllowedSize: 2e5,
3930
+ supportsReasoning: true,
3931
+ onlyReasoning: true,
3932
+ temperature: 1,
3933
+ reasoning: {
3934
+ effort: "medium",
3935
+ summary: "auto"
3936
+ },
3937
+ descriptionEn: "GLM-4.7 model, Well-rounded model for everyday use",
3938
+ descriptionZh: "GLM-4.7 大模型,能力均衡,适合日常使用"
3939
+ }, {
3940
+ id: "glm-4.7-flash",
3941
+ name: "GLM-4.7 Flash",
3942
+ vendor: "f",
3943
+ maxOutputTokens: 4e4,
3944
+ maxInputTokens: 128e3,
3945
+ supportsToolCall: true,
3946
+ supportsImages: false,
3947
+ disabledMultimodal: false,
3948
+ maxAllowedSize: 128e3,
3949
+ supportsReasoning: false,
3950
+ onlyReasoning: false,
3951
+ temperature: .7,
3952
+ reasoning: {
3953
+ effort: "low",
3954
+ summary: "never"
3955
+ },
3956
+ descriptionEn: "GLM-4.7 Flash, Fast and efficient model",
3957
+ descriptionZh: "GLM-4.7 Flash,快速高效的模型"
3958
+ }];
3959
+ console.log("[BackendProvider] getModels called for repository:", request.repository);
3960
+ return { models: mockModels };
3961
+ }
3962
+ /**
3963
+ * 获取当前账号信息
3964
+ * API 端点: GET /console/accounts (返回账号列表)
3965
+ *
3966
+ * 逻辑:
3967
+ * 1. 从 localStorage 读取 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID
3968
+ * 2. 根据 CODEBUDDY_IDE_SELECTED_ACCOUNT_ID 找到对应账号
3969
+ * - personal 类型: 用 uid 匹配
3970
+ * - 其他类型: 用 enterpriseId 匹配
3971
+ * 3. 如果没有选中的账号,跳转到账号选择页面
3972
+ * 4. 获取套餐信息并合并到账号中
3973
+ * 5. 同步到 accountService
3974
+ */
3975
+ async getAccount() {
3976
+ const url = `${this.baseUrl}/console/accounts`;
3977
+ const headers = {
3978
+ "Content-Type": "application/json",
3979
+ "Accept": "application/json"
3980
+ };
3981
+ if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
3982
+ try {
3983
+ const response = await fetch(url, {
3984
+ method: "GET",
3985
+ headers,
3986
+ credentials: "include"
3987
+ });
3988
+ if (!response.ok) {
3989
+ if (response.status === 401 || response.status === 403) {
3990
+ accountService.setAccount(null);
3991
+ return null;
3992
+ }
3993
+ const error = await response.json().catch(() => ({ message: response.statusText }));
3994
+ throw new Error(error.message || `HTTP ${response.status}`);
3995
+ }
3996
+ if (!(response.headers.get("Content-Type") || "").includes("application/json")) {
3997
+ accountService.setAccount(null);
3998
+ return null;
3999
+ }
4000
+ const { data = {} } = await response.json();
4001
+ const accounts = data?.accounts || [];
4002
+ if (!accounts || accounts.length === 0) {
4003
+ accountService.setAccount(null);
4004
+ return null;
4005
+ }
4006
+ const selectedAccountId = localStorage.getItem(SELECTED_ACCOUNT_KEY);
4007
+ let selectedAccount;
4008
+ if (selectedAccountId) {
4009
+ selectedAccount = accounts.find((account) => {
4010
+ if (account.type === "personal") return account.uid === selectedAccountId;
4011
+ return account.enterpriseId === selectedAccountId;
4012
+ });
4013
+ if (selectedAccount) try {
4014
+ const plan = await this.getCurrentPlan();
4015
+ const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
4016
+ console.log("account", {
4017
+ ...selectedAccount,
4018
+ ...plan,
4019
+ editionType
4020
+ });
4021
+ const account = {
4022
+ ...selectedAccount,
4023
+ ...plan,
4024
+ editionType
4025
+ };
4026
+ accountService.setAccount(account);
4027
+ return account;
4028
+ } catch (error) {
4029
+ accountService.setAccount(selectedAccount);
4030
+ return { ...selectedAccount };
4031
+ }
4032
+ }
4033
+ if (accounts.length === 1) {
4034
+ selectedAccount = accounts[0];
4035
+ const accountId = selectedAccount.type === "personal" ? selectedAccount.uid : selectedAccount.enterpriseId;
4036
+ if (accountId) localStorage.setItem(SELECTED_ACCOUNT_KEY, accountId);
4037
+ const plan = await this.getCurrentPlan();
4038
+ const editionType = this.getEditionDisplayType(selectedAccount.type, plan.isPro);
4039
+ console.log("account (auto-selected)", {
4040
+ ...selectedAccount,
4041
+ ...plan,
4042
+ editionType
4043
+ });
4044
+ const account = {
4045
+ ...selectedAccount,
4046
+ ...plan,
4047
+ editionType
4048
+ };
4049
+ accountService.setAccount(account);
4050
+ return account;
4051
+ }
4052
+ const redirectUrl = encodeURIComponent(window.location.href);
4053
+ window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
4054
+ accountService.setAccount(null);
4055
+ return null;
4056
+ } catch (error) {
4057
+ console.error("[BackendProvider] getAccount failed:", error);
4058
+ accountService.setAccount(null);
4059
+ return null;
4060
+ }
4061
+ }
4062
+ /**
4063
+ * 获取当前套餐信息
4064
+ * 从计量计费接口获取用户的套餐信息
4065
+ * API: POST /billing/meter/get-user-resource
4066
+ */
4067
+ async getCurrentPlan() {
4068
+ const defaultPlan = {
4069
+ isPro: false,
4070
+ expireAt: 0,
4071
+ renewFlag: 0,
4072
+ PackageCode: void 0,
4073
+ name: ""
4074
+ };
4075
+ try {
4076
+ const url = `${this.baseUrl}/billing/meter/get-user-resource`;
4077
+ const headers = {
4078
+ "Content-Type": "application/json",
4079
+ "Accept": "application/json"
4080
+ };
4081
+ if (this.authToken) headers["Authorization"] = `Bearer ${this.authToken}`;
4082
+ const now = /* @__PURE__ */ new Date();
4083
+ const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
4084
+ const formatDate = (d) => {
4085
+ const pad = (n) => n.toString().padStart(2, "0");
4086
+ return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
4087
+ };
4088
+ const body = {
4089
+ PageNumber: 1,
4090
+ PageSize: 100,
4091
+ ProductCode: "p_tcaca",
4092
+ Status: [AccountStatus.valid, AccountStatus.usedUp],
4093
+ PackageEndTimeRangeBegin: formatDate(now),
4094
+ PackageEndTimeRangeEnd: formatDate(futureDate)
4095
+ };
4096
+ const response = await fetch(url, {
4097
+ method: "POST",
4098
+ headers,
4099
+ credentials: "include",
4100
+ body: JSON.stringify(body)
4101
+ });
4102
+ if (!response.ok) {
4103
+ console.warn("[BackendProvider] getCurrentPlan failed:", response.status);
4104
+ return defaultPlan;
4105
+ }
4106
+ const resources = (await response.json())?.data?.Response?.Data?.Accounts || [];
4107
+ if (!resources || resources.length === 0) return defaultPlan;
4108
+ const proPlan = resources.find((r) => r.PackageCode === CommodityCode.proYear || r.PackageCode === CommodityCode.proMon || r.PackageCode === CommodityCode.proMonPlus);
4109
+ const trialPlan = resources.find((r) => r.PackageCode === CommodityCode.gift || r.PackageCode === CommodityCode.freeMon);
4110
+ const activePlan = proPlan || trialPlan;
4111
+ if (activePlan) {
4112
+ const parseTime = (time) => {
4113
+ if (!time) return 0;
4114
+ return new Date(time).getTime();
4115
+ };
4116
+ return {
4117
+ isPro: !!proPlan,
4118
+ isTria: [AccountStatus.valid, AccountStatus.usedUp].includes(trialPlan?.Status),
4119
+ expireAt: parseTime(activePlan.DeductionEndTime || activePlan.ExpiredTime || activePlan.CycleEndTime),
4120
+ refreshAt: parseTime(activePlan.CycleEndTime),
4121
+ renewFlag: Number(activePlan.AutoRenewFlag) === 1 ? 1 : 0,
4122
+ PackageCode: activePlan.PackageCode,
4123
+ name: activePlan.PackageName || "",
4124
+ usageTotal: activePlan.CycleCapacitySizePrecise,
4125
+ usageUsed: activePlan.CapacityUsedPrecise,
4126
+ usageLeft: activePlan.CycleCapacityRemainPrecise
4127
+ };
4128
+ }
4129
+ return defaultPlan;
4130
+ } catch (error) {
4131
+ console.error("[BackendProvider] getCurrentPlan error:", error);
4132
+ return defaultPlan;
4133
+ }
4134
+ }
4135
+ /**
4136
+ * 根据账号类型和 Pro 状态计算版本展示类型
4137
+ * - personal + isPro = 'pro'
4138
+ * - personal + !isPro = 'free'
4139
+ * - ultimate = 'ultimate' (旗舰版/团队版)
4140
+ * - exclusive = 'exclusive' (专享版/企业版)
4141
+ */
4142
+ getEditionDisplayType(type, isPro) {
4143
+ if (type === "personal") return isPro ? "pro" : "free";
4144
+ if (type === "ultimate") return "ultimate";
4145
+ if (type === "exclusive") return "exclusive";
4146
+ return "free";
4147
+ }
4148
+ /**
4149
+ * 触发登录流程
4150
+ * Web 环境: 跳转到登录页面
4151
+ */
4152
+ async login() {
4153
+ const redirectUrl = encodeURIComponent(window.location.href);
4154
+ window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
4155
+ }
4156
+ /**
4157
+ * 登出账号
4158
+ * Web 环境: 调用登出接口并清除本地状态
4159
+ */
4160
+ async logout() {
4161
+ const url = `${this.baseUrl}/console/logout`;
4162
+ try {
4163
+ await fetch(url, {
4164
+ method: "POST",
4165
+ credentials: "include"
4166
+ });
4167
+ } catch (error) {
4168
+ console.error("[BackendProvider] logout failed:", error);
4169
+ }
4170
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY);
4171
+ accountService.clearAccount();
4172
+ }
4173
+ };
4174
+ /**
4175
+ * 创建 BackendProvider 实例
4176
+ */
4177
+ function createBackendProvider(config) {
4178
+ return new BackendProvider(config);
4179
+ }
4180
+
4181
+ //#endregion
4182
+ //#region ../agent-provider/src/backend/ipc-backend-provider.ts
4183
+ /**
4184
+ * Backend 请求类型常量
4185
+ */
4186
+ const BACKEND_REQUEST_TYPES = {
4187
+ GET_AGENTS: "backend:get-agents-request",
4188
+ GET_MODELS: "backend:get-models",
4189
+ LOGIN: "backend:login",
4190
+ LOGOUT: "backend:logout",
4191
+ GET_ACCOUNT: "backend:get-account"
4192
+ };
4193
+ /**
4194
+ * 生成唯一请求 ID
4195
+ */
4196
+ function generateRequestId() {
4197
+ return `req-${Date.now()}-${Math.random().toString(36).slice(2)}`;
4198
+ }
4199
+ /**
4200
+ * IPC Backend Provider 实现类
4201
+ *
4202
+ * 通过 IWidgetChannel 与后端通信获取 Agent 列表
4203
+ */
4204
+ var IPCBackendProvider = class {
4205
+ constructor(config) {
4206
+ this.channel = config.channel;
4207
+ this.debug = config.debug ?? false;
4208
+ this.timeoutMs = config.timeoutMs ?? 3e4;
4209
+ this.log("Initialized with IWidgetChannel");
4210
+ }
4211
+ /**
4212
+ * 发送统一格式的后端请求
4213
+ * @param requestType 请求类型
4214
+ * @param params 请求参数
4215
+ * @returns 响应数据
4216
+ */
4217
+ async sendBackendRequest(requestType, params) {
4218
+ const message = {
4219
+ type: "backend",
4220
+ requestId: generateRequestId(),
4221
+ params: {
4222
+ type: requestType,
4223
+ params
4224
+ }
4225
+ };
4226
+ this.log("Sending backend request:", message);
4227
+ const response = await this.channel.callMethod("__backend__", message, this.timeoutMs);
4228
+ this.log("Received response:", response);
4229
+ if (response?.error) throw new Error(response.error);
4230
+ return response?.data !== void 0 ? response.data : response;
4231
+ }
4232
+ /**
4233
+ * 获取 Agent 列表
4234
+ * 通过 IWidgetChannel 发送请求到后端
4235
+ */
4236
+ async getAgents(request = {}) {
4237
+ this.log("Getting agents with request:", request);
4238
+ try {
4239
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_AGENTS, request);
4240
+ } catch (error) {
4241
+ this.log("Get agents failed:", error);
4242
+ throw error;
4243
+ }
4244
+ }
4245
+ /**
4246
+ * 获取可用模型列表
4247
+ * 通过 IWidgetChannel 发送请求到后端
4248
+ */
4249
+ async getModels(request) {
4250
+ this.log("Getting models with request:", request);
4251
+ try {
4252
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_MODELS, request);
4253
+ } catch (error) {
4254
+ this.log("Get models failed:", error);
4255
+ throw error;
4256
+ }
4257
+ }
4258
+ /**
4259
+ * 获取当前账号信息
4260
+ * IDE 环境: 通过 IPC 获取账号信息,并同步到 accountService
4261
+ */
4262
+ async getAccount() {
4263
+ this.log("Getting account via IPC");
4264
+ try {
4265
+ const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
4266
+ accountService.setAccount(account);
4267
+ return account;
4268
+ } catch (error) {
4269
+ this.log("Get account failed:", error);
4270
+ accountService.setAccount(null);
4271
+ return null;
4272
+ }
4273
+ }
4274
+ /**
4275
+ * 触发登录流程
4276
+ * IDE 环境: 通过 IPC 通知 IDE 打开登录流程
4277
+ */
4278
+ async login() {
4279
+ this.log("Triggering login via IPC");
4280
+ try {
4281
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGIN);
4282
+ } catch (error) {
4283
+ this.log("Login request failed:", error);
4284
+ throw error;
4285
+ }
4286
+ }
4287
+ /**
4288
+ * 登出账号
4289
+ * IDE 环境: 通过 IPC 通知 IDE 登出
4290
+ */
4291
+ async logout() {
4292
+ this.log("Triggering logout via IPC");
4293
+ try {
4294
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.LOGOUT);
4295
+ accountService.clearAccount();
4296
+ } catch (error) {
4297
+ this.log("Logout request failed:", error);
4298
+ throw error;
4299
+ }
4300
+ }
4301
+ /**
4302
+ * 调试日志
4303
+ */
4304
+ log(...args) {
4305
+ if (this.debug) console.log("[IPCBackendProvider]", ...args);
4306
+ }
4307
+ };
4308
+ /**
4309
+ * 创建 IPCBackendProvider 实例
4310
+ */
4311
+ function createIPCBackendProvider(config) {
4312
+ return new IPCBackendProvider(config);
4313
+ }
4314
+
3867
4315
  //#endregion
3868
4316
  exports.ActiveSessionImpl = ActiveSessionImpl;
3869
4317
  exports.AgentClient = AgentClient;