@tencent-ai/cloud-agent-sdk 0.2.14-next.e4d2ba9.20260321 → 0.2.15-next.2d2b062.20260324
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 +3804 -160
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +313 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +313 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +3852 -199
- package/dist/index.mjs.map +1 -1
- package/dist/legacy/index.cjs +4563 -417
- package/dist/legacy/index.cjs.map +1 -1
- package/dist/legacy/index.d.cts +283 -1
- package/dist/legacy/index.d.cts.map +1 -1
- package/dist/legacy/index.d.mts +283 -1
- package/dist/legacy/index.d.mts.map +1 -1
- package/dist/legacy/index.mjs +4568 -412
- package/dist/legacy/index.mjs.map +1 -1
- package/dist/tencent-ai-cloud-agent-sdk-0.2.15-next.2d2b062.20260324.tgz +0 -0
- package/package.json +2 -2
- package/dist/tencent-ai-cloud-agent-sdk-0.2.14-next.e4d2ba9.20260321.tgz +0 -0
package/dist/index.cjs
CHANGED
|
@@ -963,6 +963,7 @@ function streamableHttp(options) {
|
|
|
963
963
|
const headers = buildHeaders();
|
|
964
964
|
headers["Accept"] = "text/event-stream";
|
|
965
965
|
if (lastEventId) headers["Last-Event-ID"] = lastEventId;
|
|
966
|
+
if (connectionId) headers["Acp-Connection-Id"] = connectionId;
|
|
966
967
|
const connectTimeoutMs = connectionTimeout > 0 ? connectionTimeout : 3e4;
|
|
967
968
|
const connectController = new AbortController();
|
|
968
969
|
const connectTimer = setTimeout(() => connectController.abort(), connectTimeoutMs);
|
|
@@ -1182,6 +1183,10 @@ var ArtifactManager = class {
|
|
|
1182
1183
|
*/
|
|
1183
1184
|
const DEFAULT_INITIALIZE_TIMEOUT = 3e4;
|
|
1184
1185
|
/**
|
|
1186
|
+
* Default timeout for session/new operation (waiting for sessionId via SSE)
|
|
1187
|
+
*/
|
|
1188
|
+
const DEFAULT_SESSION_TIMEOUT = 3e4;
|
|
1189
|
+
/**
|
|
1185
1190
|
* Default timeout for permission requests
|
|
1186
1191
|
*/
|
|
1187
1192
|
const DEFAULT_PERMISSION_TIMEOUT = 3e5;
|
|
@@ -1237,8 +1242,14 @@ var InitializationError = class extends ACPClientError {
|
|
|
1237
1242
|
* Error thrown for session-related failures
|
|
1238
1243
|
*/
|
|
1239
1244
|
var SessionError = class extends ACPClientError {
|
|
1240
|
-
constructor(message, sessionId, cause) {
|
|
1241
|
-
|
|
1245
|
+
constructor(message, sessionId, agentIdOrCause, cause) {
|
|
1246
|
+
if (agentIdOrCause instanceof Error) {
|
|
1247
|
+
super(message, "SESSION_ERROR", agentIdOrCause);
|
|
1248
|
+
this.agentId = void 0;
|
|
1249
|
+
} else {
|
|
1250
|
+
super(message, "SESSION_ERROR", cause);
|
|
1251
|
+
this.agentId = agentIdOrCause;
|
|
1252
|
+
}
|
|
1242
1253
|
this.name = "SessionError";
|
|
1243
1254
|
this.sessionId = sessionId;
|
|
1244
1255
|
}
|
|
@@ -2000,16 +2011,31 @@ var StreamableHttpClient = class {
|
|
|
2000
2011
|
*
|
|
2001
2012
|
* Retries on transient network errors (e.g., proxy connection reset)
|
|
2002
2013
|
* since session/new is idempotent and safe to retry.
|
|
2014
|
+
*
|
|
2015
|
+
* A timeout (`options.sessionTimeout`, default 30 s) guards against the
|
|
2016
|
+
* SSE response never arriving — the POST returns 202 immediately and the
|
|
2017
|
+
* sessionId comes back via GET SSE, which can hang indefinitely.
|
|
2018
|
+
* On timeout a `SessionError` is thrown.
|
|
2003
2019
|
*/
|
|
2004
2020
|
async createSession(cwd) {
|
|
2005
2021
|
this.ensureInitialized("createSession");
|
|
2006
2022
|
const maxRetries = 2;
|
|
2023
|
+
const timeout = this.options.sessionTimeout ?? DEFAULT_SESSION_TIMEOUT;
|
|
2007
2024
|
let lastError;
|
|
2008
2025
|
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
2009
|
-
const
|
|
2026
|
+
const sessionPromise = this.connection.newSession({
|
|
2010
2027
|
cwd,
|
|
2011
2028
|
mcpServers: []
|
|
2012
2029
|
});
|
|
2030
|
+
let response;
|
|
2031
|
+
if (timeout > 0) {
|
|
2032
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
2033
|
+
setTimeout(() => {
|
|
2034
|
+
reject(new SessionError(`session/new timed out after ${timeout}ms`));
|
|
2035
|
+
}, timeout);
|
|
2036
|
+
});
|
|
2037
|
+
response = await Promise.race([sessionPromise, timeoutPromise]);
|
|
2038
|
+
} else response = await sessionPromise;
|
|
2013
2039
|
this.options.logger?.info(`Session created: ${response.sessionId}`);
|
|
2014
2040
|
return response;
|
|
2015
2041
|
} catch (err) {
|
|
@@ -18100,226 +18126,3654 @@ var CloudAgentProvider = class CloudAgentProvider {
|
|
|
18100
18126
|
};
|
|
18101
18127
|
|
|
18102
18128
|
//#endregion
|
|
18103
|
-
//#region ../agent-provider/src/common/providers/local-agent-provider/
|
|
18129
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/acp/json-rpc.ts
|
|
18104
18130
|
/**
|
|
18105
|
-
*
|
|
18106
|
-
* Wraps AcpJsonRpcClient to implement AgentConnection interface
|
|
18131
|
+
* JSON-RPC 编解码器
|
|
18107
18132
|
*
|
|
18108
|
-
*
|
|
18109
|
-
* Migrated from ipc-agent-provider for unified local agent access
|
|
18133
|
+
* 提供 JSON-RPC 2.0 消息的编码和解码功能
|
|
18110
18134
|
*/
|
|
18111
|
-
|
|
18112
|
-
|
|
18113
|
-
|
|
18135
|
+
var JsonRpcEncoder = class {
|
|
18136
|
+
/**
|
|
18137
|
+
* 编码请求消息
|
|
18138
|
+
* @param id 请求 ID
|
|
18139
|
+
* @param method 方法名
|
|
18140
|
+
* @param params 参数 (可选)
|
|
18141
|
+
* @returns AcpRpcEnvelope 包装的 JSON-RPC 请求
|
|
18142
|
+
*/
|
|
18143
|
+
static encodeRequest(id, method, params) {
|
|
18144
|
+
return {
|
|
18145
|
+
type: "acp-rpc",
|
|
18146
|
+
payload: {
|
|
18147
|
+
jsonrpc: "2.0",
|
|
18148
|
+
id,
|
|
18149
|
+
method,
|
|
18150
|
+
params
|
|
18151
|
+
}
|
|
18152
|
+
};
|
|
18153
|
+
}
|
|
18154
|
+
/**
|
|
18155
|
+
* 编码通知消息 (无响应)
|
|
18156
|
+
* @param method 方法名
|
|
18157
|
+
* @param params 参数 (可选)
|
|
18158
|
+
* @returns AcpRpcEnvelope 包装的 JSON-RPC 通知
|
|
18159
|
+
*/
|
|
18160
|
+
static encodeNotification(method, params) {
|
|
18161
|
+
return {
|
|
18162
|
+
type: "acp-rpc",
|
|
18163
|
+
payload: {
|
|
18164
|
+
jsonrpc: "2.0",
|
|
18165
|
+
method,
|
|
18166
|
+
params
|
|
18167
|
+
}
|
|
18168
|
+
};
|
|
18169
|
+
}
|
|
18170
|
+
/**
|
|
18171
|
+
* 编码成功响应
|
|
18172
|
+
* @param id 对应请求的 ID
|
|
18173
|
+
* @param result 结果数据
|
|
18174
|
+
* @returns AcpRpcEnvelope 包装的 JSON-RPC 响应
|
|
18175
|
+
*/
|
|
18176
|
+
static encodeSuccessResponse(id, result) {
|
|
18177
|
+
return {
|
|
18178
|
+
type: "acp-rpc",
|
|
18179
|
+
payload: {
|
|
18180
|
+
jsonrpc: "2.0",
|
|
18181
|
+
id,
|
|
18182
|
+
result
|
|
18183
|
+
}
|
|
18184
|
+
};
|
|
18185
|
+
}
|
|
18186
|
+
/**
|
|
18187
|
+
* 编码错误响应
|
|
18188
|
+
* @param id 对应请求的 ID
|
|
18189
|
+
* @param error 错误信息
|
|
18190
|
+
* @returns AcpRpcEnvelope 包装的 JSON-RPC 错误响应
|
|
18191
|
+
*/
|
|
18192
|
+
static encodeErrorResponse(id, error) {
|
|
18193
|
+
return {
|
|
18194
|
+
type: "acp-rpc",
|
|
18195
|
+
payload: {
|
|
18196
|
+
jsonrpc: "2.0",
|
|
18197
|
+
id,
|
|
18198
|
+
error
|
|
18199
|
+
}
|
|
18200
|
+
};
|
|
18201
|
+
}
|
|
18202
|
+
/**
|
|
18203
|
+
* 解码 ACP RPC 消息
|
|
18204
|
+
* @param envelope AcpRpcEnvelope
|
|
18205
|
+
* @returns 解码后的 JSON-RPC 消息
|
|
18206
|
+
* @throws 如果消息格式无效
|
|
18207
|
+
*/
|
|
18208
|
+
static decode(envelope) {
|
|
18209
|
+
if (!this.isAcpRpcMessage(envelope)) throw new Error("Invalid ACP RPC message format");
|
|
18210
|
+
const payload = envelope.payload;
|
|
18211
|
+
if (typeof payload !== "object" || payload === null) throw new Error("Invalid JSON-RPC payload");
|
|
18212
|
+
if (payload.jsonrpc !== "2.0") throw new Error("Invalid JSON-RPC version (must be \"2.0\")");
|
|
18213
|
+
const hasId = "id" in payload;
|
|
18214
|
+
const hasMethod = typeof payload.method === "string";
|
|
18215
|
+
if (!hasId && !hasMethod) throw new Error("Invalid JSON-RPC message (must have id or method)");
|
|
18216
|
+
return payload;
|
|
18217
|
+
}
|
|
18218
|
+
/**
|
|
18219
|
+
* 检查是否为 ACP RPC 消息
|
|
18220
|
+
* @param message 待检查的消息
|
|
18221
|
+
* @returns 是否为有效的 AcpRpcEnvelope
|
|
18222
|
+
*/
|
|
18223
|
+
static isAcpRpcMessage(message) {
|
|
18224
|
+
return typeof message === "object" && message !== null && "type" in message && message.type === "acp-rpc" && "payload" in message;
|
|
18225
|
+
}
|
|
18226
|
+
/**
|
|
18227
|
+
* 检查是否为 JSON-RPC 请求 (有 id 字段)
|
|
18228
|
+
* @param message JSON-RPC 消息
|
|
18229
|
+
* @returns 是否为请求消息
|
|
18230
|
+
*/
|
|
18231
|
+
static isRequest(message) {
|
|
18232
|
+
return "id" in message && "method" in message;
|
|
18233
|
+
}
|
|
18234
|
+
/**
|
|
18235
|
+
* 检查是否为 JSON-RPC 响应 (有 id 字段,有 result 或 error)
|
|
18236
|
+
* @param message JSON-RPC 消息
|
|
18237
|
+
* @returns 是否为响应消息
|
|
18238
|
+
*/
|
|
18239
|
+
static isResponse(message) {
|
|
18240
|
+
return "id" in message && ("result" in message || "error" in message);
|
|
18241
|
+
}
|
|
18242
|
+
/**
|
|
18243
|
+
* 检查是否为 JSON-RPC 通知 (无 id 字段)
|
|
18244
|
+
* @param message JSON-RPC 消息
|
|
18245
|
+
* @returns 是否为通知消息
|
|
18246
|
+
*/
|
|
18247
|
+
static isNotification(message) {
|
|
18248
|
+
return !("id" in message) && "method" in message;
|
|
18249
|
+
}
|
|
18250
|
+
};
|
|
18114
18251
|
/**
|
|
18115
|
-
*
|
|
18116
|
-
*
|
|
18117
|
-
* This class wraps an AgentConnection and provides the session-centric API.
|
|
18118
|
-
* It is created by SessionManager when creating or loading sessions.
|
|
18119
|
-
*
|
|
18120
|
-
* @example
|
|
18121
|
-
* ```typescript
|
|
18122
|
-
* // Created by client.sessions.new() or client.sessions.load()
|
|
18123
|
-
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
18124
|
-
*
|
|
18125
|
-
* // Access agent state
|
|
18126
|
-
* console.log(session.agentState.status);
|
|
18127
|
-
*
|
|
18128
|
-
* // Send prompt
|
|
18129
|
-
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
18252
|
+
* 请求 ID 生成器
|
|
18130
18253
|
*
|
|
18131
|
-
*
|
|
18132
|
-
*
|
|
18133
|
-
* ```
|
|
18254
|
+
* 格式: acp_{序号}_{时间戳}
|
|
18255
|
+
* 示例: acp_1_1704067200000
|
|
18134
18256
|
*/
|
|
18135
|
-
var
|
|
18257
|
+
var RequestIdGenerator = class {
|
|
18258
|
+
constructor() {
|
|
18259
|
+
this.counter = 0;
|
|
18260
|
+
}
|
|
18136
18261
|
/**
|
|
18137
|
-
*
|
|
18138
|
-
*
|
|
18139
|
-
* @param sessionId - Session ID
|
|
18140
|
-
* @param agentId - Agent ID
|
|
18141
|
-
* @param connection - Already connected AgentConnection
|
|
18142
|
-
* @param options - Additional options
|
|
18262
|
+
* 生成下一个请求 ID
|
|
18263
|
+
* @returns 请求 ID 字符串
|
|
18143
18264
|
*/
|
|
18144
|
-
|
|
18145
|
-
this.
|
|
18146
|
-
this.listeners = /* @__PURE__ */ new Map();
|
|
18147
|
-
this.onceListeners = /* @__PURE__ */ new Map();
|
|
18148
|
-
this.connectionListeners = [];
|
|
18149
|
-
this._id = sessionId;
|
|
18150
|
-
this._agentId = agentId;
|
|
18151
|
-
this.connection = connection;
|
|
18152
|
-
this.logger = options.logger;
|
|
18153
|
-
this._getFilesystem = options.getFilesystem;
|
|
18154
|
-
this._connectionInfo = options.connectionInfo;
|
|
18155
|
-
this.setupConnectionEvents(connection);
|
|
18156
|
-
this.agent = this.createAgentOperations();
|
|
18157
|
-
this.prompts = this.createPromptsResource();
|
|
18158
|
-
this.artifacts = this.createArtifactsResource();
|
|
18159
|
-
this.files = this.createFilesResource();
|
|
18265
|
+
generate() {
|
|
18266
|
+
return `acp_${++this.counter}`;
|
|
18160
18267
|
}
|
|
18161
18268
|
/**
|
|
18162
|
-
*
|
|
18269
|
+
* 重置计数器
|
|
18163
18270
|
*/
|
|
18164
|
-
|
|
18165
|
-
|
|
18271
|
+
reset() {
|
|
18272
|
+
this.counter = 0;
|
|
18273
|
+
}
|
|
18274
|
+
};
|
|
18275
|
+
|
|
18276
|
+
//#endregion
|
|
18277
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/acp/types.ts
|
|
18278
|
+
/**
|
|
18279
|
+
* 特殊 sessionId 常量
|
|
18280
|
+
* 这些 sessionId 不需要路由到特定窗口
|
|
18281
|
+
*/
|
|
18282
|
+
const SPECIAL_SESSION_IDS = {
|
|
18283
|
+
GLOBAL: "__global__",
|
|
18284
|
+
NEW: "__new__",
|
|
18285
|
+
WORKSPACE: "__workspace__",
|
|
18286
|
+
BROADCAST: "__broadcast__"
|
|
18287
|
+
};
|
|
18288
|
+
/**
|
|
18289
|
+
* 检查是否为保留 sessionId
|
|
18290
|
+
* @param sessionId 会话 ID
|
|
18291
|
+
* @returns 是否为保留 sessionId
|
|
18292
|
+
*/
|
|
18293
|
+
function isReservedSessionId(sessionId) {
|
|
18294
|
+
return Object.values(SPECIAL_SESSION_IDS).includes(sessionId) && sessionId !== SPECIAL_SESSION_IDS.BROADCAST;
|
|
18295
|
+
}
|
|
18296
|
+
|
|
18297
|
+
//#endregion
|
|
18298
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/acp/acp-json-rpc-client.ts
|
|
18299
|
+
/**
|
|
18300
|
+
* ACP JSON-RPC Client 实现
|
|
18301
|
+
*
|
|
18302
|
+
* 实现 IAcpJsonRpcClient 接口
|
|
18303
|
+
*/
|
|
18304
|
+
var AcpJsonRpcClient = class {
|
|
18305
|
+
constructor(channel, config = {}) {
|
|
18306
|
+
this.requestIdGenerator = new RequestIdGenerator();
|
|
18307
|
+
this.pendingRequests = /* @__PURE__ */ new Map();
|
|
18308
|
+
this.sessionUpdateCallbacks = /* @__PURE__ */ new Set();
|
|
18309
|
+
this.permissionRequestResolvers = /* @__PURE__ */ new Map();
|
|
18310
|
+
this.extNotificationCallbacks = /* @__PURE__ */ new Set();
|
|
18311
|
+
this.eventHandlers = [];
|
|
18312
|
+
this.channel = channel;
|
|
18313
|
+
this.debug = true;
|
|
18314
|
+
this.setupMessageListener();
|
|
18315
|
+
this.log("AcpJsonRpcClient initialized");
|
|
18316
|
+
}
|
|
18317
|
+
async initialize(request) {
|
|
18318
|
+
this.log("initialize called");
|
|
18319
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.GLOBAL, "initialize", request);
|
|
18320
|
+
}
|
|
18321
|
+
async authenticate(request) {
|
|
18322
|
+
this.log("authenticate called");
|
|
18323
|
+
await this.sendRequest(SPECIAL_SESSION_IDS.GLOBAL, "authenticate", request);
|
|
18324
|
+
}
|
|
18325
|
+
async newSession(request) {
|
|
18326
|
+
this.log("newSession called");
|
|
18327
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.NEW, "newSession", request);
|
|
18328
|
+
}
|
|
18329
|
+
async loadSession(request) {
|
|
18330
|
+
this.log(`[AcpJsonRpcClient] loadSession called:, ${request.sessionId}`);
|
|
18331
|
+
const response = await this.sendRequest(request.sessionId, "loadSession", request);
|
|
18332
|
+
this.log(`[AcpJsonRpcClient] loadSession response:, ${response}`);
|
|
18333
|
+
return response;
|
|
18334
|
+
}
|
|
18335
|
+
async prompt(request) {
|
|
18336
|
+
this.log("prompt called for session:", request.sessionId);
|
|
18337
|
+
return await this.sendRequest(request.sessionId, "prompt", request);
|
|
18338
|
+
}
|
|
18339
|
+
async cancel(params) {
|
|
18340
|
+
this.log("cancel called for session:", params.sessionId);
|
|
18341
|
+
const envelope = JsonRpcEncoder.encodeNotification("cancel", params);
|
|
18342
|
+
this.send(params.sessionId, envelope);
|
|
18343
|
+
}
|
|
18344
|
+
async setSessionModel(params) {
|
|
18345
|
+
this.log("setSessionModel called for session:", params.sessionId);
|
|
18346
|
+
return await this.sendRequest(params.sessionId, "setSessionModel", params);
|
|
18347
|
+
}
|
|
18348
|
+
async setSessionMode(params) {
|
|
18349
|
+
this.log("setSessionMode called for session:", params.sessionId);
|
|
18350
|
+
return await this.sendRequest(params.sessionId, "setSessionMode", params);
|
|
18351
|
+
}
|
|
18352
|
+
async toolCallback(request) {
|
|
18353
|
+
this.log("toolCallback called for session:", request.sessionId, "action:", request.action);
|
|
18354
|
+
return await this.sendRequest(request.sessionId, "toolCallback", request);
|
|
18166
18355
|
}
|
|
18167
18356
|
/**
|
|
18168
|
-
*
|
|
18357
|
+
* 打开工作区窗口
|
|
18358
|
+
* 使用 __workspace__ session ID,由 Main Process 直接处理
|
|
18359
|
+
* @param request 打开工作区请求
|
|
18360
|
+
* @returns 打开工作区响应
|
|
18169
18361
|
*/
|
|
18170
|
-
|
|
18171
|
-
|
|
18362
|
+
async openWorkspace(request) {
|
|
18363
|
+
this.log("openWorkspace called", request);
|
|
18364
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.WORKSPACE, "openWorkspace", request);
|
|
18172
18365
|
}
|
|
18173
18366
|
/**
|
|
18174
|
-
*
|
|
18367
|
+
* 发送全局请求
|
|
18368
|
+
* 使用 __global__ session ID,会根据 params.cwd 路由到对应窗口
|
|
18369
|
+
* cwd 为空时会路由到 Default Window
|
|
18370
|
+
* @param method 方法名
|
|
18371
|
+
* @param params 请求参数
|
|
18372
|
+
* @returns 响应结果
|
|
18175
18373
|
*/
|
|
18176
|
-
|
|
18177
|
-
|
|
18374
|
+
async sendBroadcastRequest(method, params) {
|
|
18375
|
+
this.log(`sendGlobalRequest called: ${method}`, params);
|
|
18376
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, method, params);
|
|
18178
18377
|
}
|
|
18179
18378
|
/**
|
|
18180
|
-
*
|
|
18379
|
+
* 获取当前工作区列表
|
|
18380
|
+
* 使用 __workspace__ session ID,由 Main Process 直接处理
|
|
18381
|
+
* @param request 获取工作区列表请求
|
|
18382
|
+
* @returns 获取工作区列表响应
|
|
18181
18383
|
*/
|
|
18182
|
-
|
|
18183
|
-
this.
|
|
18384
|
+
async getCurrentWorkspaces(request) {
|
|
18385
|
+
this.log("getCurrentWorkspaces called", request);
|
|
18386
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.WORKSPACE, "getCurrentWorkspaces", request ?? {});
|
|
18184
18387
|
}
|
|
18185
18388
|
/**
|
|
18186
|
-
*
|
|
18187
|
-
*
|
|
18389
|
+
* 获取已安装插件列表
|
|
18390
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18391
|
+
* @param request 获取插件请求
|
|
18392
|
+
* @returns 已安装插件列表响应
|
|
18188
18393
|
*/
|
|
18189
|
-
|
|
18190
|
-
|
|
18191
|
-
|
|
18192
|
-
status: this.connection.state,
|
|
18193
|
-
capabilities: this.connection.capabilities,
|
|
18194
|
-
type: this.connection.transport,
|
|
18195
|
-
cwd: this.connection.cwd || ""
|
|
18196
|
-
};
|
|
18394
|
+
async getInstalledPlugins(request) {
|
|
18395
|
+
this.log("getInstalledPlugins called", request);
|
|
18396
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getInstalledPlugins", request ?? {});
|
|
18197
18397
|
}
|
|
18198
18398
|
/**
|
|
18199
|
-
*
|
|
18399
|
+
* 安装插件
|
|
18400
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18401
|
+
* @param request 安装插件请求
|
|
18402
|
+
* @returns 安装结果
|
|
18200
18403
|
*/
|
|
18201
|
-
|
|
18202
|
-
|
|
18404
|
+
async installPlugins(request) {
|
|
18405
|
+
this.log("installPlugins called", request);
|
|
18406
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "installPlugins", request);
|
|
18203
18407
|
}
|
|
18204
18408
|
/**
|
|
18205
|
-
*
|
|
18409
|
+
* 卸载插件
|
|
18410
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18411
|
+
* @param request 卸载插件请求
|
|
18412
|
+
* @returns 卸载结果
|
|
18206
18413
|
*/
|
|
18207
|
-
|
|
18208
|
-
|
|
18414
|
+
async uninstallPlugin(request) {
|
|
18415
|
+
this.log("uninstallPlugin called", request);
|
|
18416
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "uninstallPlugin", request);
|
|
18209
18417
|
}
|
|
18210
18418
|
/**
|
|
18211
|
-
*
|
|
18419
|
+
* 更新插件到最新版本
|
|
18420
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18421
|
+
* @param request 更新插件请求
|
|
18422
|
+
* @returns 更新结果
|
|
18212
18423
|
*/
|
|
18213
|
-
|
|
18214
|
-
|
|
18424
|
+
async updatePlugin(request) {
|
|
18425
|
+
this.log("updatePlugin called", request);
|
|
18426
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "updatePlugin", request);
|
|
18215
18427
|
}
|
|
18216
18428
|
/**
|
|
18217
|
-
*
|
|
18429
|
+
* 批量切换插件状态
|
|
18430
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18431
|
+
* @param request 批量切换插件请求
|
|
18432
|
+
* @returns 批量操作结果
|
|
18218
18433
|
*/
|
|
18219
|
-
|
|
18220
|
-
|
|
18434
|
+
async batchTogglePlugins(request) {
|
|
18435
|
+
this.log("batchTogglePlugins called", request);
|
|
18436
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "batchTogglePlugins", request);
|
|
18221
18437
|
}
|
|
18222
18438
|
/**
|
|
18223
|
-
*
|
|
18439
|
+
* 获取插件市场列表
|
|
18440
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18441
|
+
* @param request 获取市场列表请求
|
|
18442
|
+
* @returns 插件市场列表响应
|
|
18224
18443
|
*/
|
|
18225
|
-
|
|
18226
|
-
|
|
18444
|
+
async getPluginMarketplaces(request) {
|
|
18445
|
+
this.log("getPluginMarketplaces called", request);
|
|
18446
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getPluginMarketplaces", request ?? {});
|
|
18227
18447
|
}
|
|
18228
18448
|
/**
|
|
18229
|
-
*
|
|
18230
|
-
*
|
|
18231
|
-
*
|
|
18232
|
-
*
|
|
18449
|
+
* 获取市场下的插件列表
|
|
18450
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18451
|
+
* @param request 获取市场插件请求
|
|
18452
|
+
* @returns 市场插件列表响应
|
|
18233
18453
|
*/
|
|
18234
|
-
|
|
18235
|
-
|
|
18454
|
+
async getMarketplacePlugins(request) {
|
|
18455
|
+
this.log("getMarketplacePlugins called", request);
|
|
18456
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getMarketplacePlugins", request);
|
|
18236
18457
|
}
|
|
18237
18458
|
/**
|
|
18238
|
-
*
|
|
18459
|
+
* 获取插件详情
|
|
18460
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18461
|
+
* @param request 获取插件详情请求
|
|
18462
|
+
* @returns 插件详情响应
|
|
18239
18463
|
*/
|
|
18240
|
-
|
|
18241
|
-
this.
|
|
18242
|
-
this.
|
|
18464
|
+
async getPluginDetail(request) {
|
|
18465
|
+
this.log("getPluginDetail called", request);
|
|
18466
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getPluginDetail", request);
|
|
18243
18467
|
}
|
|
18244
18468
|
/**
|
|
18245
|
-
*
|
|
18469
|
+
* 添加插件市场
|
|
18470
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18471
|
+
* @param request 添加市场请求
|
|
18472
|
+
* @returns 添加结果
|
|
18246
18473
|
*/
|
|
18247
|
-
|
|
18248
|
-
|
|
18474
|
+
async addPluginMarketplace(request) {
|
|
18475
|
+
this.log("addPluginMarketplace called", request);
|
|
18476
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "addPluginMarketplace", request);
|
|
18249
18477
|
}
|
|
18250
18478
|
/**
|
|
18251
|
-
*
|
|
18252
|
-
*
|
|
18479
|
+
* 删除插件市场
|
|
18480
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18481
|
+
* @param request 删除市场请求
|
|
18482
|
+
* @returns 删除结果
|
|
18253
18483
|
*/
|
|
18254
|
-
|
|
18255
|
-
|
|
18484
|
+
async removePluginMarketplace(request) {
|
|
18485
|
+
this.log("removePluginMarketplace called", request);
|
|
18486
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "removePluginMarketplace", request);
|
|
18256
18487
|
}
|
|
18257
18488
|
/**
|
|
18258
|
-
*
|
|
18489
|
+
* 刷新插件市场
|
|
18490
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18491
|
+
* @param request 刷新市场请求
|
|
18492
|
+
* @returns 刷新结果
|
|
18259
18493
|
*/
|
|
18260
|
-
|
|
18261
|
-
this.
|
|
18262
|
-
this.
|
|
18494
|
+
async refreshPluginMarketplace(request) {
|
|
18495
|
+
this.log("refreshPluginMarketplace called", request);
|
|
18496
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "refreshPluginMarketplace", request);
|
|
18263
18497
|
}
|
|
18264
18498
|
/**
|
|
18265
|
-
*
|
|
18499
|
+
* 用新窗口打开文件夹
|
|
18500
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18501
|
+
*
|
|
18502
|
+
* @param request 打开文件夹请求
|
|
18266
18503
|
*/
|
|
18267
|
-
|
|
18268
|
-
this.
|
|
18269
|
-
this.
|
|
18504
|
+
async openFolderInNewWindow(request) {
|
|
18505
|
+
this.log("openFolderInNewWindow called", request);
|
|
18506
|
+
await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "openFolderInNewWindow", request);
|
|
18270
18507
|
}
|
|
18271
|
-
|
|
18272
|
-
|
|
18273
|
-
|
|
18274
|
-
|
|
18275
|
-
|
|
18276
|
-
|
|
18277
|
-
|
|
18278
|
-
|
|
18279
|
-
|
|
18280
|
-
|
|
18281
|
-
|
|
18282
|
-
|
|
18283
|
-
|
|
18284
|
-
|
|
18285
|
-
|
|
18508
|
+
/**
|
|
18509
|
+
* 获取支持的场景列表(从后端 API)
|
|
18510
|
+
* 使用 __broadcast__ session ID,由 Extension Host 处理
|
|
18511
|
+
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
18512
|
+
*
|
|
18513
|
+
* 调用链:
|
|
18514
|
+
* 1. AgentNewAdapter.getTemplates() 调用此方法
|
|
18515
|
+
* 2. 通过 ACP 协议发送请求到 Extension Host
|
|
18516
|
+
* 3. Extension Host 调用 RestOperations -> GET /v2/as/support/scenes
|
|
18517
|
+
* 4. 返回 SupportSceneInfo[] 数据
|
|
18518
|
+
*
|
|
18519
|
+
* @param request 获取支持场景请求
|
|
18520
|
+
* @returns 支持的场景列表响应
|
|
18521
|
+
*/
|
|
18522
|
+
async getSupportScenes(request) {
|
|
18523
|
+
this.log("getSupportScenes called", request);
|
|
18524
|
+
return await this.sendRequest(SPECIAL_SESSION_IDS.BROADCAST, "getSupportScenes", request ?? {});
|
|
18525
|
+
}
|
|
18526
|
+
getAutomationBridge() {
|
|
18527
|
+
return globalThis.__codebuddyAutomationBridge;
|
|
18528
|
+
}
|
|
18529
|
+
requireAutomationBridge() {
|
|
18530
|
+
const bridge = this.getAutomationBridge();
|
|
18531
|
+
if (!bridge) throw new Error("Automation bridge is unavailable; automation now only supports the Main/Renderer orchestration path");
|
|
18532
|
+
return bridge;
|
|
18533
|
+
}
|
|
18534
|
+
async getAutomationSnapshot() {
|
|
18535
|
+
this.log("getAutomationSnapshot called");
|
|
18536
|
+
const bridge = this.requireAutomationBridge();
|
|
18537
|
+
if (!bridge.getAutomationSnapshot) throw new Error("Automation bridge does not implement getAutomationSnapshot");
|
|
18538
|
+
return await bridge.getAutomationSnapshot();
|
|
18539
|
+
}
|
|
18540
|
+
async updateAutomation(request) {
|
|
18541
|
+
this.log("updateAutomation called", request);
|
|
18542
|
+
const bridge = this.requireAutomationBridge();
|
|
18543
|
+
if (!bridge.updateAutomation) throw new Error("Automation bridge does not implement updateAutomation");
|
|
18544
|
+
return await bridge.updateAutomation(request);
|
|
18545
|
+
}
|
|
18546
|
+
async deleteAutomation(request) {
|
|
18547
|
+
this.log("deleteAutomation called", request);
|
|
18548
|
+
const bridge = this.requireAutomationBridge();
|
|
18549
|
+
if (!bridge.deleteAutomation) throw new Error("Automation bridge does not implement deleteAutomation");
|
|
18550
|
+
return await bridge.deleteAutomation(request.id);
|
|
18551
|
+
}
|
|
18552
|
+
async archiveAutomationInboxItem(request) {
|
|
18553
|
+
this.log("archiveAutomationInboxItem called", request);
|
|
18554
|
+
const bridge = this.requireAutomationBridge();
|
|
18555
|
+
if (!bridge.archiveAutomationInboxItem) throw new Error("Automation bridge does not implement archiveAutomationInboxItem");
|
|
18556
|
+
return await bridge.archiveAutomationInboxItem(request.itemId);
|
|
18557
|
+
}
|
|
18558
|
+
async deleteAutomationInboxItem(request) {
|
|
18559
|
+
this.log("deleteAutomationInboxItem called", request);
|
|
18560
|
+
const bridge = this.requireAutomationBridge();
|
|
18561
|
+
if (!bridge.deleteAutomationInboxItem) throw new Error("Automation bridge does not implement deleteAutomationInboxItem");
|
|
18562
|
+
return await bridge.deleteAutomationInboxItem(request.itemId);
|
|
18563
|
+
}
|
|
18564
|
+
async testAutomation(request) {
|
|
18565
|
+
this.log("testAutomation called", request);
|
|
18566
|
+
const bridge = this.requireAutomationBridge();
|
|
18567
|
+
if (!bridge.testAutomation) throw new Error("Automation bridge does not implement testAutomation");
|
|
18568
|
+
return await bridge.testAutomation(request.id);
|
|
18569
|
+
}
|
|
18570
|
+
onSessionUpdate(callback) {
|
|
18571
|
+
this.sessionUpdateCallbacks.add(callback);
|
|
18572
|
+
this.log("onSessionUpdate: callback registered");
|
|
18573
|
+
return () => {
|
|
18574
|
+
this.sessionUpdateCallbacks.delete(callback);
|
|
18575
|
+
this.log("onSessionUpdate: callback unregistered");
|
|
18286
18576
|
};
|
|
18287
18577
|
}
|
|
18288
|
-
|
|
18289
|
-
|
|
18290
|
-
|
|
18291
|
-
|
|
18292
|
-
|
|
18293
|
-
|
|
18294
|
-
stream: (params) => {
|
|
18295
|
-
return this.getConnectionOrThrow().promptStream(this._id, params);
|
|
18296
|
-
},
|
|
18297
|
-
cancel: async () => {
|
|
18298
|
-
await this.getConnectionOrThrow().cancel(this._id);
|
|
18299
|
-
}
|
|
18578
|
+
onRequestPermission(sessionId, callback) {
|
|
18579
|
+
this.permissionRequestResolvers.set(sessionId, callback);
|
|
18580
|
+
this.log("onRequestPermission: callback registered");
|
|
18581
|
+
return () => {
|
|
18582
|
+
this.permissionRequestResolvers.delete(sessionId);
|
|
18583
|
+
this.log("onRequestPermission: callback unregistered");
|
|
18300
18584
|
};
|
|
18301
18585
|
}
|
|
18302
|
-
|
|
18303
|
-
|
|
18304
|
-
|
|
18305
|
-
|
|
18306
|
-
|
|
18307
|
-
|
|
18308
|
-
|
|
18309
|
-
|
|
18310
|
-
|
|
18311
|
-
|
|
18312
|
-
notSupported();
|
|
18313
|
-
},
|
|
18314
|
-
content: async (_artifactId) => {
|
|
18315
|
-
notSupported();
|
|
18316
|
-
return "";
|
|
18317
|
-
}
|
|
18586
|
+
/**
|
|
18587
|
+
* 监听 extNotification 推送
|
|
18588
|
+
* 用于接收 artifact 等扩展通知
|
|
18589
|
+
*/
|
|
18590
|
+
onExtNotification(callback) {
|
|
18591
|
+
this.extNotificationCallbacks.add(callback);
|
|
18592
|
+
this.log("onExtNotification: callback registered");
|
|
18593
|
+
return () => {
|
|
18594
|
+
this.extNotificationCallbacks.delete(callback);
|
|
18595
|
+
this.log("onExtNotification: callback unregistered");
|
|
18318
18596
|
};
|
|
18319
18597
|
}
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18598
|
+
destroy() {
|
|
18599
|
+
this.log("Destroying AcpJsonRpcClient");
|
|
18600
|
+
this.eventHandlers.forEach(({ event, handler }) => {
|
|
18601
|
+
this.channel.off(event, handler);
|
|
18602
|
+
});
|
|
18603
|
+
this.eventHandlers.length = 0;
|
|
18604
|
+
this.pendingRequests.forEach(({ reject }) => {
|
|
18605
|
+
reject(/* @__PURE__ */ new Error("Client destroyed"));
|
|
18606
|
+
});
|
|
18607
|
+
this.pendingRequests.clear();
|
|
18608
|
+
this.sessionUpdateCallbacks.clear();
|
|
18609
|
+
this.permissionRequestResolvers.clear();
|
|
18610
|
+
}
|
|
18611
|
+
/**
|
|
18612
|
+
* 发送 JSON-RPC 请求并等待响应
|
|
18613
|
+
* @param sessionId 会话 ID
|
|
18614
|
+
* @param method 方法名
|
|
18615
|
+
* @param params 参数
|
|
18616
|
+
* @returns Promise<T> 响应结果
|
|
18617
|
+
*/
|
|
18618
|
+
async sendRequest(sessionId, method, params) {
|
|
18619
|
+
const id = this.requestIdGenerator.generate();
|
|
18620
|
+
const envelope = JsonRpcEncoder.encodeRequest(id, method, {
|
|
18621
|
+
sessionId: isReservedSessionId(sessionId) ? void 0 : sessionId,
|
|
18622
|
+
...params ?? {}
|
|
18623
|
+
});
|
|
18624
|
+
return new Promise(async (resolve, reject) => {
|
|
18625
|
+
this.pendingRequests.set(id, {
|
|
18626
|
+
resolve,
|
|
18627
|
+
reject,
|
|
18628
|
+
method,
|
|
18629
|
+
sessionId
|
|
18630
|
+
});
|
|
18631
|
+
if (!await this.send(sessionId, envelope)) {
|
|
18632
|
+
this.pendingRequests.delete(id);
|
|
18633
|
+
reject(/* @__PURE__ */ new Error(`Failed to send ${method} request for session: ${sessionId}`));
|
|
18634
|
+
}
|
|
18635
|
+
this.log(`Request sent: ${method} (id: ${id}, session: ${sessionId})`);
|
|
18636
|
+
});
|
|
18637
|
+
}
|
|
18638
|
+
/**
|
|
18639
|
+
* 发送消息到指定 session
|
|
18640
|
+
* @param sessionId 会话 ID
|
|
18641
|
+
* @param envelope ACP RPC 消息包装
|
|
18642
|
+
* @returns 是否发送成功
|
|
18643
|
+
*/
|
|
18644
|
+
send(sessionId, envelope) {
|
|
18645
|
+
return this.channel.sendNotification(sessionId, envelope);
|
|
18646
|
+
}
|
|
18647
|
+
/**
|
|
18648
|
+
* 设置消息监听器
|
|
18649
|
+
* 处理来自 ExtensionHost 的 JSON-RPC 响应和推送
|
|
18650
|
+
*/
|
|
18651
|
+
setupMessageListener() {
|
|
18652
|
+
const responseHandler = (data) => {
|
|
18653
|
+
if (!JsonRpcEncoder.isAcpRpcMessage(data)) return;
|
|
18654
|
+
try {
|
|
18655
|
+
const message = JsonRpcEncoder.decode(data);
|
|
18656
|
+
if (JsonRpcEncoder.isResponse(message)) this.handleResponse(message);
|
|
18657
|
+
else if (JsonRpcEncoder.isNotification(message)) this.handleNotification(message);
|
|
18658
|
+
else if (JsonRpcEncoder.isRequest(message)) this.handleNotification(message);
|
|
18659
|
+
} catch (error) {
|
|
18660
|
+
this.log("Error handling message:", error);
|
|
18661
|
+
}
|
|
18662
|
+
};
|
|
18663
|
+
this.channel.on("acp-rpc-response", responseHandler);
|
|
18664
|
+
this.eventHandlers.push({
|
|
18665
|
+
event: "acp-rpc-response",
|
|
18666
|
+
handler: responseHandler
|
|
18667
|
+
});
|
|
18668
|
+
this.channel.on("message", responseHandler);
|
|
18669
|
+
this.eventHandlers.push({
|
|
18670
|
+
event: "message",
|
|
18671
|
+
handler: responseHandler
|
|
18672
|
+
});
|
|
18673
|
+
this.log("Message listener setup complete");
|
|
18674
|
+
}
|
|
18675
|
+
/**
|
|
18676
|
+
* 处理 JSON-RPC 响应
|
|
18677
|
+
*/
|
|
18678
|
+
handleResponse(response) {
|
|
18679
|
+
const pending = this.pendingRequests.get(response.id);
|
|
18680
|
+
if (!pending) {
|
|
18681
|
+
this.log(`No pending request found for response id: ${response.id}`);
|
|
18682
|
+
return;
|
|
18683
|
+
}
|
|
18684
|
+
this.pendingRequests.delete(response.id);
|
|
18685
|
+
if (response.error) {
|
|
18686
|
+
const error = new Error(response.error.message);
|
|
18687
|
+
error.code = response.error.code;
|
|
18688
|
+
pending.reject(error);
|
|
18689
|
+
} else pending.resolve(response.result);
|
|
18690
|
+
this.log(`Response received for request: ${pending.method} (id: ${response.id})`);
|
|
18691
|
+
}
|
|
18692
|
+
/**
|
|
18693
|
+
* 处理 JSON-RPC 推送消息
|
|
18694
|
+
*/
|
|
18695
|
+
handleNotification(notification) {
|
|
18696
|
+
const method = notification.method;
|
|
18697
|
+
let params = notification.params;
|
|
18698
|
+
if (notification.id) params = {
|
|
18699
|
+
...params,
|
|
18700
|
+
requestId: notification.id
|
|
18701
|
+
};
|
|
18702
|
+
this.log(`Notification received: ${method}`);
|
|
18703
|
+
switch (method) {
|
|
18704
|
+
case "session/update":
|
|
18705
|
+
case "sessionUpdate":
|
|
18706
|
+
this.handleSessionUpdate(params);
|
|
18707
|
+
break;
|
|
18708
|
+
case "request/permission":
|
|
18709
|
+
case "requestPermission":
|
|
18710
|
+
this.handlePermissionRequest(params);
|
|
18711
|
+
break;
|
|
18712
|
+
case "extNotification":
|
|
18713
|
+
this.handleExtNotification(params);
|
|
18714
|
+
break;
|
|
18715
|
+
default: this.log(`Unknown notification method: ${method}`);
|
|
18716
|
+
}
|
|
18717
|
+
}
|
|
18718
|
+
/**
|
|
18719
|
+
* 处理 extNotification 推送
|
|
18720
|
+
*/
|
|
18721
|
+
handleExtNotification(params) {
|
|
18722
|
+
if (!params) {
|
|
18723
|
+
this.log("extNotification missing params");
|
|
18724
|
+
return;
|
|
18725
|
+
}
|
|
18726
|
+
const method = params.method;
|
|
18727
|
+
const extParams = params.params || {};
|
|
18728
|
+
this.log("handleExtNotification:", {
|
|
18729
|
+
method,
|
|
18730
|
+
extParams
|
|
18731
|
+
});
|
|
18732
|
+
this.extNotificationCallbacks.forEach((callback) => {
|
|
18733
|
+
try {
|
|
18734
|
+
callback(method, extParams);
|
|
18735
|
+
} catch (error) {
|
|
18736
|
+
this.log("extNotification callback error:", error);
|
|
18737
|
+
}
|
|
18738
|
+
});
|
|
18739
|
+
}
|
|
18740
|
+
/**
|
|
18741
|
+
* 处理会话更新推送
|
|
18742
|
+
*
|
|
18743
|
+
* 支持 ACP 协议定义的多种 session update 类型:
|
|
18744
|
+
* - available_commands_update: 可用命令列表更新
|
|
18745
|
+
* - 其他类型: 原样转发给调用方
|
|
18746
|
+
*/
|
|
18747
|
+
handleSessionUpdate(params) {
|
|
18748
|
+
if (!params) {
|
|
18749
|
+
this.log("Session update notification missing params");
|
|
18750
|
+
return;
|
|
18751
|
+
}
|
|
18752
|
+
const sessionId = params.sessionId;
|
|
18753
|
+
const messageId = params.messageId;
|
|
18754
|
+
if (!sessionId) {
|
|
18755
|
+
this.log("Invalid session update params:", params);
|
|
18756
|
+
return;
|
|
18757
|
+
}
|
|
18758
|
+
const update = params.update;
|
|
18759
|
+
if (update?.sessionUpdate === "available_commands_update") {
|
|
18760
|
+
this.log(`Available commands update received for session: ${sessionId}`);
|
|
18761
|
+
const rawCommands = update?.availableCommands;
|
|
18762
|
+
if (Array.isArray(rawCommands)) {
|
|
18763
|
+
const commands = rawCommands.filter((cmd) => cmd !== null && typeof cmd === "object" && typeof cmd.name === "string").map((cmd) => ({
|
|
18764
|
+
name: String(cmd.name),
|
|
18765
|
+
description: typeof cmd.description === "string" ? cmd.description : "",
|
|
18766
|
+
input: cmd.input && typeof cmd.input === "object" ? { hint: typeof cmd.input.hint === "string" ? cmd.input.hint : void 0 } : void 0
|
|
18767
|
+
}));
|
|
18768
|
+
this.log(`Parsed ${commands.length} available commands`);
|
|
18769
|
+
} else this.log("Invalid availableCommands format in available_commands_update");
|
|
18770
|
+
}
|
|
18771
|
+
const sessionUpdateParams = {
|
|
18772
|
+
notification: params,
|
|
18773
|
+
...messageId && { messageId }
|
|
18774
|
+
};
|
|
18775
|
+
if (this.sessionUpdateCallbacks.size === 0) this.log(`handleSessionUpdate: no callbacks registered (expected for sharedAcpClient), type=${update?.sessionUpdate}, sessionId=${sessionId?.substring(0, 8)}`);
|
|
18776
|
+
this.sessionUpdateCallbacks.forEach((callback) => {
|
|
18777
|
+
try {
|
|
18778
|
+
callback(sessionUpdateParams);
|
|
18779
|
+
} catch (error) {
|
|
18780
|
+
this.log("Session update callback error:", error);
|
|
18781
|
+
}
|
|
18782
|
+
});
|
|
18783
|
+
}
|
|
18784
|
+
/**
|
|
18785
|
+
* 处理权限请求推送
|
|
18786
|
+
*/
|
|
18787
|
+
async handlePermissionRequest(params) {
|
|
18788
|
+
if (!params) {
|
|
18789
|
+
this.log("Permission request notification missing params");
|
|
18790
|
+
return;
|
|
18791
|
+
}
|
|
18792
|
+
const sessionId = params.sessionId;
|
|
18793
|
+
const requestId = params.requestId;
|
|
18794
|
+
const toolCall = params.toolCall;
|
|
18795
|
+
const options = params.options;
|
|
18796
|
+
if (!sessionId || !requestId || !toolCall) {
|
|
18797
|
+
this.log("Invalid permission request params:", params);
|
|
18798
|
+
return;
|
|
18799
|
+
}
|
|
18800
|
+
const permissionParams = {
|
|
18801
|
+
sessionId,
|
|
18802
|
+
requestId,
|
|
18803
|
+
toolCall,
|
|
18804
|
+
options
|
|
18805
|
+
};
|
|
18806
|
+
const resolver = this.permissionRequestResolvers.get(sessionId);
|
|
18807
|
+
if (!resolver) {
|
|
18808
|
+
this.log("No permission resolver registered, cancelling...");
|
|
18809
|
+
return;
|
|
18810
|
+
}
|
|
18811
|
+
try {
|
|
18812
|
+
const outcome = await resolver(permissionParams);
|
|
18813
|
+
this.sendPermissionResponse(sessionId, requestId, outcome);
|
|
18814
|
+
} catch (error) {
|
|
18815
|
+
this.log("Permission resolver error:", error);
|
|
18816
|
+
this.sendPermissionResponse(sessionId, requestId, { outcome: "cancelled" });
|
|
18817
|
+
}
|
|
18818
|
+
}
|
|
18819
|
+
/**
|
|
18820
|
+
* 发送权限响应
|
|
18821
|
+
*/
|
|
18822
|
+
sendPermissionResponse(sessionId, requestId, outcome) {
|
|
18823
|
+
const envelope = JsonRpcEncoder.encodeSuccessResponse(requestId, outcome);
|
|
18824
|
+
this.send(sessionId, envelope);
|
|
18825
|
+
this.log("Permission response sent for request:", requestId);
|
|
18826
|
+
}
|
|
18827
|
+
/**
|
|
18828
|
+
* 调试日志
|
|
18829
|
+
*/
|
|
18830
|
+
log(...args) {
|
|
18831
|
+
if (this.debug) console.log("[AcpJsonRpcClient]", ...args);
|
|
18832
|
+
}
|
|
18833
|
+
};
|
|
18834
|
+
|
|
18835
|
+
//#endregion
|
|
18836
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/local-connection.ts
|
|
18837
|
+
/**
|
|
18838
|
+
* Local Agent Connection
|
|
18839
|
+
* Wraps AcpJsonRpcClient to implement AgentConnection interface
|
|
18840
|
+
*
|
|
18841
|
+
* Uses IWidgetChannel for IPC communication with ExtensionHost
|
|
18842
|
+
* Migrated from ipc-agent-provider for unified local agent access
|
|
18843
|
+
*/
|
|
18844
|
+
/**
|
|
18845
|
+
* Local Agent Connection implementation
|
|
18846
|
+
* Uses AcpJsonRpcClient to communicate with ExtensionHost via IWidgetChannel
|
|
18847
|
+
*
|
|
18848
|
+
* Phase 1 Implementation:
|
|
18849
|
+
* - connect/disconnect: ✅ via initialize
|
|
18850
|
+
* - createSession/loadSession: ✅ via AcpJsonRpcClient
|
|
18851
|
+
* - prompt/cancel: ✅ via AcpJsonRpcClient
|
|
18852
|
+
* - Event forwarding: ✅ sessionUpdate, permissionRequest
|
|
18853
|
+
* - resolvePermission/rejectPermission: ✅ via callback
|
|
18854
|
+
*
|
|
18855
|
+
* Phase 2 (Not Implemented):
|
|
18856
|
+
* - promptStream: throws 'Not implemented'
|
|
18857
|
+
* - Artifact methods: return empty
|
|
18858
|
+
* - Question methods: return empty
|
|
18859
|
+
* - Extension methods: throw 'Not implemented'
|
|
18860
|
+
* - Filesystem methods: throw 'Not implemented'
|
|
18861
|
+
*/
|
|
18862
|
+
var LocalAgentConnection = class {
|
|
18863
|
+
constructor(agentId, config) {
|
|
18864
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
18865
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
18866
|
+
this._state = "disconnected";
|
|
18867
|
+
this._isInitialized = false;
|
|
18868
|
+
this.pendingPermissions = /* @__PURE__ */ new Map();
|
|
18869
|
+
this.artifactCache = /* @__PURE__ */ new Map();
|
|
18870
|
+
this.transport = "local";
|
|
18871
|
+
this.onRequest = void 0;
|
|
18872
|
+
this.agentId = agentId;
|
|
18873
|
+
this.cwd = agentId;
|
|
18874
|
+
this.debug = config.debug ?? false;
|
|
18875
|
+
this.permissionTimeout = config.permissionTimeout ?? 3e4;
|
|
18876
|
+
this.permissionAutoRejectOnTimeout = config.permissionAutoRejectOnTimeout ?? false;
|
|
18877
|
+
this.acpClient = new AcpJsonRpcClient(config.channel, {
|
|
18878
|
+
timeoutMs: config.acpConfig?.timeoutMs ?? 3e4,
|
|
18879
|
+
debug: this.debug
|
|
18880
|
+
});
|
|
18881
|
+
this.setupEventForwarding();
|
|
18882
|
+
this.log("LocalAgentConnection initialized");
|
|
18883
|
+
}
|
|
18884
|
+
setupEventForwarding() {
|
|
18885
|
+
this.acpClient.onSessionUpdate((params) => {
|
|
18886
|
+
this.emit("sessionUpdate", params.notification);
|
|
18887
|
+
});
|
|
18888
|
+
this.acpClient.onExtNotification((method, params) => {
|
|
18889
|
+
console.log("[LocalConnection] Received extNotification:", {
|
|
18890
|
+
method,
|
|
18891
|
+
paramsKeys: Object.keys(params)
|
|
18892
|
+
});
|
|
18893
|
+
if (method === "_codebuddy.ai/artifact") {
|
|
18894
|
+
const event = params.event;
|
|
18895
|
+
const artifact = params.artifact;
|
|
18896
|
+
const notificationSessionId = params.sessionId;
|
|
18897
|
+
if (artifact?.uri) {
|
|
18898
|
+
if (event === "created" || event === "updated") this.artifactCache.set(artifact.uri, artifact);
|
|
18899
|
+
else if (event === "deleted") this.artifactCache.delete(artifact.uri);
|
|
18900
|
+
}
|
|
18901
|
+
if (artifact && notificationSessionId) artifact.__sessionId = notificationSessionId;
|
|
18902
|
+
if (event === "created") this.emit("artifactCreated", artifact);
|
|
18903
|
+
else if (event === "updated") this.emit("artifactUpdated", artifact);
|
|
18904
|
+
else if (event === "deleted") this.emit("artifactDeleted", artifact);
|
|
18905
|
+
}
|
|
18906
|
+
if (method === "_codebuddy.ai/checkpoint") {
|
|
18907
|
+
const event = params.event;
|
|
18908
|
+
const checkpoint = params.checkpoint;
|
|
18909
|
+
const checkpointSessionId = params.sessionId;
|
|
18910
|
+
console.log("[LocalConnection] Emitting checkpoint event:", {
|
|
18911
|
+
event,
|
|
18912
|
+
checkpointId: checkpoint?.id,
|
|
18913
|
+
checkpointSessionId,
|
|
18914
|
+
filesCount: checkpoint?.fileChanges?.files?.length ?? 0
|
|
18915
|
+
});
|
|
18916
|
+
if (checkpoint && checkpointSessionId) checkpoint.__sessionId = checkpointSessionId;
|
|
18917
|
+
if (event === "created") this.emit("checkpointCreated", checkpoint);
|
|
18918
|
+
else if (event === "updated") this.emit("checkpointUpdated", checkpoint);
|
|
18919
|
+
}
|
|
18920
|
+
if (method === ExtensionMethod.COMMAND) {
|
|
18921
|
+
const action = params.action;
|
|
18922
|
+
const commandParams = params.params;
|
|
18923
|
+
const commandSessionId = params.sessionId;
|
|
18924
|
+
console.log("[LocalConnection] Emitting command event:", {
|
|
18925
|
+
action,
|
|
18926
|
+
commandSessionId,
|
|
18927
|
+
paramsKeys: commandParams ? Object.keys(commandParams) : []
|
|
18928
|
+
});
|
|
18929
|
+
const commandData = {
|
|
18930
|
+
action,
|
|
18931
|
+
params: commandParams
|
|
18932
|
+
};
|
|
18933
|
+
if (commandSessionId) commandData.__sessionId = commandSessionId;
|
|
18934
|
+
this.emit("command", commandData);
|
|
18935
|
+
}
|
|
18936
|
+
});
|
|
18937
|
+
this.log("Event forwarding setup complete");
|
|
18938
|
+
}
|
|
18939
|
+
on(event, listener) {
|
|
18940
|
+
if (!this.listeners.has(event)) this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
18941
|
+
this.listeners.get(event).add(listener);
|
|
18942
|
+
return this;
|
|
18943
|
+
}
|
|
18944
|
+
off(event, listener) {
|
|
18945
|
+
const eventListeners = this.listeners.get(event);
|
|
18946
|
+
if (eventListeners) eventListeners.delete(listener);
|
|
18947
|
+
const onceEventListeners = this.onceListeners.get(event);
|
|
18948
|
+
if (onceEventListeners) onceEventListeners.delete(listener);
|
|
18949
|
+
return this;
|
|
18950
|
+
}
|
|
18951
|
+
once(event, listener) {
|
|
18952
|
+
if (!this.onceListeners.has(event)) this.onceListeners.set(event, /* @__PURE__ */ new Set());
|
|
18953
|
+
this.onceListeners.get(event).add(listener);
|
|
18954
|
+
return this;
|
|
18955
|
+
}
|
|
18956
|
+
emit(event, data) {
|
|
18957
|
+
const regularListeners = this.listeners.get(event);
|
|
18958
|
+
const onceEventListeners = this.onceListeners.get(event);
|
|
18959
|
+
if (event === "sessionUpdate") {
|
|
18960
|
+
const updateType = data?.update?.sessionUpdate;
|
|
18961
|
+
const notifSessionId = data?.sessionId;
|
|
18962
|
+
if ((regularListeners?.size ?? 0) + (onceEventListeners?.size ?? 0) === 0) console.warn(`[RT-DEBUG][LocalConn] emit sessionUpdate NO LISTENERS: type=${updateType}, sessionId=${notifSessionId?.substring(0, 8)}, cwd=${this.cwd?.substring(this.cwd.length - 20)}`);
|
|
18963
|
+
}
|
|
18964
|
+
let hasListeners = false;
|
|
18965
|
+
if (regularListeners && regularListeners.size > 0) {
|
|
18966
|
+
hasListeners = true;
|
|
18967
|
+
for (const listener of regularListeners) try {
|
|
18968
|
+
const result = listener(data);
|
|
18969
|
+
if (result instanceof Promise) result.catch((err) => {
|
|
18970
|
+
console.error(`Error in async event listener for '${String(event)}':`, err);
|
|
18971
|
+
});
|
|
18972
|
+
} catch (err) {
|
|
18973
|
+
console.error(`Error in event listener for '${String(event)}':`, err);
|
|
18974
|
+
}
|
|
18975
|
+
}
|
|
18976
|
+
if (onceEventListeners && onceEventListeners.size > 0) {
|
|
18977
|
+
hasListeners = true;
|
|
18978
|
+
const listenersToCall = Array.from(onceEventListeners);
|
|
18979
|
+
this.onceListeners.delete(event);
|
|
18980
|
+
for (const listener of listenersToCall) try {
|
|
18981
|
+
const result = listener(data);
|
|
18982
|
+
if (result instanceof Promise) result.catch((err) => {
|
|
18983
|
+
console.error(`Error in async once event listener for '${String(event)}':`, err);
|
|
18984
|
+
});
|
|
18985
|
+
} catch (err) {
|
|
18986
|
+
console.error(`Error in once event listener for '${String(event)}':`, err);
|
|
18987
|
+
}
|
|
18988
|
+
}
|
|
18989
|
+
return hasListeners;
|
|
18990
|
+
}
|
|
18991
|
+
removeAllListeners(event) {
|
|
18992
|
+
if (event !== void 0) {
|
|
18993
|
+
this.listeners.delete(event);
|
|
18994
|
+
this.onceListeners.delete(event);
|
|
18995
|
+
} else {
|
|
18996
|
+
this.listeners.clear();
|
|
18997
|
+
this.onceListeners.clear();
|
|
18998
|
+
}
|
|
18999
|
+
return this;
|
|
19000
|
+
}
|
|
19001
|
+
get state() {
|
|
19002
|
+
return this._state;
|
|
19003
|
+
}
|
|
19004
|
+
get isInitialized() {
|
|
19005
|
+
return this._isInitialized;
|
|
19006
|
+
}
|
|
19007
|
+
get capabilities() {
|
|
19008
|
+
return this._capabilities;
|
|
19009
|
+
}
|
|
19010
|
+
get initializeResult() {
|
|
19011
|
+
return this._initializeResult;
|
|
19012
|
+
}
|
|
19013
|
+
async connect(_clientCapabilities) {
|
|
19014
|
+
this.log("Connecting...");
|
|
19015
|
+
this._state = "connecting";
|
|
19016
|
+
this.emit("connecting", void 0);
|
|
19017
|
+
this._isInitialized = true;
|
|
19018
|
+
this._initializeResult = { protocolVersion: _agentclientprotocol_sdk.PROTOCOL_VERSION };
|
|
19019
|
+
this.emit("connected", void 0);
|
|
19020
|
+
this._state = "initialized";
|
|
19021
|
+
return this._initializeResult;
|
|
19022
|
+
}
|
|
19023
|
+
disconnect() {
|
|
19024
|
+
this.log("Disconnecting...");
|
|
19025
|
+
this.cleanupPendingPermissionsOnDisconnect();
|
|
19026
|
+
this._state = "disconnected";
|
|
19027
|
+
this._isInitialized = false;
|
|
19028
|
+
this.acpClient.destroy();
|
|
19029
|
+
this.emit("disconnected", void 0);
|
|
19030
|
+
this.log("Disconnected");
|
|
19031
|
+
}
|
|
19032
|
+
/**
|
|
19033
|
+
* 清理断开连接时所有待处理的权限请求
|
|
19034
|
+
* 在连接断开时主动清理所有 pending 权限请求并返回 cancelled
|
|
19035
|
+
*/
|
|
19036
|
+
cleanupPendingPermissionsOnDisconnect() {
|
|
19037
|
+
const count = this.pendingPermissions.size;
|
|
19038
|
+
if (count === 0) return;
|
|
19039
|
+
for (const [requestId] of this.pendingPermissions) {
|
|
19040
|
+
const resolver = this[`_permissionResolver_${requestId}`];
|
|
19041
|
+
if (resolver) {
|
|
19042
|
+
resolver({ outcome: "cancelled" });
|
|
19043
|
+
delete this[`_permissionResolver_${requestId}`];
|
|
19044
|
+
}
|
|
19045
|
+
}
|
|
19046
|
+
this.pendingPermissions.clear();
|
|
19047
|
+
this.log(`Cleaned up ${count} pending permission request(s) on disconnect`);
|
|
19048
|
+
}
|
|
19049
|
+
async createSession(params) {
|
|
19050
|
+
this.log("Creating session with cwd:", params.cwd, params._meta);
|
|
19051
|
+
const response = await this.acpClient.newSession({
|
|
19052
|
+
_meta: params._meta,
|
|
19053
|
+
cwd: params.cwd,
|
|
19054
|
+
mcpServers: params.mcpServers ?? []
|
|
19055
|
+
});
|
|
19056
|
+
this.bindPermissionRequest(response.sessionId);
|
|
19057
|
+
return response;
|
|
19058
|
+
}
|
|
19059
|
+
async loadSession(params) {
|
|
19060
|
+
if (!params.sessionId) throw new Error("sessionId is required for loadSession");
|
|
19061
|
+
this.log(`[LocalAgentConnection] loadSession: ${params.sessionId}`);
|
|
19062
|
+
const response = await this.acpClient.loadSession({
|
|
19063
|
+
sessionId: params.sessionId,
|
|
19064
|
+
cwd: params.cwd,
|
|
19065
|
+
mcpServers: params.mcpServers ?? []
|
|
19066
|
+
});
|
|
19067
|
+
this.log(`[LocalAgentConnection] loadSession response:${response}`);
|
|
19068
|
+
this.bindPermissionRequest(params.sessionId);
|
|
19069
|
+
return response;
|
|
19070
|
+
}
|
|
19071
|
+
async bindPermissionRequest(sessionId) {
|
|
19072
|
+
if (this.onRequest) this.onRequest();
|
|
19073
|
+
this.onRequest = this.acpClient.onRequestPermission(sessionId, async (params) => {
|
|
19074
|
+
this.pendingPermissions.set(params.requestId, {
|
|
19075
|
+
params,
|
|
19076
|
+
createdAt: Date.now()
|
|
19077
|
+
});
|
|
19078
|
+
const permissionRequest = {
|
|
19079
|
+
sessionId: params.sessionId,
|
|
19080
|
+
toolCall: params.toolCall,
|
|
19081
|
+
options: params.options
|
|
19082
|
+
};
|
|
19083
|
+
this.emit("permissionRequest", {
|
|
19084
|
+
requestId: params.requestId,
|
|
19085
|
+
params: permissionRequest
|
|
19086
|
+
});
|
|
19087
|
+
return new Promise((resolve) => {
|
|
19088
|
+
const checkResolution = () => {
|
|
19089
|
+
if (!this.pendingPermissions.has(params.requestId)) return;
|
|
19090
|
+
const pending = this.pendingPermissions.get(params.requestId);
|
|
19091
|
+
if (pending && Date.now() - pending.createdAt > this.permissionTimeout) {
|
|
19092
|
+
this.pendingPermissions.delete(params.requestId);
|
|
19093
|
+
this.emit("permissionTimeout", { requestId: params.requestId });
|
|
19094
|
+
if (this.permissionAutoRejectOnTimeout) resolve({ outcome: "cancelled" });
|
|
19095
|
+
}
|
|
19096
|
+
};
|
|
19097
|
+
this[`_permissionResolver_${params.requestId}`] = resolve;
|
|
19098
|
+
setTimeout(checkResolution, this.permissionTimeout);
|
|
19099
|
+
});
|
|
19100
|
+
});
|
|
19101
|
+
}
|
|
19102
|
+
async setSessionMode(sessionId, modeId) {
|
|
19103
|
+
this.log("Setting session mode:", sessionId, "to", modeId);
|
|
19104
|
+
return await this.acpClient.setSessionMode({
|
|
19105
|
+
sessionId,
|
|
19106
|
+
modeId
|
|
19107
|
+
});
|
|
19108
|
+
}
|
|
19109
|
+
async setSessionModel(sessionId, modelId) {
|
|
19110
|
+
this.log("Setting session model:", sessionId, "to", modelId);
|
|
19111
|
+
return await this.acpClient.setSessionModel({
|
|
19112
|
+
sessionId,
|
|
19113
|
+
modelId
|
|
19114
|
+
});
|
|
19115
|
+
}
|
|
19116
|
+
/**
|
|
19117
|
+
* 打开工作区窗口
|
|
19118
|
+
* 使用 __workspace__ session ID,由 Main Process 直接处理,不转发到 ExtensionHost
|
|
19119
|
+
* @param params 打开工作区请求参数
|
|
19120
|
+
* @returns 打开工作区响应
|
|
19121
|
+
*/
|
|
19122
|
+
async openWorkspace(params) {
|
|
19123
|
+
this.log("Opening workspace:", params.cwd);
|
|
19124
|
+
return await this.acpClient.openWorkspace(params);
|
|
19125
|
+
}
|
|
19126
|
+
async prompt(sessionId, params) {
|
|
19127
|
+
const prompt = typeof params.content === "string" ? [{
|
|
19128
|
+
type: "text",
|
|
19129
|
+
text: params.content
|
|
19130
|
+
}] : params.content;
|
|
19131
|
+
this.log("Sending prompt to session:", sessionId);
|
|
19132
|
+
return await this.acpClient.prompt({
|
|
19133
|
+
sessionId,
|
|
19134
|
+
prompt,
|
|
19135
|
+
_meta: {
|
|
19136
|
+
...params._meta,
|
|
19137
|
+
planMode: params.planMode
|
|
19138
|
+
}
|
|
19139
|
+
});
|
|
19140
|
+
}
|
|
19141
|
+
async *promptStream(_sessionId, _params) {
|
|
19142
|
+
throw new Error("promptStream not implemented for Local connection");
|
|
19143
|
+
}
|
|
19144
|
+
async cancel(sessionId) {
|
|
19145
|
+
this.log("Cancelling session:", sessionId);
|
|
19146
|
+
await this.acpClient.cancel({ sessionId });
|
|
19147
|
+
}
|
|
19148
|
+
getArtifacts() {
|
|
19149
|
+
return new Map(this.artifactCache);
|
|
19150
|
+
}
|
|
19151
|
+
getArtifact(uri) {
|
|
19152
|
+
return this.artifactCache.get(uri);
|
|
19153
|
+
}
|
|
19154
|
+
getArtifactsByType(type) {
|
|
19155
|
+
return Array.from(this.artifactCache.values()).filter((artifact) => artifact.type === type);
|
|
19156
|
+
}
|
|
19157
|
+
async fetchArtifactContent(_artifact) {
|
|
19158
|
+
throw new Error("fetchArtifactContent not implemented for Local connection");
|
|
19159
|
+
}
|
|
19160
|
+
async fetchArtifactContentById(_id) {
|
|
19161
|
+
throw new Error("fetchArtifactContentById not implemented for Local connection");
|
|
19162
|
+
}
|
|
19163
|
+
resolvePermission(requestId, optionId) {
|
|
19164
|
+
const resolver = this[`_permissionResolver_${requestId}`];
|
|
19165
|
+
if (resolver) {
|
|
19166
|
+
this.pendingPermissions.delete(requestId);
|
|
19167
|
+
delete this[`_permissionResolver_${requestId}`];
|
|
19168
|
+
resolver({
|
|
19169
|
+
outcome: "selected",
|
|
19170
|
+
optionId
|
|
19171
|
+
});
|
|
19172
|
+
this.emit("permissionResolved", {
|
|
19173
|
+
requestId,
|
|
19174
|
+
optionId
|
|
19175
|
+
});
|
|
19176
|
+
return true;
|
|
19177
|
+
}
|
|
19178
|
+
return false;
|
|
19179
|
+
}
|
|
19180
|
+
rejectPermission(requestId, reason) {
|
|
19181
|
+
const resolver = this[`_permissionResolver_${requestId}`];
|
|
19182
|
+
if (resolver) {
|
|
19183
|
+
this.pendingPermissions.delete(requestId);
|
|
19184
|
+
delete this[`_permissionResolver_${requestId}`];
|
|
19185
|
+
resolver({ outcome: "cancelled" });
|
|
19186
|
+
this.emit("permissionRejected", {
|
|
19187
|
+
requestId,
|
|
19188
|
+
reason
|
|
19189
|
+
});
|
|
19190
|
+
return true;
|
|
19191
|
+
}
|
|
19192
|
+
return false;
|
|
19193
|
+
}
|
|
19194
|
+
getPendingPermissions() {
|
|
19195
|
+
const result = /* @__PURE__ */ new Map();
|
|
19196
|
+
for (const [requestId, pending] of this.pendingPermissions) result.set(requestId, {
|
|
19197
|
+
params: {
|
|
19198
|
+
sessionId: pending.params.sessionId,
|
|
19199
|
+
toolCall: pending.params.toolCall,
|
|
19200
|
+
options: pending.params.options
|
|
19201
|
+
},
|
|
19202
|
+
createdAt: pending.createdAt
|
|
19203
|
+
});
|
|
19204
|
+
return result;
|
|
19205
|
+
}
|
|
19206
|
+
hasPendingPermissions() {
|
|
19207
|
+
return this.pendingPermissions.size > 0;
|
|
19208
|
+
}
|
|
19209
|
+
answerQuestion(_toolCallId, _answers) {
|
|
19210
|
+
return false;
|
|
19211
|
+
}
|
|
19212
|
+
/**
|
|
19213
|
+
* 工具回调操作
|
|
19214
|
+
* 用于对正在执行的工具进行 approve / skip / cancel 操作
|
|
19215
|
+
* @param sessionId 会话 ID
|
|
19216
|
+
* @param toolCallId 工具调用 ID
|
|
19217
|
+
* @param toolName 工具名称
|
|
19218
|
+
* @param action 操作类型 ('approve' | 'skip' | 'cancel')
|
|
19219
|
+
* @returns 工具回调响应
|
|
19220
|
+
*/
|
|
19221
|
+
async toolCallback(sessionId, toolCallId, toolName, action) {
|
|
19222
|
+
this.log("toolCallback called for session:", sessionId, "action:", action);
|
|
19223
|
+
const request = {
|
|
19224
|
+
sessionId,
|
|
19225
|
+
toolCallId,
|
|
19226
|
+
toolName,
|
|
19227
|
+
action
|
|
19228
|
+
};
|
|
19229
|
+
return await this.acpClient.toolCallback(request);
|
|
19230
|
+
}
|
|
19231
|
+
cancelQuestion(_toolCallId, _reason) {
|
|
19232
|
+
return false;
|
|
19233
|
+
}
|
|
19234
|
+
getPendingQuestions() {
|
|
19235
|
+
return /* @__PURE__ */ new Map();
|
|
19236
|
+
}
|
|
19237
|
+
hasPendingQuestions() {
|
|
19238
|
+
return false;
|
|
19239
|
+
}
|
|
19240
|
+
async reportTelemetry(eventName, payload) {
|
|
19241
|
+
try {
|
|
19242
|
+
await this.acpClient.sendRequest(this.agentId, "reportTelemetry", {
|
|
19243
|
+
eventName,
|
|
19244
|
+
payload
|
|
19245
|
+
});
|
|
19246
|
+
} catch (error) {
|
|
19247
|
+
console.warn("[LocalAgentConnection] reportTelemetry failed:", error);
|
|
19248
|
+
}
|
|
19249
|
+
}
|
|
19250
|
+
async extMethod(_method, _params) {
|
|
19251
|
+
throw new Error("extMethod not implemented for Local connection");
|
|
19252
|
+
}
|
|
19253
|
+
async extNotification(_method, _params) {
|
|
19254
|
+
throw new Error("extNotification not implemented for Local connection");
|
|
19255
|
+
}
|
|
19256
|
+
async readFile(_path) {
|
|
19257
|
+
throw new Error("readFile not implemented for Local connection");
|
|
19258
|
+
}
|
|
19259
|
+
async listDir(_path) {
|
|
19260
|
+
throw new Error("listDir not implemented for Local connection");
|
|
19261
|
+
}
|
|
19262
|
+
async fileExists(_path) {
|
|
19263
|
+
throw new Error("fileExists not implemented for Local connection");
|
|
19264
|
+
}
|
|
19265
|
+
async fileStat(_path) {
|
|
19266
|
+
throw new Error("fileStat not implemented for Local connection");
|
|
19267
|
+
}
|
|
19268
|
+
log(...args) {
|
|
19269
|
+
console.log("[LocalAgentConnection]", ...args);
|
|
19270
|
+
}
|
|
19271
|
+
};
|
|
19272
|
+
|
|
19273
|
+
//#endregion
|
|
19274
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/local-filesystem.ts
|
|
19275
|
+
/**
|
|
19276
|
+
* Filesystem JSON-RPC 方法名称
|
|
19277
|
+
*/
|
|
19278
|
+
const FILESYSTEM_METHODS = {
|
|
19279
|
+
READ: "fs/read",
|
|
19280
|
+
WRITE: "fs/write",
|
|
19281
|
+
LIST: "fs/list",
|
|
19282
|
+
EXISTS: "fs/exists",
|
|
19283
|
+
MAKE_DIR: "fs/makeDir",
|
|
19284
|
+
REMOVE: "fs/remove",
|
|
19285
|
+
RENAME: "fs/rename",
|
|
19286
|
+
GET_INFO: "fs/getInfo",
|
|
19287
|
+
WATCH_DIR: "fs/watchDir",
|
|
19288
|
+
UNWATCH: "fs/unwatch"
|
|
19289
|
+
};
|
|
19290
|
+
/**
|
|
19291
|
+
* Watch 句柄 ID 生成计数器
|
|
19292
|
+
*/
|
|
19293
|
+
let watchIdCounter = 0;
|
|
19294
|
+
/**
|
|
19295
|
+
* LocalFilesystem
|
|
19296
|
+
*
|
|
19297
|
+
* 实现 FilesResource 接口,将 filesystem 操作通过 JSON-RPC 发送到 ExtensionHost
|
|
19298
|
+
*/
|
|
19299
|
+
var LocalFilesystem = class {
|
|
19300
|
+
constructor(client, sessionId, options) {
|
|
19301
|
+
this.watchCallbacks = /* @__PURE__ */ new Map();
|
|
19302
|
+
this.client = client;
|
|
19303
|
+
this.sessionId = sessionId;
|
|
19304
|
+
this.debug = options?.debug ?? false;
|
|
19305
|
+
this.setupEventListener();
|
|
19306
|
+
}
|
|
19307
|
+
/**
|
|
19308
|
+
* 设置 filesystem 事件监听(用于 watchDir)
|
|
19309
|
+
*/
|
|
19310
|
+
setupEventListener() {
|
|
19311
|
+
const dispose = this.client.onExtNotification((method, params) => {
|
|
19312
|
+
if (method !== "fs-event") return;
|
|
19313
|
+
const data = params;
|
|
19314
|
+
if (data?.watchId) {
|
|
19315
|
+
const callback = this.watchCallbacks.get(data.watchId);
|
|
19316
|
+
if (callback) callback({
|
|
19317
|
+
type: data.eventType,
|
|
19318
|
+
name: data.name,
|
|
19319
|
+
path: data.path
|
|
19320
|
+
});
|
|
19321
|
+
}
|
|
19322
|
+
});
|
|
19323
|
+
this.eventCleanup = () => dispose?.();
|
|
19324
|
+
}
|
|
19325
|
+
/**
|
|
19326
|
+
* 发送 filesystem 请求到 ExtensionHost
|
|
19327
|
+
*/
|
|
19328
|
+
async sendRequest(method, params) {
|
|
19329
|
+
console.log(`[LocalFilesystem] sendRequest: ${method}`, params);
|
|
19330
|
+
try {
|
|
19331
|
+
const result = await this.client.sendRequest(this.sessionId, method, params);
|
|
19332
|
+
if (result && typeof result === "object") {
|
|
19333
|
+
if ("error" in result && result.error) throw new Error(result.error);
|
|
19334
|
+
if ("data" in result) return result.data;
|
|
19335
|
+
}
|
|
19336
|
+
return result;
|
|
19337
|
+
} catch (error) {
|
|
19338
|
+
console.error("[LocalFilesystem] error:", error);
|
|
19339
|
+
throw error;
|
|
19340
|
+
}
|
|
19341
|
+
}
|
|
19342
|
+
async read(path, opts) {
|
|
19343
|
+
const format = opts?.format ?? "text";
|
|
19344
|
+
if (format === "text") return this.sendRequest(FILESYSTEM_METHODS.READ, {
|
|
19345
|
+
path,
|
|
19346
|
+
opts,
|
|
19347
|
+
format: "text"
|
|
19348
|
+
});
|
|
19349
|
+
const base64Data = await this.sendRequest(FILESYSTEM_METHODS.READ, {
|
|
19350
|
+
path,
|
|
19351
|
+
opts,
|
|
19352
|
+
format: "bytes"
|
|
19353
|
+
});
|
|
19354
|
+
const binaryString = atob(base64Data);
|
|
19355
|
+
const bytes = new Uint8Array(binaryString.length);
|
|
19356
|
+
for (let i = 0; i < binaryString.length; i++) bytes[i] = binaryString.charCodeAt(i);
|
|
19357
|
+
if (format === "bytes") return bytes;
|
|
19358
|
+
else if (format === "blob") return new Blob([bytes]);
|
|
19359
|
+
else return new ReadableStream({ start(controller) {
|
|
19360
|
+
controller.enqueue(bytes);
|
|
19361
|
+
controller.close();
|
|
19362
|
+
} });
|
|
19363
|
+
}
|
|
19364
|
+
async write(pathOrFiles, dataOrOpts, opts) {
|
|
19365
|
+
console.log("[LocalFilesystem] write() called:", {
|
|
19366
|
+
sessionId: this.sessionId,
|
|
19367
|
+
isArray: Array.isArray(pathOrFiles),
|
|
19368
|
+
pathOrFiles: Array.isArray(pathOrFiles) ? `${pathOrFiles.length} files` : pathOrFiles
|
|
19369
|
+
});
|
|
19370
|
+
if (Array.isArray(pathOrFiles)) {
|
|
19371
|
+
const files = await Promise.all(pathOrFiles.map(async (entry) => ({
|
|
19372
|
+
path: entry.path,
|
|
19373
|
+
data: await this.convertDataToString(entry.data)
|
|
19374
|
+
})));
|
|
19375
|
+
return this.sendRequest(FILESYSTEM_METHODS.WRITE, {
|
|
19376
|
+
path: "",
|
|
19377
|
+
files,
|
|
19378
|
+
opts: dataOrOpts
|
|
19379
|
+
});
|
|
19380
|
+
} else {
|
|
19381
|
+
const dataStr = await this.convertDataToString(dataOrOpts);
|
|
19382
|
+
return this.sendRequest(FILESYSTEM_METHODS.WRITE, {
|
|
19383
|
+
path: pathOrFiles,
|
|
19384
|
+
data: dataStr,
|
|
19385
|
+
opts
|
|
19386
|
+
});
|
|
19387
|
+
}
|
|
19388
|
+
}
|
|
19389
|
+
/**
|
|
19390
|
+
* 将数据转换为字符串(用于传输)
|
|
19391
|
+
*/
|
|
19392
|
+
async convertDataToString(data) {
|
|
19393
|
+
if (typeof data === "string") return data;
|
|
19394
|
+
else if (data instanceof ArrayBuffer) return this.arrayBufferToBase64(data);
|
|
19395
|
+
else if (data instanceof Blob) return this.blobToBase64(data);
|
|
19396
|
+
else if (data && typeof data.getReader === "function") {
|
|
19397
|
+
const reader = data.getReader();
|
|
19398
|
+
const chunks = [];
|
|
19399
|
+
let done = false;
|
|
19400
|
+
while (!done) {
|
|
19401
|
+
const result = await reader.read();
|
|
19402
|
+
done = result.done;
|
|
19403
|
+
if (result.value) chunks.push(result.value);
|
|
19404
|
+
}
|
|
19405
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
19406
|
+
const combined = new Uint8Array(totalLength);
|
|
19407
|
+
let offset = 0;
|
|
19408
|
+
for (const chunk of chunks) {
|
|
19409
|
+
combined.set(chunk, offset);
|
|
19410
|
+
offset += chunk.length;
|
|
19411
|
+
}
|
|
19412
|
+
return this.arrayBufferToBase64(combined.buffer);
|
|
19413
|
+
}
|
|
19414
|
+
return String(data);
|
|
19415
|
+
}
|
|
19416
|
+
/**
|
|
19417
|
+
* 列出目录内容
|
|
19418
|
+
*/
|
|
19419
|
+
async list(path, opts) {
|
|
19420
|
+
return this.sendRequest(FILESYSTEM_METHODS.LIST, {
|
|
19421
|
+
path,
|
|
19422
|
+
opts,
|
|
19423
|
+
depth: opts?.depth
|
|
19424
|
+
});
|
|
19425
|
+
}
|
|
19426
|
+
/**
|
|
19427
|
+
* 检查路径是否存在
|
|
19428
|
+
*/
|
|
19429
|
+
async exists(path, opts) {
|
|
19430
|
+
return this.sendRequest(FILESYSTEM_METHODS.EXISTS, {
|
|
19431
|
+
path,
|
|
19432
|
+
opts
|
|
19433
|
+
});
|
|
19434
|
+
}
|
|
19435
|
+
/**
|
|
19436
|
+
* 创建目录
|
|
19437
|
+
*/
|
|
19438
|
+
async makeDir(path, opts) {
|
|
19439
|
+
return this.sendRequest(FILESYSTEM_METHODS.MAKE_DIR, {
|
|
19440
|
+
path,
|
|
19441
|
+
opts
|
|
19442
|
+
});
|
|
19443
|
+
}
|
|
19444
|
+
/**
|
|
19445
|
+
* 删除文件或目录
|
|
19446
|
+
*/
|
|
19447
|
+
async remove(path, opts) {
|
|
19448
|
+
await this.sendRequest(FILESYSTEM_METHODS.REMOVE, {
|
|
19449
|
+
path,
|
|
19450
|
+
opts
|
|
19451
|
+
});
|
|
19452
|
+
}
|
|
19453
|
+
/**
|
|
19454
|
+
* 重命名/移动文件或目录
|
|
19455
|
+
*/
|
|
19456
|
+
async rename(oldPath, newPath, opts) {
|
|
19457
|
+
return this.sendRequest(FILESYSTEM_METHODS.RENAME, {
|
|
19458
|
+
path: oldPath,
|
|
19459
|
+
newPath,
|
|
19460
|
+
opts
|
|
19461
|
+
});
|
|
19462
|
+
}
|
|
19463
|
+
/**
|
|
19464
|
+
* 获取文件或目录信息
|
|
19465
|
+
*/
|
|
19466
|
+
async getInfo(path, opts) {
|
|
19467
|
+
return this.sendRequest(FILESYSTEM_METHODS.GET_INFO, {
|
|
19468
|
+
path,
|
|
19469
|
+
opts
|
|
19470
|
+
});
|
|
19471
|
+
}
|
|
19472
|
+
/**
|
|
19473
|
+
* 监听目录变化
|
|
19474
|
+
*/
|
|
19475
|
+
async watchDir(path, onEvent, opts) {
|
|
19476
|
+
const watchId = `watch-${++watchIdCounter}-${Date.now()}`;
|
|
19477
|
+
this.watchCallbacks.set(watchId, onEvent);
|
|
19478
|
+
await this.sendRequest(FILESYSTEM_METHODS.WATCH_DIR, {
|
|
19479
|
+
path,
|
|
19480
|
+
recursive: opts?.recursive,
|
|
19481
|
+
opts: {
|
|
19482
|
+
...opts,
|
|
19483
|
+
user: watchId
|
|
19484
|
+
}
|
|
19485
|
+
});
|
|
19486
|
+
return { stop: async () => {
|
|
19487
|
+
this.watchCallbacks.delete(watchId);
|
|
19488
|
+
try {
|
|
19489
|
+
await this.sendRequest(FILESYSTEM_METHODS.UNWATCH, {
|
|
19490
|
+
path,
|
|
19491
|
+
opts: { user: watchId }
|
|
19492
|
+
});
|
|
19493
|
+
} catch (error) {
|
|
19494
|
+
this.log(`Failed to unwatch: ${path}`, error);
|
|
19495
|
+
}
|
|
19496
|
+
if (opts?.onExit) opts.onExit();
|
|
19497
|
+
} };
|
|
19498
|
+
}
|
|
19499
|
+
/**
|
|
19500
|
+
* ArrayBuffer 转 Base64
|
|
19501
|
+
*/
|
|
19502
|
+
arrayBufferToBase64(buffer) {
|
|
19503
|
+
const bytes = new Uint8Array(buffer);
|
|
19504
|
+
let binary = "";
|
|
19505
|
+
for (let i = 0; i < bytes.byteLength; i++) binary += String.fromCharCode(bytes[i]);
|
|
19506
|
+
return btoa(binary);
|
|
19507
|
+
}
|
|
19508
|
+
/**
|
|
19509
|
+
* Blob 转 Base64
|
|
19510
|
+
*/
|
|
19511
|
+
async blobToBase64(blob) {
|
|
19512
|
+
return new Promise((resolve, reject) => {
|
|
19513
|
+
const reader = new FileReader();
|
|
19514
|
+
reader.onloadend = () => {
|
|
19515
|
+
const result = reader.result;
|
|
19516
|
+
resolve(result.split(",")[1] || result);
|
|
19517
|
+
};
|
|
19518
|
+
reader.onerror = reject;
|
|
19519
|
+
reader.readAsDataURL(blob);
|
|
19520
|
+
});
|
|
19521
|
+
}
|
|
19522
|
+
/**
|
|
19523
|
+
* 日志输出
|
|
19524
|
+
*/
|
|
19525
|
+
log(...args) {
|
|
19526
|
+
if (this.debug) console.log("[LocalFilesystem]", ...args);
|
|
19527
|
+
}
|
|
19528
|
+
/**
|
|
19529
|
+
* 销毁实例,清理资源
|
|
19530
|
+
*/
|
|
19531
|
+
destroy() {
|
|
19532
|
+
this.watchCallbacks.clear();
|
|
19533
|
+
if (this.eventCleanup) {
|
|
19534
|
+
this.eventCleanup();
|
|
19535
|
+
this.eventCleanup = void 0;
|
|
19536
|
+
}
|
|
19537
|
+
}
|
|
19538
|
+
};
|
|
19539
|
+
|
|
19540
|
+
//#endregion
|
|
19541
|
+
//#region ../agent-provider/src/common/providers/local-agent-provider/local-provider.ts
|
|
19542
|
+
/**
|
|
19543
|
+
* LocalAgentProvider
|
|
19544
|
+
*
|
|
19545
|
+
* 实现统一两层模型的 Local 端适配:
|
|
19546
|
+
* - 第一层 (Window/Workspace): 由 IDE/用户管理,Provider 只负责调用 openWorkspace
|
|
19547
|
+
* - 第二层 (Session): 由 Provider 管理
|
|
19548
|
+
*
|
|
19549
|
+
* 核心方法语义:
|
|
19550
|
+
* - create(): 返回 cwd 作为 "agentId"(需先调用 prepareCreate 预设)
|
|
19551
|
+
* - get(id): 支持 cwd 和 sessionId 两种输入,返回 Session 状态
|
|
19552
|
+
* - list(): 返回所有 Sessions
|
|
19553
|
+
* - connect(id): 智能判断 id 类型,建立连接
|
|
19554
|
+
* - delete(id): 删除 Session
|
|
19555
|
+
*
|
|
19556
|
+
* 同时实现 FilesystemProvider 接口,提供文件系统访问能力
|
|
19557
|
+
*/
|
|
19558
|
+
var LocalAgentProvider = class LocalAgentProvider {
|
|
19559
|
+
static {
|
|
19560
|
+
this.ARCHIVED_SESSIONS_KEY = "genie-archived-session-ids";
|
|
19561
|
+
}
|
|
19562
|
+
/**
|
|
19563
|
+
* 从 localStorage 读取归档 session ID 集合
|
|
19564
|
+
*/
|
|
19565
|
+
getArchivedSessionIds() {
|
|
19566
|
+
try {
|
|
19567
|
+
const raw = localStorage.getItem(LocalAgentProvider.ARCHIVED_SESSIONS_KEY);
|
|
19568
|
+
return raw ? new Set(JSON.parse(raw)) : /* @__PURE__ */ new Set();
|
|
19569
|
+
} catch {
|
|
19570
|
+
return /* @__PURE__ */ new Set();
|
|
19571
|
+
}
|
|
19572
|
+
}
|
|
19573
|
+
/**
|
|
19574
|
+
* 将归档 session ID 集合保存到 localStorage
|
|
19575
|
+
*/
|
|
19576
|
+
saveArchivedSessionIds(ids) {
|
|
19577
|
+
try {
|
|
19578
|
+
localStorage.setItem(LocalAgentProvider.ARCHIVED_SESSIONS_KEY, JSON.stringify([...ids]));
|
|
19579
|
+
} catch {}
|
|
19580
|
+
}
|
|
19581
|
+
constructor(options) {
|
|
19582
|
+
this.connections = /* @__PURE__ */ new Map();
|
|
19583
|
+
this.sessionCwdMap = /* @__PURE__ */ new Map();
|
|
19584
|
+
this.filesystemCache = /* @__PURE__ */ new Map();
|
|
19585
|
+
this.samplingRequestCallbacks = /* @__PURE__ */ new Map();
|
|
19586
|
+
this.rootsRequestCallbacks = /* @__PURE__ */ new Map();
|
|
19587
|
+
this.mcpEventListenersSetup = false;
|
|
19588
|
+
if (!options.channel) throw new Error("Channel is required for LocalAgentProvider");
|
|
19589
|
+
this.options = options;
|
|
19590
|
+
this.channel = options.channel;
|
|
19591
|
+
this.sharedAcpClient = new AcpJsonRpcClient(this.channel, {
|
|
19592
|
+
timeoutMs: options.acpConfig?.timeoutMs ?? 3e4,
|
|
19593
|
+
debug: options.debug ?? false
|
|
19594
|
+
});
|
|
19595
|
+
this.sharedAcpClient.onExtNotification((method, params) => {
|
|
19596
|
+
if (method === "_codebuddy.ai/automation_snapshot") {
|
|
19597
|
+
const snapshot = params.snapshot;
|
|
19598
|
+
if (snapshot) this.channel.emit("automationSnapshotUpdate", snapshot);
|
|
19599
|
+
}
|
|
19600
|
+
if (method === "_codebuddy.ai/plugins_changed") this.channel.emit("pluginsChanged", params);
|
|
19601
|
+
if (method === "_codebuddy.ai/models_changed") this.channel.emit("modelsChanged", params);
|
|
19602
|
+
if (method === "_codebuddy.ai/product_config_changed") this.channel.emit("productConfigChanged", params);
|
|
19603
|
+
if (method === "_codebuddy.ai/identity_changed") {
|
|
19604
|
+
console.log("[LocalProvider] Received identity_changed extNotification, emitting identityChanged event:", JSON.stringify(params));
|
|
19605
|
+
this.channel.emit("identityChanged", params);
|
|
19606
|
+
}
|
|
19607
|
+
if (method === "_codebuddy.ai/mcp_servers_changed") this.channel.emit("mcpServersChanged", params);
|
|
19608
|
+
});
|
|
19609
|
+
}
|
|
19610
|
+
/**
|
|
19611
|
+
* 实现 AgentProvider.filesystem 属性
|
|
19612
|
+
* 返回 this,因为 LocalAgentProvider 本身实现了 FilesystemProvider 接口
|
|
19613
|
+
*/
|
|
19614
|
+
get filesystem() {
|
|
19615
|
+
return this;
|
|
19616
|
+
}
|
|
19617
|
+
/**
|
|
19618
|
+
* 创建 "Agent"
|
|
19619
|
+
*
|
|
19620
|
+
* 对于 Local,返回 cwd 作为 agentId
|
|
19621
|
+
* 后续 connect(cwd) 和 connection.createSession() 会使用它
|
|
19622
|
+
*
|
|
19623
|
+
* @param params - 会话创建参数,包含 cwd
|
|
19624
|
+
* @returns cwd 作为 "agentId"
|
|
19625
|
+
*/
|
|
19626
|
+
async create(params) {
|
|
19627
|
+
const cwd = params?.cwd;
|
|
19628
|
+
if (cwd === void 0) throw new Error("cwd is required for LocalAgentProvider.create()");
|
|
19629
|
+
this.log(`create() returning cwd as agentId: ${cwd}`);
|
|
19630
|
+
return cwd;
|
|
19631
|
+
}
|
|
19632
|
+
/**
|
|
19633
|
+
* 获取 Session 状态
|
|
19634
|
+
*
|
|
19635
|
+
* SessionManager.loadSession() 会用 sessionId 调用此方法
|
|
19636
|
+
* 需要返回包含 cwd 的状态,以便后续 connect()
|
|
19637
|
+
*
|
|
19638
|
+
* @param id - 可能是 sessionId 或 cwd
|
|
19639
|
+
* @returns Session 状态
|
|
19640
|
+
*/
|
|
19641
|
+
async get(id) {
|
|
19642
|
+
this.log(`get() called with: ${id}`);
|
|
19643
|
+
if (id.startsWith("/")) return {
|
|
19644
|
+
id,
|
|
19645
|
+
type: "local",
|
|
19646
|
+
status: "disconnected",
|
|
19647
|
+
cwd: id
|
|
19648
|
+
};
|
|
19649
|
+
const cachedCwd = this.sessionCwdMap.get(id);
|
|
19650
|
+
if (cachedCwd) return {
|
|
19651
|
+
id,
|
|
19652
|
+
type: "local",
|
|
19653
|
+
status: "connected",
|
|
19654
|
+
cwd: cachedCwd
|
|
19655
|
+
};
|
|
19656
|
+
try {
|
|
19657
|
+
const session = (await this.channel.callMethod("__backend__", {
|
|
19658
|
+
type: "backend",
|
|
19659
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19660
|
+
params: {
|
|
19661
|
+
type: "backend:get-session-request",
|
|
19662
|
+
params: { sessionId: id }
|
|
19663
|
+
}
|
|
19664
|
+
}, 5e3)).data?.session;
|
|
19665
|
+
if (!session) {
|
|
19666
|
+
this.log(`Session not found: ${id}`);
|
|
19667
|
+
return;
|
|
19668
|
+
}
|
|
19669
|
+
if (session.cwd) this.sessionCwdMap.set(id, session.cwd);
|
|
19670
|
+
return {
|
|
19671
|
+
id: session.sessionId,
|
|
19672
|
+
type: "local",
|
|
19673
|
+
status: session.status === "active" ? "connected" : "disconnected",
|
|
19674
|
+
cwd: session.cwd,
|
|
19675
|
+
name: session.name,
|
|
19676
|
+
createdAt: session.createdAt ? new Date(session.createdAt) : void 0
|
|
19677
|
+
};
|
|
19678
|
+
} catch (error) {
|
|
19679
|
+
this.log(`Failed to get session ${id}:`, error);
|
|
19680
|
+
return;
|
|
19681
|
+
}
|
|
19682
|
+
}
|
|
19683
|
+
/**
|
|
19684
|
+
* 列出所有 Sessions
|
|
19685
|
+
*
|
|
19686
|
+
* @param options - 可选的查询参数(过滤、排序、userId)
|
|
19687
|
+
* @returns Session 状态列表和分页信息
|
|
19688
|
+
*/
|
|
19689
|
+
async list(options) {
|
|
19690
|
+
this.log("list() called with options:", options);
|
|
19691
|
+
if (!options?.userId) {
|
|
19692
|
+
this.log("No userId provided, returning empty list");
|
|
19693
|
+
return {
|
|
19694
|
+
agents: [],
|
|
19695
|
+
pagination: {
|
|
19696
|
+
page: 1,
|
|
19697
|
+
size: 0,
|
|
19698
|
+
total: 0,
|
|
19699
|
+
totalPages: 0,
|
|
19700
|
+
hasNext: false,
|
|
19701
|
+
hasPrev: false
|
|
19702
|
+
}
|
|
19703
|
+
};
|
|
19704
|
+
}
|
|
19705
|
+
try {
|
|
19706
|
+
const sessions = (await this.channel.callMethod("__backend__", {
|
|
19707
|
+
type: "backend",
|
|
19708
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19709
|
+
params: {
|
|
19710
|
+
type: "backend:list-sessions-request",
|
|
19711
|
+
params: { userId: options.userId }
|
|
19712
|
+
}
|
|
19713
|
+
}, 5e3)).data?.sessions ?? [];
|
|
19714
|
+
this.log(`Found ${sessions.length} sessions for userId: ${options.userId}`);
|
|
19715
|
+
for (const session of sessions) if (session.sessionId && session.cwd) this.sessionCwdMap.set(session.sessionId, session.cwd);
|
|
19716
|
+
let result = sessions.map((session) => ({
|
|
19717
|
+
id: session.sessionId,
|
|
19718
|
+
type: "local",
|
|
19719
|
+
status: session.status,
|
|
19720
|
+
cwd: session.cwd,
|
|
19721
|
+
name: session.name,
|
|
19722
|
+
createdAt: session.createdAt ? new Date(session.createdAt) : void 0,
|
|
19723
|
+
isPlayground: session.isPlayground,
|
|
19724
|
+
isUserDefinedTitle: session.isUserDefinedTitle
|
|
19725
|
+
}));
|
|
19726
|
+
const archivedIds = this.getArchivedSessionIds();
|
|
19727
|
+
const isQueryingArchived = options?.filters?.some((f) => f.field === "status" && f.value.split(",").map((v) => v.trim()).includes("archived"));
|
|
19728
|
+
if (isQueryingArchived) result = result.filter((s) => archivedIds.has(s.id)).map((s) => ({
|
|
19729
|
+
...s,
|
|
19730
|
+
status: "archived"
|
|
19731
|
+
}));
|
|
19732
|
+
else result = result.filter((s) => !archivedIds.has(s.id));
|
|
19733
|
+
if (options) result = this.applyFilters(result, options, isQueryingArchived);
|
|
19734
|
+
return {
|
|
19735
|
+
agents: result,
|
|
19736
|
+
pagination: {
|
|
19737
|
+
page: 1,
|
|
19738
|
+
size: result.length,
|
|
19739
|
+
total: result.length,
|
|
19740
|
+
totalPages: 1,
|
|
19741
|
+
hasNext: false,
|
|
19742
|
+
hasPrev: false
|
|
19743
|
+
}
|
|
19744
|
+
};
|
|
19745
|
+
} catch (error) {
|
|
19746
|
+
this.log("Failed to list sessions:", error);
|
|
19747
|
+
return {
|
|
19748
|
+
agents: [],
|
|
19749
|
+
pagination: {
|
|
19750
|
+
page: 1,
|
|
19751
|
+
size: 0,
|
|
19752
|
+
total: 0,
|
|
19753
|
+
totalPages: 0,
|
|
19754
|
+
hasNext: false,
|
|
19755
|
+
hasPrev: false
|
|
19756
|
+
}
|
|
19757
|
+
};
|
|
19758
|
+
}
|
|
19759
|
+
}
|
|
19760
|
+
/**
|
|
19761
|
+
* 应用本地过滤和排序
|
|
19762
|
+
*/
|
|
19763
|
+
applyFilters(sessions, options, skipStatusArchived = false) {
|
|
19764
|
+
let filtered = [...sessions];
|
|
19765
|
+
if (options.title) {
|
|
19766
|
+
const keyword = options.title.toLowerCase();
|
|
19767
|
+
filtered = filtered.filter((s) => s.name?.toLowerCase().includes(keyword));
|
|
19768
|
+
}
|
|
19769
|
+
if (options.filters?.length) for (const filter of options.filters) {
|
|
19770
|
+
if (skipStatusArchived && filter.field === "status" && filter.value.includes("archived")) continue;
|
|
19771
|
+
const values = filter.value.split(",").map((v) => v.trim());
|
|
19772
|
+
filtered = filtered.filter((s) => {
|
|
19773
|
+
const fieldValue = s[filter.field];
|
|
19774
|
+
if (filter.field === "sessionStatus") return values.includes(s.status.toLowerCase()) || values.includes(String(fieldValue));
|
|
19775
|
+
return values.includes(String(fieldValue));
|
|
19776
|
+
});
|
|
19777
|
+
}
|
|
19778
|
+
if (options.dayRange !== void 0) {
|
|
19779
|
+
const cutoff = /* @__PURE__ */ new Date();
|
|
19780
|
+
cutoff.setDate(cutoff.getDate() - (options.dayRange - 1));
|
|
19781
|
+
cutoff.setHours(0, 0, 0, 0);
|
|
19782
|
+
filtered = filtered.filter((s) => s.createdAt && s.createdAt >= cutoff);
|
|
19783
|
+
}
|
|
19784
|
+
if (options.sort) {
|
|
19785
|
+
const { orderBy, order = "asc" } = options.sort;
|
|
19786
|
+
filtered.sort((a, b) => {
|
|
19787
|
+
const aVal = a[orderBy];
|
|
19788
|
+
const bVal = b[orderBy];
|
|
19789
|
+
if (aVal === void 0) return 1;
|
|
19790
|
+
if (bVal === void 0) return -1;
|
|
19791
|
+
let cmp = 0;
|
|
19792
|
+
if (aVal instanceof Date && bVal instanceof Date) cmp = aVal.getTime() - bVal.getTime();
|
|
19793
|
+
else if (typeof aVal === "string" && typeof bVal === "string") cmp = aVal.localeCompare(bVal);
|
|
19794
|
+
else cmp = aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
19795
|
+
return order === "desc" ? -cmp : cmp;
|
|
19796
|
+
});
|
|
19797
|
+
}
|
|
19798
|
+
return filtered;
|
|
19799
|
+
}
|
|
19800
|
+
/**
|
|
19801
|
+
* 连接到工作区
|
|
19802
|
+
*
|
|
19803
|
+
* @param id - 可能是 cwd(createSession 流程)或 sessionId(loadSession 流程)
|
|
19804
|
+
* @returns LocalAgentConnection 实例
|
|
19805
|
+
*/
|
|
19806
|
+
async connect(id) {
|
|
19807
|
+
this.log(`connect() called with: ${id}`);
|
|
19808
|
+
let cwd;
|
|
19809
|
+
if (id.includes("/")) cwd = id;
|
|
19810
|
+
else {
|
|
19811
|
+
cwd = this.sessionCwdMap.get(id) || "";
|
|
19812
|
+
if (cwd === void 0) cwd = (await this.get(id))?.cwd || "";
|
|
19813
|
+
if (cwd === void 0) throw new Error(`Cannot find cwd for session: ${id}`);
|
|
19814
|
+
}
|
|
19815
|
+
this.log(`Resolved cwd: ${cwd}`);
|
|
19816
|
+
const existing = this.connections.get(cwd);
|
|
19817
|
+
if (existing?.isInitialized) {
|
|
19818
|
+
this.log(`Reusing existing connection for: ${cwd}`);
|
|
19819
|
+
return existing;
|
|
19820
|
+
}
|
|
19821
|
+
this.log(`Opening workspace: ${cwd}`);
|
|
19822
|
+
const clientCapabilities = {
|
|
19823
|
+
...this.options.clientCapabilities,
|
|
19824
|
+
_meta: {
|
|
19825
|
+
...this.options.clientCapabilities?._meta,
|
|
19826
|
+
"codebuddy.ai": {
|
|
19827
|
+
...this.options.clientCapabilities?._meta?.["codebuddy.ai"],
|
|
19828
|
+
cwd
|
|
19829
|
+
}
|
|
19830
|
+
}
|
|
19831
|
+
};
|
|
19832
|
+
const config = {
|
|
19833
|
+
channel: this.channel,
|
|
19834
|
+
debug: this.options.debug,
|
|
19835
|
+
acpConfig: this.options.acpConfig,
|
|
19836
|
+
permissionTimeout: this.options.permissionTimeout,
|
|
19837
|
+
permissionAutoRejectOnTimeout: this.options.permissionAutoRejectOnTimeout,
|
|
19838
|
+
clientCapabilities
|
|
19839
|
+
};
|
|
19840
|
+
const connection = new LocalAgentConnection(cwd, config);
|
|
19841
|
+
await connection.connect(clientCapabilities);
|
|
19842
|
+
this.connections.set(cwd, connection);
|
|
19843
|
+
connection.once("disconnected", () => {
|
|
19844
|
+
this.connections.delete(cwd);
|
|
19845
|
+
this.log(`Connection removed from cache: ${cwd}`);
|
|
19846
|
+
});
|
|
19847
|
+
this.log(`Connected to workspace: ${cwd}`);
|
|
19848
|
+
return connection;
|
|
19849
|
+
}
|
|
19850
|
+
/**
|
|
19851
|
+
* 删除 Session
|
|
19852
|
+
*
|
|
19853
|
+
* @param sessionId - 会话 ID
|
|
19854
|
+
* @returns 是否成功删除
|
|
19855
|
+
*/
|
|
19856
|
+
async delete(sessionId) {
|
|
19857
|
+
this.log(`delete() called with: ${sessionId}`);
|
|
19858
|
+
this.sessionCwdMap.delete(sessionId);
|
|
19859
|
+
try {
|
|
19860
|
+
return (await this.channel.callMethod("__backend__", {
|
|
19861
|
+
type: "backend",
|
|
19862
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19863
|
+
params: {
|
|
19864
|
+
type: "backend:delete-session-request",
|
|
19865
|
+
params: { sessionId }
|
|
19866
|
+
}
|
|
19867
|
+
}, 5e3)).data?.success ?? false;
|
|
19868
|
+
} catch (error) {
|
|
19869
|
+
this.log(`Failed to delete session ${sessionId}:`, error);
|
|
19870
|
+
return false;
|
|
19871
|
+
}
|
|
19872
|
+
}
|
|
19873
|
+
/**
|
|
19874
|
+
* 归档 Session
|
|
19875
|
+
*
|
|
19876
|
+
* 纯前端实现:将 sessionId 加入 localStorage 的归档集合,
|
|
19877
|
+
* 不删除后端数据,以便后续可恢复。
|
|
19878
|
+
*
|
|
19879
|
+
* @param sessionId - 会话 ID
|
|
19880
|
+
* @returns 包含归档的 session ID 的对象
|
|
19881
|
+
*/
|
|
19882
|
+
async archive(sessionId) {
|
|
19883
|
+
this.log(`archive() called with: ${sessionId}`);
|
|
19884
|
+
const archivedIds = this.getArchivedSessionIds();
|
|
19885
|
+
archivedIds.add(sessionId);
|
|
19886
|
+
this.saveArchivedSessionIds(archivedIds);
|
|
19887
|
+
return { id: sessionId };
|
|
19888
|
+
}
|
|
19889
|
+
/**
|
|
19890
|
+
* 更新 Session 状态
|
|
19891
|
+
*
|
|
19892
|
+
* 纯前端实现:当 status 不为 'archived' 时,将 sessionId 从归档集合中移除(恢复归档)。
|
|
19893
|
+
*
|
|
19894
|
+
* @param sessionId - 会话 ID
|
|
19895
|
+
* @param status - 新状态
|
|
19896
|
+
* @returns 包含 session ID 的对象
|
|
19897
|
+
*/
|
|
19898
|
+
async updateStatus(sessionId, status) {
|
|
19899
|
+
this.log(`updateStatus() called with: ${sessionId}, status: ${status}`);
|
|
19900
|
+
if (status !== "archived") {
|
|
19901
|
+
const archivedIds = this.getArchivedSessionIds();
|
|
19902
|
+
archivedIds.delete(sessionId);
|
|
19903
|
+
this.saveArchivedSessionIds(archivedIds);
|
|
19904
|
+
} else {
|
|
19905
|
+
const archivedIds = this.getArchivedSessionIds();
|
|
19906
|
+
archivedIds.add(sessionId);
|
|
19907
|
+
this.saveArchivedSessionIds(archivedIds);
|
|
19908
|
+
}
|
|
19909
|
+
return { id: sessionId };
|
|
19910
|
+
}
|
|
19911
|
+
/**
|
|
19912
|
+
* 重命名 Session
|
|
19913
|
+
*
|
|
19914
|
+
* @param sessionId - 会话 ID
|
|
19915
|
+
* @param title - 新标题
|
|
19916
|
+
* @returns 包含重命名的 session ID 的对象
|
|
19917
|
+
*/
|
|
19918
|
+
async rename(sessionId, title) {
|
|
19919
|
+
this.log(`rename() called with: ${sessionId}, title: ${title}`);
|
|
19920
|
+
try {
|
|
19921
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
19922
|
+
type: "backend",
|
|
19923
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19924
|
+
params: {
|
|
19925
|
+
type: "backend:rename-session-request",
|
|
19926
|
+
params: {
|
|
19927
|
+
sessionId,
|
|
19928
|
+
title
|
|
19929
|
+
}
|
|
19930
|
+
}
|
|
19931
|
+
}, 5e3);
|
|
19932
|
+
if (!response.data?.success) throw new Error(response.data?.error || "Failed to rename session");
|
|
19933
|
+
return { id: sessionId };
|
|
19934
|
+
} catch (error) {
|
|
19935
|
+
this.log(`Failed to rename session ${sessionId}:`, error);
|
|
19936
|
+
throw error;
|
|
19937
|
+
}
|
|
19938
|
+
}
|
|
19939
|
+
/**
|
|
19940
|
+
* 移动 Session
|
|
19941
|
+
* 将 Playground 会话转换为普通会话(从 Playground 移动到 Workspace)
|
|
19942
|
+
*
|
|
19943
|
+
* @param sessionId - 会话 ID
|
|
19944
|
+
* @returns 包含移动的 session ID 的对象
|
|
19945
|
+
*/
|
|
19946
|
+
async move(sessionId) {
|
|
19947
|
+
this.log(`move() called with: ${sessionId}`);
|
|
19948
|
+
try {
|
|
19949
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
19950
|
+
type: "backend",
|
|
19951
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
19952
|
+
params: {
|
|
19953
|
+
type: "backend:move-session-request",
|
|
19954
|
+
params: { sessionId }
|
|
19955
|
+
}
|
|
19956
|
+
}, 5e3);
|
|
19957
|
+
if (!response.data?.success) throw new Error(response.data?.error || "Failed to move session");
|
|
19958
|
+
this.log(`move() succeeded for: ${sessionId}`);
|
|
19959
|
+
return { id: sessionId };
|
|
19960
|
+
} catch (error) {
|
|
19961
|
+
this.log(`Failed to move session ${sessionId}:`, error);
|
|
19962
|
+
throw error;
|
|
19963
|
+
}
|
|
19964
|
+
}
|
|
19965
|
+
/**
|
|
19966
|
+
* 注册 sessionId → cwd 映射
|
|
19967
|
+
*
|
|
19968
|
+
* 在 connection.createSession() 成功后调用,记录新创建的 session
|
|
19969
|
+
*
|
|
19970
|
+
* @param sessionId - 会话 ID
|
|
19971
|
+
* @param cwd - 工作区路径
|
|
19972
|
+
*/
|
|
19973
|
+
registerSession(sessionId, cwd) {
|
|
19974
|
+
this.sessionCwdMap.set(sessionId, cwd);
|
|
19975
|
+
this.log(`Registered session: ${sessionId} → ${cwd}`);
|
|
19976
|
+
}
|
|
19977
|
+
/**
|
|
19978
|
+
* 获取可用模型列表
|
|
19979
|
+
*
|
|
19980
|
+
* 通过 ACP JSON-RPC 调用 getModels 方法
|
|
19981
|
+
* cwd 为空时会路由到 Default Window
|
|
19982
|
+
*
|
|
19983
|
+
* @param cwd - 工作目录,可选。为空时使用 Default Window
|
|
19984
|
+
* @param mode - 模式过滤,可选
|
|
19985
|
+
* @returns 模型列表
|
|
19986
|
+
*/
|
|
19987
|
+
async getModels(cwd, mode) {
|
|
19988
|
+
this.log(`getModels() called with cwd: ${cwd || "(empty)"}, mode: ${mode || "(default)"}`);
|
|
19989
|
+
try {
|
|
19990
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getModels", {
|
|
19991
|
+
cwd: cwd || "",
|
|
19992
|
+
mode
|
|
19993
|
+
});
|
|
19994
|
+
if (response?.models) {
|
|
19995
|
+
this.log(`getModels() returned ${response.models.length} models`);
|
|
19996
|
+
return response.models.map((model) => ({
|
|
19997
|
+
...model,
|
|
19998
|
+
...model._meta?.["codebuddy.ai"] || {}
|
|
19999
|
+
}));
|
|
20000
|
+
}
|
|
20001
|
+
this.log("getModels() - no models in response");
|
|
20002
|
+
return [];
|
|
20003
|
+
} catch (error) {
|
|
20004
|
+
this.log("getModels() failed:", error);
|
|
20005
|
+
return [];
|
|
20006
|
+
}
|
|
20007
|
+
}
|
|
20008
|
+
/**
|
|
20009
|
+
* 获取可用模式列表
|
|
20010
|
+
*
|
|
20011
|
+
* 通过 ACP JSON-RPC 调用 getModes 方法
|
|
20012
|
+
* cwd 为空时会路由到 Default Window
|
|
20013
|
+
*
|
|
20014
|
+
* @param cwd - 工作目录,可选。为空时使用 Default Window
|
|
20015
|
+
* @returns 模式列表
|
|
20016
|
+
*/
|
|
20017
|
+
async getModes(cwd) {
|
|
20018
|
+
this.log(`getModes() called with cwd: ${cwd || "(empty)"}`);
|
|
20019
|
+
try {
|
|
20020
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getModes", { cwd: cwd || "" });
|
|
20021
|
+
if (response?.modes) {
|
|
20022
|
+
this.log(`getModes() returned ${response.modes.length} modes`);
|
|
20023
|
+
return response.modes;
|
|
20024
|
+
}
|
|
20025
|
+
this.log("getModes() - no modes in response");
|
|
20026
|
+
return [];
|
|
20027
|
+
} catch (error) {
|
|
20028
|
+
this.log("getModes() failed:", error);
|
|
20029
|
+
return [];
|
|
20030
|
+
}
|
|
20031
|
+
}
|
|
20032
|
+
/**
|
|
20033
|
+
* 获取产品配置子集
|
|
20034
|
+
*
|
|
20035
|
+
* 通过 ACP JSON-RPC 调用 getProductConfiguration 方法
|
|
20036
|
+
* 返回 deploymentType、creditPurchaseActions、links 等配置
|
|
20037
|
+
*
|
|
20038
|
+
* @returns 产品配置子集
|
|
20039
|
+
*/
|
|
20040
|
+
async getProductConfiguration() {
|
|
20041
|
+
this.log("getProductConfiguration() called");
|
|
20042
|
+
try {
|
|
20043
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getProductConfiguration", {});
|
|
20044
|
+
this.log("getProductConfiguration() returned:", response);
|
|
20045
|
+
return response ?? {};
|
|
20046
|
+
} catch (error) {
|
|
20047
|
+
this.log("getProductConfiguration() failed:", error);
|
|
20048
|
+
return {};
|
|
20049
|
+
}
|
|
20050
|
+
}
|
|
20051
|
+
/**
|
|
20052
|
+
* 获取 Identity 名称
|
|
20053
|
+
* 读取 ~/.workbuddy/IDENTITY.md 中的 Name 字段,用于兼容 localStorage 尚未缓存的历史用户
|
|
20054
|
+
*/
|
|
20055
|
+
async getIdentityName() {
|
|
20056
|
+
try {
|
|
20057
|
+
return await this.sharedAcpClient.sendBroadcastRequest("getIdentityName", {}) ?? {};
|
|
20058
|
+
} catch (error) {
|
|
20059
|
+
this.log("getIdentityName() failed:", error);
|
|
20060
|
+
return {};
|
|
20061
|
+
}
|
|
20062
|
+
}
|
|
20063
|
+
/**
|
|
20064
|
+
* 获取用户信息
|
|
20065
|
+
*
|
|
20066
|
+
* 通过 ACP JSON-RPC 调用 getUserInfo 方法
|
|
20067
|
+
* 返回当前登录用户的 enterpriseId 等信息
|
|
20068
|
+
*
|
|
20069
|
+
* @returns 用户信息
|
|
20070
|
+
*/
|
|
20071
|
+
async getUserInfo() {
|
|
20072
|
+
this.log("getUserInfo() called");
|
|
20073
|
+
try {
|
|
20074
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getUserInfo", {});
|
|
20075
|
+
this.log("getUserInfo() returned:", response);
|
|
20076
|
+
return response ?? {};
|
|
20077
|
+
} catch (error) {
|
|
20078
|
+
this.log("getUserInfo() failed:", error);
|
|
20079
|
+
return {};
|
|
20080
|
+
}
|
|
20081
|
+
}
|
|
20082
|
+
/**
|
|
20083
|
+
* 打开工作区窗口
|
|
20084
|
+
* 这是 LocalAgentProvider 特有的能力,通过 shared ACP client 发送请求,
|
|
20085
|
+
* 使用 __workspace__ session ID,由 Main Process 直接处理,不转发到 ExtensionHost
|
|
20086
|
+
*
|
|
20087
|
+
* @param params 打开工作区请求参数
|
|
20088
|
+
* @returns 打开工作区响应
|
|
20089
|
+
*/
|
|
20090
|
+
async openWorkspace(params) {
|
|
20091
|
+
this.log("Opening workspace:", params.cwd);
|
|
20092
|
+
return await this.sharedAcpClient.openWorkspace(params);
|
|
20093
|
+
}
|
|
20094
|
+
/**
|
|
20095
|
+
* 选择文件
|
|
20096
|
+
* 通过 BackendService 的 __backend__ session 处理
|
|
20097
|
+
*
|
|
20098
|
+
* @param params 文件选择参数
|
|
20099
|
+
* @returns 选择结果
|
|
20100
|
+
*/
|
|
20101
|
+
async pickFile(params) {
|
|
20102
|
+
this.log("Picking file:", params);
|
|
20103
|
+
try {
|
|
20104
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
20105
|
+
type: "backend",
|
|
20106
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20107
|
+
params: {
|
|
20108
|
+
type: "backend:pick-file",
|
|
20109
|
+
params
|
|
20110
|
+
}
|
|
20111
|
+
}, 3e4);
|
|
20112
|
+
this.log("pickFile() response:", response.data);
|
|
20113
|
+
const data = response.data;
|
|
20114
|
+
return {
|
|
20115
|
+
files: data.filePaths || [],
|
|
20116
|
+
canceled: data.canceled ?? false,
|
|
20117
|
+
error: data.error
|
|
20118
|
+
};
|
|
20119
|
+
} catch (error) {
|
|
20120
|
+
this.log("pickFile() failed:", error);
|
|
20121
|
+
return {
|
|
20122
|
+
files: [],
|
|
20123
|
+
canceled: true,
|
|
20124
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20125
|
+
};
|
|
20126
|
+
}
|
|
20127
|
+
}
|
|
20128
|
+
/**
|
|
20129
|
+
* 选择文件夹
|
|
20130
|
+
* 通过 BackendService 的 __backend__ session 处理
|
|
20131
|
+
*
|
|
20132
|
+
* @param params 文件夹选择参数
|
|
20133
|
+
* @returns 选择结果
|
|
20134
|
+
*/
|
|
20135
|
+
async pickFolder(params) {
|
|
20136
|
+
this.log("Picking folder:", params);
|
|
20137
|
+
try {
|
|
20138
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
20139
|
+
type: "backend",
|
|
20140
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20141
|
+
params: {
|
|
20142
|
+
type: "backend:pick-folder",
|
|
20143
|
+
params
|
|
20144
|
+
}
|
|
20145
|
+
}, 3e4);
|
|
20146
|
+
this.log("pickFolder() response:", response.data);
|
|
20147
|
+
const data = response.data;
|
|
20148
|
+
return {
|
|
20149
|
+
folderPaths: data?.paths || [],
|
|
20150
|
+
canceled: data?.cancelled ?? true,
|
|
20151
|
+
error: data?.error
|
|
20152
|
+
};
|
|
20153
|
+
} catch (error) {
|
|
20154
|
+
this.log("pickFolder() failed:", error);
|
|
20155
|
+
return {
|
|
20156
|
+
folderPaths: [],
|
|
20157
|
+
canceled: true,
|
|
20158
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20159
|
+
};
|
|
20160
|
+
}
|
|
20161
|
+
}
|
|
20162
|
+
/**
|
|
20163
|
+
* 搜索文件
|
|
20164
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20165
|
+
* Extension Host 中的 FileSearchService 会处理这个请求
|
|
20166
|
+
*
|
|
20167
|
+
* @param params 搜索参数
|
|
20168
|
+
* @returns 搜索结果
|
|
20169
|
+
*/
|
|
20170
|
+
async searchFile(params) {
|
|
20171
|
+
this.log("Searching files:", params.options);
|
|
20172
|
+
try {
|
|
20173
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getWorkspaceFiles", {
|
|
20174
|
+
cwd: params.cwd,
|
|
20175
|
+
search: params.options.search,
|
|
20176
|
+
resultNum: params.options.resultNum
|
|
20177
|
+
});
|
|
20178
|
+
this.log("searchFile() response:", response);
|
|
20179
|
+
return {
|
|
20180
|
+
results: response.results.map((r) => ({
|
|
20181
|
+
...r,
|
|
20182
|
+
type: r.type
|
|
20183
|
+
})),
|
|
20184
|
+
error: void 0
|
|
20185
|
+
};
|
|
20186
|
+
} catch (error) {
|
|
20187
|
+
this.log("searchFile() failed:", error);
|
|
20188
|
+
return {
|
|
20189
|
+
results: [],
|
|
20190
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20191
|
+
};
|
|
20192
|
+
}
|
|
20193
|
+
}
|
|
20194
|
+
/**
|
|
20195
|
+
* 获取 Subagent 列表
|
|
20196
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20197
|
+
* Extension Host 中的 SubagentsService 会处理这个请求
|
|
20198
|
+
*
|
|
20199
|
+
* @param params 查询参数
|
|
20200
|
+
* @returns Subagent 列表
|
|
20201
|
+
*/
|
|
20202
|
+
async getSubagentList(params) {
|
|
20203
|
+
this.log("Getting subagent list:", params.options);
|
|
20204
|
+
try {
|
|
20205
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getSubagentList", {
|
|
20206
|
+
cwd: params.cwd,
|
|
20207
|
+
search: params.options.search,
|
|
20208
|
+
resultNum: params.options.resultNum,
|
|
20209
|
+
agentMode: params.options.agentMode || "all",
|
|
20210
|
+
onlyEnabled: params.options.onlyEnabled
|
|
20211
|
+
});
|
|
20212
|
+
this.log("getSubagentList() response:", response);
|
|
20213
|
+
return {
|
|
20214
|
+
results: response.subagents,
|
|
20215
|
+
error: void 0
|
|
20216
|
+
};
|
|
20217
|
+
} catch (error) {
|
|
20218
|
+
this.log("getSubagentList() failed:", error);
|
|
20219
|
+
return {
|
|
20220
|
+
results: [],
|
|
20221
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20222
|
+
};
|
|
20223
|
+
}
|
|
20224
|
+
}
|
|
20225
|
+
/**
|
|
20226
|
+
* 获取 Skill 列表
|
|
20227
|
+
*
|
|
20228
|
+
* @param params 查询参数
|
|
20229
|
+
* @returns Skill 列表
|
|
20230
|
+
*/
|
|
20231
|
+
async getSkillList(params) {
|
|
20232
|
+
this.log("Getting skill list:", params);
|
|
20233
|
+
const useGlobal = params?.global ?? true;
|
|
20234
|
+
try {
|
|
20235
|
+
if (!useGlobal || params?.cwd) {
|
|
20236
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getSkillList", {
|
|
20237
|
+
cwd: params?.cwd ?? "",
|
|
20238
|
+
excludePluginSkills: params?.excludePluginSkills ?? false
|
|
20239
|
+
});
|
|
20240
|
+
this.log("getSkillList() broadcast response:", response);
|
|
20241
|
+
return {
|
|
20242
|
+
results: response.skills,
|
|
20243
|
+
error: void 0
|
|
20244
|
+
};
|
|
20245
|
+
}
|
|
20246
|
+
const backendResponse = await this.channel.callMethod("__backend__", {
|
|
20247
|
+
type: "backend",
|
|
20248
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20249
|
+
params: {
|
|
20250
|
+
type: "backend:get-skill-list",
|
|
20251
|
+
params: { skillScanDirs: params?.skillScanDirs }
|
|
20252
|
+
}
|
|
20253
|
+
}, 3e4);
|
|
20254
|
+
this.log("getSkillList() backend response:", backendResponse.data);
|
|
20255
|
+
const data = backendResponse.data;
|
|
20256
|
+
return {
|
|
20257
|
+
results: data?.skills ?? [],
|
|
20258
|
+
error: data?.error
|
|
20259
|
+
};
|
|
20260
|
+
} catch (error) {
|
|
20261
|
+
this.log("getSkillList() failed:", error);
|
|
20262
|
+
return {
|
|
20263
|
+
results: [],
|
|
20264
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20265
|
+
};
|
|
20266
|
+
}
|
|
20267
|
+
}
|
|
20268
|
+
/**
|
|
20269
|
+
* 导入 Skill 文件夹
|
|
20270
|
+
*
|
|
20271
|
+
* @param params 导入参数
|
|
20272
|
+
* @returns 导入结果
|
|
20273
|
+
*/
|
|
20274
|
+
async importSkill(params) {
|
|
20275
|
+
this.log("Importing skill folder:", params);
|
|
20276
|
+
try {
|
|
20277
|
+
const backendResponse = await this.channel.callMethod("__backend__", {
|
|
20278
|
+
type: "backend",
|
|
20279
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20280
|
+
params: {
|
|
20281
|
+
type: "backend:import-skill",
|
|
20282
|
+
params: {
|
|
20283
|
+
cwd: params.cwd,
|
|
20284
|
+
source: params.source,
|
|
20285
|
+
folderPath: params.folderPath
|
|
20286
|
+
}
|
|
20287
|
+
}
|
|
20288
|
+
}, 3e4);
|
|
20289
|
+
this.log("importSkill() backend response:", backendResponse.data);
|
|
20290
|
+
const data = backendResponse.data;
|
|
20291
|
+
return {
|
|
20292
|
+
success: data?.success ?? false,
|
|
20293
|
+
error: data?.error,
|
|
20294
|
+
skillName: data?.skillName
|
|
20295
|
+
};
|
|
20296
|
+
} catch (error) {
|
|
20297
|
+
this.log("importSkill() failed:", error);
|
|
20298
|
+
return {
|
|
20299
|
+
success: false,
|
|
20300
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20301
|
+
};
|
|
20302
|
+
}
|
|
20303
|
+
}
|
|
20304
|
+
/**
|
|
20305
|
+
* 切换 Skill 启用/禁用状态
|
|
20306
|
+
*
|
|
20307
|
+
* @param params 切换参数
|
|
20308
|
+
* @returns 切换结果
|
|
20309
|
+
*/
|
|
20310
|
+
async toggleSkill(params) {
|
|
20311
|
+
this.log("Toggling skill:", params.filePath, "disable:", params.disable);
|
|
20312
|
+
try {
|
|
20313
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("toggleSkill", {
|
|
20314
|
+
filePath: params.filePath,
|
|
20315
|
+
disable: params.disable
|
|
20316
|
+
});
|
|
20317
|
+
this.log("toggleSkill() response:", response);
|
|
20318
|
+
return {
|
|
20319
|
+
success: response?.success ?? false,
|
|
20320
|
+
error: response?.error
|
|
20321
|
+
};
|
|
20322
|
+
} catch (error) {
|
|
20323
|
+
this.log("toggleSkill() failed:", error);
|
|
20324
|
+
return {
|
|
20325
|
+
success: false,
|
|
20326
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20327
|
+
};
|
|
20328
|
+
}
|
|
20329
|
+
}
|
|
20330
|
+
/**
|
|
20331
|
+
* 删除 Skill
|
|
20332
|
+
*
|
|
20333
|
+
* @param params 删除参数
|
|
20334
|
+
* @returns 删除结果
|
|
20335
|
+
*/
|
|
20336
|
+
async deleteSkill(params) {
|
|
20337
|
+
this.log("Deleting skill:", params.name, "at", params.filePath);
|
|
20338
|
+
try {
|
|
20339
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("deleteSkill", {
|
|
20340
|
+
filePath: params.filePath,
|
|
20341
|
+
name: params.name
|
|
20342
|
+
});
|
|
20343
|
+
this.log("deleteSkill() response:", response);
|
|
20344
|
+
return {
|
|
20345
|
+
success: response?.success ?? false,
|
|
20346
|
+
error: response?.error
|
|
20347
|
+
};
|
|
20348
|
+
} catch (error) {
|
|
20349
|
+
this.log("deleteSkill() failed:", error);
|
|
20350
|
+
return {
|
|
20351
|
+
success: false,
|
|
20352
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20353
|
+
};
|
|
20354
|
+
}
|
|
20355
|
+
}
|
|
20356
|
+
/**
|
|
20357
|
+
* 获取 Skill 内容
|
|
20358
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20359
|
+
*
|
|
20360
|
+
* @param params 包含 filePath 的参数
|
|
20361
|
+
* @returns Skill 文件内容
|
|
20362
|
+
*/
|
|
20363
|
+
async getSkillContent(params) {
|
|
20364
|
+
this.log("Getting skill content:", params.filePath);
|
|
20365
|
+
try {
|
|
20366
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getSkillContent", { filePath: params.filePath });
|
|
20367
|
+
this.log("getSkillContent() response:", response);
|
|
20368
|
+
return {
|
|
20369
|
+
content: response?.content ?? "",
|
|
20370
|
+
error: response?.error
|
|
20371
|
+
};
|
|
20372
|
+
} catch (error) {
|
|
20373
|
+
this.log("getSkillContent() failed:", error);
|
|
20374
|
+
return {
|
|
20375
|
+
content: "",
|
|
20376
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20377
|
+
};
|
|
20378
|
+
}
|
|
20379
|
+
}
|
|
20380
|
+
/**
|
|
20381
|
+
* 获取推荐 Skill 列表(从市场)
|
|
20382
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20383
|
+
*
|
|
20384
|
+
* @returns 推荐 Skill 列表
|
|
20385
|
+
*/
|
|
20386
|
+
async getMarketplaceSkills() {
|
|
20387
|
+
this.log("Getting marketplace skills");
|
|
20388
|
+
try {
|
|
20389
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getMarketplaceSkills", {});
|
|
20390
|
+
this.log("getMarketplaceSkills() response:", response);
|
|
20391
|
+
return { results: response?.skills ?? [] };
|
|
20392
|
+
} catch (error) {
|
|
20393
|
+
this.log("getMarketplaceSkills() failed:", error);
|
|
20394
|
+
return {
|
|
20395
|
+
results: [],
|
|
20396
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20397
|
+
};
|
|
20398
|
+
}
|
|
20399
|
+
}
|
|
20400
|
+
/**
|
|
20401
|
+
* 获取市场 Skill 内容
|
|
20402
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20403
|
+
*
|
|
20404
|
+
* @param params 包含 skillName 的参数
|
|
20405
|
+
* @returns Skill 文件内容
|
|
20406
|
+
*/
|
|
20407
|
+
async getMarketplaceSkillContent(params) {
|
|
20408
|
+
this.log("Getting marketplace skill content:", params.skillName);
|
|
20409
|
+
try {
|
|
20410
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("getMarketplaceSkillContent", { skillName: params.skillName });
|
|
20411
|
+
this.log("getMarketplaceSkillContent() response:", response);
|
|
20412
|
+
return {
|
|
20413
|
+
content: response?.content ?? "",
|
|
20414
|
+
error: response?.error
|
|
20415
|
+
};
|
|
20416
|
+
} catch (error) {
|
|
20417
|
+
this.log("getMarketplaceSkillContent() failed:", error);
|
|
20418
|
+
return {
|
|
20419
|
+
content: "",
|
|
20420
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20421
|
+
};
|
|
20422
|
+
}
|
|
20423
|
+
}
|
|
20424
|
+
/**
|
|
20425
|
+
* 安装市场 Skill 到用户目录
|
|
20426
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
20427
|
+
*
|
|
20428
|
+
* @param params 包含 skillName 的参数
|
|
20429
|
+
* @returns 安装结果
|
|
20430
|
+
*/
|
|
20431
|
+
async installMarketplaceSkill(params) {
|
|
20432
|
+
this.log("Installing marketplace skill:", params.skillName);
|
|
20433
|
+
try {
|
|
20434
|
+
const response = await this.sharedAcpClient.sendBroadcastRequest("installMarketplaceSkill", { skillName: params.skillName });
|
|
20435
|
+
this.log("installMarketplaceSkill() response:", response);
|
|
20436
|
+
return {
|
|
20437
|
+
success: response?.success ?? false,
|
|
20438
|
+
skillName: response?.skillName ?? params.skillName,
|
|
20439
|
+
errorMessage: response?.errorMessage
|
|
20440
|
+
};
|
|
20441
|
+
} catch (error) {
|
|
20442
|
+
this.log("installMarketplaceSkill() failed:", error);
|
|
20443
|
+
return {
|
|
20444
|
+
success: false,
|
|
20445
|
+
skillName: params.skillName,
|
|
20446
|
+
errorMessage: error instanceof Error ? error.message : "Unknown error"
|
|
20447
|
+
};
|
|
20448
|
+
}
|
|
20449
|
+
}
|
|
20450
|
+
/**
|
|
20451
|
+
* 批量切换插件状态
|
|
20452
|
+
* 通过 ACP 协议调用 Extension Host 的 PluginService
|
|
20453
|
+
*
|
|
20454
|
+
* @param request 批量插件操作请求
|
|
20455
|
+
* @returns 批量操作结果
|
|
20456
|
+
*/
|
|
20457
|
+
async batchTogglePlugins(request) {
|
|
20458
|
+
this.log("Batch toggling plugins:", request);
|
|
20459
|
+
try {
|
|
20460
|
+
const result = await this.sharedAcpClient.batchTogglePlugins(request);
|
|
20461
|
+
this.log("batchTogglePlugins() response:", result);
|
|
20462
|
+
return {
|
|
20463
|
+
success: result.success,
|
|
20464
|
+
succeededPlugins: result.succeededPlugins,
|
|
20465
|
+
failedPlugins: result.failedPlugins.map((item) => ({
|
|
20466
|
+
pluginName: item.pluginName,
|
|
20467
|
+
marketplaceName: item.marketplaceName,
|
|
20468
|
+
scope: item.scope,
|
|
20469
|
+
operation: item.operation,
|
|
20470
|
+
error: item.error ?? "Unknown error"
|
|
20471
|
+
}))
|
|
20472
|
+
};
|
|
20473
|
+
} catch (error) {
|
|
20474
|
+
this.log("batchTogglePlugins() failed:", error);
|
|
20475
|
+
return {
|
|
20476
|
+
success: false,
|
|
20477
|
+
succeededPlugins: [],
|
|
20478
|
+
failedPlugins: request.items.map((item) => ({
|
|
20479
|
+
...item,
|
|
20480
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20481
|
+
}))
|
|
20482
|
+
};
|
|
20483
|
+
}
|
|
20484
|
+
}
|
|
20485
|
+
/**
|
|
20486
|
+
* 获取当前工作区列表
|
|
20487
|
+
* 这是 LocalAgentProvider 特有的能力,通过 shared ACP client 发送请求,
|
|
20488
|
+
* 使用 __workspace__ session ID,由 Main Process 直接处理
|
|
20489
|
+
*
|
|
20490
|
+
* @param filter 可选的过滤参数
|
|
20491
|
+
* @returns 工作区列表
|
|
20492
|
+
*/
|
|
20493
|
+
async getCurrentWorkspaces(filter) {
|
|
20494
|
+
this.log("Getting current workspaces:", filter);
|
|
20495
|
+
try {
|
|
20496
|
+
return (await this.sharedAcpClient.getCurrentWorkspaces({ filter })).workspaces ?? [];
|
|
20497
|
+
} catch (error) {
|
|
20498
|
+
this.log("getCurrentWorkspaces() failed:", error);
|
|
20499
|
+
return [];
|
|
20500
|
+
}
|
|
20501
|
+
}
|
|
20502
|
+
async getAutomationSnapshot() {
|
|
20503
|
+
this.log("Getting automation snapshot");
|
|
20504
|
+
return await this.sharedAcpClient.getAutomationSnapshot();
|
|
20505
|
+
}
|
|
20506
|
+
async updateAutomation(payload) {
|
|
20507
|
+
this.log("Updating automation:", payload?.id || payload?.name || "(new)");
|
|
20508
|
+
return await this.sharedAcpClient.updateAutomation(payload);
|
|
20509
|
+
}
|
|
20510
|
+
async deleteAutomation(id) {
|
|
20511
|
+
this.log("Deleting automation:", id);
|
|
20512
|
+
return await this.sharedAcpClient.deleteAutomation({ id });
|
|
20513
|
+
}
|
|
20514
|
+
async archiveAutomationInboxItem(itemId) {
|
|
20515
|
+
this.log("Archiving automation inbox item:", itemId);
|
|
20516
|
+
return await this.sharedAcpClient.archiveAutomationInboxItem({ itemId });
|
|
20517
|
+
}
|
|
20518
|
+
async deleteAutomationInboxItem(itemId) {
|
|
20519
|
+
this.log("Deleting automation inbox item:", itemId);
|
|
20520
|
+
return await this.sharedAcpClient.deleteAutomationInboxItem({ itemId });
|
|
20521
|
+
}
|
|
20522
|
+
async testAutomation(id) {
|
|
20523
|
+
this.log("Testing automation:", id);
|
|
20524
|
+
return await this.sharedAcpClient.testAutomation({ id });
|
|
20525
|
+
}
|
|
20526
|
+
/**
|
|
20527
|
+
* 获取已安装插件列表
|
|
20528
|
+
* 通过 shared ACP client 发送请求
|
|
20529
|
+
*
|
|
20530
|
+
* @param forceRefresh 是否强制刷新缓存
|
|
20531
|
+
* @returns 已安装插件列表
|
|
20532
|
+
*/
|
|
20533
|
+
async getInstalledPlugins(forceRefresh) {
|
|
20534
|
+
this.log("Getting installed plugins, forceRefresh:", forceRefresh);
|
|
20535
|
+
try {
|
|
20536
|
+
const result = await this.sharedAcpClient.getInstalledPlugins({ forceRefresh });
|
|
20537
|
+
this.log(`Got ${result.plugins?.length ?? 0} installed plugins`);
|
|
20538
|
+
return result.plugins ?? [];
|
|
20539
|
+
} catch (error) {
|
|
20540
|
+
this.log("getInstalledPlugins() failed:", error);
|
|
20541
|
+
return [];
|
|
20542
|
+
}
|
|
20543
|
+
}
|
|
20544
|
+
/**
|
|
20545
|
+
* 安装插件
|
|
20546
|
+
* 通过 shared ACP client 发送请求
|
|
20547
|
+
*
|
|
20548
|
+
* @param pluginNames 插件名称数组
|
|
20549
|
+
* @param marketplaceName 插件市场名称
|
|
20550
|
+
* @param installScope 安装范围
|
|
20551
|
+
* @param marketplaceSource 市场源地址(当市场不存在时用于自动添加市场)
|
|
20552
|
+
* @returns 安装结果
|
|
20553
|
+
*/
|
|
20554
|
+
async installPlugins(pluginNames, marketplaceName, installScope, marketplaceSource, workspacePath) {
|
|
20555
|
+
this.log("Installing plugins:", pluginNames, "from", marketplaceName, marketplaceSource ? `(source: ${marketplaceSource})` : "", workspacePath ? `(workspacePath: ${workspacePath})` : "");
|
|
20556
|
+
try {
|
|
20557
|
+
return await this.sharedAcpClient.installPlugins({
|
|
20558
|
+
pluginNames,
|
|
20559
|
+
marketplaceName,
|
|
20560
|
+
installScope,
|
|
20561
|
+
marketplaceSource,
|
|
20562
|
+
workspacePath
|
|
20563
|
+
});
|
|
20564
|
+
} catch (error) {
|
|
20565
|
+
this.log("installPlugins() failed:", error);
|
|
20566
|
+
return {
|
|
20567
|
+
success: false,
|
|
20568
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20569
|
+
};
|
|
20570
|
+
}
|
|
20571
|
+
}
|
|
20572
|
+
/**
|
|
20573
|
+
* 卸载插件
|
|
20574
|
+
* 通过 shared ACP client 发送请求
|
|
20575
|
+
*
|
|
20576
|
+
* @param pluginName 插件名称
|
|
20577
|
+
* @param marketplaceName 插件市场名称
|
|
20578
|
+
* @param scope 卸载范围
|
|
20579
|
+
* @returns 卸载结果
|
|
20580
|
+
*/
|
|
20581
|
+
async uninstallPlugin(pluginName, marketplaceName, scope) {
|
|
20582
|
+
this.log("Uninstalling plugin:", pluginName, "from", marketplaceName, "scope:", scope);
|
|
20583
|
+
try {
|
|
20584
|
+
return await this.sharedAcpClient.uninstallPlugin({
|
|
20585
|
+
pluginName,
|
|
20586
|
+
marketplaceName,
|
|
20587
|
+
scope
|
|
20588
|
+
});
|
|
20589
|
+
} catch (error) {
|
|
20590
|
+
this.log("uninstallPlugin() failed:", error);
|
|
20591
|
+
return {
|
|
20592
|
+
success: false,
|
|
20593
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20594
|
+
};
|
|
20595
|
+
}
|
|
20596
|
+
}
|
|
20597
|
+
/**
|
|
20598
|
+
* 更新插件到最新版本
|
|
20599
|
+
* 通过 shared ACP client 发送请求
|
|
20600
|
+
*
|
|
20601
|
+
* @param pluginName 插件名称
|
|
20602
|
+
* @param marketplaceName 插件市场名称
|
|
20603
|
+
* @returns 更新结果
|
|
20604
|
+
*/
|
|
20605
|
+
async updatePlugin(pluginName, marketplaceName) {
|
|
20606
|
+
this.log("Updating plugin:", pluginName, "from", marketplaceName);
|
|
20607
|
+
try {
|
|
20608
|
+
return await this.sharedAcpClient.updatePlugin({
|
|
20609
|
+
pluginName,
|
|
20610
|
+
marketplaceName
|
|
20611
|
+
});
|
|
20612
|
+
} catch (error) {
|
|
20613
|
+
this.log("updatePlugin() failed:", error);
|
|
20614
|
+
return {
|
|
20615
|
+
success: false,
|
|
20616
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20617
|
+
};
|
|
20618
|
+
}
|
|
20619
|
+
}
|
|
20620
|
+
/**
|
|
20621
|
+
* 获取插件市场列表
|
|
20622
|
+
* 通过 shared ACP client 发送请求
|
|
20623
|
+
*
|
|
20624
|
+
* @param forceRefresh 是否强制刷新缓存
|
|
20625
|
+
* @returns 插件市场列表
|
|
20626
|
+
*/
|
|
20627
|
+
async getPluginMarketplaces(forceRefresh) {
|
|
20628
|
+
this.log("Getting plugin marketplaces, forceRefresh:", forceRefresh);
|
|
20629
|
+
try {
|
|
20630
|
+
const result = await this.sharedAcpClient.getPluginMarketplaces({ forceRefresh });
|
|
20631
|
+
this.log(`Got ${result.marketplaces?.length ?? 0} marketplaces`);
|
|
20632
|
+
return result.marketplaces ?? [];
|
|
20633
|
+
} catch (error) {
|
|
20634
|
+
this.log("getPluginMarketplaces() failed:", error);
|
|
20635
|
+
return [];
|
|
20636
|
+
}
|
|
20637
|
+
}
|
|
20638
|
+
/**
|
|
20639
|
+
* 获取市场下的插件列表
|
|
20640
|
+
* 通过 shared ACP client 发送请求
|
|
20641
|
+
*
|
|
20642
|
+
* @param marketplaceName 市场名称
|
|
20643
|
+
* @param forceRefresh 是否强制刷新缓存
|
|
20644
|
+
* @param searchText 搜索关键词
|
|
20645
|
+
* @returns 插件列表
|
|
20646
|
+
*/
|
|
20647
|
+
async getMarketplacePlugins(marketplaceName, forceRefresh, searchText) {
|
|
20648
|
+
this.log("Getting marketplace plugins:", marketplaceName, "forceRefresh:", forceRefresh, "searchText:", searchText);
|
|
20649
|
+
try {
|
|
20650
|
+
const result = await this.sharedAcpClient.getMarketplacePlugins({
|
|
20651
|
+
marketplaceName,
|
|
20652
|
+
forceRefresh,
|
|
20653
|
+
searchText
|
|
20654
|
+
});
|
|
20655
|
+
this.log(`Got ${result.plugins?.length ?? 0} plugins from marketplace ${marketplaceName}`);
|
|
20656
|
+
return result.plugins ?? [];
|
|
20657
|
+
} catch (error) {
|
|
20658
|
+
this.log("getMarketplacePlugins() failed:", error);
|
|
20659
|
+
return [];
|
|
20660
|
+
}
|
|
20661
|
+
}
|
|
20662
|
+
/**
|
|
20663
|
+
* 获取插件详情
|
|
20664
|
+
* 通过 shared ACP client 发送请求
|
|
20665
|
+
*
|
|
20666
|
+
* @param pluginName 插件名称
|
|
20667
|
+
* @param marketplaceName 市场名称
|
|
20668
|
+
* @returns 插件详情
|
|
20669
|
+
*/
|
|
20670
|
+
async getPluginDetail(pluginName, marketplaceName) {
|
|
20671
|
+
this.log("Getting plugin detail:", pluginName, "from", marketplaceName);
|
|
20672
|
+
try {
|
|
20673
|
+
return (await this.sharedAcpClient.getPluginDetail({
|
|
20674
|
+
pluginName,
|
|
20675
|
+
marketplaceName
|
|
20676
|
+
})).plugin ?? null;
|
|
20677
|
+
} catch (error) {
|
|
20678
|
+
this.log("getPluginDetail() failed:", error);
|
|
20679
|
+
return null;
|
|
20680
|
+
}
|
|
20681
|
+
}
|
|
20682
|
+
/**
|
|
20683
|
+
* 添加插件市场
|
|
20684
|
+
* 通过 shared ACP client 发送请求
|
|
20685
|
+
*
|
|
20686
|
+
* @param source 市场源 URL 或 GitHub repo
|
|
20687
|
+
* @param name 市场名称(可选)
|
|
20688
|
+
* @returns 添加结果
|
|
20689
|
+
*/
|
|
20690
|
+
async addPluginMarketplace(source, name) {
|
|
20691
|
+
this.log("Adding plugin marketplace:", source, name ? `as ${name}` : "");
|
|
20692
|
+
try {
|
|
20693
|
+
return await this.sharedAcpClient.addPluginMarketplace({
|
|
20694
|
+
source,
|
|
20695
|
+
name
|
|
20696
|
+
});
|
|
20697
|
+
} catch (error) {
|
|
20698
|
+
this.log("addPluginMarketplace() failed:", error);
|
|
20699
|
+
return {
|
|
20700
|
+
success: false,
|
|
20701
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20702
|
+
};
|
|
20703
|
+
}
|
|
20704
|
+
}
|
|
20705
|
+
/**
|
|
20706
|
+
* 删除插件市场
|
|
20707
|
+
* 通过 shared ACP client 发送请求
|
|
20708
|
+
*
|
|
20709
|
+
* @param marketplaceName 市场名称
|
|
20710
|
+
* @returns 删除结果
|
|
20711
|
+
*/
|
|
20712
|
+
async removePluginMarketplace(marketplaceName) {
|
|
20713
|
+
this.log("Removing plugin marketplace:", marketplaceName);
|
|
20714
|
+
try {
|
|
20715
|
+
return await this.sharedAcpClient.removePluginMarketplace({ marketplaceName });
|
|
20716
|
+
} catch (error) {
|
|
20717
|
+
this.log("removePluginMarketplace() failed:", error);
|
|
20718
|
+
return {
|
|
20719
|
+
success: false,
|
|
20720
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20721
|
+
};
|
|
20722
|
+
}
|
|
20723
|
+
}
|
|
20724
|
+
/**
|
|
20725
|
+
* 刷新插件市场
|
|
20726
|
+
* 通过 shared ACP client 发送请求
|
|
20727
|
+
*
|
|
20728
|
+
* @param marketplaceName 市场名称
|
|
20729
|
+
* @returns 刷新结果
|
|
20730
|
+
*/
|
|
20731
|
+
async refreshPluginMarketplace(marketplaceName) {
|
|
20732
|
+
this.log("Refreshing plugin marketplace:", marketplaceName);
|
|
20733
|
+
try {
|
|
20734
|
+
return await this.sharedAcpClient.refreshPluginMarketplace({ marketplaceName });
|
|
20735
|
+
} catch (error) {
|
|
20736
|
+
this.log("refreshPluginMarketplace() failed:", error);
|
|
20737
|
+
return {
|
|
20738
|
+
success: false,
|
|
20739
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
20740
|
+
};
|
|
20741
|
+
}
|
|
20742
|
+
}
|
|
20743
|
+
/**
|
|
20744
|
+
* 用新窗口打开文件夹
|
|
20745
|
+
* 通过 shared ACP client 发送请求到 Extension Host
|
|
20746
|
+
*
|
|
20747
|
+
* 调用链:
|
|
20748
|
+
* 1. client.sessions.openFolderInNewWindow() 调用此方法
|
|
20749
|
+
* 2. LocalAgentProvider -> AcpJsonRpcClient.openFolderInNewWindow()
|
|
20750
|
+
* 3. Extension Host -> PluginService.openFolderInNewWindow()
|
|
20751
|
+
* 4. VS Code 执行 vscode.openFolder 命令
|
|
20752
|
+
*
|
|
20753
|
+
* @param folderPath 文件夹路径
|
|
20754
|
+
*/
|
|
20755
|
+
async openFolderInNewWindow(folderPath) {
|
|
20756
|
+
this.log("Opening folder in new window:", folderPath);
|
|
20757
|
+
try {
|
|
20758
|
+
await this.sharedAcpClient.openFolderInNewWindow({ folderPath });
|
|
20759
|
+
} catch (error) {
|
|
20760
|
+
this.log("openFolderInNewWindow() failed:", error);
|
|
20761
|
+
throw error;
|
|
20762
|
+
}
|
|
20763
|
+
}
|
|
20764
|
+
/**
|
|
20765
|
+
* 在系统文件管理器中打开目录
|
|
20766
|
+
* 通过 __backend__ 路由到 BackendService → Main Process → shell.openPath
|
|
20767
|
+
*
|
|
20768
|
+
* @param folderPath 文件夹路径
|
|
20769
|
+
*/
|
|
20770
|
+
async openFolder(folderPath) {
|
|
20771
|
+
this.log("Opening folder in system file manager:", folderPath);
|
|
20772
|
+
try {
|
|
20773
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
20774
|
+
type: "backend",
|
|
20775
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20776
|
+
params: {
|
|
20777
|
+
type: "backend:open-folder",
|
|
20778
|
+
params: { folderPath }
|
|
20779
|
+
}
|
|
20780
|
+
}, 1e4);
|
|
20781
|
+
this.log("openFolder() response:", response.data);
|
|
20782
|
+
return response.data;
|
|
20783
|
+
} catch (error) {
|
|
20784
|
+
this.log("openFolder() failed:", error);
|
|
20785
|
+
throw error;
|
|
20786
|
+
}
|
|
20787
|
+
}
|
|
20788
|
+
/**
|
|
20789
|
+
* 获取支持的场景列表(从后端 API)
|
|
20790
|
+
* 通过 shared ACP client 发送请求到 Extension Host
|
|
20791
|
+
* 用于 Welcome 页面的 QuickActions 快捷操作
|
|
20792
|
+
*
|
|
20793
|
+
* 调用链:
|
|
20794
|
+
* 1. client.sessions.getSupportScenes() 调用此方法
|
|
20795
|
+
* 2. LocalAgentProvider -> AcpJsonRpcClient.getSupportScenes()
|
|
20796
|
+
* 3. Extension Host 调用 RestOperations -> GET /v2/as/support/scenes
|
|
20797
|
+
* 4. 返回 SupportSceneInfo[] 数据
|
|
20798
|
+
*
|
|
20799
|
+
* @param locale - 可选,语言环境(如 'zh-CN', 'en-US'),用于获取对应语言的场景数据
|
|
20800
|
+
* @returns 支持的场景列表
|
|
20801
|
+
*/
|
|
20802
|
+
async getSupportScenes(locale) {
|
|
20803
|
+
this.log("Getting support scenes", locale ? `with locale: ${locale}` : "");
|
|
20804
|
+
try {
|
|
20805
|
+
const result = await this.sharedAcpClient.getSupportScenes({ locale });
|
|
20806
|
+
this.log(`Got ${result.scenes?.length ?? 0} support scenes`);
|
|
20807
|
+
return result.scenes ?? [];
|
|
20808
|
+
} catch (error) {
|
|
20809
|
+
this.log("getSupportScenes() failed:", error);
|
|
20810
|
+
return [];
|
|
20811
|
+
}
|
|
20812
|
+
}
|
|
20813
|
+
/**
|
|
20814
|
+
* 获取产品配置中的场景列表
|
|
20815
|
+
* 通过 BackendService 的 __backend__ session 路由到 Main Process 直接读取本地 scenes.json 文件
|
|
20816
|
+
* 无需等待 Extension Host 初始化,页面打开时即可快速加载
|
|
20817
|
+
*
|
|
20818
|
+
* 调用链:
|
|
20819
|
+
* 1. LocalAgentProvider.getProductScenes()
|
|
20820
|
+
* 2. channel.callMethod('__backend__', ...) → BackendService.getProductScenes()
|
|
20821
|
+
* 3. ipcRenderer.invoke('codebuddy:getProductScenes') → Main Process
|
|
20822
|
+
* 4. fs.readFile(~/.codebuddy/plugins/marketplaces/cb_teams_marketplace/scenes.json)
|
|
20823
|
+
* 5. 返回 SupportSceneInfo[] 数据
|
|
20824
|
+
*
|
|
20825
|
+
* @param locale - 可选,语言环境
|
|
20826
|
+
* @returns 产品配置中的场景列表
|
|
20827
|
+
*/
|
|
20828
|
+
async getProductScenes(locale) {
|
|
20829
|
+
this.log("Getting product scenes", locale ? `with locale: ${locale}` : "");
|
|
20830
|
+
try {
|
|
20831
|
+
const scenes = (await this.channel.callMethod("__backend__", {
|
|
20832
|
+
type: "backend",
|
|
20833
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
20834
|
+
params: {
|
|
20835
|
+
type: "backend:get-product-scenes",
|
|
20836
|
+
params: { locale }
|
|
20837
|
+
}
|
|
20838
|
+
}, 1e4)).data?.scenes ?? [];
|
|
20839
|
+
this.log(`Got ${scenes.length} product scenes`);
|
|
20840
|
+
return scenes;
|
|
20841
|
+
} catch (error) {
|
|
20842
|
+
this.log("getProductScenes() failed:", error);
|
|
20843
|
+
return [];
|
|
20844
|
+
}
|
|
20845
|
+
}
|
|
20846
|
+
/**
|
|
20847
|
+
* 获取会话的可用命令列表
|
|
20848
|
+
* 通过 shared ACP client 发送请求到 Extension Host
|
|
20849
|
+
* 如果 session.availableCommands 有值则返回缓存值,否则从后端获取
|
|
20850
|
+
*
|
|
20851
|
+
* 调用链:
|
|
20852
|
+
* 1. client.sessions.getAvailableCommands(params) 调用此方法
|
|
20853
|
+
* 2. LocalAgentProvider -> AcpJsonRpcClient.sendRequest()
|
|
20854
|
+
* 3. Extension Host 返回命令列表
|
|
20855
|
+
*
|
|
20856
|
+
* @param params - 命令获取参数
|
|
20857
|
+
* @returns 可用命令列表
|
|
20858
|
+
*/
|
|
20859
|
+
async getAvailableCommands(params) {
|
|
20860
|
+
this.log("Getting available commands for session:", params?.sessionId ?? "(default)");
|
|
20861
|
+
try {
|
|
20862
|
+
let result;
|
|
20863
|
+
if (params?.sessionId) result = await this.sharedAcpClient.sendRequest(params.sessionId, "getAvailableCommands", params);
|
|
20864
|
+
else result = await this.sharedAcpClient.sendBroadcastRequest("getAvailableCommands", params || {});
|
|
20865
|
+
this.log(`Got ${result.availableCommands?.length ?? 0} available commands`);
|
|
20866
|
+
return result.availableCommands ?? [];
|
|
20867
|
+
} catch (error) {
|
|
20868
|
+
this.log("getAvailableCommands() failed:", error);
|
|
20869
|
+
return [];
|
|
20870
|
+
}
|
|
20871
|
+
}
|
|
20872
|
+
async requestYieldAfterCurrentStep(sessionId) {
|
|
20873
|
+
this.log(`requestYieldAfterCurrentStep() called: sessionId=${sessionId}`);
|
|
20874
|
+
try {
|
|
20875
|
+
const accepted = !!(await this.sharedAcpClient.sendRequest(sessionId, "requestYieldAfterCurrentStep", { sessionId }))?.accepted;
|
|
20876
|
+
this.log(`requestYieldAfterCurrentStep() completed: sessionId=${sessionId}, accepted=${accepted}`);
|
|
20877
|
+
return accepted;
|
|
20878
|
+
} catch (error) {
|
|
20879
|
+
this.log("requestYieldAfterCurrentStep() failed:", error);
|
|
20880
|
+
return false;
|
|
20881
|
+
}
|
|
20882
|
+
}
|
|
20883
|
+
/**
|
|
20884
|
+
* 上报 telemetry 事件
|
|
20885
|
+
* 通过 ACP 链路发送到 IDE 端,由 AcpAgentImpl → EventService 完成上报
|
|
20886
|
+
*/
|
|
20887
|
+
async reportTelemetry(eventName, payload) {
|
|
20888
|
+
try {
|
|
20889
|
+
await this.sharedAcpClient.sendBroadcastRequest("reportTelemetry", {
|
|
20890
|
+
eventName,
|
|
20891
|
+
payload
|
|
20892
|
+
});
|
|
20893
|
+
} catch (error) {
|
|
20894
|
+
this.log("reportTelemetry() failed:", error);
|
|
20895
|
+
}
|
|
20896
|
+
}
|
|
20897
|
+
/**
|
|
20898
|
+
* 设置 MCP 事件监听器
|
|
20899
|
+
* 只需要设置一次,用于接收后端推送的确认请求
|
|
20900
|
+
*/
|
|
20901
|
+
setupMcpEventListeners() {
|
|
20902
|
+
if (this.mcpEventListenersSetup) return;
|
|
20903
|
+
this.mcpEventListenersSetup = true;
|
|
20904
|
+
this.channel.on("mcp-sampling-confirm-request", (data) => {
|
|
20905
|
+
this.log("[MCP] Received sampling confirm request:", data);
|
|
20906
|
+
const serverName = data?.serverName;
|
|
20907
|
+
if (serverName) this.samplingRequestCallbacks.get(serverName)?.forEach((callback) => {
|
|
20908
|
+
try {
|
|
20909
|
+
callback(data);
|
|
20910
|
+
} catch (error) {
|
|
20911
|
+
this.log("[MCP] Sampling callback error:", error);
|
|
20912
|
+
}
|
|
20913
|
+
});
|
|
20914
|
+
});
|
|
20915
|
+
this.channel.on("mcp-roots-confirm-request", (data) => {
|
|
20916
|
+
this.log("[MCP] Received roots confirm request:", data);
|
|
20917
|
+
const serverName = data?.serverName;
|
|
20918
|
+
if (serverName) this.rootsRequestCallbacks.get(serverName)?.forEach((callback) => {
|
|
20919
|
+
try {
|
|
20920
|
+
callback(data);
|
|
20921
|
+
} catch (error) {
|
|
20922
|
+
this.log("[MCP] Roots callback error:", error);
|
|
20923
|
+
}
|
|
20924
|
+
});
|
|
20925
|
+
});
|
|
20926
|
+
this.log("[MCP] Event listeners setup completed");
|
|
20927
|
+
}
|
|
20928
|
+
/**
|
|
20929
|
+
* 响应 MCP Sampling 确认请求
|
|
20930
|
+
* 将用户的决策发送到后端
|
|
20931
|
+
*
|
|
20932
|
+
* @param sessionId 会话 ID
|
|
20933
|
+
* @param response Sampling 确认响应
|
|
20934
|
+
*/
|
|
20935
|
+
async respondToSampling(sessionId, response) {
|
|
20936
|
+
this.log(`respondToSampling() called: sessionId=${sessionId}, requestId=${response.id}, approved=${response.approved}`);
|
|
20937
|
+
try {
|
|
20938
|
+
await this.sharedAcpClient.sendRequest(sessionId, "respondToSampling", {
|
|
20939
|
+
sessionId,
|
|
20940
|
+
...response
|
|
20941
|
+
});
|
|
20942
|
+
this.log(`respondToSampling() completed for request: ${response.id}`);
|
|
20943
|
+
} catch (error) {
|
|
20944
|
+
this.log("respondToSampling() failed:", error);
|
|
20945
|
+
throw error;
|
|
20946
|
+
}
|
|
20947
|
+
}
|
|
20948
|
+
/**
|
|
20949
|
+
* 响应 MCP Roots 确认请求
|
|
20950
|
+
* 将用户的决策发送到后端
|
|
20951
|
+
*
|
|
20952
|
+
* @param sessionId 会话 ID
|
|
20953
|
+
* @param response Roots 确认响应
|
|
20954
|
+
*/
|
|
20955
|
+
async respondToRoots(sessionId, response) {
|
|
20956
|
+
this.log(`respondToRoots() called: sessionId=${sessionId}, requestId=${response.id}, approved=${response.approved}`);
|
|
20957
|
+
try {
|
|
20958
|
+
await this.sharedAcpClient.sendRequest(sessionId, "respondToRoots", {
|
|
20959
|
+
sessionId,
|
|
20960
|
+
...response
|
|
20961
|
+
});
|
|
20962
|
+
this.log(`respondToRoots() completed for request: ${response.id}`);
|
|
20963
|
+
} catch (error) {
|
|
20964
|
+
this.log("respondToRoots() failed:", error);
|
|
20965
|
+
throw error;
|
|
20966
|
+
}
|
|
20967
|
+
}
|
|
20968
|
+
/**
|
|
20969
|
+
* 订阅 MCP Sampling 确认请求
|
|
20970
|
+
* 当 MCP 服务器发起 Sampling 请求时触发回调
|
|
20971
|
+
*
|
|
20972
|
+
* @param serverName MCP 服务器名称
|
|
20973
|
+
* @param callback 请求回调
|
|
20974
|
+
* @returns 取消订阅函数
|
|
20975
|
+
*/
|
|
20976
|
+
subscribeSamplingRequests(serverName, callback) {
|
|
20977
|
+
this.log(`subscribeSamplingRequests() called for server: ${serverName}`);
|
|
20978
|
+
this.setupMcpEventListeners();
|
|
20979
|
+
let callbacks = this.samplingRequestCallbacks.get(serverName);
|
|
20980
|
+
if (!callbacks) {
|
|
20981
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
20982
|
+
this.samplingRequestCallbacks.set(serverName, callbacks);
|
|
20983
|
+
}
|
|
20984
|
+
callbacks.add(callback);
|
|
20985
|
+
return () => {
|
|
20986
|
+
this.log(`Unsubscribing sampling requests for server: ${serverName}`);
|
|
20987
|
+
const cbs = this.samplingRequestCallbacks.get(serverName);
|
|
20988
|
+
cbs?.delete(callback);
|
|
20989
|
+
if (cbs?.size === 0) this.samplingRequestCallbacks.delete(serverName);
|
|
20990
|
+
};
|
|
20991
|
+
}
|
|
20992
|
+
/**
|
|
20993
|
+
* 订阅 MCP Roots 确认请求
|
|
20994
|
+
* 当 MCP 服务器发起 Roots 请求时触发回调
|
|
20995
|
+
*
|
|
20996
|
+
* @param serverName MCP 服务器名称
|
|
20997
|
+
* @param callback 请求回调
|
|
20998
|
+
* @returns 取消订阅函数
|
|
20999
|
+
*/
|
|
21000
|
+
subscribeRootsRequests(serverName, callback) {
|
|
21001
|
+
this.log(`subscribeRootsRequests() called for server: ${serverName}`);
|
|
21002
|
+
this.setupMcpEventListeners();
|
|
21003
|
+
let callbacks = this.rootsRequestCallbacks.get(serverName);
|
|
21004
|
+
if (!callbacks) {
|
|
21005
|
+
callbacks = /* @__PURE__ */ new Set();
|
|
21006
|
+
this.rootsRequestCallbacks.set(serverName, callbacks);
|
|
21007
|
+
}
|
|
21008
|
+
callbacks.add(callback);
|
|
21009
|
+
return () => {
|
|
21010
|
+
this.log(`Unsubscribing roots requests for server: ${serverName}`);
|
|
21011
|
+
const cbs = this.rootsRequestCallbacks.get(serverName);
|
|
21012
|
+
cbs?.delete(callback);
|
|
21013
|
+
if (cbs?.size === 0) this.rootsRequestCallbacks.delete(serverName);
|
|
21014
|
+
};
|
|
21015
|
+
}
|
|
21016
|
+
/**
|
|
21017
|
+
* 获取灵感卡片列表
|
|
21018
|
+
*
|
|
21019
|
+
* @param query - 查询参数(分页、分类、日期筛选等)
|
|
21020
|
+
* @returns 灵感卡片列表结果
|
|
21021
|
+
*/
|
|
21022
|
+
async getInspirations(query) {
|
|
21023
|
+
this.log("getInspirations() called:", query);
|
|
21024
|
+
try {
|
|
21025
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
21026
|
+
type: "backend",
|
|
21027
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21028
|
+
params: {
|
|
21029
|
+
type: "backend:inspiration-list",
|
|
21030
|
+
params: query
|
|
21031
|
+
}
|
|
21032
|
+
}, 1e4);
|
|
21033
|
+
this.log("getInspirations() response:", response.data);
|
|
21034
|
+
return response.data;
|
|
21035
|
+
} catch (error) {
|
|
21036
|
+
this.log("getInspirations() failed:", error);
|
|
21037
|
+
return {
|
|
21038
|
+
cards: [],
|
|
21039
|
+
total: 0
|
|
21040
|
+
};
|
|
21041
|
+
}
|
|
21042
|
+
}
|
|
21043
|
+
/**
|
|
21044
|
+
* 获取灵感卡片详情
|
|
21045
|
+
*
|
|
21046
|
+
* @param params - 包含 cardId 的参数
|
|
21047
|
+
* @returns 灵感卡片详情
|
|
21048
|
+
*/
|
|
21049
|
+
async getInspirationDetail(params) {
|
|
21050
|
+
this.log("getInspirationDetail() called:", params);
|
|
21051
|
+
try {
|
|
21052
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
21053
|
+
type: "backend",
|
|
21054
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21055
|
+
params: {
|
|
21056
|
+
type: "backend:inspiration-detail",
|
|
21057
|
+
params
|
|
21058
|
+
}
|
|
21059
|
+
}, 5e3);
|
|
21060
|
+
this.log("getInspirationDetail() response:", response.data);
|
|
21061
|
+
return response.data;
|
|
21062
|
+
} catch (error) {
|
|
21063
|
+
this.log("getInspirationDetail() failed:", error);
|
|
21064
|
+
return;
|
|
21065
|
+
}
|
|
21066
|
+
}
|
|
21067
|
+
/**
|
|
21068
|
+
* 标记灵感卡片为已读
|
|
21069
|
+
*
|
|
21070
|
+
* @param params - 包含 cardId 的参数
|
|
21071
|
+
* @returns 操作结果
|
|
21072
|
+
*/
|
|
21073
|
+
async markInspirationRead(params) {
|
|
21074
|
+
this.log("markInspirationRead() called:", params);
|
|
21075
|
+
try {
|
|
21076
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21077
|
+
type: "backend",
|
|
21078
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21079
|
+
params: {
|
|
21080
|
+
type: "backend:inspiration-mark-read",
|
|
21081
|
+
params
|
|
21082
|
+
}
|
|
21083
|
+
}, 5e3)).data;
|
|
21084
|
+
} catch (error) {
|
|
21085
|
+
this.log("markInspirationRead() failed:", error);
|
|
21086
|
+
return { success: false };
|
|
21087
|
+
}
|
|
21088
|
+
}
|
|
21089
|
+
/**
|
|
21090
|
+
* 对灵感卡片提交反馈
|
|
21091
|
+
*
|
|
21092
|
+
* @param feedback - 反馈数据(cardId、feedbackType 等)
|
|
21093
|
+
* @returns 操作结果
|
|
21094
|
+
*/
|
|
21095
|
+
async addInspirationFeedback(feedback) {
|
|
21096
|
+
this.log("addInspirationFeedback() called:", feedback);
|
|
21097
|
+
try {
|
|
21098
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21099
|
+
type: "backend",
|
|
21100
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21101
|
+
params: {
|
|
21102
|
+
type: "backend:inspiration-feedback",
|
|
21103
|
+
params: feedback
|
|
21104
|
+
}
|
|
21105
|
+
}, 5e3)).data;
|
|
21106
|
+
} catch (error) {
|
|
21107
|
+
this.log("addInspirationFeedback() failed:", error);
|
|
21108
|
+
return { success: false };
|
|
21109
|
+
}
|
|
21110
|
+
}
|
|
21111
|
+
/**
|
|
21112
|
+
* 收藏/取消收藏灵感卡片
|
|
21113
|
+
*
|
|
21114
|
+
* @param params - 包含 cardId 的参数
|
|
21115
|
+
* @returns 操作结果
|
|
21116
|
+
*/
|
|
21117
|
+
async saveInspiration(params) {
|
|
21118
|
+
this.log("saveInspiration() called:", params);
|
|
21119
|
+
try {
|
|
21120
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21121
|
+
type: "backend",
|
|
21122
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21123
|
+
params: {
|
|
21124
|
+
type: "backend:inspiration-save",
|
|
21125
|
+
params
|
|
21126
|
+
}
|
|
21127
|
+
}, 5e3)).data;
|
|
21128
|
+
} catch (error) {
|
|
21129
|
+
this.log("saveInspiration() failed:", error);
|
|
21130
|
+
return { success: false };
|
|
21131
|
+
}
|
|
21132
|
+
}
|
|
21133
|
+
/**
|
|
21134
|
+
* 获取灵感功能用户配置
|
|
21135
|
+
*
|
|
21136
|
+
* @returns 用户配置
|
|
21137
|
+
*/
|
|
21138
|
+
async getInspirationSettings() {
|
|
21139
|
+
this.log("getInspirationSettings() called");
|
|
21140
|
+
try {
|
|
21141
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
21142
|
+
type: "backend",
|
|
21143
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21144
|
+
params: {
|
|
21145
|
+
type: "backend:inspiration-settings-get",
|
|
21146
|
+
params: {}
|
|
21147
|
+
}
|
|
21148
|
+
}, 5e3);
|
|
21149
|
+
this.log("getInspirationSettings() response:", response.data);
|
|
21150
|
+
return response.data;
|
|
21151
|
+
} catch (error) {
|
|
21152
|
+
this.log("getInspirationSettings() failed:", error);
|
|
21153
|
+
return {};
|
|
21154
|
+
}
|
|
21155
|
+
}
|
|
21156
|
+
/**
|
|
21157
|
+
* 保存灵感功能用户配置
|
|
21158
|
+
*
|
|
21159
|
+
* @param config - 用户配置数据
|
|
21160
|
+
* @returns 操作结果
|
|
21161
|
+
*/
|
|
21162
|
+
async saveInspirationSettings(config) {
|
|
21163
|
+
this.log("saveInspirationSettings() called:", config);
|
|
21164
|
+
try {
|
|
21165
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21166
|
+
type: "backend",
|
|
21167
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21168
|
+
params: {
|
|
21169
|
+
type: "backend:inspiration-settings-save",
|
|
21170
|
+
params: config
|
|
21171
|
+
}
|
|
21172
|
+
}, 5e3)).data;
|
|
21173
|
+
} catch (error) {
|
|
21174
|
+
this.log("saveInspirationSettings() failed:", error);
|
|
21175
|
+
return { success: false };
|
|
21176
|
+
}
|
|
21177
|
+
}
|
|
21178
|
+
/**
|
|
21179
|
+
* 检查灵感引导是否完成
|
|
21180
|
+
*
|
|
21181
|
+
* @returns 引导完成状态
|
|
21182
|
+
*/
|
|
21183
|
+
async checkInspirationOnboarding() {
|
|
21184
|
+
this.log("checkInspirationOnboarding() called");
|
|
21185
|
+
try {
|
|
21186
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21187
|
+
type: "backend",
|
|
21188
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21189
|
+
params: {
|
|
21190
|
+
type: "backend:inspiration-onboarding-check",
|
|
21191
|
+
params: {}
|
|
21192
|
+
}
|
|
21193
|
+
}, 5e3)).data;
|
|
21194
|
+
} catch (error) {
|
|
21195
|
+
this.log("checkInspirationOnboarding() failed:", error);
|
|
21196
|
+
return { completed: false };
|
|
21197
|
+
}
|
|
21198
|
+
}
|
|
21199
|
+
/**
|
|
21200
|
+
* 完成灵感引导
|
|
21201
|
+
*
|
|
21202
|
+
* @returns 操作结果
|
|
21203
|
+
*/
|
|
21204
|
+
async completeInspirationOnboarding() {
|
|
21205
|
+
this.log("completeInspirationOnboarding() called");
|
|
21206
|
+
try {
|
|
21207
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21208
|
+
type: "backend",
|
|
21209
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21210
|
+
params: {
|
|
21211
|
+
type: "backend:inspiration-onboarding-complete",
|
|
21212
|
+
params: {}
|
|
21213
|
+
}
|
|
21214
|
+
}, 5e3)).data;
|
|
21215
|
+
} catch (error) {
|
|
21216
|
+
this.log("completeInspirationOnboarding() failed:", error);
|
|
21217
|
+
return { success: false };
|
|
21218
|
+
}
|
|
21219
|
+
}
|
|
21220
|
+
/**
|
|
21221
|
+
* 注入 demo 灵感卡片(首次引导完成后调用)
|
|
21222
|
+
*
|
|
21223
|
+
* @returns 操作结果
|
|
21224
|
+
*/
|
|
21225
|
+
async injectDemoInspirationCards() {
|
|
21226
|
+
this.log("injectDemoInspirationCards() called");
|
|
21227
|
+
try {
|
|
21228
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21229
|
+
type: "backend",
|
|
21230
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21231
|
+
params: {
|
|
21232
|
+
type: "backend:inspiration-inject-demo",
|
|
21233
|
+
params: {}
|
|
21234
|
+
}
|
|
21235
|
+
}, 1e4)).data;
|
|
21236
|
+
} catch (error) {
|
|
21237
|
+
this.log("injectDemoInspirationCards() failed:", error);
|
|
21238
|
+
return { success: false };
|
|
21239
|
+
}
|
|
21240
|
+
}
|
|
21241
|
+
/**
|
|
21242
|
+
* 添加灵感策展指令
|
|
21243
|
+
*
|
|
21244
|
+
* @param params - 策展指令数据
|
|
21245
|
+
* @returns 操作结果
|
|
21246
|
+
*/
|
|
21247
|
+
async addInspirationCuration(params) {
|
|
21248
|
+
this.log("addInspirationCuration() called:", params);
|
|
21249
|
+
try {
|
|
21250
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21251
|
+
type: "backend",
|
|
21252
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21253
|
+
params: {
|
|
21254
|
+
type: "backend:inspiration-curation-add",
|
|
21255
|
+
params
|
|
21256
|
+
}
|
|
21257
|
+
}, 5e3)).data;
|
|
21258
|
+
} catch (error) {
|
|
21259
|
+
this.log("addInspirationCuration() failed:", error);
|
|
21260
|
+
return { success: false };
|
|
21261
|
+
}
|
|
21262
|
+
}
|
|
21263
|
+
/**
|
|
21264
|
+
* 获取灵感策展指令列表
|
|
21265
|
+
*
|
|
21266
|
+
* @returns 策展指令列表
|
|
21267
|
+
*/
|
|
21268
|
+
async listInspirationCurations() {
|
|
21269
|
+
this.log("listInspirationCurations() called");
|
|
21270
|
+
try {
|
|
21271
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
21272
|
+
type: "backend",
|
|
21273
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21274
|
+
params: {
|
|
21275
|
+
type: "backend:inspiration-curation-list",
|
|
21276
|
+
params: {}
|
|
21277
|
+
}
|
|
21278
|
+
}, 5e3);
|
|
21279
|
+
this.log("listInspirationCurations() response:", response.data);
|
|
21280
|
+
return response.data;
|
|
21281
|
+
} catch (error) {
|
|
21282
|
+
this.log("listInspirationCurations() failed:", error);
|
|
21283
|
+
return { directives: [] };
|
|
21284
|
+
}
|
|
21285
|
+
}
|
|
21286
|
+
/**
|
|
21287
|
+
* 更新灵感策展指令
|
|
21288
|
+
*
|
|
21289
|
+
* @param params - 包含 directiveId 和更新字段的参数
|
|
21290
|
+
* @returns 操作结果
|
|
21291
|
+
*/
|
|
21292
|
+
async updateInspirationCuration(params) {
|
|
21293
|
+
this.log("updateInspirationCuration() called:", params);
|
|
21294
|
+
try {
|
|
21295
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21296
|
+
type: "backend",
|
|
21297
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21298
|
+
params: {
|
|
21299
|
+
type: "backend:inspiration-curation-update",
|
|
21300
|
+
params
|
|
21301
|
+
}
|
|
21302
|
+
}, 5e3)).data;
|
|
21303
|
+
} catch (error) {
|
|
21304
|
+
this.log("updateInspirationCuration() failed:", error);
|
|
21305
|
+
return { success: false };
|
|
21306
|
+
}
|
|
21307
|
+
}
|
|
21308
|
+
/**
|
|
21309
|
+
* 删除灵感策展指令
|
|
21310
|
+
*
|
|
21311
|
+
* @param params - 包含 directiveId 的参数
|
|
21312
|
+
* @returns 操作结果
|
|
21313
|
+
*/
|
|
21314
|
+
async deleteInspirationCuration(params) {
|
|
21315
|
+
this.log("deleteInspirationCuration() called:", params);
|
|
21316
|
+
try {
|
|
21317
|
+
return (await this.channel.callMethod("__backend__", {
|
|
21318
|
+
type: "backend",
|
|
21319
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21320
|
+
params: {
|
|
21321
|
+
type: "backend:inspiration-curation-delete",
|
|
21322
|
+
params
|
|
21323
|
+
}
|
|
21324
|
+
}, 5e3)).data;
|
|
21325
|
+
} catch (error) {
|
|
21326
|
+
this.log("deleteInspirationCuration() failed:", error);
|
|
21327
|
+
return { success: false };
|
|
21328
|
+
}
|
|
21329
|
+
}
|
|
21330
|
+
/**
|
|
21331
|
+
* 生成灵感测试数据
|
|
21332
|
+
*
|
|
21333
|
+
* @returns 生成结果
|
|
21334
|
+
*/
|
|
21335
|
+
async generateInspirationTestData() {
|
|
21336
|
+
this.log("generateInspirationTestData() called");
|
|
21337
|
+
try {
|
|
21338
|
+
const response = await this.channel.callMethod("__backend__", {
|
|
21339
|
+
type: "backend",
|
|
21340
|
+
requestId: `req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
21341
|
+
params: {
|
|
21342
|
+
type: "backend:inspiration-generate-test-data",
|
|
21343
|
+
params: {}
|
|
21344
|
+
}
|
|
21345
|
+
}, 1e4);
|
|
21346
|
+
this.log("generateInspirationTestData() response:", response.data);
|
|
21347
|
+
return response.data;
|
|
21348
|
+
} catch (error) {
|
|
21349
|
+
this.log("generateInspirationTestData() failed:", error);
|
|
21350
|
+
return { success: false };
|
|
21351
|
+
}
|
|
21352
|
+
}
|
|
21353
|
+
/**
|
|
21354
|
+
* 获取已有连接
|
|
21355
|
+
*
|
|
21356
|
+
* @param cwd - 工作区路径
|
|
21357
|
+
* @returns LocalAgentConnection 实例或 undefined
|
|
21358
|
+
*/
|
|
21359
|
+
getConnection(cwd) {
|
|
21360
|
+
return this.connections.get(cwd);
|
|
21361
|
+
}
|
|
21362
|
+
/**
|
|
21363
|
+
* 检查是否存在连接
|
|
21364
|
+
*
|
|
21365
|
+
* @param cwd - 工作区路径
|
|
21366
|
+
* @returns 是否存在连接
|
|
21367
|
+
*/
|
|
21368
|
+
hasConnection(cwd) {
|
|
21369
|
+
return this.connections.has(cwd);
|
|
21370
|
+
}
|
|
21371
|
+
/**
|
|
21372
|
+
* 断开所有连接
|
|
21373
|
+
*/
|
|
21374
|
+
disconnectAll() {
|
|
21375
|
+
for (const connection of this.connections.values()) connection.disconnect();
|
|
21376
|
+
this.connections.clear();
|
|
21377
|
+
this.sessionCwdMap.clear();
|
|
21378
|
+
for (const fs of this.filesystemCache.values()) fs.destroy();
|
|
21379
|
+
this.filesystemCache.clear();
|
|
21380
|
+
this.samplingRequestCallbacks.clear();
|
|
21381
|
+
this.rootsRequestCallbacks.clear();
|
|
21382
|
+
}
|
|
21383
|
+
/**
|
|
21384
|
+
* 获取 Filesystem 实例
|
|
21385
|
+
*
|
|
21386
|
+
* 对于 Local 模式,agentId 实际上是 sessionId
|
|
21387
|
+
* 通过已有的 MessagePort 通道将 filesystem 请求路由到 ExtensionHost
|
|
21388
|
+
*
|
|
21389
|
+
* @param agentId - Session ID(对于 Local 模式)
|
|
21390
|
+
* @returns FilesResource 实例
|
|
21391
|
+
*/
|
|
21392
|
+
async getFilesystem(agentId) {
|
|
21393
|
+
this.log(`getFilesystem() called with: ${agentId}`);
|
|
21394
|
+
const cached = this.filesystemCache.get(agentId);
|
|
21395
|
+
if (cached) {
|
|
21396
|
+
this.log(`Returning cached filesystem for: ${agentId}`);
|
|
21397
|
+
return cached;
|
|
21398
|
+
}
|
|
21399
|
+
const filesystem = new LocalFilesystem(this.sharedAcpClient, agentId, { debug: this.options.debug });
|
|
21400
|
+
this.filesystemCache.set(agentId, filesystem);
|
|
21401
|
+
this.log(`Created new filesystem for: ${agentId}`);
|
|
21402
|
+
return filesystem;
|
|
21403
|
+
}
|
|
21404
|
+
/**
|
|
21405
|
+
* Register an event listener
|
|
21406
|
+
* Forwards to the underlying channel's event system
|
|
21407
|
+
*
|
|
21408
|
+
* @param event - Event name
|
|
21409
|
+
* @param handler - Event handler function
|
|
21410
|
+
*/
|
|
21411
|
+
on(event, handler) {
|
|
21412
|
+
this.channel.on(event, handler);
|
|
21413
|
+
this.log(`Registered event listener: ${event}`);
|
|
21414
|
+
}
|
|
21415
|
+
/**
|
|
21416
|
+
* Unregister an event listener
|
|
21417
|
+
* Forwards to the underlying channel's event system
|
|
21418
|
+
*
|
|
21419
|
+
* @param event - Event name
|
|
21420
|
+
* @param handler - Event handler function to remove
|
|
21421
|
+
*/
|
|
21422
|
+
off(event, handler) {
|
|
21423
|
+
this.channel.off(event, handler);
|
|
21424
|
+
this.log(`Unregistered event listener: ${event}`);
|
|
21425
|
+
}
|
|
21426
|
+
/**
|
|
21427
|
+
* 获取 MCP 服务器列表
|
|
21428
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21429
|
+
*
|
|
21430
|
+
* @returns MCP 服务器列表
|
|
21431
|
+
*/
|
|
21432
|
+
async getMcpServers() {
|
|
21433
|
+
this.log("Getting MCP servers");
|
|
21434
|
+
try {
|
|
21435
|
+
const result = await this.sharedAcpClient.sendBroadcastRequest("getMcpServers", {});
|
|
21436
|
+
this.log(`Got ${result.servers?.length ?? 0} MCP servers`);
|
|
21437
|
+
return result.servers ?? [];
|
|
21438
|
+
} catch (error) {
|
|
21439
|
+
this.log("getMcpServers() failed:", error);
|
|
21440
|
+
return [];
|
|
21441
|
+
}
|
|
21442
|
+
}
|
|
21443
|
+
/**
|
|
21444
|
+
* 切换 MCP 服务器启用/禁用状态
|
|
21445
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21446
|
+
*
|
|
21447
|
+
* @param serverName 服务器名称
|
|
21448
|
+
* @param enabled 是否启用
|
|
21449
|
+
*/
|
|
21450
|
+
async toggleMcpServer(serverName, enabled) {
|
|
21451
|
+
this.log(`Toggling MCP server: ${serverName}, enabled: ${enabled}`);
|
|
21452
|
+
try {
|
|
21453
|
+
await this.sharedAcpClient.sendBroadcastRequest("toggleMcpServer", {
|
|
21454
|
+
serverName,
|
|
21455
|
+
enabled
|
|
21456
|
+
});
|
|
21457
|
+
this.log(`toggleMcpServer() completed for: ${serverName}`);
|
|
21458
|
+
} catch (error) {
|
|
21459
|
+
this.log("toggleMcpServer() failed:", error);
|
|
21460
|
+
throw error;
|
|
21461
|
+
}
|
|
21462
|
+
}
|
|
21463
|
+
/**
|
|
21464
|
+
* 重新连接 MCP 服务器
|
|
21465
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21466
|
+
*
|
|
21467
|
+
* @param serverName 服务器名称
|
|
21468
|
+
* @param forceHttpCallback 是否强制使用 HTTP 回调(忽略 mcpSchemaUrl 配置)
|
|
21469
|
+
*/
|
|
21470
|
+
async reconnectMcpServer(serverName, forceHttpCallback) {
|
|
21471
|
+
this.log(`Reconnecting MCP server: ${serverName}, forceHttpCallback: ${forceHttpCallback}`);
|
|
21472
|
+
try {
|
|
21473
|
+
await this.sharedAcpClient.sendBroadcastRequest("reconnectMcpServer", {
|
|
21474
|
+
serverName,
|
|
21475
|
+
forceHttpCallback
|
|
21476
|
+
});
|
|
21477
|
+
this.log(`reconnectMcpServer() completed for: ${serverName}`);
|
|
21478
|
+
} catch (error) {
|
|
21479
|
+
this.log("reconnectMcpServer() failed:", error);
|
|
21480
|
+
throw error;
|
|
21481
|
+
}
|
|
21482
|
+
}
|
|
21483
|
+
/**
|
|
21484
|
+
* 删除 MCP 服务器配置
|
|
21485
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21486
|
+
*
|
|
21487
|
+
* @param serverName 服务器名称
|
|
21488
|
+
*/
|
|
21489
|
+
async deleteMcpServer(serverName) {
|
|
21490
|
+
this.log(`Deleting MCP server: ${serverName}`);
|
|
21491
|
+
try {
|
|
21492
|
+
await this.sharedAcpClient.sendBroadcastRequest("deleteMcpServer", { serverName });
|
|
21493
|
+
this.log(`deleteMcpServer() completed for: ${serverName}`);
|
|
21494
|
+
} catch (error) {
|
|
21495
|
+
this.log("deleteMcpServer() failed:", error);
|
|
21496
|
+
throw error;
|
|
21497
|
+
}
|
|
21498
|
+
}
|
|
21499
|
+
/**
|
|
21500
|
+
* 打开 MCP 配置文件
|
|
21501
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21502
|
+
* Extension Host 调用 VS Code 命令打开配置文件
|
|
21503
|
+
*/
|
|
21504
|
+
async openMcpConfig() {
|
|
21505
|
+
this.log("Opening MCP config");
|
|
21506
|
+
try {
|
|
21507
|
+
await this.sharedAcpClient.sendBroadcastRequest("openMcpConfig", {});
|
|
21508
|
+
this.log("openMcpConfig() completed");
|
|
21509
|
+
} catch (error) {
|
|
21510
|
+
this.log("openMcpConfig() failed:", error);
|
|
21511
|
+
throw error;
|
|
21512
|
+
}
|
|
21513
|
+
}
|
|
21514
|
+
/**
|
|
21515
|
+
* 获取 MCP 配置文件内容
|
|
21516
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21517
|
+
* @returns 配置文件路径和内容
|
|
21518
|
+
*/
|
|
21519
|
+
async getMcpConfigContent() {
|
|
21520
|
+
this.log("Getting MCP config content");
|
|
21521
|
+
try {
|
|
21522
|
+
const result = await this.sharedAcpClient.sendBroadcastRequest("getMcpConfigContent", {});
|
|
21523
|
+
this.log("getMcpConfigContent() completed:", result.filePath);
|
|
21524
|
+
return result;
|
|
21525
|
+
} catch (error) {
|
|
21526
|
+
this.log("getMcpConfigContent() failed:", error);
|
|
21527
|
+
throw error;
|
|
21528
|
+
}
|
|
21529
|
+
}
|
|
21530
|
+
/**
|
|
21531
|
+
* 保存 MCP 配置文件内容
|
|
21532
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21533
|
+
* @param content 新的配置内容
|
|
21534
|
+
*/
|
|
21535
|
+
async saveMcpConfigContent(content) {
|
|
21536
|
+
this.log("Saving MCP config content");
|
|
21537
|
+
try {
|
|
21538
|
+
await this.sharedAcpClient.sendBroadcastRequest("saveMcpConfigContent", { content });
|
|
21539
|
+
this.log("saveMcpConfigContent() completed");
|
|
21540
|
+
} catch (error) {
|
|
21541
|
+
this.log("saveMcpConfigContent() failed:", error);
|
|
21542
|
+
throw error;
|
|
21543
|
+
}
|
|
21544
|
+
}
|
|
21545
|
+
/**
|
|
21546
|
+
* 读取系统剪贴板文本
|
|
21547
|
+
* 通过 shared ACP client 发送广播请求到 Extension Host
|
|
21548
|
+
* @returns 剪贴板文本内容
|
|
21549
|
+
*/
|
|
21550
|
+
async clipboardReadText() {
|
|
21551
|
+
this.log("Reading clipboard text");
|
|
21552
|
+
try {
|
|
21553
|
+
const result = await this.sharedAcpClient.sendBroadcastRequest("clipboardReadText", {});
|
|
21554
|
+
this.log("clipboardReadText() completed");
|
|
21555
|
+
return result?.text ?? "";
|
|
21556
|
+
} catch (error) {
|
|
21557
|
+
this.log("clipboardReadText() failed:", error);
|
|
21558
|
+
throw error;
|
|
21559
|
+
}
|
|
21560
|
+
}
|
|
21561
|
+
log(...args) {
|
|
21562
|
+
if (this.options.debug) console.log("[LocalAgentProvider]", ...args);
|
|
21563
|
+
}
|
|
21564
|
+
};
|
|
21565
|
+
|
|
21566
|
+
//#endregion
|
|
21567
|
+
//#region ../agent-provider/src/common/client/session.ts
|
|
21568
|
+
/**
|
|
21569
|
+
* ActiveSessionImpl - Implements the ActiveSession interface
|
|
21570
|
+
*
|
|
21571
|
+
* This class wraps an AgentConnection and provides the session-centric API.
|
|
21572
|
+
* It is created by SessionManager when creating or loading sessions.
|
|
21573
|
+
*
|
|
21574
|
+
* @example
|
|
21575
|
+
* ```typescript
|
|
21576
|
+
* // Created by client.sessions.new() or client.sessions.load()
|
|
21577
|
+
* const session = await client.sessions.new({ cwd: '/workspace' });
|
|
21578
|
+
*
|
|
21579
|
+
* // Access agent state
|
|
21580
|
+
* console.log(session.agentState.status);
|
|
21581
|
+
*
|
|
21582
|
+
* // Send prompt
|
|
21583
|
+
* const response = await session.prompts.send({ content: 'Hello!' });
|
|
21584
|
+
*
|
|
21585
|
+
* // Cleanup
|
|
21586
|
+
* session.disconnect();
|
|
21587
|
+
* ```
|
|
21588
|
+
*/
|
|
21589
|
+
var ActiveSessionImpl = class {
|
|
21590
|
+
/**
|
|
21591
|
+
* Create an ActiveSessionImpl instance
|
|
21592
|
+
*
|
|
21593
|
+
* @param sessionId - Session ID
|
|
21594
|
+
* @param agentId - Agent ID
|
|
21595
|
+
* @param connection - Already connected AgentConnection
|
|
21596
|
+
* @param options - Additional options
|
|
21597
|
+
*/
|
|
21598
|
+
constructor(sessionId, agentId, connection, options = {}) {
|
|
21599
|
+
this._availableCommands = [];
|
|
21600
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
21601
|
+
this.onceListeners = /* @__PURE__ */ new Map();
|
|
21602
|
+
this.connectionListeners = [];
|
|
21603
|
+
this._id = sessionId;
|
|
21604
|
+
this._agentId = agentId;
|
|
21605
|
+
this.connection = connection;
|
|
21606
|
+
this.logger = options.logger;
|
|
21607
|
+
this._getFilesystem = options.getFilesystem;
|
|
21608
|
+
this._connectionInfo = options.connectionInfo;
|
|
21609
|
+
this.setupConnectionEvents(connection);
|
|
21610
|
+
this.agent = this.createAgentOperations();
|
|
21611
|
+
this.prompts = this.createPromptsResource();
|
|
21612
|
+
this.artifacts = this.createArtifactsResource();
|
|
21613
|
+
this.files = this.createFilesResource();
|
|
21614
|
+
}
|
|
21615
|
+
/**
|
|
21616
|
+
* Session ID
|
|
21617
|
+
*/
|
|
21618
|
+
get id() {
|
|
21619
|
+
return this._id;
|
|
21620
|
+
}
|
|
21621
|
+
/**
|
|
21622
|
+
* Agent ID
|
|
21623
|
+
*/
|
|
21624
|
+
get agentId() {
|
|
21625
|
+
return this._agentId;
|
|
21626
|
+
}
|
|
21627
|
+
/**
|
|
21628
|
+
* Actual workspace path (set from newSession response _meta)
|
|
21629
|
+
*/
|
|
21630
|
+
get cwd() {
|
|
21631
|
+
return this._cwd;
|
|
21632
|
+
}
|
|
21633
|
+
/**
|
|
21634
|
+
* Set actual workspace path (called by SessionManager after createSession)
|
|
21635
|
+
*/
|
|
21636
|
+
setCwd(cwd) {
|
|
21637
|
+
this._cwd = cwd;
|
|
21638
|
+
}
|
|
21639
|
+
/**
|
|
21640
|
+
* Agent state (live connection state)
|
|
21641
|
+
* Returns LocalAgentState or CloudAgentState based on transport type
|
|
21642
|
+
*/
|
|
21643
|
+
get agentState() {
|
|
21644
|
+
return {
|
|
21645
|
+
id: this._agentId,
|
|
21646
|
+
status: this.connection.state,
|
|
21647
|
+
capabilities: this.connection.capabilities,
|
|
21648
|
+
type: this.connection.transport,
|
|
21649
|
+
cwd: this.connection.cwd || ""
|
|
21650
|
+
};
|
|
21651
|
+
}
|
|
21652
|
+
/**
|
|
21653
|
+
* Get agent capabilities (available after connection)
|
|
21654
|
+
*/
|
|
21655
|
+
get capabilities() {
|
|
21656
|
+
return this.connection.capabilities;
|
|
21657
|
+
}
|
|
21658
|
+
/**
|
|
21659
|
+
* Available session modes
|
|
21660
|
+
*/
|
|
21661
|
+
get availableModes() {
|
|
21662
|
+
return this._availableModes;
|
|
21663
|
+
}
|
|
21664
|
+
/**
|
|
21665
|
+
* Current session mode
|
|
21666
|
+
*/
|
|
21667
|
+
get currentMode() {
|
|
21668
|
+
return this._currentMode;
|
|
21669
|
+
}
|
|
21670
|
+
/**
|
|
21671
|
+
* Available models for this session
|
|
21672
|
+
*/
|
|
21673
|
+
get availableModels() {
|
|
21674
|
+
return this._availableModels;
|
|
21675
|
+
}
|
|
21676
|
+
/**
|
|
21677
|
+
* Current model ID
|
|
21678
|
+
*/
|
|
21679
|
+
get currentModelId() {
|
|
21680
|
+
return this._currentModelId;
|
|
21681
|
+
}
|
|
21682
|
+
/**
|
|
21683
|
+
* Available slash commands
|
|
21684
|
+
*
|
|
21685
|
+
* When Agent sends available_commands_update, this list is automatically updated.
|
|
21686
|
+
* Commands can be accessed directly without waiting for events.
|
|
21687
|
+
*/
|
|
21688
|
+
get availableCommands() {
|
|
21689
|
+
return this._availableCommands;
|
|
21690
|
+
}
|
|
21691
|
+
/**
|
|
21692
|
+
* Set available commands (called when available_commands_update is received)
|
|
21693
|
+
*/
|
|
21694
|
+
setAvailableCommands(commands) {
|
|
21695
|
+
this._availableCommands = commands;
|
|
21696
|
+
this.logger?.info(`Session ${this._id}: Available commands updated, count: ${commands.length}`);
|
|
21697
|
+
}
|
|
21698
|
+
/**
|
|
21699
|
+
* Check if the session is active
|
|
21700
|
+
*/
|
|
21701
|
+
get isActive() {
|
|
21702
|
+
return this.connection.isInitialized;
|
|
21703
|
+
}
|
|
21704
|
+
/**
|
|
21705
|
+
* Session connection information (only available for cloud sessions)
|
|
21706
|
+
* 会话连接信息,包括sandboxId、link、token等
|
|
21707
|
+
*/
|
|
21708
|
+
get connectionInfo() {
|
|
21709
|
+
return this._connectionInfo;
|
|
21710
|
+
}
|
|
21711
|
+
/**
|
|
21712
|
+
* Set session modes (called after create/load)
|
|
21713
|
+
*/
|
|
21714
|
+
setModes(availableModes, currentMode) {
|
|
21715
|
+
this._availableModes = availableModes;
|
|
21716
|
+
this._currentMode = currentMode;
|
|
21717
|
+
}
|
|
21718
|
+
/**
|
|
21719
|
+
* Set available models (called after create/load)
|
|
21720
|
+
*/
|
|
21721
|
+
setModels(availableModels, currentModelId) {
|
|
21722
|
+
this._availableModels = availableModels;
|
|
21723
|
+
this._currentModelId = currentModelId;
|
|
21724
|
+
}
|
|
21725
|
+
createAgentOperations() {
|
|
21726
|
+
const self = this;
|
|
21727
|
+
return {
|
|
21728
|
+
get id() {
|
|
21729
|
+
return self._agentId;
|
|
21730
|
+
},
|
|
21731
|
+
get state() {
|
|
21732
|
+
return self.agentState;
|
|
21733
|
+
},
|
|
21734
|
+
get isConnected() {
|
|
21735
|
+
return self.connection.isInitialized;
|
|
21736
|
+
},
|
|
21737
|
+
get capabilities() {
|
|
21738
|
+
return self.connection.capabilities;
|
|
21739
|
+
}
|
|
21740
|
+
};
|
|
21741
|
+
}
|
|
21742
|
+
createPromptsResource() {
|
|
21743
|
+
return {
|
|
21744
|
+
send: async (params) => {
|
|
21745
|
+
const response = await this.getConnectionOrThrow().prompt(this._id, params);
|
|
21746
|
+
return this.mapPromptResponse(response);
|
|
21747
|
+
},
|
|
21748
|
+
stream: (params) => {
|
|
21749
|
+
return this.getConnectionOrThrow().promptStream(this._id, params);
|
|
21750
|
+
},
|
|
21751
|
+
cancel: async () => {
|
|
21752
|
+
await this.getConnectionOrThrow().cancel(this._id);
|
|
21753
|
+
}
|
|
21754
|
+
};
|
|
21755
|
+
}
|
|
21756
|
+
createArtifactsResource() {
|
|
21757
|
+
const notSupported = () => {
|
|
21758
|
+
throw new Error("Artifact management is no longer supported through this API");
|
|
21759
|
+
};
|
|
21760
|
+
return {
|
|
21761
|
+
list: async (_params) => {
|
|
21762
|
+
notSupported();
|
|
21763
|
+
return [];
|
|
21764
|
+
},
|
|
21765
|
+
retrieve: async (_artifactId) => {
|
|
21766
|
+
notSupported();
|
|
21767
|
+
},
|
|
21768
|
+
content: async (_artifactId) => {
|
|
21769
|
+
notSupported();
|
|
21770
|
+
return "";
|
|
21771
|
+
}
|
|
21772
|
+
};
|
|
21773
|
+
}
|
|
21774
|
+
/**
|
|
21775
|
+
* Create files resource with lazy-loaded filesystem
|
|
21776
|
+
*
|
|
18323
21777
|
* The filesystem is lazily loaded on first use to avoid unnecessary
|
|
18324
21778
|
* connections to the sandbox. The actual filesystem instance is obtained
|
|
18325
21779
|
* via the getter function provided by SessionManager.
|
|
@@ -18665,6 +22119,7 @@ var ActiveSessionImpl = class {
|
|
|
18665
22119
|
*/
|
|
18666
22120
|
var SessionManager = class {
|
|
18667
22121
|
constructor(options) {
|
|
22122
|
+
this.pendingConnections = /* @__PURE__ */ new Map();
|
|
18668
22123
|
this.provider = options.provider;
|
|
18669
22124
|
this.logger = options.logger;
|
|
18670
22125
|
}
|
|
@@ -18732,11 +22187,76 @@ var SessionManager = class {
|
|
|
18732
22187
|
} else throw new Error("Provider does not support creating agents. Use sessions.load() with an existing sessionId.");
|
|
18733
22188
|
const connection = await this.provider.connect(agentId);
|
|
18734
22189
|
this.logger?.debug(`Connected to agent: ${agentId}`);
|
|
22190
|
+
let response;
|
|
22191
|
+
try {
|
|
22192
|
+
response = await connection.createSession({
|
|
22193
|
+
_meta: params.options?._meta,
|
|
22194
|
+
cwd: params.cwd,
|
|
22195
|
+
mcpServers: params.options?.mcpServers
|
|
22196
|
+
});
|
|
22197
|
+
} catch (err) {
|
|
22198
|
+
this.pendingConnections.set(agentId, connection);
|
|
22199
|
+
this.logger?.debug(`Cached pending connection for agent: ${agentId}`);
|
|
22200
|
+
if (err instanceof SessionError) throw new SessionError(err.message, err.sessionId, agentId, err.cause instanceof Error ? err.cause : void 0);
|
|
22201
|
+
const cause = err instanceof Error ? err : new Error(String(err));
|
|
22202
|
+
throw new SessionError(`Failed to create session: ${cause.message}`, void 0, agentId, cause);
|
|
22203
|
+
}
|
|
22204
|
+
return this.buildActiveSession(response, agentId, connection, params);
|
|
22205
|
+
}
|
|
22206
|
+
/**
|
|
22207
|
+
* Retry creating a session after initial session/new request failed.
|
|
22208
|
+
*
|
|
22209
|
+
* Use this when `sessions.create()` fails at the `session/new` step
|
|
22210
|
+
* (after agent creation and connection establishment succeeded).
|
|
22211
|
+
* The caller retrieves `agentId` from the thrown `SessionError.agentId`,
|
|
22212
|
+
* then calls this method to re-attempt only the `session/new` request
|
|
22213
|
+
* while reusing the already-initialized connection (no re-connect).
|
|
22214
|
+
*
|
|
22215
|
+
* @experimental This API is subject to change
|
|
22216
|
+
*
|
|
22217
|
+
* @param agentId - Agent ID from the failed SessionError
|
|
22218
|
+
* @param params - Original create session params (cwd, mcpServers, etc.)
|
|
22219
|
+
* @returns ActiveSession on success
|
|
22220
|
+
* @throws SessionError if session/new fails again
|
|
22221
|
+
*
|
|
22222
|
+
* @example
|
|
22223
|
+
* ```typescript
|
|
22224
|
+
* try {
|
|
22225
|
+
* const session = await client.sessions.create({ cwd: '/workspace' });
|
|
22226
|
+
* } catch (err) {
|
|
22227
|
+
* if (err instanceof SessionError && err.agentId) {
|
|
22228
|
+
* // Retry with custom logic
|
|
22229
|
+
* for (let i = 0; i < 3; i++) {
|
|
22230
|
+
* try {
|
|
22231
|
+
* const session = await client.sessions.retryNewSession(err.agentId, { cwd: '/workspace' });
|
|
22232
|
+
* break; // success
|
|
22233
|
+
* } catch (retryErr) {
|
|
22234
|
+
* await new Promise(r => setTimeout(r, 1000 * (i + 1)));
|
|
22235
|
+
* }
|
|
22236
|
+
* }
|
|
22237
|
+
* }
|
|
22238
|
+
* }
|
|
22239
|
+
* ```
|
|
22240
|
+
*/
|
|
22241
|
+
async retryNewSession(agentId, params) {
|
|
22242
|
+
this.logger?.info(`Retrying session/new for agent: ${agentId}`);
|
|
22243
|
+
const connection = this.pendingConnections.get(agentId);
|
|
22244
|
+
if (!connection) throw new SessionError(`No pending connection found for agent: ${agentId}. retryNewSession() can only be called after a failed sessions.create().`, void 0, agentId);
|
|
22245
|
+
this.logger?.debug(`Reusing cached connection for agent: ${agentId}`);
|
|
18735
22246
|
const response = await connection.createSession({
|
|
18736
22247
|
_meta: params.options?._meta,
|
|
18737
22248
|
cwd: params.cwd,
|
|
18738
22249
|
mcpServers: params.options?.mcpServers
|
|
18739
22250
|
});
|
|
22251
|
+
this.pendingConnections.delete(agentId);
|
|
22252
|
+
this.logger?.debug(`Cleared pending connection for agent: ${agentId}`);
|
|
22253
|
+
return this.buildActiveSession(response, agentId, connection, params);
|
|
22254
|
+
}
|
|
22255
|
+
/**
|
|
22256
|
+
* Build an ActiveSession from a NewSessionResponse.
|
|
22257
|
+
* Shared by createSession() and retryNewSession().
|
|
22258
|
+
*/
|
|
22259
|
+
buildActiveSession(response, agentId, connection, params) {
|
|
18740
22260
|
if (this.provider.registerSession) {
|
|
18741
22261
|
this.provider.registerSession(response.sessionId, agentId);
|
|
18742
22262
|
this.logger?.debug(`Registered session mapping: ${response.sessionId} → ${agentId}`);
|
|
@@ -18880,6 +22400,7 @@ var AgentClient = class {
|
|
|
18880
22400
|
return {
|
|
18881
22401
|
list: async (options) => this.sessionManager.listSessions(options),
|
|
18882
22402
|
create: async (params) => this.sessionManager.createSession(params),
|
|
22403
|
+
retryNewSession: async (agentId, params) => this.sessionManager.retryNewSession(agentId, params),
|
|
18883
22404
|
load: async (params) => {
|
|
18884
22405
|
console.log("[AgentClient] sessions.load called:", params.sessionId);
|
|
18885
22406
|
return this.sessionManager.loadSession(params);
|
|
@@ -20744,6 +24265,28 @@ var BackendProvider = class BackendProvider {
|
|
|
20744
24265
|
if (result?.code === 0 && result?.data) return result.data;
|
|
20745
24266
|
throw new Error(result?.msg || "Checkin failed");
|
|
20746
24267
|
}
|
|
24268
|
+
/**
|
|
24269
|
+
* 获取专家排行榜数据
|
|
24270
|
+
* API 端点: GET /console/expert/ranking
|
|
24271
|
+
* 完全对齐 getCheckinStatus 的调用方式
|
|
24272
|
+
*/
|
|
24273
|
+
async getExpertRanking() {
|
|
24274
|
+
try {
|
|
24275
|
+
const result = await httpService.get("/console/expert/ranking");
|
|
24276
|
+
if (result?.code === 0 && result?.data) {
|
|
24277
|
+
const normalizedItems = Array.isArray(result.data.items) ? result.data.items : Array.isArray(result.data.experts) ? result.data.experts : [];
|
|
24278
|
+
return {
|
|
24279
|
+
items: normalizedItems,
|
|
24280
|
+
total: result.data.total ?? normalizedItems.length,
|
|
24281
|
+
updateTime: result.data.updateTime ?? Date.now()
|
|
24282
|
+
};
|
|
24283
|
+
}
|
|
24284
|
+
return null;
|
|
24285
|
+
} catch (error) {
|
|
24286
|
+
console.error("[BackendProvider] getExpertRanking failed:", error);
|
|
24287
|
+
return null;
|
|
24288
|
+
}
|
|
24289
|
+
}
|
|
20747
24290
|
static {
|
|
20748
24291
|
this.SKILLHUB_BASE_URL = "https://lightmake.site";
|
|
20749
24292
|
}
|
|
@@ -20860,13 +24403,20 @@ const BACKEND_REQUEST_TYPES = {
|
|
|
20860
24403
|
GET_FILE: "backend:get-file",
|
|
20861
24404
|
RELOAD_WINDOW: "backend:reload-window",
|
|
20862
24405
|
SAVE_LOCALE: "backend:save-locale",
|
|
24406
|
+
SAVE_POWER_BLOCKER: "backend:save-power-blocker",
|
|
20863
24407
|
CLOSE_AGENT_MANAGER: "backend:close-agent-manager",
|
|
20864
24408
|
OPEN_EXTERNAL: "backend:open-external",
|
|
24409
|
+
OPEN_LOCAL_FILE: "backend:open-local-file",
|
|
24410
|
+
GET_LOCAL_CUSTOM_MODELS: "backend:get-local-custom-models",
|
|
24411
|
+
SAVE_LOCAL_CUSTOM_MODEL: "backend:save-local-custom-model",
|
|
24412
|
+
DELETE_LOCAL_CUSTOM_MODEL: "backend:delete-local-custom-model",
|
|
20865
24413
|
BATCH_TOGGLE_PLUGINS: "backend:batch-toggle-plugins",
|
|
20866
24414
|
GET_SUPPORT_SCENES: "backend:get-support-scenes",
|
|
20867
24415
|
GET_ACCOUNT_USAGE: "backend:get-account-usage",
|
|
20868
24416
|
GET_CHECKIN_STATUS: "backend:get-checkin-status",
|
|
20869
24417
|
CLAIM_DAILY_CHECKIN: "backend:claim-daily-checkin",
|
|
24418
|
+
GET_ACTIVITY_BANNER: "backend:get-activity-banner",
|
|
24419
|
+
GET_EXPERT_RANKING: "backend:get-expert-ranking",
|
|
20870
24420
|
SKILLHUB_LIST: "backend:skillhub-list",
|
|
20871
24421
|
SKILLHUB_CATEGORIES: "backend:skillhub-categories",
|
|
20872
24422
|
SKILLHUB_SEARCH: "backend:skillhub-search",
|
|
@@ -20892,6 +24442,25 @@ var IPCBackendProvider = class {
|
|
|
20892
24442
|
this.debug = config.debug ?? false;
|
|
20893
24443
|
this.timeoutMs = config.timeoutMs ?? 3e4;
|
|
20894
24444
|
this.log("Initialized with IWidgetChannel");
|
|
24445
|
+
this.setupSessionChangeListener();
|
|
24446
|
+
}
|
|
24447
|
+
/**
|
|
24448
|
+
* 设置会话变化监听器
|
|
24449
|
+
* 监听来自 Extension Host (BackendBridgeService) 推送的 auth:session-changed 事件
|
|
24450
|
+
* 并更新本地 accountService
|
|
24451
|
+
*/
|
|
24452
|
+
setupSessionChangeListener() {
|
|
24453
|
+
this.log("Setting up session change listener");
|
|
24454
|
+
this.channel.on("auth:session-changed", (data) => {
|
|
24455
|
+
this.log("Received auth:session-changed event from Extension Host:", data);
|
|
24456
|
+
const account = data?.account || null;
|
|
24457
|
+
accountService.setAccount(account);
|
|
24458
|
+
this.log("Updated accountService with new session", {
|
|
24459
|
+
hasAccount: !!account,
|
|
24460
|
+
accountNickname: account?.nickname
|
|
24461
|
+
});
|
|
24462
|
+
});
|
|
24463
|
+
this.log("Session change listener setup complete");
|
|
20895
24464
|
}
|
|
20896
24465
|
/**
|
|
20897
24466
|
* 发送统一格式的后端请求
|
|
@@ -21170,6 +24739,19 @@ var IPCBackendProvider = class {
|
|
|
21170
24739
|
}
|
|
21171
24740
|
}
|
|
21172
24741
|
/**
|
|
24742
|
+
* 设置防休眠状态
|
|
24743
|
+
* IDE 环境: 通过 IPC 通知主进程启用/禁用 powerSaveBlocker
|
|
24744
|
+
*/
|
|
24745
|
+
async savePowerBlocker(params) {
|
|
24746
|
+
this.log("Saving power blocker setting via IPC", params);
|
|
24747
|
+
try {
|
|
24748
|
+
await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_POWER_BLOCKER, params);
|
|
24749
|
+
} catch (error) {
|
|
24750
|
+
this.log("Save power blocker setting failed:", error);
|
|
24751
|
+
throw error;
|
|
24752
|
+
}
|
|
24753
|
+
}
|
|
24754
|
+
/**
|
|
21173
24755
|
* 关闭 Agent Manager 面板
|
|
21174
24756
|
* IDE 环境: 通过 IPC 通知 IDE 关闭 Agent Manager(用于返回 IDE)
|
|
21175
24757
|
*/
|
|
@@ -21197,6 +24779,41 @@ var IPCBackendProvider = class {
|
|
|
21197
24779
|
}
|
|
21198
24780
|
}
|
|
21199
24781
|
/**
|
|
24782
|
+
* 打开本地文件
|
|
24783
|
+
* IDE 环境: 通过 IPC 通知 IDE 打开本地配置文件
|
|
24784
|
+
* @param filePath 要打开的文件路径
|
|
24785
|
+
*/
|
|
24786
|
+
async openLocalFile(filePath) {
|
|
24787
|
+
this.log("Opening local file via IPC:", filePath);
|
|
24788
|
+
try {
|
|
24789
|
+
if (!await this.sendBackendRequest(BACKEND_REQUEST_TYPES.OPEN_LOCAL_FILE, { filePath })) throw new Error(`Failed to open local file: ${filePath}`);
|
|
24790
|
+
} catch (error) {
|
|
24791
|
+
this.log("Open local file request failed:", error);
|
|
24792
|
+
throw error;
|
|
24793
|
+
}
|
|
24794
|
+
}
|
|
24795
|
+
/**
|
|
24796
|
+
* 获取用户级本地自定义模型
|
|
24797
|
+
*/
|
|
24798
|
+
async getLocalCustomModels() {
|
|
24799
|
+
this.log("Getting local custom models via IPC");
|
|
24800
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_LOCAL_CUSTOM_MODELS);
|
|
24801
|
+
}
|
|
24802
|
+
/**
|
|
24803
|
+
* 保存用户级本地自定义模型
|
|
24804
|
+
*/
|
|
24805
|
+
async saveLocalCustomModel(request) {
|
|
24806
|
+
this.log("Saving local custom model via IPC:", request);
|
|
24807
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.SAVE_LOCAL_CUSTOM_MODEL, request);
|
|
24808
|
+
}
|
|
24809
|
+
/**
|
|
24810
|
+
* 删除用户级本地自定义模型
|
|
24811
|
+
*/
|
|
24812
|
+
async deleteLocalCustomModel(id) {
|
|
24813
|
+
this.log("Deleting local custom model via IPC:", id);
|
|
24814
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.DELETE_LOCAL_CUSTOM_MODEL, { id });
|
|
24815
|
+
}
|
|
24816
|
+
/**
|
|
21200
24817
|
* 批量切换插件状态
|
|
21201
24818
|
* IDE 环境: 通过 IPC 调用 Extension Host 的 PluginService
|
|
21202
24819
|
*/
|
|
@@ -21275,6 +24892,32 @@ var IPCBackendProvider = class {
|
|
|
21275
24892
|
}
|
|
21276
24893
|
}
|
|
21277
24894
|
/**
|
|
24895
|
+
* 获取活动 Banner
|
|
24896
|
+
* IDE 环境: 通过 IPC 获取活动 Banner
|
|
24897
|
+
*/
|
|
24898
|
+
async getActivityBanner() {
|
|
24899
|
+
this.log("Getting activity banner via IPC");
|
|
24900
|
+
try {
|
|
24901
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_ACTIVITY_BANNER);
|
|
24902
|
+
} catch (error) {
|
|
24903
|
+
this.log("Get activity banner failed:", error);
|
|
24904
|
+
return null;
|
|
24905
|
+
}
|
|
24906
|
+
}
|
|
24907
|
+
/**
|
|
24908
|
+
* 获取专家排行榜数据
|
|
24909
|
+
* IDE 环境: 通过 IPC 获取排行榜数据
|
|
24910
|
+
*/
|
|
24911
|
+
async getExpertRanking() {
|
|
24912
|
+
this.log("Getting expert ranking via IPC");
|
|
24913
|
+
try {
|
|
24914
|
+
return await this.sendBackendRequest(BACKEND_REQUEST_TYPES.GET_EXPERT_RANKING);
|
|
24915
|
+
} catch (error) {
|
|
24916
|
+
this.log("Get expert ranking failed:", error);
|
|
24917
|
+
return null;
|
|
24918
|
+
}
|
|
24919
|
+
}
|
|
24920
|
+
/**
|
|
21278
24921
|
* 获取 SkillHub 技能列表
|
|
21279
24922
|
* IDE 环境: 通过 IPC 获取技能列表
|
|
21280
24923
|
*/
|
|
@@ -21420,6 +25063,7 @@ exports.CloudAgentProvider = CloudAgentProvider;
|
|
|
21420
25063
|
exports.E2BFilesystem = require_e2b_filesystem.E2BFilesystem;
|
|
21421
25064
|
exports.HttpService = HttpService;
|
|
21422
25065
|
exports.IPCBackendProvider = IPCBackendProvider;
|
|
25066
|
+
exports.SessionError = SessionError;
|
|
21423
25067
|
exports.SessionManager = SessionManager;
|
|
21424
25068
|
exports.__toESM = __toESM;
|
|
21425
25069
|
exports.createBackendProvider = createBackendProvider;
|