@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.cjs CHANGED
@@ -2411,7 +2411,7 @@ var CloudAgentConnection = class {
2411
2411
  }
2412
2412
  async createSession(params) {
2413
2413
  return {
2414
- ...await this.client.loadSession(this.agentId, this.cwd),
2414
+ ...await this.client.createSession(this.cwd),
2415
2415
  sessionId: this.agentId
2416
2416
  };
2417
2417
  }
@@ -15923,7 +15923,7 @@ var axios_default = axios;
15923
15923
  * 特性:
15924
15924
  * - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
15925
15925
  * - 支持拦截器注册(其他模块可注入 header)
15926
- * - 统一 401/403 处理
15926
+ * - 统一 401 处理(支持自动刷新 token 并重试)
15927
15927
  * - 自动携带凭证(withCredentials)
15928
15928
  * - 类型安全
15929
15929
  *
@@ -15963,6 +15963,8 @@ var HttpService = class HttpService {
15963
15963
  */
15964
15964
  constructor(config = {}) {
15965
15965
  this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
15966
+ this.isRefreshing = false;
15967
+ this.refreshSubscribers = [];
15966
15968
  this.config = config;
15967
15969
  this.axiosInstance = axios_default.create({
15968
15970
  baseURL: config.baseURL?.replace(/\/$/, "") || "",
@@ -16003,18 +16005,54 @@ var HttpService = class HttpService {
16003
16005
  }, (error) => Promise.reject(error));
16004
16006
  }
16005
16007
  /**
16006
- * 注册默认响应拦截器(处理 401/403)
16008
+ * 注册默认响应拦截器(处理 401,支持自动重试)
16007
16009
  */
16008
16010
  registerDefaultResponseInterceptor() {
16009
- this.axiosInstance.interceptors.response.use((response) => response, (error) => {
16010
- if (error.response?.status === 401 || error.response?.status === 403) {
16011
- console.warn("[HttpService] Unauthorized (401/403), triggering callbacks");
16012
- this.triggerUnauthorizedCallbacks();
16011
+ this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
16012
+ const originalRequest = error.config;
16013
+ if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
16014
+ if (originalRequest.url?.includes("/console/accounts")) {
16015
+ console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
16016
+ return Promise.reject(error);
16017
+ }
16018
+ console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
16019
+ originalRequest._retry = true;
16020
+ if (this.isRefreshing) return new Promise((resolve, reject) => {
16021
+ this.refreshSubscribers.push((success) => {
16022
+ if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
16023
+ else reject(error);
16024
+ });
16025
+ });
16026
+ this.isRefreshing = true;
16027
+ try {
16028
+ await this.triggerUnauthorizedCallbacks();
16029
+ this.onRefreshSuccess();
16030
+ return this.axiosInstance.request(originalRequest);
16031
+ } catch (refreshError) {
16032
+ this.onRefreshFailure();
16033
+ return Promise.reject(error);
16034
+ } finally {
16035
+ this.isRefreshing = false;
16036
+ }
16013
16037
  }
16014
16038
  return Promise.reject(error);
16015
16039
  });
16016
16040
  }
16017
16041
  /**
16042
+ * token 刷新成功,通知所有等待的请求
16043
+ */
16044
+ onRefreshSuccess() {
16045
+ this.refreshSubscribers.forEach((callback) => callback(true));
16046
+ this.refreshSubscribers = [];
16047
+ }
16048
+ /**
16049
+ * token 刷新失败,通知所有等待的请求
16050
+ */
16051
+ onRefreshFailure() {
16052
+ this.refreshSubscribers.forEach((callback) => callback(false));
16053
+ this.refreshSubscribers = [];
16054
+ }
16055
+ /**
16018
16056
  * 注册请求拦截器
16019
16057
  * @param onFulfilled 请求成功拦截器
16020
16058
  * @param onRejected 请求失败拦截器
@@ -16091,16 +16129,18 @@ var HttpService = class HttpService {
16091
16129
  this.unauthorizedCallbacks.delete(callback);
16092
16130
  }
16093
16131
  /**
16094
- * 触发所有 401 回调
16132
+ * 触发所有 401 回调并等待完成
16133
+ * @returns Promise,等待所有回调完成
16134
+ * @throws 如果任何回调失败,则抛出错误
16095
16135
  */
16096
- triggerUnauthorizedCallbacks() {
16097
- this.unauthorizedCallbacks.forEach((callback) => {
16098
- try {
16099
- callback();
16100
- } catch (error) {
16101
- console.error("[HttpService] Error in unauthorized callback:", error);
16102
- }
16103
- });
16136
+ async triggerUnauthorizedCallbacks() {
16137
+ const callbacks = Array.from(this.unauthorizedCallbacks);
16138
+ const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
16139
+ if (failedResults.length > 0) {
16140
+ const errors = failedResults.map((r) => r.reason);
16141
+ console.error("[HttpService] Some unauthorized callbacks failed:", errors);
16142
+ throw errors[0];
16143
+ }
16104
16144
  }
16105
16145
  /**
16106
16146
  * 更新 authToken
@@ -16212,6 +16252,7 @@ var AccountService = class {
16212
16252
  this.initPromise = null;
16213
16253
  this.initResolve = null;
16214
16254
  this.requestInterceptorId = null;
16255
+ this.crossTabBroadcaster = null;
16215
16256
  this.initPromise = new Promise((resolve) => {
16216
16257
  this.initResolve = resolve;
16217
16258
  });
@@ -16260,15 +16301,34 @@ var AccountService = class {
16260
16301
  this.initialized = true;
16261
16302
  this.initResolve?.(account);
16262
16303
  }
16263
- if (!wasInitialized || prev?.uid !== account?.uid) this.notifyListeners();
16304
+ if (!wasInitialized || prev?.uid !== account?.uid) {
16305
+ this.notifyListeners();
16306
+ if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
16307
+ }
16264
16308
  }
16265
16309
  /**
16266
16310
  * 清除账号(登出)
16311
+ * 先广播登出消息,再清除本地账号
16267
16312
  */
16268
16313
  clearAccount() {
16314
+ if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
16269
16315
  this.setAccount(null);
16270
16316
  }
16271
16317
  /**
16318
+ * 静默清除账号(不广播)
16319
+ * 用于收到其他标签页 logout 消息时,避免循环广播
16320
+ */
16321
+ clearAccountSilently() {
16322
+ this.setAccount(null);
16323
+ }
16324
+ /**
16325
+ * 设置跨标签页认证同步广播器
16326
+ * 应在应用初始化时由上层(如 agent-ui)调用
16327
+ */
16328
+ setCrossTabBroadcaster(broadcaster) {
16329
+ this.crossTabBroadcaster = broadcaster;
16330
+ }
16331
+ /**
16272
16332
  * 订阅账号变化
16273
16333
  * @param callback 变化时的回调函数
16274
16334
  * @returns 取消订阅函数
@@ -16333,6 +16393,11 @@ var AccountService = class {
16333
16393
  * 导出单例实例
16334
16394
  */
16335
16395
  const accountService = new AccountService();
16396
+ /**
16397
+ * 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
16398
+ * 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
16399
+ */
16400
+ if (typeof window !== "undefined") window.__genieAccountService = accountService;
16336
16401
 
16337
16402
  //#endregion
16338
16403
  //#region ../agent-provider/src/common/utils/concurrency.ts
@@ -16922,15 +16987,9 @@ var CloudAgentProvider = class CloudAgentProvider {
16922
16987
  const url = this.buildGetUrl("/console/as/conversations/", params);
16923
16988
  const apiResponse = await httpService.get(url);
16924
16989
  if (!apiResponse.data) throw new Error("No data in API response");
16925
- const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
16926
- const pagination = apiResponse.data.pagination;
16927
- console.log("[CloudAgentProvider] API response:", {
16928
- agentsCount: agents.length,
16929
- pagination
16930
- });
16931
16990
  return {
16932
- agents,
16933
- pagination
16991
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
16992
+ pagination: apiResponse.data.pagination
16934
16993
  };
16935
16994
  } catch (error) {
16936
16995
  this.logger?.error("Failed to list agents:", error);
@@ -16940,13 +16999,21 @@ var CloudAgentProvider = class CloudAgentProvider {
16940
16999
  /**
16941
17000
  * Create a new conversation
16942
17001
  * POST {endpoint}/console/as/conversations
17002
+ * @param params - Session params containing cwd and optional configuration
16943
17003
  */
16944
- async create() {
17004
+ async create(params) {
16945
17005
  try {
16946
- const apiResponse = await httpService.post("/console/as/conversations/", {
16947
- prompt: "",
16948
- model: "deepseek-r1"
16949
- });
17006
+ const { options = {} } = params;
17007
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
17008
+ const tagsObj = options.tags || codebuddyMeta?.tags;
17009
+ const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
17010
+ const createPayload = {
17011
+ prompt: (options.prompt || "").slice(0, 100),
17012
+ model: options.model || "deepseek-r1",
17013
+ ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
17014
+ };
17015
+ console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
17016
+ const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
16950
17017
  if (!apiResponse.data) throw new Error("No data in API response");
16951
17018
  this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
16952
17019
  return apiResponse.data.id;
@@ -17312,17 +17379,20 @@ var CloudAgentProvider = class CloudAgentProvider {
17312
17379
  * API 端点: GET /console/as/support/scenes
17313
17380
  * 用于 Welcome 页面的 QuickActions 快捷操作
17314
17381
  *
17382
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
17315
17383
  * @returns Promise<SupportScene[]> 支持的场景列表
17316
17384
  */
17317
- async getSupportScenes() {
17385
+ async getSupportScenes(locale) {
17318
17386
  try {
17319
- const apiResponse = await httpService.get("/console/as/support/scenes");
17387
+ let url = "/console/as/support/scenes";
17388
+ if (locale) url += `?locale=${encodeURIComponent(locale)}`;
17389
+ const apiResponse = await httpService.get(url);
17320
17390
  if (!apiResponse.data) {
17321
17391
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
17322
17392
  return [];
17323
17393
  }
17324
17394
  const scenes = apiResponse.data.scenes || [];
17325
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
17395
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
17326
17396
  return scenes;
17327
17397
  } catch (error) {
17328
17398
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -17338,7 +17408,8 @@ var CloudAgentProvider = class CloudAgentProvider {
17338
17408
  type: "cloud",
17339
17409
  status,
17340
17410
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
17341
- capabilities: this.options.clientCapabilities
17411
+ capabilities: this.options.clientCapabilities,
17412
+ isUserDefinedTitle: data.isUserDefinedTitle
17342
17413
  };
17343
17414
  }
17344
17415
  /**
@@ -17643,8 +17714,8 @@ var ActiveSessionImpl = class {
17643
17714
  * await session.setMode('architect');
17644
17715
  * ```
17645
17716
  */
17646
- async setMode(modeId) {
17647
- if (this._availableModes) {
17717
+ async setMode(modeId, skipAvailableChecker) {
17718
+ if (this._availableModes && !skipAvailableChecker) {
17648
17719
  if (!this._availableModes.some((m) => m.id === modeId)) {
17649
17720
  const availableIds = this._availableModes.map((m) => m.id).join(", ");
17650
17721
  throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
@@ -17835,14 +17906,6 @@ var ActiveSessionImpl = class {
17835
17906
  //#endregion
17836
17907
  //#region ../agent-provider/src/common/client/session-manager.ts
17837
17908
  /**
17838
- * SessionManager - Manages session lifecycle and connections
17839
- *
17840
- * Provides the core implementation for session-centric API operations:
17841
- * - list() - Lists sessions (mapped from agents)
17842
- * - createSession() - Creates new session (auto-creates agent)
17843
- * - loadSession() - Loads existing session (finds agent by sessionId)
17844
- */
17845
- /**
17846
17909
  * SessionManager - Session lifecycle management
17847
17910
  *
17848
17911
  * This class manages the relationship between sessions and agents.
@@ -17892,7 +17955,8 @@ var SessionManager = class {
17892
17955
  createdAt: agent.createdAt,
17893
17956
  lastActivityAt: agent.updatedAt,
17894
17957
  cwd: agent.type === "local" ? agent.cwd : void 0,
17895
- isPlayground: agent.isPlayground
17958
+ isPlayground: agent.isPlayground,
17959
+ isUserDefinedTitle: agent.isUserDefinedTitle
17896
17960
  }));
17897
17961
  console.log("[SessionManager] Returning sessions:", {
17898
17962
  count: sessions.length,
@@ -17923,9 +17987,9 @@ var SessionManager = class {
17923
17987
  const connection = await this.provider.connect(agentId);
17924
17988
  this.logger?.debug(`Connected to agent: ${agentId}`);
17925
17989
  const response = await connection.createSession({
17926
- _meta: params._meta,
17990
+ _meta: params.options?._meta,
17927
17991
  cwd: params.cwd,
17928
- mcpServers: params.mcpServers
17992
+ mcpServers: params.options?.mcpServers
17929
17993
  });
17930
17994
  if (this.provider.registerSession) {
17931
17995
  this.provider.registerSession(response.sessionId, agentId);
@@ -17938,14 +18002,8 @@ var SessionManager = class {
17938
18002
  connectionInfo
17939
18003
  });
17940
18004
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
17941
- if (response.models?.availableModels) {
17942
- const localModels = response.models.availableModels.map((m) => ({
17943
- id: m.modelId,
17944
- name: m.name,
17945
- description: m.description ?? void 0
17946
- }));
17947
- session.setModels(localModels, response.models?.currentModelId);
17948
- }
18005
+ const availableModels = this.extractAvailableModels(response);
18006
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
17949
18007
  this.logger?.info(`Session created: ${response.sessionId}`);
17950
18008
  return session;
17951
18009
  }
@@ -17981,17 +18039,31 @@ var SessionManager = class {
17981
18039
  mcpServers: params.mcpServers
17982
18040
  });
17983
18041
  session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
17984
- if (response.models?.availableModels) {
17985
- const localModels = response.models.availableModels.map((m) => ({
17986
- id: m.modelId,
17987
- name: m.name,
17988
- description: m.description ?? void 0
17989
- }));
17990
- session.setModels(localModels, response.models?.currentModelId);
17991
- }
18042
+ const availableModels = this.extractAvailableModels(response);
18043
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
17992
18044
  this.logger?.info(`Session loaded: ${params.sessionId}`);
17993
18045
  return session;
17994
18046
  }
18047
+ /**
18048
+ * 从 ACP response 中提取可用模型列表
18049
+ *
18050
+ * 优先级:
18051
+ * 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
18052
+ * 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
18053
+ * 3. undefined - 都没有时返回 undefined
18054
+ *
18055
+ * @param response - ACP 响应对象
18056
+ * @returns ModelInfo[] | undefined
18057
+ */
18058
+ extractAvailableModels(response) {
18059
+ const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
18060
+ if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
18061
+ const availableModels = response.models?.availableModels;
18062
+ if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
18063
+ ...model,
18064
+ ...model._meta?.["codebuddy.ai"] || {}
18065
+ }));
18066
+ }
17995
18067
  };
17996
18068
 
17997
18069
  //#endregion
@@ -18264,6 +18336,28 @@ var AgentClient = class {
18264
18336
  };
18265
18337
  }
18266
18338
  },
18339
+ getSubagentList: async (params) => {
18340
+ try {
18341
+ if (this.provider && this.provider.getSubagentList) {
18342
+ const result = await this.provider.getSubagentList(params);
18343
+ this.logger?.info("Subagent list retrieved", {
18344
+ resultCount: result.results.length,
18345
+ hasError: !!result.error
18346
+ });
18347
+ return result;
18348
+ }
18349
+ return {
18350
+ results: [],
18351
+ error: "Provider does not support getSubagentList"
18352
+ };
18353
+ } catch (error) {
18354
+ this.logger?.error("Failed to get subagent list", error);
18355
+ return {
18356
+ results: [],
18357
+ error: error instanceof Error ? error.message : "Unknown error"
18358
+ };
18359
+ }
18360
+ },
18267
18361
  batchTogglePlugins: async (request) => {
18268
18362
  try {
18269
18363
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -18308,10 +18402,10 @@ var AgentClient = class {
18308
18402
  return [];
18309
18403
  }
18310
18404
  },
18311
- installPlugins: async (pluginNames, marketplaceName, installScope) => {
18405
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource) => {
18312
18406
  try {
18313
18407
  if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
18314
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
18408
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource);
18315
18409
  this.logger?.info("Install plugins", {
18316
18410
  pluginNames,
18317
18411
  marketplaceName,
@@ -18332,13 +18426,9 @@ var AgentClient = class {
18332
18426
  };
18333
18427
  }
18334
18428
  },
18335
- getSupportScenes: async () => {
18429
+ getSupportScenes: async (locale) => {
18336
18430
  try {
18337
- if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") {
18338
- const result = await this.provider.getSupportScenes();
18339
- this.logger?.info("Got support scenes", { count: result?.length ?? 0 });
18340
- return result;
18341
- }
18431
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
18342
18432
  this.logger?.warn("Provider does not support getSupportScenes");
18343
18433
  return [];
18344
18434
  } catch (error) {
@@ -18346,6 +18436,69 @@ var AgentClient = class {
18346
18436
  return [];
18347
18437
  }
18348
18438
  },
18439
+ getAvailableCommands: async (sessionId) => {
18440
+ try {
18441
+ if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
18442
+ const result = await this.provider.getAvailableCommands(sessionId);
18443
+ this.logger?.info("Got available commands from provider", {
18444
+ sessionId: sessionId ?? "(default)",
18445
+ count: result?.length ?? 0
18446
+ });
18447
+ return result;
18448
+ }
18449
+ this.logger?.warn("Provider does not support getAvailableCommands", { sessionId });
18450
+ return [];
18451
+ } catch (error) {
18452
+ this.logger?.error("Failed to get available commands", error);
18453
+ return [];
18454
+ }
18455
+ },
18456
+ respondToSampling: async (sessionId, response) => {
18457
+ try {
18458
+ if (this.provider?.respondToSampling) {
18459
+ await this.provider.respondToSampling(sessionId, response);
18460
+ this.logger?.info("Responded to sampling request", {
18461
+ sessionId,
18462
+ requestId: response.id,
18463
+ approved: response.approved
18464
+ });
18465
+ } else this.logger?.warn("Provider does not support respondToSampling");
18466
+ } catch (error) {
18467
+ this.logger?.error("Failed to respond to sampling request", error);
18468
+ throw error;
18469
+ }
18470
+ },
18471
+ respondToRoots: async (sessionId, response) => {
18472
+ try {
18473
+ if (this.provider?.respondToRoots) {
18474
+ await this.provider.respondToRoots(sessionId, response);
18475
+ this.logger?.info("Responded to roots request", {
18476
+ sessionId,
18477
+ requestId: response.id,
18478
+ approved: response.approved
18479
+ });
18480
+ } else this.logger?.warn("Provider does not support respondToRoots");
18481
+ } catch (error) {
18482
+ this.logger?.error("Failed to respond to roots request", error);
18483
+ throw error;
18484
+ }
18485
+ },
18486
+ subscribeSamplingRequests: (serverName, callback) => {
18487
+ if (this.provider?.subscribeSamplingRequests) {
18488
+ this.logger?.info("Subscribing to sampling requests", { serverName });
18489
+ return this.provider.subscribeSamplingRequests(serverName, callback);
18490
+ }
18491
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
18492
+ return () => {};
18493
+ },
18494
+ subscribeRootsRequests: (serverName, callback) => {
18495
+ if (this.provider?.subscribeRootsRequests) {
18496
+ this.logger?.info("Subscribing to roots requests", { serverName });
18497
+ return this.provider.subscribeRootsRequests(serverName, callback);
18498
+ }
18499
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
18500
+ return () => {};
18501
+ },
18349
18502
  models: this.createModelsResource()
18350
18503
  };
18351
18504
  }
@@ -18412,6 +18565,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
18412
18565
  return AccountStatus;
18413
18566
  }({});
18414
18567
 
18568
+ //#endregion
18569
+ //#region ../agent-provider/src/backend/service/oauth-repository-service.ts
18570
+ /**
18571
+ * OAuth Repository Service
18572
+ *
18573
+ * 封装 OAuth 连接器相关的仓库和分支操作
18574
+ */
18575
+ /**
18576
+ * OAuth Repository Service
18577
+ *
18578
+ * 提供仓库和分支的查询操作
18579
+ */
18580
+ var OAuthRepositoryService = class {
18581
+ /**
18582
+ * 获取仓库分支列表
18583
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
18584
+ *
18585
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
18586
+ * @param params 平台特定的查询参数
18587
+ * @param page 页码,从1开始,0表示不分页获取全部
18588
+ * @param perPage 每页数量,最大100
18589
+ * @returns Promise<OauthBranch[]> 分支列表
18590
+ *
18591
+ * @example
18592
+ * ```typescript
18593
+ * // GitHub
18594
+ * const branches = await service.getBranches('github', {
18595
+ * owner: 'CodeBuddy-Official-Account',
18596
+ * repo: 'CodeBuddyIDE'
18597
+ * });
18598
+ *
18599
+ * // Gongfeng
18600
+ * const branches = await service.getBranches('gongfeng', {
18601
+ * project_id: '1611499'
18602
+ * });
18603
+ *
18604
+ * // CNB
18605
+ * const branches = await service.getBranches('cnb', {
18606
+ * repo: 'genie/genie-ide'
18607
+ * });
18608
+ * ```
18609
+ */
18610
+ async getBranches(connector, params, page = 0, perPage = 100) {
18611
+ try {
18612
+ const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
18613
+ console.log(`[OAuthRepositoryService] GET ${url}`);
18614
+ const apiResponse = await httpService.get(url);
18615
+ if (!apiResponse.data) {
18616
+ console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
18617
+ return [];
18618
+ }
18619
+ const branches = apiResponse.data.branches || [];
18620
+ console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
18621
+ return branches;
18622
+ } catch (error) {
18623
+ console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
18624
+ throw error;
18625
+ }
18626
+ }
18627
+ /**
18628
+ * 获取仓库列表
18629
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
18630
+ *
18631
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
18632
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
18633
+ *
18634
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
18635
+ * @param page 页码,从1开始,0表示不分页获取全部
18636
+ * - GitHub 只支持全量数据,必须传 0
18637
+ * - 工蜂和 CNB 依据前端逻辑而定
18638
+ * @param perPage 每页数量,最大100
18639
+ * @returns Promise<ListReposResponse> 仓库列表响应
18640
+ *
18641
+ * @example
18642
+ * ```typescript
18643
+ * // GitHub - 必须传 page=0 获取全量数据
18644
+ * const response = await service.getRepositories('github', 0, 100);
18645
+ * // response.github_repos 是 map: installation_id => repo[]
18646
+ *
18647
+ * // Gongfeng
18648
+ * const response = await service.getRepositories('gongfeng', 0, 100);
18649
+ * // response.gongfeng_repos 是数组
18650
+ *
18651
+ * // CNB
18652
+ * const response = await service.getRepositories('cnb', 0, 100);
18653
+ * // response.cnb_repos 是数组
18654
+ * ```
18655
+ */
18656
+ async getRepositories(connector, page = 0, perPage = 100) {
18657
+ try {
18658
+ const queryParams = new URLSearchParams();
18659
+ queryParams.append("page", String(page));
18660
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
18661
+ const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
18662
+ console.log(`[OAuthRepositoryService] GET ${url}`);
18663
+ const apiResponse = await httpService.get(url);
18664
+ if (!apiResponse.data) {
18665
+ console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
18666
+ return {};
18667
+ }
18668
+ const response = apiResponse.data;
18669
+ this.logRepositoryCounts(response);
18670
+ return response;
18671
+ } catch (error) {
18672
+ console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
18673
+ throw error;
18674
+ }
18675
+ }
18676
+ /**
18677
+ * 构建分支查询参数
18678
+ */
18679
+ buildBranchQueryParams(connector, params, page, perPage) {
18680
+ const queryParams = new URLSearchParams();
18681
+ queryParams.append("page", String(page));
18682
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
18683
+ if (connector === "github") {
18684
+ const githubParams = params;
18685
+ if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
18686
+ queryParams.append("owner", githubParams.owner);
18687
+ queryParams.append("repo", githubParams.repo);
18688
+ } else if (connector === "gongfeng") {
18689
+ const gongfengParams = params;
18690
+ if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
18691
+ queryParams.append("project_id", gongfengParams.project_id);
18692
+ } else if (connector === "cnb") {
18693
+ const cnbParams = params;
18694
+ if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
18695
+ queryParams.append("repo", cnbParams.repo);
18696
+ } else throw new Error(`Unknown connector: ${connector}`);
18697
+ return queryParams;
18698
+ }
18699
+ /**
18700
+ * 记录仓库数量日志
18701
+ */
18702
+ logRepositoryCounts(response) {
18703
+ if (response.github_repos) {
18704
+ const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
18705
+ console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
18706
+ }
18707
+ if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
18708
+ if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
18709
+ }
18710
+ };
18711
+ /**
18712
+ * OAuth Repository Service 单例实例
18713
+ */
18714
+ const oauthRepositoryService = new OAuthRepositoryService();
18715
+
18415
18716
  //#endregion
18416
18717
  //#region ../agent-provider/src/backend/backend-provider.ts
18417
18718
  /**
@@ -18420,27 +18721,36 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
18420
18721
  * 封装与后端 API 的 HTTP 通信
18421
18722
  */
18422
18723
  /**
18423
- * 判断当前是否在 SSO 域名下
18424
- * SSO 域名格式: xxx.sso.copilot.tencent.com xxx.sso.codebuddy.cn
18724
+ * 判断当前账号是否是 SSO 账号
18725
+ * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
18425
18726
  */
18426
18727
  const isSSODomain = () => {
18427
18728
  const { hostname } = window.location;
18428
18729
  return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
18429
18730
  };
18731
+ const safeParseJSON = (jsonString) => {
18732
+ try {
18733
+ return JSON.parse(jsonString);
18734
+ } catch (error) {
18735
+ return {};
18736
+ }
18737
+ };
18430
18738
  /**
18431
- * 获取登录页面 URL
18432
- * - SSO 域名下需要跳转到对应的预发/生产域名
18433
- * - 非 SSO 域名直接使用当前域名
18739
+ * 根据路径获取完整 URL
18740
+ * - SSO 账号需要跳转到对应的预发/生产域名
18741
+ * - 非 SSO 账号直接使用当前域名
18742
+ * @param path 路径,如 '/login'、'/logout'、'/home' 等
18743
+ * @returns 完整的 URL 地址
18434
18744
  */
18435
- const getLoginUrl = () => {
18745
+ const getFullUrl = (path) => {
18436
18746
  const { hostname, protocol } = window.location;
18437
18747
  if (isSSODomain()) {
18438
18748
  const isCodebuddy = hostname.includes("codebuddy.cn");
18439
18749
  const isStaging = hostname.includes("staging");
18440
- if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
18441
- else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
18750
+ if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn${path}` : `${protocol}//www.codebuddy.cn${path}`;
18751
+ else return isStaging ? `${protocol}//staging-copilot.tencent.com${path}` : `${protocol}//copilot.tencent.com${path}`;
18442
18752
  }
18443
- return `${window.location.origin}/login`;
18753
+ return `${window.location.origin}${path}`;
18444
18754
  };
18445
18755
  /** 获取当前域名的账号选择页面 URL */
18446
18756
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
@@ -18478,12 +18788,30 @@ var BackendProvider = class {
18478
18788
  constructor(config) {
18479
18789
  httpService.setBaseURL(config.baseUrl);
18480
18790
  if (config.authToken) httpService.setAuthToken(config.authToken);
18481
- httpService.onUnauthorized(() => {
18482
- console.log("[BackendProvider] User unauthorized (401/403), triggering logout");
18483
- this.logout().catch((error) => {
18484
- console.error("[BackendProvider] Logout failed in 401 handler:", error);
18791
+ httpService.onUnauthorized(() => this.handleUnauthorized());
18792
+ }
18793
+ /**
18794
+ * 处理 401 未授权错误
18795
+ * 先尝试刷新 token,失败后再执行登出流程
18796
+ *
18797
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
18798
+ */
18799
+ async handleUnauthorized() {
18800
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
18801
+ try {
18802
+ if (await this.refreshToken()) {
18803
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
18804
+ return;
18805
+ }
18806
+ throw new Error("Token refresh returned null");
18807
+ } catch (error) {
18808
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
18809
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
18810
+ this.logout().catch((logoutError) => {
18811
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
18485
18812
  });
18486
- });
18813
+ throw error;
18814
+ }
18487
18815
  }
18488
18816
  /**
18489
18817
  * 获取当前账号信息
@@ -18528,7 +18856,7 @@ var BackendProvider = class {
18528
18856
  return account;
18529
18857
  }
18530
18858
  const redirectUrl = encodeURIComponent(window.location.href);
18531
- window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
18859
+ window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
18532
18860
  accountService.setAccount(null);
18533
18861
  return null;
18534
18862
  } catch (error) {
@@ -18551,7 +18879,8 @@ var BackendProvider = class {
18551
18879
  activeStatus: connector.active_status,
18552
18880
  displayName: connector.display_name,
18553
18881
  oauthClientId: connector.oauth_client_id,
18554
- oauthRedirectUrl: connector.oauth_redirect_url
18882
+ oauthRedirectUrl: connector.oauth_redirect_url,
18883
+ oauthAppName: connector.oauth_app_name
18555
18884
  })) };
18556
18885
  }
18557
18886
  throw result;
@@ -18633,7 +18962,8 @@ var BackendProvider = class {
18633
18962
  connectStatus: connector.connect_status,
18634
18963
  displayName: connector.display_name,
18635
18964
  oauthClientId: connector.oauth_client_id,
18636
- oauthRedirectUrl: connector.oauth_redirect_url
18965
+ oauthRedirectUrl: connector.oauth_redirect_url,
18966
+ oauthAppName: connector.oauth_app_name
18637
18967
  })) };
18638
18968
  }
18639
18969
  throw result;
@@ -18724,6 +19054,9 @@ var BackendProvider = class {
18724
19054
  PackageCode: void 0,
18725
19055
  name: ""
18726
19056
  };
19057
+ const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
19058
+ console.log("[PRODUCT_FEATURES]", productFeatures);
19059
+ if (!productFeatures.billing) return defaultPlan;
18727
19060
  try {
18728
19061
  const now = /* @__PURE__ */ new Date();
18729
19062
  const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
@@ -18745,7 +19078,7 @@ var BackendProvider = class {
18745
19078
  if (!time) return 0;
18746
19079
  return new Date(time).getTime();
18747
19080
  };
18748
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
19081
+ const dailyCredits = [CommodityCode.free];
18749
19082
  const planResources = resources.map((r) => {
18750
19083
  const isDaily = dailyCredits.includes(r.PackageCode);
18751
19084
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
@@ -18766,10 +19099,11 @@ var BackendProvider = class {
18766
19099
  CommodityCode.proMon,
18767
19100
  CommodityCode.proMonPlus,
18768
19101
  CommodityCode.proYear,
19102
+ CommodityCode.freeMon,
18769
19103
  CommodityCode.extra
18770
19104
  ].includes(code)) return 1;
18771
19105
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
18772
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
19106
+ if ([CommodityCode.free].includes(code)) return 3;
18773
19107
  return 4;
18774
19108
  };
18775
19109
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -18890,7 +19224,7 @@ var BackendProvider = class {
18890
19224
  */
18891
19225
  async login() {
18892
19226
  const redirectUrl = encodeURIComponent(window.location.href);
18893
- window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
19227
+ window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
18894
19228
  }
18895
19229
  /**
18896
19230
  * 登出账号
@@ -18953,6 +19287,52 @@ var BackendProvider = class {
18953
19287
  return null;
18954
19288
  }
18955
19289
  }
19290
+ /**
19291
+ * 刷新 Token
19292
+ * 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
19293
+ * @returns Promise<Account | null> 刷新后的账号信息
19294
+ */
19295
+ async refreshToken() {
19296
+ console.log("[BackendProvider] Refreshing token...");
19297
+ try {
19298
+ const account = await this.getAccount();
19299
+ console.log("[BackendProvider] Token refreshed, account:", account?.uid);
19300
+ return account;
19301
+ } catch (error) {
19302
+ console.error("[BackendProvider] refreshToken failed:", error);
19303
+ return null;
19304
+ }
19305
+ }
19306
+ /**
19307
+ * 获取仓库分支列表
19308
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
19309
+ *
19310
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
19311
+ * @param params 平台特定的查询参数
19312
+ * @param page 页码,从1开始,0表示不分页获取全部
19313
+ * @param perPage 每页数量,最大100
19314
+ * @returns Promise<OauthBranch[]> 分支列表
19315
+ */
19316
+ async getBranches(connector, params, page = 0, perPage = 100) {
19317
+ return oauthRepositoryService.getBranches(connector, params, page, perPage);
19318
+ }
19319
+ /**
19320
+ * 获取仓库列表
19321
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
19322
+ *
19323
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
19324
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
19325
+ *
19326
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
19327
+ * @param page 页码,从1开始,0表示不分页获取全部
19328
+ * - GitHub 只支持全量数据,必须传 0
19329
+ * - 工蜂和 CNB 依据前端逻辑而定
19330
+ * @param perPage 每页数量,最大100
19331
+ * @returns Promise<ListReposResponse> 仓库列表响应
19332
+ */
19333
+ async getRepositories(connector, page = 0, perPage = 100) {
19334
+ return oauthRepositoryService.getRepositories(connector, page, perPage);
19335
+ }
18956
19336
  };
18957
19337
  /**
18958
19338
  * 创建 BackendProvider 实例
@@ -18992,6 +19372,7 @@ const BACKEND_REQUEST_TYPES = {
18992
19372
  GET_FILE: "backend:get-file",
18993
19373
  RELOAD_WINDOW: "backend:reload-window",
18994
19374
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
19375
+ OPEN_EXTERNAL: "backend:open-external",
18995
19376
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
18996
19377
  GET_SUPPORT_SCENES: "backend:get-support-scenes"
18997
19378
  };
@@ -19287,6 +19668,20 @@ var IPCBackendProvider = class {
19287
19668
  }
19288
19669
  }
19289
19670
  /**
19671
+ * 在外部浏览器中打开链接
19672
+ * IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
19673
+ * @param url 要打开的 URL
19674
+ */
19675
+ async openExternal(url) {
19676
+ this.log("Opening external URL via IPC:", url);
19677
+ try {
19678
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
19679
+ } catch (error) {
19680
+ this.log("Open external request failed:", error);
19681
+ throw error;
19682
+ }
19683
+ }
19684
+ /**
19290
19685
  * 批量切换插件状态
19291
19686
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
19292
19687
  */