@tencent-ai/cloud-agent-sdk 0.2.12 → 0.2.13-next.06ef89d.20260310
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 +2134 -385
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1467 -127
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1467 -127
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +2127 -378
- package/dist/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.13-next.06ef89d.20260310.tgz +0 -0
- package/package.json +6 -5
- package/dist/tencent-ai-cloud-agent-sdk-0.2.12.tgz +0 -0
package/dist/index.cjs
CHANGED
|
@@ -323,9 +323,9 @@ const ToolOutputSchemas = {
|
|
|
323
323
|
}),
|
|
324
324
|
search_content: zod.z.object({
|
|
325
325
|
type: zod.z.literal("search_content_result"),
|
|
326
|
-
|
|
326
|
+
path: zod.z.string(),
|
|
327
327
|
pattern: zod.z.string(),
|
|
328
|
-
|
|
328
|
+
glob: zod.z.string(),
|
|
329
329
|
matches: zod.z.array(zod.z.object({
|
|
330
330
|
filePath: zod.z.string(),
|
|
331
331
|
content: zod.z.string(),
|
|
@@ -337,7 +337,7 @@ const ToolOutputSchemas = {
|
|
|
337
337
|
totalCount: zod.z.number(),
|
|
338
338
|
hasMore: zod.z.boolean(),
|
|
339
339
|
offset: zod.z.number(),
|
|
340
|
-
|
|
340
|
+
headLimit: zod.z.number(),
|
|
341
341
|
contextBefore: zod.z.number(),
|
|
342
342
|
contextAfter: zod.z.number(),
|
|
343
343
|
contextAround: zod.z.number().optional(),
|
|
@@ -700,7 +700,8 @@ const ExtensionMethod = {
|
|
|
700
700
|
CHECKPOINT: "_codebuddy.ai/checkpoint",
|
|
701
701
|
USAGE: "_codebuddy.ai/usage",
|
|
702
702
|
COMMAND: "_codebuddy.ai/command",
|
|
703
|
-
AUTH_URL: "_codebuddy.ai/authUrl"
|
|
703
|
+
AUTH_URL: "_codebuddy.ai/authUrl",
|
|
704
|
+
FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot"
|
|
704
705
|
};
|
|
705
706
|
/**
|
|
706
707
|
* All known extension methods
|
|
@@ -711,7 +712,8 @@ const KNOWN_EXTENSIONS = [
|
|
|
711
712
|
ExtensionMethod.CHECKPOINT,
|
|
712
713
|
ExtensionMethod.USAGE,
|
|
713
714
|
ExtensionMethod.COMMAND,
|
|
714
|
-
ExtensionMethod.AUTH_URL
|
|
715
|
+
ExtensionMethod.AUTH_URL,
|
|
716
|
+
ExtensionMethod.FILE_HISTORY_SNAPSHOT
|
|
715
717
|
];
|
|
716
718
|
|
|
717
719
|
//#endregion
|
|
@@ -762,7 +764,7 @@ function parseSSELine(line, currentEvent) {
|
|
|
762
764
|
};
|
|
763
765
|
}
|
|
764
766
|
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;
|
|
767
|
+
const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, connectionTimeout = 3e4, postTimeout = 3e4, backpressure = {} } = options;
|
|
766
768
|
const { enabled: reconnectEnabled = true, initialDelay = 1e3, maxDelay = 3e4, maxRetries = Infinity, jitter: jitterEnabled = true } = reconnect;
|
|
767
769
|
const { highWaterMark = 100, lowWaterMark = 50, pauseTimeout = 5e3 } = backpressure;
|
|
768
770
|
let connectionId;
|
|
@@ -973,11 +975,21 @@ function streamableHttp(options) {
|
|
|
973
975
|
const headers = buildHeaders();
|
|
974
976
|
headers["Accept"] = "text/event-stream";
|
|
975
977
|
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
976
|
-
const
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
});
|
|
978
|
+
const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
|
|
979
|
+
const connectController = new AbortController();
|
|
980
|
+
const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
|
|
981
|
+
if (externalSignal) externalSignal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
982
|
+
abortController.signal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
983
|
+
let response;
|
|
984
|
+
try {
|
|
985
|
+
response = await customFetch(endpoint, {
|
|
986
|
+
method: "GET",
|
|
987
|
+
headers,
|
|
988
|
+
signal: connectController.signal
|
|
989
|
+
});
|
|
990
|
+
} finally {
|
|
991
|
+
clearTimeout(connectTimer);
|
|
992
|
+
}
|
|
981
993
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
982
994
|
const newConnectionId = response.headers.get("Acp-Connection-Id");
|
|
983
995
|
if (!newConnectionId) throw new Error("Server did not return Acp-Connection-Id header");
|
|
@@ -1913,6 +1925,9 @@ var StreamableHttpClient = class {
|
|
|
1913
1925
|
headers: this.options.headers,
|
|
1914
1926
|
reconnect: this.options.reconnect,
|
|
1915
1927
|
fetch: this.options.fetch,
|
|
1928
|
+
heartbeatTimeout: this.options.heartbeatTimeout,
|
|
1929
|
+
postTimeout: this.options.postTimeout,
|
|
1930
|
+
connectionTimeout: this.options.connectionTimeout,
|
|
1916
1931
|
onConnect: (connectionId) => {
|
|
1917
1932
|
this.options.logger?.debug(`Transport connected: ${connectionId}`);
|
|
1918
1933
|
},
|
|
@@ -1987,10 +2002,6 @@ var StreamableHttpClient = class {
|
|
|
1987
2002
|
},
|
|
1988
2003
|
requestPermission: async (params) => this.handleRequestPermission(params),
|
|
1989
2004
|
extNotification: async (method, params) => {
|
|
1990
|
-
console.log("[ACP-Client] extNotification callback invoked:", {
|
|
1991
|
-
method,
|
|
1992
|
-
paramsKeys: Object.keys(params)
|
|
1993
|
-
});
|
|
1994
2005
|
await this.handleExtNotification(method, params);
|
|
1995
2006
|
},
|
|
1996
2007
|
extMethod: async (method, params) => this.handleExtMethod(method, params)
|
|
@@ -1998,10 +2009,15 @@ var StreamableHttpClient = class {
|
|
|
1998
2009
|
}
|
|
1999
2010
|
/**
|
|
2000
2011
|
* Create a new session
|
|
2012
|
+
*
|
|
2013
|
+
* Retries on transient network errors (e.g., proxy connection reset)
|
|
2014
|
+
* since session/new is idempotent and safe to retry.
|
|
2001
2015
|
*/
|
|
2002
2016
|
async createSession(cwd) {
|
|
2003
2017
|
this.ensureInitialized("createSession");
|
|
2004
|
-
|
|
2018
|
+
const maxRetries = 2;
|
|
2019
|
+
let lastError;
|
|
2020
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
2005
2021
|
const response = await this.connection.newSession({
|
|
2006
2022
|
cwd,
|
|
2007
2023
|
mcpServers: []
|
|
@@ -2009,8 +2025,16 @@ var StreamableHttpClient = class {
|
|
|
2009
2025
|
this.options.logger?.info(`Session created: ${response.sessionId}`);
|
|
2010
2026
|
return response;
|
|
2011
2027
|
} catch (err) {
|
|
2012
|
-
|
|
2028
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
2029
|
+
if (attempt < maxRetries && isRetryableNetworkError(err)) {
|
|
2030
|
+
const delay = 500 * Math.pow(2, attempt);
|
|
2031
|
+
this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
|
|
2032
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2033
|
+
continue;
|
|
2034
|
+
}
|
|
2035
|
+
throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
|
|
2013
2036
|
}
|
|
2037
|
+
throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
|
|
2014
2038
|
}
|
|
2015
2039
|
/**
|
|
2016
2040
|
* Load an existing session
|
|
@@ -2234,6 +2258,18 @@ var StreamableHttpClient = class {
|
|
|
2234
2258
|
if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
|
|
2235
2259
|
}
|
|
2236
2260
|
};
|
|
2261
|
+
/**
|
|
2262
|
+
* Check if an error is a retryable network-level error.
|
|
2263
|
+
* Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
|
|
2264
|
+
*/
|
|
2265
|
+
function isRetryableNetworkError(error) {
|
|
2266
|
+
if (error instanceof TypeError) return true;
|
|
2267
|
+
if (error instanceof Error) {
|
|
2268
|
+
const msg = error.message.toLowerCase();
|
|
2269
|
+
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");
|
|
2270
|
+
}
|
|
2271
|
+
return false;
|
|
2272
|
+
}
|
|
2237
2273
|
|
|
2238
2274
|
//#endregion
|
|
2239
2275
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
|
|
@@ -2269,7 +2305,7 @@ var CloudAgentConnection = class {
|
|
|
2269
2305
|
fetch: config.fetch,
|
|
2270
2306
|
clientCapabilities: config.clientCapabilities,
|
|
2271
2307
|
onSessionUpdate: (update) => {
|
|
2272
|
-
if (!this._isStreaming) this.emit("sessionUpdate", update);
|
|
2308
|
+
if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
|
|
2273
2309
|
},
|
|
2274
2310
|
onArtifact: (artifact, event) => {
|
|
2275
2311
|
console.log("[CloudConnection] onArtifact callback:", {
|
|
@@ -2283,10 +2319,41 @@ var CloudAgentConnection = class {
|
|
|
2283
2319
|
},
|
|
2284
2320
|
onUsageUpdate: (usage) => {
|
|
2285
2321
|
this.emit("usageUpdate", usage);
|
|
2322
|
+
},
|
|
2323
|
+
onExtNotification: (method, params) => {
|
|
2324
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2325
|
+
method,
|
|
2326
|
+
paramsKeys: Object.keys(params)
|
|
2327
|
+
});
|
|
2328
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2329
|
+
const action = params.action;
|
|
2330
|
+
const commandParams = params.params;
|
|
2331
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2332
|
+
action,
|
|
2333
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2334
|
+
});
|
|
2335
|
+
this.emit("command", {
|
|
2336
|
+
action,
|
|
2337
|
+
params: commandParams
|
|
2338
|
+
});
|
|
2339
|
+
}
|
|
2286
2340
|
}
|
|
2287
2341
|
});
|
|
2288
2342
|
this.setupEventForwarding();
|
|
2289
2343
|
}
|
|
2344
|
+
/**
|
|
2345
|
+
* Check whether a notification belongs to this connection's own session.
|
|
2346
|
+
*
|
|
2347
|
+
* CloudConnection.createSession() overrides sessionId to agentId, so the
|
|
2348
|
+
* rest of the client stack uses agentId as the canonical session
|
|
2349
|
+
* identifier. Notifications whose sessionId differs from agentId
|
|
2350
|
+
* originate from sub-agent sessions running inside the same sandbox and
|
|
2351
|
+
* should be silently ignored at this layer — the adapter layer handles
|
|
2352
|
+
* sub-agent messages independently via parentToolUseId in _meta.
|
|
2353
|
+
*/
|
|
2354
|
+
isOwnSessionNotification(notification) {
|
|
2355
|
+
return notification.sessionId === this.agentId;
|
|
2356
|
+
}
|
|
2290
2357
|
setupEventForwarding() {
|
|
2291
2358
|
this.client.on("connecting", () => {
|
|
2292
2359
|
this.emit("connecting", void 0);
|
|
@@ -2411,7 +2478,7 @@ var CloudAgentConnection = class {
|
|
|
2411
2478
|
}
|
|
2412
2479
|
async createSession(params) {
|
|
2413
2480
|
return {
|
|
2414
|
-
...await this.client.
|
|
2481
|
+
...await this.client.createSession(this.cwd),
|
|
2415
2482
|
sessionId: this.agentId
|
|
2416
2483
|
};
|
|
2417
2484
|
}
|
|
@@ -2447,6 +2514,7 @@ var CloudAgentConnection = class {
|
|
|
2447
2514
|
let resolveUpdate = null;
|
|
2448
2515
|
let done = false;
|
|
2449
2516
|
const listener = (update) => {
|
|
2517
|
+
if (!this.isOwnSessionNotification(update)) return;
|
|
2450
2518
|
if (resolveUpdate) {
|
|
2451
2519
|
resolveUpdate(update);
|
|
2452
2520
|
resolveUpdate = null;
|
|
@@ -2529,6 +2597,16 @@ var CloudAgentConnection = class {
|
|
|
2529
2597
|
get sessionConnectionInfo() {
|
|
2530
2598
|
return this._sessionConnectionInfo;
|
|
2531
2599
|
}
|
|
2600
|
+
async reportTelemetry(eventName, payload) {
|
|
2601
|
+
try {
|
|
2602
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2603
|
+
eventName,
|
|
2604
|
+
payload
|
|
2605
|
+
});
|
|
2606
|
+
} catch (error) {
|
|
2607
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2608
|
+
}
|
|
2609
|
+
}
|
|
2532
2610
|
async extMethod(method, params) {
|
|
2533
2611
|
return this.client.extMethod(method, params);
|
|
2534
2612
|
}
|
|
@@ -3131,7 +3209,7 @@ var utils_default = {
|
|
|
3131
3209
|
*
|
|
3132
3210
|
* @returns {Error} The created error.
|
|
3133
3211
|
*/
|
|
3134
|
-
function AxiosError(message, code, config, request, response) {
|
|
3212
|
+
function AxiosError$1(message, code, config, request, response) {
|
|
3135
3213
|
Error.call(this);
|
|
3136
3214
|
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
3137
3215
|
else this.stack = (/* @__PURE__ */ new Error()).stack;
|
|
@@ -3145,7 +3223,7 @@ function AxiosError(message, code, config, request, response) {
|
|
|
3145
3223
|
this.status = response.status ? response.status : null;
|
|
3146
3224
|
}
|
|
3147
3225
|
}
|
|
3148
|
-
utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
3226
|
+
utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
|
|
3149
3227
|
return {
|
|
3150
3228
|
message: this.message,
|
|
3151
3229
|
name: this.name,
|
|
@@ -3160,7 +3238,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
|
3160
3238
|
status: this.status
|
|
3161
3239
|
};
|
|
3162
3240
|
} });
|
|
3163
|
-
const prototype$1 = AxiosError.prototype;
|
|
3241
|
+
const prototype$1 = AxiosError$1.prototype;
|
|
3164
3242
|
const descriptors = {};
|
|
3165
3243
|
[
|
|
3166
3244
|
"ERR_BAD_OPTION_VALUE",
|
|
@@ -3178,22 +3256,22 @@ const descriptors = {};
|
|
|
3178
3256
|
].forEach((code) => {
|
|
3179
3257
|
descriptors[code] = { value: code };
|
|
3180
3258
|
});
|
|
3181
|
-
Object.defineProperties(AxiosError, descriptors);
|
|
3259
|
+
Object.defineProperties(AxiosError$1, descriptors);
|
|
3182
3260
|
Object.defineProperty(prototype$1, "isAxiosError", { value: true });
|
|
3183
|
-
AxiosError.from = (error, code, config, request, response, customProps) => {
|
|
3261
|
+
AxiosError$1.from = (error, code, config, request, response, customProps) => {
|
|
3184
3262
|
const axiosError = Object.create(prototype$1);
|
|
3185
3263
|
utils_default.toFlatObject(error, axiosError, function filter(obj) {
|
|
3186
3264
|
return obj !== Error.prototype;
|
|
3187
3265
|
}, (prop) => {
|
|
3188
3266
|
return prop !== "isAxiosError";
|
|
3189
3267
|
});
|
|
3190
|
-
AxiosError.call(axiosError, error.message, code, config, request, response);
|
|
3268
|
+
AxiosError$1.call(axiosError, error.message, code, config, request, response);
|
|
3191
3269
|
axiosError.cause = error;
|
|
3192
3270
|
axiosError.name = error.name;
|
|
3193
3271
|
customProps && Object.assign(axiosError, customProps);
|
|
3194
3272
|
return axiosError;
|
|
3195
3273
|
};
|
|
3196
|
-
var AxiosError_default = AxiosError;
|
|
3274
|
+
var AxiosError_default = AxiosError$1;
|
|
3197
3275
|
|
|
3198
3276
|
//#endregion
|
|
3199
3277
|
//#region ../../node_modules/delayed-stream/lib/delayed_stream.js
|
|
@@ -11920,7 +11998,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
|
|
|
11920
11998
|
*
|
|
11921
11999
|
* @returns
|
|
11922
12000
|
*/
|
|
11923
|
-
function toFormData(obj, formData, options) {
|
|
12001
|
+
function toFormData$1(obj, formData, options) {
|
|
11924
12002
|
if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
|
|
11925
12003
|
formData = formData || new (FormData_default || FormData)();
|
|
11926
12004
|
options = utils_default.toFlatObject(options, {
|
|
@@ -11991,7 +12069,7 @@ function toFormData(obj, formData, options) {
|
|
|
11991
12069
|
build(obj);
|
|
11992
12070
|
return formData;
|
|
11993
12071
|
}
|
|
11994
|
-
var toFormData_default = toFormData;
|
|
12072
|
+
var toFormData_default = toFormData$1;
|
|
11995
12073
|
|
|
11996
12074
|
//#endregion
|
|
11997
12075
|
//#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
|
|
@@ -12515,7 +12593,7 @@ function buildAccessors(obj, header) {
|
|
|
12515
12593
|
});
|
|
12516
12594
|
});
|
|
12517
12595
|
}
|
|
12518
|
-
var AxiosHeaders = class {
|
|
12596
|
+
var AxiosHeaders$1 = class {
|
|
12519
12597
|
constructor(headers) {
|
|
12520
12598
|
headers && this.set(headers);
|
|
12521
12599
|
}
|
|
@@ -12653,7 +12731,7 @@ var AxiosHeaders = class {
|
|
|
12653
12731
|
return this;
|
|
12654
12732
|
}
|
|
12655
12733
|
};
|
|
12656
|
-
AxiosHeaders.accessor([
|
|
12734
|
+
AxiosHeaders$1.accessor([
|
|
12657
12735
|
"Content-Type",
|
|
12658
12736
|
"Content-Length",
|
|
12659
12737
|
"Accept",
|
|
@@ -12661,7 +12739,7 @@ AxiosHeaders.accessor([
|
|
|
12661
12739
|
"User-Agent",
|
|
12662
12740
|
"Authorization"
|
|
12663
12741
|
]);
|
|
12664
|
-
utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
12742
|
+
utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
|
|
12665
12743
|
let mapped = key[0].toUpperCase() + key.slice(1);
|
|
12666
12744
|
return {
|
|
12667
12745
|
get: () => value,
|
|
@@ -12670,8 +12748,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
|
12670
12748
|
}
|
|
12671
12749
|
};
|
|
12672
12750
|
});
|
|
12673
|
-
utils_default.freezeMethods(AxiosHeaders);
|
|
12674
|
-
var AxiosHeaders_default = AxiosHeaders;
|
|
12751
|
+
utils_default.freezeMethods(AxiosHeaders$1);
|
|
12752
|
+
var AxiosHeaders_default = AxiosHeaders$1;
|
|
12675
12753
|
|
|
12676
12754
|
//#endregion
|
|
12677
12755
|
//#region ../agent-provider/node_modules/axios/lib/core/transformData.js
|
|
@@ -12697,7 +12775,7 @@ function transformData(fns, response) {
|
|
|
12697
12775
|
|
|
12698
12776
|
//#endregion
|
|
12699
12777
|
//#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
|
|
12700
|
-
function isCancel(value) {
|
|
12778
|
+
function isCancel$1(value) {
|
|
12701
12779
|
return !!(value && value.__CANCEL__);
|
|
12702
12780
|
}
|
|
12703
12781
|
|
|
@@ -12712,12 +12790,12 @@ function isCancel(value) {
|
|
|
12712
12790
|
*
|
|
12713
12791
|
* @returns {CanceledError} The created error.
|
|
12714
12792
|
*/
|
|
12715
|
-
function CanceledError(message, config, request) {
|
|
12793
|
+
function CanceledError$1(message, config, request) {
|
|
12716
12794
|
AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
|
|
12717
12795
|
this.name = "CanceledError";
|
|
12718
12796
|
}
|
|
12719
|
-
utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
|
|
12720
|
-
var CanceledError_default = CanceledError;
|
|
12797
|
+
utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
|
|
12798
|
+
var CanceledError_default = CanceledError$1;
|
|
12721
12799
|
|
|
12722
12800
|
//#endregion
|
|
12723
12801
|
//#region ../agent-provider/node_modules/axios/lib/core/settle.js
|
|
@@ -14119,7 +14197,9 @@ var require_follow_redirects = /* @__PURE__ */ __commonJSMin(((exports, module)
|
|
|
14119
14197
|
|
|
14120
14198
|
//#endregion
|
|
14121
14199
|
//#region ../agent-provider/node_modules/axios/lib/env/data.js
|
|
14122
|
-
|
|
14200
|
+
var import_follow_redirects = /* @__PURE__ */ __toESM(require_follow_redirects(), 1);
|
|
14201
|
+
var import_proxy_from_env = /* @__PURE__ */ __toESM(require_proxy_from_env(), 1);
|
|
14202
|
+
const VERSION$1 = "1.10.0";
|
|
14123
14203
|
|
|
14124
14204
|
//#endregion
|
|
14125
14205
|
//#region ../agent-provider/node_modules/axios/lib/helpers/parseProtocol.js
|
|
@@ -14482,8 +14562,6 @@ const asyncDecorator = (fn) => (...args) => utils_default.asap(() => fn(...args)
|
|
|
14482
14562
|
|
|
14483
14563
|
//#endregion
|
|
14484
14564
|
//#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
14565
|
const zlibOptions = {
|
|
14488
14566
|
flush: zlib.default.constants.Z_SYNC_FLUSH,
|
|
14489
14567
|
finishFlush: zlib.default.constants.Z_SYNC_FLUSH
|
|
@@ -14649,7 +14727,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
|
|
|
14649
14727
|
}
|
|
14650
14728
|
if (supportedProtocols.indexOf(protocol) === -1) return reject(new AxiosError_default("Unsupported protocol " + protocol, AxiosError_default.ERR_BAD_REQUEST, config));
|
|
14651
14729
|
const headers = AxiosHeaders_default.from(config.headers).normalize();
|
|
14652
|
-
headers.set("User-Agent", "axios/" + VERSION, false);
|
|
14730
|
+
headers.set("User-Agent", "axios/" + VERSION$1, false);
|
|
14653
14731
|
const { onUploadProgress, onDownloadProgress } = config;
|
|
14654
14732
|
const maxRate = config.maxRate;
|
|
14655
14733
|
let maxUploadRate = void 0;
|
|
@@ -14659,7 +14737,7 @@ var http_default = isHttpAdapterSupported && function httpAdapter(config) {
|
|
|
14659
14737
|
data = formDataToStream_default(data, (formHeaders) => {
|
|
14660
14738
|
headers.set(formHeaders);
|
|
14661
14739
|
}, {
|
|
14662
|
-
tag: `axios-${VERSION}-boundary`,
|
|
14740
|
+
tag: `axios-${VERSION$1}-boundary`,
|
|
14663
14741
|
boundary: userBoundary && userBoundary[1] || void 0
|
|
14664
14742
|
});
|
|
14665
14743
|
} else if (utils_default.isFormData(data) && utils_default.isFunction(data.getHeaders)) {
|
|
@@ -14924,7 +15002,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
|
|
|
14924
15002
|
*
|
|
14925
15003
|
* @returns {Object} New object resulting from merging config2 to config1
|
|
14926
15004
|
*/
|
|
14927
|
-
function mergeConfig(config1, config2) {
|
|
15005
|
+
function mergeConfig$1(config1, config2) {
|
|
14928
15006
|
config2 = config2 || {};
|
|
14929
15007
|
const config = {};
|
|
14930
15008
|
function getMergedValue(target, source, prop, caseless) {
|
|
@@ -14990,7 +15068,7 @@ function mergeConfig(config1, config2) {
|
|
|
14990
15068
|
//#endregion
|
|
14991
15069
|
//#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
|
|
14992
15070
|
var resolveConfig_default = (config) => {
|
|
14993
|
-
const newConfig = mergeConfig({}, config);
|
|
15071
|
+
const newConfig = mergeConfig$1({}, config);
|
|
14994
15072
|
let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
|
|
14995
15073
|
newConfig.headers = headers = AxiosHeaders_default.from(headers);
|
|
14996
15074
|
newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
@@ -15421,7 +15499,7 @@ function dispatchRequest(config) {
|
|
|
15421
15499
|
response.headers = AxiosHeaders_default.from(response.headers);
|
|
15422
15500
|
return response;
|
|
15423
15501
|
}, function onAdapterRejection(reason) {
|
|
15424
|
-
if (!isCancel(reason)) {
|
|
15502
|
+
if (!isCancel$1(reason)) {
|
|
15425
15503
|
throwIfCancellationRequested(config);
|
|
15426
15504
|
if (reason && reason.response) {
|
|
15427
15505
|
reason.response.data = transformData.call(config, config.transformResponse, reason.response);
|
|
@@ -15459,7 +15537,7 @@ const deprecatedWarnings = {};
|
|
|
15459
15537
|
*/
|
|
15460
15538
|
validators$1.transitional = function transitional(validator, version, message) {
|
|
15461
15539
|
function formatMessage(opt, desc) {
|
|
15462
|
-
return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
15540
|
+
return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
15463
15541
|
}
|
|
15464
15542
|
return (value, opt, opts) => {
|
|
15465
15543
|
if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
|
|
@@ -15516,7 +15594,7 @@ const validators = validator_default.validators;
|
|
|
15516
15594
|
*
|
|
15517
15595
|
* @return {Axios} A new instance of Axios
|
|
15518
15596
|
*/
|
|
15519
|
-
var Axios = class {
|
|
15597
|
+
var Axios$1 = class {
|
|
15520
15598
|
constructor(instanceConfig) {
|
|
15521
15599
|
this.defaults = instanceConfig || {};
|
|
15522
15600
|
this.interceptors = {
|
|
@@ -15553,7 +15631,7 @@ var Axios = class {
|
|
|
15553
15631
|
config = config || {};
|
|
15554
15632
|
config.url = configOrUrl;
|
|
15555
15633
|
} else config = configOrUrl || {};
|
|
15556
|
-
config = mergeConfig(this.defaults, config);
|
|
15634
|
+
config = mergeConfig$1(this.defaults, config);
|
|
15557
15635
|
const { transitional, paramsSerializer, headers } = config;
|
|
15558
15636
|
if (transitional !== void 0) validator_default.assertOptions(transitional, {
|
|
15559
15637
|
silentJSONParsing: validators.transitional(validators.boolean),
|
|
@@ -15632,7 +15710,7 @@ var Axios = class {
|
|
|
15632
15710
|
return promise;
|
|
15633
15711
|
}
|
|
15634
15712
|
getUri(config) {
|
|
15635
|
-
config = mergeConfig(this.defaults, config);
|
|
15713
|
+
config = mergeConfig$1(this.defaults, config);
|
|
15636
15714
|
return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
15637
15715
|
}
|
|
15638
15716
|
};
|
|
@@ -15642,8 +15720,8 @@ utils_default.forEach([
|
|
|
15642
15720
|
"head",
|
|
15643
15721
|
"options"
|
|
15644
15722
|
], function forEachMethodNoData(method) {
|
|
15645
|
-
Axios.prototype[method] = function(url, config) {
|
|
15646
|
-
return this.request(mergeConfig(config || {}, {
|
|
15723
|
+
Axios$1.prototype[method] = function(url, config) {
|
|
15724
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
15647
15725
|
method,
|
|
15648
15726
|
url,
|
|
15649
15727
|
data: (config || {}).data
|
|
@@ -15657,7 +15735,7 @@ utils_default.forEach([
|
|
|
15657
15735
|
], function forEachMethodWithData(method) {
|
|
15658
15736
|
function generateHTTPMethod(isForm) {
|
|
15659
15737
|
return function httpMethod(url, data, config) {
|
|
15660
|
-
return this.request(mergeConfig(config || {}, {
|
|
15738
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
15661
15739
|
method,
|
|
15662
15740
|
headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
|
|
15663
15741
|
url,
|
|
@@ -15665,10 +15743,10 @@ utils_default.forEach([
|
|
|
15665
15743
|
}));
|
|
15666
15744
|
};
|
|
15667
15745
|
}
|
|
15668
|
-
Axios.prototype[method] = generateHTTPMethod();
|
|
15669
|
-
Axios.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
15746
|
+
Axios$1.prototype[method] = generateHTTPMethod();
|
|
15747
|
+
Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
15670
15748
|
});
|
|
15671
|
-
var Axios_default = Axios;
|
|
15749
|
+
var Axios_default = Axios$1;
|
|
15672
15750
|
|
|
15673
15751
|
//#endregion
|
|
15674
15752
|
//#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
|
|
@@ -15679,7 +15757,7 @@ var Axios_default = Axios;
|
|
|
15679
15757
|
*
|
|
15680
15758
|
* @returns {CancelToken}
|
|
15681
15759
|
*/
|
|
15682
|
-
var CancelToken = class CancelToken {
|
|
15760
|
+
var CancelToken$1 = class CancelToken$1 {
|
|
15683
15761
|
constructor(executor) {
|
|
15684
15762
|
if (typeof executor !== "function") throw new TypeError("executor must be a function.");
|
|
15685
15763
|
let resolvePromise;
|
|
@@ -15751,14 +15829,14 @@ var CancelToken = class CancelToken {
|
|
|
15751
15829
|
static source() {
|
|
15752
15830
|
let cancel;
|
|
15753
15831
|
return {
|
|
15754
|
-
token: new CancelToken(function executor(c) {
|
|
15832
|
+
token: new CancelToken$1(function executor(c) {
|
|
15755
15833
|
cancel = c;
|
|
15756
15834
|
}),
|
|
15757
15835
|
cancel
|
|
15758
15836
|
};
|
|
15759
15837
|
}
|
|
15760
15838
|
};
|
|
15761
|
-
var CancelToken_default = CancelToken;
|
|
15839
|
+
var CancelToken_default = CancelToken$1;
|
|
15762
15840
|
|
|
15763
15841
|
//#endregion
|
|
15764
15842
|
//#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
|
|
@@ -15783,7 +15861,7 @@ var CancelToken_default = CancelToken;
|
|
|
15783
15861
|
*
|
|
15784
15862
|
* @returns {Function}
|
|
15785
15863
|
*/
|
|
15786
|
-
function spread(callback) {
|
|
15864
|
+
function spread$1(callback) {
|
|
15787
15865
|
return function wrap(arr) {
|
|
15788
15866
|
return callback.apply(null, arr);
|
|
15789
15867
|
};
|
|
@@ -15798,13 +15876,13 @@ function spread(callback) {
|
|
|
15798
15876
|
*
|
|
15799
15877
|
* @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
|
|
15800
15878
|
*/
|
|
15801
|
-
function isAxiosError(payload) {
|
|
15879
|
+
function isAxiosError$1(payload) {
|
|
15802
15880
|
return utils_default.isObject(payload) && payload.isAxiosError === true;
|
|
15803
15881
|
}
|
|
15804
15882
|
|
|
15805
15883
|
//#endregion
|
|
15806
15884
|
//#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
|
|
15807
|
-
const HttpStatusCode = {
|
|
15885
|
+
const HttpStatusCode$1 = {
|
|
15808
15886
|
Continue: 100,
|
|
15809
15887
|
SwitchingProtocols: 101,
|
|
15810
15888
|
Processing: 102,
|
|
@@ -15869,10 +15947,10 @@ const HttpStatusCode = {
|
|
|
15869
15947
|
NotExtended: 510,
|
|
15870
15948
|
NetworkAuthenticationRequired: 511
|
|
15871
15949
|
};
|
|
15872
|
-
Object.entries(HttpStatusCode).forEach(([key, value]) => {
|
|
15873
|
-
HttpStatusCode[value] = key;
|
|
15950
|
+
Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
|
|
15951
|
+
HttpStatusCode$1[value] = key;
|
|
15874
15952
|
});
|
|
15875
|
-
var HttpStatusCode_default = HttpStatusCode;
|
|
15953
|
+
var HttpStatusCode_default = HttpStatusCode$1;
|
|
15876
15954
|
|
|
15877
15955
|
//#endregion
|
|
15878
15956
|
//#region ../agent-provider/node_modules/axios/lib/axios.js
|
|
@@ -15889,7 +15967,7 @@ function createInstance(defaultConfig) {
|
|
|
15889
15967
|
utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
|
|
15890
15968
|
utils_default.extend(instance, context, null, { allOwnKeys: true });
|
|
15891
15969
|
instance.create = function create(instanceConfig) {
|
|
15892
|
-
return createInstance(mergeConfig(defaultConfig, instanceConfig));
|
|
15970
|
+
return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
|
|
15893
15971
|
};
|
|
15894
15972
|
return instance;
|
|
15895
15973
|
}
|
|
@@ -15897,17 +15975,17 @@ const axios = createInstance(defaults_default);
|
|
|
15897
15975
|
axios.Axios = Axios_default;
|
|
15898
15976
|
axios.CanceledError = CanceledError_default;
|
|
15899
15977
|
axios.CancelToken = CancelToken_default;
|
|
15900
|
-
axios.isCancel = isCancel;
|
|
15901
|
-
axios.VERSION = VERSION;
|
|
15978
|
+
axios.isCancel = isCancel$1;
|
|
15979
|
+
axios.VERSION = VERSION$1;
|
|
15902
15980
|
axios.toFormData = toFormData_default;
|
|
15903
15981
|
axios.AxiosError = AxiosError_default;
|
|
15904
15982
|
axios.Cancel = axios.CanceledError;
|
|
15905
15983
|
axios.all = function all(promises) {
|
|
15906
15984
|
return Promise.all(promises);
|
|
15907
15985
|
};
|
|
15908
|
-
axios.spread = spread;
|
|
15909
|
-
axios.isAxiosError = isAxiosError;
|
|
15910
|
-
axios.mergeConfig = mergeConfig;
|
|
15986
|
+
axios.spread = spread$1;
|
|
15987
|
+
axios.isAxiosError = isAxiosError$1;
|
|
15988
|
+
axios.mergeConfig = mergeConfig$1;
|
|
15911
15989
|
axios.AxiosHeaders = AxiosHeaders_default;
|
|
15912
15990
|
axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
|
|
15913
15991
|
axios.getAdapter = adapters_default.getAdapter;
|
|
@@ -15915,6 +15993,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
|
|
|
15915
15993
|
axios.default = axios;
|
|
15916
15994
|
var axios_default = axios;
|
|
15917
15995
|
|
|
15996
|
+
//#endregion
|
|
15997
|
+
//#region ../agent-provider/node_modules/axios/index.js
|
|
15998
|
+
const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
|
|
15999
|
+
|
|
15918
16000
|
//#endregion
|
|
15919
16001
|
//#region ../agent-provider/src/http/http-service.ts
|
|
15920
16002
|
/**
|
|
@@ -15923,7 +16005,7 @@ var axios_default = axios;
|
|
|
15923
16005
|
* 特性:
|
|
15924
16006
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
15925
16007
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
15926
|
-
* - 统一 401
|
|
16008
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
15927
16009
|
* - 自动携带凭证(withCredentials)
|
|
15928
16010
|
* - 类型安全
|
|
15929
16011
|
*
|
|
@@ -15963,6 +16045,8 @@ var HttpService = class HttpService {
|
|
|
15963
16045
|
*/
|
|
15964
16046
|
constructor(config = {}) {
|
|
15965
16047
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
16048
|
+
this.isRefreshing = false;
|
|
16049
|
+
this.refreshSubscribers = [];
|
|
15966
16050
|
this.config = config;
|
|
15967
16051
|
this.axiosInstance = axios_default.create({
|
|
15968
16052
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -16003,18 +16087,54 @@ var HttpService = class HttpService {
|
|
|
16003
16087
|
}, (error) => Promise.reject(error));
|
|
16004
16088
|
}
|
|
16005
16089
|
/**
|
|
16006
|
-
* 注册默认响应拦截器(处理 401
|
|
16090
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
16007
16091
|
*/
|
|
16008
16092
|
registerDefaultResponseInterceptor() {
|
|
16009
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
16010
|
-
|
|
16011
|
-
|
|
16012
|
-
|
|
16093
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
16094
|
+
const originalRequest = error.config;
|
|
16095
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
16096
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
16097
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
16098
|
+
return Promise.reject(error);
|
|
16099
|
+
}
|
|
16100
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
16101
|
+
originalRequest._retry = true;
|
|
16102
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
16103
|
+
this.refreshSubscribers.push((success) => {
|
|
16104
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
16105
|
+
else reject(error);
|
|
16106
|
+
});
|
|
16107
|
+
});
|
|
16108
|
+
this.isRefreshing = true;
|
|
16109
|
+
try {
|
|
16110
|
+
await this.triggerUnauthorizedCallbacks();
|
|
16111
|
+
this.onRefreshSuccess();
|
|
16112
|
+
return this.axiosInstance.request(originalRequest);
|
|
16113
|
+
} catch (refreshError) {
|
|
16114
|
+
this.onRefreshFailure();
|
|
16115
|
+
return Promise.reject(error);
|
|
16116
|
+
} finally {
|
|
16117
|
+
this.isRefreshing = false;
|
|
16118
|
+
}
|
|
16013
16119
|
}
|
|
16014
16120
|
return Promise.reject(error);
|
|
16015
16121
|
});
|
|
16016
16122
|
}
|
|
16017
16123
|
/**
|
|
16124
|
+
* token 刷新成功,通知所有等待的请求
|
|
16125
|
+
*/
|
|
16126
|
+
onRefreshSuccess() {
|
|
16127
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
16128
|
+
this.refreshSubscribers = [];
|
|
16129
|
+
}
|
|
16130
|
+
/**
|
|
16131
|
+
* token 刷新失败,通知所有等待的请求
|
|
16132
|
+
*/
|
|
16133
|
+
onRefreshFailure() {
|
|
16134
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
16135
|
+
this.refreshSubscribers = [];
|
|
16136
|
+
}
|
|
16137
|
+
/**
|
|
16018
16138
|
* 注册请求拦截器
|
|
16019
16139
|
* @param onFulfilled 请求成功拦截器
|
|
16020
16140
|
* @param onRejected 请求失败拦截器
|
|
@@ -16091,16 +16211,18 @@ var HttpService = class HttpService {
|
|
|
16091
16211
|
this.unauthorizedCallbacks.delete(callback);
|
|
16092
16212
|
}
|
|
16093
16213
|
/**
|
|
16094
|
-
* 触发所有 401
|
|
16214
|
+
* 触发所有 401 回调并等待完成
|
|
16215
|
+
* @returns Promise,等待所有回调完成
|
|
16216
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
16095
16217
|
*/
|
|
16096
|
-
triggerUnauthorizedCallbacks() {
|
|
16097
|
-
this.unauthorizedCallbacks
|
|
16098
|
-
|
|
16099
|
-
|
|
16100
|
-
|
|
16101
|
-
|
|
16102
|
-
|
|
16103
|
-
}
|
|
16218
|
+
async triggerUnauthorizedCallbacks() {
|
|
16219
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
16220
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
16221
|
+
if (failedResults.length > 0) {
|
|
16222
|
+
const errors = failedResults.map((r) => r.reason);
|
|
16223
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
16224
|
+
throw errors[0];
|
|
16225
|
+
}
|
|
16104
16226
|
}
|
|
16105
16227
|
/**
|
|
16106
16228
|
* 更新 authToken
|
|
@@ -16212,6 +16334,7 @@ var AccountService = class {
|
|
|
16212
16334
|
this.initPromise = null;
|
|
16213
16335
|
this.initResolve = null;
|
|
16214
16336
|
this.requestInterceptorId = null;
|
|
16337
|
+
this.crossTabBroadcaster = null;
|
|
16215
16338
|
this.initPromise = new Promise((resolve) => {
|
|
16216
16339
|
this.initResolve = resolve;
|
|
16217
16340
|
});
|
|
@@ -16260,15 +16383,34 @@ var AccountService = class {
|
|
|
16260
16383
|
this.initialized = true;
|
|
16261
16384
|
this.initResolve?.(account);
|
|
16262
16385
|
}
|
|
16263
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
16386
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
16387
|
+
this.notifyListeners();
|
|
16388
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
16389
|
+
}
|
|
16264
16390
|
}
|
|
16265
16391
|
/**
|
|
16266
16392
|
* 清除账号(登出)
|
|
16393
|
+
* 先广播登出消息,再清除本地账号
|
|
16267
16394
|
*/
|
|
16268
16395
|
clearAccount() {
|
|
16396
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
16269
16397
|
this.setAccount(null);
|
|
16270
16398
|
}
|
|
16271
16399
|
/**
|
|
16400
|
+
* 静默清除账号(不广播)
|
|
16401
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
16402
|
+
*/
|
|
16403
|
+
clearAccountSilently() {
|
|
16404
|
+
this.setAccount(null);
|
|
16405
|
+
}
|
|
16406
|
+
/**
|
|
16407
|
+
* 设置跨标签页认证同步广播器
|
|
16408
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
16409
|
+
*/
|
|
16410
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
16411
|
+
this.crossTabBroadcaster = broadcaster;
|
|
16412
|
+
}
|
|
16413
|
+
/**
|
|
16272
16414
|
* 订阅账号变化
|
|
16273
16415
|
* @param callback 变化时的回调函数
|
|
16274
16416
|
* @returns 取消订阅函数
|
|
@@ -16333,6 +16475,114 @@ var AccountService = class {
|
|
|
16333
16475
|
* 导出单例实例
|
|
16334
16476
|
*/
|
|
16335
16477
|
const accountService = new AccountService();
|
|
16478
|
+
/**
|
|
16479
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
16480
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
16481
|
+
*/
|
|
16482
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
16483
|
+
|
|
16484
|
+
//#endregion
|
|
16485
|
+
//#region ../agent-provider/src/common/utils/lru-cache.ts
|
|
16486
|
+
/**
|
|
16487
|
+
* LRU (Least Recently Used) Cache
|
|
16488
|
+
* 当缓存达到容量上限时,自动淘汰最久未使用的数据
|
|
16489
|
+
*
|
|
16490
|
+
* @template K - Key 类型
|
|
16491
|
+
* @template V - Value 类型
|
|
16492
|
+
*/
|
|
16493
|
+
var LRUCache = class {
|
|
16494
|
+
/**
|
|
16495
|
+
* 创建 LRU 缓存实例
|
|
16496
|
+
* @param capacity - 缓存容量上限
|
|
16497
|
+
*/
|
|
16498
|
+
constructor(capacity) {
|
|
16499
|
+
if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
|
|
16500
|
+
this.capacity = capacity;
|
|
16501
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
16502
|
+
}
|
|
16503
|
+
/**
|
|
16504
|
+
* 获取缓存值
|
|
16505
|
+
* @param key - 键
|
|
16506
|
+
* @returns 值,如果不存在返回 undefined
|
|
16507
|
+
*/
|
|
16508
|
+
get(key) {
|
|
16509
|
+
if (!this.cache.has(key)) return;
|
|
16510
|
+
const value = this.cache.get(key);
|
|
16511
|
+
this.cache.delete(key);
|
|
16512
|
+
this.cache.set(key, value);
|
|
16513
|
+
return value;
|
|
16514
|
+
}
|
|
16515
|
+
/**
|
|
16516
|
+
* 设置缓存值
|
|
16517
|
+
* @param key - 键
|
|
16518
|
+
* @param value - 值
|
|
16519
|
+
*/
|
|
16520
|
+
set(key, value) {
|
|
16521
|
+
if (this.cache.has(key)) this.cache.delete(key);
|
|
16522
|
+
else if (this.cache.size >= this.capacity) {
|
|
16523
|
+
const firstKey = this.cache.keys().next().value;
|
|
16524
|
+
this.cache.delete(firstKey);
|
|
16525
|
+
}
|
|
16526
|
+
this.cache.set(key, value);
|
|
16527
|
+
}
|
|
16528
|
+
/**
|
|
16529
|
+
* 检查 key 是否存在
|
|
16530
|
+
* @param key - 键
|
|
16531
|
+
* @returns 是否存在
|
|
16532
|
+
*/
|
|
16533
|
+
has(key) {
|
|
16534
|
+
return this.cache.has(key);
|
|
16535
|
+
}
|
|
16536
|
+
/**
|
|
16537
|
+
* 删除指定 key
|
|
16538
|
+
* @param key - 键
|
|
16539
|
+
* @returns 是否删除成功
|
|
16540
|
+
*/
|
|
16541
|
+
delete(key) {
|
|
16542
|
+
return this.cache.delete(key);
|
|
16543
|
+
}
|
|
16544
|
+
/**
|
|
16545
|
+
* 清空缓存
|
|
16546
|
+
*/
|
|
16547
|
+
clear() {
|
|
16548
|
+
this.cache.clear();
|
|
16549
|
+
}
|
|
16550
|
+
/**
|
|
16551
|
+
* 获取当前缓存大小
|
|
16552
|
+
* @returns 当前缓存的元素数量
|
|
16553
|
+
*/
|
|
16554
|
+
get size() {
|
|
16555
|
+
return this.cache.size;
|
|
16556
|
+
}
|
|
16557
|
+
/**
|
|
16558
|
+
* 获取缓存容量
|
|
16559
|
+
* @returns 缓存容量上限
|
|
16560
|
+
*/
|
|
16561
|
+
get maxSize() {
|
|
16562
|
+
return this.capacity;
|
|
16563
|
+
}
|
|
16564
|
+
/**
|
|
16565
|
+
* 获取所有 key
|
|
16566
|
+
* @returns key 数组
|
|
16567
|
+
*/
|
|
16568
|
+
keys() {
|
|
16569
|
+
return Array.from(this.cache.keys());
|
|
16570
|
+
}
|
|
16571
|
+
/**
|
|
16572
|
+
* 获取所有 value
|
|
16573
|
+
* @returns value 数组
|
|
16574
|
+
*/
|
|
16575
|
+
values() {
|
|
16576
|
+
return Array.from(this.cache.values());
|
|
16577
|
+
}
|
|
16578
|
+
/**
|
|
16579
|
+
* 遍历缓存
|
|
16580
|
+
* @param callback - 回调函数
|
|
16581
|
+
*/
|
|
16582
|
+
forEach(callback) {
|
|
16583
|
+
this.cache.forEach((value, key) => callback(value, key));
|
|
16584
|
+
}
|
|
16585
|
+
};
|
|
16336
16586
|
|
|
16337
16587
|
//#endregion
|
|
16338
16588
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -16435,20 +16685,32 @@ var CosUploadService = class {
|
|
|
16435
16685
|
* 上传单个文件到 COS
|
|
16436
16686
|
*
|
|
16437
16687
|
* @param file - 要上传的文件
|
|
16688
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16438
16689
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
16439
16690
|
*/
|
|
16440
|
-
async uploadFile(file) {
|
|
16691
|
+
async uploadFile(file, abortSignal) {
|
|
16441
16692
|
const filename = file.name;
|
|
16442
16693
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
16443
16694
|
try {
|
|
16695
|
+
if (abortSignal?.aborted) return {
|
|
16696
|
+
success: false,
|
|
16697
|
+
error: "Upload cancelled",
|
|
16698
|
+
aborted: true
|
|
16699
|
+
};
|
|
16444
16700
|
const objectKey = this.generateObjectKey(filename);
|
|
16445
16701
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
16446
16702
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
16447
16703
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
16704
|
+
if (abortSignal?.aborted) return {
|
|
16705
|
+
success: false,
|
|
16706
|
+
error: "Upload cancelled",
|
|
16707
|
+
aborted: true
|
|
16708
|
+
};
|
|
16448
16709
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16449
16710
|
method: "PUT",
|
|
16450
16711
|
body: file,
|
|
16451
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16712
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16713
|
+
signal: abortSignal
|
|
16452
16714
|
});
|
|
16453
16715
|
if (!uploadResponse.ok) {
|
|
16454
16716
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16462,6 +16724,11 @@ var CosUploadService = class {
|
|
|
16462
16724
|
objectKey
|
|
16463
16725
|
};
|
|
16464
16726
|
} catch (error) {
|
|
16727
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16728
|
+
success: false,
|
|
16729
|
+
error: "Upload cancelled",
|
|
16730
|
+
aborted: true
|
|
16731
|
+
};
|
|
16465
16732
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
16466
16733
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
16467
16734
|
return {
|
|
@@ -16476,14 +16743,25 @@ var CosUploadService = class {
|
|
|
16476
16743
|
* 使用并发控制,限制同时上传的文件数量
|
|
16477
16744
|
*
|
|
16478
16745
|
* @param files - 要上传的文件数组
|
|
16746
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
16479
16747
|
* @returns 所有文件的上传结果
|
|
16480
16748
|
*/
|
|
16481
|
-
async uploadFiles(files) {
|
|
16749
|
+
async uploadFiles(files, abortSignal) {
|
|
16482
16750
|
if (files.length === 0) return {
|
|
16483
16751
|
success: true,
|
|
16484
16752
|
urls: [],
|
|
16485
16753
|
results: []
|
|
16486
16754
|
};
|
|
16755
|
+
if (abortSignal?.aborted) return {
|
|
16756
|
+
success: false,
|
|
16757
|
+
error: "Upload cancelled",
|
|
16758
|
+
aborted: true,
|
|
16759
|
+
results: files.map(() => ({
|
|
16760
|
+
success: false,
|
|
16761
|
+
error: "Upload cancelled",
|
|
16762
|
+
aborted: true
|
|
16763
|
+
}))
|
|
16764
|
+
};
|
|
16487
16765
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
16488
16766
|
try {
|
|
16489
16767
|
const fileInfos = files.map((file) => ({
|
|
@@ -16495,6 +16773,12 @@ var CosUploadService = class {
|
|
|
16495
16773
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
16496
16774
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
16497
16775
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
16776
|
+
if (abortSignal?.aborted) return {
|
|
16777
|
+
success: false,
|
|
16778
|
+
error: "Upload cancelled",
|
|
16779
|
+
aborted: true,
|
|
16780
|
+
objectKey: fileInfos[index].objectKey
|
|
16781
|
+
};
|
|
16498
16782
|
const presignedItem = presignedResponse.items[index];
|
|
16499
16783
|
if (!presignedItem) return {
|
|
16500
16784
|
success: false,
|
|
@@ -16505,7 +16789,8 @@ var CosUploadService = class {
|
|
|
16505
16789
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
16506
16790
|
method: "PUT",
|
|
16507
16791
|
body: file,
|
|
16508
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
16792
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
16793
|
+
signal: abortSignal
|
|
16509
16794
|
});
|
|
16510
16795
|
if (!uploadResponse.ok) {
|
|
16511
16796
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -16522,6 +16807,12 @@ var CosUploadService = class {
|
|
|
16522
16807
|
objectKey: presignedItem.object_key
|
|
16523
16808
|
};
|
|
16524
16809
|
} catch (error) {
|
|
16810
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
16811
|
+
success: false,
|
|
16812
|
+
error: "Upload cancelled",
|
|
16813
|
+
aborted: true,
|
|
16814
|
+
objectKey: presignedItem.object_key
|
|
16815
|
+
};
|
|
16525
16816
|
return {
|
|
16526
16817
|
success: false,
|
|
16527
16818
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -16575,35 +16866,35 @@ var CosUploadService = class {
|
|
|
16575
16866
|
* E2B Filesystem Implementation
|
|
16576
16867
|
*
|
|
16577
16868
|
* Provides FilesResource implementation using E2B Sandbox SDK.
|
|
16578
|
-
*
|
|
16869
|
+
* Supports optional auto-reconnect on auth failure (401/403).
|
|
16579
16870
|
*
|
|
16580
16871
|
* @see https://e2b.dev/docs/filesystem/read-write
|
|
16581
16872
|
* @see https://e2b.dev/docs/filesystem/watch
|
|
16582
16873
|
*/
|
|
16583
16874
|
/**
|
|
16584
|
-
* E2B Filesystem
|
|
16875
|
+
* E2B Filesystem
|
|
16585
16876
|
*
|
|
16586
|
-
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource
|
|
16877
|
+
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource.
|
|
16878
|
+
* When `reconnectFn` is provided, automatically reconnects on auth errors.
|
|
16587
16879
|
*
|
|
16588
16880
|
* @example
|
|
16589
16881
|
* ```typescript
|
|
16590
|
-
*
|
|
16591
|
-
*
|
|
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');
|
|
16882
|
+
* // Basic usage (no auto-reconnect)
|
|
16883
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
16598
16884
|
*
|
|
16599
|
-
* //
|
|
16600
|
-
* const
|
|
16601
|
-
*
|
|
16602
|
-
* });
|
|
16885
|
+
* // With auto-reconnect on token expiry
|
|
16886
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
16887
|
+
* fs.setReconnectFn(async () => fetchFreshConnectionInfo());
|
|
16603
16888
|
* ```
|
|
16604
16889
|
*/
|
|
16605
16890
|
var E2BFilesystem = class E2BFilesystem {
|
|
16891
|
+
static {
|
|
16892
|
+
this.MIN_RECONNECT_INTERVAL_MS = 10 * 1e3;
|
|
16893
|
+
}
|
|
16606
16894
|
constructor(sandbox) {
|
|
16895
|
+
this.isReconnecting = false;
|
|
16896
|
+
this.reconnectSubscribers = [];
|
|
16897
|
+
this.lastReconnectAt = 0;
|
|
16607
16898
|
this.sandbox = sandbox;
|
|
16608
16899
|
}
|
|
16609
16900
|
/**
|
|
@@ -16619,38 +16910,111 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
16619
16910
|
}));
|
|
16620
16911
|
}
|
|
16621
16912
|
/**
|
|
16913
|
+
* Set reconnect callback. When set, auth errors trigger automatic reconnect + retry.
|
|
16914
|
+
*/
|
|
16915
|
+
setReconnectFn(fn) {
|
|
16916
|
+
this.reconnectFn = fn;
|
|
16917
|
+
}
|
|
16918
|
+
/**
|
|
16622
16919
|
* Get the underlying E2B Sandbox instance
|
|
16623
16920
|
*/
|
|
16624
16921
|
getSandbox() {
|
|
16625
16922
|
return this.sandbox;
|
|
16626
16923
|
}
|
|
16924
|
+
isAuthError(error) {
|
|
16925
|
+
if (!error || typeof error !== "object") return false;
|
|
16926
|
+
const err = error;
|
|
16927
|
+
if (err.status === 401 || err.status === 403) return true;
|
|
16928
|
+
if (err.statusCode === 401 || err.statusCode === 403) return true;
|
|
16929
|
+
if (err.response && typeof err.response === "object") {
|
|
16930
|
+
const resp = err.response;
|
|
16931
|
+
if (resp.status === 401 || resp.status === 403) return true;
|
|
16932
|
+
}
|
|
16933
|
+
if (typeof err.message === "string") {
|
|
16934
|
+
const msg = err.message.toLowerCase();
|
|
16935
|
+
if (msg.includes("unauthorized") || msg.includes("token expired") || msg.includes("authentication")) return true;
|
|
16936
|
+
}
|
|
16937
|
+
return false;
|
|
16938
|
+
}
|
|
16939
|
+
canAttemptReconnect() {
|
|
16940
|
+
return Date.now() - this.lastReconnectAt >= E2BFilesystem.MIN_RECONNECT_INTERVAL_MS;
|
|
16941
|
+
}
|
|
16942
|
+
/**
|
|
16943
|
+
* Reconnect with fresh credentials.
|
|
16944
|
+
* Only one reconnect in-flight at a time; concurrent callers share the result.
|
|
16945
|
+
*/
|
|
16946
|
+
async reconnect() {
|
|
16947
|
+
if (this.isReconnecting) return new Promise((resolve, reject) => {
|
|
16948
|
+
this.reconnectSubscribers.push((success) => {
|
|
16949
|
+
if (success) resolve();
|
|
16950
|
+
else reject(/* @__PURE__ */ new Error("E2B sandbox reconnect failed"));
|
|
16951
|
+
});
|
|
16952
|
+
});
|
|
16953
|
+
this.isReconnecting = true;
|
|
16954
|
+
this.lastReconnectAt = Date.now();
|
|
16955
|
+
try {
|
|
16956
|
+
const info = await this.reconnectFn();
|
|
16957
|
+
this.sandbox = await e2b.Sandbox.connect(info.sandboxId, {
|
|
16958
|
+
domain: info.domain,
|
|
16959
|
+
apiUrl: info.apiUrl,
|
|
16960
|
+
requestTimeoutMs: info.requestTimeoutMs,
|
|
16961
|
+
debug: info.debug,
|
|
16962
|
+
headers: info.headers
|
|
16963
|
+
});
|
|
16964
|
+
this.reconnectSubscribers.forEach((cb) => cb(true));
|
|
16965
|
+
this.reconnectSubscribers = [];
|
|
16966
|
+
} catch (error) {
|
|
16967
|
+
this.reconnectSubscribers.forEach((cb) => cb(false));
|
|
16968
|
+
this.reconnectSubscribers = [];
|
|
16969
|
+
throw error;
|
|
16970
|
+
} finally {
|
|
16971
|
+
this.isReconnecting = false;
|
|
16972
|
+
}
|
|
16973
|
+
}
|
|
16974
|
+
/**
|
|
16975
|
+
* Execute an operation. If reconnectFn is set and an auth error occurs,
|
|
16976
|
+
* reconnect and retry once.
|
|
16977
|
+
*/
|
|
16978
|
+
async exec(operation) {
|
|
16979
|
+
try {
|
|
16980
|
+
return await operation(this.sandbox.files);
|
|
16981
|
+
} catch (error) {
|
|
16982
|
+
if (this.reconnectFn && this.isAuthError(error) && this.canAttemptReconnect()) {
|
|
16983
|
+
await this.reconnect();
|
|
16984
|
+
return operation(this.sandbox.files);
|
|
16985
|
+
}
|
|
16986
|
+
throw error;
|
|
16987
|
+
}
|
|
16988
|
+
}
|
|
16627
16989
|
read(path, opts) {
|
|
16628
|
-
return this.
|
|
16990
|
+
return this.exec((f) => f.read(path, opts));
|
|
16629
16991
|
}
|
|
16630
16992
|
write(pathOrFiles, dataOrOpts, opts) {
|
|
16631
|
-
|
|
16632
|
-
|
|
16993
|
+
return this.exec((f) => {
|
|
16994
|
+
if (Array.isArray(pathOrFiles)) return f.write(pathOrFiles, dataOrOpts);
|
|
16995
|
+
return f.write(pathOrFiles, dataOrOpts, opts);
|
|
16996
|
+
});
|
|
16633
16997
|
}
|
|
16634
16998
|
async list(path, opts) {
|
|
16635
|
-
return this.
|
|
16999
|
+
return this.exec((f) => f.list(path, opts));
|
|
16636
17000
|
}
|
|
16637
17001
|
async exists(path, opts) {
|
|
16638
|
-
return this.
|
|
17002
|
+
return this.exec((f) => f.exists(path, opts));
|
|
16639
17003
|
}
|
|
16640
17004
|
async makeDir(path, opts) {
|
|
16641
|
-
return this.
|
|
17005
|
+
return this.exec((f) => f.makeDir(path, opts));
|
|
16642
17006
|
}
|
|
16643
17007
|
async remove(path, opts) {
|
|
16644
|
-
return this.
|
|
17008
|
+
return this.exec((f) => f.remove(path, opts));
|
|
16645
17009
|
}
|
|
16646
17010
|
async rename(oldPath, newPath, opts) {
|
|
16647
|
-
return this.
|
|
17011
|
+
return this.exec((f) => f.rename(oldPath, newPath, opts));
|
|
16648
17012
|
}
|
|
16649
17013
|
async getInfo(path, opts) {
|
|
16650
|
-
return this.
|
|
17014
|
+
return this.exec((f) => f.getInfo(path, opts));
|
|
16651
17015
|
}
|
|
16652
17016
|
async watchDir(path, onEvent, opts) {
|
|
16653
|
-
return this.
|
|
17017
|
+
return this.exec((f) => f.watchDir(path, onEvent, opts));
|
|
16654
17018
|
}
|
|
16655
17019
|
};
|
|
16656
17020
|
|
|
@@ -16802,8 +17166,11 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16802
17166
|
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
16803
17167
|
this.connectionCache = /* @__PURE__ */ new Map();
|
|
16804
17168
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
17169
|
+
this.productConfigCache = null;
|
|
16805
17170
|
this.options = options;
|
|
16806
17171
|
this.logger = options.logger;
|
|
17172
|
+
this.marketplaceCache = new LRUCache(200);
|
|
17173
|
+
this.pluginCache = new LRUCache(2e3);
|
|
16807
17174
|
if (options.endpoint) httpService.setBaseURL(options.endpoint);
|
|
16808
17175
|
if (options.authToken) httpService.setAuthToken(options.authToken);
|
|
16809
17176
|
if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
|
|
@@ -16854,7 +17221,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16854
17221
|
const cached = this.filesystemCache.get(agentId);
|
|
16855
17222
|
if (cached) return cached;
|
|
16856
17223
|
const info = await this.getSandboxInfo(agentId);
|
|
16857
|
-
const
|
|
17224
|
+
const e2bFilesystem = await E2BFilesystem.connect(info);
|
|
17225
|
+
e2bFilesystem.setReconnectFn(async () => {
|
|
17226
|
+
this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
|
|
17227
|
+
const newInfo = await this.getSandboxInfo(agentId);
|
|
17228
|
+
this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
|
|
17229
|
+
return newInfo;
|
|
17230
|
+
});
|
|
17231
|
+
const filesystem = createAgentFilesystem(e2bFilesystem);
|
|
16858
17232
|
this.filesystemCache.set(agentId, filesystem);
|
|
16859
17233
|
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
16860
17234
|
return filesystem;
|
|
@@ -16922,15 +17296,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16922
17296
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
16923
17297
|
const apiResponse = await httpService.get(url);
|
|
16924
17298
|
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
17299
|
return {
|
|
16932
|
-
agents,
|
|
16933
|
-
pagination
|
|
17300
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
17301
|
+
pagination: apiResponse.data.pagination
|
|
16934
17302
|
};
|
|
16935
17303
|
} catch (error) {
|
|
16936
17304
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -16940,13 +17308,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16940
17308
|
/**
|
|
16941
17309
|
* Create a new conversation
|
|
16942
17310
|
* POST {endpoint}/console/as/conversations
|
|
17311
|
+
* @param params - Session params containing cwd and optional configuration
|
|
16943
17312
|
*/
|
|
16944
|
-
async create() {
|
|
17313
|
+
async create(params) {
|
|
16945
17314
|
try {
|
|
16946
|
-
const
|
|
16947
|
-
|
|
16948
|
-
|
|
16949
|
-
});
|
|
17315
|
+
const { options = {} } = params;
|
|
17316
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
17317
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
17318
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
17319
|
+
const createPayload = {
|
|
17320
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
17321
|
+
model: options.model || "deepseek-r1",
|
|
17322
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
17323
|
+
};
|
|
17324
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
17325
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
16950
17326
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
16951
17327
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
16952
17328
|
return apiResponse.data.id;
|
|
@@ -16996,7 +17372,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
16996
17372
|
} catch (error) {
|
|
16997
17373
|
this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
|
|
16998
17374
|
}
|
|
16999
|
-
|
|
17375
|
+
const existingConnection = this.connectionCache.get(endpoint);
|
|
17376
|
+
if (existingConnection) {
|
|
17377
|
+
this.connectionCache.delete(endpoint);
|
|
17378
|
+
existingConnection.removeAllListeners();
|
|
17379
|
+
existingConnection.disconnect().catch((err) => {
|
|
17380
|
+
this.logger?.debug("Failed to disconnect old connection:", err);
|
|
17381
|
+
});
|
|
17382
|
+
}
|
|
17000
17383
|
const clientCapabilities = {
|
|
17001
17384
|
...this.options.clientCapabilities,
|
|
17002
17385
|
_meta: {
|
|
@@ -17116,6 +17499,35 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17116
17499
|
}
|
|
17117
17500
|
}
|
|
17118
17501
|
/**
|
|
17502
|
+
* Update conversation status by ID
|
|
17503
|
+
* POST {endpoint}/console/as/conversations/{agentId}
|
|
17504
|
+
*
|
|
17505
|
+
* @param agentId - Conversation ID to update
|
|
17506
|
+
* @param status - New status for the conversation
|
|
17507
|
+
* @returns PatchConversationResponse containing the updated conversation ID
|
|
17508
|
+
*
|
|
17509
|
+
* @example
|
|
17510
|
+
* ```typescript
|
|
17511
|
+
* const result = await provider.updateStatus('agent-123', 'completed');
|
|
17512
|
+
* console.log('Updated conversation status:', result.id);
|
|
17513
|
+
* ```
|
|
17514
|
+
*/
|
|
17515
|
+
async updateStatus(agentId, status) {
|
|
17516
|
+
try {
|
|
17517
|
+
const body = { status };
|
|
17518
|
+
const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
|
|
17519
|
+
if (!apiResponse.data) {
|
|
17520
|
+
this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
|
|
17521
|
+
return { id: agentId };
|
|
17522
|
+
}
|
|
17523
|
+
this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
|
|
17524
|
+
return apiResponse.data;
|
|
17525
|
+
} catch (error) {
|
|
17526
|
+
this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
|
|
17527
|
+
throw error;
|
|
17528
|
+
}
|
|
17529
|
+
}
|
|
17530
|
+
/**
|
|
17119
17531
|
* Get available models from product configuration
|
|
17120
17532
|
*
|
|
17121
17533
|
* GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
|
|
@@ -17146,9 +17558,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17146
17558
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
17147
17559
|
return [];
|
|
17148
17560
|
}
|
|
17149
|
-
|
|
17150
|
-
|
|
17151
|
-
|
|
17561
|
+
this.productConfigCache = apiResponse.data;
|
|
17562
|
+
const productConfig = apiResponse.data;
|
|
17563
|
+
const allModels = productConfig.models ?? [];
|
|
17564
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
17565
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
17566
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
17567
|
+
return filteredModels.map((model) => ({
|
|
17152
17568
|
id: model.id,
|
|
17153
17569
|
name: model.name ?? model.id,
|
|
17154
17570
|
description: model.description,
|
|
@@ -17170,6 +17586,43 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17170
17586
|
}
|
|
17171
17587
|
}
|
|
17172
17588
|
/**
|
|
17589
|
+
* 获取产品部署类型(从缓存)
|
|
17590
|
+
* 需要先调用 getModels() 初始化缓存
|
|
17591
|
+
*
|
|
17592
|
+
* @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
|
|
17593
|
+
*/
|
|
17594
|
+
getDeploymentType() {
|
|
17595
|
+
return this.productConfigCache?.deploymentType ?? "SaaS";
|
|
17596
|
+
}
|
|
17597
|
+
/**
|
|
17598
|
+
* 获取 Credit 购买引导配置(从缓存)
|
|
17599
|
+
* 需要先调用 getModels() 初始化缓存
|
|
17600
|
+
*
|
|
17601
|
+
* @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
|
|
17602
|
+
*/
|
|
17603
|
+
getCreditPurchaseActions() {
|
|
17604
|
+
return this.productConfigCache?.config?.creditPurchaseActions ?? {
|
|
17605
|
+
"14018": {
|
|
17606
|
+
labelZh: "获取 Credits",
|
|
17607
|
+
labelEn: "Get credits",
|
|
17608
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
17609
|
+
showButton: true
|
|
17610
|
+
},
|
|
17611
|
+
"6004": {
|
|
17612
|
+
labelZh: "升级专业版",
|
|
17613
|
+
labelEn: "Upgrade to Pro",
|
|
17614
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
17615
|
+
showButton: true
|
|
17616
|
+
},
|
|
17617
|
+
"6005": {
|
|
17618
|
+
labelZh: "升级专业版",
|
|
17619
|
+
labelEn: "Upgrade to Pro",
|
|
17620
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
17621
|
+
showButton: true
|
|
17622
|
+
}
|
|
17623
|
+
};
|
|
17624
|
+
}
|
|
17625
|
+
/**
|
|
17173
17626
|
* Generate a unique request ID
|
|
17174
17627
|
*/
|
|
17175
17628
|
generateRequestId() {
|
|
@@ -17252,7 +17705,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17252
17705
|
/**
|
|
17253
17706
|
* Upload files to cloud storage via COS presigned URL
|
|
17254
17707
|
*
|
|
17255
|
-
* @param params - files array (File objects in browser)
|
|
17708
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
17256
17709
|
* @returns Response with corresponding cloud URLs
|
|
17257
17710
|
*/
|
|
17258
17711
|
async uploadFile(params) {
|
|
@@ -17262,12 +17715,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17262
17715
|
success: false,
|
|
17263
17716
|
error: "No valid File objects provided"
|
|
17264
17717
|
};
|
|
17265
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
17718
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
17266
17719
|
return {
|
|
17267
17720
|
success: result.success,
|
|
17268
17721
|
urls: result.urls,
|
|
17269
17722
|
expireSeconds: result.expireSeconds,
|
|
17270
|
-
error: result.error
|
|
17723
|
+
error: result.error,
|
|
17724
|
+
aborted: result.aborted
|
|
17271
17725
|
};
|
|
17272
17726
|
}
|
|
17273
17727
|
/**
|
|
@@ -17309,20 +17763,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17309
17763
|
}
|
|
17310
17764
|
/**
|
|
17311
17765
|
* 获取支持的场景列表
|
|
17312
|
-
* API 端点: GET /
|
|
17766
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
17313
17767
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
17314
17768
|
*
|
|
17769
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
17315
17770
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
17316
17771
|
*/
|
|
17317
|
-
async getSupportScenes() {
|
|
17772
|
+
async getSupportScenes(locale) {
|
|
17318
17773
|
try {
|
|
17319
|
-
const
|
|
17774
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
17775
|
+
const apiResponse = await httpService.get(url);
|
|
17320
17776
|
if (!apiResponse.data) {
|
|
17321
17777
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
17322
17778
|
return [];
|
|
17323
17779
|
}
|
|
17324
17780
|
const scenes = apiResponse.data.scenes || [];
|
|
17325
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
17781
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
17326
17782
|
return scenes;
|
|
17327
17783
|
} catch (error) {
|
|
17328
17784
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -17338,7 +17794,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17338
17794
|
type: "cloud",
|
|
17339
17795
|
status,
|
|
17340
17796
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
17341
|
-
capabilities: this.options.clientCapabilities
|
|
17797
|
+
capabilities: this.options.clientCapabilities,
|
|
17798
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
17342
17799
|
};
|
|
17343
17800
|
}
|
|
17344
17801
|
/**
|
|
@@ -17354,61 +17811,504 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
17354
17811
|
const queryString = searchParams.toString();
|
|
17355
17812
|
return queryString ? `${path}?${queryString}` : path;
|
|
17356
17813
|
}
|
|
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
17814
|
/**
|
|
17394
|
-
*
|
|
17815
|
+
* 获取已安装插件列表
|
|
17816
|
+
* GET /console/as/user/plugins/installed
|
|
17817
|
+
*/
|
|
17818
|
+
async getInstalledPlugins(forceRefresh) {
|
|
17819
|
+
try {
|
|
17820
|
+
const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
|
|
17821
|
+
name: p.plugin_name,
|
|
17822
|
+
marketplaceName: p.marketplace_name,
|
|
17823
|
+
status: p.enabled ? "enabled" : "disabled",
|
|
17824
|
+
description: p.description,
|
|
17825
|
+
version: p.version,
|
|
17826
|
+
installScope: p.scope === "local" ? "project" : p.scope,
|
|
17827
|
+
installedScopes: [p.scope],
|
|
17828
|
+
installId: p.id
|
|
17829
|
+
}));
|
|
17830
|
+
result.forEach((plugin) => {
|
|
17831
|
+
this.pluginCache.set(plugin.name, plugin);
|
|
17832
|
+
});
|
|
17833
|
+
return result;
|
|
17834
|
+
} catch (error) {
|
|
17835
|
+
this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
|
|
17836
|
+
throw error;
|
|
17837
|
+
}
|
|
17838
|
+
}
|
|
17839
|
+
/**
|
|
17840
|
+
* 安装插件
|
|
17841
|
+
* POST /console/as/user/plugins/install
|
|
17395
17842
|
*
|
|
17396
|
-
* @param
|
|
17397
|
-
* @param
|
|
17398
|
-
* @param connection - Already connected AgentConnection
|
|
17399
|
-
* @param options - Additional options
|
|
17843
|
+
* @param pluginNames - 插件名称数组
|
|
17844
|
+
* @param marketplaceNameOrId - 市场名称或 ID
|
|
17400
17845
|
*/
|
|
17401
|
-
|
|
17402
|
-
|
|
17403
|
-
|
|
17404
|
-
|
|
17405
|
-
|
|
17406
|
-
|
|
17407
|
-
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
|
|
17411
|
-
|
|
17846
|
+
async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
|
|
17847
|
+
try {
|
|
17848
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
17849
|
+
if (!marketplaceId) return {
|
|
17850
|
+
success: false,
|
|
17851
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
17852
|
+
};
|
|
17853
|
+
const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
|
|
17854
|
+
plugin_name: pluginName,
|
|
17855
|
+
marketplace_id: marketplaceId,
|
|
17856
|
+
version: "latest"
|
|
17857
|
+
})))).filter((r) => r.status === "rejected");
|
|
17858
|
+
if (failed.length > 0) {
|
|
17859
|
+
const errors = failed.map((r) => r.reason?.message || "Unknown error");
|
|
17860
|
+
return {
|
|
17861
|
+
success: false,
|
|
17862
|
+
error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
|
|
17863
|
+
};
|
|
17864
|
+
}
|
|
17865
|
+
return { success: true };
|
|
17866
|
+
} catch (error) {
|
|
17867
|
+
return {
|
|
17868
|
+
success: false,
|
|
17869
|
+
error: this.extractErrorMessage(error)
|
|
17870
|
+
};
|
|
17871
|
+
}
|
|
17872
|
+
}
|
|
17873
|
+
/**
|
|
17874
|
+
* 卸载插件
|
|
17875
|
+
* POST /console/as/user/plugins/installed/:id/uninstall
|
|
17876
|
+
*
|
|
17877
|
+
* 完整链路:
|
|
17878
|
+
* CloudAgentProvider.uninstallPlugin()
|
|
17879
|
+
* -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
|
|
17880
|
+
* -> agentserver: PluginInstallService.Uninstall()
|
|
17881
|
+
* -> 软删除 DB + 异步同步到活跃沙箱
|
|
17882
|
+
*
|
|
17883
|
+
* @param pluginName - 插件名称
|
|
17884
|
+
* @param marketplaceName - 市场名称(用于标识唯一插件)
|
|
17885
|
+
* @param scope - 卸载范围 ('user' | 'project' | 'project-local')
|
|
17886
|
+
*/
|
|
17887
|
+
async uninstallPlugin(pluginName, marketplaceName, scope) {
|
|
17888
|
+
try {
|
|
17889
|
+
const installId = await this.findPluginInstallId(pluginName);
|
|
17890
|
+
if (!installId) return {
|
|
17891
|
+
success: false,
|
|
17892
|
+
error: `Plugin not found or not installed: ${pluginName}`
|
|
17893
|
+
};
|
|
17894
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
|
|
17895
|
+
return { success: true };
|
|
17896
|
+
} catch (error) {
|
|
17897
|
+
return {
|
|
17898
|
+
success: false,
|
|
17899
|
+
error: this.extractErrorMessage(error)
|
|
17900
|
+
};
|
|
17901
|
+
}
|
|
17902
|
+
}
|
|
17903
|
+
/**
|
|
17904
|
+
* 获取插件市场列表
|
|
17905
|
+
* GET /console/as/marketplace/sources
|
|
17906
|
+
*/
|
|
17907
|
+
async getPluginMarketplaces(forceRefresh) {
|
|
17908
|
+
try {
|
|
17909
|
+
const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
|
|
17910
|
+
id: src.id,
|
|
17911
|
+
name: src.name,
|
|
17912
|
+
type: this.mapSourceType(src.source_type),
|
|
17913
|
+
source: { url: src.url },
|
|
17914
|
+
description: src.name,
|
|
17915
|
+
isBuiltin: src.is_default
|
|
17916
|
+
}));
|
|
17917
|
+
result.forEach((m) => {
|
|
17918
|
+
const marketplaceInfo = {
|
|
17919
|
+
id: m.id,
|
|
17920
|
+
name: m.name
|
|
17921
|
+
};
|
|
17922
|
+
this.marketplaceCache.set(m.name, marketplaceInfo);
|
|
17923
|
+
this.marketplaceCache.set(m.id, marketplaceInfo);
|
|
17924
|
+
});
|
|
17925
|
+
return result;
|
|
17926
|
+
} catch (error) {
|
|
17927
|
+
this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
|
|
17928
|
+
throw error;
|
|
17929
|
+
}
|
|
17930
|
+
}
|
|
17931
|
+
/**
|
|
17932
|
+
* 获取市场下的插件列表
|
|
17933
|
+
* - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
|
|
17934
|
+
* - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
|
|
17935
|
+
*
|
|
17936
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
17937
|
+
* @param forceRefresh - 是否强制刷新
|
|
17938
|
+
* @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
|
|
17939
|
+
*/
|
|
17940
|
+
async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
|
|
17941
|
+
try {
|
|
17942
|
+
if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
|
|
17943
|
+
q: searchText,
|
|
17944
|
+
page: 1,
|
|
17945
|
+
page_size: 100
|
|
17946
|
+
} })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
17947
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
17948
|
+
if (!sourceId) {
|
|
17949
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
17950
|
+
return [];
|
|
17951
|
+
}
|
|
17952
|
+
const params = {
|
|
17953
|
+
source_id: sourceId,
|
|
17954
|
+
page: 1,
|
|
17955
|
+
page_size: 100
|
|
17956
|
+
};
|
|
17957
|
+
return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
17958
|
+
} catch (error) {
|
|
17959
|
+
this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
|
|
17960
|
+
throw error;
|
|
17961
|
+
}
|
|
17962
|
+
}
|
|
17963
|
+
/**
|
|
17964
|
+
* 获取插件详情
|
|
17965
|
+
* GET /console/as/marketplace/plugins/:name/detail
|
|
17966
|
+
*
|
|
17967
|
+
* @param pluginName - 插件名称
|
|
17968
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
17969
|
+
*/
|
|
17970
|
+
async getPluginDetail(pluginName, marketplaceNameOrId) {
|
|
17971
|
+
try {
|
|
17972
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
17973
|
+
if (!sourceId) {
|
|
17974
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
17975
|
+
return null;
|
|
17976
|
+
}
|
|
17977
|
+
const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
|
|
17978
|
+
if (!p) return null;
|
|
17979
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
17980
|
+
const tags = p.tags ? JSON.parse(p.tags) : [];
|
|
17981
|
+
return {
|
|
17982
|
+
name: p.name,
|
|
17983
|
+
marketplaceName: p.marketplace_name,
|
|
17984
|
+
description: p.description,
|
|
17985
|
+
version: p.version,
|
|
17986
|
+
iconUrl: p.icon_url,
|
|
17987
|
+
tags,
|
|
17988
|
+
installed: p.installed,
|
|
17989
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
17990
|
+
readme: p.readme,
|
|
17991
|
+
author: p.author,
|
|
17992
|
+
homepage: p.homepage,
|
|
17993
|
+
repositoryUrl: p.repository_url,
|
|
17994
|
+
license: p.license,
|
|
17995
|
+
...capabilities
|
|
17996
|
+
};
|
|
17997
|
+
} catch (error) {
|
|
17998
|
+
this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
|
|
17999
|
+
throw error;
|
|
18000
|
+
}
|
|
18001
|
+
}
|
|
18002
|
+
/**
|
|
18003
|
+
* 添加插件市场
|
|
18004
|
+
* POST /console/as/marketplace/sources
|
|
18005
|
+
*/
|
|
18006
|
+
async addPluginMarketplace(sourceUrl, name) {
|
|
18007
|
+
try {
|
|
18008
|
+
const body = {
|
|
18009
|
+
source_type: sourceUrl.startsWith("http") ? "url" : "github",
|
|
18010
|
+
url: sourceUrl,
|
|
18011
|
+
name: name || sourceUrl
|
|
18012
|
+
};
|
|
18013
|
+
const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
|
|
18014
|
+
if (!sourceData) throw new Error("Invalid response from server");
|
|
18015
|
+
const marketplaceInfo = {
|
|
18016
|
+
id: sourceData.id,
|
|
18017
|
+
name: sourceData.name
|
|
18018
|
+
};
|
|
18019
|
+
this.marketplaceCache.set(sourceData.name, marketplaceInfo);
|
|
18020
|
+
this.marketplaceCache.set(sourceData.id, marketplaceInfo);
|
|
18021
|
+
return {
|
|
18022
|
+
success: true,
|
|
18023
|
+
marketplace: {
|
|
18024
|
+
id: sourceData.id,
|
|
18025
|
+
name: sourceData.name,
|
|
18026
|
+
type: this.mapSourceType(sourceData.source_type),
|
|
18027
|
+
source: { url: sourceData.url },
|
|
18028
|
+
isBuiltin: sourceData.is_default
|
|
18029
|
+
}
|
|
18030
|
+
};
|
|
18031
|
+
} catch (error) {
|
|
18032
|
+
return {
|
|
18033
|
+
success: false,
|
|
18034
|
+
error: this.extractErrorMessage(error)
|
|
18035
|
+
};
|
|
18036
|
+
}
|
|
18037
|
+
}
|
|
18038
|
+
/**
|
|
18039
|
+
* 删除插件市场
|
|
18040
|
+
* POST /console/as/marketplace/sources/:id/delete
|
|
18041
|
+
*/
|
|
18042
|
+
async removePluginMarketplace(marketplaceNameOrId) {
|
|
18043
|
+
try {
|
|
18044
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
18045
|
+
if (!marketplaceId) return {
|
|
18046
|
+
success: false,
|
|
18047
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
18048
|
+
};
|
|
18049
|
+
await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
|
|
18050
|
+
return { success: true };
|
|
18051
|
+
} catch (error) {
|
|
18052
|
+
return {
|
|
18053
|
+
success: false,
|
|
18054
|
+
error: this.extractErrorMessage(error)
|
|
18055
|
+
};
|
|
18056
|
+
}
|
|
18057
|
+
}
|
|
18058
|
+
/**
|
|
18059
|
+
* 刷新插件市场
|
|
18060
|
+
* POST /console/as/marketplace/sources/:id/check-updates
|
|
18061
|
+
*/
|
|
18062
|
+
async refreshPluginMarketplace(marketplaceNameOrId) {
|
|
18063
|
+
try {
|
|
18064
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
18065
|
+
if (!marketplaceId) return {
|
|
18066
|
+
success: false,
|
|
18067
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
18068
|
+
};
|
|
18069
|
+
return {
|
|
18070
|
+
success: true,
|
|
18071
|
+
plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
|
|
18072
|
+
};
|
|
18073
|
+
} catch (error) {
|
|
18074
|
+
return {
|
|
18075
|
+
success: false,
|
|
18076
|
+
error: this.extractErrorMessage(error)
|
|
18077
|
+
};
|
|
18078
|
+
}
|
|
18079
|
+
}
|
|
18080
|
+
/**
|
|
18081
|
+
* 批量切换插件启用/禁用状态
|
|
18082
|
+
* POST /console/as/user/plugins/installed/:id/toggle
|
|
18083
|
+
*/
|
|
18084
|
+
async batchTogglePlugins(request) {
|
|
18085
|
+
try {
|
|
18086
|
+
const results = await Promise.allSettled(request.items.map(async (item) => {
|
|
18087
|
+
const installId = await this.findPluginInstallId(item.pluginName);
|
|
18088
|
+
if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
|
|
18089
|
+
const enabled = item.operation === "enable";
|
|
18090
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
|
|
18091
|
+
return item;
|
|
18092
|
+
}));
|
|
18093
|
+
const succeededPlugins = [];
|
|
18094
|
+
const failedPlugins = [];
|
|
18095
|
+
results.forEach((r, i) => {
|
|
18096
|
+
const item = request.items[i];
|
|
18097
|
+
if (r.status === "fulfilled") succeededPlugins.push(item);
|
|
18098
|
+
else failedPlugins.push({
|
|
18099
|
+
...item,
|
|
18100
|
+
error: r.reason?.message || "Unknown error"
|
|
18101
|
+
});
|
|
18102
|
+
});
|
|
18103
|
+
return {
|
|
18104
|
+
success: failedPlugins.length === 0,
|
|
18105
|
+
succeededPlugins,
|
|
18106
|
+
failedPlugins
|
|
18107
|
+
};
|
|
18108
|
+
} catch (error) {
|
|
18109
|
+
return {
|
|
18110
|
+
success: false,
|
|
18111
|
+
succeededPlugins: [],
|
|
18112
|
+
failedPlugins: request.items.map((item) => ({
|
|
18113
|
+
...item,
|
|
18114
|
+
error: this.extractErrorMessage(error)
|
|
18115
|
+
}))
|
|
18116
|
+
};
|
|
18117
|
+
}
|
|
18118
|
+
}
|
|
18119
|
+
/**
|
|
18120
|
+
* 将后端插件数据映射为前端格式
|
|
18121
|
+
*/
|
|
18122
|
+
mapPluginData(p) {
|
|
18123
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
18124
|
+
let tags = [];
|
|
18125
|
+
if (p.tags) {
|
|
18126
|
+
if (Array.isArray(p.tags)) tags = p.tags;
|
|
18127
|
+
else if (typeof p.tags === "string") try {
|
|
18128
|
+
const parsed = JSON.parse(p.tags);
|
|
18129
|
+
tags = Array.isArray(parsed) ? parsed : [parsed];
|
|
18130
|
+
} catch {
|
|
18131
|
+
tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
|
|
18132
|
+
}
|
|
18133
|
+
}
|
|
18134
|
+
return {
|
|
18135
|
+
name: p.name,
|
|
18136
|
+
marketplaceName: p.marketplace_name,
|
|
18137
|
+
description: p.description,
|
|
18138
|
+
version: p.version,
|
|
18139
|
+
iconUrl: p.icon_url,
|
|
18140
|
+
tags,
|
|
18141
|
+
installed: p.installed,
|
|
18142
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
18143
|
+
installedScopes: p.installed ? [p.installed_scope] : [],
|
|
18144
|
+
...capabilities
|
|
18145
|
+
};
|
|
18146
|
+
}
|
|
18147
|
+
/**
|
|
18148
|
+
* 从缓存中查找插件的 install_id
|
|
18149
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
18150
|
+
*
|
|
18151
|
+
* @param pluginName - 插件名称
|
|
18152
|
+
* @returns install_id 或 null
|
|
18153
|
+
*/
|
|
18154
|
+
async findPluginInstallId(pluginName) {
|
|
18155
|
+
const cached = this.pluginCache.get(pluginName);
|
|
18156
|
+
if (cached) return cached.installId;
|
|
18157
|
+
await this.getInstalledPlugins();
|
|
18158
|
+
return this.pluginCache.get(pluginName)?.installId || null;
|
|
18159
|
+
}
|
|
18160
|
+
/**
|
|
18161
|
+
* 从缓存中查找 marketplace ID
|
|
18162
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
18163
|
+
*/
|
|
18164
|
+
async findMarketplaceId(nameOrId) {
|
|
18165
|
+
const cached = this.marketplaceCache.get(nameOrId);
|
|
18166
|
+
if (cached) return cached.id;
|
|
18167
|
+
await this.getPluginMarketplaces();
|
|
18168
|
+
return this.marketplaceCache.get(nameOrId)?.id || null;
|
|
18169
|
+
}
|
|
18170
|
+
/**
|
|
18171
|
+
* 提取 API 错误信息
|
|
18172
|
+
* 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
|
|
18173
|
+
*/
|
|
18174
|
+
extractErrorMessage(error) {
|
|
18175
|
+
if (error instanceof AxiosError) {
|
|
18176
|
+
const status = error.response?.status;
|
|
18177
|
+
const apiResponse = error.response?.data;
|
|
18178
|
+
const parts = [];
|
|
18179
|
+
if (status) parts.push(`HTTP ${status}`);
|
|
18180
|
+
if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
|
|
18181
|
+
if (apiResponse?.msg) parts.push(apiResponse.msg);
|
|
18182
|
+
else if (error.message) parts.push(error.message);
|
|
18183
|
+
const errorMessage = parts.join(" - ");
|
|
18184
|
+
this.logger?.error("[CloudAgentProvider] API Error:", {
|
|
18185
|
+
status,
|
|
18186
|
+
code: apiResponse?.code,
|
|
18187
|
+
msg: apiResponse?.msg,
|
|
18188
|
+
requestId: apiResponse?.requestId,
|
|
18189
|
+
url: error.config?.url,
|
|
18190
|
+
method: error.config?.method
|
|
18191
|
+
});
|
|
18192
|
+
return errorMessage;
|
|
18193
|
+
}
|
|
18194
|
+
if (error instanceof Error) return error.message;
|
|
18195
|
+
return "Unknown error";
|
|
18196
|
+
}
|
|
18197
|
+
/**
|
|
18198
|
+
* 映射后端 source_type 到前端类型
|
|
18199
|
+
*/
|
|
18200
|
+
mapSourceType(sourceType) {
|
|
18201
|
+
switch (sourceType) {
|
|
18202
|
+
case "github": return "github";
|
|
18203
|
+
case "official": return "custom";
|
|
18204
|
+
default: return "custom";
|
|
18205
|
+
}
|
|
18206
|
+
}
|
|
18207
|
+
/**
|
|
18208
|
+
* 解析 capabilities JSON 字符串
|
|
18209
|
+
*/
|
|
18210
|
+
parseCapabilities(capabilitiesStr) {
|
|
18211
|
+
try {
|
|
18212
|
+
const cap = JSON.parse(capabilitiesStr);
|
|
18213
|
+
return {
|
|
18214
|
+
commands: cap.commands,
|
|
18215
|
+
skills: cap.skills,
|
|
18216
|
+
mcpServers: cap.mcp,
|
|
18217
|
+
agents: cap.agents,
|
|
18218
|
+
hooks: cap.hooks,
|
|
18219
|
+
rules: cap.rules
|
|
18220
|
+
};
|
|
18221
|
+
} catch {
|
|
18222
|
+
return {};
|
|
18223
|
+
}
|
|
18224
|
+
}
|
|
18225
|
+
/**
|
|
18226
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
18227
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
18228
|
+
* 注入用户信息和浏览器环境等公共字段
|
|
18229
|
+
*/
|
|
18230
|
+
async reportTelemetry(eventName, payload) {
|
|
18231
|
+
try {
|
|
18232
|
+
const account = accountService.getAccount();
|
|
18233
|
+
const commonFields = {};
|
|
18234
|
+
if (account) {
|
|
18235
|
+
commonFields.userId = account.uid;
|
|
18236
|
+
commonFields.userNickname = account.nickname;
|
|
18237
|
+
if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
|
|
18238
|
+
if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
|
|
18239
|
+
}
|
|
18240
|
+
if (typeof navigator !== "undefined") {
|
|
18241
|
+
commonFields.userAgent = navigator.userAgent;
|
|
18242
|
+
commonFields.os = navigator.platform;
|
|
18243
|
+
}
|
|
18244
|
+
const events = [{
|
|
18245
|
+
eventCode: eventName,
|
|
18246
|
+
timestamp: Date.now(),
|
|
18247
|
+
reportDelay: 0,
|
|
18248
|
+
...commonFields,
|
|
18249
|
+
...payload
|
|
18250
|
+
}];
|
|
18251
|
+
await httpService.post("/v2/report", events);
|
|
18252
|
+
} catch (error) {
|
|
18253
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
18254
|
+
}
|
|
18255
|
+
}
|
|
18256
|
+
};
|
|
18257
|
+
|
|
18258
|
+
//#endregion
|
|
18259
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
|
|
18260
|
+
/**
|
|
18261
|
+
* Local Agent Connection
|
|
18262
|
+
* Wraps AcpJsonRpcClient to implement AgentConnection interface
|
|
18263
|
+
*
|
|
18264
|
+
* Uses IWidgetChannel for IPC communication with ExtensionHost
|
|
18265
|
+
* Migrated from ipc-agent-provider for unified local agent access
|
|
18266
|
+
*/
|
|
18267
|
+
|
|
18268
|
+
//#endregion
|
|
18269
|
+
//#region ../agent-provider/src/common/client/session.ts
|
|
18270
|
+
/**
|
|
18271
|
+
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
18272
|
+
*
|
|
18273
|
+
* This class wraps an AgentConnection and provides the session-centric API.
|
|
18274
|
+
* It is created by SessionManager when creating or loading sessions.
|
|
18275
|
+
*
|
|
18276
|
+
* @example
|
|
18277
|
+
* ```typescript
|
|
18278
|
+
* // Created by client.sessions.new() or client.sessions.load()
|
|
18279
|
+
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
18280
|
+
*
|
|
18281
|
+
* // Access agent state
|
|
18282
|
+
* console.log(session.agentState.status);
|
|
18283
|
+
*
|
|
18284
|
+
* // Send prompt
|
|
18285
|
+
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
18286
|
+
*
|
|
18287
|
+
* // Cleanup
|
|
18288
|
+
* session.disconnect();
|
|
18289
|
+
* ```
|
|
18290
|
+
*/
|
|
18291
|
+
var ActiveSessionImpl = class {
|
|
18292
|
+
/**
|
|
18293
|
+
* Create an ActiveSessionImpl instance
|
|
18294
|
+
*
|
|
18295
|
+
* @param sessionId - Session ID
|
|
18296
|
+
* @param agentId - Agent ID
|
|
18297
|
+
* @param connection - Already connected AgentConnection
|
|
18298
|
+
* @param options - Additional options
|
|
18299
|
+
*/
|
|
18300
|
+
constructor(sessionId, agentId, connection, options = {}) {
|
|
18301
|
+
this._availableCommands = [];
|
|
18302
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
18303
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
18304
|
+
this.connectionListeners = [];
|
|
18305
|
+
this._id = sessionId;
|
|
18306
|
+
this._agentId = agentId;
|
|
18307
|
+
this.connection = connection;
|
|
18308
|
+
this.logger = options.logger;
|
|
18309
|
+
this._getFilesystem = options.getFilesystem;
|
|
18310
|
+
this._connectionInfo = options.connectionInfo;
|
|
18311
|
+
this.setupConnectionEvents(connection);
|
|
17412
18312
|
this.agent = this.createAgentOperations();
|
|
17413
18313
|
this.prompts = this.createPromptsResource();
|
|
17414
18314
|
this.artifacts = this.createArtifactsResource();
|
|
@@ -17427,6 +18327,18 @@ var ActiveSessionImpl = class {
|
|
|
17427
18327
|
return this._agentId;
|
|
17428
18328
|
}
|
|
17429
18329
|
/**
|
|
18330
|
+
* Actual workspace path (set from newSession response _meta)
|
|
18331
|
+
*/
|
|
18332
|
+
get cwd() {
|
|
18333
|
+
return this._cwd;
|
|
18334
|
+
}
|
|
18335
|
+
/**
|
|
18336
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
18337
|
+
*/
|
|
18338
|
+
setCwd(cwd) {
|
|
18339
|
+
this._cwd = cwd;
|
|
18340
|
+
}
|
|
18341
|
+
/**
|
|
17430
18342
|
* Agent state (live connection state)
|
|
17431
18343
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
17432
18344
|
*/
|
|
@@ -17643,8 +18555,8 @@ var ActiveSessionImpl = class {
|
|
|
17643
18555
|
* await session.setMode('architect');
|
|
17644
18556
|
* ```
|
|
17645
18557
|
*/
|
|
17646
|
-
async setMode(modeId) {
|
|
17647
|
-
if (this._availableModes) {
|
|
18558
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
18559
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
17648
18560
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
17649
18561
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
17650
18562
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -17667,6 +18579,7 @@ var ActiveSessionImpl = class {
|
|
|
17667
18579
|
* ```
|
|
17668
18580
|
*/
|
|
17669
18581
|
async setSessionModel(modelId) {
|
|
18582
|
+
this._currentModelId = modelId;
|
|
17670
18583
|
await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
|
|
17671
18584
|
}
|
|
17672
18585
|
/**
|
|
@@ -17744,11 +18657,23 @@ var ActiveSessionImpl = class {
|
|
|
17744
18657
|
* Disconnect from the session/agent
|
|
17745
18658
|
*/
|
|
17746
18659
|
disconnect() {
|
|
18660
|
+
this.removeConnectionListeners();
|
|
17747
18661
|
this.connection.disconnect();
|
|
17748
18662
|
this.removeAllListeners();
|
|
17749
18663
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
17750
18664
|
}
|
|
17751
18665
|
/**
|
|
18666
|
+
* Detach the session from connection events without disconnecting the connection.
|
|
18667
|
+
* This should be called when the session is being replaced but the connection is shared.
|
|
18668
|
+
* Unlike disconnect(), this only removes event listeners without closing the connection.
|
|
18669
|
+
*/
|
|
18670
|
+
detach() {
|
|
18671
|
+
this.logger?.info(`Session ${this._id}: Detaching from connection events`);
|
|
18672
|
+
this.removeConnectionListeners();
|
|
18673
|
+
this.removeAllListeners();
|
|
18674
|
+
this.logger?.info(`Session ${this._id}: Detached successfully`);
|
|
18675
|
+
}
|
|
18676
|
+
/**
|
|
17752
18677
|
* Symbol.dispose for 'using' keyword support
|
|
17753
18678
|
* Automatically disconnects and cleans up when session goes out of scope
|
|
17754
18679
|
*
|
|
@@ -17767,60 +18692,85 @@ var ActiveSessionImpl = class {
|
|
|
17767
18692
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
17768
18693
|
return this.connection;
|
|
17769
18694
|
}
|
|
18695
|
+
/**
|
|
18696
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
18697
|
+
*/
|
|
18698
|
+
addConnectionListener(connection, event, listener) {
|
|
18699
|
+
connection.on(event, listener);
|
|
18700
|
+
this.connectionListeners.push({
|
|
18701
|
+
event,
|
|
18702
|
+
listener
|
|
18703
|
+
});
|
|
18704
|
+
}
|
|
18705
|
+
/**
|
|
18706
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
18707
|
+
*/
|
|
18708
|
+
removeConnectionListeners() {
|
|
18709
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
18710
|
+
this.connectionListeners = [];
|
|
18711
|
+
}
|
|
17770
18712
|
setupConnectionEvents(connection) {
|
|
17771
|
-
|
|
18713
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
17772
18714
|
this.emit("connected", void 0);
|
|
17773
18715
|
});
|
|
17774
|
-
|
|
18716
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
17775
18717
|
this.emit("disconnected", void 0);
|
|
17776
18718
|
});
|
|
17777
|
-
|
|
18719
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
17778
18720
|
this.emit("error", error);
|
|
17779
18721
|
});
|
|
17780
|
-
|
|
18722
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
18723
|
+
const notificationSessionId = update?.sessionId;
|
|
18724
|
+
if (notificationSessionId && notificationSessionId !== this._id) {
|
|
18725
|
+
console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
|
|
18726
|
+
return;
|
|
18727
|
+
}
|
|
17781
18728
|
this.emit("sessionUpdate", update);
|
|
17782
18729
|
});
|
|
17783
|
-
|
|
17784
|
-
|
|
17785
|
-
artifactUri: artifact.uri,
|
|
17786
|
-
artifactType: artifact.type
|
|
17787
|
-
});
|
|
18730
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
18731
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17788
18732
|
this.emit("artifactCreated", artifact);
|
|
17789
18733
|
});
|
|
17790
|
-
|
|
17791
|
-
|
|
17792
|
-
artifactUri: artifact.uri,
|
|
17793
|
-
artifactType: artifact.type
|
|
17794
|
-
});
|
|
18734
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
18735
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17795
18736
|
this.emit("artifactUpdated", artifact);
|
|
17796
18737
|
});
|
|
17797
|
-
|
|
17798
|
-
|
|
18738
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
18739
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
17799
18740
|
this.emit("artifactDeleted", artifact);
|
|
17800
18741
|
});
|
|
17801
|
-
|
|
18742
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
17802
18743
|
this.emit("permissionRequest", request);
|
|
17803
18744
|
});
|
|
17804
|
-
|
|
18745
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
17805
18746
|
this.emit("questionRequest", request);
|
|
17806
18747
|
});
|
|
17807
|
-
|
|
18748
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
17808
18749
|
this.prompts.cancel();
|
|
17809
18750
|
});
|
|
17810
|
-
|
|
18751
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
17811
18752
|
this.emit("usageUpdate", usage);
|
|
17812
18753
|
});
|
|
17813
|
-
|
|
18754
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
18755
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18756
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17814
18757
|
this.emit("checkpointCreated", checkpoint);
|
|
17815
18758
|
});
|
|
17816
|
-
|
|
18759
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
18760
|
+
const originSessionId = checkpoint.__sessionId;
|
|
18761
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
17817
18762
|
this.emit("checkpointUpdated", checkpoint);
|
|
17818
18763
|
});
|
|
17819
|
-
|
|
17820
|
-
|
|
17821
|
-
|
|
17822
|
-
|
|
17823
|
-
|
|
18764
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
18765
|
+
const originSessionId = command.__sessionId;
|
|
18766
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
18767
|
+
console.log("[Session] Command not forwarded:", {
|
|
18768
|
+
command,
|
|
18769
|
+
originSessionId,
|
|
18770
|
+
sessionId: this._id
|
|
18771
|
+
});
|
|
18772
|
+
return;
|
|
18773
|
+
}
|
|
17824
18774
|
this.emit("command", command);
|
|
17825
18775
|
});
|
|
17826
18776
|
}
|
|
@@ -17830,19 +18780,38 @@ var ActiveSessionImpl = class {
|
|
|
17830
18780
|
_meta: response._meta ?? void 0
|
|
17831
18781
|
};
|
|
17832
18782
|
}
|
|
18783
|
+
/**
|
|
18784
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
18785
|
+
* - media 类型:按 cwd 路径隔离(同 cwd 下不同 session 可共享 media)
|
|
18786
|
+
* - 其余类型:按 __sessionId 严格隔离
|
|
18787
|
+
*/
|
|
18788
|
+
shouldForwardArtifact(artifact) {
|
|
18789
|
+
const originSessionId = artifact.__sessionId;
|
|
18790
|
+
console.log("[Session] shouldForwardArtifact:", {
|
|
18791
|
+
artifact,
|
|
18792
|
+
originSessionId,
|
|
18793
|
+
sessionId: this._id,
|
|
18794
|
+
cwd: this.connection?.cwd
|
|
18795
|
+
});
|
|
18796
|
+
if (artifact.type === "media") {
|
|
18797
|
+
const cwd = this.connection?.cwd;
|
|
18798
|
+
const uri = artifact?.uri;
|
|
18799
|
+
if (cwd && uri) {
|
|
18800
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
18801
|
+
const uriPath = toPosix(uri.replace(/^(?:file|agent):\/\//, ""));
|
|
18802
|
+
const posixCwd = toPosix(cwd);
|
|
18803
|
+
const normalizedCwd = posixCwd.endsWith("/") ? posixCwd : posixCwd + "/";
|
|
18804
|
+
return uriPath.startsWith(normalizedCwd);
|
|
18805
|
+
}
|
|
18806
|
+
}
|
|
18807
|
+
if (originSessionId && originSessionId !== this._id) return false;
|
|
18808
|
+
return true;
|
|
18809
|
+
}
|
|
17833
18810
|
};
|
|
17834
18811
|
|
|
17835
18812
|
//#endregion
|
|
17836
18813
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
17837
18814
|
/**
|
|
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
18815
|
* SessionManager - Session lifecycle management
|
|
17847
18816
|
*
|
|
17848
18817
|
* This class manages the relationship between sessions and agents.
|
|
@@ -17892,7 +18861,8 @@ var SessionManager = class {
|
|
|
17892
18861
|
createdAt: agent.createdAt,
|
|
17893
18862
|
lastActivityAt: agent.updatedAt,
|
|
17894
18863
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
17895
|
-
isPlayground: agent.isPlayground
|
|
18864
|
+
isPlayground: agent.isPlayground,
|
|
18865
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
17896
18866
|
}));
|
|
17897
18867
|
console.log("[SessionManager] Returning sessions:", {
|
|
17898
18868
|
count: sessions.length,
|
|
@@ -17919,13 +18889,26 @@ var SessionManager = class {
|
|
|
17919
18889
|
if (this.provider.create) {
|
|
17920
18890
|
agentId = await this.provider.create(params);
|
|
17921
18891
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
18892
|
+
if (params.options?.onSessionPrepared) {
|
|
18893
|
+
const initialPrompt = params.options?.prompt;
|
|
18894
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
18895
|
+
params.options.onSessionPrepared({
|
|
18896
|
+
id: agentId,
|
|
18897
|
+
agentId,
|
|
18898
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
18899
|
+
status: "connecting",
|
|
18900
|
+
cwd: params.cwd || "",
|
|
18901
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
18902
|
+
});
|
|
18903
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
18904
|
+
}
|
|
17922
18905
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
17923
18906
|
const connection = await this.provider.connect(agentId);
|
|
17924
18907
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
17925
18908
|
const response = await connection.createSession({
|
|
17926
|
-
_meta: params._meta,
|
|
18909
|
+
_meta: params.options?._meta,
|
|
17927
18910
|
cwd: params.cwd,
|
|
17928
|
-
mcpServers: params.mcpServers
|
|
18911
|
+
mcpServers: params.options?.mcpServers
|
|
17929
18912
|
});
|
|
17930
18913
|
if (this.provider.registerSession) {
|
|
17931
18914
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -17938,14 +18921,10 @@ var SessionManager = class {
|
|
|
17938
18921
|
connectionInfo
|
|
17939
18922
|
});
|
|
17940
18923
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
17941
|
-
|
|
17942
|
-
|
|
17943
|
-
|
|
17944
|
-
|
|
17945
|
-
description: m.description ?? void 0
|
|
17946
|
-
}));
|
|
17947
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
17948
|
-
}
|
|
18924
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18925
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
18926
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
18927
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
17949
18928
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
17950
18929
|
return session;
|
|
17951
18930
|
}
|
|
@@ -17981,17 +18960,31 @@ var SessionManager = class {
|
|
|
17981
18960
|
mcpServers: params.mcpServers
|
|
17982
18961
|
});
|
|
17983
18962
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
17984
|
-
|
|
17985
|
-
|
|
17986
|
-
id: m.modelId,
|
|
17987
|
-
name: m.name,
|
|
17988
|
-
description: m.description ?? void 0
|
|
17989
|
-
}));
|
|
17990
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
17991
|
-
}
|
|
18963
|
+
const availableModels = this.extractAvailableModels(response);
|
|
18964
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
17992
18965
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
17993
18966
|
return session;
|
|
17994
18967
|
}
|
|
18968
|
+
/**
|
|
18969
|
+
* 从 ACP response 中提取可用模型列表
|
|
18970
|
+
*
|
|
18971
|
+
* 优先级:
|
|
18972
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
18973
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
18974
|
+
* 3. undefined - 都没有时返回 undefined
|
|
18975
|
+
*
|
|
18976
|
+
* @param response - ACP 响应对象
|
|
18977
|
+
* @returns ModelInfo[] | undefined
|
|
18978
|
+
*/
|
|
18979
|
+
extractAvailableModels(response) {
|
|
18980
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
18981
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
18982
|
+
const availableModels = response.models?.availableModels;
|
|
18983
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
18984
|
+
...model,
|
|
18985
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
18986
|
+
}));
|
|
18987
|
+
}
|
|
17995
18988
|
};
|
|
17996
18989
|
|
|
17997
18990
|
//#endregion
|
|
@@ -18098,9 +19091,29 @@ var AgentClient = class {
|
|
|
18098
19091
|
throw error;
|
|
18099
19092
|
}
|
|
18100
19093
|
},
|
|
18101
|
-
|
|
18102
|
-
this.logger?.debug("AgentClient.sessions.
|
|
18103
|
-
|
|
19094
|
+
updateStatus: async (sessionId, status) => {
|
|
19095
|
+
this.logger?.debug("AgentClient.sessions.updateStatus called", {
|
|
19096
|
+
sessionId,
|
|
19097
|
+
status
|
|
19098
|
+
});
|
|
19099
|
+
try {
|
|
19100
|
+
if (this.provider.updateStatus) {
|
|
19101
|
+
const result = await this.provider.updateStatus(sessionId, status);
|
|
19102
|
+
this.logger?.info("Session status updated successfully", {
|
|
19103
|
+
sessionId,
|
|
19104
|
+
status
|
|
19105
|
+
});
|
|
19106
|
+
return result;
|
|
19107
|
+
}
|
|
19108
|
+
throw new Error("Provider does not support updateStatus method");
|
|
19109
|
+
} catch (error) {
|
|
19110
|
+
this.logger?.error("Failed to update session status", error);
|
|
19111
|
+
throw error;
|
|
19112
|
+
}
|
|
19113
|
+
},
|
|
19114
|
+
move: async (sessionId) => {
|
|
19115
|
+
this.logger?.debug("AgentClient.sessions.move called", { sessionId });
|
|
19116
|
+
try {
|
|
18104
19117
|
if (this.provider.move) {
|
|
18105
19118
|
const result = await this.provider.move(sessionId);
|
|
18106
19119
|
this.logger?.info("Session moved successfully", { sessionId });
|
|
@@ -18145,6 +19158,100 @@ var AgentClient = class {
|
|
|
18145
19158
|
return [];
|
|
18146
19159
|
}
|
|
18147
19160
|
},
|
|
19161
|
+
getAutomationSnapshot: async () => {
|
|
19162
|
+
try {
|
|
19163
|
+
if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
|
|
19164
|
+
this.logger?.warn("Provider does not support getAutomationSnapshot");
|
|
19165
|
+
} catch (error) {
|
|
19166
|
+
this.logger?.error("Failed to get automation snapshot", error);
|
|
19167
|
+
}
|
|
19168
|
+
return {
|
|
19169
|
+
automations: [],
|
|
19170
|
+
inbox: [],
|
|
19171
|
+
runtimeState: {},
|
|
19172
|
+
updatedAt: Date.now()
|
|
19173
|
+
};
|
|
19174
|
+
},
|
|
19175
|
+
updateAutomation: async (payload) => {
|
|
19176
|
+
try {
|
|
19177
|
+
if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
|
|
19178
|
+
this.logger?.warn("Provider does not support updateAutomation");
|
|
19179
|
+
} catch (error) {
|
|
19180
|
+
this.logger?.error("Failed to update automation", error);
|
|
19181
|
+
return {
|
|
19182
|
+
success: false,
|
|
19183
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
19184
|
+
};
|
|
19185
|
+
}
|
|
19186
|
+
return {
|
|
19187
|
+
success: false,
|
|
19188
|
+
message: "Provider does not support updateAutomation"
|
|
19189
|
+
};
|
|
19190
|
+
},
|
|
19191
|
+
deleteAutomation: async (id) => {
|
|
19192
|
+
try {
|
|
19193
|
+
if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
|
|
19194
|
+
this.logger?.warn("Provider does not support deleteAutomation");
|
|
19195
|
+
} catch (error) {
|
|
19196
|
+
this.logger?.error("Failed to delete automation", error);
|
|
19197
|
+
return {
|
|
19198
|
+
success: false,
|
|
19199
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
19200
|
+
};
|
|
19201
|
+
}
|
|
19202
|
+
return {
|
|
19203
|
+
success: false,
|
|
19204
|
+
message: "Provider does not support deleteAutomation"
|
|
19205
|
+
};
|
|
19206
|
+
},
|
|
19207
|
+
archiveAutomationInboxItem: async (itemId) => {
|
|
19208
|
+
try {
|
|
19209
|
+
if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
|
|
19210
|
+
this.logger?.warn("Provider does not support archiveAutomationInboxItem");
|
|
19211
|
+
} catch (error) {
|
|
19212
|
+
this.logger?.error("Failed to archive automation inbox item", error);
|
|
19213
|
+
return {
|
|
19214
|
+
success: false,
|
|
19215
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
19216
|
+
};
|
|
19217
|
+
}
|
|
19218
|
+
return {
|
|
19219
|
+
success: false,
|
|
19220
|
+
message: "Provider does not support archiveAutomationInboxItem"
|
|
19221
|
+
};
|
|
19222
|
+
},
|
|
19223
|
+
deleteAutomationInboxItem: async (itemId) => {
|
|
19224
|
+
try {
|
|
19225
|
+
if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
|
|
19226
|
+
this.logger?.warn("Provider does not support deleteAutomationInboxItem");
|
|
19227
|
+
} catch (error) {
|
|
19228
|
+
this.logger?.error("Failed to delete automation inbox item", error);
|
|
19229
|
+
return {
|
|
19230
|
+
success: false,
|
|
19231
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
19232
|
+
};
|
|
19233
|
+
}
|
|
19234
|
+
return {
|
|
19235
|
+
success: false,
|
|
19236
|
+
message: "Provider does not support deleteAutomationInboxItem"
|
|
19237
|
+
};
|
|
19238
|
+
},
|
|
19239
|
+
testAutomation: async (id) => {
|
|
19240
|
+
try {
|
|
19241
|
+
if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
|
|
19242
|
+
this.logger?.warn("Provider does not support testAutomation");
|
|
19243
|
+
} catch (error) {
|
|
19244
|
+
this.logger?.error("Failed to test automation", error);
|
|
19245
|
+
return {
|
|
19246
|
+
success: false,
|
|
19247
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
19248
|
+
};
|
|
19249
|
+
}
|
|
19250
|
+
return {
|
|
19251
|
+
success: false,
|
|
19252
|
+
message: "Provider does not support testAutomation"
|
|
19253
|
+
};
|
|
19254
|
+
},
|
|
18148
19255
|
on: (event, handler) => {
|
|
18149
19256
|
if (this.provider.on) this.provider.on(event, handler);
|
|
18150
19257
|
else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
|
|
@@ -18188,164 +19295,517 @@ var AgentClient = class {
|
|
|
18188
19295
|
error: "Provider does not support pickFile"
|
|
18189
19296
|
};
|
|
18190
19297
|
} catch (error) {
|
|
18191
|
-
this.logger?.error("Failed to pick file", error);
|
|
18192
|
-
return {
|
|
18193
|
-
files: [],
|
|
18194
|
-
canceled: true,
|
|
18195
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
18196
|
-
};
|
|
19298
|
+
this.logger?.error("Failed to pick file", error);
|
|
19299
|
+
return {
|
|
19300
|
+
files: [],
|
|
19301
|
+
canceled: true,
|
|
19302
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19303
|
+
};
|
|
19304
|
+
}
|
|
19305
|
+
},
|
|
19306
|
+
pickFolder: async (params) => {
|
|
19307
|
+
try {
|
|
19308
|
+
if (this.provider && this.provider.pickFolder) {
|
|
19309
|
+
const result = await this.provider.pickFolder(params);
|
|
19310
|
+
this.logger?.info("Folder picker completed", {
|
|
19311
|
+
folderPaths: result.folderPaths,
|
|
19312
|
+
canceled: result.canceled
|
|
19313
|
+
});
|
|
19314
|
+
return result;
|
|
19315
|
+
}
|
|
19316
|
+
return {
|
|
19317
|
+
folderPaths: [],
|
|
19318
|
+
canceled: true,
|
|
19319
|
+
error: "Provider does not support pickFolder"
|
|
19320
|
+
};
|
|
19321
|
+
} catch (error) {
|
|
19322
|
+
this.logger?.error("Failed to pick folder", error);
|
|
19323
|
+
return {
|
|
19324
|
+
folderPaths: [],
|
|
19325
|
+
canceled: true,
|
|
19326
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19327
|
+
};
|
|
19328
|
+
}
|
|
19329
|
+
},
|
|
19330
|
+
uploadFile: async (params) => {
|
|
19331
|
+
try {
|
|
19332
|
+
if (this.provider && this.provider.uploadFile) {
|
|
19333
|
+
const result = await this.provider.uploadFile(params);
|
|
19334
|
+
this.logger?.info("File upload completed", {
|
|
19335
|
+
count: params.files.length,
|
|
19336
|
+
success: result.success
|
|
19337
|
+
});
|
|
19338
|
+
return result;
|
|
19339
|
+
}
|
|
19340
|
+
return {
|
|
19341
|
+
success: false,
|
|
19342
|
+
error: "Provider does not support uploadFile"
|
|
19343
|
+
};
|
|
19344
|
+
} catch (error) {
|
|
19345
|
+
this.logger?.error("Failed to upload file", error);
|
|
19346
|
+
return {
|
|
19347
|
+
success: false,
|
|
19348
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19349
|
+
};
|
|
19350
|
+
}
|
|
19351
|
+
},
|
|
19352
|
+
searchFile: async (params) => {
|
|
19353
|
+
try {
|
|
19354
|
+
if (this.provider && this.provider.searchFile) {
|
|
19355
|
+
const result = await this.provider.searchFile(params);
|
|
19356
|
+
this.logger?.info("File search completed", {
|
|
19357
|
+
resultCount: result.results.length,
|
|
19358
|
+
hasError: !!result.error
|
|
19359
|
+
});
|
|
19360
|
+
return result;
|
|
19361
|
+
}
|
|
19362
|
+
return {
|
|
19363
|
+
results: [],
|
|
19364
|
+
error: "Provider does not support searchFile"
|
|
19365
|
+
};
|
|
19366
|
+
} catch (error) {
|
|
19367
|
+
this.logger?.error("Failed to search file", error);
|
|
19368
|
+
return {
|
|
19369
|
+
results: [],
|
|
19370
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19371
|
+
};
|
|
19372
|
+
}
|
|
19373
|
+
},
|
|
19374
|
+
getSubagentList: async (params) => {
|
|
19375
|
+
try {
|
|
19376
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
19377
|
+
const result = await this.provider.getSubagentList(params);
|
|
19378
|
+
this.logger?.info("Subagent list retrieved", {
|
|
19379
|
+
resultCount: result.results.length,
|
|
19380
|
+
hasError: !!result.error
|
|
19381
|
+
});
|
|
19382
|
+
return result;
|
|
19383
|
+
}
|
|
19384
|
+
return {
|
|
19385
|
+
results: [],
|
|
19386
|
+
error: "Provider does not support getSubagentList"
|
|
19387
|
+
};
|
|
19388
|
+
} catch (error) {
|
|
19389
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
19390
|
+
return {
|
|
19391
|
+
results: [],
|
|
19392
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19393
|
+
};
|
|
19394
|
+
}
|
|
19395
|
+
},
|
|
19396
|
+
getSkillList: async (params) => {
|
|
19397
|
+
try {
|
|
19398
|
+
if (this.provider && this.provider.getSkillList) {
|
|
19399
|
+
const result = await this.provider.getSkillList(params);
|
|
19400
|
+
this.logger?.info("Skill list retrieved", {
|
|
19401
|
+
resultCount: result.results.length,
|
|
19402
|
+
hasError: !!result.error
|
|
19403
|
+
});
|
|
19404
|
+
return result;
|
|
19405
|
+
}
|
|
19406
|
+
return {
|
|
19407
|
+
results: [],
|
|
19408
|
+
error: "Provider does not support getSkillList"
|
|
19409
|
+
};
|
|
19410
|
+
} catch (error) {
|
|
19411
|
+
this.logger?.error("Failed to get skill list", error);
|
|
19412
|
+
return {
|
|
19413
|
+
results: [],
|
|
19414
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19415
|
+
};
|
|
19416
|
+
}
|
|
19417
|
+
},
|
|
19418
|
+
importSkill: async (params) => {
|
|
19419
|
+
try {
|
|
19420
|
+
if (this.provider && this.provider.importSkill) {
|
|
19421
|
+
const result = await this.provider.importSkill(params);
|
|
19422
|
+
this.logger?.info("Import skill completed", {
|
|
19423
|
+
success: result.success,
|
|
19424
|
+
hasError: !!result.error
|
|
19425
|
+
});
|
|
19426
|
+
return result;
|
|
19427
|
+
}
|
|
19428
|
+
return {
|
|
19429
|
+
success: false,
|
|
19430
|
+
error: "Provider does not support importSkill"
|
|
19431
|
+
};
|
|
19432
|
+
} catch (error) {
|
|
19433
|
+
this.logger?.error("Failed to import skill", error);
|
|
19434
|
+
return {
|
|
19435
|
+
success: false,
|
|
19436
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19437
|
+
};
|
|
19438
|
+
}
|
|
19439
|
+
},
|
|
19440
|
+
batchTogglePlugins: async (request) => {
|
|
19441
|
+
try {
|
|
19442
|
+
if (this.provider && this.provider.batchTogglePlugins) {
|
|
19443
|
+
const result = await this.provider.batchTogglePlugins(request);
|
|
19444
|
+
this.logger?.info("Batch toggle plugins completed", {
|
|
19445
|
+
succeededCount: result.succeededPlugins.length,
|
|
19446
|
+
failedCount: result.failedPlugins.length
|
|
19447
|
+
});
|
|
19448
|
+
return result;
|
|
19449
|
+
}
|
|
19450
|
+
return {
|
|
19451
|
+
success: false,
|
|
19452
|
+
succeededPlugins: [],
|
|
19453
|
+
failedPlugins: request.items.map((item) => ({
|
|
19454
|
+
...item,
|
|
19455
|
+
error: "Provider does not support batchTogglePlugins"
|
|
19456
|
+
}))
|
|
19457
|
+
};
|
|
19458
|
+
} catch (error) {
|
|
19459
|
+
this.logger?.error("Failed to batch toggle plugins", error);
|
|
19460
|
+
return {
|
|
19461
|
+
success: false,
|
|
19462
|
+
succeededPlugins: [],
|
|
19463
|
+
failedPlugins: request.items.map((item) => ({
|
|
19464
|
+
...item,
|
|
19465
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19466
|
+
}))
|
|
19467
|
+
};
|
|
19468
|
+
}
|
|
19469
|
+
},
|
|
19470
|
+
getInstalledPlugins: async (forceRefresh) => {
|
|
19471
|
+
try {
|
|
19472
|
+
if (this.provider && "getInstalledPlugins" in this.provider && typeof this.provider.getInstalledPlugins === "function") {
|
|
19473
|
+
const result = await this.provider.getInstalledPlugins(forceRefresh);
|
|
19474
|
+
this.logger?.info("Got installed plugins", { count: result?.length ?? 0 });
|
|
19475
|
+
return result;
|
|
19476
|
+
}
|
|
19477
|
+
this.logger?.warn("Provider does not support getInstalledPlugins");
|
|
19478
|
+
return [];
|
|
19479
|
+
} catch (error) {
|
|
19480
|
+
this.logger?.error("Failed to get installed plugins", error);
|
|
19481
|
+
return [];
|
|
19482
|
+
}
|
|
19483
|
+
},
|
|
19484
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
19485
|
+
try {
|
|
19486
|
+
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
19487
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
19488
|
+
this.logger?.info("Install plugins", {
|
|
19489
|
+
pluginNames,
|
|
19490
|
+
marketplaceName,
|
|
19491
|
+
success: result.success
|
|
19492
|
+
});
|
|
19493
|
+
return result;
|
|
19494
|
+
}
|
|
19495
|
+
this.logger?.warn("Provider does not support installPlugins");
|
|
19496
|
+
return {
|
|
19497
|
+
success: false,
|
|
19498
|
+
error: "Provider does not support installPlugins"
|
|
19499
|
+
};
|
|
19500
|
+
} catch (error) {
|
|
19501
|
+
this.logger?.error("Failed to install plugins", error);
|
|
19502
|
+
return {
|
|
19503
|
+
success: false,
|
|
19504
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19505
|
+
};
|
|
19506
|
+
}
|
|
19507
|
+
},
|
|
19508
|
+
uninstallPlugin: async (pluginName, marketplaceName, scope) => {
|
|
19509
|
+
try {
|
|
19510
|
+
if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
|
|
19511
|
+
const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
|
|
19512
|
+
this.logger?.info("Uninstall plugin", {
|
|
19513
|
+
pluginName,
|
|
19514
|
+
marketplaceName,
|
|
19515
|
+
scope,
|
|
19516
|
+
success: result.success
|
|
19517
|
+
});
|
|
19518
|
+
return result;
|
|
19519
|
+
}
|
|
19520
|
+
this.logger?.warn("Provider does not support uninstallPlugin");
|
|
19521
|
+
return {
|
|
19522
|
+
success: false,
|
|
19523
|
+
error: "Provider does not support uninstallPlugin"
|
|
19524
|
+
};
|
|
19525
|
+
} catch (error) {
|
|
19526
|
+
this.logger?.error("Failed to uninstall plugin", error);
|
|
19527
|
+
return {
|
|
19528
|
+
success: false,
|
|
19529
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19530
|
+
};
|
|
19531
|
+
}
|
|
19532
|
+
},
|
|
19533
|
+
updatePlugin: async (pluginName, marketplaceName) => {
|
|
19534
|
+
try {
|
|
19535
|
+
if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
|
|
19536
|
+
const result = await this.provider.updatePlugin(pluginName, marketplaceName);
|
|
19537
|
+
this.logger?.info("Update plugin", {
|
|
19538
|
+
pluginName,
|
|
19539
|
+
marketplaceName,
|
|
19540
|
+
success: result.success
|
|
19541
|
+
});
|
|
19542
|
+
return result;
|
|
19543
|
+
}
|
|
19544
|
+
this.logger?.warn("Provider does not support updatePlugin");
|
|
19545
|
+
return {
|
|
19546
|
+
success: false,
|
|
19547
|
+
error: "Provider does not support updatePlugin"
|
|
19548
|
+
};
|
|
19549
|
+
} catch (error) {
|
|
19550
|
+
this.logger?.error("Failed to update plugin", error);
|
|
19551
|
+
return {
|
|
19552
|
+
success: false,
|
|
19553
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
19554
|
+
};
|
|
19555
|
+
}
|
|
19556
|
+
},
|
|
19557
|
+
getPluginMarketplaces: async (forceRefresh) => {
|
|
19558
|
+
try {
|
|
19559
|
+
if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
|
|
19560
|
+
const result = await this.provider.getPluginMarketplaces(forceRefresh);
|
|
19561
|
+
this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
|
|
19562
|
+
return result;
|
|
19563
|
+
}
|
|
19564
|
+
this.logger?.warn("Provider does not support getPluginMarketplaces");
|
|
19565
|
+
return [];
|
|
19566
|
+
} catch (error) {
|
|
19567
|
+
this.logger?.error("Failed to get plugin marketplaces", error);
|
|
19568
|
+
return [];
|
|
19569
|
+
}
|
|
19570
|
+
},
|
|
19571
|
+
getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
|
|
19572
|
+
try {
|
|
19573
|
+
if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
|
|
19574
|
+
const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
|
|
19575
|
+
this.logger?.info("Got marketplace plugins", {
|
|
19576
|
+
marketplaceName,
|
|
19577
|
+
count: result?.length ?? 0
|
|
19578
|
+
});
|
|
19579
|
+
return result;
|
|
19580
|
+
}
|
|
19581
|
+
this.logger?.warn("Provider does not support getMarketplacePlugins");
|
|
19582
|
+
return [];
|
|
19583
|
+
} catch (error) {
|
|
19584
|
+
this.logger?.error("Failed to get marketplace plugins", error);
|
|
19585
|
+
return [];
|
|
18197
19586
|
}
|
|
18198
19587
|
},
|
|
18199
|
-
|
|
19588
|
+
getPluginDetail: async (pluginName, marketplaceName) => {
|
|
18200
19589
|
try {
|
|
18201
|
-
if (this.provider && this.provider.
|
|
18202
|
-
const result = await this.provider.
|
|
18203
|
-
this.logger?.info("
|
|
18204
|
-
|
|
18205
|
-
|
|
19590
|
+
if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
|
|
19591
|
+
const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
|
|
19592
|
+
this.logger?.info("Got plugin detail", {
|
|
19593
|
+
pluginName,
|
|
19594
|
+
marketplaceName
|
|
18206
19595
|
});
|
|
18207
19596
|
return result;
|
|
18208
19597
|
}
|
|
18209
|
-
|
|
18210
|
-
|
|
18211
|
-
canceled: true,
|
|
18212
|
-
error: "Provider does not support pickFolder"
|
|
18213
|
-
};
|
|
19598
|
+
this.logger?.warn("Provider does not support getPluginDetail");
|
|
19599
|
+
return null;
|
|
18214
19600
|
} catch (error) {
|
|
18215
|
-
this.logger?.error("Failed to
|
|
18216
|
-
return
|
|
18217
|
-
folderPaths: [],
|
|
18218
|
-
canceled: true,
|
|
18219
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
18220
|
-
};
|
|
19601
|
+
this.logger?.error("Failed to get plugin detail", error);
|
|
19602
|
+
return null;
|
|
18221
19603
|
}
|
|
18222
19604
|
},
|
|
18223
|
-
|
|
19605
|
+
addPluginMarketplace: async (source, name) => {
|
|
18224
19606
|
try {
|
|
18225
|
-
if (this.provider && this.provider.
|
|
18226
|
-
const result = await this.provider.
|
|
18227
|
-
this.logger?.info("
|
|
18228
|
-
|
|
19607
|
+
if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
|
|
19608
|
+
const result = await this.provider.addPluginMarketplace(source, name);
|
|
19609
|
+
this.logger?.info("Add plugin marketplace", {
|
|
19610
|
+
source,
|
|
19611
|
+
name,
|
|
18229
19612
|
success: result.success
|
|
18230
19613
|
});
|
|
18231
19614
|
return result;
|
|
18232
19615
|
}
|
|
19616
|
+
this.logger?.warn("Provider does not support addPluginMarketplace");
|
|
18233
19617
|
return {
|
|
18234
19618
|
success: false,
|
|
18235
|
-
error: "Provider does not support
|
|
19619
|
+
error: "Provider does not support addPluginMarketplace"
|
|
18236
19620
|
};
|
|
18237
19621
|
} catch (error) {
|
|
18238
|
-
this.logger?.error("Failed to
|
|
19622
|
+
this.logger?.error("Failed to add plugin marketplace", error);
|
|
18239
19623
|
return {
|
|
18240
19624
|
success: false,
|
|
18241
19625
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
18242
19626
|
};
|
|
18243
19627
|
}
|
|
18244
19628
|
},
|
|
18245
|
-
|
|
19629
|
+
removePluginMarketplace: async (marketplaceName) => {
|
|
18246
19630
|
try {
|
|
18247
|
-
if (this.provider && this.provider.
|
|
18248
|
-
const result = await this.provider.
|
|
18249
|
-
this.logger?.info("
|
|
18250
|
-
|
|
18251
|
-
|
|
19631
|
+
if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
|
|
19632
|
+
const result = await this.provider.removePluginMarketplace(marketplaceName);
|
|
19633
|
+
this.logger?.info("Remove plugin marketplace", {
|
|
19634
|
+
marketplaceName,
|
|
19635
|
+
success: result.success
|
|
18252
19636
|
});
|
|
18253
19637
|
return result;
|
|
18254
19638
|
}
|
|
19639
|
+
this.logger?.warn("Provider does not support removePluginMarketplace");
|
|
18255
19640
|
return {
|
|
18256
|
-
|
|
18257
|
-
error: "Provider does not support
|
|
19641
|
+
success: false,
|
|
19642
|
+
error: "Provider does not support removePluginMarketplace"
|
|
18258
19643
|
};
|
|
18259
19644
|
} catch (error) {
|
|
18260
|
-
this.logger?.error("Failed to
|
|
19645
|
+
this.logger?.error("Failed to remove plugin marketplace", error);
|
|
18261
19646
|
return {
|
|
18262
|
-
|
|
19647
|
+
success: false,
|
|
18263
19648
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
18264
19649
|
};
|
|
18265
19650
|
}
|
|
18266
19651
|
},
|
|
18267
|
-
|
|
19652
|
+
refreshPluginMarketplace: async (marketplaceName) => {
|
|
18268
19653
|
try {
|
|
18269
|
-
if (this.provider && this.provider.
|
|
18270
|
-
const result = await this.provider.
|
|
18271
|
-
this.logger?.info("
|
|
18272
|
-
|
|
18273
|
-
|
|
19654
|
+
if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
|
|
19655
|
+
const result = await this.provider.refreshPluginMarketplace(marketplaceName);
|
|
19656
|
+
this.logger?.info("Refresh plugin marketplace", {
|
|
19657
|
+
marketplaceName,
|
|
19658
|
+
success: result.success
|
|
18274
19659
|
});
|
|
18275
19660
|
return result;
|
|
18276
19661
|
}
|
|
19662
|
+
this.logger?.warn("Provider does not support refreshPluginMarketplace");
|
|
18277
19663
|
return {
|
|
18278
19664
|
success: false,
|
|
18279
|
-
|
|
18280
|
-
failedPlugins: request.items.map((item) => ({
|
|
18281
|
-
...item,
|
|
18282
|
-
error: "Provider does not support batchTogglePlugins"
|
|
18283
|
-
}))
|
|
19665
|
+
error: "Provider does not support refreshPluginMarketplace"
|
|
18284
19666
|
};
|
|
18285
19667
|
} catch (error) {
|
|
18286
|
-
this.logger?.error("Failed to
|
|
19668
|
+
this.logger?.error("Failed to refresh plugin marketplace", error);
|
|
18287
19669
|
return {
|
|
18288
19670
|
success: false,
|
|
18289
|
-
|
|
18290
|
-
failedPlugins: request.items.map((item) => ({
|
|
18291
|
-
...item,
|
|
18292
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
18293
|
-
}))
|
|
19671
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
18294
19672
|
};
|
|
18295
19673
|
}
|
|
18296
19674
|
},
|
|
18297
|
-
|
|
19675
|
+
openFolderInNewWindow: async (folderPath) => {
|
|
18298
19676
|
try {
|
|
18299
|
-
if (this.provider && "
|
|
18300
|
-
|
|
18301
|
-
this.logger?.info("
|
|
18302
|
-
|
|
19677
|
+
if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
|
|
19678
|
+
await this.provider.openFolderInNewWindow(folderPath);
|
|
19679
|
+
this.logger?.info("Opened folder in new window", { folderPath });
|
|
19680
|
+
} else {
|
|
19681
|
+
this.logger?.warn("Provider does not support openFolderInNewWindow");
|
|
19682
|
+
throw new Error("Provider does not support openFolderInNewWindow");
|
|
18303
19683
|
}
|
|
18304
|
-
|
|
19684
|
+
} catch (error) {
|
|
19685
|
+
this.logger?.error("Failed to open folder in new window", error);
|
|
19686
|
+
throw error;
|
|
19687
|
+
}
|
|
19688
|
+
},
|
|
19689
|
+
getSupportScenes: async (locale) => {
|
|
19690
|
+
try {
|
|
19691
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
19692
|
+
this.logger?.warn("Provider does not support getSupportScenes");
|
|
18305
19693
|
return [];
|
|
18306
19694
|
} catch (error) {
|
|
18307
|
-
this.logger?.error("Failed to get
|
|
19695
|
+
this.logger?.error("Failed to get support scenes", error);
|
|
18308
19696
|
return [];
|
|
18309
19697
|
}
|
|
18310
19698
|
},
|
|
18311
|
-
|
|
19699
|
+
getProductScenes: async (locale) => {
|
|
18312
19700
|
try {
|
|
18313
|
-
if (this.provider
|
|
18314
|
-
const result = await this.provider.
|
|
18315
|
-
this.logger?.info("
|
|
18316
|
-
pluginNames,
|
|
18317
|
-
marketplaceName,
|
|
18318
|
-
success: result.success
|
|
18319
|
-
});
|
|
19701
|
+
if (this.provider?.getProductScenes) {
|
|
19702
|
+
const result = await this.provider.getProductScenes(locale);
|
|
19703
|
+
this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
|
|
18320
19704
|
return result;
|
|
18321
19705
|
}
|
|
18322
|
-
this.logger?.warn("Provider does not support
|
|
18323
|
-
return
|
|
18324
|
-
success: false,
|
|
18325
|
-
error: "Provider does not support installPlugins"
|
|
18326
|
-
};
|
|
19706
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
19707
|
+
return [];
|
|
18327
19708
|
} catch (error) {
|
|
18328
|
-
this.logger?.error("Failed to
|
|
18329
|
-
return
|
|
18330
|
-
success: false,
|
|
18331
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
18332
|
-
};
|
|
19709
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
19710
|
+
return [];
|
|
18333
19711
|
}
|
|
18334
19712
|
},
|
|
18335
|
-
|
|
19713
|
+
getAvailableCommands: async (params) => {
|
|
18336
19714
|
try {
|
|
18337
|
-
if (this.provider && "
|
|
18338
|
-
const result = await this.provider.
|
|
18339
|
-
this.logger?.info("Got
|
|
19715
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
19716
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
19717
|
+
this.logger?.info("Got available commands from provider", {
|
|
19718
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
19719
|
+
count: result?.length ?? 0
|
|
19720
|
+
});
|
|
18340
19721
|
return result;
|
|
18341
19722
|
}
|
|
18342
|
-
this.logger?.warn("Provider does not support
|
|
19723
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
18343
19724
|
return [];
|
|
18344
19725
|
} catch (error) {
|
|
18345
|
-
this.logger?.error("Failed to get
|
|
19726
|
+
this.logger?.error("Failed to get available commands", error);
|
|
18346
19727
|
return [];
|
|
18347
19728
|
}
|
|
18348
19729
|
},
|
|
19730
|
+
reportTelemetry: async (eventName, payload) => {
|
|
19731
|
+
try {
|
|
19732
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
19733
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
19734
|
+
} catch (error) {
|
|
19735
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
19736
|
+
}
|
|
19737
|
+
},
|
|
19738
|
+
getProductConfiguration: async () => {
|
|
19739
|
+
try {
|
|
19740
|
+
if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
|
|
19741
|
+
this.logger?.warn("Provider does not support getProductConfiguration");
|
|
19742
|
+
return {};
|
|
19743
|
+
} catch (error) {
|
|
19744
|
+
this.logger?.error("Failed to get product configuration", error);
|
|
19745
|
+
return {};
|
|
19746
|
+
}
|
|
19747
|
+
},
|
|
19748
|
+
getUserInfo: async () => {
|
|
19749
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() called");
|
|
19750
|
+
try {
|
|
19751
|
+
if (this.provider?.getUserInfo) {
|
|
19752
|
+
const result = await this.provider.getUserInfo();
|
|
19753
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
|
|
19754
|
+
return result;
|
|
19755
|
+
}
|
|
19756
|
+
this.logger?.warn("Provider does not support getUserInfo");
|
|
19757
|
+
return {};
|
|
19758
|
+
} catch (error) {
|
|
19759
|
+
this.logger?.error("Failed to get user info", error);
|
|
19760
|
+
return {};
|
|
19761
|
+
}
|
|
19762
|
+
},
|
|
19763
|
+
respondToSampling: async (sessionId, response) => {
|
|
19764
|
+
try {
|
|
19765
|
+
if (this.provider?.respondToSampling) {
|
|
19766
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
19767
|
+
this.logger?.info("Responded to sampling request", {
|
|
19768
|
+
sessionId,
|
|
19769
|
+
requestId: response.id,
|
|
19770
|
+
approved: response.approved
|
|
19771
|
+
});
|
|
19772
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
19773
|
+
} catch (error) {
|
|
19774
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
19775
|
+
throw error;
|
|
19776
|
+
}
|
|
19777
|
+
},
|
|
19778
|
+
respondToRoots: async (sessionId, response) => {
|
|
19779
|
+
try {
|
|
19780
|
+
if (this.provider?.respondToRoots) {
|
|
19781
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
19782
|
+
this.logger?.info("Responded to roots request", {
|
|
19783
|
+
sessionId,
|
|
19784
|
+
requestId: response.id,
|
|
19785
|
+
approved: response.approved
|
|
19786
|
+
});
|
|
19787
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
19788
|
+
} catch (error) {
|
|
19789
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
19790
|
+
throw error;
|
|
19791
|
+
}
|
|
19792
|
+
},
|
|
19793
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
19794
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
19795
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
19796
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
19797
|
+
}
|
|
19798
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
19799
|
+
return () => {};
|
|
19800
|
+
},
|
|
19801
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
19802
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
19803
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
19804
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
19805
|
+
}
|
|
19806
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
19807
|
+
return () => {};
|
|
19808
|
+
},
|
|
18349
19809
|
models: this.createModelsResource()
|
|
18350
19810
|
};
|
|
18351
19811
|
}
|
|
@@ -18412,6 +19872,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18412
19872
|
return AccountStatus;
|
|
18413
19873
|
}({});
|
|
18414
19874
|
|
|
19875
|
+
//#endregion
|
|
19876
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
19877
|
+
/**
|
|
19878
|
+
* OAuth Repository Service
|
|
19879
|
+
*
|
|
19880
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
19881
|
+
*/
|
|
19882
|
+
/**
|
|
19883
|
+
* OAuth Repository Service
|
|
19884
|
+
*
|
|
19885
|
+
* 提供仓库和分支的查询操作
|
|
19886
|
+
*/
|
|
19887
|
+
var OAuthRepositoryService = class {
|
|
19888
|
+
/**
|
|
19889
|
+
* 获取仓库分支列表
|
|
19890
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
19891
|
+
*
|
|
19892
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19893
|
+
* @param params 平台特定的查询参数
|
|
19894
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19895
|
+
* @param perPage 每页数量,最大100
|
|
19896
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
19897
|
+
*
|
|
19898
|
+
* @example
|
|
19899
|
+
* ```typescript
|
|
19900
|
+
* // GitHub
|
|
19901
|
+
* const branches = await service.getBranches('github', {
|
|
19902
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
19903
|
+
* repo: 'CodeBuddyIDE'
|
|
19904
|
+
* });
|
|
19905
|
+
*
|
|
19906
|
+
* // Gongfeng
|
|
19907
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
19908
|
+
* project_id: '1611499'
|
|
19909
|
+
* });
|
|
19910
|
+
*
|
|
19911
|
+
* // CNB
|
|
19912
|
+
* const branches = await service.getBranches('cnb', {
|
|
19913
|
+
* repo: 'genie/genie-ide'
|
|
19914
|
+
* });
|
|
19915
|
+
* ```
|
|
19916
|
+
*/
|
|
19917
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
19918
|
+
try {
|
|
19919
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
19920
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
19921
|
+
const apiResponse = await httpService.get(url);
|
|
19922
|
+
if (!apiResponse.data) {
|
|
19923
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
19924
|
+
return [];
|
|
19925
|
+
}
|
|
19926
|
+
const branches = apiResponse.data.branches || [];
|
|
19927
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
19928
|
+
return branches;
|
|
19929
|
+
} catch (error) {
|
|
19930
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
19931
|
+
throw error;
|
|
19932
|
+
}
|
|
19933
|
+
}
|
|
19934
|
+
/**
|
|
19935
|
+
* 获取仓库列表
|
|
19936
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
19937
|
+
*
|
|
19938
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
19939
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
19940
|
+
*
|
|
19941
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
19942
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
19943
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
19944
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
19945
|
+
* @param perPage 每页数量,最大100
|
|
19946
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
19947
|
+
*
|
|
19948
|
+
* @example
|
|
19949
|
+
* ```typescript
|
|
19950
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
19951
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
19952
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
19953
|
+
*
|
|
19954
|
+
* // Gongfeng
|
|
19955
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
19956
|
+
* // response.gongfeng_repos 是数组
|
|
19957
|
+
*
|
|
19958
|
+
* // CNB
|
|
19959
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
19960
|
+
* // response.cnb_repos 是数组
|
|
19961
|
+
* ```
|
|
19962
|
+
*/
|
|
19963
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
19964
|
+
try {
|
|
19965
|
+
const queryParams = new URLSearchParams();
|
|
19966
|
+
queryParams.append("page", String(page));
|
|
19967
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
19968
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
19969
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
19970
|
+
const apiResponse = await httpService.get(url);
|
|
19971
|
+
if (!apiResponse.data) {
|
|
19972
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
19973
|
+
return {};
|
|
19974
|
+
}
|
|
19975
|
+
const response = apiResponse.data;
|
|
19976
|
+
this.logRepositoryCounts(response);
|
|
19977
|
+
return response;
|
|
19978
|
+
} catch (error) {
|
|
19979
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
19980
|
+
throw error;
|
|
19981
|
+
}
|
|
19982
|
+
}
|
|
19983
|
+
/**
|
|
19984
|
+
* 构建分支查询参数
|
|
19985
|
+
*/
|
|
19986
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
19987
|
+
const queryParams = new URLSearchParams();
|
|
19988
|
+
queryParams.append("page", String(page));
|
|
19989
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
19990
|
+
if (connector === "github") {
|
|
19991
|
+
const githubParams = params;
|
|
19992
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
19993
|
+
queryParams.append("owner", githubParams.owner);
|
|
19994
|
+
queryParams.append("repo", githubParams.repo);
|
|
19995
|
+
} else if (connector === "gongfeng") {
|
|
19996
|
+
const gongfengParams = params;
|
|
19997
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
19998
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
19999
|
+
} else if (connector === "cnb") {
|
|
20000
|
+
const cnbParams = params;
|
|
20001
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
20002
|
+
queryParams.append("repo", cnbParams.repo);
|
|
20003
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
20004
|
+
return queryParams;
|
|
20005
|
+
}
|
|
20006
|
+
/**
|
|
20007
|
+
* 记录仓库数量日志
|
|
20008
|
+
*/
|
|
20009
|
+
logRepositoryCounts(response) {
|
|
20010
|
+
if (response.github_repos) {
|
|
20011
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
20012
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
20013
|
+
}
|
|
20014
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
20015
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
20016
|
+
}
|
|
20017
|
+
};
|
|
20018
|
+
/**
|
|
20019
|
+
* OAuth Repository Service 单例实例
|
|
20020
|
+
*/
|
|
20021
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
20022
|
+
|
|
18415
20023
|
//#endregion
|
|
18416
20024
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
18417
20025
|
/**
|
|
@@ -18420,28 +20028,24 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
18420
20028
|
* 封装与后端 API 的 HTTP 通信
|
|
18421
20029
|
*/
|
|
18422
20030
|
/**
|
|
18423
|
-
*
|
|
18424
|
-
*
|
|
20031
|
+
* 判断当前账号是否是 SSO 账号
|
|
20032
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
18425
20033
|
*/
|
|
18426
|
-
const
|
|
18427
|
-
|
|
18428
|
-
|
|
20034
|
+
const safeParseJSON = (jsonString) => {
|
|
20035
|
+
try {
|
|
20036
|
+
return JSON.parse(jsonString);
|
|
20037
|
+
} catch (error) {
|
|
20038
|
+
return {};
|
|
20039
|
+
}
|
|
18429
20040
|
};
|
|
18430
20041
|
/**
|
|
18431
|
-
*
|
|
18432
|
-
* - SSO
|
|
18433
|
-
* - 非 SSO
|
|
20042
|
+
* 根据路径获取完整 URL
|
|
20043
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
20044
|
+
* - 非 SSO 账号直接使用当前域名
|
|
20045
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
20046
|
+
* @returns 完整的 URL 地址
|
|
18434
20047
|
*/
|
|
18435
|
-
const
|
|
18436
|
-
const { hostname, protocol } = window.location;
|
|
18437
|
-
if (isSSODomain()) {
|
|
18438
|
-
const isCodebuddy = hostname.includes("codebuddy.cn");
|
|
18439
|
-
const isStaging = hostname.includes("staging");
|
|
18440
|
-
if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
|
|
18441
|
-
else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
|
|
18442
|
-
}
|
|
18443
|
-
return `${window.location.origin}/login`;
|
|
18444
|
-
};
|
|
20048
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
18445
20049
|
/** 获取当前域名的账号选择页面 URL */
|
|
18446
20050
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
18447
20051
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -18478,12 +20082,30 @@ var BackendProvider = class {
|
|
|
18478
20082
|
constructor(config) {
|
|
18479
20083
|
httpService.setBaseURL(config.baseUrl);
|
|
18480
20084
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
18481
|
-
httpService.onUnauthorized(() =>
|
|
18482
|
-
|
|
18483
|
-
|
|
18484
|
-
|
|
20085
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
20086
|
+
}
|
|
20087
|
+
/**
|
|
20088
|
+
* 处理 401 未授权错误
|
|
20089
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
20090
|
+
*
|
|
20091
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
20092
|
+
*/
|
|
20093
|
+
async handleUnauthorized() {
|
|
20094
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
20095
|
+
try {
|
|
20096
|
+
if (await this.refreshToken()) {
|
|
20097
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
20098
|
+
return;
|
|
20099
|
+
}
|
|
20100
|
+
throw new Error("Token refresh returned null");
|
|
20101
|
+
} catch (error) {
|
|
20102
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
20103
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
20104
|
+
this.logout().catch((logoutError) => {
|
|
20105
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
18485
20106
|
});
|
|
18486
|
-
|
|
20107
|
+
throw error;
|
|
20108
|
+
}
|
|
18487
20109
|
}
|
|
18488
20110
|
/**
|
|
18489
20111
|
* 获取当前账号信息
|
|
@@ -18528,7 +20150,7 @@ var BackendProvider = class {
|
|
|
18528
20150
|
return account;
|
|
18529
20151
|
}
|
|
18530
20152
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18531
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
20153
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18532
20154
|
accountService.setAccount(null);
|
|
18533
20155
|
return null;
|
|
18534
20156
|
} catch (error) {
|
|
@@ -18551,7 +20173,8 @@ var BackendProvider = class {
|
|
|
18551
20173
|
activeStatus: connector.active_status,
|
|
18552
20174
|
displayName: connector.display_name,
|
|
18553
20175
|
oauthClientId: connector.oauth_client_id,
|
|
18554
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
20176
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
20177
|
+
oauthAppName: connector.oauth_app_name
|
|
18555
20178
|
})) };
|
|
18556
20179
|
}
|
|
18557
20180
|
throw result;
|
|
@@ -18633,7 +20256,8 @@ var BackendProvider = class {
|
|
|
18633
20256
|
connectStatus: connector.connect_status,
|
|
18634
20257
|
displayName: connector.display_name,
|
|
18635
20258
|
oauthClientId: connector.oauth_client_id,
|
|
18636
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
20259
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
20260
|
+
oauthAppName: connector.oauth_app_name
|
|
18637
20261
|
})) };
|
|
18638
20262
|
}
|
|
18639
20263
|
throw result;
|
|
@@ -18724,6 +20348,9 @@ var BackendProvider = class {
|
|
|
18724
20348
|
PackageCode: void 0,
|
|
18725
20349
|
name: ""
|
|
18726
20350
|
};
|
|
20351
|
+
const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
|
|
20352
|
+
console.log("[PRODUCT_FEATURES]", productFeatures);
|
|
20353
|
+
if (!productFeatures.billing) return defaultPlan;
|
|
18727
20354
|
try {
|
|
18728
20355
|
const now = /* @__PURE__ */ new Date();
|
|
18729
20356
|
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
@@ -18745,10 +20372,11 @@ var BackendProvider = class {
|
|
|
18745
20372
|
if (!time) return 0;
|
|
18746
20373
|
return new Date(time).getTime();
|
|
18747
20374
|
};
|
|
18748
|
-
const dailyCredits = [CommodityCode.free
|
|
20375
|
+
const dailyCredits = [CommodityCode.free];
|
|
18749
20376
|
const planResources = resources.map((r) => {
|
|
18750
20377
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
18751
20378
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
20379
|
+
const refreshAt = parseTime(r.CycleEndTime) + 1e3;
|
|
18752
20380
|
return {
|
|
18753
20381
|
id: r.ResourceId,
|
|
18754
20382
|
name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
|
|
@@ -18758,7 +20386,7 @@ var BackendProvider = class {
|
|
|
18758
20386
|
used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
|
|
18759
20387
|
left: Number(r.CycleCapacityRemainPrecise) || 0,
|
|
18760
20388
|
expireAt: parseTime(endTime),
|
|
18761
|
-
refreshAt: isDaily ? void 0 :
|
|
20389
|
+
refreshAt: isDaily ? void 0 : refreshAt
|
|
18762
20390
|
};
|
|
18763
20391
|
}).sort((a, b) => {
|
|
18764
20392
|
const getPriority = (code) => {
|
|
@@ -18766,10 +20394,11 @@ var BackendProvider = class {
|
|
|
18766
20394
|
CommodityCode.proMon,
|
|
18767
20395
|
CommodityCode.proMonPlus,
|
|
18768
20396
|
CommodityCode.proYear,
|
|
20397
|
+
CommodityCode.freeMon,
|
|
18769
20398
|
CommodityCode.extra
|
|
18770
20399
|
].includes(code)) return 1;
|
|
18771
20400
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
18772
|
-
if ([CommodityCode.free
|
|
20401
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
18773
20402
|
return 4;
|
|
18774
20403
|
};
|
|
18775
20404
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -18890,38 +20519,69 @@ var BackendProvider = class {
|
|
|
18890
20519
|
*/
|
|
18891
20520
|
async login() {
|
|
18892
20521
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
18893
|
-
window.location.href = `${
|
|
20522
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
18894
20523
|
}
|
|
18895
20524
|
/**
|
|
18896
20525
|
* 登出账号
|
|
18897
|
-
*
|
|
20526
|
+
*
|
|
20527
|
+
* 策略:
|
|
20528
|
+
* - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
|
|
20529
|
+
* - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
|
|
18898
20530
|
*/
|
|
18899
20531
|
async logout() {
|
|
18900
|
-
const
|
|
20532
|
+
const account = accountService.getAccount();
|
|
20533
|
+
if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
|
|
20534
|
+
else await this.logoutViaHttp();
|
|
20535
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
20536
|
+
accountService.clearAccount();
|
|
20537
|
+
}
|
|
20538
|
+
/**
|
|
20539
|
+
* IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
|
|
20540
|
+
* 轮询 iframe URL 变化检测完成,兜底超时 5 秒
|
|
20541
|
+
*/
|
|
20542
|
+
async logoutViaIframe() {
|
|
20543
|
+
const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
|
|
18901
20544
|
try {
|
|
18902
20545
|
await new Promise((resolve) => {
|
|
18903
20546
|
const iframe = document.createElement("iframe");
|
|
18904
20547
|
iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
|
|
18905
|
-
iframe.src =
|
|
18906
|
-
|
|
18907
|
-
|
|
18908
|
-
|
|
18909
|
-
|
|
18910
|
-
|
|
20548
|
+
iframe.src = logoutUrl;
|
|
20549
|
+
let pollTimer;
|
|
20550
|
+
let settled = false;
|
|
20551
|
+
const done = () => {
|
|
20552
|
+
if (settled) return;
|
|
20553
|
+
settled = true;
|
|
20554
|
+
clearInterval(pollTimer);
|
|
18911
20555
|
clearTimeout(timeout);
|
|
18912
20556
|
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
18913
|
-
};
|
|
18914
|
-
iframe.onerror = () => {
|
|
18915
|
-
cleanup();
|
|
18916
20557
|
resolve();
|
|
18917
20558
|
};
|
|
20559
|
+
let wasRedirecting = false;
|
|
20560
|
+
pollTimer = setInterval(() => {
|
|
20561
|
+
try {
|
|
20562
|
+
const href = iframe.contentWindow?.location?.href;
|
|
20563
|
+
if (wasRedirecting && href) done();
|
|
20564
|
+
} catch {
|
|
20565
|
+
wasRedirecting = true;
|
|
20566
|
+
}
|
|
20567
|
+
}, 100);
|
|
20568
|
+
const timeout = setTimeout(done, 5e3);
|
|
20569
|
+
iframe.onerror = done;
|
|
18918
20570
|
document.body.appendChild(iframe);
|
|
18919
20571
|
});
|
|
18920
20572
|
} catch (error) {
|
|
18921
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
20573
|
+
console.error("[BackendProvider] logout via iframe failed:", error);
|
|
20574
|
+
}
|
|
20575
|
+
}
|
|
20576
|
+
/**
|
|
20577
|
+
* 非 IOA 企业登出:直接 HTTP 请求 /console/logout
|
|
20578
|
+
*/
|
|
20579
|
+
async logoutViaHttp() {
|
|
20580
|
+
try {
|
|
20581
|
+
await httpService.get("/console/logout");
|
|
20582
|
+
} catch (error) {
|
|
20583
|
+
console.error("[BackendProvider] logout via http failed:", error);
|
|
18922
20584
|
}
|
|
18923
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
18924
|
-
accountService.clearAccount();
|
|
18925
20585
|
}
|
|
18926
20586
|
/**
|
|
18927
20587
|
* 批量切换插件状态
|
|
@@ -18953,6 +20613,78 @@ var BackendProvider = class {
|
|
|
18953
20613
|
return null;
|
|
18954
20614
|
}
|
|
18955
20615
|
}
|
|
20616
|
+
/**
|
|
20617
|
+
* 刷新 Token
|
|
20618
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
20619
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
20620
|
+
*/
|
|
20621
|
+
async refreshToken() {
|
|
20622
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
20623
|
+
try {
|
|
20624
|
+
const account = await this.getAccount();
|
|
20625
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
20626
|
+
return account;
|
|
20627
|
+
} catch (error) {
|
|
20628
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
20629
|
+
return null;
|
|
20630
|
+
}
|
|
20631
|
+
}
|
|
20632
|
+
/**
|
|
20633
|
+
* 获取仓库分支列表
|
|
20634
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
20635
|
+
*
|
|
20636
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
20637
|
+
* @param params 平台特定的查询参数
|
|
20638
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
20639
|
+
* @param perPage 每页数量,最大100
|
|
20640
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
20641
|
+
*/
|
|
20642
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
20643
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
20644
|
+
}
|
|
20645
|
+
/**
|
|
20646
|
+
* 获取仓库列表
|
|
20647
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
20648
|
+
*
|
|
20649
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
20650
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
20651
|
+
*
|
|
20652
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
20653
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
20654
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
20655
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
20656
|
+
* @param perPage 每页数量,最大100
|
|
20657
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
20658
|
+
*/
|
|
20659
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
20660
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
20661
|
+
}
|
|
20662
|
+
/**
|
|
20663
|
+
* 保存待发送的输入内容到后端
|
|
20664
|
+
* API 端点: POST /api/v1/code-id
|
|
20665
|
+
*/
|
|
20666
|
+
async savePendingInput(code) {
|
|
20667
|
+
try {
|
|
20668
|
+
const result = await httpService.post("/api/v1/code-id", { code });
|
|
20669
|
+
return result?.codeId || result?.data?.codeId || null;
|
|
20670
|
+
} catch (e) {
|
|
20671
|
+
console.warn("[BackendProvider] savePendingInput failed:", e);
|
|
20672
|
+
return null;
|
|
20673
|
+
}
|
|
20674
|
+
}
|
|
20675
|
+
/**
|
|
20676
|
+
* 从后端加载待发送的输入内容
|
|
20677
|
+
* API 端点: GET /api/v1/code?id=xxx
|
|
20678
|
+
*/
|
|
20679
|
+
async loadPendingInput(codeId) {
|
|
20680
|
+
try {
|
|
20681
|
+
const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
|
|
20682
|
+
return result?.code || result?.data?.code || null;
|
|
20683
|
+
} catch (e) {
|
|
20684
|
+
console.warn("[BackendProvider] loadPendingInput failed:", e);
|
|
20685
|
+
return null;
|
|
20686
|
+
}
|
|
20687
|
+
}
|
|
18956
20688
|
};
|
|
18957
20689
|
/**
|
|
18958
20690
|
* 创建 BackendProvider 实例
|
|
@@ -18992,6 +20724,7 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
18992
20724
|
GET_FILE: "backend:get-file",
|
|
18993
20725
|
RELOAD_WINDOW: "backend:reload-window",
|
|
18994
20726
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
20727
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
18995
20728
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
18996
20729
|
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
18997
20730
|
};
|
|
@@ -19040,12 +20773,14 @@ var IPCBackendProvider = class {
|
|
|
19040
20773
|
*/
|
|
19041
20774
|
async getAccount() {
|
|
19042
20775
|
this.log("Getting account via IPC");
|
|
20776
|
+
const startTime = performance.now();
|
|
19043
20777
|
try {
|
|
19044
20778
|
const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
20779
|
+
this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
|
|
19045
20780
|
accountService.setAccount(account);
|
|
19046
20781
|
return account;
|
|
19047
20782
|
} catch (error) {
|
|
19048
|
-
this.log(
|
|
20783
|
+
this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
|
|
19049
20784
|
accountService.setAccount(null);
|
|
19050
20785
|
return null;
|
|
19051
20786
|
}
|
|
@@ -19287,6 +21022,20 @@ var IPCBackendProvider = class {
|
|
|
19287
21022
|
}
|
|
19288
21023
|
}
|
|
19289
21024
|
/**
|
|
21025
|
+
* 在外部浏览器中打开链接
|
|
21026
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
21027
|
+
* @param url 要打开的 URL
|
|
21028
|
+
*/
|
|
21029
|
+
async openExternal(url) {
|
|
21030
|
+
this.log("Opening external URL via IPC:", url);
|
|
21031
|
+
try {
|
|
21032
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
21033
|
+
} catch (error) {
|
|
21034
|
+
this.log("Open external request failed:", error);
|
|
21035
|
+
throw error;
|
|
21036
|
+
}
|
|
21037
|
+
}
|
|
21038
|
+
/**
|
|
19290
21039
|
* 批量切换插件状态
|
|
19291
21040
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
19292
21041
|
*/
|