@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.00cf97b.20260317

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
@@ -93,7 +93,6 @@ const ToolInputSchemas = {
93
93
  queryString: zod.z.string().describe("用户的实际问题或搜索查询"),
94
94
  knowledgeBaseNames: zod.z.string().describe("知识库名称,多个用逗号分隔")
95
95
  }),
96
- read_rules: zod.z.object({ ruleNames: zod.z.string().describe("要读取的规则关键词,用逗号分隔,格式:{ruleName}_{ruleId}") }),
97
96
  mcp_get_tool_description: zod.z.object({ toolRequests: zod.z.string().describe("JSON 字符串,二维数组格式:[[\"server1\", \"tool1\"], [\"server2\", \"tool2\"]]") }),
98
97
  mcp_call_tool: zod.z.object({
99
98
  serverName: zod.z.string().describe("MCP 服务器名称"),
@@ -107,13 +106,6 @@ const ToolInputSchemas = {
107
106
  arguments: zod.z.record(zod.z.unknown()).optional().describe("资源模板的参数"),
108
107
  downloadPath: zod.z.string().optional().describe("可选的绝对路径,用于保存资源到磁盘")
109
108
  }),
110
- create_rule: zod.z.object({
111
- ruleScope: zod.z.string().describe("规则范围,project rule 或 user rule"),
112
- ruleName: zod.z.string().describe("规则文件名,不带扩展名"),
113
- ruleType: zod.z.string().describe("规则类型,always、manual 或 requested"),
114
- ruleContent: zod.z.string().describe("规则内容,使用 Markdown 格式"),
115
- ruleDescription: zod.z.string().optional().describe("规则描述,使用 Markdown 格式")
116
- }),
117
109
  update_memory: zod.z.object({
118
110
  action: zod.z.enum([
119
111
  "create",
@@ -172,7 +164,6 @@ const ToolInputSchemas = {
172
164
  cloud_studio_fetch_log: zod.z.object({}).passthrough(),
173
165
  cloud_studio_execute_command: zod.z.object({}).passthrough(),
174
166
  cloud_studio_deploy_sandbox: zod.z.object({}).passthrough(),
175
- component_get_prompt: zod.z.object({}).passthrough(),
176
167
  web_fetch: zod.z.object({
177
168
  url: zod.z.string().describe("要获取内容的 URL"),
178
169
  fetchInfo: zod.z.string().describe("用户想要获取的信息描述")
@@ -180,7 +171,9 @@ const ToolInputSchemas = {
180
171
  use_skill: zod.z.object({ command: zod.z.string().describe("技能名称(不含参数),如 \"pdf\" 或 \"xlsx\"") }),
181
172
  web_search: zod.z.object({
182
173
  explanation: zod.z.string().describe("为什么使用此工具的一句话解释"),
183
- searchTerm: zod.z.string().describe("要在网络上搜索的搜索词")
174
+ query: zod.z.string().describe("搜索关键词"),
175
+ max_results: zod.z.number().optional().describe("最大返回数量"),
176
+ language: zod.z.string().optional().describe("语言代码,例如 zh-CN")
184
177
  }),
185
178
  task: zod.z.object({
186
179
  subagent_name: zod.z.string().describe("要调用的子代理名称"),
@@ -258,11 +251,6 @@ const ToolOutputSchemas = {
258
251
  selectedKnowledgeBases: zod.z.string(),
259
252
  queryInput: zod.z.string()
260
253
  }),
261
- read_rules: zod.z.object({
262
- type: zod.z.literal("rule_match_result"),
263
- ruleDescription: zod.z.string(),
264
- filePaths: zod.z.array(zod.z.string())
265
- }),
266
254
  mcp_get_tool_description: zod.z.object({}).passthrough(),
267
255
  mcp_call_tool: zod.z.object({
268
256
  type: zod.z.literal("mcp_call_tool_result"),
@@ -299,17 +287,6 @@ const ToolOutputSchemas = {
299
287
  content: zod.z.string(),
300
288
  downloadPath: zod.z.string().optional()
301
289
  }),
302
- create_rule: zod.z.object({
303
- type: zod.z.literal("rule_create_result"),
304
- ruleName: zod.z.string(),
305
- createState: zod.z.enum([
306
- "success",
307
- "invoke",
308
- "cancelled"
309
- ]),
310
- hint: zod.z.string().optional(),
311
- filePath: zod.z.string().optional()
312
- }),
313
290
  update_memory: zod.z.object({
314
291
  type: zod.z.literal("update_memory_result"),
315
292
  success: zod.z.boolean(),
@@ -323,9 +300,9 @@ const ToolOutputSchemas = {
323
300
  }),
324
301
  search_content: zod.z.object({
325
302
  type: zod.z.literal("search_content_result"),
326
- directory: zod.z.string(),
303
+ path: zod.z.string(),
327
304
  pattern: zod.z.string(),
328
- fileTypes: zod.z.string(),
305
+ glob: zod.z.string(),
329
306
  matches: zod.z.array(zod.z.object({
330
307
  filePath: zod.z.string(),
331
308
  content: zod.z.string(),
@@ -337,7 +314,7 @@ const ToolOutputSchemas = {
337
314
  totalCount: zod.z.number(),
338
315
  hasMore: zod.z.boolean(),
339
316
  offset: zod.z.number(),
340
- limit: zod.z.number(),
317
+ headLimit: zod.z.number(),
341
318
  contextBefore: zod.z.number(),
342
319
  contextAfter: zod.z.number(),
343
320
  contextAround: zod.z.number().optional(),
@@ -523,15 +500,6 @@ const ToolOutputSchemas = {
523
500
  }).optional()
524
501
  }))
525
502
  }),
526
- component_get_prompt: zod.z.object({
527
- type: zod.z.literal("component_get_prompt_result"),
528
- componentType: zod.z.string(),
529
- webFramework: zod.z.string(),
530
- data: zod.z.object({
531
- type: zod.z.literal("text"),
532
- text: zod.z.string()
533
- })
534
- }),
535
503
  web_fetch: zod.z.object({
536
504
  type: zod.z.literal("web_fetch_tool_result"),
537
505
  message: zod.z.string(),
@@ -547,14 +515,29 @@ const ToolOutputSchemas = {
547
515
  }),
548
516
  web_search: zod.z.object({
549
517
  type: zod.z.literal("web_search_tool_result"),
550
- data: zod.z.array(zod.z.object({
551
- passage: zod.z.string(),
552
- uri: zod.z.string(),
553
- site: zod.z.string(),
518
+ query: zod.z.string().optional(),
519
+ searchType: zod.z.literal("text2text").optional(),
520
+ provider: zod.z.string().optional(),
521
+ results: zod.z.array(zod.z.object({
554
522
  title: zod.z.string(),
555
- snippets: zod.z.array(zod.z.string()).optional(),
556
- content: zod.z.string().optional()
523
+ url: zod.z.string(),
524
+ snippet: zod.z.string().optional(),
525
+ site: zod.z.string().optional(),
526
+ highlights: zod.z.array(zod.z.string()).optional(),
527
+ content: zod.z.string().optional(),
528
+ favicon: zod.z.string().optional()
557
529
  })),
530
+ images: zod.z.array(zod.z.object({
531
+ url: zod.z.string(),
532
+ description: zod.z.string().optional(),
533
+ sourceUrl: zod.z.string().optional(),
534
+ width: zod.z.number().optional(),
535
+ height: zod.z.number().optional(),
536
+ title: zod.z.string().optional(),
537
+ siteName: zod.z.string().optional()
538
+ })),
539
+ totalResults: zod.z.number().optional(),
540
+ responseTimeMs: zod.z.number().optional(),
558
541
  searchInput: zod.z.string().optional()
559
542
  }),
560
543
  task: zod.z.object({
@@ -700,7 +683,11 @@ const ExtensionMethod = {
700
683
  CHECKPOINT: "_codebuddy.ai/checkpoint",
701
684
  USAGE: "_codebuddy.ai/usage",
702
685
  COMMAND: "_codebuddy.ai/command",
703
- AUTH_URL: "_codebuddy.ai/authUrl"
686
+ AUTH_URL: "_codebuddy.ai/authUrl",
687
+ FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot",
688
+ DELEGATE_TOOL: "_codebuddy.ai/delegateTool",
689
+ DELEGATE_TOOLS_CHANGED: "_codebuddy.ai/delegateToolsChanged",
690
+ UI_CONTROL: "_codebuddy.ai/uiControl"
704
691
  };
705
692
  /**
706
693
  * All known extension methods
@@ -711,7 +698,10 @@ const KNOWN_EXTENSIONS = [
711
698
  ExtensionMethod.CHECKPOINT,
712
699
  ExtensionMethod.USAGE,
713
700
  ExtensionMethod.COMMAND,
714
- ExtensionMethod.AUTH_URL
701
+ ExtensionMethod.AUTH_URL,
702
+ ExtensionMethod.FILE_HISTORY_SNAPSHOT,
703
+ ExtensionMethod.DELEGATE_TOOL,
704
+ ExtensionMethod.DELEGATE_TOOLS_CHANGED
715
705
  ];
716
706
 
717
707
  //#endregion
@@ -762,7 +752,7 @@ function parseSSELine(line, currentEvent) {
762
752
  };
763
753
  }
764
754
  function streamableHttp(options) {
765
- const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, postTimeout = 3e4, backpressure = {} } = options;
755
+ const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, connectionTimeout = 3e4, postTimeout = 3e4, backpressure = {} } = options;
766
756
  const { enabled: reconnectEnabled = true, initialDelay = 1e3, maxDelay = 3e4, maxRetries = Infinity, jitter: jitterEnabled = true } = reconnect;
767
757
  const { highWaterMark = 100, lowWaterMark = 50, pauseTimeout = 5e3 } = backpressure;
768
758
  let connectionId;
@@ -973,11 +963,21 @@ function streamableHttp(options) {
973
963
  const headers = buildHeaders();
974
964
  headers["Accept"] = "text/event-stream";
975
965
  if (lastEventId) headers["Last-Event-ID"] = lastEventId;
976
- const response = await customFetch(endpoint, {
977
- method: "GET",
978
- headers,
979
- signal: combinedSignal
980
- });
966
+ const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
967
+ const connectController = new AbortController();
968
+ const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
969
+ if (externalSignal) externalSignal.addEventListener("abort", () => connectController.abort(), { once: true });
970
+ abortController.signal.addEventListener("abort", () => connectController.abort(), { once: true });
971
+ let response;
972
+ try {
973
+ response = await customFetch(endpoint, {
974
+ method: "GET",
975
+ headers,
976
+ signal: connectController.signal
977
+ });
978
+ } finally {
979
+ clearTimeout(connectTimer);
980
+ }
981
981
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
982
982
  const newConnectionId = response.headers.get("Acp-Connection-Id");
983
983
  if (!newConnectionId) throw new Error("Server did not return Acp-Connection-Id header");
@@ -1913,6 +1913,9 @@ var StreamableHttpClient = class {
1913
1913
  headers: this.options.headers,
1914
1914
  reconnect: this.options.reconnect,
1915
1915
  fetch: this.options.fetch,
1916
+ heartbeatTimeout: this.options.heartbeatTimeout,
1917
+ postTimeout: this.options.postTimeout,
1918
+ connectionTimeout: this.options.connectionTimeout,
1916
1919
  onConnect: (connectionId) => {
1917
1920
  this.options.logger?.debug(`Transport connected: ${connectionId}`);
1918
1921
  },
@@ -1987,10 +1990,6 @@ var StreamableHttpClient = class {
1987
1990
  },
1988
1991
  requestPermission: async (params) => this.handleRequestPermission(params),
1989
1992
  extNotification: async (method, params) => {
1990
- console.log("[ACP-Client] extNotification callback invoked:", {
1991
- method,
1992
- paramsKeys: Object.keys(params)
1993
- });
1994
1993
  await this.handleExtNotification(method, params);
1995
1994
  },
1996
1995
  extMethod: async (method, params) => this.handleExtMethod(method, params)
@@ -1998,10 +1997,15 @@ var StreamableHttpClient = class {
1998
1997
  }
1999
1998
  /**
2000
1999
  * Create a new session
2000
+ *
2001
+ * Retries on transient network errors (e.g., proxy connection reset)
2002
+ * since session/new is idempotent and safe to retry.
2001
2003
  */
2002
2004
  async createSession(cwd) {
2003
2005
  this.ensureInitialized("createSession");
2004
- try {
2006
+ const maxRetries = 2;
2007
+ let lastError;
2008
+ for (let attempt = 0; attempt <= maxRetries; attempt++) try {
2005
2009
  const response = await this.connection.newSession({
2006
2010
  cwd,
2007
2011
  mcpServers: []
@@ -2009,8 +2013,16 @@ var StreamableHttpClient = class {
2009
2013
  this.options.logger?.info(`Session created: ${response.sessionId}`);
2010
2014
  return response;
2011
2015
  } catch (err) {
2012
- throw new SessionError(`Failed to create session: ${err instanceof Error ? err.message : String(err)}`, void 0, err instanceof Error ? err : void 0);
2016
+ lastError = err instanceof Error ? err : new Error(String(err));
2017
+ if (attempt < maxRetries && isRetryableNetworkError(err)) {
2018
+ const delay = 500 * Math.pow(2, attempt);
2019
+ this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
2020
+ await new Promise((resolve) => setTimeout(resolve, delay));
2021
+ continue;
2022
+ }
2023
+ throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
2013
2024
  }
2025
+ throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
2014
2026
  }
2015
2027
  /**
2016
2028
  * Load an existing session
@@ -2234,6 +2246,18 @@ var StreamableHttpClient = class {
2234
2246
  if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
2235
2247
  }
2236
2248
  };
2249
+ /**
2250
+ * Check if an error is a retryable network-level error.
2251
+ * Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
2252
+ */
2253
+ function isRetryableNetworkError(error) {
2254
+ if (error instanceof TypeError) return true;
2255
+ if (error instanceof Error) {
2256
+ const msg = error.message.toLowerCase();
2257
+ return msg.includes("failed to fetch") || msg.includes("fetch failed") || msg.includes("network request failed") || msg.includes("econnreset") || msg.includes("econnrefused") || msg.includes("socket hang up");
2258
+ }
2259
+ return false;
2260
+ }
2237
2261
 
2238
2262
  //#endregion
2239
2263
  //#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
@@ -2269,7 +2293,7 @@ var CloudAgentConnection = class {
2269
2293
  fetch: config.fetch,
2270
2294
  clientCapabilities: config.clientCapabilities,
2271
2295
  onSessionUpdate: (update) => {
2272
- if (!this._isStreaming) this.emit("sessionUpdate", update);
2296
+ if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
2273
2297
  },
2274
2298
  onArtifact: (artifact, event) => {
2275
2299
  console.log("[CloudConnection] onArtifact callback:", {
@@ -2283,10 +2307,41 @@ var CloudAgentConnection = class {
2283
2307
  },
2284
2308
  onUsageUpdate: (usage) => {
2285
2309
  this.emit("usageUpdate", usage);
2310
+ },
2311
+ onExtNotification: (method, params) => {
2312
+ console.log("[CloudConnection] Received extNotification:", {
2313
+ method,
2314
+ paramsKeys: Object.keys(params)
2315
+ });
2316
+ if (method === ExtensionMethod.COMMAND) {
2317
+ const action = params.action;
2318
+ const commandParams = params.params;
2319
+ console.log("[CloudConnection] Emitting command event:", {
2320
+ action,
2321
+ paramsKeys: commandParams ? Object.keys(commandParams) : []
2322
+ });
2323
+ this.emit("command", {
2324
+ action,
2325
+ params: commandParams
2326
+ });
2327
+ }
2286
2328
  }
2287
2329
  });
2288
2330
  this.setupEventForwarding();
2289
2331
  }
2332
+ /**
2333
+ * Check whether a notification belongs to this connection's own session.
2334
+ *
2335
+ * CloudConnection.createSession() overrides sessionId to agentId, so the
2336
+ * rest of the client stack uses agentId as the canonical session
2337
+ * identifier. Notifications whose sessionId differs from agentId
2338
+ * originate from sub-agent sessions running inside the same sandbox and
2339
+ * should be silently ignored at this layer — the adapter layer handles
2340
+ * sub-agent messages independently via parentToolUseId in _meta.
2341
+ */
2342
+ isOwnSessionNotification(notification) {
2343
+ return notification.sessionId === this.agentId;
2344
+ }
2290
2345
  setupEventForwarding() {
2291
2346
  this.client.on("connecting", () => {
2292
2347
  this.emit("connecting", void 0);
@@ -2411,7 +2466,7 @@ var CloudAgentConnection = class {
2411
2466
  }
2412
2467
  async createSession(params) {
2413
2468
  return {
2414
- ...await this.client.loadSession(this.agentId, this.cwd),
2469
+ ...await this.client.createSession(this.cwd),
2415
2470
  sessionId: this.agentId
2416
2471
  };
2417
2472
  }
@@ -2447,6 +2502,7 @@ var CloudAgentConnection = class {
2447
2502
  let resolveUpdate = null;
2448
2503
  let done = false;
2449
2504
  const listener = (update) => {
2505
+ if (!this.isOwnSessionNotification(update)) return;
2450
2506
  if (resolveUpdate) {
2451
2507
  resolveUpdate(update);
2452
2508
  resolveUpdate = null;
@@ -2529,6 +2585,16 @@ var CloudAgentConnection = class {
2529
2585
  get sessionConnectionInfo() {
2530
2586
  return this._sessionConnectionInfo;
2531
2587
  }
2588
+ async reportTelemetry(eventName, payload) {
2589
+ try {
2590
+ await this.client.extMethod("reportTelemetry", {
2591
+ eventName,
2592
+ payload
2593
+ });
2594
+ } catch (error) {
2595
+ console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
2596
+ }
2597
+ }
2532
2598
  async extMethod(method, params) {
2533
2599
  return this.client.extMethod(method, params);
2534
2600
  }
@@ -3131,7 +3197,7 @@ var utils_default = {
3131
3197
  *
3132
3198
  * @returns {Error} The created error.
3133
3199
  */
3134
- function AxiosError(message, code, config, request, response) {
3200
+ function AxiosError$1(message, code, config, request, response) {
3135
3201
  Error.call(this);
3136
3202
  if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
3137
3203
  else this.stack = (/* @__PURE__ */ new Error()).stack;
@@ -3145,7 +3211,7 @@ function AxiosError(message, code, config, request, response) {
3145
3211
  this.status = response.status ? response.status : null;
3146
3212
  }
3147
3213
  }
3148
- utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3214
+ utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
3149
3215
  return {
3150
3216
  message: this.message,
3151
3217
  name: this.name,
@@ -3160,7 +3226,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
3160
3226
  status: this.status
3161
3227
  };
3162
3228
  } });
3163
- const prototype$1 = AxiosError.prototype;
3229
+ const prototype$1 = AxiosError$1.prototype;
3164
3230
  const descriptors = {};
3165
3231
  [
3166
3232
  "ERR_BAD_OPTION_VALUE",
@@ -3178,22 +3244,22 @@ const descriptors = {};
3178
3244
  ].forEach((code) => {
3179
3245
  descriptors[code] = { value: code };
3180
3246
  });
3181
- Object.defineProperties(AxiosError, descriptors);
3247
+ Object.defineProperties(AxiosError$1, descriptors);
3182
3248
  Object.defineProperty(prototype$1, "isAxiosError", { value: true });
3183
- AxiosError.from = (error, code, config, request, response, customProps) => {
3249
+ AxiosError$1.from = (error, code, config, request, response, customProps) => {
3184
3250
  const axiosError = Object.create(prototype$1);
3185
3251
  utils_default.toFlatObject(error, axiosError, function filter(obj) {
3186
3252
  return obj !== Error.prototype;
3187
3253
  }, (prop) => {
3188
3254
  return prop !== "isAxiosError";
3189
3255
  });
3190
- AxiosError.call(axiosError, error.message, code, config, request, response);
3256
+ AxiosError$1.call(axiosError, error.message, code, config, request, response);
3191
3257
  axiosError.cause = error;
3192
3258
  axiosError.name = error.name;
3193
3259
  customProps && Object.assign(axiosError, customProps);
3194
3260
  return axiosError;
3195
3261
  };
3196
- var AxiosError_default = AxiosError;
3262
+ var AxiosError_default = AxiosError$1;
3197
3263
 
3198
3264
  //#endregion
3199
3265
  //#region ../../node_modules/delayed-stream/lib/delayed_stream.js
@@ -11920,7 +11986,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
11920
11986
  *
11921
11987
  * @returns
11922
11988
  */
11923
- function toFormData(obj, formData, options) {
11989
+ function toFormData$1(obj, formData, options) {
11924
11990
  if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
11925
11991
  formData = formData || new (FormData_default || FormData)();
11926
11992
  options = utils_default.toFlatObject(options, {
@@ -11991,7 +12057,7 @@ function toFormData(obj, formData, options) {
11991
12057
  build(obj);
11992
12058
  return formData;
11993
12059
  }
11994
- var toFormData_default = toFormData;
12060
+ var toFormData_default = toFormData$1;
11995
12061
 
11996
12062
  //#endregion
11997
12063
  //#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
@@ -12515,7 +12581,7 @@ function buildAccessors(obj, header) {
12515
12581
  });
12516
12582
  });
12517
12583
  }
12518
- var AxiosHeaders = class {
12584
+ var AxiosHeaders$1 = class {
12519
12585
  constructor(headers) {
12520
12586
  headers && this.set(headers);
12521
12587
  }
@@ -12653,7 +12719,7 @@ var AxiosHeaders = class {
12653
12719
  return this;
12654
12720
  }
12655
12721
  };
12656
- AxiosHeaders.accessor([
12722
+ AxiosHeaders$1.accessor([
12657
12723
  "Content-Type",
12658
12724
  "Content-Length",
12659
12725
  "Accept",
@@ -12661,7 +12727,7 @@ AxiosHeaders.accessor([
12661
12727
  "User-Agent",
12662
12728
  "Authorization"
12663
12729
  ]);
12664
- utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
12730
+ utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
12665
12731
  let mapped = key[0].toUpperCase() + key.slice(1);
12666
12732
  return {
12667
12733
  get: () => value,
@@ -12670,8 +12736,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
12670
12736
  }
12671
12737
  };
12672
12738
  });
12673
- utils_default.freezeMethods(AxiosHeaders);
12674
- var AxiosHeaders_default = AxiosHeaders;
12739
+ utils_default.freezeMethods(AxiosHeaders$1);
12740
+ var AxiosHeaders_default = AxiosHeaders$1;
12675
12741
 
12676
12742
  //#endregion
12677
12743
  //#region ../agent-provider/node_modules/axios/lib/core/transformData.js
@@ -12697,7 +12763,7 @@ function transformData(fns, response) {
12697
12763
 
12698
12764
  //#endregion
12699
12765
  //#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
12700
- function isCancel(value) {
12766
+ function isCancel$1(value) {
12701
12767
  return !!(value && value.__CANCEL__);
12702
12768
  }
12703
12769
 
@@ -12712,12 +12778,12 @@ function isCancel(value) {
12712
12778
  *
12713
12779
  * @returns {CanceledError} The created error.
12714
12780
  */
12715
- function CanceledError(message, config, request) {
12781
+ function CanceledError$1(message, config, request) {
12716
12782
  AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
12717
12783
  this.name = "CanceledError";
12718
12784
  }
12719
- utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
12720
- var CanceledError_default = CanceledError;
12785
+ utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
12786
+ var CanceledError_default = CanceledError$1;
12721
12787
 
12722
12788
  //#endregion
12723
12789
  //#region ../agent-provider/node_modules/axios/lib/core/settle.js
@@ -14119,7 +14185,9 @@ var require_follow_redirects = /* @__PURE__ */ __commonJSMin(((exports, module)
14119
14185
 
14120
14186
  //#endregion
14121
14187
  //#region ../agent-provider/node_modules/axios/lib/env/data.js
14122
- const VERSION = "1.10.0";
14188
+ var import_follow_redirects = /* @__PURE__ */ __toESM(require_follow_redirects(), 1);
14189
+ var import_proxy_from_env = /* @__PURE__ */ __toESM(require_proxy_from_env(), 1);
14190
+ const VERSION$1 = "1.10.0";
14123
14191
 
14124
14192
  //#endregion
14125
14193
  //#region ../agent-provider/node_modules/axios/lib/helpers/parseProtocol.js
@@ -14482,8 +14550,6 @@ const asyncDecorator = (fn) => (...args) => utils_default.asap(() => fn(...args)
14482
14550
 
14483
14551
  //#endregion
14484
14552
  //#region ../agent-provider/node_modules/axios/lib/adapters/http.js
14485
- var import_proxy_from_env = /* @__PURE__ */ __toESM(require_proxy_from_env(), 1);
14486
- var import_follow_redirects = /* @__PURE__ */ __toESM(require_follow_redirects(), 1);
14487
14553
  const zlibOptions = {
14488
14554
  flush: zlib.default.constants.Z_SYNC_FLUSH,
14489
14555
  finishFlush: zlib.default.constants.Z_SYNC_FLUSH
@@ -14649,7 +14715,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
14649
14715
  }
14650
14716
  if (supportedProtocols.indexOf(protocol) === -1) return reject(new AxiosError_default("Unsupported protocol " + protocol, AxiosError_default.ERR_BAD_REQUEST, config));
14651
14717
  const headers = AxiosHeaders_default.from(config.headers).normalize();
14652
- headers.set("User-Agent", "axios/" + VERSION, false);
14718
+ headers.set("User-Agent", "axios/" + VERSION$1, false);
14653
14719
  const { onUploadProgress, onDownloadProgress } = config;
14654
14720
  const maxRate = config.maxRate;
14655
14721
  let maxUploadRate = void 0;
@@ -14659,7 +14725,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
14659
14725
  data = formDataToStream_default(data, (formHeaders) => {
14660
14726
  headers.set(formHeaders);
14661
14727
  }, {
14662
- tag: `axios-${VERSION}-boundary`,
14728
+ tag: `axios-${VERSION$1}-boundary`,
14663
14729
  boundary: userBoundary && userBoundary[1] || void 0
14664
14730
  });
14665
14731
  } else if (utils_default.isFormData(data) && utils_default.isFunction(data.getHeaders)) {
@@ -14924,7 +14990,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
14924
14990
  *
14925
14991
  * @returns {Object} New object resulting from merging config2 to config1
14926
14992
  */
14927
- function mergeConfig(config1, config2) {
14993
+ function mergeConfig$1(config1, config2) {
14928
14994
  config2 = config2 || {};
14929
14995
  const config = {};
14930
14996
  function getMergedValue(target, source, prop, caseless) {
@@ -14990,7 +15056,7 @@ function mergeConfig(config1, config2) {
14990
15056
  //#endregion
14991
15057
  //#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
14992
15058
  var resolveConfig_default = (config) => {
14993
- const newConfig = mergeConfig({}, config);
15059
+ const newConfig = mergeConfig$1({}, config);
14994
15060
  let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
14995
15061
  newConfig.headers = headers = AxiosHeaders_default.from(headers);
14996
15062
  newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
@@ -15421,7 +15487,7 @@ function dispatchRequest(config) {
15421
15487
  response.headers = AxiosHeaders_default.from(response.headers);
15422
15488
  return response;
15423
15489
  }, function onAdapterRejection(reason) {
15424
- if (!isCancel(reason)) {
15490
+ if (!isCancel$1(reason)) {
15425
15491
  throwIfCancellationRequested(config);
15426
15492
  if (reason && reason.response) {
15427
15493
  reason.response.data = transformData.call(config, config.transformResponse, reason.response);
@@ -15459,7 +15525,7 @@ const deprecatedWarnings = {};
15459
15525
  */
15460
15526
  validators$1.transitional = function transitional(validator, version, message) {
15461
15527
  function formatMessage(opt, desc) {
15462
- return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
15528
+ return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
15463
15529
  }
15464
15530
  return (value, opt, opts) => {
15465
15531
  if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
@@ -15516,7 +15582,7 @@ const validators = validator_default.validators;
15516
15582
  *
15517
15583
  * @return {Axios} A new instance of Axios
15518
15584
  */
15519
- var Axios = class {
15585
+ var Axios$1 = class {
15520
15586
  constructor(instanceConfig) {
15521
15587
  this.defaults = instanceConfig || {};
15522
15588
  this.interceptors = {
@@ -15553,7 +15619,7 @@ var Axios = class {
15553
15619
  config = config || {};
15554
15620
  config.url = configOrUrl;
15555
15621
  } else config = configOrUrl || {};
15556
- config = mergeConfig(this.defaults, config);
15622
+ config = mergeConfig$1(this.defaults, config);
15557
15623
  const { transitional, paramsSerializer, headers } = config;
15558
15624
  if (transitional !== void 0) validator_default.assertOptions(transitional, {
15559
15625
  silentJSONParsing: validators.transitional(validators.boolean),
@@ -15632,7 +15698,7 @@ var Axios = class {
15632
15698
  return promise;
15633
15699
  }
15634
15700
  getUri(config) {
15635
- config = mergeConfig(this.defaults, config);
15701
+ config = mergeConfig$1(this.defaults, config);
15636
15702
  return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
15637
15703
  }
15638
15704
  };
@@ -15642,8 +15708,8 @@ utils_default.forEach([
15642
15708
  "head",
15643
15709
  "options"
15644
15710
  ], function forEachMethodNoData(method) {
15645
- Axios.prototype[method] = function(url, config) {
15646
- return this.request(mergeConfig(config || {}, {
15711
+ Axios$1.prototype[method] = function(url, config) {
15712
+ return this.request(mergeConfig$1(config || {}, {
15647
15713
  method,
15648
15714
  url,
15649
15715
  data: (config || {}).data
@@ -15657,7 +15723,7 @@ utils_default.forEach([
15657
15723
  ], function forEachMethodWithData(method) {
15658
15724
  function generateHTTPMethod(isForm) {
15659
15725
  return function httpMethod(url, data, config) {
15660
- return this.request(mergeConfig(config || {}, {
15726
+ return this.request(mergeConfig$1(config || {}, {
15661
15727
  method,
15662
15728
  headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
15663
15729
  url,
@@ -15665,10 +15731,10 @@ utils_default.forEach([
15665
15731
  }));
15666
15732
  };
15667
15733
  }
15668
- Axios.prototype[method] = generateHTTPMethod();
15669
- Axios.prototype[method + "Form"] = generateHTTPMethod(true);
15734
+ Axios$1.prototype[method] = generateHTTPMethod();
15735
+ Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
15670
15736
  });
15671
- var Axios_default = Axios;
15737
+ var Axios_default = Axios$1;
15672
15738
 
15673
15739
  //#endregion
15674
15740
  //#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
@@ -15679,7 +15745,7 @@ var Axios_default = Axios;
15679
15745
  *
15680
15746
  * @returns {CancelToken}
15681
15747
  */
15682
- var CancelToken = class CancelToken {
15748
+ var CancelToken$1 = class CancelToken$1 {
15683
15749
  constructor(executor) {
15684
15750
  if (typeof executor !== "function") throw new TypeError("executor must be a function.");
15685
15751
  let resolvePromise;
@@ -15751,14 +15817,14 @@ var CancelToken = class CancelToken {
15751
15817
  static source() {
15752
15818
  let cancel;
15753
15819
  return {
15754
- token: new CancelToken(function executor(c) {
15820
+ token: new CancelToken$1(function executor(c) {
15755
15821
  cancel = c;
15756
15822
  }),
15757
15823
  cancel
15758
15824
  };
15759
15825
  }
15760
15826
  };
15761
- var CancelToken_default = CancelToken;
15827
+ var CancelToken_default = CancelToken$1;
15762
15828
 
15763
15829
  //#endregion
15764
15830
  //#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
@@ -15783,7 +15849,7 @@ var CancelToken_default = CancelToken;
15783
15849
  *
15784
15850
  * @returns {Function}
15785
15851
  */
15786
- function spread(callback) {
15852
+ function spread$1(callback) {
15787
15853
  return function wrap(arr) {
15788
15854
  return callback.apply(null, arr);
15789
15855
  };
@@ -15798,13 +15864,13 @@ function spread(callback) {
15798
15864
  *
15799
15865
  * @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
15800
15866
  */
15801
- function isAxiosError(payload) {
15867
+ function isAxiosError$1(payload) {
15802
15868
  return utils_default.isObject(payload) && payload.isAxiosError === true;
15803
15869
  }
15804
15870
 
15805
15871
  //#endregion
15806
15872
  //#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
15807
- const HttpStatusCode = {
15873
+ const HttpStatusCode$1 = {
15808
15874
  Continue: 100,
15809
15875
  SwitchingProtocols: 101,
15810
15876
  Processing: 102,
@@ -15869,10 +15935,10 @@ const HttpStatusCode = {
15869
15935
  NotExtended: 510,
15870
15936
  NetworkAuthenticationRequired: 511
15871
15937
  };
15872
- Object.entries(HttpStatusCode).forEach(([key, value]) => {
15873
- HttpStatusCode[value] = key;
15938
+ Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
15939
+ HttpStatusCode$1[value] = key;
15874
15940
  });
15875
- var HttpStatusCode_default = HttpStatusCode;
15941
+ var HttpStatusCode_default = HttpStatusCode$1;
15876
15942
 
15877
15943
  //#endregion
15878
15944
  //#region ../agent-provider/node_modules/axios/lib/axios.js
@@ -15889,7 +15955,7 @@ function createInstance(defaultConfig) {
15889
15955
  utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
15890
15956
  utils_default.extend(instance, context, null, { allOwnKeys: true });
15891
15957
  instance.create = function create(instanceConfig) {
15892
- return createInstance(mergeConfig(defaultConfig, instanceConfig));
15958
+ return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
15893
15959
  };
15894
15960
  return instance;
15895
15961
  }
@@ -15897,17 +15963,17 @@ const axios = createInstance(defaults_default);
15897
15963
  axios.Axios = Axios_default;
15898
15964
  axios.CanceledError = CanceledError_default;
15899
15965
  axios.CancelToken = CancelToken_default;
15900
- axios.isCancel = isCancel;
15901
- axios.VERSION = VERSION;
15966
+ axios.isCancel = isCancel$1;
15967
+ axios.VERSION = VERSION$1;
15902
15968
  axios.toFormData = toFormData_default;
15903
15969
  axios.AxiosError = AxiosError_default;
15904
15970
  axios.Cancel = axios.CanceledError;
15905
15971
  axios.all = function all(promises) {
15906
15972
  return Promise.all(promises);
15907
15973
  };
15908
- axios.spread = spread;
15909
- axios.isAxiosError = isAxiosError;
15910
- axios.mergeConfig = mergeConfig;
15974
+ axios.spread = spread$1;
15975
+ axios.isAxiosError = isAxiosError$1;
15976
+ axios.mergeConfig = mergeConfig$1;
15911
15977
  axios.AxiosHeaders = AxiosHeaders_default;
15912
15978
  axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
15913
15979
  axios.getAdapter = adapters_default.getAdapter;
@@ -15915,6 +15981,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
15915
15981
  axios.default = axios;
15916
15982
  var axios_default = axios;
15917
15983
 
15984
+ //#endregion
15985
+ //#region ../agent-provider/node_modules/axios/index.js
15986
+ const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
15987
+
15918
15988
  //#endregion
15919
15989
  //#region ../agent-provider/src/http/http-service.ts
15920
15990
  /**
@@ -15923,7 +15993,7 @@ var axios_default = axios;
15923
15993
  * 特性:
15924
15994
  * - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
15925
15995
  * - 支持拦截器注册(其他模块可注入 header)
15926
- * - 统一 401/403 处理
15996
+ * - 统一 401 处理(支持自动刷新 token 并重试)
15927
15997
  * - 自动携带凭证(withCredentials)
15928
15998
  * - 类型安全
15929
15999
  *
@@ -15963,6 +16033,8 @@ var HttpService = class HttpService {
15963
16033
  */
15964
16034
  constructor(config = {}) {
15965
16035
  this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
16036
+ this.isRefreshing = false;
16037
+ this.refreshSubscribers = [];
15966
16038
  this.config = config;
15967
16039
  this.axiosInstance = axios_default.create({
15968
16040
  baseURL: config.baseURL?.replace(/\/$/, "") || "",
@@ -16003,18 +16075,54 @@ var HttpService = class HttpService {
16003
16075
  }, (error) => Promise.reject(error));
16004
16076
  }
16005
16077
  /**
16006
- * 注册默认响应拦截器(处理 401/403)
16078
+ * 注册默认响应拦截器(处理 401,支持自动重试)
16007
16079
  */
16008
16080
  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();
16081
+ this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
16082
+ const originalRequest = error.config;
16083
+ if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
16084
+ if (originalRequest.url?.includes("/console/accounts")) {
16085
+ console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
16086
+ return Promise.reject(error);
16087
+ }
16088
+ console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
16089
+ originalRequest._retry = true;
16090
+ if (this.isRefreshing) return new Promise((resolve, reject) => {
16091
+ this.refreshSubscribers.push((success) => {
16092
+ if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
16093
+ else reject(error);
16094
+ });
16095
+ });
16096
+ this.isRefreshing = true;
16097
+ try {
16098
+ await this.triggerUnauthorizedCallbacks();
16099
+ this.onRefreshSuccess();
16100
+ return this.axiosInstance.request(originalRequest);
16101
+ } catch (refreshError) {
16102
+ this.onRefreshFailure();
16103
+ return Promise.reject(error);
16104
+ } finally {
16105
+ this.isRefreshing = false;
16106
+ }
16013
16107
  }
16014
16108
  return Promise.reject(error);
16015
16109
  });
16016
16110
  }
16017
16111
  /**
16112
+ * token 刷新成功,通知所有等待的请求
16113
+ */
16114
+ onRefreshSuccess() {
16115
+ this.refreshSubscribers.forEach((callback) => callback(true));
16116
+ this.refreshSubscribers = [];
16117
+ }
16118
+ /**
16119
+ * token 刷新失败,通知所有等待的请求
16120
+ */
16121
+ onRefreshFailure() {
16122
+ this.refreshSubscribers.forEach((callback) => callback(false));
16123
+ this.refreshSubscribers = [];
16124
+ }
16125
+ /**
16018
16126
  * 注册请求拦截器
16019
16127
  * @param onFulfilled 请求成功拦截器
16020
16128
  * @param onRejected 请求失败拦截器
@@ -16091,16 +16199,18 @@ var HttpService = class HttpService {
16091
16199
  this.unauthorizedCallbacks.delete(callback);
16092
16200
  }
16093
16201
  /**
16094
- * 触发所有 401 回调
16202
+ * 触发所有 401 回调并等待完成
16203
+ * @returns Promise,等待所有回调完成
16204
+ * @throws 如果任何回调失败,则抛出错误
16095
16205
  */
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
- });
16206
+ async triggerUnauthorizedCallbacks() {
16207
+ const callbacks = Array.from(this.unauthorizedCallbacks);
16208
+ const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
16209
+ if (failedResults.length > 0) {
16210
+ const errors = failedResults.map((r) => r.reason);
16211
+ console.error("[HttpService] Some unauthorized callbacks failed:", errors);
16212
+ throw errors[0];
16213
+ }
16104
16214
  }
16105
16215
  /**
16106
16216
  * 更新 authToken
@@ -16212,6 +16322,7 @@ var AccountService = class {
16212
16322
  this.initPromise = null;
16213
16323
  this.initResolve = null;
16214
16324
  this.requestInterceptorId = null;
16325
+ this.crossTabBroadcaster = null;
16215
16326
  this.initPromise = new Promise((resolve) => {
16216
16327
  this.initResolve = resolve;
16217
16328
  });
@@ -16260,15 +16371,34 @@ var AccountService = class {
16260
16371
  this.initialized = true;
16261
16372
  this.initResolve?.(account);
16262
16373
  }
16263
- if (!wasInitialized || prev?.uid !== account?.uid) this.notifyListeners();
16374
+ if (!wasInitialized || prev?.uid !== account?.uid) {
16375
+ this.notifyListeners();
16376
+ if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
16377
+ }
16264
16378
  }
16265
16379
  /**
16266
16380
  * 清除账号(登出)
16381
+ * 先广播登出消息,再清除本地账号
16267
16382
  */
16268
16383
  clearAccount() {
16384
+ if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
16385
+ this.setAccount(null);
16386
+ }
16387
+ /**
16388
+ * 静默清除账号(不广播)
16389
+ * 用于收到其他标签页 logout 消息时,避免循环广播
16390
+ */
16391
+ clearAccountSilently() {
16269
16392
  this.setAccount(null);
16270
16393
  }
16271
16394
  /**
16395
+ * 设置跨标签页认证同步广播器
16396
+ * 应在应用初始化时由上层(如 agent-ui)调用
16397
+ */
16398
+ setCrossTabBroadcaster(broadcaster) {
16399
+ this.crossTabBroadcaster = broadcaster;
16400
+ }
16401
+ /**
16272
16402
  * 订阅账号变化
16273
16403
  * @param callback 变化时的回调函数
16274
16404
  * @returns 取消订阅函数
@@ -16333,6 +16463,114 @@ var AccountService = class {
16333
16463
  * 导出单例实例
16334
16464
  */
16335
16465
  const accountService = new AccountService();
16466
+ /**
16467
+ * 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
16468
+ * 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
16469
+ */
16470
+ if (typeof window !== "undefined") window.__genieAccountService = accountService;
16471
+
16472
+ //#endregion
16473
+ //#region ../agent-provider/src/common/utils/lru-cache.ts
16474
+ /**
16475
+ * LRU (Least Recently Used) Cache
16476
+ * 当缓存达到容量上限时,自动淘汰最久未使用的数据
16477
+ *
16478
+ * @template K - Key 类型
16479
+ * @template V - Value 类型
16480
+ */
16481
+ var LRUCache = class {
16482
+ /**
16483
+ * 创建 LRU 缓存实例
16484
+ * @param capacity - 缓存容量上限
16485
+ */
16486
+ constructor(capacity) {
16487
+ if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
16488
+ this.capacity = capacity;
16489
+ this.cache = /* @__PURE__ */ new Map();
16490
+ }
16491
+ /**
16492
+ * 获取缓存值
16493
+ * @param key - 键
16494
+ * @returns 值,如果不存在返回 undefined
16495
+ */
16496
+ get(key) {
16497
+ if (!this.cache.has(key)) return;
16498
+ const value = this.cache.get(key);
16499
+ this.cache.delete(key);
16500
+ this.cache.set(key, value);
16501
+ return value;
16502
+ }
16503
+ /**
16504
+ * 设置缓存值
16505
+ * @param key - 键
16506
+ * @param value - 值
16507
+ */
16508
+ set(key, value) {
16509
+ if (this.cache.has(key)) this.cache.delete(key);
16510
+ else if (this.cache.size >= this.capacity) {
16511
+ const firstKey = this.cache.keys().next().value;
16512
+ this.cache.delete(firstKey);
16513
+ }
16514
+ this.cache.set(key, value);
16515
+ }
16516
+ /**
16517
+ * 检查 key 是否存在
16518
+ * @param key - 键
16519
+ * @returns 是否存在
16520
+ */
16521
+ has(key) {
16522
+ return this.cache.has(key);
16523
+ }
16524
+ /**
16525
+ * 删除指定 key
16526
+ * @param key - 键
16527
+ * @returns 是否删除成功
16528
+ */
16529
+ delete(key) {
16530
+ return this.cache.delete(key);
16531
+ }
16532
+ /**
16533
+ * 清空缓存
16534
+ */
16535
+ clear() {
16536
+ this.cache.clear();
16537
+ }
16538
+ /**
16539
+ * 获取当前缓存大小
16540
+ * @returns 当前缓存的元素数量
16541
+ */
16542
+ get size() {
16543
+ return this.cache.size;
16544
+ }
16545
+ /**
16546
+ * 获取缓存容量
16547
+ * @returns 缓存容量上限
16548
+ */
16549
+ get maxSize() {
16550
+ return this.capacity;
16551
+ }
16552
+ /**
16553
+ * 获取所有 key
16554
+ * @returns key 数组
16555
+ */
16556
+ keys() {
16557
+ return Array.from(this.cache.keys());
16558
+ }
16559
+ /**
16560
+ * 获取所有 value
16561
+ * @returns value 数组
16562
+ */
16563
+ values() {
16564
+ return Array.from(this.cache.values());
16565
+ }
16566
+ /**
16567
+ * 遍历缓存
16568
+ * @param callback - 回调函数
16569
+ */
16570
+ forEach(callback) {
16571
+ this.cache.forEach((value, key) => callback(value, key));
16572
+ }
16573
+ };
16336
16574
 
16337
16575
  //#endregion
16338
16576
  //#region ../agent-provider/src/common/utils/concurrency.ts
@@ -16435,20 +16673,32 @@ var CosUploadService = class {
16435
16673
  * 上传单个文件到 COS
16436
16674
  *
16437
16675
  * @param file - 要上传的文件
16676
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
16438
16677
  * @returns 上传结果,包含访问 URL 或错误信息
16439
16678
  */
16440
- async uploadFile(file) {
16679
+ async uploadFile(file, abortSignal) {
16441
16680
  const filename = file.name;
16442
16681
  this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
16443
16682
  try {
16683
+ if (abortSignal?.aborted) return {
16684
+ success: false,
16685
+ error: "Upload cancelled",
16686
+ aborted: true
16687
+ };
16444
16688
  const objectKey = this.generateObjectKey(filename);
16445
16689
  this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
16446
16690
  const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
16447
16691
  if (!presignedItem) throw new Error("No presigned URL item returned");
16692
+ if (abortSignal?.aborted) return {
16693
+ success: false,
16694
+ error: "Upload cancelled",
16695
+ aborted: true
16696
+ };
16448
16697
  const uploadResponse = await fetch(presignedItem.upload_url, {
16449
16698
  method: "PUT",
16450
16699
  body: file,
16451
- headers: { "Content-Type": file.type || "application/octet-stream" }
16700
+ headers: { "Content-Type": file.type || "application/octet-stream" },
16701
+ signal: abortSignal
16452
16702
  });
16453
16703
  if (!uploadResponse.ok) {
16454
16704
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -16462,6 +16712,11 @@ var CosUploadService = class {
16462
16712
  objectKey
16463
16713
  };
16464
16714
  } catch (error) {
16715
+ if (error instanceof Error && error.name === "AbortError") return {
16716
+ success: false,
16717
+ error: "Upload cancelled",
16718
+ aborted: true
16719
+ };
16465
16720
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
16466
16721
  this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
16467
16722
  return {
@@ -16476,14 +16731,25 @@ var CosUploadService = class {
16476
16731
  * 使用并发控制,限制同时上传的文件数量
16477
16732
  *
16478
16733
  * @param files - 要上传的文件数组
16734
+ * @param abortSignal - 可选的 AbortSignal,用于取消上传
16479
16735
  * @returns 所有文件的上传结果
16480
16736
  */
16481
- async uploadFiles(files) {
16737
+ async uploadFiles(files, abortSignal) {
16482
16738
  if (files.length === 0) return {
16483
16739
  success: true,
16484
16740
  urls: [],
16485
16741
  results: []
16486
16742
  };
16743
+ if (abortSignal?.aborted) return {
16744
+ success: false,
16745
+ error: "Upload cancelled",
16746
+ aborted: true,
16747
+ results: files.map(() => ({
16748
+ success: false,
16749
+ error: "Upload cancelled",
16750
+ aborted: true
16751
+ }))
16752
+ };
16487
16753
  this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
16488
16754
  try {
16489
16755
  const fileInfos = files.map((file) => ({
@@ -16495,6 +16761,12 @@ var CosUploadService = class {
16495
16761
  this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
16496
16762
  if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
16497
16763
  const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
16764
+ if (abortSignal?.aborted) return {
16765
+ success: false,
16766
+ error: "Upload cancelled",
16767
+ aborted: true,
16768
+ objectKey: fileInfos[index].objectKey
16769
+ };
16498
16770
  const presignedItem = presignedResponse.items[index];
16499
16771
  if (!presignedItem) return {
16500
16772
  success: false,
@@ -16505,7 +16777,8 @@ var CosUploadService = class {
16505
16777
  const uploadResponse = await fetch(presignedItem.upload_url, {
16506
16778
  method: "PUT",
16507
16779
  body: file,
16508
- headers: { "Content-Type": file.type || "application/octet-stream" }
16780
+ headers: { "Content-Type": file.type || "application/octet-stream" },
16781
+ signal: abortSignal
16509
16782
  });
16510
16783
  if (!uploadResponse.ok) {
16511
16784
  const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
@@ -16522,6 +16795,12 @@ var CosUploadService = class {
16522
16795
  objectKey: presignedItem.object_key
16523
16796
  };
16524
16797
  } catch (error) {
16798
+ if (error instanceof Error && error.name === "AbortError") return {
16799
+ success: false,
16800
+ error: "Upload cancelled",
16801
+ aborted: true,
16802
+ objectKey: presignedItem.object_key
16803
+ };
16525
16804
  return {
16526
16805
  success: false,
16527
16806
  error: error instanceof Error ? error.message : "Unknown error",
@@ -16575,35 +16854,35 @@ var CosUploadService = class {
16575
16854
  * E2B Filesystem Implementation
16576
16855
  *
16577
16856
  * Provides FilesResource implementation using E2B Sandbox SDK.
16578
- * Directly uses e2b SDK types.
16857
+ * Supports optional auto-reconnect on auth failure (401/403).
16579
16858
  *
16580
16859
  * @see https://e2b.dev/docs/filesystem/read-write
16581
16860
  * @see https://e2b.dev/docs/filesystem/watch
16582
16861
  */
16583
16862
  /**
16584
- * E2B Filesystem Implementation
16863
+ * E2B Filesystem
16585
16864
  *
16586
- * Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource interface.
16865
+ * Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource.
16866
+ * When `reconnectFn` is provided, automatically reconnects on auth errors.
16587
16867
  *
16588
16868
  * @example
16589
16869
  * ```typescript
16590
- * const fs = await E2BFilesystem.connect({
16591
- * sandboxId: 'sandbox-123',
16592
- * apiKey: 'e2b_xxx'
16593
- * });
16594
- *
16595
- * // Read/write files
16596
- * await fs.write('/test.txt', 'Hello World');
16597
- * const content = await fs.read('/test.txt');
16870
+ * // Basic usage (no auto-reconnect)
16871
+ * const fs = await E2BFilesystem.connect({ sandboxId: '...' });
16598
16872
  *
16599
- * // Watch for changes
16600
- * const handle = await fs.watchDir('/workspace', (event) => {
16601
- * console.log('File changed:', event);
16602
- * });
16873
+ * // With auto-reconnect on token expiry
16874
+ * const fs = await E2BFilesystem.connect({ sandboxId: '...' });
16875
+ * fs.setReconnectFn(async () => fetchFreshConnectionInfo());
16603
16876
  * ```
16604
16877
  */
16605
16878
  var E2BFilesystem = class E2BFilesystem {
16879
+ static {
16880
+ this.MIN_RECONNECT_INTERVAL_MS = 10 * 1e3;
16881
+ }
16606
16882
  constructor(sandbox) {
16883
+ this.isReconnecting = false;
16884
+ this.reconnectSubscribers = [];
16885
+ this.lastReconnectAt = 0;
16607
16886
  this.sandbox = sandbox;
16608
16887
  }
16609
16888
  /**
@@ -16619,38 +16898,111 @@ var E2BFilesystem = class E2BFilesystem {
16619
16898
  }));
16620
16899
  }
16621
16900
  /**
16901
+ * Set reconnect callback. When set, auth errors trigger automatic reconnect + retry.
16902
+ */
16903
+ setReconnectFn(fn) {
16904
+ this.reconnectFn = fn;
16905
+ }
16906
+ /**
16622
16907
  * Get the underlying E2B Sandbox instance
16623
16908
  */
16624
16909
  getSandbox() {
16625
16910
  return this.sandbox;
16626
16911
  }
16912
+ isAuthError(error) {
16913
+ if (!error || typeof error !== "object") return false;
16914
+ const err = error;
16915
+ if (err.status === 401 || err.status === 403) return true;
16916
+ if (err.statusCode === 401 || err.statusCode === 403) return true;
16917
+ if (err.response && typeof err.response === "object") {
16918
+ const resp = err.response;
16919
+ if (resp.status === 401 || resp.status === 403) return true;
16920
+ }
16921
+ if (typeof err.message === "string") {
16922
+ const msg = err.message.toLowerCase();
16923
+ if (msg.includes("unauthorized") || msg.includes("token expired") || msg.includes("authentication")) return true;
16924
+ }
16925
+ return false;
16926
+ }
16927
+ canAttemptReconnect() {
16928
+ return Date.now() - this.lastReconnectAt >= E2BFilesystem.MIN_RECONNECT_INTERVAL_MS;
16929
+ }
16930
+ /**
16931
+ * Reconnect with fresh credentials.
16932
+ * Only one reconnect in-flight at a time; concurrent callers share the result.
16933
+ */
16934
+ async reconnect() {
16935
+ if (this.isReconnecting) return new Promise((resolve, reject) => {
16936
+ this.reconnectSubscribers.push((success) => {
16937
+ if (success) resolve();
16938
+ else reject(/* @__PURE__ */ new Error("E2B sandbox reconnect failed"));
16939
+ });
16940
+ });
16941
+ this.isReconnecting = true;
16942
+ this.lastReconnectAt = Date.now();
16943
+ try {
16944
+ const info = await this.reconnectFn();
16945
+ this.sandbox = await e2b.Sandbox.connect(info.sandboxId, {
16946
+ domain: info.domain,
16947
+ apiUrl: info.apiUrl,
16948
+ requestTimeoutMs: info.requestTimeoutMs,
16949
+ debug: info.debug,
16950
+ headers: info.headers
16951
+ });
16952
+ this.reconnectSubscribers.forEach((cb) => cb(true));
16953
+ this.reconnectSubscribers = [];
16954
+ } catch (error) {
16955
+ this.reconnectSubscribers.forEach((cb) => cb(false));
16956
+ this.reconnectSubscribers = [];
16957
+ throw error;
16958
+ } finally {
16959
+ this.isReconnecting = false;
16960
+ }
16961
+ }
16962
+ /**
16963
+ * Execute an operation. If reconnectFn is set and an auth error occurs,
16964
+ * reconnect and retry once.
16965
+ */
16966
+ async exec(operation) {
16967
+ try {
16968
+ return await operation(this.sandbox.files);
16969
+ } catch (error) {
16970
+ if (this.reconnectFn && this.isAuthError(error) && this.canAttemptReconnect()) {
16971
+ await this.reconnect();
16972
+ return operation(this.sandbox.files);
16973
+ }
16974
+ throw error;
16975
+ }
16976
+ }
16627
16977
  read(path, opts) {
16628
- return this.sandbox.files.read(path, opts);
16978
+ return this.exec((f) => f.read(path, opts));
16629
16979
  }
16630
16980
  write(pathOrFiles, dataOrOpts, opts) {
16631
- if (Array.isArray(pathOrFiles)) return this.sandbox.files.write(pathOrFiles, dataOrOpts);
16632
- return this.sandbox.files.write(pathOrFiles, dataOrOpts, opts);
16981
+ return this.exec((f) => {
16982
+ if (Array.isArray(pathOrFiles)) return f.write(pathOrFiles, dataOrOpts);
16983
+ return f.write(pathOrFiles, dataOrOpts, opts);
16984
+ });
16633
16985
  }
16634
16986
  async list(path, opts) {
16635
- return this.sandbox.files.list(path, opts);
16987
+ return this.exec((f) => f.list(path, opts));
16636
16988
  }
16637
16989
  async exists(path, opts) {
16638
- return this.sandbox.files.exists(path, opts);
16990
+ return this.exec((f) => f.exists(path, opts));
16639
16991
  }
16640
16992
  async makeDir(path, opts) {
16641
- return this.sandbox.files.makeDir(path, opts);
16993
+ return this.exec((f) => f.makeDir(path, opts));
16642
16994
  }
16643
16995
  async remove(path, opts) {
16644
- return this.sandbox.files.remove(path, opts);
16996
+ return this.exec((f) => f.remove(path, opts));
16645
16997
  }
16646
16998
  async rename(oldPath, newPath, opts) {
16647
- return this.sandbox.files.rename(oldPath, newPath, opts);
16999
+ return this.exec((f) => f.rename(oldPath, newPath, opts));
16648
17000
  }
16649
17001
  async getInfo(path, opts) {
16650
- return this.sandbox.files.getInfo(path, opts);
17002
+ return this.exec((f) => f.getInfo(path, opts));
16651
17003
  }
16652
17004
  async watchDir(path, onEvent, opts) {
16653
- return this.sandbox.files.watchDir(path, onEvent, opts);
17005
+ return this.exec((f) => f.watchDir(path, onEvent, opts));
16654
17006
  }
16655
17007
  };
16656
17008
 
@@ -16802,8 +17154,11 @@ var CloudAgentProvider = class CloudAgentProvider {
16802
17154
  this.filesystemCache = /* @__PURE__ */ new Map();
16803
17155
  this.connectionCache = /* @__PURE__ */ new Map();
16804
17156
  this.eventListeners = /* @__PURE__ */ new Map();
17157
+ this.productConfigCache = null;
16805
17158
  this.options = options;
16806
17159
  this.logger = options.logger;
17160
+ this.marketplaceCache = new LRUCache(200);
17161
+ this.pluginCache = new LRUCache(2e3);
16807
17162
  if (options.endpoint) httpService.setBaseURL(options.endpoint);
16808
17163
  if (options.authToken) httpService.setAuthToken(options.authToken);
16809
17164
  if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
@@ -16854,7 +17209,14 @@ var CloudAgentProvider = class CloudAgentProvider {
16854
17209
  const cached = this.filesystemCache.get(agentId);
16855
17210
  if (cached) return cached;
16856
17211
  const info = await this.getSandboxInfo(agentId);
16857
- const filesystem = createAgentFilesystem(await E2BFilesystem.connect(info));
17212
+ const e2bFilesystem = await E2BFilesystem.connect(info);
17213
+ e2bFilesystem.setReconnectFn(async () => {
17214
+ this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
17215
+ const newInfo = await this.getSandboxInfo(agentId);
17216
+ this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
17217
+ return newInfo;
17218
+ });
17219
+ const filesystem = createAgentFilesystem(e2bFilesystem);
16858
17220
  this.filesystemCache.set(agentId, filesystem);
16859
17221
  this.logger?.debug(`Created filesystem for agent: ${agentId}`);
16860
17222
  return filesystem;
@@ -16922,15 +17284,9 @@ var CloudAgentProvider = class CloudAgentProvider {
16922
17284
  const url = this.buildGetUrl("/console/as/conversations/", params);
16923
17285
  const apiResponse = await httpService.get(url);
16924
17286
  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
17287
  return {
16932
- agents,
16933
- pagination
17288
+ agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
17289
+ pagination: apiResponse.data.pagination
16934
17290
  };
16935
17291
  } catch (error) {
16936
17292
  this.logger?.error("Failed to list agents:", error);
@@ -16940,13 +17296,21 @@ var CloudAgentProvider = class CloudAgentProvider {
16940
17296
  /**
16941
17297
  * Create a new conversation
16942
17298
  * POST {endpoint}/console/as/conversations
17299
+ * @param params - Session params containing cwd and optional configuration
16943
17300
  */
16944
- async create() {
17301
+ async create(params) {
16945
17302
  try {
16946
- const apiResponse = await httpService.post("/console/as/conversations/", {
16947
- prompt: "",
16948
- model: "deepseek-r1"
16949
- });
17303
+ const { options = {} } = params;
17304
+ const codebuddyMeta = options._meta?.["codebuddy.ai"];
17305
+ const tagsObj = options.tags || codebuddyMeta?.tags;
17306
+ const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
17307
+ const createPayload = {
17308
+ prompt: (options.prompt || "").slice(0, 100),
17309
+ model: options.model || "deepseek-r1",
17310
+ ...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
17311
+ };
17312
+ console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
17313
+ const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
16950
17314
  if (!apiResponse.data) throw new Error("No data in API response");
16951
17315
  this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
16952
17316
  return apiResponse.data.id;
@@ -16996,7 +17360,14 @@ var CloudAgentProvider = class CloudAgentProvider {
16996
17360
  } catch (error) {
16997
17361
  this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
16998
17362
  }
16999
- if (this.connectionCache.get(endpoint)) this.connectionCache.delete(endpoint);
17363
+ const existingConnection = this.connectionCache.get(endpoint);
17364
+ if (existingConnection) {
17365
+ this.connectionCache.delete(endpoint);
17366
+ existingConnection.removeAllListeners();
17367
+ existingConnection.disconnect().catch((err) => {
17368
+ this.logger?.debug("Failed to disconnect old connection:", err);
17369
+ });
17370
+ }
17000
17371
  const clientCapabilities = {
17001
17372
  ...this.options.clientCapabilities,
17002
17373
  _meta: {
@@ -17116,6 +17487,35 @@ var CloudAgentProvider = class CloudAgentProvider {
17116
17487
  }
17117
17488
  }
17118
17489
  /**
17490
+ * Update conversation status by ID
17491
+ * POST {endpoint}/console/as/conversations/{agentId}
17492
+ *
17493
+ * @param agentId - Conversation ID to update
17494
+ * @param status - New status for the conversation
17495
+ * @returns PatchConversationResponse containing the updated conversation ID
17496
+ *
17497
+ * @example
17498
+ * ```typescript
17499
+ * const result = await provider.updateStatus('agent-123', 'completed');
17500
+ * console.log('Updated conversation status:', result.id);
17501
+ * ```
17502
+ */
17503
+ async updateStatus(agentId, status) {
17504
+ try {
17505
+ const body = { status };
17506
+ const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
17507
+ if (!apiResponse.data) {
17508
+ this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
17509
+ return { id: agentId };
17510
+ }
17511
+ this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
17512
+ return apiResponse.data;
17513
+ } catch (error) {
17514
+ this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
17515
+ throw error;
17516
+ }
17517
+ }
17518
+ /**
17119
17519
  * Get available models from product configuration
17120
17520
  *
17121
17521
  * GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
@@ -17146,9 +17546,13 @@ var CloudAgentProvider = class CloudAgentProvider {
17146
17546
  this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
17147
17547
  return [];
17148
17548
  }
17149
- const models = apiResponse.data.models ?? [];
17150
- this.logger?.info(`[CloudAgentProvider] Retrieved ${models.length} models from /console/enterprises API`);
17151
- return models.map((model) => ({
17549
+ this.productConfigCache = apiResponse.data;
17550
+ const productConfig = apiResponse.data;
17551
+ const allModels = productConfig.models ?? [];
17552
+ const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
17553
+ const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
17554
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
17555
+ return filteredModels.map((model) => ({
17152
17556
  id: model.id,
17153
17557
  name: model.name ?? model.id,
17154
17558
  description: model.description,
@@ -17170,6 +17574,43 @@ var CloudAgentProvider = class CloudAgentProvider {
17170
17574
  }
17171
17575
  }
17172
17576
  /**
17577
+ * 获取产品部署类型(从缓存)
17578
+ * 需要先调用 getModels() 初始化缓存
17579
+ *
17580
+ * @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
17581
+ */
17582
+ getDeploymentType() {
17583
+ return this.productConfigCache?.deploymentType ?? "SaaS";
17584
+ }
17585
+ /**
17586
+ * 获取 Credit 购买引导配置(从缓存)
17587
+ * 需要先调用 getModels() 初始化缓存
17588
+ *
17589
+ * @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
17590
+ */
17591
+ getCreditPurchaseActions() {
17592
+ return this.productConfigCache?.config?.creditPurchaseActions ?? {
17593
+ "14018": {
17594
+ labelZh: "获取 Credits",
17595
+ labelEn: "Get credits",
17596
+ url: "https://www.codebuddy.cn/profile/plan",
17597
+ showButton: true
17598
+ },
17599
+ "6004": {
17600
+ labelZh: "升级专业版",
17601
+ labelEn: "Upgrade to Pro",
17602
+ url: "https://www.codebuddy.cn/profile/plan",
17603
+ showButton: true
17604
+ },
17605
+ "6005": {
17606
+ labelZh: "升级专业版",
17607
+ labelEn: "Upgrade to Pro",
17608
+ url: "https://www.codebuddy.cn/profile/plan",
17609
+ showButton: true
17610
+ }
17611
+ };
17612
+ }
17613
+ /**
17173
17614
  * Generate a unique request ID
17174
17615
  */
17175
17616
  generateRequestId() {
@@ -17252,7 +17693,7 @@ var CloudAgentProvider = class CloudAgentProvider {
17252
17693
  /**
17253
17694
  * Upload files to cloud storage via COS presigned URL
17254
17695
  *
17255
- * @param params - files array (File objects in browser)
17696
+ * @param params - files array (File objects in browser), optional abortSignal
17256
17697
  * @returns Response with corresponding cloud URLs
17257
17698
  */
17258
17699
  async uploadFile(params) {
@@ -17262,12 +17703,13 @@ var CloudAgentProvider = class CloudAgentProvider {
17262
17703
  success: false,
17263
17704
  error: "No valid File objects provided"
17264
17705
  };
17265
- const result = await this.cosUploadService.uploadFiles(files);
17706
+ const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
17266
17707
  return {
17267
17708
  success: result.success,
17268
17709
  urls: result.urls,
17269
17710
  expireSeconds: result.expireSeconds,
17270
- error: result.error
17711
+ error: result.error,
17712
+ aborted: result.aborted
17271
17713
  };
17272
17714
  }
17273
17715
  /**
@@ -17309,20 +17751,22 @@ var CloudAgentProvider = class CloudAgentProvider {
17309
17751
  }
17310
17752
  /**
17311
17753
  * 获取支持的场景列表
17312
- * API 端点: GET /console/as/support/scenes
17754
+ * API 端点: GET /v2/as/support/scenes (不鉴权)
17313
17755
  * 用于 Welcome 页面的 QuickActions 快捷操作
17314
17756
  *
17757
+ * @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
17315
17758
  * @returns Promise<SupportScene[]> 支持的场景列表
17316
17759
  */
17317
- async getSupportScenes() {
17760
+ async getSupportScenes(locale) {
17318
17761
  try {
17319
- const apiResponse = await httpService.get("/console/as/support/scenes");
17762
+ const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
17763
+ const apiResponse = await httpService.get(url);
17320
17764
  if (!apiResponse.data) {
17321
17765
  this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
17322
17766
  return [];
17323
17767
  }
17324
17768
  const scenes = apiResponse.data.scenes || [];
17325
- this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
17769
+ this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
17326
17770
  return scenes;
17327
17771
  } catch (error) {
17328
17772
  this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
@@ -17338,7 +17782,8 @@ var CloudAgentProvider = class CloudAgentProvider {
17338
17782
  type: "cloud",
17339
17783
  status,
17340
17784
  createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
17341
- capabilities: this.options.clientCapabilities
17785
+ capabilities: this.options.clientCapabilities,
17786
+ isUserDefinedTitle: data.isUserDefinedTitle
17342
17787
  };
17343
17788
  }
17344
17789
  /**
@@ -17354,57 +17799,500 @@ var CloudAgentProvider = class CloudAgentProvider {
17354
17799
  const queryString = searchParams.toString();
17355
17800
  return queryString ? `${path}?${queryString}` : path;
17356
17801
  }
17357
- };
17358
-
17359
- //#endregion
17360
- //#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
17361
- /**
17362
- * Local Agent Connection
17363
- * Wraps AcpJsonRpcClient to implement AgentConnection interface
17364
- *
17365
- * Uses IWidgetChannel for IPC communication with ExtensionHost
17366
- * Migrated from ipc-agent-provider for unified local agent access
17367
- */
17368
-
17369
- //#endregion
17370
- //#region ../agent-provider/src/common/client/session.ts
17371
- /**
17372
- * ActiveSessionImpl - Implements the ActiveSession interface
17373
- *
17374
- * This class wraps an AgentConnection and provides the session-centric API.
17375
- * It is created by SessionManager when creating or loading sessions.
17376
- *
17377
- * @example
17378
- * ```typescript
17379
- * // Created by client.sessions.new() or client.sessions.load()
17380
- * const session = await client.sessions.new({ cwd: '/workspace' });
17381
- *
17382
- * // Access agent state
17383
- * console.log(session.agentState.status);
17384
- *
17385
- * // Send prompt
17386
- * const response = await session.prompts.send({ content: 'Hello!' });
17387
- *
17388
- * // Cleanup
17389
- * session.disconnect();
17390
- * ```
17391
- */
17392
- var ActiveSessionImpl = class {
17393
17802
  /**
17394
- * Create an ActiveSessionImpl instance
17395
- *
17396
- * @param sessionId - Session ID
17397
- * @param agentId - Agent ID
17398
- * @param connection - Already connected AgentConnection
17399
- * @param options - Additional options
17803
+ * 获取已安装插件列表
17804
+ * GET /console/as/user/plugins/installed
17400
17805
  */
17401
- constructor(sessionId, agentId, connection, options = {}) {
17402
- this._availableCommands = [];
17403
- this.listeners = /* @__PURE__ */ new Map();
17404
- this.onceListeners = /* @__PURE__ */ new Map();
17405
- this._id = sessionId;
17406
- this._agentId = agentId;
17407
- this.connection = connection;
17806
+ async getInstalledPlugins(forceRefresh) {
17807
+ try {
17808
+ const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
17809
+ name: p.plugin_name,
17810
+ marketplaceName: p.marketplace_name,
17811
+ status: p.enabled ? "enabled" : "disabled",
17812
+ description: p.description,
17813
+ version: p.version,
17814
+ installScope: p.scope === "local" ? "project" : p.scope,
17815
+ installedScopes: [p.scope],
17816
+ installId: p.id
17817
+ }));
17818
+ result.forEach((plugin) => {
17819
+ this.pluginCache.set(plugin.name, plugin);
17820
+ });
17821
+ return result;
17822
+ } catch (error) {
17823
+ this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
17824
+ throw error;
17825
+ }
17826
+ }
17827
+ /**
17828
+ * 安装插件
17829
+ * POST /console/as/user/plugins/install
17830
+ *
17831
+ * @param pluginNames - 插件名称数组
17832
+ * @param marketplaceNameOrId - 市场名称或 ID
17833
+ */
17834
+ async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
17835
+ try {
17836
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
17837
+ if (!marketplaceId) return {
17838
+ success: false,
17839
+ error: `Marketplace not found: ${marketplaceNameOrId}`
17840
+ };
17841
+ const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
17842
+ plugin_name: pluginName,
17843
+ marketplace_id: marketplaceId,
17844
+ version: "latest"
17845
+ })))).filter((r) => r.status === "rejected");
17846
+ if (failed.length > 0) {
17847
+ const errors = failed.map((r) => r.reason?.message || "Unknown error");
17848
+ return {
17849
+ success: false,
17850
+ error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
17851
+ };
17852
+ }
17853
+ return { success: true };
17854
+ } catch (error) {
17855
+ return {
17856
+ success: false,
17857
+ error: this.extractErrorMessage(error)
17858
+ };
17859
+ }
17860
+ }
17861
+ /**
17862
+ * 卸载插件
17863
+ * POST /console/as/user/plugins/installed/:id/uninstall
17864
+ *
17865
+ * 完整链路:
17866
+ * CloudAgentProvider.uninstallPlugin()
17867
+ * -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
17868
+ * -> agentserver: PluginInstallService.Uninstall()
17869
+ * -> 软删除 DB + 异步同步到活跃沙箱
17870
+ *
17871
+ * @param pluginName - 插件名称
17872
+ * @param marketplaceName - 市场名称(用于标识唯一插件)
17873
+ * @param scope - 卸载范围 ('user' | 'project' | 'project-local')
17874
+ */
17875
+ async uninstallPlugin(pluginName, marketplaceName, scope) {
17876
+ try {
17877
+ const installId = await this.findPluginInstallId(pluginName);
17878
+ if (!installId) return {
17879
+ success: false,
17880
+ error: `Plugin not found or not installed: ${pluginName}`
17881
+ };
17882
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
17883
+ return { success: true };
17884
+ } catch (error) {
17885
+ return {
17886
+ success: false,
17887
+ error: this.extractErrorMessage(error)
17888
+ };
17889
+ }
17890
+ }
17891
+ /**
17892
+ * 获取插件市场列表
17893
+ * GET /console/as/marketplace/sources
17894
+ */
17895
+ async getPluginMarketplaces(forceRefresh) {
17896
+ try {
17897
+ const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
17898
+ id: src.id,
17899
+ name: src.name,
17900
+ type: this.mapSourceType(src.source_type),
17901
+ source: { url: src.url },
17902
+ description: src.name,
17903
+ isBuiltin: src.is_default
17904
+ }));
17905
+ result.forEach((m) => {
17906
+ const marketplaceInfo = {
17907
+ id: m.id,
17908
+ name: m.name
17909
+ };
17910
+ this.marketplaceCache.set(m.name, marketplaceInfo);
17911
+ this.marketplaceCache.set(m.id, marketplaceInfo);
17912
+ });
17913
+ return result;
17914
+ } catch (error) {
17915
+ this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
17916
+ throw error;
17917
+ }
17918
+ }
17919
+ /**
17920
+ * 获取市场下的插件列表
17921
+ * - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
17922
+ * - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
17923
+ *
17924
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
17925
+ * @param forceRefresh - 是否强制刷新
17926
+ * @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
17927
+ */
17928
+ async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
17929
+ try {
17930
+ if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
17931
+ q: searchText,
17932
+ page: 1,
17933
+ page_size: 100
17934
+ } })).data?.plugins || []).map((p) => this.mapPluginData(p));
17935
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
17936
+ if (!sourceId) {
17937
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
17938
+ return [];
17939
+ }
17940
+ const params = {
17941
+ source_id: sourceId,
17942
+ page: 1,
17943
+ page_size: 100
17944
+ };
17945
+ return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
17946
+ } catch (error) {
17947
+ this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
17948
+ throw error;
17949
+ }
17950
+ }
17951
+ /**
17952
+ * 获取插件详情
17953
+ * GET /console/as/marketplace/plugins/:name/detail
17954
+ *
17955
+ * @param pluginName - 插件名称
17956
+ * @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
17957
+ */
17958
+ async getPluginDetail(pluginName, marketplaceNameOrId) {
17959
+ try {
17960
+ const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
17961
+ if (!sourceId) {
17962
+ this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
17963
+ return null;
17964
+ }
17965
+ const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
17966
+ if (!p) return null;
17967
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
17968
+ const tags = p.tags ? JSON.parse(p.tags) : [];
17969
+ return {
17970
+ name: p.name,
17971
+ marketplaceName: p.marketplace_name,
17972
+ description: p.description,
17973
+ version: p.version,
17974
+ iconUrl: p.icon_url,
17975
+ tags,
17976
+ installed: p.installed,
17977
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
17978
+ readme: p.readme,
17979
+ author: p.author,
17980
+ homepage: p.homepage,
17981
+ repositoryUrl: p.repository_url,
17982
+ license: p.license,
17983
+ ...capabilities
17984
+ };
17985
+ } catch (error) {
17986
+ this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
17987
+ throw error;
17988
+ }
17989
+ }
17990
+ /**
17991
+ * 添加插件市场
17992
+ * POST /console/as/marketplace/sources
17993
+ */
17994
+ async addPluginMarketplace(sourceUrl, name) {
17995
+ try {
17996
+ const body = {
17997
+ source_type: sourceUrl.startsWith("http") ? "url" : "github",
17998
+ url: sourceUrl,
17999
+ name: name || sourceUrl
18000
+ };
18001
+ const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
18002
+ if (!sourceData) throw new Error("Invalid response from server");
18003
+ const marketplaceInfo = {
18004
+ id: sourceData.id,
18005
+ name: sourceData.name
18006
+ };
18007
+ this.marketplaceCache.set(sourceData.name, marketplaceInfo);
18008
+ this.marketplaceCache.set(sourceData.id, marketplaceInfo);
18009
+ return {
18010
+ success: true,
18011
+ marketplace: {
18012
+ id: sourceData.id,
18013
+ name: sourceData.name,
18014
+ type: this.mapSourceType(sourceData.source_type),
18015
+ source: { url: sourceData.url },
18016
+ isBuiltin: sourceData.is_default
18017
+ }
18018
+ };
18019
+ } catch (error) {
18020
+ return {
18021
+ success: false,
18022
+ error: this.extractErrorMessage(error)
18023
+ };
18024
+ }
18025
+ }
18026
+ /**
18027
+ * 删除插件市场
18028
+ * POST /console/as/marketplace/sources/:id/delete
18029
+ */
18030
+ async removePluginMarketplace(marketplaceNameOrId) {
18031
+ try {
18032
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
18033
+ if (!marketplaceId) return {
18034
+ success: false,
18035
+ error: `Marketplace not found: ${marketplaceNameOrId}`
18036
+ };
18037
+ await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
18038
+ return { success: true };
18039
+ } catch (error) {
18040
+ return {
18041
+ success: false,
18042
+ error: this.extractErrorMessage(error)
18043
+ };
18044
+ }
18045
+ }
18046
+ /**
18047
+ * 刷新插件市场
18048
+ * POST /console/as/marketplace/sources/:id/check-updates
18049
+ */
18050
+ async refreshPluginMarketplace(marketplaceNameOrId) {
18051
+ try {
18052
+ const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
18053
+ if (!marketplaceId) return {
18054
+ success: false,
18055
+ error: `Marketplace not found: ${marketplaceNameOrId}`
18056
+ };
18057
+ return {
18058
+ success: true,
18059
+ plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
18060
+ };
18061
+ } catch (error) {
18062
+ return {
18063
+ success: false,
18064
+ error: this.extractErrorMessage(error)
18065
+ };
18066
+ }
18067
+ }
18068
+ /**
18069
+ * 批量切换插件启用/禁用状态
18070
+ * POST /console/as/user/plugins/installed/:id/toggle
18071
+ */
18072
+ async batchTogglePlugins(request) {
18073
+ try {
18074
+ const results = await Promise.allSettled(request.items.map(async (item) => {
18075
+ const installId = await this.findPluginInstallId(item.pluginName);
18076
+ if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
18077
+ const enabled = item.operation === "enable";
18078
+ await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
18079
+ return item;
18080
+ }));
18081
+ const succeededPlugins = [];
18082
+ const failedPlugins = [];
18083
+ results.forEach((r, i) => {
18084
+ const item = request.items[i];
18085
+ if (r.status === "fulfilled") succeededPlugins.push(item);
18086
+ else failedPlugins.push({
18087
+ ...item,
18088
+ error: r.reason?.message || "Unknown error"
18089
+ });
18090
+ });
18091
+ return {
18092
+ success: failedPlugins.length === 0,
18093
+ succeededPlugins,
18094
+ failedPlugins
18095
+ };
18096
+ } catch (error) {
18097
+ return {
18098
+ success: false,
18099
+ succeededPlugins: [],
18100
+ failedPlugins: request.items.map((item) => ({
18101
+ ...item,
18102
+ error: this.extractErrorMessage(error)
18103
+ }))
18104
+ };
18105
+ }
18106
+ }
18107
+ /**
18108
+ * 将后端插件数据映射为前端格式
18109
+ */
18110
+ mapPluginData(p) {
18111
+ const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
18112
+ let tags = [];
18113
+ if (p.tags) {
18114
+ if (Array.isArray(p.tags)) tags = p.tags;
18115
+ else if (typeof p.tags === "string") try {
18116
+ const parsed = JSON.parse(p.tags);
18117
+ tags = Array.isArray(parsed) ? parsed : [parsed];
18118
+ } catch {
18119
+ tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
18120
+ }
18121
+ }
18122
+ return {
18123
+ name: p.name,
18124
+ marketplaceName: p.marketplace_name,
18125
+ description: p.description,
18126
+ version: p.version,
18127
+ iconUrl: p.icon_url,
18128
+ tags,
18129
+ installed: p.installed,
18130
+ status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
18131
+ installedScopes: p.installed ? [p.installed_scope] : [],
18132
+ ...capabilities
18133
+ };
18134
+ }
18135
+ /**
18136
+ * 从缓存中查找插件的 install_id
18137
+ * 如果缓存未命中,则调用 API 获取并缓存
18138
+ *
18139
+ * @param pluginName - 插件名称
18140
+ * @returns install_id 或 null
18141
+ */
18142
+ async findPluginInstallId(pluginName) {
18143
+ const cached = this.pluginCache.get(pluginName);
18144
+ if (cached) return cached.installId;
18145
+ await this.getInstalledPlugins();
18146
+ return this.pluginCache.get(pluginName)?.installId || null;
18147
+ }
18148
+ /**
18149
+ * 从缓存中查找 marketplace ID
18150
+ * 如果缓存未命中,则调用 API 获取并缓存
18151
+ */
18152
+ async findMarketplaceId(nameOrId) {
18153
+ const cached = this.marketplaceCache.get(nameOrId);
18154
+ if (cached) return cached.id;
18155
+ await this.getPluginMarketplaces();
18156
+ return this.marketplaceCache.get(nameOrId)?.id || null;
18157
+ }
18158
+ /**
18159
+ * 提取 API 错误信息
18160
+ * 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
18161
+ */
18162
+ extractErrorMessage(error) {
18163
+ if (error instanceof AxiosError) {
18164
+ const status = error.response?.status;
18165
+ const apiResponse = error.response?.data;
18166
+ const parts = [];
18167
+ if (status) parts.push(`HTTP ${status}`);
18168
+ if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
18169
+ if (apiResponse?.msg) parts.push(apiResponse.msg);
18170
+ else if (error.message) parts.push(error.message);
18171
+ const errorMessage = parts.join(" - ");
18172
+ this.logger?.error("[CloudAgentProvider] API Error:", {
18173
+ status,
18174
+ code: apiResponse?.code,
18175
+ msg: apiResponse?.msg,
18176
+ requestId: apiResponse?.requestId,
18177
+ url: error.config?.url,
18178
+ method: error.config?.method
18179
+ });
18180
+ return errorMessage;
18181
+ }
18182
+ if (error instanceof Error) return error.message;
18183
+ return "Unknown error";
18184
+ }
18185
+ /**
18186
+ * 映射后端 source_type 到前端类型
18187
+ */
18188
+ mapSourceType(sourceType) {
18189
+ switch (sourceType) {
18190
+ case "github": return "github";
18191
+ case "official": return "custom";
18192
+ default: return "custom";
18193
+ }
18194
+ }
18195
+ /**
18196
+ * 解析 capabilities JSON 字符串
18197
+ */
18198
+ parseCapabilities(capabilitiesStr) {
18199
+ try {
18200
+ const cap = JSON.parse(capabilitiesStr);
18201
+ return {
18202
+ commands: cap.commands,
18203
+ skills: cap.skills,
18204
+ mcpServers: cap.mcp,
18205
+ agents: cap.agents,
18206
+ hooks: cap.hooks,
18207
+ rules: cap.rules
18208
+ };
18209
+ } catch {
18210
+ return {};
18211
+ }
18212
+ }
18213
+ /**
18214
+ * 上报 telemetry 事件(Cloud 模式)
18215
+ * 通过 HTTP POST 发送到 /v2/report
18216
+ * 注入用户信息和浏览器环境等公共字段
18217
+ */
18218
+ async reportTelemetry(eventName, payload) {
18219
+ try {
18220
+ const account = accountService.getAccount();
18221
+ const commonFields = {};
18222
+ if (account) {
18223
+ commonFields.userId = account.uid;
18224
+ commonFields.userNickname = account.nickname;
18225
+ if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
18226
+ if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
18227
+ }
18228
+ if (typeof navigator !== "undefined") {
18229
+ commonFields.userAgent = navigator.userAgent;
18230
+ commonFields.os = navigator.platform;
18231
+ }
18232
+ const events = [{
18233
+ eventCode: eventName,
18234
+ timestamp: Date.now(),
18235
+ reportDelay: 0,
18236
+ ...commonFields,
18237
+ ...payload
18238
+ }];
18239
+ await httpService.post("/v2/report", events);
18240
+ } catch (error) {
18241
+ this.logger?.warn("reportTelemetry() failed:", error);
18242
+ }
18243
+ }
18244
+ };
18245
+
18246
+ //#endregion
18247
+ //#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
18248
+ /**
18249
+ * Local Agent Connection
18250
+ * Wraps AcpJsonRpcClient to implement AgentConnection interface
18251
+ *
18252
+ * Uses IWidgetChannel for IPC communication with ExtensionHost
18253
+ * Migrated from ipc-agent-provider for unified local agent access
18254
+ */
18255
+
18256
+ //#endregion
18257
+ //#region ../agent-provider/src/common/client/session.ts
18258
+ /**
18259
+ * ActiveSessionImpl - Implements the ActiveSession interface
18260
+ *
18261
+ * This class wraps an AgentConnection and provides the session-centric API.
18262
+ * It is created by SessionManager when creating or loading sessions.
18263
+ *
18264
+ * @example
18265
+ * ```typescript
18266
+ * // Created by client.sessions.new() or client.sessions.load()
18267
+ * const session = await client.sessions.new({ cwd: '/workspace' });
18268
+ *
18269
+ * // Access agent state
18270
+ * console.log(session.agentState.status);
18271
+ *
18272
+ * // Send prompt
18273
+ * const response = await session.prompts.send({ content: 'Hello!' });
18274
+ *
18275
+ * // Cleanup
18276
+ * session.disconnect();
18277
+ * ```
18278
+ */
18279
+ var ActiveSessionImpl = class {
18280
+ /**
18281
+ * Create an ActiveSessionImpl instance
18282
+ *
18283
+ * @param sessionId - Session ID
18284
+ * @param agentId - Agent ID
18285
+ * @param connection - Already connected AgentConnection
18286
+ * @param options - Additional options
18287
+ */
18288
+ constructor(sessionId, agentId, connection, options = {}) {
18289
+ this._availableCommands = [];
18290
+ this.listeners = /* @__PURE__ */ new Map();
18291
+ this.onceListeners = /* @__PURE__ */ new Map();
18292
+ this.connectionListeners = [];
18293
+ this._id = sessionId;
18294
+ this._agentId = agentId;
18295
+ this.connection = connection;
17408
18296
  this.logger = options.logger;
17409
18297
  this._getFilesystem = options.getFilesystem;
17410
18298
  this._connectionInfo = options.connectionInfo;
@@ -17427,6 +18315,18 @@ var ActiveSessionImpl = class {
17427
18315
  return this._agentId;
17428
18316
  }
17429
18317
  /**
18318
+ * Actual workspace path (set from newSession response _meta)
18319
+ */
18320
+ get cwd() {
18321
+ return this._cwd;
18322
+ }
18323
+ /**
18324
+ * Set actual workspace path (called by SessionManager after createSession)
18325
+ */
18326
+ setCwd(cwd) {
18327
+ this._cwd = cwd;
18328
+ }
18329
+ /**
17430
18330
  * Agent state (live connection state)
17431
18331
  * Returns LocalAgentState or CloudAgentState based on transport type
17432
18332
  */
@@ -17643,8 +18543,8 @@ var ActiveSessionImpl = class {
17643
18543
  * await session.setMode('architect');
17644
18544
  * ```
17645
18545
  */
17646
- async setMode(modeId) {
17647
- if (this._availableModes) {
18546
+ async setMode(modeId, skipAvailableChecker) {
18547
+ if (this._availableModes && !skipAvailableChecker) {
17648
18548
  if (!this._availableModes.some((m) => m.id === modeId)) {
17649
18549
  const availableIds = this._availableModes.map((m) => m.id).join(", ");
17650
18550
  throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
@@ -17667,6 +18567,7 @@ var ActiveSessionImpl = class {
17667
18567
  * ```
17668
18568
  */
17669
18569
  async setSessionModel(modelId) {
18570
+ this._currentModelId = modelId;
17670
18571
  await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
17671
18572
  }
17672
18573
  /**
@@ -17744,11 +18645,23 @@ var ActiveSessionImpl = class {
17744
18645
  * Disconnect from the session/agent
17745
18646
  */
17746
18647
  disconnect() {
18648
+ this.removeConnectionListeners();
17747
18649
  this.connection.disconnect();
17748
18650
  this.removeAllListeners();
17749
18651
  this.logger?.info(`Session ${this._id}: Disconnected`);
17750
18652
  }
17751
18653
  /**
18654
+ * Detach the session from connection events without disconnecting the connection.
18655
+ * This should be called when the session is being replaced but the connection is shared.
18656
+ * Unlike disconnect(), this only removes event listeners without closing the connection.
18657
+ */
18658
+ detach() {
18659
+ this.logger?.info(`Session ${this._id}: Detaching from connection events`);
18660
+ this.removeConnectionListeners();
18661
+ this.removeAllListeners();
18662
+ this.logger?.info(`Session ${this._id}: Detached successfully`);
18663
+ }
18664
+ /**
17752
18665
  * Symbol.dispose for 'using' keyword support
17753
18666
  * Automatically disconnects and cleans up when session goes out of scope
17754
18667
  *
@@ -17767,60 +18680,85 @@ var ActiveSessionImpl = class {
17767
18680
  if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
17768
18681
  return this.connection;
17769
18682
  }
18683
+ /**
18684
+ * 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
18685
+ */
18686
+ addConnectionListener(connection, event, listener) {
18687
+ connection.on(event, listener);
18688
+ this.connectionListeners.push({
18689
+ event,
18690
+ listener
18691
+ });
18692
+ }
18693
+ /**
18694
+ * 从 connection 上移除所有本 session 注册的 listener
18695
+ */
18696
+ removeConnectionListeners() {
18697
+ for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
18698
+ this.connectionListeners = [];
18699
+ }
17770
18700
  setupConnectionEvents(connection) {
17771
- connection.on("connected", () => {
18701
+ this.addConnectionListener(connection, "connected", () => {
17772
18702
  this.emit("connected", void 0);
17773
18703
  });
17774
- connection.on("disconnected", () => {
18704
+ this.addConnectionListener(connection, "disconnected", () => {
17775
18705
  this.emit("disconnected", void 0);
17776
18706
  });
17777
- connection.on("error", (error) => {
18707
+ this.addConnectionListener(connection, "error", (error) => {
17778
18708
  this.emit("error", error);
17779
18709
  });
17780
- connection.on("sessionUpdate", (update) => {
18710
+ this.addConnectionListener(connection, "sessionUpdate", (update) => {
18711
+ const notificationSessionId = update?.sessionId;
18712
+ if (notificationSessionId && notificationSessionId !== this._id) {
18713
+ console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
18714
+ return;
18715
+ }
17781
18716
  this.emit("sessionUpdate", update);
17782
18717
  });
17783
- connection.on("artifactCreated", (artifact) => {
17784
- console.log("[Session] Forwarding artifactCreated:", {
17785
- artifactUri: artifact.uri,
17786
- artifactType: artifact.type
17787
- });
18718
+ this.addConnectionListener(connection, "artifactCreated", (artifact) => {
18719
+ if (!this.shouldForwardArtifact(artifact)) return;
17788
18720
  this.emit("artifactCreated", artifact);
17789
18721
  });
17790
- connection.on("artifactUpdated", (artifact) => {
17791
- console.log("[Session] Forwarding artifactUpdated:", {
17792
- artifactUri: artifact.uri,
17793
- artifactType: artifact.type
17794
- });
18722
+ this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
18723
+ if (!this.shouldForwardArtifact(artifact)) return;
17795
18724
  this.emit("artifactUpdated", artifact);
17796
18725
  });
17797
- connection.on("artifactDeleted", (artifact) => {
17798
- console.log("[Session] Forwarding artifactDeleted:", { artifactUri: artifact.uri });
18726
+ this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
18727
+ if (!this.shouldForwardArtifact(artifact)) return;
17799
18728
  this.emit("artifactDeleted", artifact);
17800
18729
  });
17801
- connection.on("permissionRequest", (request) => {
18730
+ this.addConnectionListener(connection, "permissionRequest", (request) => {
17802
18731
  this.emit("permissionRequest", request);
17803
18732
  });
17804
- connection.on("questionRequest", (request) => {
18733
+ this.addConnectionListener(connection, "questionRequest", (request) => {
17805
18734
  this.emit("questionRequest", request);
17806
18735
  });
17807
- connection.on("questionCancelled", () => {
18736
+ this.addConnectionListener(connection, "questionCancelled", () => {
17808
18737
  this.prompts.cancel();
17809
18738
  });
17810
- connection.on("usageUpdate", (usage) => {
18739
+ this.addConnectionListener(connection, "usageUpdate", (usage) => {
17811
18740
  this.emit("usageUpdate", usage);
17812
18741
  });
17813
- connection.on("checkpointCreated", (checkpoint) => {
18742
+ this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
18743
+ const originSessionId = checkpoint.__sessionId;
18744
+ if (originSessionId && originSessionId !== this._id) return;
17814
18745
  this.emit("checkpointCreated", checkpoint);
17815
18746
  });
17816
- connection.on("checkpointUpdated", (checkpoint) => {
18747
+ this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
18748
+ const originSessionId = checkpoint.__sessionId;
18749
+ if (originSessionId && originSessionId !== this._id) return;
17817
18750
  this.emit("checkpointUpdated", checkpoint);
17818
18751
  });
17819
- connection.on("command", (command) => {
17820
- console.log("[Session] Forwarding command:", {
17821
- action: command.action,
17822
- paramsKeys: command.params ? Object.keys(command.params) : []
17823
- });
18752
+ this.addConnectionListener(connection, "command", (command) => {
18753
+ const originSessionId = command.__sessionId;
18754
+ if (originSessionId && originSessionId !== this._id) {
18755
+ console.log("[Session] Command not forwarded:", {
18756
+ command,
18757
+ originSessionId,
18758
+ sessionId: this._id
18759
+ });
18760
+ return;
18761
+ }
17824
18762
  this.emit("command", command);
17825
18763
  });
17826
18764
  }
@@ -17830,19 +18768,20 @@ var ActiveSessionImpl = class {
17830
18768
  _meta: response._meta ?? void 0
17831
18769
  };
17832
18770
  }
18771
+ /**
18772
+ * 判断 artifact 是否应该转发给当前 session
18773
+ * 所有类型的 artifact 都按 __sessionId 严格隔离
18774
+ */
18775
+ shouldForwardArtifact(artifact) {
18776
+ const originSessionId = artifact.__sessionId;
18777
+ if (!originSessionId || originSessionId !== this._id) return false;
18778
+ return true;
18779
+ }
17833
18780
  };
17834
18781
 
17835
18782
  //#endregion
17836
18783
  //#region ../agent-provider/src/common/client/session-manager.ts
17837
18784
  /**
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
18785
  * SessionManager - Session lifecycle management
17847
18786
  *
17848
18787
  * This class manages the relationship between sessions and agents.
@@ -17892,7 +18831,8 @@ var SessionManager = class {
17892
18831
  createdAt: agent.createdAt,
17893
18832
  lastActivityAt: agent.updatedAt,
17894
18833
  cwd: agent.type === "local" ? agent.cwd : void 0,
17895
- isPlayground: agent.isPlayground
18834
+ isPlayground: agent.isPlayground,
18835
+ isUserDefinedTitle: agent.isUserDefinedTitle
17896
18836
  }));
17897
18837
  console.log("[SessionManager] Returning sessions:", {
17898
18838
  count: sessions.length,
@@ -17919,13 +18859,26 @@ var SessionManager = class {
17919
18859
  if (this.provider.create) {
17920
18860
  agentId = await this.provider.create(params);
17921
18861
  this.logger?.debug(`Created new agent: ${agentId}`);
18862
+ if (params.options?.onSessionPrepared) {
18863
+ const initialPrompt = params.options?.prompt;
18864
+ const initialTitle = initialPrompt?.slice(0, 50) || "";
18865
+ params.options.onSessionPrepared({
18866
+ id: agentId,
18867
+ agentId,
18868
+ name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
18869
+ status: "connecting",
18870
+ cwd: params.cwd || "",
18871
+ createdAt: /* @__PURE__ */ new Date()
18872
+ });
18873
+ this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
18874
+ }
17922
18875
  } else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
17923
18876
  const connection = await this.provider.connect(agentId);
17924
18877
  this.logger?.debug(`Connected to agent: ${agentId}`);
17925
18878
  const response = await connection.createSession({
17926
- _meta: params._meta,
18879
+ _meta: params.options?._meta,
17927
18880
  cwd: params.cwd,
17928
- mcpServers: params.mcpServers
18881
+ mcpServers: params.options?.mcpServers
17929
18882
  });
17930
18883
  if (this.provider.registerSession) {
17931
18884
  this.provider.registerSession(response.sessionId, agentId);
@@ -17938,14 +18891,10 @@ var SessionManager = class {
17938
18891
  connectionInfo
17939
18892
  });
17940
18893
  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
- }
18894
+ const availableModels = this.extractAvailableModels(response);
18895
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
18896
+ const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
18897
+ if (responseCwd) session.setCwd(responseCwd);
17949
18898
  this.logger?.info(`Session created: ${response.sessionId}`);
17950
18899
  return session;
17951
18900
  }
@@ -17981,17 +18930,31 @@ var SessionManager = class {
17981
18930
  mcpServers: params.mcpServers
17982
18931
  });
17983
18932
  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
- }
18933
+ const availableModels = this.extractAvailableModels(response);
18934
+ if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
17992
18935
  this.logger?.info(`Session loaded: ${params.sessionId}`);
17993
18936
  return session;
17994
18937
  }
18938
+ /**
18939
+ * 从 ACP response 中提取可用模型列表
18940
+ *
18941
+ * 优先级:
18942
+ * 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
18943
+ * 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
18944
+ * 3. undefined - 都没有时返回 undefined
18945
+ *
18946
+ * @param response - ACP 响应对象
18947
+ * @returns ModelInfo[] | undefined
18948
+ */
18949
+ extractAvailableModels(response) {
18950
+ const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
18951
+ if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
18952
+ const availableModels = response.models?.availableModels;
18953
+ if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
18954
+ ...model,
18955
+ ...model._meta?.["codebuddy.ai"] || {}
18956
+ }));
18957
+ }
17995
18958
  };
17996
18959
 
17997
18960
  //#endregion
@@ -18098,6 +19061,26 @@ var AgentClient = class {
18098
19061
  throw error;
18099
19062
  }
18100
19063
  },
19064
+ updateStatus: async (sessionId, status) => {
19065
+ this.logger?.debug("AgentClient.sessions.updateStatus called", {
19066
+ sessionId,
19067
+ status
19068
+ });
19069
+ try {
19070
+ if (this.provider.updateStatus) {
19071
+ const result = await this.provider.updateStatus(sessionId, status);
19072
+ this.logger?.info("Session status updated successfully", {
19073
+ sessionId,
19074
+ status
19075
+ });
19076
+ return result;
19077
+ }
19078
+ throw new Error("Provider does not support updateStatus method");
19079
+ } catch (error) {
19080
+ this.logger?.error("Failed to update session status", error);
19081
+ throw error;
19082
+ }
19083
+ },
18101
19084
  move: async (sessionId) => {
18102
19085
  this.logger?.debug("AgentClient.sessions.move called", { sessionId });
18103
19086
  try {
@@ -18145,6 +19128,100 @@ var AgentClient = class {
18145
19128
  return [];
18146
19129
  }
18147
19130
  },
19131
+ getAutomationSnapshot: async () => {
19132
+ try {
19133
+ if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
19134
+ this.logger?.warn("Provider does not support getAutomationSnapshot");
19135
+ } catch (error) {
19136
+ this.logger?.error("Failed to get automation snapshot", error);
19137
+ }
19138
+ return {
19139
+ automations: [],
19140
+ inbox: [],
19141
+ runtimeState: {},
19142
+ updatedAt: Date.now()
19143
+ };
19144
+ },
19145
+ updateAutomation: async (payload) => {
19146
+ try {
19147
+ if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
19148
+ this.logger?.warn("Provider does not support updateAutomation");
19149
+ } catch (error) {
19150
+ this.logger?.error("Failed to update automation", error);
19151
+ return {
19152
+ success: false,
19153
+ message: error instanceof Error ? error.message : "Unknown error"
19154
+ };
19155
+ }
19156
+ return {
19157
+ success: false,
19158
+ message: "Provider does not support updateAutomation"
19159
+ };
19160
+ },
19161
+ deleteAutomation: async (id) => {
19162
+ try {
19163
+ if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
19164
+ this.logger?.warn("Provider does not support deleteAutomation");
19165
+ } catch (error) {
19166
+ this.logger?.error("Failed to delete automation", error);
19167
+ return {
19168
+ success: false,
19169
+ message: error instanceof Error ? error.message : "Unknown error"
19170
+ };
19171
+ }
19172
+ return {
19173
+ success: false,
19174
+ message: "Provider does not support deleteAutomation"
19175
+ };
19176
+ },
19177
+ archiveAutomationInboxItem: async (itemId) => {
19178
+ try {
19179
+ if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
19180
+ this.logger?.warn("Provider does not support archiveAutomationInboxItem");
19181
+ } catch (error) {
19182
+ this.logger?.error("Failed to archive automation inbox item", error);
19183
+ return {
19184
+ success: false,
19185
+ message: error instanceof Error ? error.message : "Unknown error"
19186
+ };
19187
+ }
19188
+ return {
19189
+ success: false,
19190
+ message: "Provider does not support archiveAutomationInboxItem"
19191
+ };
19192
+ },
19193
+ deleteAutomationInboxItem: async (itemId) => {
19194
+ try {
19195
+ if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
19196
+ this.logger?.warn("Provider does not support deleteAutomationInboxItem");
19197
+ } catch (error) {
19198
+ this.logger?.error("Failed to delete automation inbox item", error);
19199
+ return {
19200
+ success: false,
19201
+ message: error instanceof Error ? error.message : "Unknown error"
19202
+ };
19203
+ }
19204
+ return {
19205
+ success: false,
19206
+ message: "Provider does not support deleteAutomationInboxItem"
19207
+ };
19208
+ },
19209
+ testAutomation: async (id) => {
19210
+ try {
19211
+ if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
19212
+ this.logger?.warn("Provider does not support testAutomation");
19213
+ } catch (error) {
19214
+ this.logger?.error("Failed to test automation", error);
19215
+ return {
19216
+ success: false,
19217
+ message: error instanceof Error ? error.message : "Unknown error"
19218
+ };
19219
+ }
19220
+ return {
19221
+ success: false,
19222
+ message: "Provider does not support testAutomation"
19223
+ };
19224
+ },
18148
19225
  on: (event, handler) => {
18149
19226
  if (this.provider.on) this.provider.on(event, handler);
18150
19227
  else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
@@ -18264,6 +19341,189 @@ var AgentClient = class {
18264
19341
  };
18265
19342
  }
18266
19343
  },
19344
+ getSubagentList: async (params) => {
19345
+ try {
19346
+ if (this.provider && this.provider.getSubagentList) {
19347
+ const result = await this.provider.getSubagentList(params);
19348
+ this.logger?.info("Subagent list retrieved", {
19349
+ resultCount: result.results.length,
19350
+ hasError: !!result.error
19351
+ });
19352
+ return result;
19353
+ }
19354
+ return {
19355
+ results: [],
19356
+ error: "Provider does not support getSubagentList"
19357
+ };
19358
+ } catch (error) {
19359
+ this.logger?.error("Failed to get subagent list", error);
19360
+ return {
19361
+ results: [],
19362
+ error: error instanceof Error ? error.message : "Unknown error"
19363
+ };
19364
+ }
19365
+ },
19366
+ getSkillList: async (params) => {
19367
+ try {
19368
+ if (this.provider && this.provider.getSkillList) {
19369
+ const result = await this.provider.getSkillList(params);
19370
+ this.logger?.info("Skill list retrieved", {
19371
+ resultCount: result.results.length,
19372
+ hasError: !!result.error
19373
+ });
19374
+ return result;
19375
+ }
19376
+ return {
19377
+ results: [],
19378
+ error: "Provider does not support getSkillList"
19379
+ };
19380
+ } catch (error) {
19381
+ this.logger?.error("Failed to get skill list", error);
19382
+ return {
19383
+ results: [],
19384
+ error: error instanceof Error ? error.message : "Unknown error"
19385
+ };
19386
+ }
19387
+ },
19388
+ importSkill: async (params) => {
19389
+ try {
19390
+ if (this.provider && this.provider.importSkill) {
19391
+ const result = await this.provider.importSkill(params);
19392
+ this.logger?.info("Import skill completed", {
19393
+ success: result.success,
19394
+ hasError: !!result.error
19395
+ });
19396
+ return result;
19397
+ }
19398
+ return {
19399
+ success: false,
19400
+ error: "Provider does not support importSkill"
19401
+ };
19402
+ } catch (error) {
19403
+ this.logger?.error("Failed to import skill", error);
19404
+ return {
19405
+ success: false,
19406
+ error: error instanceof Error ? error.message : "Unknown error"
19407
+ };
19408
+ }
19409
+ },
19410
+ toggleSkill: async (params) => {
19411
+ try {
19412
+ if (this.provider && this.provider.toggleSkill) {
19413
+ const result = await this.provider.toggleSkill(params);
19414
+ this.logger?.info("Toggle skill completed", {
19415
+ success: result.success,
19416
+ hasError: !!result.error
19417
+ });
19418
+ return result;
19419
+ }
19420
+ return {
19421
+ success: false,
19422
+ error: "Provider does not support toggleSkill"
19423
+ };
19424
+ } catch (error) {
19425
+ this.logger?.error("Failed to toggle skill", error);
19426
+ return {
19427
+ success: false,
19428
+ error: error instanceof Error ? error.message : "Unknown error"
19429
+ };
19430
+ }
19431
+ },
19432
+ deleteSkill: async (params) => {
19433
+ try {
19434
+ if (this.provider && this.provider.deleteSkill) {
19435
+ const result = await this.provider.deleteSkill(params);
19436
+ this.logger?.info("Delete skill completed", {
19437
+ success: result.success,
19438
+ hasError: !!result.error
19439
+ });
19440
+ return result;
19441
+ }
19442
+ return {
19443
+ success: false,
19444
+ error: "Provider does not support deleteSkill"
19445
+ };
19446
+ } catch (error) {
19447
+ this.logger?.error("Failed to delete skill", error);
19448
+ return {
19449
+ success: false,
19450
+ error: error instanceof Error ? error.message : "Unknown error"
19451
+ };
19452
+ }
19453
+ },
19454
+ getSkillContent: async (params) => {
19455
+ try {
19456
+ if (this.provider && this.provider.getSkillContent) {
19457
+ const result = await this.provider.getSkillContent(params);
19458
+ this.logger?.info("Get skill content completed", { hasError: !!result.error });
19459
+ return result;
19460
+ }
19461
+ return {
19462
+ content: "",
19463
+ error: "Provider does not support getSkillContent"
19464
+ };
19465
+ } catch (error) {
19466
+ this.logger?.error("Failed to get skill content", error);
19467
+ return {
19468
+ content: "",
19469
+ error: error instanceof Error ? error.message : "Unknown error"
19470
+ };
19471
+ }
19472
+ },
19473
+ getMarketplaceSkills: async () => {
19474
+ try {
19475
+ if (this.provider && this.provider.getMarketplaceSkills) {
19476
+ const result = await this.provider.getMarketplaceSkills();
19477
+ this.logger?.info("Marketplace skills retrieved", {
19478
+ resultCount: result.results.length,
19479
+ hasError: !!result.error
19480
+ });
19481
+ return result;
19482
+ }
19483
+ return {
19484
+ results: [],
19485
+ error: "Provider does not support getMarketplaceSkills"
19486
+ };
19487
+ } catch (error) {
19488
+ this.logger?.error("Failed to get marketplace skills", error);
19489
+ return {
19490
+ results: [],
19491
+ error: error instanceof Error ? error.message : "Unknown error"
19492
+ };
19493
+ }
19494
+ },
19495
+ getMarketplaceSkillContent: async (params) => {
19496
+ try {
19497
+ if (this.provider && this.provider.getMarketplaceSkillContent) return await this.provider.getMarketplaceSkillContent(params);
19498
+ return {
19499
+ content: "",
19500
+ error: "Provider does not support getMarketplaceSkillContent"
19501
+ };
19502
+ } catch (error) {
19503
+ this.logger?.error("Failed to get marketplace skill content", error);
19504
+ return {
19505
+ content: "",
19506
+ error: error instanceof Error ? error.message : "Unknown error"
19507
+ };
19508
+ }
19509
+ },
19510
+ installMarketplaceSkill: async (params) => {
19511
+ try {
19512
+ if (this.provider && this.provider.installMarketplaceSkill) return await this.provider.installMarketplaceSkill(params);
19513
+ return {
19514
+ success: false,
19515
+ skillName: params.skillName,
19516
+ errorMessage: "Provider does not support installMarketplaceSkill"
19517
+ };
19518
+ } catch (error) {
19519
+ this.logger?.error("Failed to install marketplace skill", error);
19520
+ return {
19521
+ success: false,
19522
+ skillName: params.skillName,
19523
+ errorMessage: error instanceof Error ? error.message : "Unknown error"
19524
+ };
19525
+ }
19526
+ },
18267
19527
  batchTogglePlugins: async (request) => {
18268
19528
  try {
18269
19529
  if (this.provider && this.provider.batchTogglePlugins) {
@@ -18304,41 +19564,233 @@ var AgentClient = class {
18304
19564
  this.logger?.warn("Provider does not support getInstalledPlugins");
18305
19565
  return [];
18306
19566
  } catch (error) {
18307
- this.logger?.error("Failed to get installed plugins", error);
18308
- return [];
19567
+ this.logger?.error("Failed to get installed plugins", error);
19568
+ return [];
19569
+ }
19570
+ },
19571
+ installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
19572
+ try {
19573
+ if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
19574
+ const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
19575
+ this.logger?.info("Install plugins", {
19576
+ pluginNames,
19577
+ marketplaceName,
19578
+ success: result.success
19579
+ });
19580
+ return result;
19581
+ }
19582
+ this.logger?.warn("Provider does not support installPlugins");
19583
+ return {
19584
+ success: false,
19585
+ error: "Provider does not support installPlugins"
19586
+ };
19587
+ } catch (error) {
19588
+ this.logger?.error("Failed to install plugins", error);
19589
+ return {
19590
+ success: false,
19591
+ error: error instanceof Error ? error.message : "Unknown error"
19592
+ };
19593
+ }
19594
+ },
19595
+ uninstallPlugin: async (pluginName, marketplaceName, scope) => {
19596
+ try {
19597
+ if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
19598
+ const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
19599
+ this.logger?.info("Uninstall plugin", {
19600
+ pluginName,
19601
+ marketplaceName,
19602
+ scope,
19603
+ success: result.success
19604
+ });
19605
+ return result;
19606
+ }
19607
+ this.logger?.warn("Provider does not support uninstallPlugin");
19608
+ return {
19609
+ success: false,
19610
+ error: "Provider does not support uninstallPlugin"
19611
+ };
19612
+ } catch (error) {
19613
+ this.logger?.error("Failed to uninstall plugin", error);
19614
+ return {
19615
+ success: false,
19616
+ error: error instanceof Error ? error.message : "Unknown error"
19617
+ };
19618
+ }
19619
+ },
19620
+ updatePlugin: async (pluginName, marketplaceName) => {
19621
+ try {
19622
+ if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
19623
+ const result = await this.provider.updatePlugin(pluginName, marketplaceName);
19624
+ this.logger?.info("Update plugin", {
19625
+ pluginName,
19626
+ marketplaceName,
19627
+ success: result.success
19628
+ });
19629
+ return result;
19630
+ }
19631
+ this.logger?.warn("Provider does not support updatePlugin");
19632
+ return {
19633
+ success: false,
19634
+ error: "Provider does not support updatePlugin"
19635
+ };
19636
+ } catch (error) {
19637
+ this.logger?.error("Failed to update plugin", error);
19638
+ return {
19639
+ success: false,
19640
+ error: error instanceof Error ? error.message : "Unknown error"
19641
+ };
19642
+ }
19643
+ },
19644
+ getPluginMarketplaces: async (forceRefresh) => {
19645
+ try {
19646
+ if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
19647
+ const result = await this.provider.getPluginMarketplaces(forceRefresh);
19648
+ this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
19649
+ return result;
19650
+ }
19651
+ this.logger?.warn("Provider does not support getPluginMarketplaces");
19652
+ return [];
19653
+ } catch (error) {
19654
+ this.logger?.error("Failed to get plugin marketplaces", error);
19655
+ return [];
19656
+ }
19657
+ },
19658
+ getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
19659
+ try {
19660
+ if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
19661
+ const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
19662
+ this.logger?.info("Got marketplace plugins", {
19663
+ marketplaceName,
19664
+ count: result?.length ?? 0
19665
+ });
19666
+ return result;
19667
+ }
19668
+ this.logger?.warn("Provider does not support getMarketplacePlugins");
19669
+ return [];
19670
+ } catch (error) {
19671
+ this.logger?.error("Failed to get marketplace plugins", error);
19672
+ return [];
19673
+ }
19674
+ },
19675
+ getPluginDetail: async (pluginName, marketplaceName) => {
19676
+ try {
19677
+ if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
19678
+ const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
19679
+ this.logger?.info("Got plugin detail", {
19680
+ pluginName,
19681
+ marketplaceName
19682
+ });
19683
+ return result;
19684
+ }
19685
+ this.logger?.warn("Provider does not support getPluginDetail");
19686
+ return null;
19687
+ } catch (error) {
19688
+ this.logger?.error("Failed to get plugin detail", error);
19689
+ return null;
19690
+ }
19691
+ },
19692
+ addPluginMarketplace: async (source, name) => {
19693
+ try {
19694
+ if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
19695
+ const result = await this.provider.addPluginMarketplace(source, name);
19696
+ this.logger?.info("Add plugin marketplace", {
19697
+ source,
19698
+ name,
19699
+ success: result.success
19700
+ });
19701
+ return result;
19702
+ }
19703
+ this.logger?.warn("Provider does not support addPluginMarketplace");
19704
+ return {
19705
+ success: false,
19706
+ error: "Provider does not support addPluginMarketplace"
19707
+ };
19708
+ } catch (error) {
19709
+ this.logger?.error("Failed to add plugin marketplace", error);
19710
+ return {
19711
+ success: false,
19712
+ error: error instanceof Error ? error.message : "Unknown error"
19713
+ };
19714
+ }
19715
+ },
19716
+ removePluginMarketplace: async (marketplaceName) => {
19717
+ try {
19718
+ if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
19719
+ const result = await this.provider.removePluginMarketplace(marketplaceName);
19720
+ this.logger?.info("Remove plugin marketplace", {
19721
+ marketplaceName,
19722
+ success: result.success
19723
+ });
19724
+ return result;
19725
+ }
19726
+ this.logger?.warn("Provider does not support removePluginMarketplace");
19727
+ return {
19728
+ success: false,
19729
+ error: "Provider does not support removePluginMarketplace"
19730
+ };
19731
+ } catch (error) {
19732
+ this.logger?.error("Failed to remove plugin marketplace", error);
19733
+ return {
19734
+ success: false,
19735
+ error: error instanceof Error ? error.message : "Unknown error"
19736
+ };
18309
19737
  }
18310
19738
  },
18311
- installPlugins: async (pluginNames, marketplaceName, installScope) => {
19739
+ refreshPluginMarketplace: async (marketplaceName) => {
18312
19740
  try {
18313
- if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
18314
- const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope);
18315
- this.logger?.info("Install plugins", {
18316
- pluginNames,
19741
+ if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
19742
+ const result = await this.provider.refreshPluginMarketplace(marketplaceName);
19743
+ this.logger?.info("Refresh plugin marketplace", {
18317
19744
  marketplaceName,
18318
19745
  success: result.success
18319
19746
  });
18320
19747
  return result;
18321
19748
  }
18322
- this.logger?.warn("Provider does not support installPlugins");
19749
+ this.logger?.warn("Provider does not support refreshPluginMarketplace");
18323
19750
  return {
18324
19751
  success: false,
18325
- error: "Provider does not support installPlugins"
19752
+ error: "Provider does not support refreshPluginMarketplace"
18326
19753
  };
18327
19754
  } catch (error) {
18328
- this.logger?.error("Failed to install plugins", error);
19755
+ this.logger?.error("Failed to refresh plugin marketplace", error);
18329
19756
  return {
18330
19757
  success: false,
18331
19758
  error: error instanceof Error ? error.message : "Unknown error"
18332
19759
  };
18333
19760
  }
18334
19761
  },
18335
- getSupportScenes: async () => {
19762
+ openFolderInNewWindow: async (folderPath) => {
19763
+ try {
19764
+ if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
19765
+ await this.provider.openFolderInNewWindow(folderPath);
19766
+ this.logger?.info("Opened folder in new window", { folderPath });
19767
+ } else {
19768
+ this.logger?.warn("Provider does not support openFolderInNewWindow");
19769
+ throw new Error("Provider does not support openFolderInNewWindow");
19770
+ }
19771
+ } catch (error) {
19772
+ this.logger?.error("Failed to open folder in new window", error);
19773
+ throw error;
19774
+ }
19775
+ },
19776
+ openFolder: async (folderPath) => {
18336
19777
  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 });
19778
+ if (this.provider && "openFolder" in this.provider && typeof this.provider.openFolder === "function") {
19779
+ const result = await this.provider.openFolder(folderPath);
19780
+ this.logger?.info("Opened folder in system file manager", { folderPath });
18340
19781
  return result;
19782
+ } else {
19783
+ this.logger?.warn("Provider does not support openFolder");
19784
+ throw new Error("Provider does not support openFolder");
18341
19785
  }
19786
+ } catch (error) {
19787
+ this.logger?.error("Failed to open folder", error);
19788
+ throw error;
19789
+ }
19790
+ },
19791
+ getSupportScenes: async (locale) => {
19792
+ try {
19793
+ if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
18342
19794
  this.logger?.warn("Provider does not support getSupportScenes");
18343
19795
  return [];
18344
19796
  } catch (error) {
@@ -18346,6 +19798,193 @@ var AgentClient = class {
18346
19798
  return [];
18347
19799
  }
18348
19800
  },
19801
+ getProductScenes: async (locale) => {
19802
+ try {
19803
+ if (this.provider?.getProductScenes) {
19804
+ const result = await this.provider.getProductScenes(locale);
19805
+ this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
19806
+ return result;
19807
+ }
19808
+ this.logger?.warn("Provider does not support getProductScenes");
19809
+ return [];
19810
+ } catch (error) {
19811
+ this.logger?.error("Failed to get product scenes", error);
19812
+ return [];
19813
+ }
19814
+ },
19815
+ getAvailableCommands: async (params) => {
19816
+ try {
19817
+ if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
19818
+ const result = await this.provider.getAvailableCommands(params);
19819
+ this.logger?.info("Got available commands from provider", {
19820
+ sessionId: params?.sessionId ?? "(default)",
19821
+ count: result?.length ?? 0
19822
+ });
19823
+ return result;
19824
+ }
19825
+ this.logger?.warn("Provider does not support getAvailableCommands", { params });
19826
+ return [];
19827
+ } catch (error) {
19828
+ this.logger?.error("Failed to get available commands", error);
19829
+ return [];
19830
+ }
19831
+ },
19832
+ reportTelemetry: async (eventName, payload) => {
19833
+ try {
19834
+ if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
19835
+ else this.logger?.warn("Provider does not support reportTelemetry");
19836
+ } catch (error) {
19837
+ this.logger?.error("Failed to report telemetry", error);
19838
+ }
19839
+ },
19840
+ getProductConfiguration: async () => {
19841
+ try {
19842
+ if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
19843
+ this.logger?.warn("Provider does not support getProductConfiguration");
19844
+ return {};
19845
+ } catch (error) {
19846
+ this.logger?.error("Failed to get product configuration", error);
19847
+ return {};
19848
+ }
19849
+ },
19850
+ getUserInfo: async () => {
19851
+ this.logger?.info("[AgentClient.sessions] getUserInfo() called");
19852
+ try {
19853
+ if (this.provider?.getUserInfo) {
19854
+ const result = await this.provider.getUserInfo();
19855
+ this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
19856
+ return result;
19857
+ }
19858
+ this.logger?.warn("Provider does not support getUserInfo");
19859
+ return {};
19860
+ } catch (error) {
19861
+ this.logger?.error("Failed to get user info", error);
19862
+ return {};
19863
+ }
19864
+ },
19865
+ respondToSampling: async (sessionId, response) => {
19866
+ try {
19867
+ if (this.provider?.respondToSampling) {
19868
+ await this.provider.respondToSampling(sessionId, response);
19869
+ this.logger?.info("Responded to sampling request", {
19870
+ sessionId,
19871
+ requestId: response.id,
19872
+ approved: response.approved
19873
+ });
19874
+ } else this.logger?.warn("Provider does not support respondToSampling");
19875
+ } catch (error) {
19876
+ this.logger?.error("Failed to respond to sampling request", error);
19877
+ throw error;
19878
+ }
19879
+ },
19880
+ respondToRoots: async (sessionId, response) => {
19881
+ try {
19882
+ if (this.provider?.respondToRoots) {
19883
+ await this.provider.respondToRoots(sessionId, response);
19884
+ this.logger?.info("Responded to roots request", {
19885
+ sessionId,
19886
+ requestId: response.id,
19887
+ approved: response.approved
19888
+ });
19889
+ } else this.logger?.warn("Provider does not support respondToRoots");
19890
+ } catch (error) {
19891
+ this.logger?.error("Failed to respond to roots request", error);
19892
+ throw error;
19893
+ }
19894
+ },
19895
+ subscribeSamplingRequests: (serverName, callback) => {
19896
+ if (this.provider?.subscribeSamplingRequests) {
19897
+ this.logger?.info("Subscribing to sampling requests", { serverName });
19898
+ return this.provider.subscribeSamplingRequests(serverName, callback);
19899
+ }
19900
+ this.logger?.warn("Provider does not support subscribeSamplingRequests");
19901
+ return () => {};
19902
+ },
19903
+ subscribeRootsRequests: (serverName, callback) => {
19904
+ if (this.provider?.subscribeRootsRequests) {
19905
+ this.logger?.info("Subscribing to roots requests", { serverName });
19906
+ return this.provider.subscribeRootsRequests(serverName, callback);
19907
+ }
19908
+ this.logger?.warn("Provider does not support subscribeRootsRequests");
19909
+ return () => {};
19910
+ },
19911
+ getMcpServers: async () => {
19912
+ if (this.provider?.getMcpServers) {
19913
+ this.logger?.info("Getting MCP servers list");
19914
+ return this.provider.getMcpServers();
19915
+ }
19916
+ this.logger?.warn("Provider does not support getMcpServers");
19917
+ return [];
19918
+ },
19919
+ toggleMcpServer: async (serverName, enabled) => {
19920
+ if (this.provider?.toggleMcpServer) {
19921
+ this.logger?.info("Toggling MCP server", {
19922
+ serverName,
19923
+ enabled
19924
+ });
19925
+ await this.provider.toggleMcpServer(serverName, enabled);
19926
+ } else {
19927
+ this.logger?.warn("Provider does not support toggleMcpServer");
19928
+ throw new Error("toggleMcpServer not supported by provider");
19929
+ }
19930
+ },
19931
+ reconnectMcpServer: async (serverName, forceHttpCallback) => {
19932
+ if (this.provider?.reconnectMcpServer) {
19933
+ this.logger?.info("Reconnecting MCP server", {
19934
+ serverName,
19935
+ forceHttpCallback
19936
+ });
19937
+ await this.provider.reconnectMcpServer(serverName, forceHttpCallback);
19938
+ } else {
19939
+ this.logger?.warn("Provider does not support reconnectMcpServer");
19940
+ throw new Error("reconnectMcpServer not supported by provider");
19941
+ }
19942
+ },
19943
+ deleteMcpServer: async (serverName) => {
19944
+ if (this.provider?.deleteMcpServer) {
19945
+ this.logger?.info("Deleting MCP server", { serverName });
19946
+ await this.provider.deleteMcpServer(serverName);
19947
+ } else {
19948
+ this.logger?.warn("Provider does not support deleteMcpServer");
19949
+ throw new Error("deleteMcpServer not supported by provider");
19950
+ }
19951
+ },
19952
+ openMcpConfig: async () => {
19953
+ if (this.provider?.openMcpConfig) {
19954
+ this.logger?.info("Opening MCP config");
19955
+ await this.provider.openMcpConfig();
19956
+ } else {
19957
+ this.logger?.warn("Provider does not support openMcpConfig");
19958
+ throw new Error("openMcpConfig not supported by provider");
19959
+ }
19960
+ },
19961
+ getMcpConfigContent: async () => {
19962
+ if (this.provider?.getMcpConfigContent) {
19963
+ this.logger?.info("Getting MCP config content");
19964
+ return await this.provider.getMcpConfigContent();
19965
+ } else {
19966
+ this.logger?.warn("Provider does not support getMcpConfigContent");
19967
+ throw new Error("getMcpConfigContent not supported by provider");
19968
+ }
19969
+ },
19970
+ saveMcpConfigContent: async (content) => {
19971
+ if (this.provider?.saveMcpConfigContent) {
19972
+ this.logger?.info("Saving MCP config content");
19973
+ await this.provider.saveMcpConfigContent(content);
19974
+ } else {
19975
+ this.logger?.warn("Provider does not support saveMcpConfigContent");
19976
+ throw new Error("saveMcpConfigContent not supported by provider");
19977
+ }
19978
+ },
19979
+ clipboardReadText: async () => {
19980
+ if (this.provider?.clipboardReadText) {
19981
+ this.logger?.info("Reading clipboard text");
19982
+ return await this.provider.clipboardReadText();
19983
+ } else {
19984
+ this.logger?.warn("Provider does not support clipboardReadText");
19985
+ throw new Error("clipboardReadText not supported by provider");
19986
+ }
19987
+ },
18349
19988
  models: this.createModelsResource()
18350
19989
  };
18351
19990
  }
@@ -18412,6 +20051,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
18412
20051
  return AccountStatus;
18413
20052
  }({});
18414
20053
 
20054
+ //#endregion
20055
+ //#region ../agent-provider/src/backend/service/oauth-repository-service.ts
20056
+ /**
20057
+ * OAuth Repository Service
20058
+ *
20059
+ * 封装 OAuth 连接器相关的仓库和分支操作
20060
+ */
20061
+ /**
20062
+ * OAuth Repository Service
20063
+ *
20064
+ * 提供仓库和分支的查询操作
20065
+ */
20066
+ var OAuthRepositoryService = class {
20067
+ /**
20068
+ * 获取仓库分支列表
20069
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
20070
+ *
20071
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
20072
+ * @param params 平台特定的查询参数
20073
+ * @param page 页码,从1开始,0表示不分页获取全部
20074
+ * @param perPage 每页数量,最大100
20075
+ * @returns Promise<OauthBranch[]> 分支列表
20076
+ *
20077
+ * @example
20078
+ * ```typescript
20079
+ * // GitHub
20080
+ * const branches = await service.getBranches('github', {
20081
+ * owner: 'CodeBuddy-Official-Account',
20082
+ * repo: 'CodeBuddyIDE'
20083
+ * });
20084
+ *
20085
+ * // Gongfeng
20086
+ * const branches = await service.getBranches('gongfeng', {
20087
+ * project_id: '1611499'
20088
+ * });
20089
+ *
20090
+ * // CNB
20091
+ * const branches = await service.getBranches('cnb', {
20092
+ * repo: 'genie/genie-ide'
20093
+ * });
20094
+ * ```
20095
+ */
20096
+ async getBranches(connector, params, page = 0, perPage = 100) {
20097
+ try {
20098
+ const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
20099
+ console.log(`[OAuthRepositoryService] GET ${url}`);
20100
+ const apiResponse = await httpService.get(url);
20101
+ if (!apiResponse.data) {
20102
+ console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
20103
+ return [];
20104
+ }
20105
+ const branches = apiResponse.data.branches || [];
20106
+ console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
20107
+ return branches;
20108
+ } catch (error) {
20109
+ console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
20110
+ throw error;
20111
+ }
20112
+ }
20113
+ /**
20114
+ * 获取仓库列表
20115
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
20116
+ *
20117
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
20118
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
20119
+ *
20120
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
20121
+ * @param page 页码,从1开始,0表示不分页获取全部
20122
+ * - GitHub 只支持全量数据,必须传 0
20123
+ * - 工蜂和 CNB 依据前端逻辑而定
20124
+ * @param perPage 每页数量,最大100
20125
+ * @returns Promise<ListReposResponse> 仓库列表响应
20126
+ *
20127
+ * @example
20128
+ * ```typescript
20129
+ * // GitHub - 必须传 page=0 获取全量数据
20130
+ * const response = await service.getRepositories('github', 0, 100);
20131
+ * // response.github_repos 是 map: installation_id => repo[]
20132
+ *
20133
+ * // Gongfeng
20134
+ * const response = await service.getRepositories('gongfeng', 0, 100);
20135
+ * // response.gongfeng_repos 是数组
20136
+ *
20137
+ * // CNB
20138
+ * const response = await service.getRepositories('cnb', 0, 100);
20139
+ * // response.cnb_repos 是数组
20140
+ * ```
20141
+ */
20142
+ async getRepositories(connector, page = 0, perPage = 100) {
20143
+ try {
20144
+ const queryParams = new URLSearchParams();
20145
+ queryParams.append("page", String(page));
20146
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
20147
+ const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
20148
+ console.log(`[OAuthRepositoryService] GET ${url}`);
20149
+ const apiResponse = await httpService.get(url);
20150
+ if (!apiResponse.data) {
20151
+ console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
20152
+ return {};
20153
+ }
20154
+ const response = apiResponse.data;
20155
+ this.logRepositoryCounts(response);
20156
+ return response;
20157
+ } catch (error) {
20158
+ console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
20159
+ throw error;
20160
+ }
20161
+ }
20162
+ /**
20163
+ * 构建分支查询参数
20164
+ */
20165
+ buildBranchQueryParams(connector, params, page, perPage) {
20166
+ const queryParams = new URLSearchParams();
20167
+ queryParams.append("page", String(page));
20168
+ queryParams.append("per_page", String(Math.min(perPage, 100)));
20169
+ if (connector === "github") {
20170
+ const githubParams = params;
20171
+ if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
20172
+ queryParams.append("owner", githubParams.owner);
20173
+ queryParams.append("repo", githubParams.repo);
20174
+ } else if (connector === "gongfeng") {
20175
+ const gongfengParams = params;
20176
+ if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
20177
+ queryParams.append("project_id", gongfengParams.project_id);
20178
+ } else if (connector === "cnb") {
20179
+ const cnbParams = params;
20180
+ if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
20181
+ queryParams.append("repo", cnbParams.repo);
20182
+ } else throw new Error(`Unknown connector: ${connector}`);
20183
+ return queryParams;
20184
+ }
20185
+ /**
20186
+ * 记录仓库数量日志
20187
+ */
20188
+ logRepositoryCounts(response) {
20189
+ if (response.github_repos) {
20190
+ const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
20191
+ console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
20192
+ }
20193
+ if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
20194
+ if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
20195
+ }
20196
+ };
20197
+ /**
20198
+ * OAuth Repository Service 单例实例
20199
+ */
20200
+ const oauthRepositoryService = new OAuthRepositoryService();
20201
+
18415
20202
  //#endregion
18416
20203
  //#region ../agent-provider/src/backend/backend-provider.ts
18417
20204
  /**
@@ -18420,28 +20207,17 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
18420
20207
  * 封装与后端 API 的 HTTP 通信
18421
20208
  */
18422
20209
  /**
18423
- * 判断当前是否在 SSO 域名下
18424
- * SSO 域名格式: xxx.sso.copilot.tencent.com xxx.sso.codebuddy.cn
20210
+ * 判断当前账号是否是 SSO 账号
20211
+ * 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
18425
20212
  */
18426
- const isSSODomain = () => {
18427
- const { hostname } = window.location;
18428
- return hostname.includes(".sso.copilot") || hostname.includes("sso.codebuddy.cn") || hostname.includes(".sso.copilot-staging") || hostname.includes(".staging-sso.codebuddy.cn");
18429
- };
18430
20213
  /**
18431
- * 获取登录页面 URL
18432
- * - SSO 域名下需要跳转到对应的预发/生产域名
18433
- * - 非 SSO 域名直接使用当前域名
20214
+ * 根据路径获取完整 URL
20215
+ * - SSO 账号需要跳转到对应的预发/生产域名
20216
+ * - 非 SSO 账号直接使用当前域名
20217
+ * @param path 路径,如 '/login'、'/logout'、'/home' 等
20218
+ * @returns 完整的 URL 地址
18434
20219
  */
18435
- const getLoginUrl = () => {
18436
- const { hostname, protocol } = window.location;
18437
- if (isSSODomain()) {
18438
- const isCodebuddy = hostname.includes("codebuddy.cn");
18439
- const isStaging = hostname.includes("staging");
18440
- if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
18441
- else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
18442
- }
18443
- return `${window.location.origin}/login`;
18444
- };
20220
+ const getFullUrl = (path) => `${window.location.origin}${path}`;
18445
20221
  /** 获取当前域名的账号选择页面 URL */
18446
20222
  const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
18447
20223
  /** localStorage 中存储选中账号 ID 的 key */
@@ -18478,12 +20254,30 @@ var BackendProvider = class {
18478
20254
  constructor(config) {
18479
20255
  httpService.setBaseURL(config.baseUrl);
18480
20256
  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);
20257
+ httpService.onUnauthorized(() => this.handleUnauthorized());
20258
+ }
20259
+ /**
20260
+ * 处理 401 未授权错误
20261
+ * 先尝试刷新 token,失败后再执行登出流程
20262
+ *
20263
+ * @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
20264
+ */
20265
+ async handleUnauthorized() {
20266
+ console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
20267
+ try {
20268
+ if (await this.refreshToken()) {
20269
+ console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
20270
+ return;
20271
+ }
20272
+ throw new Error("Token refresh returned null");
20273
+ } catch (error) {
20274
+ console.error("[BackendProvider] Token refresh failed after 401:", error);
20275
+ console.log("[BackendProvider] Token refresh failed, triggering logout");
20276
+ this.logout().catch((logoutError) => {
20277
+ console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
18485
20278
  });
18486
- });
20279
+ throw error;
20280
+ }
18487
20281
  }
18488
20282
  /**
18489
20283
  * 获取当前账号信息
@@ -18528,7 +20322,7 @@ var BackendProvider = class {
18528
20322
  return account;
18529
20323
  }
18530
20324
  const redirectUrl = encodeURIComponent(window.location.href);
18531
- window.location.href = `${getSelectAccountUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
20325
+ window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
18532
20326
  accountService.setAccount(null);
18533
20327
  return null;
18534
20328
  } catch (error) {
@@ -18551,7 +20345,8 @@ var BackendProvider = class {
18551
20345
  activeStatus: connector.active_status,
18552
20346
  displayName: connector.display_name,
18553
20347
  oauthClientId: connector.oauth_client_id,
18554
- oauthRedirectUrl: connector.oauth_redirect_url
20348
+ oauthRedirectUrl: connector.oauth_redirect_url,
20349
+ oauthAppName: connector.oauth_app_name
18555
20350
  })) };
18556
20351
  }
18557
20352
  throw result;
@@ -18633,7 +20428,8 @@ var BackendProvider = class {
18633
20428
  connectStatus: connector.connect_status,
18634
20429
  displayName: connector.display_name,
18635
20430
  oauthClientId: connector.oauth_client_id,
18636
- oauthRedirectUrl: connector.oauth_redirect_url
20431
+ oauthRedirectUrl: connector.oauth_redirect_url,
20432
+ oauthAppName: connector.oauth_app_name
18637
20433
  })) };
18638
20434
  }
18639
20435
  throw result;
@@ -18745,10 +20541,11 @@ var BackendProvider = class {
18745
20541
  if (!time) return 0;
18746
20542
  return new Date(time).getTime();
18747
20543
  };
18748
- const dailyCredits = [CommodityCode.free, CommodityCode.freeMon];
20544
+ const dailyCredits = [CommodityCode.free];
18749
20545
  const planResources = resources.map((r) => {
18750
20546
  const isDaily = dailyCredits.includes(r.PackageCode);
18751
20547
  const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
20548
+ const refreshAt = parseTime(r.CycleEndTime) + 1e3;
18752
20549
  return {
18753
20550
  id: r.ResourceId,
18754
20551
  name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
@@ -18758,7 +20555,7 @@ var BackendProvider = class {
18758
20555
  used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
18759
20556
  left: Number(r.CycleCapacityRemainPrecise) || 0,
18760
20557
  expireAt: parseTime(endTime),
18761
- refreshAt: isDaily ? void 0 : parseTime(r.CycleEndTime)
20558
+ refreshAt: isDaily ? void 0 : refreshAt
18762
20559
  };
18763
20560
  }).sort((a, b) => {
18764
20561
  const getPriority = (code) => {
@@ -18766,10 +20563,11 @@ var BackendProvider = class {
18766
20563
  CommodityCode.proMon,
18767
20564
  CommodityCode.proMonPlus,
18768
20565
  CommodityCode.proYear,
20566
+ CommodityCode.freeMon,
18769
20567
  CommodityCode.extra
18770
20568
  ].includes(code)) return 1;
18771
20569
  if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
18772
- if ([CommodityCode.free, CommodityCode.freeMon].includes(code)) return 3;
20570
+ if ([CommodityCode.free].includes(code)) return 3;
18773
20571
  return 4;
18774
20572
  };
18775
20573
  return getPriority(a.packageCode) - getPriority(b.packageCode);
@@ -18890,38 +20688,69 @@ var BackendProvider = class {
18890
20688
  */
18891
20689
  async login() {
18892
20690
  const redirectUrl = encodeURIComponent(window.location.href);
18893
- window.location.href = `${getLoginUrl()}?platform=website&state=0&redirect_uri=${redirectUrl}`;
20691
+ window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
18894
20692
  }
18895
20693
  /**
18896
20694
  * 登出账号
18897
- * Web 环境: 通过 iframe 访问登出 URL 清除 cookie
20695
+ *
20696
+ * 策略:
20697
+ * - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
20698
+ * - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
18898
20699
  */
18899
20700
  async logout() {
18900
- const url = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
20701
+ const account = accountService.getAccount();
20702
+ if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
20703
+ else await this.logoutViaHttp();
20704
+ localStorage.removeItem(SELECTED_ACCOUNT_KEY);
20705
+ accountService.clearAccount();
20706
+ }
20707
+ /**
20708
+ * IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
20709
+ * 轮询 iframe URL 变化检测完成,兜底超时 5 秒
20710
+ */
20711
+ async logoutViaIframe() {
20712
+ const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
18901
20713
  try {
18902
20714
  await new Promise((resolve) => {
18903
20715
  const iframe = document.createElement("iframe");
18904
20716
  iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
18905
- iframe.src = url;
18906
- const timeout = setTimeout(() => {
18907
- cleanup();
18908
- resolve();
18909
- }, 5e3);
18910
- const cleanup = () => {
20717
+ iframe.src = logoutUrl;
20718
+ let pollTimer;
20719
+ let settled = false;
20720
+ const done = () => {
20721
+ if (settled) return;
20722
+ settled = true;
20723
+ clearInterval(pollTimer);
18911
20724
  clearTimeout(timeout);
18912
20725
  if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
18913
- };
18914
- iframe.onerror = () => {
18915
- cleanup();
18916
20726
  resolve();
18917
20727
  };
20728
+ let wasRedirecting = false;
20729
+ pollTimer = setInterval(() => {
20730
+ try {
20731
+ const href = iframe.contentWindow?.location?.href;
20732
+ if (wasRedirecting && href) done();
20733
+ } catch {
20734
+ wasRedirecting = true;
20735
+ }
20736
+ }, 100);
20737
+ const timeout = setTimeout(done, 5e3);
20738
+ iframe.onerror = done;
18918
20739
  document.body.appendChild(iframe);
18919
20740
  });
18920
20741
  } catch (error) {
18921
- console.error("[BackendProvider] logout failed:", error);
20742
+ console.error("[BackendProvider] logout via iframe failed:", error);
20743
+ }
20744
+ }
20745
+ /**
20746
+ * 非 IOA 企业登出:直接 HTTP 请求 /console/logout
20747
+ */
20748
+ async logoutViaHttp() {
20749
+ try {
20750
+ await httpService.get("/console/logout");
20751
+ } catch (error) {
20752
+ console.error("[BackendProvider] logout via http failed:", error);
18922
20753
  }
18923
- localStorage.removeItem(SELECTED_ACCOUNT_KEY);
18924
- accountService.clearAccount();
18925
20754
  }
18926
20755
  /**
18927
20756
  * 批量切换插件状态
@@ -18953,6 +20782,101 @@ var BackendProvider = class {
18953
20782
  return null;
18954
20783
  }
18955
20784
  }
20785
+ /**
20786
+ * 刷新 Token
20787
+ * 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
20788
+ * @returns Promise<Account | null> 刷新后的账号信息
20789
+ */
20790
+ async refreshToken() {
20791
+ console.log("[BackendProvider] Refreshing token...");
20792
+ try {
20793
+ const account = await this.getAccount();
20794
+ console.log("[BackendProvider] Token refreshed, account:", account?.uid);
20795
+ return account;
20796
+ } catch (error) {
20797
+ console.error("[BackendProvider] refreshToken failed:", error);
20798
+ return null;
20799
+ }
20800
+ }
20801
+ /**
20802
+ * 获取仓库分支列表
20803
+ * API 端点: GET /console/as/connector/oauth/{name}/branches
20804
+ *
20805
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
20806
+ * @param params 平台特定的查询参数
20807
+ * @param page 页码,从1开始,0表示不分页获取全部
20808
+ * @param perPage 每页数量,最大100
20809
+ * @returns Promise<OauthBranch[]> 分支列表
20810
+ */
20811
+ async getBranches(connector, params, page = 0, perPage = 100) {
20812
+ return oauthRepositoryService.getBranches(connector, params, page, perPage);
20813
+ }
20814
+ /**
20815
+ * 获取仓库列表
20816
+ * API 端点: GET /console/as/connector/oauth/{name}/repos
20817
+ *
20818
+ * Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
20819
+ * 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
20820
+ *
20821
+ * @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
20822
+ * @param page 页码,从1开始,0表示不分页获取全部
20823
+ * - GitHub 只支持全量数据,必须传 0
20824
+ * - 工蜂和 CNB 依据前端逻辑而定
20825
+ * @param perPage 每页数量,最大100
20826
+ * @returns Promise<ListReposResponse> 仓库列表响应
20827
+ */
20828
+ async getRepositories(connector, page = 0, perPage = 100) {
20829
+ return oauthRepositoryService.getRepositories(connector, page, perPage);
20830
+ }
20831
+ /**
20832
+ * 保存待发送的输入内容到后端
20833
+ * API 端点: POST /api/v1/code-id
20834
+ */
20835
+ async savePendingInput(code) {
20836
+ try {
20837
+ const result = await httpService.post("/api/v1/code-id", { code });
20838
+ return result?.codeId || result?.data?.codeId || null;
20839
+ } catch (e) {
20840
+ console.warn("[BackendProvider] savePendingInput failed:", e);
20841
+ return null;
20842
+ }
20843
+ }
20844
+ /**
20845
+ * 从后端加载待发送的输入内容
20846
+ * API 端点: GET /api/v1/code?id=xxx
20847
+ */
20848
+ async loadPendingInput(codeId) {
20849
+ try {
20850
+ const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
20851
+ return result?.code || result?.data?.code || null;
20852
+ } catch (e) {
20853
+ console.warn("[BackendProvider] loadPendingInput failed:", e);
20854
+ return null;
20855
+ }
20856
+ }
20857
+ /**
20858
+ * 获取每日签到状态
20859
+ * API 端点: POST /billing/meter/checkin-status
20860
+ */
20861
+ async getCheckinStatus() {
20862
+ try {
20863
+ const result = await httpService.post("/billing/meter/checkin-status", {});
20864
+ if (result?.code === 0 && result?.data) return result.data;
20865
+ return null;
20866
+ } catch (error) {
20867
+ console.error("[BackendProvider] getCheckinStatus failed:", error);
20868
+ return null;
20869
+ }
20870
+ }
20871
+ /**
20872
+ * 执行每日签到
20873
+ * API 端点: POST /billing/meter/daily-checkin
20874
+ */
20875
+ async claimDailyCheckin() {
20876
+ const result = await httpService.post("/billing/meter/daily-checkin", {});
20877
+ if (result?.code === 0 && result?.data) return result.data;
20878
+ throw new Error(result?.msg || "Checkin failed");
20879
+ }
18956
20880
  };
18957
20881
  /**
18958
20882
  * 创建 BackendProvider 实例
@@ -18992,8 +20916,12 @@ const BACKEND_REQUEST_TYPES = {
18992
20916
  GET_FILE: "backend:get-file",
18993
20917
  RELOAD_WINDOW: "backend:reload-window",
18994
20918
  CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
20919
+ OPEN_EXTERNAL: "backend:open-external",
18995
20920
  BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
18996
- GET_SUPPORT_SCENES: "backend:get-support-scenes"
20921
+ GET_SUPPORT_SCENES: "backend:get-support-scenes",
20922
+ GET_ACCOUNT_USAGE: "backend:get-account-usage",
20923
+ GET_CHECKIN_STATUS: "backend:get-checkin-status",
20924
+ CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin"
18997
20925
  };
18998
20926
  /**
18999
20927
  * 生成唯一请求 ID
@@ -19040,12 +20968,14 @@ var IPCBackendProvider = class {
19040
20968
  */
19041
20969
  async getAccount() {
19042
20970
  this.log("Getting account via IPC");
20971
+ const startTime = performance.now();
19043
20972
  try {
19044
20973
  const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
20974
+ this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
19045
20975
  accountService.setAccount(account);
19046
20976
  return account;
19047
20977
  } catch (error) {
19048
- this.log("Get account failed:", error);
20978
+ this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
19049
20979
  accountService.setAccount(null);
19050
20980
  return null;
19051
20981
  }
@@ -19287,6 +21217,20 @@ var IPCBackendProvider = class {
19287
21217
  }
19288
21218
  }
19289
21219
  /**
21220
+ * 在外部浏览器中打开链接
21221
+ * IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
21222
+ * @param url 要打开的 URL
21223
+ */
21224
+ async openExternal(url) {
21225
+ this.log("Opening external URL via IPC:", url);
21226
+ try {
21227
+ await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
21228
+ } catch (error) {
21229
+ this.log("Open external request failed:", error);
21230
+ throw error;
21231
+ }
21232
+ }
21233
+ /**
19290
21234
  * 批量切换插件状态
19291
21235
  * IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
19292
21236
  */
@@ -19320,6 +21264,51 @@ var IPCBackendProvider = class {
19320
21264
  }
19321
21265
  }
19322
21266
  /**
21267
+ * 获取账号用量信息(积分/Credits)
21268
+ * IDE 环境: 通过 IPC 实时获取用量信息,每次打开菜单时调用
21269
+ *
21270
+ * 调用链:
21271
+ * 1. agent-ui: IPCBackendProvider.getAccountUsage()
21272
+ * 2. Agent Manager renderer: BackendService.getAccountUsage()
21273
+ * 3. Main Process: codebuddy:getAccountUsage IPC handler
21274
+ * 4. 返回 { usageLeft, usageTotal, editionType, refreshAt } 等字段
21275
+ */
21276
+ async getAccountUsage() {
21277
+ this.log("Getting account usage via IPC");
21278
+ try {
21279
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT_USAGE);
21280
+ } catch (error) {
21281
+ this.log("Get account usage failed:", error);
21282
+ return null;
21283
+ }
21284
+ }
21285
+ /**
21286
+ * 获取每日签到状态
21287
+ * IDE 环境: 通过 IPC 获取签到状态
21288
+ */
21289
+ async getCheckinStatus() {
21290
+ this.log("Getting checkin status via IPC");
21291
+ try {
21292
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_CHECKIN_STATUS);
21293
+ } catch (error) {
21294
+ this.log("Get checkin status failed:", error);
21295
+ return null;
21296
+ }
21297
+ }
21298
+ /**
21299
+ * 执行每日签到
21300
+ * IDE 环境: 通过 IPC 执行签到
21301
+ */
21302
+ async claimDailyCheckin() {
21303
+ this.log("Claiming daily checkin via IPC");
21304
+ try {
21305
+ return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.CLAIM_DAILY_CHECKIN);
21306
+ } catch (error) {
21307
+ this.log("Claim daily checkin failed:", error);
21308
+ throw error;
21309
+ }
21310
+ }
21311
+ /**
19323
21312
  * 调试日志
19324
21313
  */
19325
21314
  log(...args) {