@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.mjs
CHANGED
|
@@ -284,9 +284,9 @@ const ToolOutputSchemas = {
|
|
|
284
284
|
}),
|
|
285
285
|
search_content: z.object({
|
|
286
286
|
type: z.literal("search_content_result"),
|
|
287
|
-
|
|
287
|
+
path: z.string(),
|
|
288
288
|
pattern: z.string(),
|
|
289
|
-
|
|
289
|
+
glob: z.string(),
|
|
290
290
|
matches: z.array(z.object({
|
|
291
291
|
filePath: z.string(),
|
|
292
292
|
content: z.string(),
|
|
@@ -298,7 +298,7 @@ const ToolOutputSchemas = {
|
|
|
298
298
|
totalCount: z.number(),
|
|
299
299
|
hasMore: z.boolean(),
|
|
300
300
|
offset: z.number(),
|
|
301
|
-
|
|
301
|
+
headLimit: z.number(),
|
|
302
302
|
contextBefore: z.number(),
|
|
303
303
|
contextAfter: z.number(),
|
|
304
304
|
contextAround: z.number().optional(),
|
|
@@ -661,7 +661,8 @@ const ExtensionMethod = {
|
|
|
661
661
|
CHECKPOINT: "_codebuddy.ai/checkpoint",
|
|
662
662
|
USAGE: "_codebuddy.ai/usage",
|
|
663
663
|
COMMAND: "_codebuddy.ai/command",
|
|
664
|
-
AUTH_URL: "_codebuddy.ai/authUrl"
|
|
664
|
+
AUTH_URL: "_codebuddy.ai/authUrl",
|
|
665
|
+
FILE_HISTORY_SNAPSHOT: "_codebuddy.ai/file_history_snapshot"
|
|
665
666
|
};
|
|
666
667
|
/**
|
|
667
668
|
* All known extension methods
|
|
@@ -672,7 +673,8 @@ const KNOWN_EXTENSIONS = [
|
|
|
672
673
|
ExtensionMethod.CHECKPOINT,
|
|
673
674
|
ExtensionMethod.USAGE,
|
|
674
675
|
ExtensionMethod.COMMAND,
|
|
675
|
-
ExtensionMethod.AUTH_URL
|
|
676
|
+
ExtensionMethod.AUTH_URL,
|
|
677
|
+
ExtensionMethod.FILE_HISTORY_SNAPSHOT
|
|
676
678
|
];
|
|
677
679
|
|
|
678
680
|
//#endregion
|
|
@@ -723,7 +725,7 @@ function parseSSELine(line, currentEvent) {
|
|
|
723
725
|
};
|
|
724
726
|
}
|
|
725
727
|
function streamableHttp(options) {
|
|
726
|
-
const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, postTimeout = 3e4, backpressure = {} } = options;
|
|
728
|
+
const { endpoint, authToken, headers: customHeaders = {}, reconnect = {}, signal: externalSignal, fetch: customFetch = globalThis.fetch, onConnect, onDisconnect, onError, heartbeatTimeout = 6e4, connectionTimeout = 3e4, postTimeout = 3e4, backpressure = {} } = options;
|
|
727
729
|
const { enabled: reconnectEnabled = true, initialDelay = 1e3, maxDelay = 3e4, maxRetries = Infinity, jitter: jitterEnabled = true } = reconnect;
|
|
728
730
|
const { highWaterMark = 100, lowWaterMark = 50, pauseTimeout = 5e3 } = backpressure;
|
|
729
731
|
let connectionId;
|
|
@@ -934,11 +936,21 @@ function streamableHttp(options) {
|
|
|
934
936
|
const headers = buildHeaders();
|
|
935
937
|
headers["Accept"] = "text/event-stream";
|
|
936
938
|
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
937
|
-
const
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
});
|
|
939
|
+
const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
|
|
940
|
+
const connectController = new AbortController();
|
|
941
|
+
const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
|
|
942
|
+
if (externalSignal) externalSignal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
943
|
+
abortController.signal.addEventListener("abort", () => connectController.abort(), { once: true });
|
|
944
|
+
let response;
|
|
945
|
+
try {
|
|
946
|
+
response = await customFetch(endpoint, {
|
|
947
|
+
method: "GET",
|
|
948
|
+
headers,
|
|
949
|
+
signal: connectController.signal
|
|
950
|
+
});
|
|
951
|
+
} finally {
|
|
952
|
+
clearTimeout(connectTimer);
|
|
953
|
+
}
|
|
942
954
|
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
943
955
|
const newConnectionId = response.headers.get("Acp-Connection-Id");
|
|
944
956
|
if (!newConnectionId) throw new Error("Server did not return Acp-Connection-Id header");
|
|
@@ -1874,6 +1886,9 @@ var StreamableHttpClient = class {
|
|
|
1874
1886
|
headers: this.options.headers,
|
|
1875
1887
|
reconnect: this.options.reconnect,
|
|
1876
1888
|
fetch: this.options.fetch,
|
|
1889
|
+
heartbeatTimeout: this.options.heartbeatTimeout,
|
|
1890
|
+
postTimeout: this.options.postTimeout,
|
|
1891
|
+
connectionTimeout: this.options.connectionTimeout,
|
|
1877
1892
|
onConnect: (connectionId) => {
|
|
1878
1893
|
this.options.logger?.debug(`Transport connected: ${connectionId}`);
|
|
1879
1894
|
},
|
|
@@ -1948,10 +1963,6 @@ var StreamableHttpClient = class {
|
|
|
1948
1963
|
},
|
|
1949
1964
|
requestPermission: async (params) => this.handleRequestPermission(params),
|
|
1950
1965
|
extNotification: async (method, params) => {
|
|
1951
|
-
console.log("[ACP-Client] extNotification callback invoked:", {
|
|
1952
|
-
method,
|
|
1953
|
-
paramsKeys: Object.keys(params)
|
|
1954
|
-
});
|
|
1955
1966
|
await this.handleExtNotification(method, params);
|
|
1956
1967
|
},
|
|
1957
1968
|
extMethod: async (method, params) => this.handleExtMethod(method, params)
|
|
@@ -1959,10 +1970,15 @@ var StreamableHttpClient = class {
|
|
|
1959
1970
|
}
|
|
1960
1971
|
/**
|
|
1961
1972
|
* Create a new session
|
|
1973
|
+
*
|
|
1974
|
+
* Retries on transient network errors (e.g., proxy connection reset)
|
|
1975
|
+
* since session/new is idempotent and safe to retry.
|
|
1962
1976
|
*/
|
|
1963
1977
|
async createSession(cwd) {
|
|
1964
1978
|
this.ensureInitialized("createSession");
|
|
1965
|
-
|
|
1979
|
+
const maxRetries = 2;
|
|
1980
|
+
let lastError;
|
|
1981
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
1966
1982
|
const response = await this.connection.newSession({
|
|
1967
1983
|
cwd,
|
|
1968
1984
|
mcpServers: []
|
|
@@ -1970,8 +1986,16 @@ var StreamableHttpClient = class {
|
|
|
1970
1986
|
this.options.logger?.info(`Session created: ${response.sessionId}`);
|
|
1971
1987
|
return response;
|
|
1972
1988
|
} catch (err) {
|
|
1973
|
-
|
|
1989
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
1990
|
+
if (attempt < maxRetries && isRetryableNetworkError(err)) {
|
|
1991
|
+
const delay = 500 * Math.pow(2, attempt);
|
|
1992
|
+
this.options.logger?.warn(`session/new network error, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries}): ${lastError.message}`);
|
|
1993
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1994
|
+
continue;
|
|
1995
|
+
}
|
|
1996
|
+
throw new SessionError(`Failed to create session: ${lastError.message}`, void 0, lastError);
|
|
1974
1997
|
}
|
|
1998
|
+
throw new SessionError(`Failed to create session: ${lastError?.message}`, void 0, lastError);
|
|
1975
1999
|
}
|
|
1976
2000
|
/**
|
|
1977
2001
|
* Load an existing session
|
|
@@ -2195,6 +2219,18 @@ var StreamableHttpClient = class {
|
|
|
2195
2219
|
if (this.state !== "initialized") throw new InvalidStateError(operation, this.state, ["initialized"]);
|
|
2196
2220
|
}
|
|
2197
2221
|
};
|
|
2222
|
+
/**
|
|
2223
|
+
* Check if an error is a retryable network-level error.
|
|
2224
|
+
* Only network failures (TypeError from fetch) are retried, NOT HTTP errors (4xx/5xx).
|
|
2225
|
+
*/
|
|
2226
|
+
function isRetryableNetworkError(error) {
|
|
2227
|
+
if (error instanceof TypeError) return true;
|
|
2228
|
+
if (error instanceof Error) {
|
|
2229
|
+
const msg = error.message.toLowerCase();
|
|
2230
|
+
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");
|
|
2231
|
+
}
|
|
2232
|
+
return false;
|
|
2233
|
+
}
|
|
2198
2234
|
|
|
2199
2235
|
//#endregion
|
|
2200
2236
|
//#region ../agent-provider/src/common/providers/cloud-agent-provider/cloud-connection.ts
|
|
@@ -2230,7 +2266,7 @@ var CloudAgentConnection = class {
|
|
|
2230
2266
|
fetch: config.fetch,
|
|
2231
2267
|
clientCapabilities: config.clientCapabilities,
|
|
2232
2268
|
onSessionUpdate: (update) => {
|
|
2233
|
-
if (!this._isStreaming) this.emit("sessionUpdate", update);
|
|
2269
|
+
if (!this._isStreaming && this.isOwnSessionNotification(update)) this.emit("sessionUpdate", update);
|
|
2234
2270
|
},
|
|
2235
2271
|
onArtifact: (artifact, event) => {
|
|
2236
2272
|
console.log("[CloudConnection] onArtifact callback:", {
|
|
@@ -2244,10 +2280,41 @@ var CloudAgentConnection = class {
|
|
|
2244
2280
|
},
|
|
2245
2281
|
onUsageUpdate: (usage) => {
|
|
2246
2282
|
this.emit("usageUpdate", usage);
|
|
2283
|
+
},
|
|
2284
|
+
onExtNotification: (method, params) => {
|
|
2285
|
+
console.log("[CloudConnection] Received extNotification:", {
|
|
2286
|
+
method,
|
|
2287
|
+
paramsKeys: Object.keys(params)
|
|
2288
|
+
});
|
|
2289
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
2290
|
+
const action = params.action;
|
|
2291
|
+
const commandParams = params.params;
|
|
2292
|
+
console.log("[CloudConnection] Emitting command event:", {
|
|
2293
|
+
action,
|
|
2294
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
2295
|
+
});
|
|
2296
|
+
this.emit("command", {
|
|
2297
|
+
action,
|
|
2298
|
+
params: commandParams
|
|
2299
|
+
});
|
|
2300
|
+
}
|
|
2247
2301
|
}
|
|
2248
2302
|
});
|
|
2249
2303
|
this.setupEventForwarding();
|
|
2250
2304
|
}
|
|
2305
|
+
/**
|
|
2306
|
+
* Check whether a notification belongs to this connection's own session.
|
|
2307
|
+
*
|
|
2308
|
+
* CloudConnection.createSession() overrides sessionId to agentId, so the
|
|
2309
|
+
* rest of the client stack uses agentId as the canonical session
|
|
2310
|
+
* identifier. Notifications whose sessionId differs from agentId
|
|
2311
|
+
* originate from sub-agent sessions running inside the same sandbox and
|
|
2312
|
+
* should be silently ignored at this layer — the adapter layer handles
|
|
2313
|
+
* sub-agent messages independently via parentToolUseId in _meta.
|
|
2314
|
+
*/
|
|
2315
|
+
isOwnSessionNotification(notification) {
|
|
2316
|
+
return notification.sessionId === this.agentId;
|
|
2317
|
+
}
|
|
2251
2318
|
setupEventForwarding() {
|
|
2252
2319
|
this.client.on("connecting", () => {
|
|
2253
2320
|
this.emit("connecting", void 0);
|
|
@@ -2372,7 +2439,7 @@ var CloudAgentConnection = class {
|
|
|
2372
2439
|
}
|
|
2373
2440
|
async createSession(params) {
|
|
2374
2441
|
return {
|
|
2375
|
-
...await this.client.
|
|
2442
|
+
...await this.client.createSession(this.cwd),
|
|
2376
2443
|
sessionId: this.agentId
|
|
2377
2444
|
};
|
|
2378
2445
|
}
|
|
@@ -2408,6 +2475,7 @@ var CloudAgentConnection = class {
|
|
|
2408
2475
|
let resolveUpdate = null;
|
|
2409
2476
|
let done = false;
|
|
2410
2477
|
const listener = (update) => {
|
|
2478
|
+
if (!this.isOwnSessionNotification(update)) return;
|
|
2411
2479
|
if (resolveUpdate) {
|
|
2412
2480
|
resolveUpdate(update);
|
|
2413
2481
|
resolveUpdate = null;
|
|
@@ -2490,6 +2558,16 @@ var CloudAgentConnection = class {
|
|
|
2490
2558
|
get sessionConnectionInfo() {
|
|
2491
2559
|
return this._sessionConnectionInfo;
|
|
2492
2560
|
}
|
|
2561
|
+
async reportTelemetry(eventName, payload) {
|
|
2562
|
+
try {
|
|
2563
|
+
await this.client.extMethod("reportTelemetry", {
|
|
2564
|
+
eventName,
|
|
2565
|
+
payload
|
|
2566
|
+
});
|
|
2567
|
+
} catch (error) {
|
|
2568
|
+
console.warn("[CloudAgentConnection] reportTelemetry failed:", error);
|
|
2569
|
+
}
|
|
2570
|
+
}
|
|
2493
2571
|
async extMethod(method, params) {
|
|
2494
2572
|
return this.client.extMethod(method, params);
|
|
2495
2573
|
}
|
|
@@ -3092,7 +3170,7 @@ var utils_default = {
|
|
|
3092
3170
|
*
|
|
3093
3171
|
* @returns {Error} The created error.
|
|
3094
3172
|
*/
|
|
3095
|
-
function AxiosError(message, code, config, request, response) {
|
|
3173
|
+
function AxiosError$1(message, code, config, request, response) {
|
|
3096
3174
|
Error.call(this);
|
|
3097
3175
|
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
3098
3176
|
else this.stack = (/* @__PURE__ */ new Error()).stack;
|
|
@@ -3106,7 +3184,7 @@ function AxiosError(message, code, config, request, response) {
|
|
|
3106
3184
|
this.status = response.status ? response.status : null;
|
|
3107
3185
|
}
|
|
3108
3186
|
}
|
|
3109
|
-
utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
3187
|
+
utils_default.inherits(AxiosError$1, Error, { toJSON: function toJSON() {
|
|
3110
3188
|
return {
|
|
3111
3189
|
message: this.message,
|
|
3112
3190
|
name: this.name,
|
|
@@ -3121,7 +3199,7 @@ utils_default.inherits(AxiosError, Error, { toJSON: function toJSON() {
|
|
|
3121
3199
|
status: this.status
|
|
3122
3200
|
};
|
|
3123
3201
|
} });
|
|
3124
|
-
const prototype$1 = AxiosError.prototype;
|
|
3202
|
+
const prototype$1 = AxiosError$1.prototype;
|
|
3125
3203
|
const descriptors = {};
|
|
3126
3204
|
[
|
|
3127
3205
|
"ERR_BAD_OPTION_VALUE",
|
|
@@ -3139,22 +3217,22 @@ const descriptors = {};
|
|
|
3139
3217
|
].forEach((code) => {
|
|
3140
3218
|
descriptors[code] = { value: code };
|
|
3141
3219
|
});
|
|
3142
|
-
Object.defineProperties(AxiosError, descriptors);
|
|
3220
|
+
Object.defineProperties(AxiosError$1, descriptors);
|
|
3143
3221
|
Object.defineProperty(prototype$1, "isAxiosError", { value: true });
|
|
3144
|
-
AxiosError.from = (error, code, config, request, response, customProps) => {
|
|
3222
|
+
AxiosError$1.from = (error, code, config, request, response, customProps) => {
|
|
3145
3223
|
const axiosError = Object.create(prototype$1);
|
|
3146
3224
|
utils_default.toFlatObject(error, axiosError, function filter(obj) {
|
|
3147
3225
|
return obj !== Error.prototype;
|
|
3148
3226
|
}, (prop) => {
|
|
3149
3227
|
return prop !== "isAxiosError";
|
|
3150
3228
|
});
|
|
3151
|
-
AxiosError.call(axiosError, error.message, code, config, request, response);
|
|
3229
|
+
AxiosError$1.call(axiosError, error.message, code, config, request, response);
|
|
3152
3230
|
axiosError.cause = error;
|
|
3153
3231
|
axiosError.name = error.name;
|
|
3154
3232
|
customProps && Object.assign(axiosError, customProps);
|
|
3155
3233
|
return axiosError;
|
|
3156
3234
|
};
|
|
3157
|
-
var AxiosError_default = AxiosError;
|
|
3235
|
+
var AxiosError_default = AxiosError$1;
|
|
3158
3236
|
|
|
3159
3237
|
//#endregion
|
|
3160
3238
|
//#region ../agent-provider/node_modules/axios/lib/helpers/null.js
|
|
@@ -3233,7 +3311,7 @@ const predicates = utils_default.toFlatObject(utils_default, {}, null, function
|
|
|
3233
3311
|
*
|
|
3234
3312
|
* @returns
|
|
3235
3313
|
*/
|
|
3236
|
-
function toFormData(obj, formData, options) {
|
|
3314
|
+
function toFormData$1(obj, formData, options) {
|
|
3237
3315
|
if (!utils_default.isObject(obj)) throw new TypeError("target must be an object");
|
|
3238
3316
|
formData = formData || new (null_default || FormData)();
|
|
3239
3317
|
options = utils_default.toFlatObject(options, {
|
|
@@ -3304,7 +3382,7 @@ function toFormData(obj, formData, options) {
|
|
|
3304
3382
|
build(obj);
|
|
3305
3383
|
return formData;
|
|
3306
3384
|
}
|
|
3307
|
-
var toFormData_default = toFormData;
|
|
3385
|
+
var toFormData_default = toFormData$1;
|
|
3308
3386
|
|
|
3309
3387
|
//#endregion
|
|
3310
3388
|
//#region ../agent-provider/node_modules/axios/lib/helpers/AxiosURLSearchParams.js
|
|
@@ -3821,7 +3899,7 @@ function buildAccessors(obj, header) {
|
|
|
3821
3899
|
});
|
|
3822
3900
|
});
|
|
3823
3901
|
}
|
|
3824
|
-
var AxiosHeaders = class {
|
|
3902
|
+
var AxiosHeaders$1 = class {
|
|
3825
3903
|
constructor(headers) {
|
|
3826
3904
|
headers && this.set(headers);
|
|
3827
3905
|
}
|
|
@@ -3959,7 +4037,7 @@ var AxiosHeaders = class {
|
|
|
3959
4037
|
return this;
|
|
3960
4038
|
}
|
|
3961
4039
|
};
|
|
3962
|
-
AxiosHeaders.accessor([
|
|
4040
|
+
AxiosHeaders$1.accessor([
|
|
3963
4041
|
"Content-Type",
|
|
3964
4042
|
"Content-Length",
|
|
3965
4043
|
"Accept",
|
|
@@ -3967,7 +4045,7 @@ AxiosHeaders.accessor([
|
|
|
3967
4045
|
"User-Agent",
|
|
3968
4046
|
"Authorization"
|
|
3969
4047
|
]);
|
|
3970
|
-
utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
4048
|
+
utils_default.reduceDescriptors(AxiosHeaders$1.prototype, ({ value }, key) => {
|
|
3971
4049
|
let mapped = key[0].toUpperCase() + key.slice(1);
|
|
3972
4050
|
return {
|
|
3973
4051
|
get: () => value,
|
|
@@ -3976,8 +4054,8 @@ utils_default.reduceDescriptors(AxiosHeaders.prototype, ({ value }, key) => {
|
|
|
3976
4054
|
}
|
|
3977
4055
|
};
|
|
3978
4056
|
});
|
|
3979
|
-
utils_default.freezeMethods(AxiosHeaders);
|
|
3980
|
-
var AxiosHeaders_default = AxiosHeaders;
|
|
4057
|
+
utils_default.freezeMethods(AxiosHeaders$1);
|
|
4058
|
+
var AxiosHeaders_default = AxiosHeaders$1;
|
|
3981
4059
|
|
|
3982
4060
|
//#endregion
|
|
3983
4061
|
//#region ../agent-provider/node_modules/axios/lib/core/transformData.js
|
|
@@ -4003,7 +4081,7 @@ function transformData(fns, response) {
|
|
|
4003
4081
|
|
|
4004
4082
|
//#endregion
|
|
4005
4083
|
//#region ../agent-provider/node_modules/axios/lib/cancel/isCancel.js
|
|
4006
|
-
function isCancel(value) {
|
|
4084
|
+
function isCancel$1(value) {
|
|
4007
4085
|
return !!(value && value.__CANCEL__);
|
|
4008
4086
|
}
|
|
4009
4087
|
|
|
@@ -4018,12 +4096,12 @@ function isCancel(value) {
|
|
|
4018
4096
|
*
|
|
4019
4097
|
* @returns {CanceledError} The created error.
|
|
4020
4098
|
*/
|
|
4021
|
-
function CanceledError(message, config, request) {
|
|
4099
|
+
function CanceledError$1(message, config, request) {
|
|
4022
4100
|
AxiosError_default.call(this, message == null ? "canceled" : message, AxiosError_default.ERR_CANCELED, config, request);
|
|
4023
4101
|
this.name = "CanceledError";
|
|
4024
4102
|
}
|
|
4025
|
-
utils_default.inherits(CanceledError, AxiosError_default, { __CANCEL__: true });
|
|
4026
|
-
var CanceledError_default = CanceledError;
|
|
4103
|
+
utils_default.inherits(CanceledError$1, AxiosError_default, { __CANCEL__: true });
|
|
4104
|
+
var CanceledError_default = CanceledError$1;
|
|
4027
4105
|
|
|
4028
4106
|
//#endregion
|
|
4029
4107
|
//#region ../agent-provider/node_modules/axios/lib/core/settle.js
|
|
@@ -4250,7 +4328,7 @@ const headersToObject = (thing) => thing instanceof AxiosHeaders_default ? { ...
|
|
|
4250
4328
|
*
|
|
4251
4329
|
* @returns {Object} New object resulting from merging config2 to config1
|
|
4252
4330
|
*/
|
|
4253
|
-
function mergeConfig(config1, config2) {
|
|
4331
|
+
function mergeConfig$1(config1, config2) {
|
|
4254
4332
|
config2 = config2 || {};
|
|
4255
4333
|
const config = {};
|
|
4256
4334
|
function getMergedValue(target, source, prop, caseless) {
|
|
@@ -4316,7 +4394,7 @@ function mergeConfig(config1, config2) {
|
|
|
4316
4394
|
//#endregion
|
|
4317
4395
|
//#region ../agent-provider/node_modules/axios/lib/helpers/resolveConfig.js
|
|
4318
4396
|
var resolveConfig_default = (config) => {
|
|
4319
|
-
const newConfig = mergeConfig({}, config);
|
|
4397
|
+
const newConfig = mergeConfig$1({}, config);
|
|
4320
4398
|
let { data, withXSRFToken, xsrfHeaderName, xsrfCookieName, headers, auth } = newConfig;
|
|
4321
4399
|
newConfig.headers = headers = AxiosHeaders_default.from(headers);
|
|
4322
4400
|
newConfig.url = buildURL(buildFullPath(newConfig.baseURL, newConfig.url, newConfig.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
@@ -4747,7 +4825,7 @@ function dispatchRequest(config) {
|
|
|
4747
4825
|
response.headers = AxiosHeaders_default.from(response.headers);
|
|
4748
4826
|
return response;
|
|
4749
4827
|
}, function onAdapterRejection(reason) {
|
|
4750
|
-
if (!isCancel(reason)) {
|
|
4828
|
+
if (!isCancel$1(reason)) {
|
|
4751
4829
|
throwIfCancellationRequested(config);
|
|
4752
4830
|
if (reason && reason.response) {
|
|
4753
4831
|
reason.response.data = transformData.call(config, config.transformResponse, reason.response);
|
|
@@ -4760,7 +4838,7 @@ function dispatchRequest(config) {
|
|
|
4760
4838
|
|
|
4761
4839
|
//#endregion
|
|
4762
4840
|
//#region ../agent-provider/node_modules/axios/lib/env/data.js
|
|
4763
|
-
const VERSION = "1.10.0";
|
|
4841
|
+
const VERSION$1 = "1.10.0";
|
|
4764
4842
|
|
|
4765
4843
|
//#endregion
|
|
4766
4844
|
//#region ../agent-provider/node_modules/axios/lib/helpers/validator.js
|
|
@@ -4789,7 +4867,7 @@ const deprecatedWarnings = {};
|
|
|
4789
4867
|
*/
|
|
4790
4868
|
validators$1.transitional = function transitional(validator, version, message) {
|
|
4791
4869
|
function formatMessage(opt, desc) {
|
|
4792
|
-
return "[Axios v" + VERSION + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
4870
|
+
return "[Axios v" + VERSION$1 + "] Transitional option '" + opt + "'" + desc + (message ? ". " + message : "");
|
|
4793
4871
|
}
|
|
4794
4872
|
return (value, opt, opts) => {
|
|
4795
4873
|
if (validator === false) throw new AxiosError_default(formatMessage(opt, " has been removed" + (version ? " in " + version : "")), AxiosError_default.ERR_DEPRECATED);
|
|
@@ -4846,7 +4924,7 @@ const validators = validator_default.validators;
|
|
|
4846
4924
|
*
|
|
4847
4925
|
* @return {Axios} A new instance of Axios
|
|
4848
4926
|
*/
|
|
4849
|
-
var Axios = class {
|
|
4927
|
+
var Axios$1 = class {
|
|
4850
4928
|
constructor(instanceConfig) {
|
|
4851
4929
|
this.defaults = instanceConfig || {};
|
|
4852
4930
|
this.interceptors = {
|
|
@@ -4883,7 +4961,7 @@ var Axios = class {
|
|
|
4883
4961
|
config = config || {};
|
|
4884
4962
|
config.url = configOrUrl;
|
|
4885
4963
|
} else config = configOrUrl || {};
|
|
4886
|
-
config = mergeConfig(this.defaults, config);
|
|
4964
|
+
config = mergeConfig$1(this.defaults, config);
|
|
4887
4965
|
const { transitional, paramsSerializer, headers } = config;
|
|
4888
4966
|
if (transitional !== void 0) validator_default.assertOptions(transitional, {
|
|
4889
4967
|
silentJSONParsing: validators.transitional(validators.boolean),
|
|
@@ -4962,7 +5040,7 @@ var Axios = class {
|
|
|
4962
5040
|
return promise;
|
|
4963
5041
|
}
|
|
4964
5042
|
getUri(config) {
|
|
4965
|
-
config = mergeConfig(this.defaults, config);
|
|
5043
|
+
config = mergeConfig$1(this.defaults, config);
|
|
4966
5044
|
return buildURL(buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls), config.params, config.paramsSerializer);
|
|
4967
5045
|
}
|
|
4968
5046
|
};
|
|
@@ -4972,8 +5050,8 @@ utils_default.forEach([
|
|
|
4972
5050
|
"head",
|
|
4973
5051
|
"options"
|
|
4974
5052
|
], function forEachMethodNoData(method) {
|
|
4975
|
-
Axios.prototype[method] = function(url, config) {
|
|
4976
|
-
return this.request(mergeConfig(config || {}, {
|
|
5053
|
+
Axios$1.prototype[method] = function(url, config) {
|
|
5054
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
4977
5055
|
method,
|
|
4978
5056
|
url,
|
|
4979
5057
|
data: (config || {}).data
|
|
@@ -4987,7 +5065,7 @@ utils_default.forEach([
|
|
|
4987
5065
|
], function forEachMethodWithData(method) {
|
|
4988
5066
|
function generateHTTPMethod(isForm) {
|
|
4989
5067
|
return function httpMethod(url, data, config) {
|
|
4990
|
-
return this.request(mergeConfig(config || {}, {
|
|
5068
|
+
return this.request(mergeConfig$1(config || {}, {
|
|
4991
5069
|
method,
|
|
4992
5070
|
headers: isForm ? { "Content-Type": "multipart/form-data" } : {},
|
|
4993
5071
|
url,
|
|
@@ -4995,10 +5073,10 @@ utils_default.forEach([
|
|
|
4995
5073
|
}));
|
|
4996
5074
|
};
|
|
4997
5075
|
}
|
|
4998
|
-
Axios.prototype[method] = generateHTTPMethod();
|
|
4999
|
-
Axios.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
5076
|
+
Axios$1.prototype[method] = generateHTTPMethod();
|
|
5077
|
+
Axios$1.prototype[method + "Form"] = generateHTTPMethod(true);
|
|
5000
5078
|
});
|
|
5001
|
-
var Axios_default = Axios;
|
|
5079
|
+
var Axios_default = Axios$1;
|
|
5002
5080
|
|
|
5003
5081
|
//#endregion
|
|
5004
5082
|
//#region ../agent-provider/node_modules/axios/lib/cancel/CancelToken.js
|
|
@@ -5009,7 +5087,7 @@ var Axios_default = Axios;
|
|
|
5009
5087
|
*
|
|
5010
5088
|
* @returns {CancelToken}
|
|
5011
5089
|
*/
|
|
5012
|
-
var CancelToken = class CancelToken {
|
|
5090
|
+
var CancelToken$1 = class CancelToken$1 {
|
|
5013
5091
|
constructor(executor) {
|
|
5014
5092
|
if (typeof executor !== "function") throw new TypeError("executor must be a function.");
|
|
5015
5093
|
let resolvePromise;
|
|
@@ -5081,14 +5159,14 @@ var CancelToken = class CancelToken {
|
|
|
5081
5159
|
static source() {
|
|
5082
5160
|
let cancel;
|
|
5083
5161
|
return {
|
|
5084
|
-
token: new CancelToken(function executor(c) {
|
|
5162
|
+
token: new CancelToken$1(function executor(c) {
|
|
5085
5163
|
cancel = c;
|
|
5086
5164
|
}),
|
|
5087
5165
|
cancel
|
|
5088
5166
|
};
|
|
5089
5167
|
}
|
|
5090
5168
|
};
|
|
5091
|
-
var CancelToken_default = CancelToken;
|
|
5169
|
+
var CancelToken_default = CancelToken$1;
|
|
5092
5170
|
|
|
5093
5171
|
//#endregion
|
|
5094
5172
|
//#region ../agent-provider/node_modules/axios/lib/helpers/spread.js
|
|
@@ -5113,7 +5191,7 @@ var CancelToken_default = CancelToken;
|
|
|
5113
5191
|
*
|
|
5114
5192
|
* @returns {Function}
|
|
5115
5193
|
*/
|
|
5116
|
-
function spread(callback) {
|
|
5194
|
+
function spread$1(callback) {
|
|
5117
5195
|
return function wrap(arr) {
|
|
5118
5196
|
return callback.apply(null, arr);
|
|
5119
5197
|
};
|
|
@@ -5128,13 +5206,13 @@ function spread(callback) {
|
|
|
5128
5206
|
*
|
|
5129
5207
|
* @returns {boolean} True if the payload is an error thrown by Axios, otherwise false
|
|
5130
5208
|
*/
|
|
5131
|
-
function isAxiosError(payload) {
|
|
5209
|
+
function isAxiosError$1(payload) {
|
|
5132
5210
|
return utils_default.isObject(payload) && payload.isAxiosError === true;
|
|
5133
5211
|
}
|
|
5134
5212
|
|
|
5135
5213
|
//#endregion
|
|
5136
5214
|
//#region ../agent-provider/node_modules/axios/lib/helpers/HttpStatusCode.js
|
|
5137
|
-
const HttpStatusCode = {
|
|
5215
|
+
const HttpStatusCode$1 = {
|
|
5138
5216
|
Continue: 100,
|
|
5139
5217
|
SwitchingProtocols: 101,
|
|
5140
5218
|
Processing: 102,
|
|
@@ -5199,10 +5277,10 @@ const HttpStatusCode = {
|
|
|
5199
5277
|
NotExtended: 510,
|
|
5200
5278
|
NetworkAuthenticationRequired: 511
|
|
5201
5279
|
};
|
|
5202
|
-
Object.entries(HttpStatusCode).forEach(([key, value]) => {
|
|
5203
|
-
HttpStatusCode[value] = key;
|
|
5280
|
+
Object.entries(HttpStatusCode$1).forEach(([key, value]) => {
|
|
5281
|
+
HttpStatusCode$1[value] = key;
|
|
5204
5282
|
});
|
|
5205
|
-
var HttpStatusCode_default = HttpStatusCode;
|
|
5283
|
+
var HttpStatusCode_default = HttpStatusCode$1;
|
|
5206
5284
|
|
|
5207
5285
|
//#endregion
|
|
5208
5286
|
//#region ../agent-provider/node_modules/axios/lib/axios.js
|
|
@@ -5219,7 +5297,7 @@ function createInstance(defaultConfig) {
|
|
|
5219
5297
|
utils_default.extend(instance, Axios_default.prototype, context, { allOwnKeys: true });
|
|
5220
5298
|
utils_default.extend(instance, context, null, { allOwnKeys: true });
|
|
5221
5299
|
instance.create = function create(instanceConfig) {
|
|
5222
|
-
return createInstance(mergeConfig(defaultConfig, instanceConfig));
|
|
5300
|
+
return createInstance(mergeConfig$1(defaultConfig, instanceConfig));
|
|
5223
5301
|
};
|
|
5224
5302
|
return instance;
|
|
5225
5303
|
}
|
|
@@ -5227,17 +5305,17 @@ const axios = createInstance(defaults_default);
|
|
|
5227
5305
|
axios.Axios = Axios_default;
|
|
5228
5306
|
axios.CanceledError = CanceledError_default;
|
|
5229
5307
|
axios.CancelToken = CancelToken_default;
|
|
5230
|
-
axios.isCancel = isCancel;
|
|
5231
|
-
axios.VERSION = VERSION;
|
|
5308
|
+
axios.isCancel = isCancel$1;
|
|
5309
|
+
axios.VERSION = VERSION$1;
|
|
5232
5310
|
axios.toFormData = toFormData_default;
|
|
5233
5311
|
axios.AxiosError = AxiosError_default;
|
|
5234
5312
|
axios.Cancel = axios.CanceledError;
|
|
5235
5313
|
axios.all = function all(promises) {
|
|
5236
5314
|
return Promise.all(promises);
|
|
5237
5315
|
};
|
|
5238
|
-
axios.spread = spread;
|
|
5239
|
-
axios.isAxiosError = isAxiosError;
|
|
5240
|
-
axios.mergeConfig = mergeConfig;
|
|
5316
|
+
axios.spread = spread$1;
|
|
5317
|
+
axios.isAxiosError = isAxiosError$1;
|
|
5318
|
+
axios.mergeConfig = mergeConfig$1;
|
|
5241
5319
|
axios.AxiosHeaders = AxiosHeaders_default;
|
|
5242
5320
|
axios.formToJSON = (thing) => formDataToJSON_default(utils_default.isHTMLForm(thing) ? new FormData(thing) : thing);
|
|
5243
5321
|
axios.getAdapter = adapters_default.getAdapter;
|
|
@@ -5245,6 +5323,10 @@ axios.HttpStatusCode = HttpStatusCode_default;
|
|
|
5245
5323
|
axios.default = axios;
|
|
5246
5324
|
var axios_default = axios;
|
|
5247
5325
|
|
|
5326
|
+
//#endregion
|
|
5327
|
+
//#region ../agent-provider/node_modules/axios/index.js
|
|
5328
|
+
const { Axios, AxiosError, CanceledError, isCancel, CancelToken, VERSION, all, Cancel, isAxiosError, spread, toFormData, AxiosHeaders, HttpStatusCode, formToJSON, getAdapter, mergeConfig } = axios_default;
|
|
5329
|
+
|
|
5248
5330
|
//#endregion
|
|
5249
5331
|
//#region ../agent-provider/src/http/http-service.ts
|
|
5250
5332
|
/**
|
|
@@ -5253,7 +5335,7 @@ var axios_default = axios;
|
|
|
5253
5335
|
* 特性:
|
|
5254
5336
|
* - 单例模式,全局唯一实例,延迟初始化(首次使用时自动创建)
|
|
5255
5337
|
* - 支持拦截器注册(其他模块可注入 header)
|
|
5256
|
-
* - 统一 401
|
|
5338
|
+
* - 统一 401 处理(支持自动刷新 token 并重试)
|
|
5257
5339
|
* - 自动携带凭证(withCredentials)
|
|
5258
5340
|
* - 类型安全
|
|
5259
5341
|
*
|
|
@@ -5293,6 +5375,8 @@ var HttpService = class HttpService {
|
|
|
5293
5375
|
*/
|
|
5294
5376
|
constructor(config = {}) {
|
|
5295
5377
|
this.unauthorizedCallbacks = /* @__PURE__ */ new Set();
|
|
5378
|
+
this.isRefreshing = false;
|
|
5379
|
+
this.refreshSubscribers = [];
|
|
5296
5380
|
this.config = config;
|
|
5297
5381
|
this.axiosInstance = axios_default.create({
|
|
5298
5382
|
baseURL: config.baseURL?.replace(/\/$/, "") || "",
|
|
@@ -5333,18 +5417,54 @@ var HttpService = class HttpService {
|
|
|
5333
5417
|
}, (error) => Promise.reject(error));
|
|
5334
5418
|
}
|
|
5335
5419
|
/**
|
|
5336
|
-
* 注册默认响应拦截器(处理 401
|
|
5420
|
+
* 注册默认响应拦截器(处理 401,支持自动重试)
|
|
5337
5421
|
*/
|
|
5338
5422
|
registerDefaultResponseInterceptor() {
|
|
5339
|
-
this.axiosInstance.interceptors.response.use((response) => response, (error) => {
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5423
|
+
this.axiosInstance.interceptors.response.use((response) => response, async (error) => {
|
|
5424
|
+
const originalRequest = error.config;
|
|
5425
|
+
if (error.response?.status === 401 && originalRequest && !originalRequest._retry) {
|
|
5426
|
+
if (originalRequest.url?.includes("/console/accounts")) {
|
|
5427
|
+
console.warn("[HttpService] Unauthorized 401 on refresh endpoint, not retrying");
|
|
5428
|
+
return Promise.reject(error);
|
|
5429
|
+
}
|
|
5430
|
+
console.warn("[HttpService] Unauthorized 401, attempting token refresh and retry");
|
|
5431
|
+
originalRequest._retry = true;
|
|
5432
|
+
if (this.isRefreshing) return new Promise((resolve, reject) => {
|
|
5433
|
+
this.refreshSubscribers.push((success) => {
|
|
5434
|
+
if (success) this.axiosInstance.request(originalRequest).then(resolve).catch(reject);
|
|
5435
|
+
else reject(error);
|
|
5436
|
+
});
|
|
5437
|
+
});
|
|
5438
|
+
this.isRefreshing = true;
|
|
5439
|
+
try {
|
|
5440
|
+
await this.triggerUnauthorizedCallbacks();
|
|
5441
|
+
this.onRefreshSuccess();
|
|
5442
|
+
return this.axiosInstance.request(originalRequest);
|
|
5443
|
+
} catch (refreshError) {
|
|
5444
|
+
this.onRefreshFailure();
|
|
5445
|
+
return Promise.reject(error);
|
|
5446
|
+
} finally {
|
|
5447
|
+
this.isRefreshing = false;
|
|
5448
|
+
}
|
|
5343
5449
|
}
|
|
5344
5450
|
return Promise.reject(error);
|
|
5345
5451
|
});
|
|
5346
5452
|
}
|
|
5347
5453
|
/**
|
|
5454
|
+
* token 刷新成功,通知所有等待的请求
|
|
5455
|
+
*/
|
|
5456
|
+
onRefreshSuccess() {
|
|
5457
|
+
this.refreshSubscribers.forEach((callback) => callback(true));
|
|
5458
|
+
this.refreshSubscribers = [];
|
|
5459
|
+
}
|
|
5460
|
+
/**
|
|
5461
|
+
* token 刷新失败,通知所有等待的请求
|
|
5462
|
+
*/
|
|
5463
|
+
onRefreshFailure() {
|
|
5464
|
+
this.refreshSubscribers.forEach((callback) => callback(false));
|
|
5465
|
+
this.refreshSubscribers = [];
|
|
5466
|
+
}
|
|
5467
|
+
/**
|
|
5348
5468
|
* 注册请求拦截器
|
|
5349
5469
|
* @param onFulfilled 请求成功拦截器
|
|
5350
5470
|
* @param onRejected 请求失败拦截器
|
|
@@ -5421,16 +5541,18 @@ var HttpService = class HttpService {
|
|
|
5421
5541
|
this.unauthorizedCallbacks.delete(callback);
|
|
5422
5542
|
}
|
|
5423
5543
|
/**
|
|
5424
|
-
* 触发所有 401
|
|
5544
|
+
* 触发所有 401 回调并等待完成
|
|
5545
|
+
* @returns Promise,等待所有回调完成
|
|
5546
|
+
* @throws 如果任何回调失败,则抛出错误
|
|
5425
5547
|
*/
|
|
5426
|
-
triggerUnauthorizedCallbacks() {
|
|
5427
|
-
this.unauthorizedCallbacks
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
}
|
|
5548
|
+
async triggerUnauthorizedCallbacks() {
|
|
5549
|
+
const callbacks = Array.from(this.unauthorizedCallbacks);
|
|
5550
|
+
const failedResults = (await Promise.allSettled(callbacks.map((callback) => callback()))).filter((r) => r.status === "rejected");
|
|
5551
|
+
if (failedResults.length > 0) {
|
|
5552
|
+
const errors = failedResults.map((r) => r.reason);
|
|
5553
|
+
console.error("[HttpService] Some unauthorized callbacks failed:", errors);
|
|
5554
|
+
throw errors[0];
|
|
5555
|
+
}
|
|
5434
5556
|
}
|
|
5435
5557
|
/**
|
|
5436
5558
|
* 更新 authToken
|
|
@@ -5542,6 +5664,7 @@ var AccountService = class {
|
|
|
5542
5664
|
this.initPromise = null;
|
|
5543
5665
|
this.initResolve = null;
|
|
5544
5666
|
this.requestInterceptorId = null;
|
|
5667
|
+
this.crossTabBroadcaster = null;
|
|
5545
5668
|
this.initPromise = new Promise((resolve) => {
|
|
5546
5669
|
this.initResolve = resolve;
|
|
5547
5670
|
});
|
|
@@ -5590,15 +5713,34 @@ var AccountService = class {
|
|
|
5590
5713
|
this.initialized = true;
|
|
5591
5714
|
this.initResolve?.(account);
|
|
5592
5715
|
}
|
|
5593
|
-
if (!wasInitialized || prev?.uid !== account?.uid)
|
|
5716
|
+
if (!wasInitialized || prev?.uid !== account?.uid) {
|
|
5717
|
+
this.notifyListeners();
|
|
5718
|
+
if (account && this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogin();
|
|
5719
|
+
}
|
|
5594
5720
|
}
|
|
5595
5721
|
/**
|
|
5596
5722
|
* 清除账号(登出)
|
|
5723
|
+
* 先广播登出消息,再清除本地账号
|
|
5597
5724
|
*/
|
|
5598
5725
|
clearAccount() {
|
|
5726
|
+
if (this.crossTabBroadcaster) this.crossTabBroadcaster.broadcastLogout();
|
|
5727
|
+
this.setAccount(null);
|
|
5728
|
+
}
|
|
5729
|
+
/**
|
|
5730
|
+
* 静默清除账号(不广播)
|
|
5731
|
+
* 用于收到其他标签页 logout 消息时,避免循环广播
|
|
5732
|
+
*/
|
|
5733
|
+
clearAccountSilently() {
|
|
5599
5734
|
this.setAccount(null);
|
|
5600
5735
|
}
|
|
5601
5736
|
/**
|
|
5737
|
+
* 设置跨标签页认证同步广播器
|
|
5738
|
+
* 应在应用初始化时由上层(如 agent-ui)调用
|
|
5739
|
+
*/
|
|
5740
|
+
setCrossTabBroadcaster(broadcaster) {
|
|
5741
|
+
this.crossTabBroadcaster = broadcaster;
|
|
5742
|
+
}
|
|
5743
|
+
/**
|
|
5602
5744
|
* 订阅账号变化
|
|
5603
5745
|
* @param callback 变化时的回调函数
|
|
5604
5746
|
* @returns 取消订阅函数
|
|
@@ -5663,6 +5805,114 @@ var AccountService = class {
|
|
|
5663
5805
|
* 导出单例实例
|
|
5664
5806
|
*/
|
|
5665
5807
|
const accountService = new AccountService();
|
|
5808
|
+
/**
|
|
5809
|
+
* 暴露给全局,供 Agent Manager 直接调用 setAccount 刷新 Widget 状态
|
|
5810
|
+
* 这是为了解决 IDE 环境中 IPC 事件无法直接触发 Widget 账号刷新的问题
|
|
5811
|
+
*/
|
|
5812
|
+
if (typeof window !== "undefined") window.__genieAccountService = accountService;
|
|
5813
|
+
|
|
5814
|
+
//#endregion
|
|
5815
|
+
//#region ../agent-provider/src/common/utils/lru-cache.ts
|
|
5816
|
+
/**
|
|
5817
|
+
* LRU (Least Recently Used) Cache
|
|
5818
|
+
* 当缓存达到容量上限时,自动淘汰最久未使用的数据
|
|
5819
|
+
*
|
|
5820
|
+
* @template K - Key 类型
|
|
5821
|
+
* @template V - Value 类型
|
|
5822
|
+
*/
|
|
5823
|
+
var LRUCache = class {
|
|
5824
|
+
/**
|
|
5825
|
+
* 创建 LRU 缓存实例
|
|
5826
|
+
* @param capacity - 缓存容量上限
|
|
5827
|
+
*/
|
|
5828
|
+
constructor(capacity) {
|
|
5829
|
+
if (capacity <= 0) throw new Error("Cache capacity must be greater than 0");
|
|
5830
|
+
this.capacity = capacity;
|
|
5831
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
5832
|
+
}
|
|
5833
|
+
/**
|
|
5834
|
+
* 获取缓存值
|
|
5835
|
+
* @param key - 键
|
|
5836
|
+
* @returns 值,如果不存在返回 undefined
|
|
5837
|
+
*/
|
|
5838
|
+
get(key) {
|
|
5839
|
+
if (!this.cache.has(key)) return;
|
|
5840
|
+
const value = this.cache.get(key);
|
|
5841
|
+
this.cache.delete(key);
|
|
5842
|
+
this.cache.set(key, value);
|
|
5843
|
+
return value;
|
|
5844
|
+
}
|
|
5845
|
+
/**
|
|
5846
|
+
* 设置缓存值
|
|
5847
|
+
* @param key - 键
|
|
5848
|
+
* @param value - 值
|
|
5849
|
+
*/
|
|
5850
|
+
set(key, value) {
|
|
5851
|
+
if (this.cache.has(key)) this.cache.delete(key);
|
|
5852
|
+
else if (this.cache.size >= this.capacity) {
|
|
5853
|
+
const firstKey = this.cache.keys().next().value;
|
|
5854
|
+
this.cache.delete(firstKey);
|
|
5855
|
+
}
|
|
5856
|
+
this.cache.set(key, value);
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* 检查 key 是否存在
|
|
5860
|
+
* @param key - 键
|
|
5861
|
+
* @returns 是否存在
|
|
5862
|
+
*/
|
|
5863
|
+
has(key) {
|
|
5864
|
+
return this.cache.has(key);
|
|
5865
|
+
}
|
|
5866
|
+
/**
|
|
5867
|
+
* 删除指定 key
|
|
5868
|
+
* @param key - 键
|
|
5869
|
+
* @returns 是否删除成功
|
|
5870
|
+
*/
|
|
5871
|
+
delete(key) {
|
|
5872
|
+
return this.cache.delete(key);
|
|
5873
|
+
}
|
|
5874
|
+
/**
|
|
5875
|
+
* 清空缓存
|
|
5876
|
+
*/
|
|
5877
|
+
clear() {
|
|
5878
|
+
this.cache.clear();
|
|
5879
|
+
}
|
|
5880
|
+
/**
|
|
5881
|
+
* 获取当前缓存大小
|
|
5882
|
+
* @returns 当前缓存的元素数量
|
|
5883
|
+
*/
|
|
5884
|
+
get size() {
|
|
5885
|
+
return this.cache.size;
|
|
5886
|
+
}
|
|
5887
|
+
/**
|
|
5888
|
+
* 获取缓存容量
|
|
5889
|
+
* @returns 缓存容量上限
|
|
5890
|
+
*/
|
|
5891
|
+
get maxSize() {
|
|
5892
|
+
return this.capacity;
|
|
5893
|
+
}
|
|
5894
|
+
/**
|
|
5895
|
+
* 获取所有 key
|
|
5896
|
+
* @returns key 数组
|
|
5897
|
+
*/
|
|
5898
|
+
keys() {
|
|
5899
|
+
return Array.from(this.cache.keys());
|
|
5900
|
+
}
|
|
5901
|
+
/**
|
|
5902
|
+
* 获取所有 value
|
|
5903
|
+
* @returns value 数组
|
|
5904
|
+
*/
|
|
5905
|
+
values() {
|
|
5906
|
+
return Array.from(this.cache.values());
|
|
5907
|
+
}
|
|
5908
|
+
/**
|
|
5909
|
+
* 遍历缓存
|
|
5910
|
+
* @param callback - 回调函数
|
|
5911
|
+
*/
|
|
5912
|
+
forEach(callback) {
|
|
5913
|
+
this.cache.forEach((value, key) => callback(value, key));
|
|
5914
|
+
}
|
|
5915
|
+
};
|
|
5666
5916
|
|
|
5667
5917
|
//#endregion
|
|
5668
5918
|
//#region ../agent-provider/src/common/utils/concurrency.ts
|
|
@@ -5765,20 +6015,32 @@ var CosUploadService = class {
|
|
|
5765
6015
|
* 上传单个文件到 COS
|
|
5766
6016
|
*
|
|
5767
6017
|
* @param file - 要上传的文件
|
|
6018
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
5768
6019
|
* @returns 上传结果,包含访问 URL 或错误信息
|
|
5769
6020
|
*/
|
|
5770
|
-
async uploadFile(file) {
|
|
6021
|
+
async uploadFile(file, abortSignal) {
|
|
5771
6022
|
const filename = file.name;
|
|
5772
6023
|
this.logger?.info(`[CosUploadService] Uploading file: ${filename}`);
|
|
5773
6024
|
try {
|
|
6025
|
+
if (abortSignal?.aborted) return {
|
|
6026
|
+
success: false,
|
|
6027
|
+
error: "Upload cancelled",
|
|
6028
|
+
aborted: true
|
|
6029
|
+
};
|
|
5774
6030
|
const objectKey = this.generateObjectKey(filename);
|
|
5775
6031
|
this.logger?.debug(`[CosUploadService] Generated objectKey: ${objectKey}`);
|
|
5776
6032
|
const presignedItem = (await this.getPresignedUrls([objectKey])).items[0];
|
|
5777
6033
|
if (!presignedItem) throw new Error("No presigned URL item returned");
|
|
6034
|
+
if (abortSignal?.aborted) return {
|
|
6035
|
+
success: false,
|
|
6036
|
+
error: "Upload cancelled",
|
|
6037
|
+
aborted: true
|
|
6038
|
+
};
|
|
5778
6039
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
5779
6040
|
method: "PUT",
|
|
5780
6041
|
body: file,
|
|
5781
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
6042
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
6043
|
+
signal: abortSignal
|
|
5782
6044
|
});
|
|
5783
6045
|
if (!uploadResponse.ok) {
|
|
5784
6046
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -5792,6 +6054,11 @@ var CosUploadService = class {
|
|
|
5792
6054
|
objectKey
|
|
5793
6055
|
};
|
|
5794
6056
|
} catch (error) {
|
|
6057
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
6058
|
+
success: false,
|
|
6059
|
+
error: "Upload cancelled",
|
|
6060
|
+
aborted: true
|
|
6061
|
+
};
|
|
5795
6062
|
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
5796
6063
|
this.logger?.error(`[CosUploadService] Upload failed: ${filename}`, error);
|
|
5797
6064
|
return {
|
|
@@ -5806,14 +6073,25 @@ var CosUploadService = class {
|
|
|
5806
6073
|
* 使用并发控制,限制同时上传的文件数量
|
|
5807
6074
|
*
|
|
5808
6075
|
* @param files - 要上传的文件数组
|
|
6076
|
+
* @param abortSignal - 可选的 AbortSignal,用于取消上传
|
|
5809
6077
|
* @returns 所有文件的上传结果
|
|
5810
6078
|
*/
|
|
5811
|
-
async uploadFiles(files) {
|
|
6079
|
+
async uploadFiles(files, abortSignal) {
|
|
5812
6080
|
if (files.length === 0) return {
|
|
5813
6081
|
success: true,
|
|
5814
6082
|
urls: [],
|
|
5815
6083
|
results: []
|
|
5816
6084
|
};
|
|
6085
|
+
if (abortSignal?.aborted) return {
|
|
6086
|
+
success: false,
|
|
6087
|
+
error: "Upload cancelled",
|
|
6088
|
+
aborted: true,
|
|
6089
|
+
results: files.map(() => ({
|
|
6090
|
+
success: false,
|
|
6091
|
+
error: "Upload cancelled",
|
|
6092
|
+
aborted: true
|
|
6093
|
+
}))
|
|
6094
|
+
};
|
|
5817
6095
|
this.logger?.info(`[CosUploadService] Uploading ${files.length} file(s) with concurrency ${this.uploadConcurrency}`);
|
|
5818
6096
|
try {
|
|
5819
6097
|
const fileInfos = files.map((file) => ({
|
|
@@ -5825,6 +6103,12 @@ var CosUploadService = class {
|
|
|
5825
6103
|
this.logger?.debug(`[CosUploadService] Got ${presignedResponse.items.length} presigned URLs`);
|
|
5826
6104
|
if (presignedResponse.items.length !== fileInfos.length) throw new Error(`Expected ${fileInfos.length} presigned URLs, got ${presignedResponse.items.length}`);
|
|
5827
6105
|
const results = (await runWithConcurrencySettled(fileInfos.map(({ file }, index) => async () => {
|
|
6106
|
+
if (abortSignal?.aborted) return {
|
|
6107
|
+
success: false,
|
|
6108
|
+
error: "Upload cancelled",
|
|
6109
|
+
aborted: true,
|
|
6110
|
+
objectKey: fileInfos[index].objectKey
|
|
6111
|
+
};
|
|
5828
6112
|
const presignedItem = presignedResponse.items[index];
|
|
5829
6113
|
if (!presignedItem) return {
|
|
5830
6114
|
success: false,
|
|
@@ -5835,7 +6119,8 @@ var CosUploadService = class {
|
|
|
5835
6119
|
const uploadResponse = await fetch(presignedItem.upload_url, {
|
|
5836
6120
|
method: "PUT",
|
|
5837
6121
|
body: file,
|
|
5838
|
-
headers: { "Content-Type": file.type || "application/octet-stream" }
|
|
6122
|
+
headers: { "Content-Type": file.type || "application/octet-stream" },
|
|
6123
|
+
signal: abortSignal
|
|
5839
6124
|
});
|
|
5840
6125
|
if (!uploadResponse.ok) {
|
|
5841
6126
|
const errorText = await uploadResponse.text().catch(() => uploadResponse.statusText);
|
|
@@ -5852,6 +6137,12 @@ var CosUploadService = class {
|
|
|
5852
6137
|
objectKey: presignedItem.object_key
|
|
5853
6138
|
};
|
|
5854
6139
|
} catch (error) {
|
|
6140
|
+
if (error instanceof Error && error.name === "AbortError") return {
|
|
6141
|
+
success: false,
|
|
6142
|
+
error: "Upload cancelled",
|
|
6143
|
+
aborted: true,
|
|
6144
|
+
objectKey: presignedItem.object_key
|
|
6145
|
+
};
|
|
5855
6146
|
return {
|
|
5856
6147
|
success: false,
|
|
5857
6148
|
error: error instanceof Error ? error.message : "Unknown error",
|
|
@@ -5905,35 +6196,35 @@ var CosUploadService = class {
|
|
|
5905
6196
|
* E2B Filesystem Implementation
|
|
5906
6197
|
*
|
|
5907
6198
|
* Provides FilesResource implementation using E2B Sandbox SDK.
|
|
5908
|
-
*
|
|
6199
|
+
* Supports optional auto-reconnect on auth failure (401/403).
|
|
5909
6200
|
*
|
|
5910
6201
|
* @see https://e2b.dev/docs/filesystem/read-write
|
|
5911
6202
|
* @see https://e2b.dev/docs/filesystem/watch
|
|
5912
6203
|
*/
|
|
5913
6204
|
/**
|
|
5914
|
-
* E2B Filesystem
|
|
6205
|
+
* E2B Filesystem
|
|
5915
6206
|
*
|
|
5916
|
-
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource
|
|
6207
|
+
* Wraps E2B Sandbox SDK's filesystem operations to implement FilesResource.
|
|
6208
|
+
* When `reconnectFn` is provided, automatically reconnects on auth errors.
|
|
5917
6209
|
*
|
|
5918
6210
|
* @example
|
|
5919
6211
|
* ```typescript
|
|
5920
|
-
*
|
|
5921
|
-
*
|
|
5922
|
-
* apiKey: 'e2b_xxx'
|
|
5923
|
-
* });
|
|
5924
|
-
*
|
|
5925
|
-
* // Read/write files
|
|
5926
|
-
* await fs.write('/test.txt', 'Hello World');
|
|
5927
|
-
* const content = await fs.read('/test.txt');
|
|
6212
|
+
* // Basic usage (no auto-reconnect)
|
|
6213
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
5928
6214
|
*
|
|
5929
|
-
* //
|
|
5930
|
-
* const
|
|
5931
|
-
*
|
|
5932
|
-
* });
|
|
6215
|
+
* // With auto-reconnect on token expiry
|
|
6216
|
+
* const fs = await E2BFilesystem.connect({ sandboxId: '...' });
|
|
6217
|
+
* fs.setReconnectFn(async () => fetchFreshConnectionInfo());
|
|
5933
6218
|
* ```
|
|
5934
6219
|
*/
|
|
5935
6220
|
var E2BFilesystem = class E2BFilesystem {
|
|
6221
|
+
static {
|
|
6222
|
+
this.MIN_RECONNECT_INTERVAL_MS = 10 * 1e3;
|
|
6223
|
+
}
|
|
5936
6224
|
constructor(sandbox) {
|
|
6225
|
+
this.isReconnecting = false;
|
|
6226
|
+
this.reconnectSubscribers = [];
|
|
6227
|
+
this.lastReconnectAt = 0;
|
|
5937
6228
|
this.sandbox = sandbox;
|
|
5938
6229
|
}
|
|
5939
6230
|
/**
|
|
@@ -5949,38 +6240,111 @@ var E2BFilesystem = class E2BFilesystem {
|
|
|
5949
6240
|
}));
|
|
5950
6241
|
}
|
|
5951
6242
|
/**
|
|
6243
|
+
* Set reconnect callback. When set, auth errors trigger automatic reconnect + retry.
|
|
6244
|
+
*/
|
|
6245
|
+
setReconnectFn(fn) {
|
|
6246
|
+
this.reconnectFn = fn;
|
|
6247
|
+
}
|
|
6248
|
+
/**
|
|
5952
6249
|
* Get the underlying E2B Sandbox instance
|
|
5953
6250
|
*/
|
|
5954
6251
|
getSandbox() {
|
|
5955
6252
|
return this.sandbox;
|
|
5956
6253
|
}
|
|
6254
|
+
isAuthError(error) {
|
|
6255
|
+
if (!error || typeof error !== "object") return false;
|
|
6256
|
+
const err = error;
|
|
6257
|
+
if (err.status === 401 || err.status === 403) return true;
|
|
6258
|
+
if (err.statusCode === 401 || err.statusCode === 403) return true;
|
|
6259
|
+
if (err.response && typeof err.response === "object") {
|
|
6260
|
+
const resp = err.response;
|
|
6261
|
+
if (resp.status === 401 || resp.status === 403) return true;
|
|
6262
|
+
}
|
|
6263
|
+
if (typeof err.message === "string") {
|
|
6264
|
+
const msg = err.message.toLowerCase();
|
|
6265
|
+
if (msg.includes("unauthorized") || msg.includes("token expired") || msg.includes("authentication")) return true;
|
|
6266
|
+
}
|
|
6267
|
+
return false;
|
|
6268
|
+
}
|
|
6269
|
+
canAttemptReconnect() {
|
|
6270
|
+
return Date.now() - this.lastReconnectAt >= E2BFilesystem.MIN_RECONNECT_INTERVAL_MS;
|
|
6271
|
+
}
|
|
6272
|
+
/**
|
|
6273
|
+
* Reconnect with fresh credentials.
|
|
6274
|
+
* Only one reconnect in-flight at a time; concurrent callers share the result.
|
|
6275
|
+
*/
|
|
6276
|
+
async reconnect() {
|
|
6277
|
+
if (this.isReconnecting) return new Promise((resolve, reject) => {
|
|
6278
|
+
this.reconnectSubscribers.push((success) => {
|
|
6279
|
+
if (success) resolve();
|
|
6280
|
+
else reject(/* @__PURE__ */ new Error("E2B sandbox reconnect failed"));
|
|
6281
|
+
});
|
|
6282
|
+
});
|
|
6283
|
+
this.isReconnecting = true;
|
|
6284
|
+
this.lastReconnectAt = Date.now();
|
|
6285
|
+
try {
|
|
6286
|
+
const info = await this.reconnectFn();
|
|
6287
|
+
this.sandbox = await Sandbox.connect(info.sandboxId, {
|
|
6288
|
+
domain: info.domain,
|
|
6289
|
+
apiUrl: info.apiUrl,
|
|
6290
|
+
requestTimeoutMs: info.requestTimeoutMs,
|
|
6291
|
+
debug: info.debug,
|
|
6292
|
+
headers: info.headers
|
|
6293
|
+
});
|
|
6294
|
+
this.reconnectSubscribers.forEach((cb) => cb(true));
|
|
6295
|
+
this.reconnectSubscribers = [];
|
|
6296
|
+
} catch (error) {
|
|
6297
|
+
this.reconnectSubscribers.forEach((cb) => cb(false));
|
|
6298
|
+
this.reconnectSubscribers = [];
|
|
6299
|
+
throw error;
|
|
6300
|
+
} finally {
|
|
6301
|
+
this.isReconnecting = false;
|
|
6302
|
+
}
|
|
6303
|
+
}
|
|
6304
|
+
/**
|
|
6305
|
+
* Execute an operation. If reconnectFn is set and an auth error occurs,
|
|
6306
|
+
* reconnect and retry once.
|
|
6307
|
+
*/
|
|
6308
|
+
async exec(operation) {
|
|
6309
|
+
try {
|
|
6310
|
+
return await operation(this.sandbox.files);
|
|
6311
|
+
} catch (error) {
|
|
6312
|
+
if (this.reconnectFn && this.isAuthError(error) && this.canAttemptReconnect()) {
|
|
6313
|
+
await this.reconnect();
|
|
6314
|
+
return operation(this.sandbox.files);
|
|
6315
|
+
}
|
|
6316
|
+
throw error;
|
|
6317
|
+
}
|
|
6318
|
+
}
|
|
5957
6319
|
read(path, opts) {
|
|
5958
|
-
return this.
|
|
6320
|
+
return this.exec((f) => f.read(path, opts));
|
|
5959
6321
|
}
|
|
5960
6322
|
write(pathOrFiles, dataOrOpts, opts) {
|
|
5961
|
-
|
|
5962
|
-
|
|
6323
|
+
return this.exec((f) => {
|
|
6324
|
+
if (Array.isArray(pathOrFiles)) return f.write(pathOrFiles, dataOrOpts);
|
|
6325
|
+
return f.write(pathOrFiles, dataOrOpts, opts);
|
|
6326
|
+
});
|
|
5963
6327
|
}
|
|
5964
6328
|
async list(path, opts) {
|
|
5965
|
-
return this.
|
|
6329
|
+
return this.exec((f) => f.list(path, opts));
|
|
5966
6330
|
}
|
|
5967
6331
|
async exists(path, opts) {
|
|
5968
|
-
return this.
|
|
6332
|
+
return this.exec((f) => f.exists(path, opts));
|
|
5969
6333
|
}
|
|
5970
6334
|
async makeDir(path, opts) {
|
|
5971
|
-
return this.
|
|
6335
|
+
return this.exec((f) => f.makeDir(path, opts));
|
|
5972
6336
|
}
|
|
5973
6337
|
async remove(path, opts) {
|
|
5974
|
-
return this.
|
|
6338
|
+
return this.exec((f) => f.remove(path, opts));
|
|
5975
6339
|
}
|
|
5976
6340
|
async rename(oldPath, newPath, opts) {
|
|
5977
|
-
return this.
|
|
6341
|
+
return this.exec((f) => f.rename(oldPath, newPath, opts));
|
|
5978
6342
|
}
|
|
5979
6343
|
async getInfo(path, opts) {
|
|
5980
|
-
return this.
|
|
6344
|
+
return this.exec((f) => f.getInfo(path, opts));
|
|
5981
6345
|
}
|
|
5982
6346
|
async watchDir(path, onEvent, opts) {
|
|
5983
|
-
return this.
|
|
6347
|
+
return this.exec((f) => f.watchDir(path, onEvent, opts));
|
|
5984
6348
|
}
|
|
5985
6349
|
};
|
|
5986
6350
|
|
|
@@ -6132,8 +6496,11 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6132
6496
|
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
6133
6497
|
this.connectionCache = /* @__PURE__ */ new Map();
|
|
6134
6498
|
this.eventListeners = /* @__PURE__ */ new Map();
|
|
6499
|
+
this.productConfigCache = null;
|
|
6135
6500
|
this.options = options;
|
|
6136
6501
|
this.logger = options.logger;
|
|
6502
|
+
this.marketplaceCache = new LRUCache(200);
|
|
6503
|
+
this.pluginCache = new LRUCache(2e3);
|
|
6137
6504
|
if (options.endpoint) httpService.setBaseURL(options.endpoint);
|
|
6138
6505
|
if (options.authToken) httpService.setAuthToken(options.authToken);
|
|
6139
6506
|
if (options.headers && Object.keys(options.headers).length > 0) this.requestInterceptorId = httpService.registerRequestInterceptor((config) => {
|
|
@@ -6184,7 +6551,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6184
6551
|
const cached = this.filesystemCache.get(agentId);
|
|
6185
6552
|
if (cached) return cached;
|
|
6186
6553
|
const info = await this.getSandboxInfo(agentId);
|
|
6187
|
-
const
|
|
6554
|
+
const e2bFilesystem = await E2BFilesystem.connect(info);
|
|
6555
|
+
e2bFilesystem.setReconnectFn(async () => {
|
|
6556
|
+
this.logger?.debug(`Reconnecting E2B sandbox for agent: ${agentId}`);
|
|
6557
|
+
const newInfo = await this.getSandboxInfo(agentId);
|
|
6558
|
+
this.logger?.debug(`E2B sandbox reconnected for agent: ${agentId}`);
|
|
6559
|
+
return newInfo;
|
|
6560
|
+
});
|
|
6561
|
+
const filesystem = createAgentFilesystem(e2bFilesystem);
|
|
6188
6562
|
this.filesystemCache.set(agentId, filesystem);
|
|
6189
6563
|
this.logger?.debug(`Created filesystem for agent: ${agentId}`);
|
|
6190
6564
|
return filesystem;
|
|
@@ -6252,15 +6626,9 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6252
6626
|
const url = this.buildGetUrl("/console/as/conversations/", params);
|
|
6253
6627
|
const apiResponse = await httpService.get(url);
|
|
6254
6628
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
6255
|
-
const agents = apiResponse.data.conversations.map((a) => this.toAgentState(a));
|
|
6256
|
-
const pagination = apiResponse.data.pagination;
|
|
6257
|
-
console.log("[CloudAgentProvider] API response:", {
|
|
6258
|
-
agentsCount: agents.length,
|
|
6259
|
-
pagination
|
|
6260
|
-
});
|
|
6261
6629
|
return {
|
|
6262
|
-
agents,
|
|
6263
|
-
pagination
|
|
6630
|
+
agents: apiResponse.data.conversations.map((a) => this.toAgentState(a)),
|
|
6631
|
+
pagination: apiResponse.data.pagination
|
|
6264
6632
|
};
|
|
6265
6633
|
} catch (error) {
|
|
6266
6634
|
this.logger?.error("Failed to list agents:", error);
|
|
@@ -6270,13 +6638,21 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6270
6638
|
/**
|
|
6271
6639
|
* Create a new conversation
|
|
6272
6640
|
* POST {endpoint}/console/as/conversations
|
|
6641
|
+
* @param params - Session params containing cwd and optional configuration
|
|
6273
6642
|
*/
|
|
6274
|
-
async create() {
|
|
6643
|
+
async create(params) {
|
|
6275
6644
|
try {
|
|
6276
|
-
const
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
});
|
|
6645
|
+
const { options = {} } = params;
|
|
6646
|
+
const codebuddyMeta = options._meta?.["codebuddy.ai"];
|
|
6647
|
+
const tagsObj = options.tags || codebuddyMeta?.tags;
|
|
6648
|
+
const tagsArray = tagsObj ? Object.entries(tagsObj).map(([key, value]) => `${key}:${value}`) : void 0;
|
|
6649
|
+
const createPayload = {
|
|
6650
|
+
prompt: (options.prompt || "").slice(0, 100),
|
|
6651
|
+
model: options.model || "deepseek-r1",
|
|
6652
|
+
...tagsArray && tagsArray.length > 0 ? { tags: tagsArray } : {}
|
|
6653
|
+
};
|
|
6654
|
+
console.log("[CloudAgentProvider] Creating conversation with payload:", createPayload);
|
|
6655
|
+
const apiResponse = await httpService.post("/console/as/conversations/", createPayload);
|
|
6280
6656
|
if (!apiResponse.data) throw new Error("No data in API response");
|
|
6281
6657
|
this.logger?.info(`Created conversation: ${apiResponse.data.id}`);
|
|
6282
6658
|
return apiResponse.data.id;
|
|
@@ -6326,7 +6702,14 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6326
6702
|
} catch (error) {
|
|
6327
6703
|
this.logger?.debug(`Failed to fetch conversation details for ${agentId}:`, error);
|
|
6328
6704
|
}
|
|
6329
|
-
|
|
6705
|
+
const existingConnection = this.connectionCache.get(endpoint);
|
|
6706
|
+
if (existingConnection) {
|
|
6707
|
+
this.connectionCache.delete(endpoint);
|
|
6708
|
+
existingConnection.removeAllListeners();
|
|
6709
|
+
existingConnection.disconnect().catch((err) => {
|
|
6710
|
+
this.logger?.debug("Failed to disconnect old connection:", err);
|
|
6711
|
+
});
|
|
6712
|
+
}
|
|
6330
6713
|
const clientCapabilities = {
|
|
6331
6714
|
...this.options.clientCapabilities,
|
|
6332
6715
|
_meta: {
|
|
@@ -6446,6 +6829,35 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6446
6829
|
}
|
|
6447
6830
|
}
|
|
6448
6831
|
/**
|
|
6832
|
+
* Update conversation status by ID
|
|
6833
|
+
* POST {endpoint}/console/as/conversations/{agentId}
|
|
6834
|
+
*
|
|
6835
|
+
* @param agentId - Conversation ID to update
|
|
6836
|
+
* @param status - New status for the conversation
|
|
6837
|
+
* @returns PatchConversationResponse containing the updated conversation ID
|
|
6838
|
+
*
|
|
6839
|
+
* @example
|
|
6840
|
+
* ```typescript
|
|
6841
|
+
* const result = await provider.updateStatus('agent-123', 'completed');
|
|
6842
|
+
* console.log('Updated conversation status:', result.id);
|
|
6843
|
+
* ```
|
|
6844
|
+
*/
|
|
6845
|
+
async updateStatus(agentId, status) {
|
|
6846
|
+
try {
|
|
6847
|
+
const body = { status };
|
|
6848
|
+
const apiResponse = await httpService.post(`/console/as/conversations/${agentId}`, body);
|
|
6849
|
+
if (!apiResponse.data) {
|
|
6850
|
+
this.logger?.info(`Updated conversation status: ${agentId} to "${status}"`);
|
|
6851
|
+
return { id: agentId };
|
|
6852
|
+
}
|
|
6853
|
+
this.logger?.info(`Updated conversation status: ${apiResponse.data.id} to "${status}"`);
|
|
6854
|
+
return apiResponse.data;
|
|
6855
|
+
} catch (error) {
|
|
6856
|
+
this.logger?.error(`Failed to update conversation status ${agentId}:`, error);
|
|
6857
|
+
throw error;
|
|
6858
|
+
}
|
|
6859
|
+
}
|
|
6860
|
+
/**
|
|
6449
6861
|
* Get available models from product configuration
|
|
6450
6862
|
*
|
|
6451
6863
|
* GET {endpoint}/console/enterprises/{personal|enterpriseId}/models?repos[]={repo}
|
|
@@ -6476,9 +6888,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6476
6888
|
this.logger?.warn("[CloudAgentProvider] No data in config response, returning empty models");
|
|
6477
6889
|
return [];
|
|
6478
6890
|
}
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6891
|
+
this.productConfigCache = apiResponse.data;
|
|
6892
|
+
const productConfig = apiResponse.data;
|
|
6893
|
+
const allModels = productConfig.models ?? [];
|
|
6894
|
+
const cliModelIds = (productConfig.agents ?? []).find((agent) => agent.name === "cli")?.models ?? [];
|
|
6895
|
+
const filteredModels = cliModelIds.length > 0 ? allModels.filter((model) => cliModelIds.includes(model.id)) : allModels;
|
|
6896
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${filteredModels.length} models for cli agent (total: ${allModels.length})`);
|
|
6897
|
+
return filteredModels.map((model) => ({
|
|
6482
6898
|
id: model.id,
|
|
6483
6899
|
name: model.name ?? model.id,
|
|
6484
6900
|
description: model.description,
|
|
@@ -6500,6 +6916,43 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6500
6916
|
}
|
|
6501
6917
|
}
|
|
6502
6918
|
/**
|
|
6919
|
+
* 获取产品部署类型(从缓存)
|
|
6920
|
+
* 需要先调用 getModels() 初始化缓存
|
|
6921
|
+
*
|
|
6922
|
+
* @returns 部署类型:'SaaS' | 'Cloud-Hosted' | 'Self-Hosted',默认为 'SaaS'
|
|
6923
|
+
*/
|
|
6924
|
+
getDeploymentType() {
|
|
6925
|
+
return this.productConfigCache?.deploymentType ?? "SaaS";
|
|
6926
|
+
}
|
|
6927
|
+
/**
|
|
6928
|
+
* 获取 Credit 购买引导配置(从缓存)
|
|
6929
|
+
* 需要先调用 getModels() 初始化缓存
|
|
6930
|
+
*
|
|
6931
|
+
* @returns Credit 购买引导配置,key 为错误码。如果后端未返回,则使用默认配置
|
|
6932
|
+
*/
|
|
6933
|
+
getCreditPurchaseActions() {
|
|
6934
|
+
return this.productConfigCache?.config?.creditPurchaseActions ?? {
|
|
6935
|
+
"14018": {
|
|
6936
|
+
labelZh: "获取 Credits",
|
|
6937
|
+
labelEn: "Get credits",
|
|
6938
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6939
|
+
showButton: true
|
|
6940
|
+
},
|
|
6941
|
+
"6004": {
|
|
6942
|
+
labelZh: "升级专业版",
|
|
6943
|
+
labelEn: "Upgrade to Pro",
|
|
6944
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6945
|
+
showButton: true
|
|
6946
|
+
},
|
|
6947
|
+
"6005": {
|
|
6948
|
+
labelZh: "升级专业版",
|
|
6949
|
+
labelEn: "Upgrade to Pro",
|
|
6950
|
+
url: "https://www.codebuddy.cn/profile/plan",
|
|
6951
|
+
showButton: true
|
|
6952
|
+
}
|
|
6953
|
+
};
|
|
6954
|
+
}
|
|
6955
|
+
/**
|
|
6503
6956
|
* Generate a unique request ID
|
|
6504
6957
|
*/
|
|
6505
6958
|
generateRequestId() {
|
|
@@ -6582,7 +7035,7 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6582
7035
|
/**
|
|
6583
7036
|
* Upload files to cloud storage via COS presigned URL
|
|
6584
7037
|
*
|
|
6585
|
-
* @param params - files array (File objects in browser)
|
|
7038
|
+
* @param params - files array (File objects in browser), optional abortSignal
|
|
6586
7039
|
* @returns Response with corresponding cloud URLs
|
|
6587
7040
|
*/
|
|
6588
7041
|
async uploadFile(params) {
|
|
@@ -6592,12 +7045,13 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6592
7045
|
success: false,
|
|
6593
7046
|
error: "No valid File objects provided"
|
|
6594
7047
|
};
|
|
6595
|
-
const result = await this.cosUploadService.uploadFiles(files);
|
|
7048
|
+
const result = await this.cosUploadService.uploadFiles(files, params.abortSignal);
|
|
6596
7049
|
return {
|
|
6597
7050
|
success: result.success,
|
|
6598
7051
|
urls: result.urls,
|
|
6599
7052
|
expireSeconds: result.expireSeconds,
|
|
6600
|
-
error: result.error
|
|
7053
|
+
error: result.error,
|
|
7054
|
+
aborted: result.aborted
|
|
6601
7055
|
};
|
|
6602
7056
|
}
|
|
6603
7057
|
/**
|
|
@@ -6639,20 +7093,22 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6639
7093
|
}
|
|
6640
7094
|
/**
|
|
6641
7095
|
* 获取支持的场景列表
|
|
6642
|
-
* API 端点: GET /
|
|
7096
|
+
* API 端点: GET /v2/as/support/scenes (不鉴权)
|
|
6643
7097
|
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
6644
7098
|
*
|
|
7099
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
6645
7100
|
* @returns Promise<SupportScene[]> 支持的场景列表
|
|
6646
7101
|
*/
|
|
6647
|
-
async getSupportScenes() {
|
|
7102
|
+
async getSupportScenes(locale) {
|
|
6648
7103
|
try {
|
|
6649
|
-
const
|
|
7104
|
+
const url = this.buildGetUrl("/v2/as/support/scenes", locale ? { locale } : void 0);
|
|
7105
|
+
const apiResponse = await httpService.get(url);
|
|
6650
7106
|
if (!apiResponse.data) {
|
|
6651
7107
|
this.logger?.warn("[CloudAgentProvider] No data in support scenes response");
|
|
6652
7108
|
return [];
|
|
6653
7109
|
}
|
|
6654
7110
|
const scenes = apiResponse.data.scenes || [];
|
|
6655
|
-
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes`);
|
|
7111
|
+
this.logger?.info(`[CloudAgentProvider] Retrieved ${scenes.length} support scenes${locale ? ` for locale: ${locale}` : ""}`);
|
|
6656
7112
|
return scenes;
|
|
6657
7113
|
} catch (error) {
|
|
6658
7114
|
this.logger?.error("[CloudAgentProvider] Failed to get support scenes:", error);
|
|
@@ -6668,7 +7124,8 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6668
7124
|
type: "cloud",
|
|
6669
7125
|
status,
|
|
6670
7126
|
createdAt: data.createdAt ? new Date(data.createdAt) : void 0,
|
|
6671
|
-
capabilities: this.options.clientCapabilities
|
|
7127
|
+
capabilities: this.options.clientCapabilities,
|
|
7128
|
+
isUserDefinedTitle: data.isUserDefinedTitle
|
|
6672
7129
|
};
|
|
6673
7130
|
}
|
|
6674
7131
|
/**
|
|
@@ -6684,69 +7141,524 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
6684
7141
|
const queryString = searchParams.toString();
|
|
6685
7142
|
return queryString ? `${path}?${queryString}` : path;
|
|
6686
7143
|
}
|
|
6687
|
-
};
|
|
6688
|
-
|
|
6689
|
-
//#endregion
|
|
6690
|
-
//#region ../agent-provider/src/common/client/session.ts
|
|
6691
|
-
/**
|
|
6692
|
-
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
6693
|
-
*
|
|
6694
|
-
* This class wraps an AgentConnection and provides the session-centric API.
|
|
6695
|
-
* It is created by SessionManager when creating or loading sessions.
|
|
6696
|
-
*
|
|
6697
|
-
* @example
|
|
6698
|
-
* ```typescript
|
|
6699
|
-
* // Created by client.sessions.new() or client.sessions.load()
|
|
6700
|
-
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
6701
|
-
*
|
|
6702
|
-
* // Access agent state
|
|
6703
|
-
* console.log(session.agentState.status);
|
|
6704
|
-
*
|
|
6705
|
-
* // Send prompt
|
|
6706
|
-
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
6707
|
-
*
|
|
6708
|
-
* // Cleanup
|
|
6709
|
-
* session.disconnect();
|
|
6710
|
-
* ```
|
|
6711
|
-
*/
|
|
6712
|
-
var ActiveSessionImpl = class {
|
|
6713
|
-
/**
|
|
6714
|
-
* Create an ActiveSessionImpl instance
|
|
6715
|
-
*
|
|
6716
|
-
* @param sessionId - Session ID
|
|
6717
|
-
* @param agentId - Agent ID
|
|
6718
|
-
* @param connection - Already connected AgentConnection
|
|
6719
|
-
* @param options - Additional options
|
|
6720
|
-
*/
|
|
6721
|
-
constructor(sessionId, agentId, connection, options = {}) {
|
|
6722
|
-
this._availableCommands = [];
|
|
6723
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
6724
|
-
this.onceListeners = /* @__PURE__ */ new Map();
|
|
6725
|
-
this._id = sessionId;
|
|
6726
|
-
this._agentId = agentId;
|
|
6727
|
-
this.connection = connection;
|
|
6728
|
-
this.logger = options.logger;
|
|
6729
|
-
this._getFilesystem = options.getFilesystem;
|
|
6730
|
-
this._connectionInfo = options.connectionInfo;
|
|
6731
|
-
this.setupConnectionEvents(connection);
|
|
6732
|
-
this.agent = this.createAgentOperations();
|
|
6733
|
-
this.prompts = this.createPromptsResource();
|
|
6734
|
-
this.artifacts = this.createArtifactsResource();
|
|
6735
|
-
this.files = this.createFilesResource();
|
|
6736
|
-
}
|
|
6737
7144
|
/**
|
|
6738
|
-
*
|
|
7145
|
+
* 获取已安装插件列表
|
|
7146
|
+
* GET /console/as/user/plugins/installed
|
|
6739
7147
|
*/
|
|
6740
|
-
|
|
6741
|
-
|
|
7148
|
+
async getInstalledPlugins(forceRefresh) {
|
|
7149
|
+
try {
|
|
7150
|
+
const result = ((await httpService.get("/console/as/user/plugins/installed")).data?.plugins || []).map((p) => ({
|
|
7151
|
+
name: p.plugin_name,
|
|
7152
|
+
marketplaceName: p.marketplace_name,
|
|
7153
|
+
status: p.enabled ? "enabled" : "disabled",
|
|
7154
|
+
description: p.description,
|
|
7155
|
+
version: p.version,
|
|
7156
|
+
installScope: p.scope === "local" ? "project" : p.scope,
|
|
7157
|
+
installedScopes: [p.scope],
|
|
7158
|
+
installId: p.id
|
|
7159
|
+
}));
|
|
7160
|
+
result.forEach((plugin) => {
|
|
7161
|
+
this.pluginCache.set(plugin.name, plugin);
|
|
7162
|
+
});
|
|
7163
|
+
return result;
|
|
7164
|
+
} catch (error) {
|
|
7165
|
+
this.logger?.error("[CloudAgentProvider] getInstalledPlugins failed:", error);
|
|
7166
|
+
throw error;
|
|
7167
|
+
}
|
|
6742
7168
|
}
|
|
6743
7169
|
/**
|
|
6744
|
-
*
|
|
7170
|
+
* 安装插件
|
|
7171
|
+
* POST /console/as/user/plugins/install
|
|
7172
|
+
*
|
|
7173
|
+
* @param pluginNames - 插件名称数组
|
|
7174
|
+
* @param marketplaceNameOrId - 市场名称或 ID
|
|
6745
7175
|
*/
|
|
6746
|
-
|
|
6747
|
-
|
|
6748
|
-
|
|
6749
|
-
|
|
7176
|
+
async installPlugins(pluginNames, marketplaceNameOrId, installScope, marketplaceSource, workspacePath) {
|
|
7177
|
+
try {
|
|
7178
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7179
|
+
if (!marketplaceId) return {
|
|
7180
|
+
success: false,
|
|
7181
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7182
|
+
};
|
|
7183
|
+
const failed = (await Promise.allSettled(pluginNames.map((pluginName) => httpService.post("/console/as/user/plugins/install", {
|
|
7184
|
+
plugin_name: pluginName,
|
|
7185
|
+
marketplace_id: marketplaceId,
|
|
7186
|
+
version: "latest"
|
|
7187
|
+
})))).filter((r) => r.status === "rejected");
|
|
7188
|
+
if (failed.length > 0) {
|
|
7189
|
+
const errors = failed.map((r) => r.reason?.message || "Unknown error");
|
|
7190
|
+
return {
|
|
7191
|
+
success: false,
|
|
7192
|
+
error: `安装失败 ${failed.length} 个插件: ${errors.join(", ")}`
|
|
7193
|
+
};
|
|
7194
|
+
}
|
|
7195
|
+
return { success: true };
|
|
7196
|
+
} catch (error) {
|
|
7197
|
+
return {
|
|
7198
|
+
success: false,
|
|
7199
|
+
error: this.extractErrorMessage(error)
|
|
7200
|
+
};
|
|
7201
|
+
}
|
|
7202
|
+
}
|
|
7203
|
+
/**
|
|
7204
|
+
* 卸载插件
|
|
7205
|
+
* POST /console/as/user/plugins/installed/:id/uninstall
|
|
7206
|
+
*
|
|
7207
|
+
* 完整链路:
|
|
7208
|
+
* CloudAgentProvider.uninstallPlugin()
|
|
7209
|
+
* -> HTTP POST /console/as/user/plugins/installed/:id/uninstall
|
|
7210
|
+
* -> agentserver: PluginInstallService.Uninstall()
|
|
7211
|
+
* -> 软删除 DB + 异步同步到活跃沙箱
|
|
7212
|
+
*
|
|
7213
|
+
* @param pluginName - 插件名称
|
|
7214
|
+
* @param marketplaceName - 市场名称(用于标识唯一插件)
|
|
7215
|
+
* @param scope - 卸载范围 ('user' | 'project' | 'project-local')
|
|
7216
|
+
*/
|
|
7217
|
+
async uninstallPlugin(pluginName, marketplaceName, scope) {
|
|
7218
|
+
try {
|
|
7219
|
+
const installId = await this.findPluginInstallId(pluginName);
|
|
7220
|
+
if (!installId) return {
|
|
7221
|
+
success: false,
|
|
7222
|
+
error: `Plugin not found or not installed: ${pluginName}`
|
|
7223
|
+
};
|
|
7224
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/uninstall`);
|
|
7225
|
+
return { success: true };
|
|
7226
|
+
} catch (error) {
|
|
7227
|
+
return {
|
|
7228
|
+
success: false,
|
|
7229
|
+
error: this.extractErrorMessage(error)
|
|
7230
|
+
};
|
|
7231
|
+
}
|
|
7232
|
+
}
|
|
7233
|
+
/**
|
|
7234
|
+
* 获取插件市场列表
|
|
7235
|
+
* GET /console/as/marketplace/sources
|
|
7236
|
+
*/
|
|
7237
|
+
async getPluginMarketplaces(forceRefresh) {
|
|
7238
|
+
try {
|
|
7239
|
+
const result = ((await httpService.get("/console/as/marketplace/sources")).data?.sources || []).map((src) => ({
|
|
7240
|
+
id: src.id,
|
|
7241
|
+
name: src.name,
|
|
7242
|
+
type: this.mapSourceType(src.source_type),
|
|
7243
|
+
source: { url: src.url },
|
|
7244
|
+
description: src.name,
|
|
7245
|
+
isBuiltin: src.is_default
|
|
7246
|
+
}));
|
|
7247
|
+
result.forEach((m) => {
|
|
7248
|
+
const marketplaceInfo = {
|
|
7249
|
+
id: m.id,
|
|
7250
|
+
name: m.name
|
|
7251
|
+
};
|
|
7252
|
+
this.marketplaceCache.set(m.name, marketplaceInfo);
|
|
7253
|
+
this.marketplaceCache.set(m.id, marketplaceInfo);
|
|
7254
|
+
});
|
|
7255
|
+
return result;
|
|
7256
|
+
} catch (error) {
|
|
7257
|
+
this.logger?.error("[CloudAgentProvider] getPluginMarketplaces failed:", error);
|
|
7258
|
+
throw error;
|
|
7259
|
+
}
|
|
7260
|
+
}
|
|
7261
|
+
/**
|
|
7262
|
+
* 获取市场下的插件列表
|
|
7263
|
+
* - 有 searchText: GET /console/as/marketplace/plugins/search (跨所有市场搜索)
|
|
7264
|
+
* - 无 searchText: GET /console/as/marketplace/plugins (指定市场)
|
|
7265
|
+
*
|
|
7266
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
7267
|
+
* @param forceRefresh - 是否强制刷新
|
|
7268
|
+
* @param searchText - 搜索关键字(如果提供,则跨所有市场搜索)
|
|
7269
|
+
*/
|
|
7270
|
+
async getMarketplacePlugins(marketplaceNameOrId, forceRefresh, searchText) {
|
|
7271
|
+
try {
|
|
7272
|
+
if (searchText) return ((await httpService.get("/console/as/marketplace/plugins/search", { params: {
|
|
7273
|
+
q: searchText,
|
|
7274
|
+
page: 1,
|
|
7275
|
+
page_size: 100
|
|
7276
|
+
} })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
7277
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7278
|
+
if (!sourceId) {
|
|
7279
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
7280
|
+
return [];
|
|
7281
|
+
}
|
|
7282
|
+
const params = {
|
|
7283
|
+
source_id: sourceId,
|
|
7284
|
+
page: 1,
|
|
7285
|
+
page_size: 100
|
|
7286
|
+
};
|
|
7287
|
+
return ((await httpService.get("/console/as/marketplace/plugins", { params })).data?.plugins || []).map((p) => this.mapPluginData(p));
|
|
7288
|
+
} catch (error) {
|
|
7289
|
+
this.logger?.error("[CloudAgentProvider] getMarketplacePlugins failed:", error);
|
|
7290
|
+
throw error;
|
|
7291
|
+
}
|
|
7292
|
+
}
|
|
7293
|
+
/**
|
|
7294
|
+
* 获取插件详情
|
|
7295
|
+
* GET /console/as/marketplace/plugins/:name/detail
|
|
7296
|
+
*
|
|
7297
|
+
* @param pluginName - 插件名称
|
|
7298
|
+
* @param marketplaceNameOrId - 市场名称或 ID(优先使用 ID,如果是名称则从缓存查询 ID)
|
|
7299
|
+
*/
|
|
7300
|
+
async getPluginDetail(pluginName, marketplaceNameOrId) {
|
|
7301
|
+
try {
|
|
7302
|
+
const sourceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7303
|
+
if (!sourceId) {
|
|
7304
|
+
this.logger?.warn(`[CloudAgentProvider] Marketplace not found: ${marketplaceNameOrId}`);
|
|
7305
|
+
return null;
|
|
7306
|
+
}
|
|
7307
|
+
const p = (await httpService.get(`/console/as/marketplace/plugins/${pluginName}/detail`, { params: { source_id: sourceId } })).data?.plugin;
|
|
7308
|
+
if (!p) return null;
|
|
7309
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
7310
|
+
const tags = p.tags ? JSON.parse(p.tags) : [];
|
|
7311
|
+
return {
|
|
7312
|
+
name: p.name,
|
|
7313
|
+
marketplaceName: p.marketplace_name,
|
|
7314
|
+
description: p.description,
|
|
7315
|
+
version: p.version,
|
|
7316
|
+
iconUrl: p.icon_url,
|
|
7317
|
+
tags,
|
|
7318
|
+
installed: p.installed,
|
|
7319
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
7320
|
+
readme: p.readme,
|
|
7321
|
+
author: p.author,
|
|
7322
|
+
homepage: p.homepage,
|
|
7323
|
+
repositoryUrl: p.repository_url,
|
|
7324
|
+
license: p.license,
|
|
7325
|
+
...capabilities
|
|
7326
|
+
};
|
|
7327
|
+
} catch (error) {
|
|
7328
|
+
this.logger?.error("[CloudAgentProvider] getPluginDetail failed:", error);
|
|
7329
|
+
throw error;
|
|
7330
|
+
}
|
|
7331
|
+
}
|
|
7332
|
+
/**
|
|
7333
|
+
* 添加插件市场
|
|
7334
|
+
* POST /console/as/marketplace/sources
|
|
7335
|
+
*/
|
|
7336
|
+
async addPluginMarketplace(sourceUrl, name) {
|
|
7337
|
+
try {
|
|
7338
|
+
const body = {
|
|
7339
|
+
source_type: sourceUrl.startsWith("http") ? "url" : "github",
|
|
7340
|
+
url: sourceUrl,
|
|
7341
|
+
name: name || sourceUrl
|
|
7342
|
+
};
|
|
7343
|
+
const sourceData = (await httpService.post("/console/as/marketplace/sources", body)).data?.source;
|
|
7344
|
+
if (!sourceData) throw new Error("Invalid response from server");
|
|
7345
|
+
const marketplaceInfo = {
|
|
7346
|
+
id: sourceData.id,
|
|
7347
|
+
name: sourceData.name
|
|
7348
|
+
};
|
|
7349
|
+
this.marketplaceCache.set(sourceData.name, marketplaceInfo);
|
|
7350
|
+
this.marketplaceCache.set(sourceData.id, marketplaceInfo);
|
|
7351
|
+
return {
|
|
7352
|
+
success: true,
|
|
7353
|
+
marketplace: {
|
|
7354
|
+
id: sourceData.id,
|
|
7355
|
+
name: sourceData.name,
|
|
7356
|
+
type: this.mapSourceType(sourceData.source_type),
|
|
7357
|
+
source: { url: sourceData.url },
|
|
7358
|
+
isBuiltin: sourceData.is_default
|
|
7359
|
+
}
|
|
7360
|
+
};
|
|
7361
|
+
} catch (error) {
|
|
7362
|
+
return {
|
|
7363
|
+
success: false,
|
|
7364
|
+
error: this.extractErrorMessage(error)
|
|
7365
|
+
};
|
|
7366
|
+
}
|
|
7367
|
+
}
|
|
7368
|
+
/**
|
|
7369
|
+
* 删除插件市场
|
|
7370
|
+
* POST /console/as/marketplace/sources/:id/delete
|
|
7371
|
+
*/
|
|
7372
|
+
async removePluginMarketplace(marketplaceNameOrId) {
|
|
7373
|
+
try {
|
|
7374
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7375
|
+
if (!marketplaceId) return {
|
|
7376
|
+
success: false,
|
|
7377
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7378
|
+
};
|
|
7379
|
+
await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/delete`, {});
|
|
7380
|
+
return { success: true };
|
|
7381
|
+
} catch (error) {
|
|
7382
|
+
return {
|
|
7383
|
+
success: false,
|
|
7384
|
+
error: this.extractErrorMessage(error)
|
|
7385
|
+
};
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
7388
|
+
/**
|
|
7389
|
+
* 刷新插件市场
|
|
7390
|
+
* POST /console/as/marketplace/sources/:id/check-updates
|
|
7391
|
+
*/
|
|
7392
|
+
async refreshPluginMarketplace(marketplaceNameOrId) {
|
|
7393
|
+
try {
|
|
7394
|
+
const marketplaceId = await this.findMarketplaceId(marketplaceNameOrId);
|
|
7395
|
+
if (!marketplaceId) return {
|
|
7396
|
+
success: false,
|
|
7397
|
+
error: `Marketplace not found: ${marketplaceNameOrId}`
|
|
7398
|
+
};
|
|
7399
|
+
return {
|
|
7400
|
+
success: true,
|
|
7401
|
+
plugins: (await httpService.post(`/console/as/marketplace/sources/${marketplaceId}/check-updates`, {})).data?.updated_plugins || []
|
|
7402
|
+
};
|
|
7403
|
+
} catch (error) {
|
|
7404
|
+
return {
|
|
7405
|
+
success: false,
|
|
7406
|
+
error: this.extractErrorMessage(error)
|
|
7407
|
+
};
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
/**
|
|
7411
|
+
* 批量切换插件启用/禁用状态
|
|
7412
|
+
* POST /console/as/user/plugins/installed/:id/toggle
|
|
7413
|
+
*/
|
|
7414
|
+
async batchTogglePlugins(request) {
|
|
7415
|
+
try {
|
|
7416
|
+
const results = await Promise.allSettled(request.items.map(async (item) => {
|
|
7417
|
+
const installId = await this.findPluginInstallId(item.pluginName);
|
|
7418
|
+
if (!installId) throw new Error(`Plugin not found or not installed: ${item.pluginName}`);
|
|
7419
|
+
const enabled = item.operation === "enable";
|
|
7420
|
+
await httpService.post(`/console/as/user/plugins/installed/${installId}/toggle`, { enabled });
|
|
7421
|
+
return item;
|
|
7422
|
+
}));
|
|
7423
|
+
const succeededPlugins = [];
|
|
7424
|
+
const failedPlugins = [];
|
|
7425
|
+
results.forEach((r, i) => {
|
|
7426
|
+
const item = request.items[i];
|
|
7427
|
+
if (r.status === "fulfilled") succeededPlugins.push(item);
|
|
7428
|
+
else failedPlugins.push({
|
|
7429
|
+
...item,
|
|
7430
|
+
error: r.reason?.message || "Unknown error"
|
|
7431
|
+
});
|
|
7432
|
+
});
|
|
7433
|
+
return {
|
|
7434
|
+
success: failedPlugins.length === 0,
|
|
7435
|
+
succeededPlugins,
|
|
7436
|
+
failedPlugins
|
|
7437
|
+
};
|
|
7438
|
+
} catch (error) {
|
|
7439
|
+
return {
|
|
7440
|
+
success: false,
|
|
7441
|
+
succeededPlugins: [],
|
|
7442
|
+
failedPlugins: request.items.map((item) => ({
|
|
7443
|
+
...item,
|
|
7444
|
+
error: this.extractErrorMessage(error)
|
|
7445
|
+
}))
|
|
7446
|
+
};
|
|
7447
|
+
}
|
|
7448
|
+
}
|
|
7449
|
+
/**
|
|
7450
|
+
* 将后端插件数据映射为前端格式
|
|
7451
|
+
*/
|
|
7452
|
+
mapPluginData(p) {
|
|
7453
|
+
const capabilities = p.capabilities ? this.parseCapabilities(p.capabilities) : {};
|
|
7454
|
+
let tags = [];
|
|
7455
|
+
if (p.tags) {
|
|
7456
|
+
if (Array.isArray(p.tags)) tags = p.tags;
|
|
7457
|
+
else if (typeof p.tags === "string") try {
|
|
7458
|
+
const parsed = JSON.parse(p.tags);
|
|
7459
|
+
tags = Array.isArray(parsed) ? parsed : [parsed];
|
|
7460
|
+
} catch {
|
|
7461
|
+
tags = p.tags.split(",").map((t) => t.trim()).filter((t) => t);
|
|
7462
|
+
}
|
|
7463
|
+
}
|
|
7464
|
+
return {
|
|
7465
|
+
name: p.name,
|
|
7466
|
+
marketplaceName: p.marketplace_name,
|
|
7467
|
+
description: p.description,
|
|
7468
|
+
version: p.version,
|
|
7469
|
+
iconUrl: p.icon_url,
|
|
7470
|
+
tags,
|
|
7471
|
+
installed: p.installed,
|
|
7472
|
+
status: p.enabled ? "enabled" : p.installed ? "disabled" : "not-installed",
|
|
7473
|
+
installedScopes: p.installed ? [p.installed_scope] : [],
|
|
7474
|
+
...capabilities
|
|
7475
|
+
};
|
|
7476
|
+
}
|
|
7477
|
+
/**
|
|
7478
|
+
* 从缓存中查找插件的 install_id
|
|
7479
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
7480
|
+
*
|
|
7481
|
+
* @param pluginName - 插件名称
|
|
7482
|
+
* @returns install_id 或 null
|
|
7483
|
+
*/
|
|
7484
|
+
async findPluginInstallId(pluginName) {
|
|
7485
|
+
const cached = this.pluginCache.get(pluginName);
|
|
7486
|
+
if (cached) return cached.installId;
|
|
7487
|
+
await this.getInstalledPlugins();
|
|
7488
|
+
return this.pluginCache.get(pluginName)?.installId || null;
|
|
7489
|
+
}
|
|
7490
|
+
/**
|
|
7491
|
+
* 从缓存中查找 marketplace ID
|
|
7492
|
+
* 如果缓存未命中,则调用 API 获取并缓存
|
|
7493
|
+
*/
|
|
7494
|
+
async findMarketplaceId(nameOrId) {
|
|
7495
|
+
const cached = this.marketplaceCache.get(nameOrId);
|
|
7496
|
+
if (cached) return cached.id;
|
|
7497
|
+
await this.getPluginMarketplaces();
|
|
7498
|
+
return this.marketplaceCache.get(nameOrId)?.id || null;
|
|
7499
|
+
}
|
|
7500
|
+
/**
|
|
7501
|
+
* 提取 API 错误信息
|
|
7502
|
+
* 从 AxiosError 中提取详细的错误信息,包括 HTTP 状态码、错误码和错误消息
|
|
7503
|
+
*/
|
|
7504
|
+
extractErrorMessage(error) {
|
|
7505
|
+
if (error instanceof AxiosError) {
|
|
7506
|
+
const status = error.response?.status;
|
|
7507
|
+
const apiResponse = error.response?.data;
|
|
7508
|
+
const parts = [];
|
|
7509
|
+
if (status) parts.push(`HTTP ${status}`);
|
|
7510
|
+
if (apiResponse?.code) parts.push(`Code ${apiResponse.code}`);
|
|
7511
|
+
if (apiResponse?.msg) parts.push(apiResponse.msg);
|
|
7512
|
+
else if (error.message) parts.push(error.message);
|
|
7513
|
+
const errorMessage = parts.join(" - ");
|
|
7514
|
+
this.logger?.error("[CloudAgentProvider] API Error:", {
|
|
7515
|
+
status,
|
|
7516
|
+
code: apiResponse?.code,
|
|
7517
|
+
msg: apiResponse?.msg,
|
|
7518
|
+
requestId: apiResponse?.requestId,
|
|
7519
|
+
url: error.config?.url,
|
|
7520
|
+
method: error.config?.method
|
|
7521
|
+
});
|
|
7522
|
+
return errorMessage;
|
|
7523
|
+
}
|
|
7524
|
+
if (error instanceof Error) return error.message;
|
|
7525
|
+
return "Unknown error";
|
|
7526
|
+
}
|
|
7527
|
+
/**
|
|
7528
|
+
* 映射后端 source_type 到前端类型
|
|
7529
|
+
*/
|
|
7530
|
+
mapSourceType(sourceType) {
|
|
7531
|
+
switch (sourceType) {
|
|
7532
|
+
case "github": return "github";
|
|
7533
|
+
case "official": return "custom";
|
|
7534
|
+
default: return "custom";
|
|
7535
|
+
}
|
|
7536
|
+
}
|
|
7537
|
+
/**
|
|
7538
|
+
* 解析 capabilities JSON 字符串
|
|
7539
|
+
*/
|
|
7540
|
+
parseCapabilities(capabilitiesStr) {
|
|
7541
|
+
try {
|
|
7542
|
+
const cap = JSON.parse(capabilitiesStr);
|
|
7543
|
+
return {
|
|
7544
|
+
commands: cap.commands,
|
|
7545
|
+
skills: cap.skills,
|
|
7546
|
+
mcpServers: cap.mcp,
|
|
7547
|
+
agents: cap.agents,
|
|
7548
|
+
hooks: cap.hooks,
|
|
7549
|
+
rules: cap.rules
|
|
7550
|
+
};
|
|
7551
|
+
} catch {
|
|
7552
|
+
return {};
|
|
7553
|
+
}
|
|
7554
|
+
}
|
|
7555
|
+
/**
|
|
7556
|
+
* 上报 telemetry 事件(Cloud 模式)
|
|
7557
|
+
* 通过 HTTP POST 发送到 /v2/report
|
|
7558
|
+
* 注入用户信息和浏览器环境等公共字段
|
|
7559
|
+
*/
|
|
7560
|
+
async reportTelemetry(eventName, payload) {
|
|
7561
|
+
try {
|
|
7562
|
+
const account = accountService.getAccount();
|
|
7563
|
+
const commonFields = {};
|
|
7564
|
+
if (account) {
|
|
7565
|
+
commonFields.userId = account.uid;
|
|
7566
|
+
commonFields.userNickname = account.nickname;
|
|
7567
|
+
if (account.enterpriseId) commonFields.enterpriseId = account.enterpriseId;
|
|
7568
|
+
if (account.enterpriseUserName) commonFields.username = account.enterpriseUserName;
|
|
7569
|
+
}
|
|
7570
|
+
if (typeof navigator !== "undefined") {
|
|
7571
|
+
commonFields.userAgent = navigator.userAgent;
|
|
7572
|
+
commonFields.os = navigator.platform;
|
|
7573
|
+
}
|
|
7574
|
+
const events = [{
|
|
7575
|
+
eventCode: eventName,
|
|
7576
|
+
timestamp: Date.now(),
|
|
7577
|
+
reportDelay: 0,
|
|
7578
|
+
...commonFields,
|
|
7579
|
+
...payload
|
|
7580
|
+
}];
|
|
7581
|
+
await httpService.post("/v2/report", events);
|
|
7582
|
+
} catch (error) {
|
|
7583
|
+
this.logger?.warn("reportTelemetry() failed:", error);
|
|
7584
|
+
}
|
|
7585
|
+
}
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7588
|
+
//#endregion
|
|
7589
|
+
//#region ../agent-provider/src/common/client/session.ts
|
|
7590
|
+
/**
|
|
7591
|
+
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
7592
|
+
*
|
|
7593
|
+
* This class wraps an AgentConnection and provides the session-centric API.
|
|
7594
|
+
* It is created by SessionManager when creating or loading sessions.
|
|
7595
|
+
*
|
|
7596
|
+
* @example
|
|
7597
|
+
* ```typescript
|
|
7598
|
+
* // Created by client.sessions.new() or client.sessions.load()
|
|
7599
|
+
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
7600
|
+
*
|
|
7601
|
+
* // Access agent state
|
|
7602
|
+
* console.log(session.agentState.status);
|
|
7603
|
+
*
|
|
7604
|
+
* // Send prompt
|
|
7605
|
+
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
7606
|
+
*
|
|
7607
|
+
* // Cleanup
|
|
7608
|
+
* session.disconnect();
|
|
7609
|
+
* ```
|
|
7610
|
+
*/
|
|
7611
|
+
var ActiveSessionImpl = class {
|
|
7612
|
+
/**
|
|
7613
|
+
* Create an ActiveSessionImpl instance
|
|
7614
|
+
*
|
|
7615
|
+
* @param sessionId - Session ID
|
|
7616
|
+
* @param agentId - Agent ID
|
|
7617
|
+
* @param connection - Already connected AgentConnection
|
|
7618
|
+
* @param options - Additional options
|
|
7619
|
+
*/
|
|
7620
|
+
constructor(sessionId, agentId, connection, options = {}) {
|
|
7621
|
+
this._availableCommands = [];
|
|
7622
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
7623
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
7624
|
+
this.connectionListeners = [];
|
|
7625
|
+
this._id = sessionId;
|
|
7626
|
+
this._agentId = agentId;
|
|
7627
|
+
this.connection = connection;
|
|
7628
|
+
this.logger = options.logger;
|
|
7629
|
+
this._getFilesystem = options.getFilesystem;
|
|
7630
|
+
this._connectionInfo = options.connectionInfo;
|
|
7631
|
+
this.setupConnectionEvents(connection);
|
|
7632
|
+
this.agent = this.createAgentOperations();
|
|
7633
|
+
this.prompts = this.createPromptsResource();
|
|
7634
|
+
this.artifacts = this.createArtifactsResource();
|
|
7635
|
+
this.files = this.createFilesResource();
|
|
7636
|
+
}
|
|
7637
|
+
/**
|
|
7638
|
+
* Session ID
|
|
7639
|
+
*/
|
|
7640
|
+
get id() {
|
|
7641
|
+
return this._id;
|
|
7642
|
+
}
|
|
7643
|
+
/**
|
|
7644
|
+
* Agent ID
|
|
7645
|
+
*/
|
|
7646
|
+
get agentId() {
|
|
7647
|
+
return this._agentId;
|
|
7648
|
+
}
|
|
7649
|
+
/**
|
|
7650
|
+
* Actual workspace path (set from newSession response _meta)
|
|
7651
|
+
*/
|
|
7652
|
+
get cwd() {
|
|
7653
|
+
return this._cwd;
|
|
7654
|
+
}
|
|
7655
|
+
/**
|
|
7656
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
7657
|
+
*/
|
|
7658
|
+
setCwd(cwd) {
|
|
7659
|
+
this._cwd = cwd;
|
|
7660
|
+
}
|
|
7661
|
+
/**
|
|
6750
7662
|
* Agent state (live connection state)
|
|
6751
7663
|
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
6752
7664
|
*/
|
|
@@ -6963,8 +7875,8 @@ var ActiveSessionImpl = class {
|
|
|
6963
7875
|
* await session.setMode('architect');
|
|
6964
7876
|
* ```
|
|
6965
7877
|
*/
|
|
6966
|
-
async setMode(modeId) {
|
|
6967
|
-
if (this._availableModes) {
|
|
7878
|
+
async setMode(modeId, skipAvailableChecker) {
|
|
7879
|
+
if (this._availableModes && !skipAvailableChecker) {
|
|
6968
7880
|
if (!this._availableModes.some((m) => m.id === modeId)) {
|
|
6969
7881
|
const availableIds = this._availableModes.map((m) => m.id).join(", ");
|
|
6970
7882
|
throw new Error(`Invalid modeId: "${modeId}". Available modes: ${availableIds}`);
|
|
@@ -6987,6 +7899,7 @@ var ActiveSessionImpl = class {
|
|
|
6987
7899
|
* ```
|
|
6988
7900
|
*/
|
|
6989
7901
|
async setSessionModel(modelId) {
|
|
7902
|
+
this._currentModelId = modelId;
|
|
6990
7903
|
await this.getConnectionOrThrow().setSessionModel(this._id, modelId);
|
|
6991
7904
|
}
|
|
6992
7905
|
/**
|
|
@@ -7064,11 +7977,23 @@ var ActiveSessionImpl = class {
|
|
|
7064
7977
|
* Disconnect from the session/agent
|
|
7065
7978
|
*/
|
|
7066
7979
|
disconnect() {
|
|
7980
|
+
this.removeConnectionListeners();
|
|
7067
7981
|
this.connection.disconnect();
|
|
7068
7982
|
this.removeAllListeners();
|
|
7069
7983
|
this.logger?.info(`Session ${this._id}: Disconnected`);
|
|
7070
7984
|
}
|
|
7071
7985
|
/**
|
|
7986
|
+
* Detach the session from connection events without disconnecting the connection.
|
|
7987
|
+
* This should be called when the session is being replaced but the connection is shared.
|
|
7988
|
+
* Unlike disconnect(), this only removes event listeners without closing the connection.
|
|
7989
|
+
*/
|
|
7990
|
+
detach() {
|
|
7991
|
+
this.logger?.info(`Session ${this._id}: Detaching from connection events`);
|
|
7992
|
+
this.removeConnectionListeners();
|
|
7993
|
+
this.removeAllListeners();
|
|
7994
|
+
this.logger?.info(`Session ${this._id}: Detached successfully`);
|
|
7995
|
+
}
|
|
7996
|
+
/**
|
|
7072
7997
|
* Symbol.dispose for 'using' keyword support
|
|
7073
7998
|
* Automatically disconnects and cleans up when session goes out of scope
|
|
7074
7999
|
*
|
|
@@ -7087,60 +8012,85 @@ var ActiveSessionImpl = class {
|
|
|
7087
8012
|
if (!this.connection.isInitialized) throw new Error(`Session ${this._id}: Connection not initialized.`);
|
|
7088
8013
|
return this.connection;
|
|
7089
8014
|
}
|
|
8015
|
+
/**
|
|
8016
|
+
* 在 connection 上注册 listener 并保存引用,便于 disconnect 时移除
|
|
8017
|
+
*/
|
|
8018
|
+
addConnectionListener(connection, event, listener) {
|
|
8019
|
+
connection.on(event, listener);
|
|
8020
|
+
this.connectionListeners.push({
|
|
8021
|
+
event,
|
|
8022
|
+
listener
|
|
8023
|
+
});
|
|
8024
|
+
}
|
|
8025
|
+
/**
|
|
8026
|
+
* 从 connection 上移除所有本 session 注册的 listener
|
|
8027
|
+
*/
|
|
8028
|
+
removeConnectionListeners() {
|
|
8029
|
+
for (const { event, listener } of this.connectionListeners) this.connection.off(event, listener);
|
|
8030
|
+
this.connectionListeners = [];
|
|
8031
|
+
}
|
|
7090
8032
|
setupConnectionEvents(connection) {
|
|
7091
|
-
|
|
8033
|
+
this.addConnectionListener(connection, "connected", () => {
|
|
7092
8034
|
this.emit("connected", void 0);
|
|
7093
8035
|
});
|
|
7094
|
-
|
|
8036
|
+
this.addConnectionListener(connection, "disconnected", () => {
|
|
7095
8037
|
this.emit("disconnected", void 0);
|
|
7096
8038
|
});
|
|
7097
|
-
|
|
8039
|
+
this.addConnectionListener(connection, "error", (error) => {
|
|
7098
8040
|
this.emit("error", error);
|
|
7099
8041
|
});
|
|
7100
|
-
|
|
8042
|
+
this.addConnectionListener(connection, "sessionUpdate", (update) => {
|
|
8043
|
+
const notificationSessionId = update?.sessionId;
|
|
8044
|
+
if (notificationSessionId && notificationSessionId !== this._id) {
|
|
8045
|
+
console.log(`[RT-DEBUG][AgentMgr:Session] sessionUpdate SKIPPED: notifSessionId mismatch, notif=${notificationSessionId?.substring(0, 8)}, my=${this._id?.substring(0, 8)}`);
|
|
8046
|
+
return;
|
|
8047
|
+
}
|
|
7101
8048
|
this.emit("sessionUpdate", update);
|
|
7102
8049
|
});
|
|
7103
|
-
|
|
7104
|
-
|
|
7105
|
-
artifactUri: artifact.uri,
|
|
7106
|
-
artifactType: artifact.type
|
|
7107
|
-
});
|
|
8050
|
+
this.addConnectionListener(connection, "artifactCreated", (artifact) => {
|
|
8051
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7108
8052
|
this.emit("artifactCreated", artifact);
|
|
7109
8053
|
});
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
artifactUri: artifact.uri,
|
|
7113
|
-
artifactType: artifact.type
|
|
7114
|
-
});
|
|
8054
|
+
this.addConnectionListener(connection, "artifactUpdated", (artifact) => {
|
|
8055
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7115
8056
|
this.emit("artifactUpdated", artifact);
|
|
7116
8057
|
});
|
|
7117
|
-
|
|
7118
|
-
|
|
8058
|
+
this.addConnectionListener(connection, "artifactDeleted", (artifact) => {
|
|
8059
|
+
if (!this.shouldForwardArtifact(artifact)) return;
|
|
7119
8060
|
this.emit("artifactDeleted", artifact);
|
|
7120
8061
|
});
|
|
7121
|
-
|
|
8062
|
+
this.addConnectionListener(connection, "permissionRequest", (request) => {
|
|
7122
8063
|
this.emit("permissionRequest", request);
|
|
7123
8064
|
});
|
|
7124
|
-
|
|
8065
|
+
this.addConnectionListener(connection, "questionRequest", (request) => {
|
|
7125
8066
|
this.emit("questionRequest", request);
|
|
7126
8067
|
});
|
|
7127
|
-
|
|
8068
|
+
this.addConnectionListener(connection, "questionCancelled", () => {
|
|
7128
8069
|
this.prompts.cancel();
|
|
7129
8070
|
});
|
|
7130
|
-
|
|
8071
|
+
this.addConnectionListener(connection, "usageUpdate", (usage) => {
|
|
7131
8072
|
this.emit("usageUpdate", usage);
|
|
7132
8073
|
});
|
|
7133
|
-
|
|
8074
|
+
this.addConnectionListener(connection, "checkpointCreated", (checkpoint) => {
|
|
8075
|
+
const originSessionId = checkpoint.__sessionId;
|
|
8076
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
7134
8077
|
this.emit("checkpointCreated", checkpoint);
|
|
7135
8078
|
});
|
|
7136
|
-
|
|
8079
|
+
this.addConnectionListener(connection, "checkpointUpdated", (checkpoint) => {
|
|
8080
|
+
const originSessionId = checkpoint.__sessionId;
|
|
8081
|
+
if (originSessionId && originSessionId !== this._id) return;
|
|
7137
8082
|
this.emit("checkpointUpdated", checkpoint);
|
|
7138
8083
|
});
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
8084
|
+
this.addConnectionListener(connection, "command", (command) => {
|
|
8085
|
+
const originSessionId = command.__sessionId;
|
|
8086
|
+
if (originSessionId && originSessionId !== this._id) {
|
|
8087
|
+
console.log("[Session] Command not forwarded:", {
|
|
8088
|
+
command,
|
|
8089
|
+
originSessionId,
|
|
8090
|
+
sessionId: this._id
|
|
8091
|
+
});
|
|
8092
|
+
return;
|
|
8093
|
+
}
|
|
7144
8094
|
this.emit("command", command);
|
|
7145
8095
|
});
|
|
7146
8096
|
}
|
|
@@ -7150,19 +8100,38 @@ var ActiveSessionImpl = class {
|
|
|
7150
8100
|
_meta: response._meta ?? void 0
|
|
7151
8101
|
};
|
|
7152
8102
|
}
|
|
8103
|
+
/**
|
|
8104
|
+
* 判断 artifact 是否应该转发给当前 session
|
|
8105
|
+
* - media 类型:按 cwd 路径隔离(同 cwd 下不同 session 可共享 media)
|
|
8106
|
+
* - 其余类型:按 __sessionId 严格隔离
|
|
8107
|
+
*/
|
|
8108
|
+
shouldForwardArtifact(artifact) {
|
|
8109
|
+
const originSessionId = artifact.__sessionId;
|
|
8110
|
+
console.log("[Session] shouldForwardArtifact:", {
|
|
8111
|
+
artifact,
|
|
8112
|
+
originSessionId,
|
|
8113
|
+
sessionId: this._id,
|
|
8114
|
+
cwd: this.connection?.cwd
|
|
8115
|
+
});
|
|
8116
|
+
if (artifact.type === "media") {
|
|
8117
|
+
const cwd = this.connection?.cwd;
|
|
8118
|
+
const uri = artifact?.uri;
|
|
8119
|
+
if (cwd && uri) {
|
|
8120
|
+
const toPosix = (p) => p.replace(/\\/g, "/");
|
|
8121
|
+
const uriPath = toPosix(uri.replace(/^(?:file|agent):\/\//, ""));
|
|
8122
|
+
const posixCwd = toPosix(cwd);
|
|
8123
|
+
const normalizedCwd = posixCwd.endsWith("/") ? posixCwd : posixCwd + "/";
|
|
8124
|
+
return uriPath.startsWith(normalizedCwd);
|
|
8125
|
+
}
|
|
8126
|
+
}
|
|
8127
|
+
if (originSessionId && originSessionId !== this._id) return false;
|
|
8128
|
+
return true;
|
|
8129
|
+
}
|
|
7153
8130
|
};
|
|
7154
8131
|
|
|
7155
8132
|
//#endregion
|
|
7156
8133
|
//#region ../agent-provider/src/common/client/session-manager.ts
|
|
7157
8134
|
/**
|
|
7158
|
-
* SessionManager - Manages session lifecycle and connections
|
|
7159
|
-
*
|
|
7160
|
-
* Provides the core implementation for session-centric API operations:
|
|
7161
|
-
* - list() - Lists sessions (mapped from agents)
|
|
7162
|
-
* - createSession() - Creates new session (auto-creates agent)
|
|
7163
|
-
* - loadSession() - Loads existing session (finds agent by sessionId)
|
|
7164
|
-
*/
|
|
7165
|
-
/**
|
|
7166
8135
|
* SessionManager - Session lifecycle management
|
|
7167
8136
|
*
|
|
7168
8137
|
* This class manages the relationship between sessions and agents.
|
|
@@ -7212,7 +8181,8 @@ var SessionManager = class {
|
|
|
7212
8181
|
createdAt: agent.createdAt,
|
|
7213
8182
|
lastActivityAt: agent.updatedAt,
|
|
7214
8183
|
cwd: agent.type === "local" ? agent.cwd : void 0,
|
|
7215
|
-
isPlayground: agent.isPlayground
|
|
8184
|
+
isPlayground: agent.isPlayground,
|
|
8185
|
+
isUserDefinedTitle: agent.isUserDefinedTitle
|
|
7216
8186
|
}));
|
|
7217
8187
|
console.log("[SessionManager] Returning sessions:", {
|
|
7218
8188
|
count: sessions.length,
|
|
@@ -7239,13 +8209,26 @@ var SessionManager = class {
|
|
|
7239
8209
|
if (this.provider.create) {
|
|
7240
8210
|
agentId = await this.provider.create(params);
|
|
7241
8211
|
this.logger?.debug(`Created new agent: ${agentId}`);
|
|
8212
|
+
if (params.options?.onSessionPrepared) {
|
|
8213
|
+
const initialPrompt = params.options?.prompt;
|
|
8214
|
+
const initialTitle = initialPrompt?.slice(0, 50) || "";
|
|
8215
|
+
params.options.onSessionPrepared({
|
|
8216
|
+
id: agentId,
|
|
8217
|
+
agentId,
|
|
8218
|
+
name: initialTitle + (initialPrompt && initialPrompt.length > 50 ? "..." : ""),
|
|
8219
|
+
status: "connecting",
|
|
8220
|
+
cwd: params.cwd || "",
|
|
8221
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
8222
|
+
});
|
|
8223
|
+
this.logger?.debug(`Called onSessionPrepared for: ${agentId}`);
|
|
8224
|
+
}
|
|
7242
8225
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
7243
8226
|
const connection = await this.provider.connect(agentId);
|
|
7244
8227
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
7245
8228
|
const response = await connection.createSession({
|
|
7246
|
-
_meta: params._meta,
|
|
8229
|
+
_meta: params.options?._meta,
|
|
7247
8230
|
cwd: params.cwd,
|
|
7248
|
-
mcpServers: params.mcpServers
|
|
8231
|
+
mcpServers: params.options?.mcpServers
|
|
7249
8232
|
});
|
|
7250
8233
|
if (this.provider.registerSession) {
|
|
7251
8234
|
this.provider.registerSession(response.sessionId, agentId);
|
|
@@ -7258,14 +8241,10 @@ var SessionManager = class {
|
|
|
7258
8241
|
connectionInfo
|
|
7259
8242
|
});
|
|
7260
8243
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
description: m.description ?? void 0
|
|
7266
|
-
}));
|
|
7267
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
7268
|
-
}
|
|
8244
|
+
const availableModels = this.extractAvailableModels(response);
|
|
8245
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
8246
|
+
const responseCwd = response._meta?.["codebuddy.ai"]?.cwd;
|
|
8247
|
+
if (responseCwd) session.setCwd(responseCwd);
|
|
7269
8248
|
this.logger?.info(`Session created: ${response.sessionId}`);
|
|
7270
8249
|
return session;
|
|
7271
8250
|
}
|
|
@@ -7301,17 +8280,31 @@ var SessionManager = class {
|
|
|
7301
8280
|
mcpServers: params.mcpServers
|
|
7302
8281
|
});
|
|
7303
8282
|
session.setModes(response.modes?.availableModes, response.modes?.currentModeId);
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
id: m.modelId,
|
|
7307
|
-
name: m.name,
|
|
7308
|
-
description: m.description ?? void 0
|
|
7309
|
-
}));
|
|
7310
|
-
session.setModels(localModels, response.models?.currentModelId);
|
|
7311
|
-
}
|
|
8283
|
+
const availableModels = this.extractAvailableModels(response);
|
|
8284
|
+
if (availableModels) session.setModels(availableModels, response.models?.currentModelId);
|
|
7312
8285
|
this.logger?.info(`Session loaded: ${params.sessionId}`);
|
|
7313
8286
|
return session;
|
|
7314
8287
|
}
|
|
8288
|
+
/**
|
|
8289
|
+
* 从 ACP response 中提取可用模型列表
|
|
8290
|
+
*
|
|
8291
|
+
* 优先级:
|
|
8292
|
+
* 1. response.models._meta?.['codebuddy.ai']?.availableModels - 包含完整的模型信息(字段名为 'id')
|
|
8293
|
+
* 2. response.models?.availableModels - 只包含基本信息(字段名为 'modelId')
|
|
8294
|
+
* 3. undefined - 都没有时返回 undefined
|
|
8295
|
+
*
|
|
8296
|
+
* @param response - ACP 响应对象
|
|
8297
|
+
* @returns ModelInfo[] | undefined
|
|
8298
|
+
*/
|
|
8299
|
+
extractAvailableModels(response) {
|
|
8300
|
+
const metaModels = (response.models?._meta?.["codebuddy.ai"])?.availableModels;
|
|
8301
|
+
if (metaModels && Array.isArray(metaModels) && metaModels.length > 0) return metaModels;
|
|
8302
|
+
const availableModels = response.models?.availableModels;
|
|
8303
|
+
if (availableModels && Array.isArray(availableModels) && availableModels.length > 0) return availableModels.map((model) => ({
|
|
8304
|
+
...model,
|
|
8305
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
8306
|
+
}));
|
|
8307
|
+
}
|
|
7315
8308
|
};
|
|
7316
8309
|
|
|
7317
8310
|
//#endregion
|
|
@@ -7418,6 +8411,26 @@ var AgentClient = class {
|
|
|
7418
8411
|
throw error;
|
|
7419
8412
|
}
|
|
7420
8413
|
},
|
|
8414
|
+
updateStatus: async (sessionId, status) => {
|
|
8415
|
+
this.logger?.debug("AgentClient.sessions.updateStatus called", {
|
|
8416
|
+
sessionId,
|
|
8417
|
+
status
|
|
8418
|
+
});
|
|
8419
|
+
try {
|
|
8420
|
+
if (this.provider.updateStatus) {
|
|
8421
|
+
const result = await this.provider.updateStatus(sessionId, status);
|
|
8422
|
+
this.logger?.info("Session status updated successfully", {
|
|
8423
|
+
sessionId,
|
|
8424
|
+
status
|
|
8425
|
+
});
|
|
8426
|
+
return result;
|
|
8427
|
+
}
|
|
8428
|
+
throw new Error("Provider does not support updateStatus method");
|
|
8429
|
+
} catch (error) {
|
|
8430
|
+
this.logger?.error("Failed to update session status", error);
|
|
8431
|
+
throw error;
|
|
8432
|
+
}
|
|
8433
|
+
},
|
|
7421
8434
|
move: async (sessionId) => {
|
|
7422
8435
|
this.logger?.debug("AgentClient.sessions.move called", { sessionId });
|
|
7423
8436
|
try {
|
|
@@ -7465,6 +8478,100 @@ var AgentClient = class {
|
|
|
7465
8478
|
return [];
|
|
7466
8479
|
}
|
|
7467
8480
|
},
|
|
8481
|
+
getAutomationSnapshot: async () => {
|
|
8482
|
+
try {
|
|
8483
|
+
if (this.provider?.getAutomationSnapshot) return await this.provider.getAutomationSnapshot();
|
|
8484
|
+
this.logger?.warn("Provider does not support getAutomationSnapshot");
|
|
8485
|
+
} catch (error) {
|
|
8486
|
+
this.logger?.error("Failed to get automation snapshot", error);
|
|
8487
|
+
}
|
|
8488
|
+
return {
|
|
8489
|
+
automations: [],
|
|
8490
|
+
inbox: [],
|
|
8491
|
+
runtimeState: {},
|
|
8492
|
+
updatedAt: Date.now()
|
|
8493
|
+
};
|
|
8494
|
+
},
|
|
8495
|
+
updateAutomation: async (payload) => {
|
|
8496
|
+
try {
|
|
8497
|
+
if (this.provider?.updateAutomation) return await this.provider.updateAutomation(payload);
|
|
8498
|
+
this.logger?.warn("Provider does not support updateAutomation");
|
|
8499
|
+
} catch (error) {
|
|
8500
|
+
this.logger?.error("Failed to update automation", error);
|
|
8501
|
+
return {
|
|
8502
|
+
success: false,
|
|
8503
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8504
|
+
};
|
|
8505
|
+
}
|
|
8506
|
+
return {
|
|
8507
|
+
success: false,
|
|
8508
|
+
message: "Provider does not support updateAutomation"
|
|
8509
|
+
};
|
|
8510
|
+
},
|
|
8511
|
+
deleteAutomation: async (id) => {
|
|
8512
|
+
try {
|
|
8513
|
+
if (this.provider?.deleteAutomation) return await this.provider.deleteAutomation(id);
|
|
8514
|
+
this.logger?.warn("Provider does not support deleteAutomation");
|
|
8515
|
+
} catch (error) {
|
|
8516
|
+
this.logger?.error("Failed to delete automation", error);
|
|
8517
|
+
return {
|
|
8518
|
+
success: false,
|
|
8519
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8520
|
+
};
|
|
8521
|
+
}
|
|
8522
|
+
return {
|
|
8523
|
+
success: false,
|
|
8524
|
+
message: "Provider does not support deleteAutomation"
|
|
8525
|
+
};
|
|
8526
|
+
},
|
|
8527
|
+
archiveAutomationInboxItem: async (itemId) => {
|
|
8528
|
+
try {
|
|
8529
|
+
if (this.provider?.archiveAutomationInboxItem) return await this.provider.archiveAutomationInboxItem(itemId);
|
|
8530
|
+
this.logger?.warn("Provider does not support archiveAutomationInboxItem");
|
|
8531
|
+
} catch (error) {
|
|
8532
|
+
this.logger?.error("Failed to archive automation inbox item", error);
|
|
8533
|
+
return {
|
|
8534
|
+
success: false,
|
|
8535
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8536
|
+
};
|
|
8537
|
+
}
|
|
8538
|
+
return {
|
|
8539
|
+
success: false,
|
|
8540
|
+
message: "Provider does not support archiveAutomationInboxItem"
|
|
8541
|
+
};
|
|
8542
|
+
},
|
|
8543
|
+
deleteAutomationInboxItem: async (itemId) => {
|
|
8544
|
+
try {
|
|
8545
|
+
if (this.provider?.deleteAutomationInboxItem) return await this.provider.deleteAutomationInboxItem(itemId);
|
|
8546
|
+
this.logger?.warn("Provider does not support deleteAutomationInboxItem");
|
|
8547
|
+
} catch (error) {
|
|
8548
|
+
this.logger?.error("Failed to delete automation inbox item", error);
|
|
8549
|
+
return {
|
|
8550
|
+
success: false,
|
|
8551
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8552
|
+
};
|
|
8553
|
+
}
|
|
8554
|
+
return {
|
|
8555
|
+
success: false,
|
|
8556
|
+
message: "Provider does not support deleteAutomationInboxItem"
|
|
8557
|
+
};
|
|
8558
|
+
},
|
|
8559
|
+
testAutomation: async (id) => {
|
|
8560
|
+
try {
|
|
8561
|
+
if (this.provider?.testAutomation) return await this.provider.testAutomation(id);
|
|
8562
|
+
this.logger?.warn("Provider does not support testAutomation");
|
|
8563
|
+
} catch (error) {
|
|
8564
|
+
this.logger?.error("Failed to test automation", error);
|
|
8565
|
+
return {
|
|
8566
|
+
success: false,
|
|
8567
|
+
message: error instanceof Error ? error.message : "Unknown error"
|
|
8568
|
+
};
|
|
8569
|
+
}
|
|
8570
|
+
return {
|
|
8571
|
+
success: false,
|
|
8572
|
+
message: "Provider does not support testAutomation"
|
|
8573
|
+
};
|
|
8574
|
+
},
|
|
7468
8575
|
on: (event, handler) => {
|
|
7469
8576
|
if (this.provider.on) this.provider.on(event, handler);
|
|
7470
8577
|
else this.logger?.warn(`Provider does not support event registration: ${String(event)}`);
|
|
@@ -7516,156 +8623,509 @@ var AgentClient = class {
|
|
|
7516
8623
|
};
|
|
7517
8624
|
}
|
|
7518
8625
|
},
|
|
7519
|
-
pickFolder: async (params) => {
|
|
8626
|
+
pickFolder: async (params) => {
|
|
8627
|
+
try {
|
|
8628
|
+
if (this.provider && this.provider.pickFolder) {
|
|
8629
|
+
const result = await this.provider.pickFolder(params);
|
|
8630
|
+
this.logger?.info("Folder picker completed", {
|
|
8631
|
+
folderPaths: result.folderPaths,
|
|
8632
|
+
canceled: result.canceled
|
|
8633
|
+
});
|
|
8634
|
+
return result;
|
|
8635
|
+
}
|
|
8636
|
+
return {
|
|
8637
|
+
folderPaths: [],
|
|
8638
|
+
canceled: true,
|
|
8639
|
+
error: "Provider does not support pickFolder"
|
|
8640
|
+
};
|
|
8641
|
+
} catch (error) {
|
|
8642
|
+
this.logger?.error("Failed to pick folder", error);
|
|
8643
|
+
return {
|
|
8644
|
+
folderPaths: [],
|
|
8645
|
+
canceled: true,
|
|
8646
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8647
|
+
};
|
|
8648
|
+
}
|
|
8649
|
+
},
|
|
8650
|
+
uploadFile: async (params) => {
|
|
8651
|
+
try {
|
|
8652
|
+
if (this.provider && this.provider.uploadFile) {
|
|
8653
|
+
const result = await this.provider.uploadFile(params);
|
|
8654
|
+
this.logger?.info("File upload completed", {
|
|
8655
|
+
count: params.files.length,
|
|
8656
|
+
success: result.success
|
|
8657
|
+
});
|
|
8658
|
+
return result;
|
|
8659
|
+
}
|
|
8660
|
+
return {
|
|
8661
|
+
success: false,
|
|
8662
|
+
error: "Provider does not support uploadFile"
|
|
8663
|
+
};
|
|
8664
|
+
} catch (error) {
|
|
8665
|
+
this.logger?.error("Failed to upload file", error);
|
|
8666
|
+
return {
|
|
8667
|
+
success: false,
|
|
8668
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8671
|
+
},
|
|
8672
|
+
searchFile: async (params) => {
|
|
8673
|
+
try {
|
|
8674
|
+
if (this.provider && this.provider.searchFile) {
|
|
8675
|
+
const result = await this.provider.searchFile(params);
|
|
8676
|
+
this.logger?.info("File search completed", {
|
|
8677
|
+
resultCount: result.results.length,
|
|
8678
|
+
hasError: !!result.error
|
|
8679
|
+
});
|
|
8680
|
+
return result;
|
|
8681
|
+
}
|
|
8682
|
+
return {
|
|
8683
|
+
results: [],
|
|
8684
|
+
error: "Provider does not support searchFile"
|
|
8685
|
+
};
|
|
8686
|
+
} catch (error) {
|
|
8687
|
+
this.logger?.error("Failed to search file", error);
|
|
8688
|
+
return {
|
|
8689
|
+
results: [],
|
|
8690
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8691
|
+
};
|
|
8692
|
+
}
|
|
8693
|
+
},
|
|
8694
|
+
getSubagentList: async (params) => {
|
|
8695
|
+
try {
|
|
8696
|
+
if (this.provider && this.provider.getSubagentList) {
|
|
8697
|
+
const result = await this.provider.getSubagentList(params);
|
|
8698
|
+
this.logger?.info("Subagent list retrieved", {
|
|
8699
|
+
resultCount: result.results.length,
|
|
8700
|
+
hasError: !!result.error
|
|
8701
|
+
});
|
|
8702
|
+
return result;
|
|
8703
|
+
}
|
|
8704
|
+
return {
|
|
8705
|
+
results: [],
|
|
8706
|
+
error: "Provider does not support getSubagentList"
|
|
8707
|
+
};
|
|
8708
|
+
} catch (error) {
|
|
8709
|
+
this.logger?.error("Failed to get subagent list", error);
|
|
8710
|
+
return {
|
|
8711
|
+
results: [],
|
|
8712
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8713
|
+
};
|
|
8714
|
+
}
|
|
8715
|
+
},
|
|
8716
|
+
getSkillList: async (params) => {
|
|
8717
|
+
try {
|
|
8718
|
+
if (this.provider && this.provider.getSkillList) {
|
|
8719
|
+
const result = await this.provider.getSkillList(params);
|
|
8720
|
+
this.logger?.info("Skill list retrieved", {
|
|
8721
|
+
resultCount: result.results.length,
|
|
8722
|
+
hasError: !!result.error
|
|
8723
|
+
});
|
|
8724
|
+
return result;
|
|
8725
|
+
}
|
|
8726
|
+
return {
|
|
8727
|
+
results: [],
|
|
8728
|
+
error: "Provider does not support getSkillList"
|
|
8729
|
+
};
|
|
8730
|
+
} catch (error) {
|
|
8731
|
+
this.logger?.error("Failed to get skill list", error);
|
|
8732
|
+
return {
|
|
8733
|
+
results: [],
|
|
8734
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8735
|
+
};
|
|
8736
|
+
}
|
|
8737
|
+
},
|
|
8738
|
+
importSkill: async (params) => {
|
|
8739
|
+
try {
|
|
8740
|
+
if (this.provider && this.provider.importSkill) {
|
|
8741
|
+
const result = await this.provider.importSkill(params);
|
|
8742
|
+
this.logger?.info("Import skill completed", {
|
|
8743
|
+
success: result.success,
|
|
8744
|
+
hasError: !!result.error
|
|
8745
|
+
});
|
|
8746
|
+
return result;
|
|
8747
|
+
}
|
|
8748
|
+
return {
|
|
8749
|
+
success: false,
|
|
8750
|
+
error: "Provider does not support importSkill"
|
|
8751
|
+
};
|
|
8752
|
+
} catch (error) {
|
|
8753
|
+
this.logger?.error("Failed to import skill", error);
|
|
8754
|
+
return {
|
|
8755
|
+
success: false,
|
|
8756
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8757
|
+
};
|
|
8758
|
+
}
|
|
8759
|
+
},
|
|
8760
|
+
batchTogglePlugins: async (request) => {
|
|
8761
|
+
try {
|
|
8762
|
+
if (this.provider && this.provider.batchTogglePlugins) {
|
|
8763
|
+
const result = await this.provider.batchTogglePlugins(request);
|
|
8764
|
+
this.logger?.info("Batch toggle plugins completed", {
|
|
8765
|
+
succeededCount: result.succeededPlugins.length,
|
|
8766
|
+
failedCount: result.failedPlugins.length
|
|
8767
|
+
});
|
|
8768
|
+
return result;
|
|
8769
|
+
}
|
|
8770
|
+
return {
|
|
8771
|
+
success: false,
|
|
8772
|
+
succeededPlugins: [],
|
|
8773
|
+
failedPlugins: request.items.map((item) => ({
|
|
8774
|
+
...item,
|
|
8775
|
+
error: "Provider does not support batchTogglePlugins"
|
|
8776
|
+
}))
|
|
8777
|
+
};
|
|
8778
|
+
} catch (error) {
|
|
8779
|
+
this.logger?.error("Failed to batch toggle plugins", error);
|
|
8780
|
+
return {
|
|
8781
|
+
success: false,
|
|
8782
|
+
succeededPlugins: [],
|
|
8783
|
+
failedPlugins: request.items.map((item) => ({
|
|
8784
|
+
...item,
|
|
8785
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8786
|
+
}))
|
|
8787
|
+
};
|
|
8788
|
+
}
|
|
8789
|
+
},
|
|
8790
|
+
getInstalledPlugins: async (forceRefresh) => {
|
|
8791
|
+
try {
|
|
8792
|
+
if (this.provider && "getInstalledPlugins" in this.provider && typeof this.provider.getInstalledPlugins === "function") {
|
|
8793
|
+
const result = await this.provider.getInstalledPlugins(forceRefresh);
|
|
8794
|
+
this.logger?.info("Got installed plugins", { count: result?.length ?? 0 });
|
|
8795
|
+
return result;
|
|
8796
|
+
}
|
|
8797
|
+
this.logger?.warn("Provider does not support getInstalledPlugins");
|
|
8798
|
+
return [];
|
|
8799
|
+
} catch (error) {
|
|
8800
|
+
this.logger?.error("Failed to get installed plugins", error);
|
|
8801
|
+
return [];
|
|
8802
|
+
}
|
|
8803
|
+
},
|
|
8804
|
+
installPlugins: async (pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) => {
|
|
8805
|
+
try {
|
|
8806
|
+
if (this.provider && "installPlugins" in this.provider && typeof this.provider.installPlugins === "function") {
|
|
8807
|
+
const result = await this.provider.installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath);
|
|
8808
|
+
this.logger?.info("Install plugins", {
|
|
8809
|
+
pluginNames,
|
|
8810
|
+
marketplaceName,
|
|
8811
|
+
success: result.success
|
|
8812
|
+
});
|
|
8813
|
+
return result;
|
|
8814
|
+
}
|
|
8815
|
+
this.logger?.warn("Provider does not support installPlugins");
|
|
8816
|
+
return {
|
|
8817
|
+
success: false,
|
|
8818
|
+
error: "Provider does not support installPlugins"
|
|
8819
|
+
};
|
|
8820
|
+
} catch (error) {
|
|
8821
|
+
this.logger?.error("Failed to install plugins", error);
|
|
8822
|
+
return {
|
|
8823
|
+
success: false,
|
|
8824
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8825
|
+
};
|
|
8826
|
+
}
|
|
8827
|
+
},
|
|
8828
|
+
uninstallPlugin: async (pluginName, marketplaceName, scope) => {
|
|
8829
|
+
try {
|
|
8830
|
+
if (this.provider && "uninstallPlugin" in this.provider && typeof this.provider.uninstallPlugin === "function") {
|
|
8831
|
+
const result = await this.provider.uninstallPlugin(pluginName, marketplaceName, scope);
|
|
8832
|
+
this.logger?.info("Uninstall plugin", {
|
|
8833
|
+
pluginName,
|
|
8834
|
+
marketplaceName,
|
|
8835
|
+
scope,
|
|
8836
|
+
success: result.success
|
|
8837
|
+
});
|
|
8838
|
+
return result;
|
|
8839
|
+
}
|
|
8840
|
+
this.logger?.warn("Provider does not support uninstallPlugin");
|
|
8841
|
+
return {
|
|
8842
|
+
success: false,
|
|
8843
|
+
error: "Provider does not support uninstallPlugin"
|
|
8844
|
+
};
|
|
8845
|
+
} catch (error) {
|
|
8846
|
+
this.logger?.error("Failed to uninstall plugin", error);
|
|
8847
|
+
return {
|
|
8848
|
+
success: false,
|
|
8849
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8850
|
+
};
|
|
8851
|
+
}
|
|
8852
|
+
},
|
|
8853
|
+
updatePlugin: async (pluginName, marketplaceName) => {
|
|
8854
|
+
try {
|
|
8855
|
+
if (this.provider && "updatePlugin" in this.provider && typeof this.provider.updatePlugin === "function") {
|
|
8856
|
+
const result = await this.provider.updatePlugin(pluginName, marketplaceName);
|
|
8857
|
+
this.logger?.info("Update plugin", {
|
|
8858
|
+
pluginName,
|
|
8859
|
+
marketplaceName,
|
|
8860
|
+
success: result.success
|
|
8861
|
+
});
|
|
8862
|
+
return result;
|
|
8863
|
+
}
|
|
8864
|
+
this.logger?.warn("Provider does not support updatePlugin");
|
|
8865
|
+
return {
|
|
8866
|
+
success: false,
|
|
8867
|
+
error: "Provider does not support updatePlugin"
|
|
8868
|
+
};
|
|
8869
|
+
} catch (error) {
|
|
8870
|
+
this.logger?.error("Failed to update plugin", error);
|
|
8871
|
+
return {
|
|
8872
|
+
success: false,
|
|
8873
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
8874
|
+
};
|
|
8875
|
+
}
|
|
8876
|
+
},
|
|
8877
|
+
getPluginMarketplaces: async (forceRefresh) => {
|
|
8878
|
+
try {
|
|
8879
|
+
if (this.provider && "getPluginMarketplaces" in this.provider && typeof this.provider.getPluginMarketplaces === "function") {
|
|
8880
|
+
const result = await this.provider.getPluginMarketplaces(forceRefresh);
|
|
8881
|
+
this.logger?.info("Got plugin marketplaces", { count: result?.length ?? 0 });
|
|
8882
|
+
return result;
|
|
8883
|
+
}
|
|
8884
|
+
this.logger?.warn("Provider does not support getPluginMarketplaces");
|
|
8885
|
+
return [];
|
|
8886
|
+
} catch (error) {
|
|
8887
|
+
this.logger?.error("Failed to get plugin marketplaces", error);
|
|
8888
|
+
return [];
|
|
8889
|
+
}
|
|
8890
|
+
},
|
|
8891
|
+
getMarketplacePlugins: async (marketplaceName, forceRefresh, searchText) => {
|
|
8892
|
+
try {
|
|
8893
|
+
if (this.provider && "getMarketplacePlugins" in this.provider && typeof this.provider.getMarketplacePlugins === "function") {
|
|
8894
|
+
const result = await this.provider.getMarketplacePlugins(marketplaceName, forceRefresh, searchText);
|
|
8895
|
+
this.logger?.info("Got marketplace plugins", {
|
|
8896
|
+
marketplaceName,
|
|
8897
|
+
count: result?.length ?? 0
|
|
8898
|
+
});
|
|
8899
|
+
return result;
|
|
8900
|
+
}
|
|
8901
|
+
this.logger?.warn("Provider does not support getMarketplacePlugins");
|
|
8902
|
+
return [];
|
|
8903
|
+
} catch (error) {
|
|
8904
|
+
this.logger?.error("Failed to get marketplace plugins", error);
|
|
8905
|
+
return [];
|
|
8906
|
+
}
|
|
8907
|
+
},
|
|
8908
|
+
getPluginDetail: async (pluginName, marketplaceName) => {
|
|
7520
8909
|
try {
|
|
7521
|
-
if (this.provider && this.provider.
|
|
7522
|
-
const result = await this.provider.
|
|
7523
|
-
this.logger?.info("
|
|
7524
|
-
|
|
7525
|
-
|
|
8910
|
+
if (this.provider && "getPluginDetail" in this.provider && typeof this.provider.getPluginDetail === "function") {
|
|
8911
|
+
const result = await this.provider.getPluginDetail(pluginName, marketplaceName);
|
|
8912
|
+
this.logger?.info("Got plugin detail", {
|
|
8913
|
+
pluginName,
|
|
8914
|
+
marketplaceName
|
|
7526
8915
|
});
|
|
7527
8916
|
return result;
|
|
7528
8917
|
}
|
|
7529
|
-
|
|
7530
|
-
|
|
7531
|
-
canceled: true,
|
|
7532
|
-
error: "Provider does not support pickFolder"
|
|
7533
|
-
};
|
|
8918
|
+
this.logger?.warn("Provider does not support getPluginDetail");
|
|
8919
|
+
return null;
|
|
7534
8920
|
} catch (error) {
|
|
7535
|
-
this.logger?.error("Failed to
|
|
7536
|
-
return
|
|
7537
|
-
folderPaths: [],
|
|
7538
|
-
canceled: true,
|
|
7539
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7540
|
-
};
|
|
8921
|
+
this.logger?.error("Failed to get plugin detail", error);
|
|
8922
|
+
return null;
|
|
7541
8923
|
}
|
|
7542
8924
|
},
|
|
7543
|
-
|
|
8925
|
+
addPluginMarketplace: async (source, name) => {
|
|
7544
8926
|
try {
|
|
7545
|
-
if (this.provider && this.provider.
|
|
7546
|
-
const result = await this.provider.
|
|
7547
|
-
this.logger?.info("
|
|
7548
|
-
|
|
8927
|
+
if (this.provider && "addPluginMarketplace" in this.provider && typeof this.provider.addPluginMarketplace === "function") {
|
|
8928
|
+
const result = await this.provider.addPluginMarketplace(source, name);
|
|
8929
|
+
this.logger?.info("Add plugin marketplace", {
|
|
8930
|
+
source,
|
|
8931
|
+
name,
|
|
7549
8932
|
success: result.success
|
|
7550
8933
|
});
|
|
7551
8934
|
return result;
|
|
7552
8935
|
}
|
|
8936
|
+
this.logger?.warn("Provider does not support addPluginMarketplace");
|
|
7553
8937
|
return {
|
|
7554
8938
|
success: false,
|
|
7555
|
-
error: "Provider does not support
|
|
8939
|
+
error: "Provider does not support addPluginMarketplace"
|
|
7556
8940
|
};
|
|
7557
8941
|
} catch (error) {
|
|
7558
|
-
this.logger?.error("Failed to
|
|
8942
|
+
this.logger?.error("Failed to add plugin marketplace", error);
|
|
7559
8943
|
return {
|
|
7560
8944
|
success: false,
|
|
7561
8945
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
7562
8946
|
};
|
|
7563
8947
|
}
|
|
7564
8948
|
},
|
|
7565
|
-
|
|
8949
|
+
removePluginMarketplace: async (marketplaceName) => {
|
|
7566
8950
|
try {
|
|
7567
|
-
if (this.provider && this.provider.
|
|
7568
|
-
const result = await this.provider.
|
|
7569
|
-
this.logger?.info("
|
|
7570
|
-
|
|
7571
|
-
|
|
8951
|
+
if (this.provider && "removePluginMarketplace" in this.provider && typeof this.provider.removePluginMarketplace === "function") {
|
|
8952
|
+
const result = await this.provider.removePluginMarketplace(marketplaceName);
|
|
8953
|
+
this.logger?.info("Remove plugin marketplace", {
|
|
8954
|
+
marketplaceName,
|
|
8955
|
+
success: result.success
|
|
7572
8956
|
});
|
|
7573
8957
|
return result;
|
|
7574
8958
|
}
|
|
8959
|
+
this.logger?.warn("Provider does not support removePluginMarketplace");
|
|
7575
8960
|
return {
|
|
7576
|
-
|
|
7577
|
-
error: "Provider does not support
|
|
8961
|
+
success: false,
|
|
8962
|
+
error: "Provider does not support removePluginMarketplace"
|
|
7578
8963
|
};
|
|
7579
8964
|
} catch (error) {
|
|
7580
|
-
this.logger?.error("Failed to
|
|
8965
|
+
this.logger?.error("Failed to remove plugin marketplace", error);
|
|
7581
8966
|
return {
|
|
7582
|
-
|
|
8967
|
+
success: false,
|
|
7583
8968
|
error: error instanceof Error ? error.message : "Unknown error"
|
|
7584
8969
|
};
|
|
7585
8970
|
}
|
|
7586
8971
|
},
|
|
7587
|
-
|
|
8972
|
+
refreshPluginMarketplace: async (marketplaceName) => {
|
|
7588
8973
|
try {
|
|
7589
|
-
if (this.provider && this.provider.
|
|
7590
|
-
const result = await this.provider.
|
|
7591
|
-
this.logger?.info("
|
|
7592
|
-
|
|
7593
|
-
|
|
8974
|
+
if (this.provider && "refreshPluginMarketplace" in this.provider && typeof this.provider.refreshPluginMarketplace === "function") {
|
|
8975
|
+
const result = await this.provider.refreshPluginMarketplace(marketplaceName);
|
|
8976
|
+
this.logger?.info("Refresh plugin marketplace", {
|
|
8977
|
+
marketplaceName,
|
|
8978
|
+
success: result.success
|
|
7594
8979
|
});
|
|
7595
8980
|
return result;
|
|
7596
8981
|
}
|
|
8982
|
+
this.logger?.warn("Provider does not support refreshPluginMarketplace");
|
|
7597
8983
|
return {
|
|
7598
8984
|
success: false,
|
|
7599
|
-
|
|
7600
|
-
failedPlugins: request.items.map((item) => ({
|
|
7601
|
-
...item,
|
|
7602
|
-
error: "Provider does not support batchTogglePlugins"
|
|
7603
|
-
}))
|
|
8985
|
+
error: "Provider does not support refreshPluginMarketplace"
|
|
7604
8986
|
};
|
|
7605
8987
|
} catch (error) {
|
|
7606
|
-
this.logger?.error("Failed to
|
|
8988
|
+
this.logger?.error("Failed to refresh plugin marketplace", error);
|
|
7607
8989
|
return {
|
|
7608
8990
|
success: false,
|
|
7609
|
-
|
|
7610
|
-
failedPlugins: request.items.map((item) => ({
|
|
7611
|
-
...item,
|
|
7612
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7613
|
-
}))
|
|
8991
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
7614
8992
|
};
|
|
7615
8993
|
}
|
|
7616
8994
|
},
|
|
7617
|
-
|
|
8995
|
+
openFolderInNewWindow: async (folderPath) => {
|
|
7618
8996
|
try {
|
|
7619
|
-
if (this.provider && "
|
|
7620
|
-
|
|
7621
|
-
this.logger?.info("
|
|
7622
|
-
|
|
8997
|
+
if (this.provider && "openFolderInNewWindow" in this.provider && typeof this.provider.openFolderInNewWindow === "function") {
|
|
8998
|
+
await this.provider.openFolderInNewWindow(folderPath);
|
|
8999
|
+
this.logger?.info("Opened folder in new window", { folderPath });
|
|
9000
|
+
} else {
|
|
9001
|
+
this.logger?.warn("Provider does not support openFolderInNewWindow");
|
|
9002
|
+
throw new Error("Provider does not support openFolderInNewWindow");
|
|
7623
9003
|
}
|
|
7624
|
-
|
|
9004
|
+
} catch (error) {
|
|
9005
|
+
this.logger?.error("Failed to open folder in new window", error);
|
|
9006
|
+
throw error;
|
|
9007
|
+
}
|
|
9008
|
+
},
|
|
9009
|
+
getSupportScenes: async (locale) => {
|
|
9010
|
+
try {
|
|
9011
|
+
if (this.provider && "getSupportScenes" in this.provider && typeof this.provider.getSupportScenes === "function") return await this.provider.getSupportScenes(locale);
|
|
9012
|
+
this.logger?.warn("Provider does not support getSupportScenes");
|
|
7625
9013
|
return [];
|
|
7626
9014
|
} catch (error) {
|
|
7627
|
-
this.logger?.error("Failed to get
|
|
9015
|
+
this.logger?.error("Failed to get support scenes", error);
|
|
7628
9016
|
return [];
|
|
7629
9017
|
}
|
|
7630
9018
|
},
|
|
7631
|
-
|
|
9019
|
+
getProductScenes: async (locale) => {
|
|
7632
9020
|
try {
|
|
7633
|
-
if (this.provider
|
|
7634
|
-
const result = await this.provider.
|
|
7635
|
-
this.logger?.info("
|
|
7636
|
-
pluginNames,
|
|
7637
|
-
marketplaceName,
|
|
7638
|
-
success: result.success
|
|
7639
|
-
});
|
|
9021
|
+
if (this.provider?.getProductScenes) {
|
|
9022
|
+
const result = await this.provider.getProductScenes(locale);
|
|
9023
|
+
this.logger?.info("Got product scenes", { count: result?.length ?? 0 });
|
|
7640
9024
|
return result;
|
|
7641
9025
|
}
|
|
7642
|
-
this.logger?.warn("Provider does not support
|
|
7643
|
-
return
|
|
7644
|
-
success: false,
|
|
7645
|
-
error: "Provider does not support installPlugins"
|
|
7646
|
-
};
|
|
9026
|
+
this.logger?.warn("Provider does not support getProductScenes");
|
|
9027
|
+
return [];
|
|
7647
9028
|
} catch (error) {
|
|
7648
|
-
this.logger?.error("Failed to
|
|
7649
|
-
return
|
|
7650
|
-
success: false,
|
|
7651
|
-
error: error instanceof Error ? error.message : "Unknown error"
|
|
7652
|
-
};
|
|
9029
|
+
this.logger?.error("Failed to get product scenes", error);
|
|
9030
|
+
return [];
|
|
7653
9031
|
}
|
|
7654
9032
|
},
|
|
7655
|
-
|
|
9033
|
+
getAvailableCommands: async (params) => {
|
|
7656
9034
|
try {
|
|
7657
|
-
if (this.provider && "
|
|
7658
|
-
const result = await this.provider.
|
|
7659
|
-
this.logger?.info("Got
|
|
9035
|
+
if (this.provider && "getAvailableCommands" in this.provider && typeof this.provider.getAvailableCommands === "function") {
|
|
9036
|
+
const result = await this.provider.getAvailableCommands(params);
|
|
9037
|
+
this.logger?.info("Got available commands from provider", {
|
|
9038
|
+
sessionId: params?.sessionId ?? "(default)",
|
|
9039
|
+
count: result?.length ?? 0
|
|
9040
|
+
});
|
|
7660
9041
|
return result;
|
|
7661
9042
|
}
|
|
7662
|
-
this.logger?.warn("Provider does not support
|
|
9043
|
+
this.logger?.warn("Provider does not support getAvailableCommands", { params });
|
|
7663
9044
|
return [];
|
|
7664
9045
|
} catch (error) {
|
|
7665
|
-
this.logger?.error("Failed to get
|
|
9046
|
+
this.logger?.error("Failed to get available commands", error);
|
|
7666
9047
|
return [];
|
|
7667
9048
|
}
|
|
7668
9049
|
},
|
|
9050
|
+
reportTelemetry: async (eventName, payload) => {
|
|
9051
|
+
try {
|
|
9052
|
+
if (this.provider?.reportTelemetry) await this.provider.reportTelemetry(eventName, payload);
|
|
9053
|
+
else this.logger?.warn("Provider does not support reportTelemetry");
|
|
9054
|
+
} catch (error) {
|
|
9055
|
+
this.logger?.error("Failed to report telemetry", error);
|
|
9056
|
+
}
|
|
9057
|
+
},
|
|
9058
|
+
getProductConfiguration: async () => {
|
|
9059
|
+
try {
|
|
9060
|
+
if (this.provider?.getProductConfiguration) return await this.provider.getProductConfiguration();
|
|
9061
|
+
this.logger?.warn("Provider does not support getProductConfiguration");
|
|
9062
|
+
return {};
|
|
9063
|
+
} catch (error) {
|
|
9064
|
+
this.logger?.error("Failed to get product configuration", error);
|
|
9065
|
+
return {};
|
|
9066
|
+
}
|
|
9067
|
+
},
|
|
9068
|
+
getUserInfo: async () => {
|
|
9069
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() called");
|
|
9070
|
+
try {
|
|
9071
|
+
if (this.provider?.getUserInfo) {
|
|
9072
|
+
const result = await this.provider.getUserInfo();
|
|
9073
|
+
this.logger?.info("[AgentClient.sessions] getUserInfo() result:", JSON.stringify(result));
|
|
9074
|
+
return result;
|
|
9075
|
+
}
|
|
9076
|
+
this.logger?.warn("Provider does not support getUserInfo");
|
|
9077
|
+
return {};
|
|
9078
|
+
} catch (error) {
|
|
9079
|
+
this.logger?.error("Failed to get user info", error);
|
|
9080
|
+
return {};
|
|
9081
|
+
}
|
|
9082
|
+
},
|
|
9083
|
+
respondToSampling: async (sessionId, response) => {
|
|
9084
|
+
try {
|
|
9085
|
+
if (this.provider?.respondToSampling) {
|
|
9086
|
+
await this.provider.respondToSampling(sessionId, response);
|
|
9087
|
+
this.logger?.info("Responded to sampling request", {
|
|
9088
|
+
sessionId,
|
|
9089
|
+
requestId: response.id,
|
|
9090
|
+
approved: response.approved
|
|
9091
|
+
});
|
|
9092
|
+
} else this.logger?.warn("Provider does not support respondToSampling");
|
|
9093
|
+
} catch (error) {
|
|
9094
|
+
this.logger?.error("Failed to respond to sampling request", error);
|
|
9095
|
+
throw error;
|
|
9096
|
+
}
|
|
9097
|
+
},
|
|
9098
|
+
respondToRoots: async (sessionId, response) => {
|
|
9099
|
+
try {
|
|
9100
|
+
if (this.provider?.respondToRoots) {
|
|
9101
|
+
await this.provider.respondToRoots(sessionId, response);
|
|
9102
|
+
this.logger?.info("Responded to roots request", {
|
|
9103
|
+
sessionId,
|
|
9104
|
+
requestId: response.id,
|
|
9105
|
+
approved: response.approved
|
|
9106
|
+
});
|
|
9107
|
+
} else this.logger?.warn("Provider does not support respondToRoots");
|
|
9108
|
+
} catch (error) {
|
|
9109
|
+
this.logger?.error("Failed to respond to roots request", error);
|
|
9110
|
+
throw error;
|
|
9111
|
+
}
|
|
9112
|
+
},
|
|
9113
|
+
subscribeSamplingRequests: (serverName, callback) => {
|
|
9114
|
+
if (this.provider?.subscribeSamplingRequests) {
|
|
9115
|
+
this.logger?.info("Subscribing to sampling requests", { serverName });
|
|
9116
|
+
return this.provider.subscribeSamplingRequests(serverName, callback);
|
|
9117
|
+
}
|
|
9118
|
+
this.logger?.warn("Provider does not support subscribeSamplingRequests");
|
|
9119
|
+
return () => {};
|
|
9120
|
+
},
|
|
9121
|
+
subscribeRootsRequests: (serverName, callback) => {
|
|
9122
|
+
if (this.provider?.subscribeRootsRequests) {
|
|
9123
|
+
this.logger?.info("Subscribing to roots requests", { serverName });
|
|
9124
|
+
return this.provider.subscribeRootsRequests(serverName, callback);
|
|
9125
|
+
}
|
|
9126
|
+
this.logger?.warn("Provider does not support subscribeRootsRequests");
|
|
9127
|
+
return () => {};
|
|
9128
|
+
},
|
|
7669
9129
|
models: this.createModelsResource()
|
|
7670
9130
|
};
|
|
7671
9131
|
}
|
|
@@ -7732,6 +9192,154 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
7732
9192
|
return AccountStatus;
|
|
7733
9193
|
}({});
|
|
7734
9194
|
|
|
9195
|
+
//#endregion
|
|
9196
|
+
//#region ../agent-provider/src/backend/service/oauth-repository-service.ts
|
|
9197
|
+
/**
|
|
9198
|
+
* OAuth Repository Service
|
|
9199
|
+
*
|
|
9200
|
+
* 封装 OAuth 连接器相关的仓库和分支操作
|
|
9201
|
+
*/
|
|
9202
|
+
/**
|
|
9203
|
+
* OAuth Repository Service
|
|
9204
|
+
*
|
|
9205
|
+
* 提供仓库和分支的查询操作
|
|
9206
|
+
*/
|
|
9207
|
+
var OAuthRepositoryService = class {
|
|
9208
|
+
/**
|
|
9209
|
+
* 获取仓库分支列表
|
|
9210
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
9211
|
+
*
|
|
9212
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9213
|
+
* @param params 平台特定的查询参数
|
|
9214
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9215
|
+
* @param perPage 每页数量,最大100
|
|
9216
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
9217
|
+
*
|
|
9218
|
+
* @example
|
|
9219
|
+
* ```typescript
|
|
9220
|
+
* // GitHub
|
|
9221
|
+
* const branches = await service.getBranches('github', {
|
|
9222
|
+
* owner: 'CodeBuddy-Official-Account',
|
|
9223
|
+
* repo: 'CodeBuddyIDE'
|
|
9224
|
+
* });
|
|
9225
|
+
*
|
|
9226
|
+
* // Gongfeng
|
|
9227
|
+
* const branches = await service.getBranches('gongfeng', {
|
|
9228
|
+
* project_id: '1611499'
|
|
9229
|
+
* });
|
|
9230
|
+
*
|
|
9231
|
+
* // CNB
|
|
9232
|
+
* const branches = await service.getBranches('cnb', {
|
|
9233
|
+
* repo: 'genie/genie-ide'
|
|
9234
|
+
* });
|
|
9235
|
+
* ```
|
|
9236
|
+
*/
|
|
9237
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
9238
|
+
try {
|
|
9239
|
+
const url = `/console/as/connector/oauth/${connector}/branches?${this.buildBranchQueryParams(connector, params, page, perPage).toString()}`;
|
|
9240
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
9241
|
+
const apiResponse = await httpService.get(url);
|
|
9242
|
+
if (!apiResponse.data) {
|
|
9243
|
+
console.warn(`[OAuthRepositoryService] No data in branches response for ${connector}`);
|
|
9244
|
+
return [];
|
|
9245
|
+
}
|
|
9246
|
+
const branches = apiResponse.data.branches || [];
|
|
9247
|
+
console.log(`[OAuthRepositoryService] Retrieved ${branches.length} branches from ${connector}`);
|
|
9248
|
+
return branches;
|
|
9249
|
+
} catch (error) {
|
|
9250
|
+
console.error(`[OAuthRepositoryService] Failed to get branches from ${connector}:`, error);
|
|
9251
|
+
throw error;
|
|
9252
|
+
}
|
|
9253
|
+
}
|
|
9254
|
+
/**
|
|
9255
|
+
* 获取仓库列表
|
|
9256
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
9257
|
+
*
|
|
9258
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
9259
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
9260
|
+
*
|
|
9261
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9262
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9263
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
9264
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
9265
|
+
* @param perPage 每页数量,最大100
|
|
9266
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
9267
|
+
*
|
|
9268
|
+
* @example
|
|
9269
|
+
* ```typescript
|
|
9270
|
+
* // GitHub - 必须传 page=0 获取全量数据
|
|
9271
|
+
* const response = await service.getRepositories('github', 0, 100);
|
|
9272
|
+
* // response.github_repos 是 map: installation_id => repo[]
|
|
9273
|
+
*
|
|
9274
|
+
* // Gongfeng
|
|
9275
|
+
* const response = await service.getRepositories('gongfeng', 0, 100);
|
|
9276
|
+
* // response.gongfeng_repos 是数组
|
|
9277
|
+
*
|
|
9278
|
+
* // CNB
|
|
9279
|
+
* const response = await service.getRepositories('cnb', 0, 100);
|
|
9280
|
+
* // response.cnb_repos 是数组
|
|
9281
|
+
* ```
|
|
9282
|
+
*/
|
|
9283
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
9284
|
+
try {
|
|
9285
|
+
const queryParams = new URLSearchParams();
|
|
9286
|
+
queryParams.append("page", String(page));
|
|
9287
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
9288
|
+
const url = `/console/as/connector/oauth/${connector}/repos?${queryParams.toString()}`;
|
|
9289
|
+
console.log(`[OAuthRepositoryService] GET ${url}`);
|
|
9290
|
+
const apiResponse = await httpService.get(url);
|
|
9291
|
+
if (!apiResponse.data) {
|
|
9292
|
+
console.warn(`[OAuthRepositoryService] No data in repos response for ${connector}`);
|
|
9293
|
+
return {};
|
|
9294
|
+
}
|
|
9295
|
+
const response = apiResponse.data;
|
|
9296
|
+
this.logRepositoryCounts(response);
|
|
9297
|
+
return response;
|
|
9298
|
+
} catch (error) {
|
|
9299
|
+
console.error(`[OAuthRepositoryService] Failed to get repos from ${connector}:`, error);
|
|
9300
|
+
throw error;
|
|
9301
|
+
}
|
|
9302
|
+
}
|
|
9303
|
+
/**
|
|
9304
|
+
* 构建分支查询参数
|
|
9305
|
+
*/
|
|
9306
|
+
buildBranchQueryParams(connector, params, page, perPage) {
|
|
9307
|
+
const queryParams = new URLSearchParams();
|
|
9308
|
+
queryParams.append("page", String(page));
|
|
9309
|
+
queryParams.append("per_page", String(Math.min(perPage, 100)));
|
|
9310
|
+
if (connector === "github") {
|
|
9311
|
+
const githubParams = params;
|
|
9312
|
+
if (!githubParams.owner || !githubParams.repo) throw new Error("GitHub requires owner and repo parameters");
|
|
9313
|
+
queryParams.append("owner", githubParams.owner);
|
|
9314
|
+
queryParams.append("repo", githubParams.repo);
|
|
9315
|
+
} else if (connector === "gongfeng") {
|
|
9316
|
+
const gongfengParams = params;
|
|
9317
|
+
if (!gongfengParams.project_id) throw new Error("Gongfeng requires project_id parameter");
|
|
9318
|
+
queryParams.append("project_id", gongfengParams.project_id);
|
|
9319
|
+
} else if (connector === "cnb") {
|
|
9320
|
+
const cnbParams = params;
|
|
9321
|
+
if (!cnbParams.repo) throw new Error("CNB requires repo parameter");
|
|
9322
|
+
queryParams.append("repo", cnbParams.repo);
|
|
9323
|
+
} else throw new Error(`Unknown connector: ${connector}`);
|
|
9324
|
+
return queryParams;
|
|
9325
|
+
}
|
|
9326
|
+
/**
|
|
9327
|
+
* 记录仓库数量日志
|
|
9328
|
+
*/
|
|
9329
|
+
logRepositoryCounts(response) {
|
|
9330
|
+
if (response.github_repos) {
|
|
9331
|
+
const totalCount = Object.values(response.github_repos).reduce((sum, repos) => sum + repos.length, 0);
|
|
9332
|
+
console.log(`[OAuthRepositoryService] Retrieved ${totalCount} GitHub repos across ${Object.keys(response.github_repos).length} installations`);
|
|
9333
|
+
}
|
|
9334
|
+
if (response.gongfeng_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.gongfeng_repos.length} Gongfeng repos`);
|
|
9335
|
+
if (response.cnb_repos) console.log(`[OAuthRepositoryService] Retrieved ${response.cnb_repos.length} CNB repos`);
|
|
9336
|
+
}
|
|
9337
|
+
};
|
|
9338
|
+
/**
|
|
9339
|
+
* OAuth Repository Service 单例实例
|
|
9340
|
+
*/
|
|
9341
|
+
const oauthRepositoryService = new OAuthRepositoryService();
|
|
9342
|
+
|
|
7735
9343
|
//#endregion
|
|
7736
9344
|
//#region ../agent-provider/src/backend/backend-provider.ts
|
|
7737
9345
|
/**
|
|
@@ -7740,28 +9348,24 @@ let AccountStatus = /* @__PURE__ */ function(AccountStatus) {
|
|
|
7740
9348
|
* 封装与后端 API 的 HTTP 通信
|
|
7741
9349
|
*/
|
|
7742
9350
|
/**
|
|
7743
|
-
*
|
|
7744
|
-
*
|
|
9351
|
+
* 判断当前账号是否是 SSO 账号
|
|
9352
|
+
* 通过 account.accountType === 'sso' 来判断,这种不行,因为未登录之前account 为空
|
|
7745
9353
|
*/
|
|
7746
|
-
const
|
|
7747
|
-
|
|
7748
|
-
|
|
9354
|
+
const safeParseJSON = (jsonString) => {
|
|
9355
|
+
try {
|
|
9356
|
+
return JSON.parse(jsonString);
|
|
9357
|
+
} catch (error) {
|
|
9358
|
+
return {};
|
|
9359
|
+
}
|
|
7749
9360
|
};
|
|
7750
9361
|
/**
|
|
7751
|
-
*
|
|
7752
|
-
* - SSO
|
|
7753
|
-
* - 非 SSO
|
|
9362
|
+
* 根据路径获取完整 URL
|
|
9363
|
+
* - SSO 账号需要跳转到对应的预发/生产域名
|
|
9364
|
+
* - 非 SSO 账号直接使用当前域名
|
|
9365
|
+
* @param path 路径,如 '/login'、'/logout'、'/home' 等
|
|
9366
|
+
* @returns 完整的 URL 地址
|
|
7754
9367
|
*/
|
|
7755
|
-
const
|
|
7756
|
-
const { hostname, protocol } = window.location;
|
|
7757
|
-
if (isSSODomain()) {
|
|
7758
|
-
const isCodebuddy = hostname.includes("codebuddy.cn");
|
|
7759
|
-
const isStaging = hostname.includes("staging");
|
|
7760
|
-
if (isCodebuddy) return isStaging ? `${protocol}//staging.codebuddy.cn/login` : `${protocol}//www.codebuddy.cn/login`;
|
|
7761
|
-
else return isStaging ? `${protocol}//staging-copilot.tencent.com/login` : `${protocol}//copilot.tencent.com/login`;
|
|
7762
|
-
}
|
|
7763
|
-
return `${window.location.origin}/login`;
|
|
7764
|
-
};
|
|
9368
|
+
const getFullUrl = (path) => `${window.location.origin}${path}`;
|
|
7765
9369
|
/** 获取当前域名的账号选择页面 URL */
|
|
7766
9370
|
const getSelectAccountUrl = () => `${window.location.origin}/login/select`;
|
|
7767
9371
|
/** localStorage 中存储选中账号 ID 的 key */
|
|
@@ -7798,12 +9402,30 @@ var BackendProvider = class {
|
|
|
7798
9402
|
constructor(config) {
|
|
7799
9403
|
httpService.setBaseURL(config.baseUrl);
|
|
7800
9404
|
if (config.authToken) httpService.setAuthToken(config.authToken);
|
|
7801
|
-
httpService.onUnauthorized(() =>
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
9405
|
+
httpService.onUnauthorized(() => this.handleUnauthorized());
|
|
9406
|
+
}
|
|
9407
|
+
/**
|
|
9408
|
+
* 处理 401 未授权错误
|
|
9409
|
+
* 先尝试刷新 token,失败后再执行登出流程
|
|
9410
|
+
*
|
|
9411
|
+
* @throws 如果 token 刷新失败,抛出错误通知 HttpService 不要重试
|
|
9412
|
+
*/
|
|
9413
|
+
async handleUnauthorized() {
|
|
9414
|
+
console.log("[BackendProvider] User unauthorized (401), attempting token refresh first");
|
|
9415
|
+
try {
|
|
9416
|
+
if (await this.refreshToken()) {
|
|
9417
|
+
console.log("[BackendProvider] Token refresh successful after 401, user still logged in");
|
|
9418
|
+
return;
|
|
9419
|
+
}
|
|
9420
|
+
throw new Error("Token refresh returned null");
|
|
9421
|
+
} catch (error) {
|
|
9422
|
+
console.error("[BackendProvider] Token refresh failed after 401:", error);
|
|
9423
|
+
console.log("[BackendProvider] Token refresh failed, triggering logout");
|
|
9424
|
+
this.logout().catch((logoutError) => {
|
|
9425
|
+
console.error("[BackendProvider] Logout failed in 401 handler:", logoutError);
|
|
7805
9426
|
});
|
|
7806
|
-
|
|
9427
|
+
throw error;
|
|
9428
|
+
}
|
|
7807
9429
|
}
|
|
7808
9430
|
/**
|
|
7809
9431
|
* 获取当前账号信息
|
|
@@ -7848,7 +9470,7 @@ var BackendProvider = class {
|
|
|
7848
9470
|
return account;
|
|
7849
9471
|
}
|
|
7850
9472
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
7851
|
-
window.location.href = `${getSelectAccountUrl()}?platform=
|
|
9473
|
+
window.location.href = `${getSelectAccountUrl()}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
7852
9474
|
accountService.setAccount(null);
|
|
7853
9475
|
return null;
|
|
7854
9476
|
} catch (error) {
|
|
@@ -7871,7 +9493,8 @@ var BackendProvider = class {
|
|
|
7871
9493
|
activeStatus: connector.active_status,
|
|
7872
9494
|
displayName: connector.display_name,
|
|
7873
9495
|
oauthClientId: connector.oauth_client_id,
|
|
7874
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
9496
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
9497
|
+
oauthAppName: connector.oauth_app_name
|
|
7875
9498
|
})) };
|
|
7876
9499
|
}
|
|
7877
9500
|
throw result;
|
|
@@ -7953,7 +9576,8 @@ var BackendProvider = class {
|
|
|
7953
9576
|
connectStatus: connector.connect_status,
|
|
7954
9577
|
displayName: connector.display_name,
|
|
7955
9578
|
oauthClientId: connector.oauth_client_id,
|
|
7956
|
-
oauthRedirectUrl: connector.oauth_redirect_url
|
|
9579
|
+
oauthRedirectUrl: connector.oauth_redirect_url,
|
|
9580
|
+
oauthAppName: connector.oauth_app_name
|
|
7957
9581
|
})) };
|
|
7958
9582
|
}
|
|
7959
9583
|
throw result;
|
|
@@ -8044,6 +9668,9 @@ var BackendProvider = class {
|
|
|
8044
9668
|
PackageCode: void 0,
|
|
8045
9669
|
name: ""
|
|
8046
9670
|
};
|
|
9671
|
+
const productFeatures = typeof window !== "undefined" && window.PRODUCT_FEATURES ? safeParseJSON(window.PRODUCT_FEATURES) : {};
|
|
9672
|
+
console.log("[PRODUCT_FEATURES]", productFeatures);
|
|
9673
|
+
if (!productFeatures.billing) return defaultPlan;
|
|
8047
9674
|
try {
|
|
8048
9675
|
const now = /* @__PURE__ */ new Date();
|
|
8049
9676
|
const futureDate = new Date(now.getTime() + 101 * 365 * 24 * 60 * 60 * 1e3);
|
|
@@ -8065,10 +9692,11 @@ var BackendProvider = class {
|
|
|
8065
9692
|
if (!time) return 0;
|
|
8066
9693
|
return new Date(time).getTime();
|
|
8067
9694
|
};
|
|
8068
|
-
const dailyCredits = [CommodityCode.free
|
|
9695
|
+
const dailyCredits = [CommodityCode.free];
|
|
8069
9696
|
const planResources = resources.map((r) => {
|
|
8070
9697
|
const isDaily = dailyCredits.includes(r.PackageCode);
|
|
8071
9698
|
const endTime = isDaily ? r.CycleEndTime : r.DeductionEndTime;
|
|
9699
|
+
const refreshAt = parseTime(r.CycleEndTime) + 1e3;
|
|
8072
9700
|
return {
|
|
8073
9701
|
id: r.ResourceId,
|
|
8074
9702
|
name: isDaily ? "plan.addonCredits" : getPackageName(r.PackageCode),
|
|
@@ -8078,7 +9706,7 @@ var BackendProvider = class {
|
|
|
8078
9706
|
used: Math.max(0, Number(r.CycleCapacitySizePrecise) - Number(r.CycleCapacityRemainPrecise)) || 0,
|
|
8079
9707
|
left: Number(r.CycleCapacityRemainPrecise) || 0,
|
|
8080
9708
|
expireAt: parseTime(endTime),
|
|
8081
|
-
refreshAt: isDaily ? void 0 :
|
|
9709
|
+
refreshAt: isDaily ? void 0 : refreshAt
|
|
8082
9710
|
};
|
|
8083
9711
|
}).sort((a, b) => {
|
|
8084
9712
|
const getPriority = (code) => {
|
|
@@ -8086,10 +9714,11 @@ var BackendProvider = class {
|
|
|
8086
9714
|
CommodityCode.proMon,
|
|
8087
9715
|
CommodityCode.proMonPlus,
|
|
8088
9716
|
CommodityCode.proYear,
|
|
9717
|
+
CommodityCode.freeMon,
|
|
8089
9718
|
CommodityCode.extra
|
|
8090
9719
|
].includes(code)) return 1;
|
|
8091
9720
|
if ([CommodityCode.gift, CommodityCode.activity].includes(code)) return 2;
|
|
8092
|
-
if ([CommodityCode.free
|
|
9721
|
+
if ([CommodityCode.free].includes(code)) return 3;
|
|
8093
9722
|
return 4;
|
|
8094
9723
|
};
|
|
8095
9724
|
return getPriority(a.packageCode) - getPriority(b.packageCode);
|
|
@@ -8210,38 +9839,69 @@ var BackendProvider = class {
|
|
|
8210
9839
|
*/
|
|
8211
9840
|
async login() {
|
|
8212
9841
|
const redirectUrl = encodeURIComponent(window.location.href);
|
|
8213
|
-
window.location.href = `${
|
|
9842
|
+
window.location.href = `${getFullUrl("/login")}?platform=agents&state=0&redirect_uri=${redirectUrl}`;
|
|
8214
9843
|
}
|
|
8215
9844
|
/**
|
|
8216
9845
|
* 登出账号
|
|
8217
|
-
*
|
|
9846
|
+
*
|
|
9847
|
+
* 策略:
|
|
9848
|
+
* - IOA 企业:用 iframe 走 SSO/SAML SLO 登出链路(涉及跨域重定向),通过轮询 iframe URL 变化检测完成
|
|
9849
|
+
* - 非 IOA 企业:直接用 httpService 请求 /console/logout,速度快
|
|
8218
9850
|
*/
|
|
8219
9851
|
async logout() {
|
|
8220
|
-
const
|
|
9852
|
+
const account = accountService.getAccount();
|
|
9853
|
+
if (account?.enterpriseId && ["esoikz80kd8g", "etahzsqej0n4"].includes(account.enterpriseId)) await this.logoutViaIframe();
|
|
9854
|
+
else await this.logoutViaHttp();
|
|
9855
|
+
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
9856
|
+
accountService.clearAccount();
|
|
9857
|
+
}
|
|
9858
|
+
/**
|
|
9859
|
+
* IOA 企业登出:通过 iframe 走 SSO/SAML SLO 登出链路
|
|
9860
|
+
* 轮询 iframe URL 变化检测完成,兜底超时 5 秒
|
|
9861
|
+
*/
|
|
9862
|
+
async logoutViaIframe() {
|
|
9863
|
+
const logoutUrl = `${httpService.getAxiosInstance().defaults.baseURL}/console/logout`;
|
|
8221
9864
|
try {
|
|
8222
9865
|
await new Promise((resolve) => {
|
|
8223
9866
|
const iframe = document.createElement("iframe");
|
|
8224
9867
|
iframe.style.cssText = "position:fixed;top:-9999px;left:-9999px;width:1px;height:1px;border:none;";
|
|
8225
|
-
iframe.src =
|
|
8226
|
-
|
|
8227
|
-
|
|
8228
|
-
|
|
8229
|
-
|
|
8230
|
-
|
|
9868
|
+
iframe.src = logoutUrl;
|
|
9869
|
+
let pollTimer;
|
|
9870
|
+
let settled = false;
|
|
9871
|
+
const done = () => {
|
|
9872
|
+
if (settled) return;
|
|
9873
|
+
settled = true;
|
|
9874
|
+
clearInterval(pollTimer);
|
|
8231
9875
|
clearTimeout(timeout);
|
|
8232
9876
|
if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
|
|
8233
|
-
};
|
|
8234
|
-
iframe.onerror = () => {
|
|
8235
|
-
cleanup();
|
|
8236
9877
|
resolve();
|
|
8237
9878
|
};
|
|
9879
|
+
let wasRedirecting = false;
|
|
9880
|
+
pollTimer = setInterval(() => {
|
|
9881
|
+
try {
|
|
9882
|
+
const href = iframe.contentWindow?.location?.href;
|
|
9883
|
+
if (wasRedirecting && href) done();
|
|
9884
|
+
} catch {
|
|
9885
|
+
wasRedirecting = true;
|
|
9886
|
+
}
|
|
9887
|
+
}, 100);
|
|
9888
|
+
const timeout = setTimeout(done, 5e3);
|
|
9889
|
+
iframe.onerror = done;
|
|
8238
9890
|
document.body.appendChild(iframe);
|
|
8239
9891
|
});
|
|
8240
9892
|
} catch (error) {
|
|
8241
|
-
console.error("[BackendProvider] logout failed:", error);
|
|
9893
|
+
console.error("[BackendProvider] logout via iframe failed:", error);
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
/**
|
|
9897
|
+
* 非 IOA 企业登出:直接 HTTP 请求 /console/logout
|
|
9898
|
+
*/
|
|
9899
|
+
async logoutViaHttp() {
|
|
9900
|
+
try {
|
|
9901
|
+
await httpService.get("/console/logout");
|
|
9902
|
+
} catch (error) {
|
|
9903
|
+
console.error("[BackendProvider] logout via http failed:", error);
|
|
8242
9904
|
}
|
|
8243
|
-
localStorage.removeItem(SELECTED_ACCOUNT_KEY);
|
|
8244
|
-
accountService.clearAccount();
|
|
8245
9905
|
}
|
|
8246
9906
|
/**
|
|
8247
9907
|
* 批量切换插件状态
|
|
@@ -8273,6 +9933,78 @@ var BackendProvider = class {
|
|
|
8273
9933
|
return null;
|
|
8274
9934
|
}
|
|
8275
9935
|
}
|
|
9936
|
+
/**
|
|
9937
|
+
* 刷新 Token
|
|
9938
|
+
* 通过调用 getAccount 刷新 cookie,适用于 Cloud 场景下页面切换回来时刷新登录态
|
|
9939
|
+
* @returns Promise<Account | null> 刷新后的账号信息
|
|
9940
|
+
*/
|
|
9941
|
+
async refreshToken() {
|
|
9942
|
+
console.log("[BackendProvider] Refreshing token...");
|
|
9943
|
+
try {
|
|
9944
|
+
const account = await this.getAccount();
|
|
9945
|
+
console.log("[BackendProvider] Token refreshed, account:", account?.uid);
|
|
9946
|
+
return account;
|
|
9947
|
+
} catch (error) {
|
|
9948
|
+
console.error("[BackendProvider] refreshToken failed:", error);
|
|
9949
|
+
return null;
|
|
9950
|
+
}
|
|
9951
|
+
}
|
|
9952
|
+
/**
|
|
9953
|
+
* 获取仓库分支列表
|
|
9954
|
+
* API 端点: GET /console/as/connector/oauth/{name}/branches
|
|
9955
|
+
*
|
|
9956
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9957
|
+
* @param params 平台特定的查询参数
|
|
9958
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9959
|
+
* @param perPage 每页数量,最大100
|
|
9960
|
+
* @returns Promise<OauthBranch[]> 分支列表
|
|
9961
|
+
*/
|
|
9962
|
+
async getBranches(connector, params, page = 0, perPage = 100) {
|
|
9963
|
+
return oauthRepositoryService.getBranches(connector, params, page, perPage);
|
|
9964
|
+
}
|
|
9965
|
+
/**
|
|
9966
|
+
* 获取仓库列表
|
|
9967
|
+
* API 端点: GET /console/as/connector/oauth/{name}/repos
|
|
9968
|
+
*
|
|
9969
|
+
* Note: 由于工蜂原生支持的 Search 能力会匹配 path/name/description 部分,
|
|
9970
|
+
* 且不支持定制,不满足产品要求(只按 name 匹配),因此前端拉取全量数据后做筛选。
|
|
9971
|
+
*
|
|
9972
|
+
* @param connector 连接器名称 ('github' | 'gongfeng' | 'cnb')
|
|
9973
|
+
* @param page 页码,从1开始,0表示不分页获取全部
|
|
9974
|
+
* - GitHub 只支持全量数据,必须传 0
|
|
9975
|
+
* - 工蜂和 CNB 依据前端逻辑而定
|
|
9976
|
+
* @param perPage 每页数量,最大100
|
|
9977
|
+
* @returns Promise<ListReposResponse> 仓库列表响应
|
|
9978
|
+
*/
|
|
9979
|
+
async getRepositories(connector, page = 0, perPage = 100) {
|
|
9980
|
+
return oauthRepositoryService.getRepositories(connector, page, perPage);
|
|
9981
|
+
}
|
|
9982
|
+
/**
|
|
9983
|
+
* 保存待发送的输入内容到后端
|
|
9984
|
+
* API 端点: POST /api/v1/code-id
|
|
9985
|
+
*/
|
|
9986
|
+
async savePendingInput(code) {
|
|
9987
|
+
try {
|
|
9988
|
+
const result = await httpService.post("/api/v1/code-id", { code });
|
|
9989
|
+
return result?.codeId || result?.data?.codeId || null;
|
|
9990
|
+
} catch (e) {
|
|
9991
|
+
console.warn("[BackendProvider] savePendingInput failed:", e);
|
|
9992
|
+
return null;
|
|
9993
|
+
}
|
|
9994
|
+
}
|
|
9995
|
+
/**
|
|
9996
|
+
* 从后端加载待发送的输入内容
|
|
9997
|
+
* API 端点: GET /api/v1/code?id=xxx
|
|
9998
|
+
*/
|
|
9999
|
+
async loadPendingInput(codeId) {
|
|
10000
|
+
try {
|
|
10001
|
+
const result = await httpService.get(`/api/v1/code?id=${encodeURIComponent(codeId)}`);
|
|
10002
|
+
return result?.code || result?.data?.code || null;
|
|
10003
|
+
} catch (e) {
|
|
10004
|
+
console.warn("[BackendProvider] loadPendingInput failed:", e);
|
|
10005
|
+
return null;
|
|
10006
|
+
}
|
|
10007
|
+
}
|
|
8276
10008
|
};
|
|
8277
10009
|
/**
|
|
8278
10010
|
* 创建 BackendProvider 实例
|
|
@@ -8312,6 +10044,7 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
8312
10044
|
GET_FILE: "backend:get-file",
|
|
8313
10045
|
RELOAD_WINDOW: "backend:reload-window",
|
|
8314
10046
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
10047
|
+
OPEN_EXTERNAL: "backend:open-external",
|
|
8315
10048
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
8316
10049
|
GET_SUPPORT_SCENES: "backend:get-support-scenes"
|
|
8317
10050
|
};
|
|
@@ -8360,12 +10093,14 @@ var IPCBackendProvider = class {
|
|
|
8360
10093
|
*/
|
|
8361
10094
|
async getAccount() {
|
|
8362
10095
|
this.log("Getting account via IPC");
|
|
10096
|
+
const startTime = performance.now();
|
|
8363
10097
|
try {
|
|
8364
10098
|
const account = await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACCOUNT);
|
|
10099
|
+
this.log(`getAccount IPC completed in ${(performance.now() - startTime).toFixed(0)}ms`);
|
|
8365
10100
|
accountService.setAccount(account);
|
|
8366
10101
|
return account;
|
|
8367
10102
|
} catch (error) {
|
|
8368
|
-
this.log(
|
|
10103
|
+
this.log(`getAccount IPC failed after ${(performance.now() - startTime).toFixed(0)}ms:`, error);
|
|
8369
10104
|
accountService.setAccount(null);
|
|
8370
10105
|
return null;
|
|
8371
10106
|
}
|
|
@@ -8607,6 +10342,20 @@ var IPCBackendProvider = class {
|
|
|
8607
10342
|
}
|
|
8608
10343
|
}
|
|
8609
10344
|
/**
|
|
10345
|
+
* 在外部浏览器中打开链接
|
|
10346
|
+
* IDE 环境: 通过 IPC 通知 IDE 使用 vscode.env.openExternal 打开 URL
|
|
10347
|
+
* @param url 要打开的 URL
|
|
10348
|
+
*/
|
|
10349
|
+
async openExternal(url) {
|
|
10350
|
+
this.log("Opening external URL via IPC:", url);
|
|
10351
|
+
try {
|
|
10352
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_EXTERNAL, { url });
|
|
10353
|
+
} catch (error) {
|
|
10354
|
+
this.log("Open external request failed:", error);
|
|
10355
|
+
throw error;
|
|
10356
|
+
}
|
|
10357
|
+
}
|
|
10358
|
+
/**
|
|
8610
10359
|
* 批量切换插件状态
|
|
8611
10360
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
8612
10361
|
*/
|