@xmanrui/dsh-im 0.15.0 → 0.17.0
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/lib/client.js +233 -43
- package/lib/index.js +138 -131
- package/package.json +1 -1
- package/plugin-src/client/channels/feishu/api.js +23 -1
- package/plugin-src/client/channels/feishu/index.js +235 -46
- package/plugin-src/client/channels/feishu/styles.js +2 -0
- package/plugin-src/client/i18n.js +31 -0
- package/plugin-src/host/channels/feishu/production.mjs +3 -1
- package/plugin-src/host/channels/feishu/rpc.mjs +146 -12
- package/src/channels/discord/discord-api.mjs +1 -1
- package/src/channels/feishu/bridge.mjs +1080 -1
- package/src/channels/feishu/feishu-cards.mjs +230 -0
- package/src/channels/feishu/feishu-runtime.mjs +198 -0
- package/src/channels/feishu/multi-bot-controller.mjs +357 -2
- package/src/channels/feishu/plugin-controller.mjs +1 -0
- package/src/channels/feishu/repair-manager.mjs +109 -0
- package/src/channels/feishu/state-store.mjs +85 -1
- package/src/channels/shared/harness-client.mjs +119 -0
- package/src/channels/shared/workspace-command.mjs +2 -2
- package/src/channels/weixin/weixin-api.mjs +1 -1
|
@@ -1223,6 +1223,125 @@ export class HarnessClient {
|
|
|
1223
1223
|
});
|
|
1224
1224
|
}
|
|
1225
1225
|
|
|
1226
|
+
/**
|
|
1227
|
+
* Watch the global Harness event mux (all sessions) until `signal`
|
|
1228
|
+
* aborts, reconnecting on drop. The Desktop host serves the mux as a
|
|
1229
|
+
* WebSocket downlink; frames are `server-request` envelopes whose payload
|
|
1230
|
+
* is a `session/event` — only those are forwarded. `onReconnect` (when
|
|
1231
|
+
* provided) fires after every (re)connection so callers can compensate
|
|
1232
|
+
* for events missed while offline.
|
|
1233
|
+
*/
|
|
1234
|
+
async watchHarnessEvents({ signal, onSessionEvent, onReconnect } = {}) {
|
|
1235
|
+
if (typeof onSessionEvent !== 'function') {
|
|
1236
|
+
throw new TypeError('watchHarnessEvents requires onSessionEvent');
|
|
1237
|
+
}
|
|
1238
|
+
if (!signal || typeof signal.addEventListener !== 'function') {
|
|
1239
|
+
throw new TypeError('watchHarnessEvents requires an AbortSignal');
|
|
1240
|
+
}
|
|
1241
|
+
if (onReconnect !== undefined && typeof onReconnect !== 'function') {
|
|
1242
|
+
throw new TypeError('onReconnect must be a function');
|
|
1243
|
+
}
|
|
1244
|
+
const url = new URL('/api/events.mux', this.#baseUrl);
|
|
1245
|
+
url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
1246
|
+
while (!signal.aborted) {
|
|
1247
|
+
try {
|
|
1248
|
+
await this.#watchHarnessEventSocket(url.toString(), {
|
|
1249
|
+
signal,
|
|
1250
|
+
onSessionEvent,
|
|
1251
|
+
onReconnect,
|
|
1252
|
+
});
|
|
1253
|
+
} catch (error) {
|
|
1254
|
+
if (signal.aborted) return;
|
|
1255
|
+
console.warn(`[${this.#logPrefix}] Harness event mux disconnected:`, error.message);
|
|
1256
|
+
}
|
|
1257
|
+
if (signal.aborted) return;
|
|
1258
|
+
try {
|
|
1259
|
+
await sleep(this.#interactionReconnectDelayMs, signal);
|
|
1260
|
+
} catch {
|
|
1261
|
+
if (signal.aborted) return;
|
|
1262
|
+
throw new Error('Harness event mux reconnect wait failed');
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
#watchHarnessEventSocket(url, { signal, onSessionEvent, onReconnect }) {
|
|
1268
|
+
return new Promise((resolve, reject) => {
|
|
1269
|
+
let socket;
|
|
1270
|
+
try {
|
|
1271
|
+
socket = this.#createWebSocket(url);
|
|
1272
|
+
} catch (error) {
|
|
1273
|
+
reject(error);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
let opened = false;
|
|
1277
|
+
let finished = false;
|
|
1278
|
+
const close = () => {
|
|
1279
|
+
try {
|
|
1280
|
+
socket.close();
|
|
1281
|
+
} catch {
|
|
1282
|
+
// Already closed.
|
|
1283
|
+
}
|
|
1284
|
+
};
|
|
1285
|
+
const finish = (error) => {
|
|
1286
|
+
if (finished) return;
|
|
1287
|
+
finished = true;
|
|
1288
|
+
socket.removeEventListener('open', handleOpen);
|
|
1289
|
+
socket.removeEventListener('message', handleMessage);
|
|
1290
|
+
socket.removeEventListener('close', handleClose);
|
|
1291
|
+
socket.removeEventListener('error', handleError);
|
|
1292
|
+
signal.removeEventListener('abort', handleAbort);
|
|
1293
|
+
if (error) reject(error);
|
|
1294
|
+
else resolve();
|
|
1295
|
+
};
|
|
1296
|
+
const handleOpen = () => {
|
|
1297
|
+
opened = true;
|
|
1298
|
+
try {
|
|
1299
|
+
onReconnect?.();
|
|
1300
|
+
} catch (error) {
|
|
1301
|
+
console.warn(`[${this.#logPrefix}] mux reconnect hook failed:`, error.message);
|
|
1302
|
+
}
|
|
1303
|
+
};
|
|
1304
|
+
const handleMessage = (event) => {
|
|
1305
|
+
try {
|
|
1306
|
+
if (typeof event.data !== 'string') return;
|
|
1307
|
+
const envelope = JSON.parse(event.data);
|
|
1308
|
+
const payload = envelope?.payload;
|
|
1309
|
+
if (envelope?.type !== 'server-request'
|
|
1310
|
+
|| !payload
|
|
1311
|
+
|| typeof payload !== 'object'
|
|
1312
|
+
|| envelope.method !== payload.type
|
|
1313
|
+
|| payload.type !== 'session/event'
|
|
1314
|
+
|| typeof payload.sessionId !== 'string'
|
|
1315
|
+
|| !payload.event
|
|
1316
|
+
|| typeof payload.event !== 'object') return;
|
|
1317
|
+
onSessionEvent({ sessionId: payload.sessionId, event: payload.event });
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
console.warn(`[${this.#logPrefix}] ignored a malformed global mux frame:`, error.message);
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
const handleClose = () => finish(opened ? null : new Error(
|
|
1323
|
+
'Harness event mux WebSocket closed before opening',
|
|
1324
|
+
));
|
|
1325
|
+
const handleError = () => {
|
|
1326
|
+
finish(new Error(opened
|
|
1327
|
+
? 'Harness event mux WebSocket failed'
|
|
1328
|
+
: 'Harness event mux WebSocket failed before opening'));
|
|
1329
|
+
close();
|
|
1330
|
+
};
|
|
1331
|
+
const handleAbort = () => {
|
|
1332
|
+
close();
|
|
1333
|
+
finish();
|
|
1334
|
+
};
|
|
1335
|
+
|
|
1336
|
+
socket.addEventListener('open', handleOpen);
|
|
1337
|
+
socket.addEventListener('message', handleMessage);
|
|
1338
|
+
socket.addEventListener('close', handleClose, { once: true });
|
|
1339
|
+
socket.addEventListener('error', handleError, { once: true });
|
|
1340
|
+
signal.addEventListener('abort', handleAbort, { once: true });
|
|
1341
|
+
if (signal.aborted) handleAbort();
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
|
|
1226
1345
|
stopManagedProcess() {
|
|
1227
1346
|
if (this.#managedProcess?.exitCode === null) this.#managedProcess.kill('SIGTERM');
|
|
1228
1347
|
}
|
|
@@ -85,7 +85,7 @@ async function selectedWorkspacePath(value) {
|
|
|
85
85
|
}
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
async function workspacePathSnapshot(harness) {
|
|
88
|
+
export async function workspacePathSnapshot(harness) {
|
|
89
89
|
const listed = await harness.listWorkspaces();
|
|
90
90
|
const currentValue = typeof harness?.currentWorkspace === 'function'
|
|
91
91
|
? harness.currentWorkspace()
|
|
@@ -148,7 +148,7 @@ async function runWorkspaceListCommand(match, harness) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
async function resolveSessionListWorkspace(selector, harness) {
|
|
151
|
+
export async function resolveSessionListWorkspace(selector, harness) {
|
|
152
152
|
if (!selector) {
|
|
153
153
|
if (typeof harness?.currentWorkspace !== 'function') {
|
|
154
154
|
return { error: '当前机器人没有可用的工作区。' };
|