@annals/agent-mesh 0.18.4 → 0.18.6
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.js +393 -217
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -125,7 +125,7 @@ var BridgeWSClient = class extends EventEmitter {
|
|
|
125
125
|
this.opts = opts;
|
|
126
126
|
}
|
|
127
127
|
async connect() {
|
|
128
|
-
return new Promise((
|
|
128
|
+
return new Promise((resolve3, reject) => {
|
|
129
129
|
this.intentionalClose = false;
|
|
130
130
|
this.registered = false;
|
|
131
131
|
try {
|
|
@@ -143,7 +143,7 @@ var BridgeWSClient = class extends EventEmitter {
|
|
|
143
143
|
this.registered = true;
|
|
144
144
|
this.reconnectDelay = INITIAL_RECONNECT_DELAY;
|
|
145
145
|
this.startHeartbeat();
|
|
146
|
-
|
|
146
|
+
resolve3();
|
|
147
147
|
} else {
|
|
148
148
|
reject(new Error(`Registration failed: ${msg.error || "unknown"}`));
|
|
149
149
|
}
|
|
@@ -439,10 +439,10 @@ var FileReceiver = class {
|
|
|
439
439
|
this.peer = new ndc.PeerConnection("receiver", {
|
|
440
440
|
iceServers: ICE_SERVERS
|
|
441
441
|
});
|
|
442
|
-
return new Promise((
|
|
442
|
+
return new Promise((resolve3) => {
|
|
443
443
|
this.peer.onLocalDescription((sdp, type) => {
|
|
444
444
|
if (type === "offer") {
|
|
445
|
-
|
|
445
|
+
resolve3(sdp);
|
|
446
446
|
}
|
|
447
447
|
this.signalCallback?.({ signal_type: type, payload: sdp });
|
|
448
448
|
});
|
|
@@ -491,8 +491,8 @@ var FileReceiver = class {
|
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
493
|
waitForCompletion(timeoutMs = CONNECT_TIMEOUT_MS) {
|
|
494
|
-
return new Promise((
|
|
495
|
-
this.resolveComplete =
|
|
494
|
+
return new Promise((resolve3, reject) => {
|
|
495
|
+
this.resolveComplete = resolve3;
|
|
496
496
|
this.rejectComplete = reject;
|
|
497
497
|
setTimeout(() => {
|
|
498
498
|
if (!this.closed) {
|
|
@@ -572,6 +572,7 @@ var FileUploadReceiver = class {
|
|
|
572
572
|
});
|
|
573
573
|
});
|
|
574
574
|
this.peer.onDataChannel((dc) => {
|
|
575
|
+
log.debug("[WebRTC] Upload receiver: DataChannel opened");
|
|
575
576
|
dc.onMessage((msg) => {
|
|
576
577
|
if (typeof msg === "string") {
|
|
577
578
|
try {
|
|
@@ -591,6 +592,7 @@ var FileUploadReceiver = class {
|
|
|
591
592
|
}
|
|
592
593
|
try {
|
|
593
594
|
if (signal.signal_type === "offer" || signal.signal_type === "answer") {
|
|
595
|
+
log.debug(`[WebRTC] Upload receiver: setting remote ${signal.signal_type} (${signal.payload.length} chars)`);
|
|
594
596
|
this.peer.setRemoteDescription(signal.payload, signal.signal_type);
|
|
595
597
|
for (const c of this.pendingCandidates) {
|
|
596
598
|
this.peer.addRemoteCandidate(c.candidate, c.mid);
|
|
@@ -604,12 +606,13 @@ var FileUploadReceiver = class {
|
|
|
604
606
|
this.pendingCandidates.push({ candidate, mid });
|
|
605
607
|
}
|
|
606
608
|
}
|
|
607
|
-
} catch {
|
|
609
|
+
} catch (err) {
|
|
610
|
+
log.warn(`[WebRTC] Upload receiver signal error (${signal.signal_type}): ${err}`);
|
|
608
611
|
}
|
|
609
612
|
}
|
|
610
613
|
waitForCompletion(timeoutMs = 3e4) {
|
|
611
|
-
return new Promise((
|
|
612
|
-
this.resolveComplete =
|
|
614
|
+
return new Promise((resolve3, reject) => {
|
|
615
|
+
this.resolveComplete = resolve3;
|
|
613
616
|
this.rejectComplete = reject;
|
|
614
617
|
setTimeout(() => {
|
|
615
618
|
if (!this.closed) {
|
|
@@ -672,10 +675,10 @@ var FileUploadSender = class {
|
|
|
672
675
|
this.peer = new ndc.PeerConnection("upload-sender", {
|
|
673
676
|
iceServers: ICE_SERVERS
|
|
674
677
|
});
|
|
675
|
-
return new Promise((
|
|
678
|
+
return new Promise((resolve3) => {
|
|
676
679
|
this.peer.onLocalDescription((sdp, type) => {
|
|
677
680
|
if (type === "offer") {
|
|
678
|
-
|
|
681
|
+
resolve3(sdp);
|
|
679
682
|
}
|
|
680
683
|
this.signalCallback?.({ signal_type: type, payload: sdp });
|
|
681
684
|
});
|
|
@@ -712,8 +715,8 @@ var FileUploadSender = class {
|
|
|
712
715
|
}
|
|
713
716
|
}
|
|
714
717
|
waitForCompletion(timeoutMs = 3e4) {
|
|
715
|
-
return new Promise((
|
|
716
|
-
this.resolveComplete =
|
|
718
|
+
return new Promise((resolve3, reject) => {
|
|
719
|
+
this.resolveComplete = resolve3;
|
|
717
720
|
this.rejectComplete = reject;
|
|
718
721
|
setTimeout(() => {
|
|
719
722
|
if (!this.closed) {
|
|
@@ -833,7 +836,7 @@ var LocalRuntimeQueueError = class extends Error {
|
|
|
833
836
|
}
|
|
834
837
|
};
|
|
835
838
|
function sleep(ms) {
|
|
836
|
-
return new Promise((
|
|
839
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
837
840
|
}
|
|
838
841
|
function isProcessAlive2(pid) {
|
|
839
842
|
try {
|
|
@@ -1152,14 +1155,193 @@ function createLocalRuntimeQueue(config) {
|
|
|
1152
1155
|
}
|
|
1153
1156
|
|
|
1154
1157
|
// src/bridge/manager.ts
|
|
1155
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1156
|
-
import {
|
|
1158
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync } from "fs";
|
|
1159
|
+
import { readFile as readFileAsync, writeFile as writeFileAsync, unlink, mkdir as mkdirAsync } from "fs/promises";
|
|
1160
|
+
import { execSync as execSync2 } from "child_process";
|
|
1157
1161
|
import { join as join3 } from "path";
|
|
1158
1162
|
import { homedir as homedir2 } from "os";
|
|
1163
|
+
|
|
1164
|
+
// src/utils/zip.ts
|
|
1165
|
+
import { deflateRawSync, inflateRawSync } from "zlib";
|
|
1166
|
+
import { execSync } from "child_process";
|
|
1167
|
+
import { resolve, relative } from "path";
|
|
1168
|
+
function dosTime(date) {
|
|
1169
|
+
return {
|
|
1170
|
+
time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
|
|
1171
|
+
date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
function crc32(buf) {
|
|
1175
|
+
let crc = 4294967295;
|
|
1176
|
+
for (let i = 0; i < buf.length; i++) {
|
|
1177
|
+
crc ^= buf[i];
|
|
1178
|
+
for (let j = 0; j < 8; j++) {
|
|
1179
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1183
|
+
}
|
|
1184
|
+
function writeUint16LE(buf, val, offset) {
|
|
1185
|
+
buf[offset] = val & 255;
|
|
1186
|
+
buf[offset + 1] = val >>> 8 & 255;
|
|
1187
|
+
}
|
|
1188
|
+
function writeUint32LE(buf, val, offset) {
|
|
1189
|
+
buf[offset] = val & 255;
|
|
1190
|
+
buf[offset + 1] = val >>> 8 & 255;
|
|
1191
|
+
buf[offset + 2] = val >>> 16 & 255;
|
|
1192
|
+
buf[offset + 3] = val >>> 24 & 255;
|
|
1193
|
+
}
|
|
1194
|
+
function createZipBuffer(entries) {
|
|
1195
|
+
const now = /* @__PURE__ */ new Date();
|
|
1196
|
+
const { time, date } = dosTime(now);
|
|
1197
|
+
const records = [];
|
|
1198
|
+
const chunks = [];
|
|
1199
|
+
let offset = 0;
|
|
1200
|
+
for (const entry of entries) {
|
|
1201
|
+
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
1202
|
+
const crc = crc32(entry.data);
|
|
1203
|
+
const compressed = deflateRawSync(entry.data, { level: 6 });
|
|
1204
|
+
const compressedSize = compressed.length;
|
|
1205
|
+
const uncompressedSize = entry.data.length;
|
|
1206
|
+
const header = Buffer.alloc(30 + nameBytes.length);
|
|
1207
|
+
writeUint32LE(header, 67324752, 0);
|
|
1208
|
+
writeUint16LE(header, 20, 4);
|
|
1209
|
+
writeUint16LE(header, 0, 6);
|
|
1210
|
+
writeUint16LE(header, 8, 8);
|
|
1211
|
+
writeUint16LE(header, time, 10);
|
|
1212
|
+
writeUint16LE(header, date, 12);
|
|
1213
|
+
writeUint32LE(header, crc, 14);
|
|
1214
|
+
writeUint32LE(header, compressedSize, 18);
|
|
1215
|
+
writeUint32LE(header, uncompressedSize, 22);
|
|
1216
|
+
writeUint16LE(header, nameBytes.length, 26);
|
|
1217
|
+
writeUint16LE(header, 0, 28);
|
|
1218
|
+
nameBytes.copy(header, 30);
|
|
1219
|
+
records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
|
|
1220
|
+
chunks.push(header, compressed);
|
|
1221
|
+
offset += header.length + compressed.length;
|
|
1222
|
+
}
|
|
1223
|
+
const centralDirStart = offset;
|
|
1224
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1225
|
+
const entry = entries[i];
|
|
1226
|
+
const rec = records[i];
|
|
1227
|
+
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
1228
|
+
const cdh = Buffer.alloc(46 + nameBytes.length);
|
|
1229
|
+
writeUint32LE(cdh, 33639248, 0);
|
|
1230
|
+
writeUint16LE(cdh, 20, 4);
|
|
1231
|
+
writeUint16LE(cdh, 20, 6);
|
|
1232
|
+
writeUint16LE(cdh, 0, 8);
|
|
1233
|
+
writeUint16LE(cdh, 8, 10);
|
|
1234
|
+
writeUint16LE(cdh, time, 12);
|
|
1235
|
+
writeUint16LE(cdh, date, 14);
|
|
1236
|
+
writeUint32LE(cdh, rec.crc, 16);
|
|
1237
|
+
writeUint32LE(cdh, rec.compressedSize, 20);
|
|
1238
|
+
writeUint32LE(cdh, rec.uncompressedSize, 24);
|
|
1239
|
+
writeUint16LE(cdh, nameBytes.length, 28);
|
|
1240
|
+
writeUint16LE(cdh, 0, 30);
|
|
1241
|
+
writeUint16LE(cdh, 0, 32);
|
|
1242
|
+
writeUint16LE(cdh, 0, 34);
|
|
1243
|
+
writeUint16LE(cdh, 0, 36);
|
|
1244
|
+
writeUint32LE(cdh, 0, 38);
|
|
1245
|
+
writeUint32LE(cdh, rec.offset, 42);
|
|
1246
|
+
nameBytes.copy(cdh, 46);
|
|
1247
|
+
chunks.push(cdh);
|
|
1248
|
+
offset += cdh.length;
|
|
1249
|
+
}
|
|
1250
|
+
const centralDirSize = offset - centralDirStart;
|
|
1251
|
+
const eocd = Buffer.alloc(22);
|
|
1252
|
+
writeUint32LE(eocd, 101010256, 0);
|
|
1253
|
+
writeUint16LE(eocd, 0, 4);
|
|
1254
|
+
writeUint16LE(eocd, 0, 6);
|
|
1255
|
+
writeUint16LE(eocd, entries.length, 8);
|
|
1256
|
+
writeUint16LE(eocd, entries.length, 10);
|
|
1257
|
+
writeUint32LE(eocd, centralDirSize, 12);
|
|
1258
|
+
writeUint32LE(eocd, centralDirStart, 16);
|
|
1259
|
+
writeUint16LE(eocd, 0, 20);
|
|
1260
|
+
chunks.push(eocd);
|
|
1261
|
+
return Buffer.concat(chunks);
|
|
1262
|
+
}
|
|
1263
|
+
function safeUnzip(zipPath, destDir) {
|
|
1264
|
+
const absDestDir = resolve(destDir);
|
|
1265
|
+
const listing = execSync(`unzip -l "${zipPath}"`, { encoding: "utf-8" });
|
|
1266
|
+
const lines = listing.split("\n");
|
|
1267
|
+
for (const line of lines) {
|
|
1268
|
+
const match = line.match(/^\s*\d+\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+(.+)$/);
|
|
1269
|
+
if (!match) continue;
|
|
1270
|
+
const entryPath = match[1].trim();
|
|
1271
|
+
if (!entryPath) continue;
|
|
1272
|
+
if (entryPath.startsWith("/")) {
|
|
1273
|
+
throw new Error(`Blocked: ZIP contains absolute path entry: ${entryPath}`);
|
|
1274
|
+
}
|
|
1275
|
+
if (entryPath.includes("..")) {
|
|
1276
|
+
throw new Error(`Blocked: ZIP contains path traversal entry: ${entryPath}`);
|
|
1277
|
+
}
|
|
1278
|
+
const resolved = resolve(absDestDir, entryPath);
|
|
1279
|
+
const rel = relative(absDestDir, resolved);
|
|
1280
|
+
if (rel.startsWith("..") || resolve(resolved) !== resolved.replace(/\/$/, "")) {
|
|
1281
|
+
if (rel.startsWith("..")) {
|
|
1282
|
+
throw new Error(`Blocked: ZIP entry escapes target directory: ${entryPath}`);
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
execSync(`unzip -o -q "${zipPath}" -d "${destDir}"`);
|
|
1287
|
+
}
|
|
1288
|
+
function extractZipBuffer(buf) {
|
|
1289
|
+
const entries = [];
|
|
1290
|
+
let eocdOffset = -1;
|
|
1291
|
+
for (let i = buf.length - 22; i >= 0; i--) {
|
|
1292
|
+
if (buf.readUInt32LE(i) === 101010256) {
|
|
1293
|
+
eocdOffset = i;
|
|
1294
|
+
break;
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
if (eocdOffset === -1) {
|
|
1298
|
+
throw new Error("Invalid ZIP: EOCD not found");
|
|
1299
|
+
}
|
|
1300
|
+
const entryCount = buf.readUInt16LE(eocdOffset + 10);
|
|
1301
|
+
const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
1302
|
+
let offset = centralDirOffset;
|
|
1303
|
+
for (let i = 0; i < entryCount; i++) {
|
|
1304
|
+
if (buf.readUInt32LE(offset) !== 33639248) {
|
|
1305
|
+
throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
|
|
1306
|
+
}
|
|
1307
|
+
const compressionMethod = buf.readUInt16LE(offset + 10);
|
|
1308
|
+
const compressedSize = buf.readUInt32LE(offset + 20);
|
|
1309
|
+
const uncompressedSize = buf.readUInt32LE(offset + 24);
|
|
1310
|
+
const nameLen = buf.readUInt16LE(offset + 28);
|
|
1311
|
+
const extraLen = buf.readUInt16LE(offset + 30);
|
|
1312
|
+
const commentLen = buf.readUInt16LE(offset + 32);
|
|
1313
|
+
const localHeaderOffset = buf.readUInt32LE(offset + 42);
|
|
1314
|
+
const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
|
|
1315
|
+
if (!name.endsWith("/")) {
|
|
1316
|
+
const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
|
|
1317
|
+
const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
|
|
1318
|
+
const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
1319
|
+
const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
|
|
1320
|
+
let data;
|
|
1321
|
+
if (compressionMethod === 0) {
|
|
1322
|
+
data = Buffer.from(compressedData);
|
|
1323
|
+
} else if (compressionMethod === 8) {
|
|
1324
|
+
data = inflateRawSync(compressedData);
|
|
1325
|
+
} else {
|
|
1326
|
+
throw new Error(`Unsupported compression method: ${compressionMethod}`);
|
|
1327
|
+
}
|
|
1328
|
+
if (data.length !== uncompressedSize) {
|
|
1329
|
+
throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
|
|
1330
|
+
}
|
|
1331
|
+
entries.push({ path: name, data });
|
|
1332
|
+
}
|
|
1333
|
+
offset += 46 + nameLen + extraLen + commentLen;
|
|
1334
|
+
}
|
|
1335
|
+
return entries;
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
// src/bridge/manager.ts
|
|
1159
1339
|
var DUPLICATE_REQUEST_TTL_MS = 10 * 6e4;
|
|
1160
1340
|
var SESSION_SWEEP_INTERVAL_MS = 6e4;
|
|
1161
1341
|
var DEFAULT_SESSION_IDLE_TTL_MS = 10 * 6e4;
|
|
1162
1342
|
var MIN_SESSION_IDLE_TTL_MS = 6e4;
|
|
1343
|
+
var TRANSFER_ACTIVE_TTL_MS = 5 * 6e4;
|
|
1344
|
+
var TRANSFER_DORMANT_TTL_MS = 60 * 6e4;
|
|
1163
1345
|
function resolveSessionIdleTtlMs() {
|
|
1164
1346
|
const raw = process.env.AGENT_BRIDGE_SESSION_IDLE_TTL_MS;
|
|
1165
1347
|
if (!raw) {
|
|
@@ -1193,6 +1375,10 @@ var BridgeManager = class {
|
|
|
1193
1375
|
pendingTransfers = /* @__PURE__ */ new Map();
|
|
1194
1376
|
/** Pending WebRTC file uploads (caller→agent): transfer_id → FileUploadReceiver + cleanup timer */
|
|
1195
1377
|
pendingUploads = /* @__PURE__ */ new Map();
|
|
1378
|
+
/** Dormant transfers: ZIP evicted from memory, cached on disk for up to 1h */
|
|
1379
|
+
dormantTransfers = /* @__PURE__ */ new Map();
|
|
1380
|
+
/** Signals queued while a dormant transfer is being revived (prevents signal loss during async disk read) */
|
|
1381
|
+
revivingTransfers = /* @__PURE__ */ new Map();
|
|
1196
1382
|
constructor(opts) {
|
|
1197
1383
|
this.wsClient = opts.wsClient;
|
|
1198
1384
|
this.adapter = opts.adapter;
|
|
@@ -1205,6 +1391,7 @@ var BridgeManager = class {
|
|
|
1205
1391
|
this.pruneIdleSessions();
|
|
1206
1392
|
}, SESSION_SWEEP_INTERVAL_MS);
|
|
1207
1393
|
this.cleanupTimer.unref?.();
|
|
1394
|
+
void this.cleanupOrphanedTransferCache();
|
|
1208
1395
|
log.info(`Bridge manager started with ${this.adapter.displayName} adapter`);
|
|
1209
1396
|
}
|
|
1210
1397
|
stop() {
|
|
@@ -1620,11 +1807,27 @@ var BridgeManager = class {
|
|
|
1620
1807
|
};
|
|
1621
1808
|
this.wsClient.send(rtcSignal);
|
|
1622
1809
|
});
|
|
1810
|
+
const zipPath = this.transferCachePath(offer.transfer_id);
|
|
1811
|
+
void (async () => {
|
|
1812
|
+
try {
|
|
1813
|
+
await mkdirAsync(this.transferCacheDir(), { recursive: true });
|
|
1814
|
+
await writeFileAsync(zipPath, zipBuffer);
|
|
1815
|
+
log.debug(`ZIP cached to disk: transfer=${offer.transfer_id.slice(0, 8)}... path=${zipPath}`);
|
|
1816
|
+
} catch (err) {
|
|
1817
|
+
log.warn(`Failed to cache ZIP to disk: ${err}`);
|
|
1818
|
+
}
|
|
1819
|
+
})();
|
|
1623
1820
|
const timer = setTimeout(() => {
|
|
1624
1821
|
sender.close();
|
|
1625
1822
|
this.pendingTransfers.delete(offer.transfer_id);
|
|
1626
|
-
|
|
1627
|
-
|
|
1823
|
+
const dormantTimer = setTimeout(() => {
|
|
1824
|
+
this.cleanupDormantTransfer(offer.transfer_id);
|
|
1825
|
+
log.debug(`Dormant expired, disk cache removed: transfer=${offer.transfer_id.slice(0, 8)}...`);
|
|
1826
|
+
}, TRANSFER_DORMANT_TTL_MS);
|
|
1827
|
+
dormantTimer.unref?.();
|
|
1828
|
+
this.dormantTransfers.set(offer.transfer_id, { zipPath, offer, timer: dormantTimer });
|
|
1829
|
+
log.info(`Transfer transitioned to dormant: transfer=${offer.transfer_id.slice(0, 8)}...`);
|
|
1830
|
+
}, TRANSFER_ACTIVE_TTL_MS);
|
|
1628
1831
|
timer.unref?.();
|
|
1629
1832
|
this.pendingTransfers.set(offer.transfer_id, { sender, timer });
|
|
1630
1833
|
log.info(`WebRTC transfer registered: transfer=${offer.transfer_id.slice(0, 8)}... (${(zipBuffer.length / 1024).toFixed(1)} KB in memory)`);
|
|
@@ -1639,15 +1842,20 @@ var BridgeManager = class {
|
|
|
1639
1842
|
});
|
|
1640
1843
|
return;
|
|
1641
1844
|
}
|
|
1845
|
+
if (this.dormantTransfers.has(msg.transfer_id) || this.revivingTransfers.has(msg.transfer_id)) {
|
|
1846
|
+
void this.handleDormantSignal(msg);
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1642
1849
|
const uploadEntry = this.pendingUploads.get(msg.transfer_id);
|
|
1643
1850
|
if (uploadEntry) {
|
|
1851
|
+
log.debug(`[WebRTC] Routing ${msg.signal_type} signal to upload receiver: transfer=${msg.transfer_id.slice(0, 8)}...`);
|
|
1644
1852
|
void uploadEntry.receiver.handleSignal({
|
|
1645
1853
|
signal_type: msg.signal_type,
|
|
1646
1854
|
payload: msg.payload
|
|
1647
1855
|
});
|
|
1648
1856
|
return;
|
|
1649
1857
|
}
|
|
1650
|
-
log.debug(`No pending transfer for ${msg.transfer_id.slice(0, 8)}
|
|
1858
|
+
log.debug(`No pending transfer for ${msg.transfer_id.slice(0, 8)}... (active=${this.pendingTransfers.size}, dormant=${this.dormantTransfers.size}, upload=${this.pendingUploads.size})`);
|
|
1651
1859
|
}
|
|
1652
1860
|
// ========================================================
|
|
1653
1861
|
// Upload (Caller → Agent) WebRTC signaling
|
|
@@ -1680,14 +1888,14 @@ var BridgeManager = class {
|
|
|
1680
1888
|
const zipPath = join3(workspaceDir, ".upload.zip");
|
|
1681
1889
|
writeFileSync2(zipPath, zipBuffer);
|
|
1682
1890
|
try {
|
|
1683
|
-
|
|
1891
|
+
safeUnzip(zipPath, workspaceDir);
|
|
1684
1892
|
try {
|
|
1685
|
-
|
|
1893
|
+
execSync2(`rm "${zipPath}"`);
|
|
1686
1894
|
} catch {
|
|
1687
1895
|
}
|
|
1688
1896
|
log.info(`[WebRTC] Upload: ${offer.file_count} file(s) extracted to ${workspaceDir}`);
|
|
1689
|
-
} catch {
|
|
1690
|
-
log.warn(`[WebRTC] Upload: Failed to extract ZIP. Saved to: ${zipPath}`);
|
|
1897
|
+
} catch (unzipErr) {
|
|
1898
|
+
log.warn(`[WebRTC] Upload: Failed to extract ZIP: ${unzipErr.message}. Saved to: ${zipPath}`);
|
|
1691
1899
|
}
|
|
1692
1900
|
} catch (err) {
|
|
1693
1901
|
log.error(`[WebRTC] Upload extraction failed: ${err}`);
|
|
@@ -1715,6 +1923,121 @@ var BridgeManager = class {
|
|
|
1715
1923
|
entry.sender.close();
|
|
1716
1924
|
}
|
|
1717
1925
|
this.pendingTransfers.clear();
|
|
1926
|
+
for (const [id] of this.dormantTransfers) {
|
|
1927
|
+
this.cleanupDormantTransfer(id);
|
|
1928
|
+
}
|
|
1929
|
+
this.revivingTransfers.clear();
|
|
1930
|
+
}
|
|
1931
|
+
// ========================================================
|
|
1932
|
+
// Dormant transfer: disk cache + revival
|
|
1933
|
+
// ========================================================
|
|
1934
|
+
transferCacheDir() {
|
|
1935
|
+
return join3(homedir2(), ".agent-mesh", "transfers");
|
|
1936
|
+
}
|
|
1937
|
+
transferCachePath(transferId) {
|
|
1938
|
+
return join3(this.transferCacheDir(), `${transferId}.zip`);
|
|
1939
|
+
}
|
|
1940
|
+
cleanupDormantTransfer(transferId) {
|
|
1941
|
+
const entry = this.dormantTransfers.get(transferId);
|
|
1942
|
+
if (!entry) return;
|
|
1943
|
+
clearTimeout(entry.timer);
|
|
1944
|
+
this.dormantTransfers.delete(transferId);
|
|
1945
|
+
void unlink(entry.zipPath).catch(() => {
|
|
1946
|
+
});
|
|
1947
|
+
}
|
|
1948
|
+
async reviveDormantTransfer(dormant) {
|
|
1949
|
+
const zipBuffer = await readFileAsync(dormant.zipPath);
|
|
1950
|
+
if (zipBuffer.length !== dormant.offer.zip_size) {
|
|
1951
|
+
throw new Error(`ZIP size mismatch: expected ${dormant.offer.zip_size}, got ${zipBuffer.length}`);
|
|
1952
|
+
}
|
|
1953
|
+
const sender = new FileSender(dormant.offer.transfer_id, zipBuffer);
|
|
1954
|
+
sender.onSignal((signal) => {
|
|
1955
|
+
const entry = this.pendingTransfers.get(dormant.offer.transfer_id);
|
|
1956
|
+
if (!entry) return;
|
|
1957
|
+
const rtcSignal = {
|
|
1958
|
+
type: "rtc_signal",
|
|
1959
|
+
transfer_id: dormant.offer.transfer_id,
|
|
1960
|
+
target_agent_id: entry.targetAgentId || "",
|
|
1961
|
+
signal_type: signal.signal_type,
|
|
1962
|
+
payload: signal.payload
|
|
1963
|
+
};
|
|
1964
|
+
this.wsClient.send(rtcSignal);
|
|
1965
|
+
});
|
|
1966
|
+
const timer = setTimeout(() => {
|
|
1967
|
+
sender.close();
|
|
1968
|
+
this.pendingTransfers.delete(dormant.offer.transfer_id);
|
|
1969
|
+
void unlink(dormant.zipPath).catch(() => {
|
|
1970
|
+
});
|
|
1971
|
+
log.debug(`Revived transfer expired: transfer=${dormant.offer.transfer_id.slice(0, 8)}...`);
|
|
1972
|
+
}, TRANSFER_ACTIVE_TTL_MS);
|
|
1973
|
+
timer.unref?.();
|
|
1974
|
+
this.pendingTransfers.set(dormant.offer.transfer_id, { sender, timer });
|
|
1975
|
+
log.info(`Revived from disk cache: transfer=${dormant.offer.transfer_id.slice(0, 8)}... (${(zipBuffer.length / 1024).toFixed(1)} KB)`);
|
|
1976
|
+
return sender;
|
|
1977
|
+
}
|
|
1978
|
+
async handleDormantSignal(msg) {
|
|
1979
|
+
const transferId = msg.transfer_id;
|
|
1980
|
+
const alreadyRevived = this.pendingTransfers.get(transferId);
|
|
1981
|
+
if (alreadyRevived) {
|
|
1982
|
+
alreadyRevived.targetAgentId = msg.from_agent_id;
|
|
1983
|
+
void alreadyRevived.sender.handleSignal({
|
|
1984
|
+
signal_type: msg.signal_type,
|
|
1985
|
+
payload: msg.payload
|
|
1986
|
+
});
|
|
1987
|
+
return;
|
|
1988
|
+
}
|
|
1989
|
+
const queue = this.revivingTransfers.get(transferId);
|
|
1990
|
+
if (queue) {
|
|
1991
|
+
queue.push(msg);
|
|
1992
|
+
return;
|
|
1993
|
+
}
|
|
1994
|
+
const dormant = this.dormantTransfers.get(transferId);
|
|
1995
|
+
if (!dormant) return;
|
|
1996
|
+
this.revivingTransfers.set(transferId, []);
|
|
1997
|
+
clearTimeout(dormant.timer);
|
|
1998
|
+
this.dormantTransfers.delete(transferId);
|
|
1999
|
+
try {
|
|
2000
|
+
const sender = await this.reviveDormantTransfer(dormant);
|
|
2001
|
+
const entry = this.pendingTransfers.get(transferId);
|
|
2002
|
+
if (entry) {
|
|
2003
|
+
entry.targetAgentId = msg.from_agent_id;
|
|
2004
|
+
}
|
|
2005
|
+
void sender.handleSignal({
|
|
2006
|
+
signal_type: msg.signal_type,
|
|
2007
|
+
payload: msg.payload
|
|
2008
|
+
});
|
|
2009
|
+
const queued = this.revivingTransfers.get(transferId) || [];
|
|
2010
|
+
this.revivingTransfers.delete(transferId);
|
|
2011
|
+
for (const queuedMsg of queued) {
|
|
2012
|
+
void sender.handleSignal({
|
|
2013
|
+
signal_type: queuedMsg.signal_type,
|
|
2014
|
+
payload: queuedMsg.payload
|
|
2015
|
+
});
|
|
2016
|
+
}
|
|
2017
|
+
} catch (err) {
|
|
2018
|
+
log.warn(`Failed to revive dormant transfer ${transferId.slice(0, 8)}...: ${err}`);
|
|
2019
|
+
this.revivingTransfers.delete(transferId);
|
|
2020
|
+
void unlink(dormant.zipPath).catch(() => {
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
}
|
|
2024
|
+
async cleanupOrphanedTransferCache() {
|
|
2025
|
+
try {
|
|
2026
|
+
const dir = this.transferCacheDir();
|
|
2027
|
+
let files;
|
|
2028
|
+
try {
|
|
2029
|
+
files = readdirSync(dir);
|
|
2030
|
+
} catch {
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
const zipFiles = files.filter((f) => f.endsWith(".zip"));
|
|
2034
|
+
if (zipFiles.length === 0) return;
|
|
2035
|
+
await Promise.all(zipFiles.map((f) => unlink(join3(dir, f)).catch(() => {
|
|
2036
|
+
})));
|
|
2037
|
+
log.info(`Cleaned ${zipFiles.length} orphaned transfer cache file(s)`);
|
|
2038
|
+
} catch (err) {
|
|
2039
|
+
log.debug(`Transfer cache cleanup failed: ${err}`);
|
|
2040
|
+
}
|
|
1718
2041
|
}
|
|
1719
2042
|
updateSessionCount() {
|
|
1720
2043
|
this.wsClient.setActiveSessions(this.pool.size);
|
|
@@ -1729,7 +2052,7 @@ var AgentAdapter = class {
|
|
|
1729
2052
|
import { spawn } from "child_process";
|
|
1730
2053
|
|
|
1731
2054
|
// src/utils/sandbox.ts
|
|
1732
|
-
import { execSync as
|
|
2055
|
+
import { execSync as execSync3 } from "child_process";
|
|
1733
2056
|
import { join as join4 } from "path";
|
|
1734
2057
|
var SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
|
|
1735
2058
|
var SENSITIVE_PATHS = [
|
|
@@ -1788,7 +2111,7 @@ var sandboxManager = null;
|
|
|
1788
2111
|
var sandboxInitialized = false;
|
|
1789
2112
|
async function importSandboxManager() {
|
|
1790
2113
|
try {
|
|
1791
|
-
const globalRoot =
|
|
2114
|
+
const globalRoot = execSync3("npm root -g", { encoding: "utf-8" }).trim();
|
|
1792
2115
|
const srtPath = join4(globalRoot, "@anthropic-ai/sandbox-runtime/dist/index.js");
|
|
1793
2116
|
const mod = await import(srtPath);
|
|
1794
2117
|
return mod.SandboxManager;
|
|
@@ -1877,7 +2200,7 @@ function buildCommandString(command, args) {
|
|
|
1877
2200
|
function installSandboxRuntime() {
|
|
1878
2201
|
log.info(`Installing ${SRT_PACKAGE}...`);
|
|
1879
2202
|
try {
|
|
1880
|
-
|
|
2203
|
+
execSync3(`npm install -g ${SRT_PACKAGE}`, { stdio: "inherit" });
|
|
1881
2204
|
log.success(`${SRT_PACKAGE} installed successfully`);
|
|
1882
2205
|
return true;
|
|
1883
2206
|
} catch {
|
|
@@ -1986,20 +2309,20 @@ function which(command) {
|
|
|
1986
2309
|
if (!ALLOWED_COMMANDS.test(command)) {
|
|
1987
2310
|
return Promise.resolve(null);
|
|
1988
2311
|
}
|
|
1989
|
-
return new Promise((
|
|
2312
|
+
return new Promise((resolve3) => {
|
|
1990
2313
|
execFile("which", [command], async (err, stdout) => {
|
|
1991
2314
|
if (!err && stdout.trim()) {
|
|
1992
|
-
|
|
2315
|
+
resolve3(stdout.trim());
|
|
1993
2316
|
return;
|
|
1994
2317
|
}
|
|
1995
|
-
|
|
2318
|
+
resolve3(await resolveFallbackPath(command));
|
|
1996
2319
|
});
|
|
1997
2320
|
});
|
|
1998
2321
|
}
|
|
1999
2322
|
|
|
2000
2323
|
// src/utils/client-workspace.ts
|
|
2001
|
-
import { mkdirSync as mkdirSync4, readdirSync, symlinkSync, existsSync as existsSync3, lstatSync } from "fs";
|
|
2002
|
-
import { join as join5, relative } from "path";
|
|
2324
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, symlinkSync, existsSync as existsSync3, lstatSync } from "fs";
|
|
2325
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2003
2326
|
var SYMLINK_ALLOW = /* @__PURE__ */ new Set([
|
|
2004
2327
|
"CLAUDE.md",
|
|
2005
2328
|
".claude",
|
|
@@ -2031,7 +2354,7 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2031
2354
|
const wsDir = join5(projectPath, ".bridge-clients", clientId);
|
|
2032
2355
|
const isNew = !existsSync3(wsDir);
|
|
2033
2356
|
mkdirSync4(wsDir, { recursive: true });
|
|
2034
|
-
const entries =
|
|
2357
|
+
const entries = readdirSync2(projectPath, { withFileTypes: true });
|
|
2035
2358
|
for (const entry of entries) {
|
|
2036
2359
|
if (!shouldInclude(entry.name)) continue;
|
|
2037
2360
|
const link = join5(wsDir, entry.name);
|
|
@@ -2041,7 +2364,7 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2041
2364
|
} catch {
|
|
2042
2365
|
}
|
|
2043
2366
|
const target = join5(projectPath, entry.name);
|
|
2044
|
-
const relTarget =
|
|
2367
|
+
const relTarget = relative2(wsDir, target);
|
|
2045
2368
|
try {
|
|
2046
2369
|
symlinkSync(relTarget, link);
|
|
2047
2370
|
} catch (err) {
|
|
@@ -2056,11 +2379,11 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2056
2379
|
|
|
2057
2380
|
// src/adapters/claude.ts
|
|
2058
2381
|
import { writeFile, mkdir, stat as stat2 } from "fs/promises";
|
|
2059
|
-
import { join as join7, relative as
|
|
2382
|
+
import { join as join7, relative as relative4, basename } from "path";
|
|
2060
2383
|
|
|
2061
2384
|
// src/utils/auto-upload.ts
|
|
2062
2385
|
import { readdir, readFile, stat } from "fs/promises";
|
|
2063
|
-
import { join as join6, relative as
|
|
2386
|
+
import { join as join6, relative as relative3 } from "path";
|
|
2064
2387
|
var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
|
|
2065
2388
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2066
2389
|
".git",
|
|
@@ -2116,153 +2439,6 @@ async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
|
2116
2439
|
return files;
|
|
2117
2440
|
}
|
|
2118
2441
|
|
|
2119
|
-
// src/utils/zip.ts
|
|
2120
|
-
import { deflateRawSync, inflateRawSync } from "zlib";
|
|
2121
|
-
function dosTime(date) {
|
|
2122
|
-
return {
|
|
2123
|
-
time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
|
|
2124
|
-
date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
function crc32(buf) {
|
|
2128
|
-
let crc = 4294967295;
|
|
2129
|
-
for (let i = 0; i < buf.length; i++) {
|
|
2130
|
-
crc ^= buf[i];
|
|
2131
|
-
for (let j = 0; j < 8; j++) {
|
|
2132
|
-
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
2133
|
-
}
|
|
2134
|
-
}
|
|
2135
|
-
return (crc ^ 4294967295) >>> 0;
|
|
2136
|
-
}
|
|
2137
|
-
function writeUint16LE(buf, val, offset) {
|
|
2138
|
-
buf[offset] = val & 255;
|
|
2139
|
-
buf[offset + 1] = val >>> 8 & 255;
|
|
2140
|
-
}
|
|
2141
|
-
function writeUint32LE(buf, val, offset) {
|
|
2142
|
-
buf[offset] = val & 255;
|
|
2143
|
-
buf[offset + 1] = val >>> 8 & 255;
|
|
2144
|
-
buf[offset + 2] = val >>> 16 & 255;
|
|
2145
|
-
buf[offset + 3] = val >>> 24 & 255;
|
|
2146
|
-
}
|
|
2147
|
-
function createZipBuffer(entries) {
|
|
2148
|
-
const now = /* @__PURE__ */ new Date();
|
|
2149
|
-
const { time, date } = dosTime(now);
|
|
2150
|
-
const records = [];
|
|
2151
|
-
const chunks = [];
|
|
2152
|
-
let offset = 0;
|
|
2153
|
-
for (const entry of entries) {
|
|
2154
|
-
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
2155
|
-
const crc = crc32(entry.data);
|
|
2156
|
-
const compressed = deflateRawSync(entry.data, { level: 6 });
|
|
2157
|
-
const compressedSize = compressed.length;
|
|
2158
|
-
const uncompressedSize = entry.data.length;
|
|
2159
|
-
const header = Buffer.alloc(30 + nameBytes.length);
|
|
2160
|
-
writeUint32LE(header, 67324752, 0);
|
|
2161
|
-
writeUint16LE(header, 20, 4);
|
|
2162
|
-
writeUint16LE(header, 0, 6);
|
|
2163
|
-
writeUint16LE(header, 8, 8);
|
|
2164
|
-
writeUint16LE(header, time, 10);
|
|
2165
|
-
writeUint16LE(header, date, 12);
|
|
2166
|
-
writeUint32LE(header, crc, 14);
|
|
2167
|
-
writeUint32LE(header, compressedSize, 18);
|
|
2168
|
-
writeUint32LE(header, uncompressedSize, 22);
|
|
2169
|
-
writeUint16LE(header, nameBytes.length, 26);
|
|
2170
|
-
writeUint16LE(header, 0, 28);
|
|
2171
|
-
nameBytes.copy(header, 30);
|
|
2172
|
-
records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
|
|
2173
|
-
chunks.push(header, compressed);
|
|
2174
|
-
offset += header.length + compressed.length;
|
|
2175
|
-
}
|
|
2176
|
-
const centralDirStart = offset;
|
|
2177
|
-
for (let i = 0; i < entries.length; i++) {
|
|
2178
|
-
const entry = entries[i];
|
|
2179
|
-
const rec = records[i];
|
|
2180
|
-
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
2181
|
-
const cdh = Buffer.alloc(46 + nameBytes.length);
|
|
2182
|
-
writeUint32LE(cdh, 33639248, 0);
|
|
2183
|
-
writeUint16LE(cdh, 20, 4);
|
|
2184
|
-
writeUint16LE(cdh, 20, 6);
|
|
2185
|
-
writeUint16LE(cdh, 0, 8);
|
|
2186
|
-
writeUint16LE(cdh, 8, 10);
|
|
2187
|
-
writeUint16LE(cdh, time, 12);
|
|
2188
|
-
writeUint16LE(cdh, date, 14);
|
|
2189
|
-
writeUint32LE(cdh, rec.crc, 16);
|
|
2190
|
-
writeUint32LE(cdh, rec.compressedSize, 20);
|
|
2191
|
-
writeUint32LE(cdh, rec.uncompressedSize, 24);
|
|
2192
|
-
writeUint16LE(cdh, nameBytes.length, 28);
|
|
2193
|
-
writeUint16LE(cdh, 0, 30);
|
|
2194
|
-
writeUint16LE(cdh, 0, 32);
|
|
2195
|
-
writeUint16LE(cdh, 0, 34);
|
|
2196
|
-
writeUint16LE(cdh, 0, 36);
|
|
2197
|
-
writeUint32LE(cdh, 0, 38);
|
|
2198
|
-
writeUint32LE(cdh, rec.offset, 42);
|
|
2199
|
-
nameBytes.copy(cdh, 46);
|
|
2200
|
-
chunks.push(cdh);
|
|
2201
|
-
offset += cdh.length;
|
|
2202
|
-
}
|
|
2203
|
-
const centralDirSize = offset - centralDirStart;
|
|
2204
|
-
const eocd = Buffer.alloc(22);
|
|
2205
|
-
writeUint32LE(eocd, 101010256, 0);
|
|
2206
|
-
writeUint16LE(eocd, 0, 4);
|
|
2207
|
-
writeUint16LE(eocd, 0, 6);
|
|
2208
|
-
writeUint16LE(eocd, entries.length, 8);
|
|
2209
|
-
writeUint16LE(eocd, entries.length, 10);
|
|
2210
|
-
writeUint32LE(eocd, centralDirSize, 12);
|
|
2211
|
-
writeUint32LE(eocd, centralDirStart, 16);
|
|
2212
|
-
writeUint16LE(eocd, 0, 20);
|
|
2213
|
-
chunks.push(eocd);
|
|
2214
|
-
return Buffer.concat(chunks);
|
|
2215
|
-
}
|
|
2216
|
-
function extractZipBuffer(buf) {
|
|
2217
|
-
const entries = [];
|
|
2218
|
-
let eocdOffset = -1;
|
|
2219
|
-
for (let i = buf.length - 22; i >= 0; i--) {
|
|
2220
|
-
if (buf.readUInt32LE(i) === 101010256) {
|
|
2221
|
-
eocdOffset = i;
|
|
2222
|
-
break;
|
|
2223
|
-
}
|
|
2224
|
-
}
|
|
2225
|
-
if (eocdOffset === -1) {
|
|
2226
|
-
throw new Error("Invalid ZIP: EOCD not found");
|
|
2227
|
-
}
|
|
2228
|
-
const entryCount = buf.readUInt16LE(eocdOffset + 10);
|
|
2229
|
-
const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
2230
|
-
let offset = centralDirOffset;
|
|
2231
|
-
for (let i = 0; i < entryCount; i++) {
|
|
2232
|
-
if (buf.readUInt32LE(offset) !== 33639248) {
|
|
2233
|
-
throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
|
|
2234
|
-
}
|
|
2235
|
-
const compressionMethod = buf.readUInt16LE(offset + 10);
|
|
2236
|
-
const compressedSize = buf.readUInt32LE(offset + 20);
|
|
2237
|
-
const uncompressedSize = buf.readUInt32LE(offset + 24);
|
|
2238
|
-
const nameLen = buf.readUInt16LE(offset + 28);
|
|
2239
|
-
const extraLen = buf.readUInt16LE(offset + 30);
|
|
2240
|
-
const commentLen = buf.readUInt16LE(offset + 32);
|
|
2241
|
-
const localHeaderOffset = buf.readUInt32LE(offset + 42);
|
|
2242
|
-
const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
|
|
2243
|
-
if (!name.endsWith("/")) {
|
|
2244
|
-
const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
|
|
2245
|
-
const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
|
|
2246
|
-
const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
2247
|
-
const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
|
|
2248
|
-
let data;
|
|
2249
|
-
if (compressionMethod === 0) {
|
|
2250
|
-
data = Buffer.from(compressedData);
|
|
2251
|
-
} else if (compressionMethod === 8) {
|
|
2252
|
-
data = inflateRawSync(compressedData);
|
|
2253
|
-
} else {
|
|
2254
|
-
throw new Error(`Unsupported compression method: ${compressionMethod}`);
|
|
2255
|
-
}
|
|
2256
|
-
if (data.length !== uncompressedSize) {
|
|
2257
|
-
throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
|
|
2258
|
-
}
|
|
2259
|
-
entries.push({ path: name, data });
|
|
2260
|
-
}
|
|
2261
|
-
offset += 46 + nameLen + extraLen + commentLen;
|
|
2262
|
-
}
|
|
2263
|
-
return entries;
|
|
2264
|
-
}
|
|
2265
|
-
|
|
2266
2442
|
// src/adapters/claude.ts
|
|
2267
2443
|
var DEFAULT_IDLE_TIMEOUT = 30 * 60 * 1e3;
|
|
2268
2444
|
var MIN_IDLE_TIMEOUT = 60 * 1e3;
|
|
@@ -2425,7 +2601,7 @@ var ClaudeSession = class {
|
|
|
2425
2601
|
const entries = [];
|
|
2426
2602
|
let totalBytes = 0;
|
|
2427
2603
|
for (const absPath of files) {
|
|
2428
|
-
const relPath =
|
|
2604
|
+
const relPath = relative4(workspaceRoot, absPath).replace(/\\/g, "/");
|
|
2429
2605
|
if (!relPath || relPath.startsWith("..")) continue;
|
|
2430
2606
|
try {
|
|
2431
2607
|
const fileStat = await stat2(absPath);
|
|
@@ -2714,7 +2890,7 @@ function logWorkspaceHint(slug, projectPath) {
|
|
|
2714
2890
|
console.log(` ${GRAY}${getWorkspaceHint()}${RESET}`);
|
|
2715
2891
|
}
|
|
2716
2892
|
function sleep2(ms) {
|
|
2717
|
-
return new Promise((
|
|
2893
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2718
2894
|
}
|
|
2719
2895
|
function createAdapter(type, config) {
|
|
2720
2896
|
switch (type) {
|
|
@@ -2985,7 +3161,7 @@ async function pollForToken(baseUrl, deviceCode, expiresIn, interval) {
|
|
|
2985
3161
|
const deadline = Date.now() + expiresIn * 1e3;
|
|
2986
3162
|
let pollMs = Math.max(interval * 1e3, POLL_INTERVAL_MS);
|
|
2987
3163
|
while (Date.now() < deadline) {
|
|
2988
|
-
await new Promise((
|
|
3164
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
2989
3165
|
const res = await fetch(`${baseUrl}/api/auth/device/token`, {
|
|
2990
3166
|
method: "POST",
|
|
2991
3167
|
headers: { "Content-Type": "application/json" },
|
|
@@ -3112,7 +3288,7 @@ function registerStatusCommand(program2) {
|
|
|
3112
3288
|
|
|
3113
3289
|
// src/commands/start.ts
|
|
3114
3290
|
function sleep3(ms) {
|
|
3115
|
-
return new Promise((
|
|
3291
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3116
3292
|
}
|
|
3117
3293
|
function registerStartCommand(program2) {
|
|
3118
3294
|
program2.command("start [name]").description("Start agent(s) in the background").option("--all", "Start all registered agents").action(async (name, opts) => {
|
|
@@ -3207,7 +3383,7 @@ function registerStopCommand(program2) {
|
|
|
3207
3383
|
|
|
3208
3384
|
// src/commands/restart.ts
|
|
3209
3385
|
function sleep4(ms) {
|
|
3210
|
-
return new Promise((
|
|
3386
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3211
3387
|
}
|
|
3212
3388
|
function registerRestartCommand(program2) {
|
|
3213
3389
|
program2.command("restart [name]").description("Restart agent(s)").option("--all", "Restart all registered agents").action(async (name, opts) => {
|
|
@@ -3294,10 +3470,10 @@ import { createInterface as createInterface2 } from "readline";
|
|
|
3294
3470
|
import { unlinkSync } from "fs";
|
|
3295
3471
|
function confirm(prompt) {
|
|
3296
3472
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
3297
|
-
return new Promise((
|
|
3473
|
+
return new Promise((resolve3) => {
|
|
3298
3474
|
rl.question(prompt, (answer) => {
|
|
3299
3475
|
rl.close();
|
|
3300
|
-
|
|
3476
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
3301
3477
|
});
|
|
3302
3478
|
});
|
|
3303
3479
|
}
|
|
@@ -3350,7 +3526,7 @@ function registerOpenCommand(program2) {
|
|
|
3350
3526
|
import { writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
3351
3527
|
import { join as join8 } from "path";
|
|
3352
3528
|
import { homedir as homedir5 } from "os";
|
|
3353
|
-
import { execSync as
|
|
3529
|
+
import { execSync as execSync4 } from "child_process";
|
|
3354
3530
|
var LABEL = "com.agents-hot.agent-mesh";
|
|
3355
3531
|
var PLIST_DIR = join8(homedir5(), "Library", "LaunchAgents");
|
|
3356
3532
|
var PLIST_PATH = join8(PLIST_DIR, `${LABEL}.plist`);
|
|
@@ -3416,17 +3592,17 @@ function registerInstallCommand(program2) {
|
|
|
3416
3592
|
}
|
|
3417
3593
|
if (existsSync5(PLIST_PATH)) {
|
|
3418
3594
|
try {
|
|
3419
|
-
|
|
3595
|
+
execSync4(`launchctl bootout gui/$(id -u) "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
3420
3596
|
} catch {
|
|
3421
3597
|
}
|
|
3422
3598
|
}
|
|
3423
3599
|
const plist = generatePlist(node, script);
|
|
3424
3600
|
writeFileSync3(PLIST_PATH, plist, { encoding: "utf-8" });
|
|
3425
3601
|
try {
|
|
3426
|
-
|
|
3602
|
+
execSync4(`launchctl bootstrap gui/$(id -u) "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
3427
3603
|
} catch {
|
|
3428
3604
|
try {
|
|
3429
|
-
|
|
3605
|
+
execSync4(`launchctl load "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
3430
3606
|
} catch (err) {
|
|
3431
3607
|
log.error(`Failed to load LaunchAgent: ${err}`);
|
|
3432
3608
|
log.info(`Plist written to ${PLIST_PATH} \u2014 you can load it manually.`);
|
|
@@ -3450,7 +3626,7 @@ function registerInstallCommand(program2) {
|
|
|
3450
3626
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
3451
3627
|
import { join as join9 } from "path";
|
|
3452
3628
|
import { homedir as homedir6 } from "os";
|
|
3453
|
-
import { execSync as
|
|
3629
|
+
import { execSync as execSync5 } from "child_process";
|
|
3454
3630
|
var LABEL2 = "com.agents-hot.agent-mesh";
|
|
3455
3631
|
var PLIST_PATH2 = join9(homedir6(), "Library", "LaunchAgents", `${LABEL2}.plist`);
|
|
3456
3632
|
function registerUninstallCommand(program2) {
|
|
@@ -3466,10 +3642,10 @@ function registerUninstallCommand(program2) {
|
|
|
3466
3642
|
return;
|
|
3467
3643
|
}
|
|
3468
3644
|
try {
|
|
3469
|
-
|
|
3645
|
+
execSync5(`launchctl bootout gui/$(id -u) "${PLIST_PATH2}" 2>/dev/null`, { stdio: "ignore" });
|
|
3470
3646
|
} catch {
|
|
3471
3647
|
try {
|
|
3472
|
-
|
|
3648
|
+
execSync5(`launchctl unload "${PLIST_PATH2}" 2>/dev/null`, { stdio: "ignore" });
|
|
3473
3649
|
} catch {
|
|
3474
3650
|
}
|
|
3475
3651
|
}
|
|
@@ -3670,10 +3846,10 @@ function parseVisibilityOrExit(input) {
|
|
|
3670
3846
|
}
|
|
3671
3847
|
function readLine(prompt) {
|
|
3672
3848
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
3673
|
-
return new Promise((
|
|
3849
|
+
return new Promise((resolve3) => {
|
|
3674
3850
|
rl.question(prompt, (answer) => {
|
|
3675
3851
|
rl.close();
|
|
3676
|
-
|
|
3852
|
+
resolve3(answer.trim());
|
|
3677
3853
|
});
|
|
3678
3854
|
});
|
|
3679
3855
|
}
|
|
@@ -3866,7 +4042,7 @@ function registerAgentsCommand(program2) {
|
|
|
3866
4042
|
import { createInterface as createInterface4 } from "readline";
|
|
3867
4043
|
import { existsSync as existsSync7, readFileSync as readFileSync2, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2 } from "fs";
|
|
3868
4044
|
import { basename as basename2, join as join10 } from "path";
|
|
3869
|
-
import { execSync as
|
|
4045
|
+
import { execSync as execSync6 } from "child_process";
|
|
3870
4046
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
3871
4047
|
import { tmpdir } from "os";
|
|
3872
4048
|
|
|
@@ -3898,10 +4074,10 @@ function prepareUploadFile(filePath) {
|
|
|
3898
4074
|
const tempFile = join10(tempDir, fileName);
|
|
3899
4075
|
copyFileSync2(filePath, tempFile);
|
|
3900
4076
|
const zipPath = join10(tempDir, "upload.zip");
|
|
3901
|
-
|
|
4077
|
+
execSync6(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
|
|
3902
4078
|
const zipBuffer = readFileSync2(zipPath);
|
|
3903
4079
|
try {
|
|
3904
|
-
|
|
4080
|
+
execSync6(`rm -rf "${tempDir}"`);
|
|
3905
4081
|
} catch {
|
|
3906
4082
|
}
|
|
3907
4083
|
const zipSha256 = createHash2("sha256").update(zipBuffer).digest("hex");
|
|
@@ -3917,7 +4093,7 @@ function prepareUploadFile(filePath) {
|
|
|
3917
4093
|
};
|
|
3918
4094
|
}
|
|
3919
4095
|
function sleep5(ms) {
|
|
3920
|
-
return new Promise((
|
|
4096
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3921
4097
|
}
|
|
3922
4098
|
async function chatWebrtcUpload(agentId, offer, zipBuffer, token, baseUrl) {
|
|
3923
4099
|
log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
|
|
@@ -4276,8 +4452,8 @@ function registerChatCommand(program2) {
|
|
|
4276
4452
|
}
|
|
4277
4453
|
|
|
4278
4454
|
// src/commands/skills.ts
|
|
4279
|
-
import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink } from "fs/promises";
|
|
4280
|
-
import { join as join12, resolve, relative as
|
|
4455
|
+
import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink as unlink2 } from "fs/promises";
|
|
4456
|
+
import { join as join12, resolve as resolve2, relative as relative5 } from "path";
|
|
4281
4457
|
|
|
4282
4458
|
// src/utils/skill-parser.ts
|
|
4283
4459
|
import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
|
|
@@ -4451,7 +4627,7 @@ function outputError(error, message, hint) {
|
|
|
4451
4627
|
process.exit(1);
|
|
4452
4628
|
}
|
|
4453
4629
|
function resolveSkillDir(pathArg) {
|
|
4454
|
-
return pathArg ?
|
|
4630
|
+
return pathArg ? resolve2(pathArg) : process.cwd();
|
|
4455
4631
|
}
|
|
4456
4632
|
function parseSkillRef(ref) {
|
|
4457
4633
|
if (!ref.includes("/")) {
|
|
@@ -4467,7 +4643,7 @@ function skillApiPath(authorLogin, slug) {
|
|
|
4467
4643
|
return `/api/skills/${encodeURIComponent(authorLogin)}/${encodeURIComponent(slug)}`;
|
|
4468
4644
|
}
|
|
4469
4645
|
async function resolveSkillsRootAsync(pathArg) {
|
|
4470
|
-
const projectRoot = pathArg ?
|
|
4646
|
+
const projectRoot = pathArg ? resolve2(pathArg) : process.cwd();
|
|
4471
4647
|
const skillsDir = join12(projectRoot, ".agents", "skills");
|
|
4472
4648
|
const claudeSkillsDir = join12(projectRoot, ".claude", "skills");
|
|
4473
4649
|
return { projectRoot, skillsDir, claudeSkillsDir };
|
|
@@ -4476,7 +4652,7 @@ async function ensureClaudeSymlink(claudeSkillsDir, slug) {
|
|
|
4476
4652
|
await mkdir2(claudeSkillsDir, { recursive: true });
|
|
4477
4653
|
const linkPath = join12(claudeSkillsDir, slug);
|
|
4478
4654
|
try {
|
|
4479
|
-
await
|
|
4655
|
+
await unlink2(linkPath);
|
|
4480
4656
|
} catch {
|
|
4481
4657
|
}
|
|
4482
4658
|
try {
|
|
@@ -4489,7 +4665,7 @@ async function collectPackFiles(dir, manifest) {
|
|
|
4489
4665
|
const results = [];
|
|
4490
4666
|
const all = await walkDir(dir);
|
|
4491
4667
|
for (const f of all) {
|
|
4492
|
-
const rel =
|
|
4668
|
+
const rel = relative5(dir, f);
|
|
4493
4669
|
results.push(rel);
|
|
4494
4670
|
}
|
|
4495
4671
|
const mainFile = manifest.main || "SKILL.md";
|
|
@@ -4963,7 +5139,7 @@ function registerSkillsCommand(program2) {
|
|
|
4963
5139
|
}
|
|
4964
5140
|
await rm(targetDir, { recursive: true, force: true });
|
|
4965
5141
|
try {
|
|
4966
|
-
await
|
|
5142
|
+
await unlink2(join12(claudeSkillsDir, slug));
|
|
4967
5143
|
} catch {
|
|
4968
5144
|
}
|
|
4969
5145
|
slog.success(`Removed skill: ${slug}`);
|
|
@@ -5101,7 +5277,7 @@ function registerDiscoverCommand(program2) {
|
|
|
5101
5277
|
|
|
5102
5278
|
// src/commands/call.ts
|
|
5103
5279
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7, copyFileSync as copyFileSync3, existsSync as existsSync8 } from "fs";
|
|
5104
|
-
import { execSync as
|
|
5280
|
+
import { execSync as execSync7 } from "child_process";
|
|
5105
5281
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
5106
5282
|
import { join as join13, basename as basename3 } from "path";
|
|
5107
5283
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -5126,7 +5302,7 @@ async function submitRating(baseUrl, token, agentId, callId, rating) {
|
|
|
5126
5302
|
}
|
|
5127
5303
|
}
|
|
5128
5304
|
function sleep6(ms) {
|
|
5129
|
-
return new Promise((
|
|
5305
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5130
5306
|
}
|
|
5131
5307
|
function handleError2(err) {
|
|
5132
5308
|
if (err instanceof PlatformApiError) {
|
|
@@ -5201,13 +5377,13 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
|
|
|
5201
5377
|
const zipPath = join13(outputDir, ".transfer.zip");
|
|
5202
5378
|
writeFileSync4(zipPath, zipBuffer);
|
|
5203
5379
|
try {
|
|
5204
|
-
|
|
5380
|
+
safeUnzip(zipPath, outputDir);
|
|
5205
5381
|
try {
|
|
5206
|
-
|
|
5382
|
+
execSync7(`rm "${zipPath}"`);
|
|
5207
5383
|
} catch {
|
|
5208
5384
|
}
|
|
5209
|
-
} catch {
|
|
5210
|
-
log.warn(`Failed to extract ZIP. Saved to: ${zipPath}`);
|
|
5385
|
+
} catch (unzipErr) {
|
|
5386
|
+
log.warn(`Failed to extract ZIP: ${unzipErr.message}. Saved to: ${zipPath}`);
|
|
5211
5387
|
return;
|
|
5212
5388
|
}
|
|
5213
5389
|
if (json) {
|
|
@@ -5239,10 +5415,10 @@ function prepareFileForUpload(filePath) {
|
|
|
5239
5415
|
const tempFile = join13(tempDir, fileName);
|
|
5240
5416
|
copyFileSync3(filePath, tempFile);
|
|
5241
5417
|
const zipPath = join13(tempDir, "upload.zip");
|
|
5242
|
-
|
|
5418
|
+
execSync7(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
|
|
5243
5419
|
const zipBuffer = readFileSync3(zipPath);
|
|
5244
5420
|
try {
|
|
5245
|
-
|
|
5421
|
+
execSync7(`rm -rf "${tempDir}"`);
|
|
5246
5422
|
} catch {
|
|
5247
5423
|
}
|
|
5248
5424
|
const zipSha256 = createHash3("sha256").update(zipBuffer).digest("hex");
|