@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.2f2e439.20260206

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.mjs CHANGED
@@ -2372,7 +2372,7 @@ var CloudAgentConnection = class {
2372
2372
  }
2373
2373
  async createSession(params) {
2374
2374
  return {
2375
- ...await this.client.loadSession(this.agentId, this.cwd),
2375
+ ...await this.client.createSession(this.cwd),
2376
2376
  sessionId: this.agentId
2377
2377
  };
2378
2378
  }
@@ -5253,7 +5253,7 @@ var axios_default = axios;
5253
5253
  * 特性:
5254
5254
  * - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
5255
5255
  * - 支持拦截器注册(其他模块可注入 header)
5256
- * - 统一 401/403 处理
5256
+ * - 统一 401 处理(支持自动刷新 token 并重试)
5257
5257
  * - 自动携带凭证(withCredentials)
5258
5258
  * - 类型安全
5259
5259
  *
@@ -5293,6 +5293,8 @@ var HttpService = class HttpService {
5293
5293
  */
5294
5294
  constructor(config = {}) {
5295
5295
  this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
5296
+ this.isRefreshing = false;
5297
+ this.refreshSubscribers = [];
5296
5298
  this.config = config;
5297
5299
  this.axiosInstance = axios_default.create({
5298
5300
  baseURL: config.baseURL?.replace(/\/$/, "") || "",
@@ -5333,18 +5335,54 @@ var HttpService = class HttpService {
5333
5335
  }, (error) => Promise.reject(error));
5334
5336
  }
5335
5337
  /**
5336
- * 注册默认响应拦截器(处理 401/403)
5338
+ * 注册默认响应拦截器(处理 401,支持自动重试)
5337
5339
  */
5338
5340
  registerDefaultResponseInterceptor() {
5339
- this.axiosInstance.interceptors.response.use((response) => response, (error) => {
5340
- if (error.response?.status === 401 || error.response?.status === 403) {
5341
- console.warn("[HttpService] Unauthorized (401/403), triggering callbacks");
5342
- this.triggerUnauthorizedCallbacks();
5341
+ this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
5342
+ const originalRequest = error.config;
5343
+ if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
5344
+ if (originalRequest.url?.includes("/console/accounts")) {
5345
+ console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
5346
+ return Promise.reject(error);
5347
+ }
5348
+ console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
5349
+ originalRequest._retry = true;
5350
+ if (this.isRefreshing) return new Promise((resolve, reject) => {
5351
+ this.refreshSubscribers.push((success) => {
5352
+ if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
5353
+ else reject(error);
5354
+ });
5355
+ });
5356
+ this.isRefreshing = true;
5357
+ try {
5358
+ await this.triggerUnauthorizedCallbacks();
5359
+ this.onRefreshSuccess();
5360
+ return this.axiosInstance.request(originalRequest);
5361
+ } catch (refreshError) {
5362
+ this.onRefreshFailure();
5363
+ return Promise.reject(error);
5364
+ } finally {
5365
+ this.isRefreshing = false;
5366
+ }
5343
5367
  }
5344
5368
  return Promise.reject(error);
5345
5369
  });
5346
5370
  }
5347
5371
  /**
5372
+ * token 刷新成功,通知所有等待的请求
5373
+ */
5374
+ onRefreshSuccess() {
5375
+ this.refreshSubscribers.forEach((callback) => callback(true));
5376
+ this.refreshSubscribers = [];
5377
+ }
5378
+ /**
5379
+ * token 刷新失败,通知所有等待的请求
5380
+ */
5381
+ onRefreshFailure() {
5382
+ this.refreshSubscribers.forEach((callback) => callback(false));
5383
+ this.refreshSubscribers = [];
5384
+ }
5385
+ /**
5348
5386
  * 注册请求拦截器
5349
5387
  * @param onFulfilled 请求成功拦截器
5350
5388
  * @param onRejected 请求失败拦截器
@@ -5421,16 +5459,18 @@ var HttpService = class HttpService {
5421
5459
  this.unauthorizedCallbacks.delete(callback);
5422
5460
  }
5423
5461
  /**
5424
- * 触发所有 401 回调
5462
+ * 触发所有 401 回调并等待完成
5463
+ * @returns Promise,等待所有回调完成
5464
+ * @throws 如果任何回调失败,则抛出错误
5425
5465
  */
5426
- triggerUnauthorizedCallbacks() {
5427
- this.unauthorizedCallbacks.forEach((callback) => {
5428
- try {
5429
- callback();
5430
- } catch (error) {
5431
- console.error("[HttpService] Error in unauthorized callback:", error);
5432
- }
5433
- });
5466
+ async triggerUnauthorizedCallbacks() {
5467
+ const callbacks = Array.from(this.unauthorizedCallbacks);
5468
+ const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
5469
+ if (failedResults.length > 0) {
5470
+ const errors = failedResults.map((r) => r.reason);
5471
+ console.error("[HttpService] Some unauthorized callbacks failed:", errors);
5472
+ throw errors[0];
5473
+ }
5434
5474
  }
5435
5475
  /**
5436
5476
  * 更新 authToken
@@ -5542,6 +5582,7 @@ var AccountService = class {
5542
5582
  this.initPromise = null;
5543
5583
  this.initResolve = null;
5544
5584
  this.requestInterceptorId = null;
5585
+ this.crossTabBroadcaster = null;
5545
5586
  this.initPromise = new Promise((resolve) => {
5546
5587
  this.initResolve = resolve;
5547
5588
  });
@@ -5590,15 +5631,34 @@ var AccountService = class {
5590
5631
  this.initialized = true;
5591
5632
  this.initResolve?.(account);
5592
5633
  }
5593
- if (!wasInitialized || prev?.uid !== account?.uid) this.notifyListeners();
5634
+ if (!wasInitialized || prev?.uid !== account?.uid) {
5635
+ this.notifyListeners();
5636
+ if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
5637
+ }
5594
5638
  }
5595
5639
  /**
5596
5640
  * 清除账号(登出)
5641
+ * 先广播登出消息,再清除本地账号
5597
5642
  */
5598
5643
  clearAccount() {
5644
+ if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
5645
+ this.setAccount(null);
5646
+ }
5647
+ /**
5648
+ * 静默清除账号(不广播)
5649
+ * 用于收到其他标签页 logout 消息时,避免循环广播
5650
+ */
5651
+ clearAccountSilently() {
5599
5652
  this.setAccount(null);
5600
5653
  }
5601
5654
  /**
5655
+ * 设置跨标签页认证同步广播器
5656
+ * 应在应用初始化时由上层(如 agent-ui)调用
5657
+ */
5658
+ setCrossTabBroadcaster(broadcaster) {
5659
+ this.crossTabBroadcaster = broadcaster;
5660
+ }
5661
+ /**
5602
5662
  * 订阅账号变化
5603
5663
  * @param callback 变化时的回调函数
5604
5664
  * @returns 取消订阅函数
@@ -5663,6 +5723,11 @@ var AccountService = class {
5663
5723
  * 导出单例实例
5664
5724
  */
5665
5725
  const accountService = new AccountService();
5726
+ /**
5727
+ * 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
5728
+ * 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
5729
+ */
5730
+ if (typeof window !== "undefined") window.__genieAccountService = accountService;
5666
5731
 
5667
5732
  //#endregion
5668
5733
  //#region ../agent-provider/src/common/utils/concurrency.ts
@@ -6252,15 +6317,9 @@ var CloudAgentProvider = class CloudAgentProvider {
6252
6317
  const url = this.buildGetUrl("/console/as/conversations/", params);
6253
6318
  const apiResponse = await httpService.get(url);
6254
6319
  if (!apiResponse.data) throw new Error("No data in API response");
6255
- const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
6256
- const pagination = apiResponse.data.pagination;
6257
- console.log("[CloudAgentProvider] API response:", {
6258
- agentsCount: agents.length,
6259
- pagination
6260
- });
6261
6320
  return {
6262
- agents,
6263
- pagination
6321
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
6322
+ pagination: apiResponse.data.pagination
6264
6323
  };
6265
6324
  } catch (error) {
6266
6325
  this.logger?.error("Failed to list agents:", error);
@@ -6270,13 +6329,21 @@ var CloudAgentProvider = class CloudAgentProvider {
6270
6329
  /**
6271
6330
  * Create a new conversation
6272
6331
  * POST {endpoint}/console/as/conversations
6332
+ * @param params - Session params containing cwd and optional configuration
6273
6333
  */
6274
- async create() {
6334
+ async create(params) {
6275
6335
  try {
6276
- const apiResponse = await httpService.post("/console/as/conversations/", {
6277
- prompt: "",
6278
- model: "deepseek-r1"
6279
- });
6336
+ const { options = {} } = params;
6337
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
6338
+ const tagsObj = options.tags || codebuddyMeta?.tags;
6339
+ const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
6340
+ const createPayload = {
6341
+ prompt: (options.prompt || "").slice(0, 100),
6342
+ model: options.model || "deepseek-r1",
6343
+ ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
6344
+ };
6345
+ console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
6346
+ const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
6280
6347
  if (!apiResponse.data) throw new Error("No data in API response");
6281
6348
  this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
6282
6349
  return apiResponse.data.id;
@@ -6642,17 +6709,20 @@ var CloudAgentProvider = class CloudAgentProvider {
6642
6709
  * API 端点: GET /console/as/support/scenes
6643
6710
  * 用于 Welcome 页面的 QuickActions 快捷操作
6644
6711
  *
6712
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
6645
6713
  * @returns Promise<SupportScene[]> 支持的场景列表
6646
6714
  */
6647
- async getSupportScenes() {
6715
+ async getSupportScenes(locale) {
6648
6716
  try {
6649
- const apiResponse = await httpService.get("/console/as/support/scenes");
6717
+ let url = "/console/as/support/scenes";
6718
+ if (locale) url += `?locale=${encodeURIComponent(locale)}`;
6719
+ const apiResponse = await httpService.get(url);
6650
6720
  if (!apiResponse.data) {
6651
6721
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
6652
6722
  return [];
6653
6723
  }
6654
6724
  const scenes = apiResponse.data.scenes || [];
6655
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
6725
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
6656
6726
  return scenes;
6657
6727
  } catch (error) {
6658
6728
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -6668,7 +6738,8 @@ var CloudAgentProvider = class CloudAgentProvider {
6668
6738
  type: "cloud",
6669
6739
  status,
6670
6740
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
6671
- capabilities: this.options.clientCapabilities
6741
+ capabilities: this.options.clientCapabilities,
6742
+ isUserDefinedTitle: data.isUserDefinedTitle
6672
6743
  };
6673
6744
  }
6674
6745
  /**
@@ -6963,8 +7034,8 @@ var ActiveSessionImpl = class {
6963
7034
  * await session.setMode('architect');
6964
7035
  * ```
6965
7036
  */
6966
- async setMode(modeId) {
6967
- if (this._availableModes) {
7037
+ async setMode(modeId, skipAvailableChecker) {
7038
+ if (this._availableModes && !skipAvailableChecker) {
6968
7039
  if (!this._availableModes.some((m) => m.id === modeId)) {
6969
7040
  const availableIds = this._availableModes.map((m) => m.id).join(", ");
6970
7041
  throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
@@ -7155,14 +7226,6 @@ var ActiveSessionImpl = class {
7155
7226
  //#endregion
7156
7227
  //#region ../agent-provider/src/common/client/session-manager.ts
7157
7228
  /**
7158
- * SessionManager - Manages session lifecycle and connections
7159
- *
7160
- * Provides the core implementation for session-centric API operations:
7161
- * - list() - Lists sessions (mapped from agents)
7162
- * - createSession() - Creates new session (auto-creates agent)
7163
- * - loadSession() - Loads existing session (finds agent by sessionId)
7164
- */
7165
- /**
7166
7229
  * SessionManager - Session lifecycle management
7167
7230
  *
7168
7231
  * This class manages the relationship between sessions and agents.
@@ -7212,7 +7275,8 @@ var SessionManager = class {
7212
7275
  createdAt: agent.createdAt,
7213
7276
  lastActivityAt: agent.updatedAt,
7214
7277
  cwd: agent.type === "local" ? agent.cwd : void 0,
7215
- isPlayground: agent.isPlayground
7278
+ isPlayground: agent.isPlayground,
7279
+ isUserDefinedTitle: agent.isUserDefinedTitle
7216
7280
  }));
7217
7281
  console.log("[SessionManager] Returning sessions:", {
7218
7282
  count: sessions.length,
@@ -7243,9 +7307,9 @@ var SessionManager = class {
7243
7307
  const connection = await this.provider.connect(agentId);
7244
7308
  this.logger?.debug(`Connected to agent: ${agentId}`);
7245
7309
  const response = await connection.createSession({
7246
- _meta: params._meta,
7310
+ _meta: params.options?._meta,
7247
7311
  cwd: params.cwd,
7248
- mcpServers: params.mcpServers
7312
+ mcpServers: params.options?.mcpServers
7249
7313
  });
7250
7314
  if (this.provider.registerSession) {
7251
7315
  this.provider.registerSession(response.sessionId, agentId);
@@ -7258,14 +7322,8 @@ var SessionManager = class {
7258
7322
  connectionInfo
7259
7323
  });
7260
7324
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7261
- if (response.models?.availableModels) {
7262
- const localModels = response.models.availableModels.map((m) => ({
7263
- id: m.modelId,
7264
- name: m.name,
7265
- description: m.description ?? void 0
7266
- }));
7267
- session.setModels(localModels, response.models?.currentModelId);
7268
- }
7325
+ const availableModels = this.extractAvailableModels(response);
7326
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
7269
7327
  this.logger?.info(`Session created: ${response.sessionId}`);
7270
7328
  return session;
7271
7329
  }
@@ -7301,17 +7359,31 @@ var SessionManager = class {
7301
7359
  mcpServers: params.mcpServers
7302
7360
  });
7303
7361
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
7304
- if (response.models?.availableModels) {
7305
- const localModels = response.models.availableModels.map((m) => ({
7306
- id: m.modelId,
7307
- name: m.name,
7308
- description: m.description ?? void 0
7309
- }));
7310
- session.setModels(localModels, response.models?.currentModelId);
7311
- }
7362
+ const availableModels = this.extractAvailableModels(response);
7363
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
7312
7364
  this.logger?.info(`Session loaded: ${params.sessionId}`);
7313
7365
  return session;
7314
7366
  }
7367
+ /**
7368
+ * 从 ACP response 中提取可用模型列表
7369
+ *
7370
+ * 优先级:
7371
+ * 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
7372
+ * 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
7373
+ * 3. undefined - 都没有时返回 undefined
7374
+ *
7375
+ * @param response - ACP 响应对象
7376
+ * @returns ModelInfo[] | undefined
7377
+ */
7378
+ extractAvailableModels(response) {
7379
+ const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
7380
+ if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
7381
+ const availableModels = response.models?.availableModels;
7382
+ if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
7383
+ ...model,
7384
+ ...model._meta?.["codebuddy.ai"] || {}
7385
+ }));
7386
+ }
7315
7387
  };
7316
7388
 
7317
7389
  //#endregion
@@ -7584,6 +7656,28 @@ var AgentClient = class {
7584
7656
  };
7585
7657
  }
7586
7658
  },
7659
+ getSubagentList: async (params) => {
7660
+ try {
7661
+ if (this.provider && this.provider.getSubagentList) {
7662
+ const result = await this.provider.getSubagentList(params);
7663
+ this.logger?.info("Subagent list retrieved", {
7664
+ resultCount: result.results.length,
7665
+ hasError: !!result.error
7666
+ });
7667
+ return result;
7668
+ }
7669
+ return {
7670
+ results: [],
7671
+ error: "Provider does not support getSubagentList"
7672
+ };
7673
+ } catch (error) {
7674
+ this.logger?.error("Failed to get subagent list", error);
7675
+ return {
7676
+ results: [],
7677
+ error: error instanceof Error ? error.message : "Unknown error"
7678
+ };
7679
+ }
7680
+ },
7587
7681
  batchTogglePlugins: async (request) => {
7588
7682
  try {
7589
7683
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -7628,10 +7722,10 @@ var AgentClient = class {
7628
7722
  return [];
7629
7723
  }
7630
7724
  },
7631
- installPlugins: async (pluginNames, marketplaceName, installScope) => {
7725
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource) => {
7632
7726
  try {
7633
7727
  if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
7634
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
7728
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource);
7635
7729
  this.logger?.info("Install plugins", {
7636
7730
  pluginNames,
7637
7731
  marketplaceName,
@@ -7652,13 +7746,9 @@ var AgentClient = class {
7652
7746
  };
7653
7747
  }
7654
7748
  },
7655
- getSupportScenes: async () => {
7749
+ getSupportScenes: async (locale) => {
7656
7750
  try {
7657
- if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") {
7658
- const result = await this.provider.getSupportScenes();
7659
- this.logger?.info("Got support scenes", { count: result?.length ?? 0 });
7660
- return result;
7661
- }
7751
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
7662
7752
  this.logger?.warn("Provider does not support getSupportScenes");
7663
7753
  return [];
7664
7754
  } catch (error) {
@@ -7666,6 +7756,69 @@ var AgentClient = class {
7666
7756
  return [];
7667
7757
  }
7668
7758
  },
7759
+ getAvailableCommands: async (sessionId) => {
7760
+ try {
7761
+ if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
7762
+ const result = await this.provider.getAvailableCommands(sessionId);
7763
+ this.logger?.info("Got available commands from provider", {
7764
+ sessionId: sessionId ?? "(default)",
7765
+ count: result?.length ?? 0
7766
+ });
7767
+ return result;
7768
+ }
7769
+ this.logger?.warn("Provider does not support getAvailableCommands", { sessionId });
7770
+ return [];
7771
+ } catch (error) {
7772
+ this.logger?.error("Failed to get available commands", error);
7773
+ return [];
7774
+ }
7775
+ },
7776
+ respondToSampling: async (sessionId, response) => {
7777
+ try {
7778
+ if (this.provider?.respondToSampling) {
7779
+ await this.provider.respondToSampling(sessionId, response);
7780
+ this.logger?.info("Responded to sampling request", {
7781
+ sessionId,
7782
+ requestId: response.id,
7783
+ approved: response.approved
7784
+ });
7785
+ } else this.logger?.warn("Provider does not support respondToSampling");
7786
+ } catch (error) {
7787
+ this.logger?.error("Failed to respond to sampling request", error);
7788
+ throw error;
7789
+ }
7790
+ },
7791
+ respondToRoots: async (sessionId, response) => {
7792
+ try {
7793
+ if (this.provider?.respondToRoots) {
7794
+ await this.provider.respondToRoots(sessionId, response);
7795
+ this.logger?.info("Responded to roots request", {
7796
+ sessionId,
7797
+ requestId: response.id,
7798
+ approved: response.approved
7799
+ });
7800
+ } else this.logger?.warn("Provider does not support respondToRoots");
7801
+ } catch (error) {
7802
+ this.logger?.error("Failed to respond to roots request", error);
7803
+ throw error;
7804
+ }
7805
+ },
7806
+ subscribeSamplingRequests: (serverName, callback) => {
7807
+ if (this.provider?.subscribeSamplingRequests) {
7808
+ this.logger?.info("Subscribing to sampling requests", { serverName });
7809
+ return this.provider.subscribeSamplingRequests(serverName, callback);
7810
+ }
7811
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
7812
+ return () => {};
7813
+ },
7814
+ subscribeRootsRequests: (serverName, callback) => {
7815
+ if (this.provider?.subscribeRootsRequests) {
7816
+ this.logger?.info("Subscribing to roots requests", { serverName });
7817
+ return this.provider.subscribeRootsRequests(serverName, callback);
7818
+ }
7819
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
7820
+ return () => {};
7821
+ },
7669
7822
  models: this.createModelsResource()
7670
7823
  };
7671
7824
  }
@@ -7732,6 +7885,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
7732
7885
  return AccountStatus;
7733
7886
  }({});
7734
7887
 
7888
+ //#endregion
7889
+ //#region ../agent-provider/src/backend/service/oauth-repository-service.ts
7890
+ /**
7891
+ * OAuth Repository Service
7892
+ *
7893
+ * 封装 OAuth 连接器相关的仓库和分支操作
7894
+ */
7895
+ /**
7896
+ * OAuth Repository Service
7897
+ *
7898
+ * 提供仓库和分支的查询操作
7899
+ */
7900
+ var OAuthRepositoryService = class {
7901
+ /**
7902
+ * 获取仓库分支列表
7903
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
7904
+ *
7905
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
7906
+ * @param params 平台特定的查询参数
7907
+ * @param page 页码,从1开始,0表示不分页获取全部
7908
+ * @param perPage 每页数量,最大100
7909
+ * @returns Promise<OauthBranch[]> 分支列表
7910
+ *
7911
+ * @example
7912
+ * ```typescript
7913
+ * // GitHub
7914
+ * const branches = await service.getBranches('github', {
7915
+ * owner: 'CodeBuddy-Official-Account',
7916
+ * repo: 'CodeBuddyIDE'
7917
+ * });
7918
+ *
7919
+ * // Gongfeng
7920
+ * const branches = await service.getBranches('gongfeng', {
7921
+ * project_id: '1611499'
7922
+ * });
7923
+ *
7924
+ * // CNB
7925
+ * const branches = await service.getBranches('cnb', {
7926
+ * repo: 'genie/genie-ide'
7927
+ * });
7928
+ * ```
7929
+ */
7930
+ async getBranches(connector, params, page = 0, perPage = 100) {
7931
+ try {
7932
+ const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
7933
+ console.log(`[OAuthRepositoryService] GET ${url}`);
7934
+ const apiResponse = await httpService.get(url);
7935
+ if (!apiResponse.data) {
7936
+ console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
7937
+ return [];
7938
+ }
7939
+ const branches = apiResponse.data.branches || [];
7940
+ console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
7941
+ return branches;
7942
+ } catch (error) {
7943
+ console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
7944
+ throw error;
7945
+ }
7946
+ }
7947
+ /**
7948
+ * 获取仓库列表
7949
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
7950
+ *
7951
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
7952
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
7953
+ *
7954
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
7955
+ * @param page 页码,从1开始,0表示不分页获取全部
7956
+ * - GitHub 只支持全量数据,必须传 0
7957
+ * - 工蜂和 CNB 依据前端逻辑而定
7958
+ * @param perPage 每页数量,最大100
7959
+ * @returns Promise<ListReposResponse> 仓库列表响应
7960
+ *
7961
+ * @example
7962
+ * ```typescript
7963
+ * // GitHub - 必须传 page=0 获取全量数据
7964
+ * const response = await service.getRepositories('github', 0, 100);
7965
+ * // response.github_repos 是 map: installation_id => repo[]
7966
+ *
7967
+ * // Gongfeng
7968
+ * const response = await service.getRepositories('gongfeng', 0, 100);
7969
+ * // response.gongfeng_repos 是数组
7970
+ *
7971
+ * // CNB
7972
+ * const response = await service.getRepositories('cnb', 0, 100);
7973
+ * // response.cnb_repos 是数组
7974
+ * ```
7975
+ */
7976
+ async getRepositories(connector, page = 0, perPage = 100) {
7977
+ try {
7978
+ const queryParams = new URLSearchParams();
7979
+ queryParams.append("page", String(page));
7980
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
7981
+ const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
7982
+ console.log(`[OAuthRepositoryService] GET ${url}`);
7983
+ const apiResponse = await httpService.get(url);
7984
+ if (!apiResponse.data) {
7985
+ console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
7986
+ return {};
7987
+ }
7988
+ const response = apiResponse.data;
7989
+ this.logRepositoryCounts(response);
7990
+ return response;
7991
+ } catch (error) {
7992
+ console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
7993
+ throw error;
7994
+ }
7995
+ }
7996
+ /**
7997
+ * 构建分支查询参数
7998
+ */
7999
+ buildBranchQueryParams(connector, params, page, perPage) {
8000
+ const queryParams = new URLSearchParams();
8001
+ queryParams.append("page", String(page));
8002
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
8003
+ if (connector === "github") {
8004
+ const githubParams = params;
8005
+ if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
8006
+ queryParams.append("owner", githubParams.owner);
8007
+ queryParams.append("repo", githubParams.repo);
8008
+ } else if (connector === "gongfeng") {
8009
+ const gongfengParams = params;
8010
+ if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
8011
+ queryParams.append("project_id", gongfengParams.project_id);
8012
+ } else if (connector === "cnb") {
8013
+ const cnbParams = params;
8014
+ if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
8015
+ queryParams.append("repo", cnbParams.repo);
8016
+ } else throw new Error(`Unknown connector: ${connector}`);
8017
+ return queryParams;
8018
+ }
8019
+ /**
8020
+ * 记录仓库数量日志
8021
+ */
8022
+ logRepositoryCounts(response) {
8023
+ if (response.github_repos) {
8024
+ const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
8025
+ console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
8026
+ }
8027
+ if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
8028
+ if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
8029
+ }
8030
+ };
8031
+ /**
8032
+ * OAuth Repository Service 单例实例
8033
+ */
8034
+ const oauthRepositoryService = new OAuthRepositoryService();
8035
+
7735
8036
  //#endregion
7736
8037
  //#region ../agent-provider/src/backend/backend-provider.ts
7737
8038
  /**
@@ -7740,27 +8041,36 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
7740
8041
  * 封装与后端 API 的 HTTP 通信
7741
8042
  */
7742
8043
  /**
7743
- * 判断当前是否在 SSO 域名下
7744
- * SSO 域名格式: xxx.sso.copilot.tencent.com xxx.sso.codebuddy.cn
8044
+ * 判断当前账号是否是 SSO 账号
8045
+ * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
7745
8046
  */
7746
8047
  const isSSODomain = () => {
7747
8048
  const { hostname } = window.location;
7748
8049
  return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
7749
8050
  };
8051
+ const safeParseJSON = (jsonString) => {
8052
+ try {
8053
+ return JSON.parse(jsonString);
8054
+ } catch (error) {
8055
+ return {};
8056
+ }
8057
+ };
7750
8058
  /**
7751
- * 获取登录页面 URL
7752
- * - SSO 域名下需要跳转到对应的预发/生产域名
7753
- * - 非 SSO 域名直接使用当前域名
8059
+ * 根据路径获取完整 URL
8060
+ * - SSO 账号需要跳转到对应的预发/生产域名
8061
+ * - 非 SSO 账号直接使用当前域名
8062
+ * @param path 路径,如 '/login'、'/logout'、'/home' 等
8063
+ * @returns 完整的 URL 地址
7754
8064
  */
7755
- const getLoginUrl = () => {
8065
+ const getFullUrl = (path) => {
7756
8066
  const { hostname, protocol } = window.location;
7757
8067
  if (isSSODomain()) {
7758
8068
  const isCodebuddy = hostname.includes("codebuddy.cn");
7759
8069
  const isStaging = hostname.includes("staging");
7760
- if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
7761
- else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
8070
+ if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn${path}` : `${protocol}//www.codebuddy.cn${path}`;
8071
+ else return isStaging ? `${protocol}//staging-copilot.tencent.com${path}` : `${protocol}//copilot.tencent.com${path}`;
7762
8072
  }
7763
- return `${window.location.origin}/login`;
8073
+ return `${window.location.origin}${path}`;
7764
8074
  };
7765
8075
  /** 获取当前域名的账号选择页面 URL */
7766
8076
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
@@ -7798,12 +8108,30 @@ var BackendProvider = class {
7798
8108
  constructor(config) {
7799
8109
  httpService.setBaseURL(config.baseUrl);
7800
8110
  if (config.authToken) httpService.setAuthToken(config.authToken);
7801
- httpService.onUnauthorized(() => {
7802
- console.log("[BackendProvider] User unauthorized (401/403), triggering logout");
7803
- this.logout().catch((error) => {
7804
- console.error("[BackendProvider] Logout failed in 401 handler:", error);
8111
+ httpService.onUnauthorized(() => this.handleUnauthorized());
8112
+ }
8113
+ /**
8114
+ * 处理 401 未授权错误
8115
+ * 先尝试刷新 token,失败后再执行登出流程
8116
+ *
8117
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
8118
+ */
8119
+ async handleUnauthorized() {
8120
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
8121
+ try {
8122
+ if (await this.refreshToken()) {
8123
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
8124
+ return;
8125
+ }
8126
+ throw new Error("Token refresh returned null");
8127
+ } catch (error) {
8128
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
8129
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
8130
+ this.logout().catch((logoutError) => {
8131
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
7805
8132
  });
7806
- });
8133
+ throw error;
8134
+ }
7807
8135
  }
7808
8136
  /**
7809
8137
  * 获取当前账号信息
@@ -7848,7 +8176,7 @@ var BackendProvider = class {
7848
8176
  return account;
7849
8177
  }
7850
8178
  const redirectUrl = encodeURIComponent(window.location.href);
7851
- window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
8179
+ window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
7852
8180
  accountService.setAccount(null);
7853
8181
  return null;
7854
8182
  } catch (error) {
@@ -7871,7 +8199,8 @@ var BackendProvider = class {
7871
8199
  activeStatus: connector.active_status,
7872
8200
  displayName: connector.display_name,
7873
8201
  oauthClientId: connector.oauth_client_id,
7874
- oauthRedirectUrl: connector.oauth_redirect_url
8202
+ oauthRedirectUrl: connector.oauth_redirect_url,
8203
+ oauthAppName: connector.oauth_app_name
7875
8204
  })) };
7876
8205
  }
7877
8206
  throw result;
@@ -7953,7 +8282,8 @@ var BackendProvider = class {
7953
8282
  connectStatus: connector.connect_status,
7954
8283
  displayName: connector.display_name,
7955
8284
  oauthClientId: connector.oauth_client_id,
7956
- oauthRedirectUrl: connector.oauth_redirect_url
8285
+ oauthRedirectUrl: connector.oauth_redirect_url,
8286
+ oauthAppName: connector.oauth_app_name
7957
8287
  })) };
7958
8288
  }
7959
8289
  throw result;
@@ -8044,6 +8374,9 @@ var BackendProvider = class {
8044
8374
  PackageCode: void 0,
8045
8375
  name: ""
8046
8376
  };
8377
+ const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
8378
+ console.log("[PRODUCT_FEATURES]", productFeatures);
8379
+ if (!productFeatures.billing) return defaultPlan;
8047
8380
  try {
8048
8381
  const now = /* @__PURE__ */ new Date();
8049
8382
  const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
@@ -8065,7 +8398,7 @@ var BackendProvider = class {
8065
8398
  if (!time) return 0;
8066
8399
  return new Date(time).getTime();
8067
8400
  };
8068
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
8401
+ const dailyCredits = [CommodityCode.free];
8069
8402
  const planResources = resources.map((r) => {
8070
8403
  const isDaily = dailyCredits.includes(r.PackageCode);
8071
8404
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
@@ -8086,10 +8419,11 @@ var BackendProvider = class {
8086
8419
  CommodityCode.proMon,
8087
8420
  CommodityCode.proMonPlus,
8088
8421
  CommodityCode.proYear,
8422
+ CommodityCode.freeMon,
8089
8423
  CommodityCode.extra
8090
8424
  ].includes(code)) return 1;
8091
8425
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
8092
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
8426
+ if ([CommodityCode.free].includes(code)) return 3;
8093
8427
  return 4;
8094
8428
  };
8095
8429
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -8210,7 +8544,7 @@ var BackendProvider = class {
8210
8544
  */
8211
8545
  async login() {
8212
8546
  const redirectUrl = encodeURIComponent(window.location.href);
8213
- window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
8547
+ window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
8214
8548
  }
8215
8549
  /**
8216
8550
  * 登出账号
@@ -8273,6 +8607,52 @@ var BackendProvider = class {
8273
8607
  return null;
8274
8608
  }
8275
8609
  }
8610
+ /**
8611
+ * 刷新 Token
8612
+ * 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
8613
+ * @returns Promise<Account | null> 刷新后的账号信息
8614
+ */
8615
+ async refreshToken() {
8616
+ console.log("[BackendProvider] Refreshing token...");
8617
+ try {
8618
+ const account = await this.getAccount();
8619
+ console.log("[BackendProvider] Token refreshed, account:", account?.uid);
8620
+ return account;
8621
+ } catch (error) {
8622
+ console.error("[BackendProvider] refreshToken failed:", error);
8623
+ return null;
8624
+ }
8625
+ }
8626
+ /**
8627
+ * 获取仓库分支列表
8628
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
8629
+ *
8630
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
8631
+ * @param params 平台特定的查询参数
8632
+ * @param page 页码,从1开始,0表示不分页获取全部
8633
+ * @param perPage 每页数量,最大100
8634
+ * @returns Promise<OauthBranch[]> 分支列表
8635
+ */
8636
+ async getBranches(connector, params, page = 0, perPage = 100) {
8637
+ return oauthRepositoryService.getBranches(connector, params, page, perPage);
8638
+ }
8639
+ /**
8640
+ * 获取仓库列表
8641
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
8642
+ *
8643
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
8644
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
8645
+ *
8646
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
8647
+ * @param page 页码,从1开始,0表示不分页获取全部
8648
+ * - GitHub 只支持全量数据,必须传 0
8649
+ * - 工蜂和 CNB 依据前端逻辑而定
8650
+ * @param perPage 每页数量,最大100
8651
+ * @returns Promise<ListReposResponse> 仓库列表响应
8652
+ */
8653
+ async getRepositories(connector, page = 0, perPage = 100) {
8654
+ return oauthRepositoryService.getRepositories(connector, page, perPage);
8655
+ }
8276
8656
  };
8277
8657
  /**
8278
8658
  * 创建 BackendProvider 实例
@@ -8312,6 +8692,7 @@ const BACKEND_REQUEST_TYPES = {
8312
8692
  GET_FILE: "backend:get-file",
8313
8693
  RELOAD_WINDOW: "backend:reload-window",
8314
8694
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
8695
+ OPEN_EXTERNAL: "backend:open-external",
8315
8696
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
8316
8697
  GET_SUPPORT_SCENES: "backend:get-support-scenes"
8317
8698
  };
@@ -8607,6 +8988,20 @@ var IPCBackendProvider = class {
8607
8988
  }
8608
8989
  }
8609
8990
  /**
8991
+ * 在外部浏览器中打开链接
8992
+ * IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
8993
+ * @param url 要打开的 URL
8994
+ */
8995
+ async openExternal(url) {
8996
+ this.log("Opening external URL via IPC:", url);
8997
+ try {
8998
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
8999
+ } catch (error) {
9000
+ this.log("Open external request failed:", error);
9001
+ throw error;
9002
+ }
9003
+ }
9004
+ /**
8610
9005
  * 批量切换插件状态
8611
9006
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
8612
9007
  */