@annals/agent-mesh 0.18.3 → 0.18.5
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 +402 -227
- 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) {
|
|
@@ -589,24 +589,27 @@ var FileUploadReceiver = class {
|
|
|
589
589
|
});
|
|
590
590
|
});
|
|
591
591
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
this.peer.
|
|
602
|
-
|
|
603
|
-
|
|
592
|
+
try {
|
|
593
|
+
if (signal.signal_type === "offer" || signal.signal_type === "answer") {
|
|
594
|
+
this.peer.setRemoteDescription(signal.payload, signal.signal_type);
|
|
595
|
+
for (const c of this.pendingCandidates) {
|
|
596
|
+
this.peer.addRemoteCandidate(c.candidate, c.mid);
|
|
597
|
+
}
|
|
598
|
+
this.pendingCandidates = [];
|
|
599
|
+
} else if (signal.signal_type === "candidate") {
|
|
600
|
+
const { candidate, mid } = JSON.parse(signal.payload);
|
|
601
|
+
if (this.peer.remoteDescription()) {
|
|
602
|
+
this.peer.addRemoteCandidate(candidate, mid);
|
|
603
|
+
} else {
|
|
604
|
+
this.pendingCandidates.push({ candidate, mid });
|
|
605
|
+
}
|
|
604
606
|
}
|
|
607
|
+
} catch {
|
|
605
608
|
}
|
|
606
609
|
}
|
|
607
610
|
waitForCompletion(timeoutMs = 3e4) {
|
|
608
|
-
return new Promise((
|
|
609
|
-
this.resolveComplete =
|
|
611
|
+
return new Promise((resolve3, reject) => {
|
|
612
|
+
this.resolveComplete = resolve3;
|
|
610
613
|
this.rejectComplete = reject;
|
|
611
614
|
setTimeout(() => {
|
|
612
615
|
if (!this.closed) {
|
|
@@ -669,10 +672,10 @@ var FileUploadSender = class {
|
|
|
669
672
|
this.peer = new ndc.PeerConnection("upload-sender", {
|
|
670
673
|
iceServers: ICE_SERVERS
|
|
671
674
|
});
|
|
672
|
-
return new Promise((
|
|
675
|
+
return new Promise((resolve3) => {
|
|
673
676
|
this.peer.onLocalDescription((sdp, type) => {
|
|
674
677
|
if (type === "offer") {
|
|
675
|
-
|
|
678
|
+
resolve3(sdp);
|
|
676
679
|
}
|
|
677
680
|
this.signalCallback?.({ signal_type: type, payload: sdp });
|
|
678
681
|
});
|
|
@@ -709,8 +712,8 @@ var FileUploadSender = class {
|
|
|
709
712
|
}
|
|
710
713
|
}
|
|
711
714
|
waitForCompletion(timeoutMs = 3e4) {
|
|
712
|
-
return new Promise((
|
|
713
|
-
this.resolveComplete =
|
|
715
|
+
return new Promise((resolve3, reject) => {
|
|
716
|
+
this.resolveComplete = resolve3;
|
|
714
717
|
this.rejectComplete = reject;
|
|
715
718
|
setTimeout(() => {
|
|
716
719
|
if (!this.closed) {
|
|
@@ -830,7 +833,7 @@ var LocalRuntimeQueueError = class extends Error {
|
|
|
830
833
|
}
|
|
831
834
|
};
|
|
832
835
|
function sleep(ms) {
|
|
833
|
-
return new Promise((
|
|
836
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
834
837
|
}
|
|
835
838
|
function isProcessAlive2(pid) {
|
|
836
839
|
try {
|
|
@@ -1149,14 +1152,193 @@ function createLocalRuntimeQueue(config) {
|
|
|
1149
1152
|
}
|
|
1150
1153
|
|
|
1151
1154
|
// src/bridge/manager.ts
|
|
1152
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1153
|
-
import {
|
|
1155
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readdirSync } from "fs";
|
|
1156
|
+
import { readFile as readFileAsync, writeFile as writeFileAsync, unlink, mkdir as mkdirAsync } from "fs/promises";
|
|
1157
|
+
import { execSync as execSync2 } from "child_process";
|
|
1154
1158
|
import { join as join3 } from "path";
|
|
1155
1159
|
import { homedir as homedir2 } from "os";
|
|
1160
|
+
|
|
1161
|
+
// src/utils/zip.ts
|
|
1162
|
+
import { deflateRawSync, inflateRawSync } from "zlib";
|
|
1163
|
+
import { execSync } from "child_process";
|
|
1164
|
+
import { resolve, relative } from "path";
|
|
1165
|
+
function dosTime(date) {
|
|
1166
|
+
return {
|
|
1167
|
+
time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
|
|
1168
|
+
date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
function crc32(buf) {
|
|
1172
|
+
let crc = 4294967295;
|
|
1173
|
+
for (let i = 0; i < buf.length; i++) {
|
|
1174
|
+
crc ^= buf[i];
|
|
1175
|
+
for (let j = 0; j < 8; j++) {
|
|
1176
|
+
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return (crc ^ 4294967295) >>> 0;
|
|
1180
|
+
}
|
|
1181
|
+
function writeUint16LE(buf, val, offset) {
|
|
1182
|
+
buf[offset] = val & 255;
|
|
1183
|
+
buf[offset + 1] = val >>> 8 & 255;
|
|
1184
|
+
}
|
|
1185
|
+
function writeUint32LE(buf, val, offset) {
|
|
1186
|
+
buf[offset] = val & 255;
|
|
1187
|
+
buf[offset + 1] = val >>> 8 & 255;
|
|
1188
|
+
buf[offset + 2] = val >>> 16 & 255;
|
|
1189
|
+
buf[offset + 3] = val >>> 24 & 255;
|
|
1190
|
+
}
|
|
1191
|
+
function createZipBuffer(entries) {
|
|
1192
|
+
const now = /* @__PURE__ */ new Date();
|
|
1193
|
+
const { time, date } = dosTime(now);
|
|
1194
|
+
const records = [];
|
|
1195
|
+
const chunks = [];
|
|
1196
|
+
let offset = 0;
|
|
1197
|
+
for (const entry of entries) {
|
|
1198
|
+
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
1199
|
+
const crc = crc32(entry.data);
|
|
1200
|
+
const compressed = deflateRawSync(entry.data, { level: 6 });
|
|
1201
|
+
const compressedSize = compressed.length;
|
|
1202
|
+
const uncompressedSize = entry.data.length;
|
|
1203
|
+
const header = Buffer.alloc(30 + nameBytes.length);
|
|
1204
|
+
writeUint32LE(header, 67324752, 0);
|
|
1205
|
+
writeUint16LE(header, 20, 4);
|
|
1206
|
+
writeUint16LE(header, 0, 6);
|
|
1207
|
+
writeUint16LE(header, 8, 8);
|
|
1208
|
+
writeUint16LE(header, time, 10);
|
|
1209
|
+
writeUint16LE(header, date, 12);
|
|
1210
|
+
writeUint32LE(header, crc, 14);
|
|
1211
|
+
writeUint32LE(header, compressedSize, 18);
|
|
1212
|
+
writeUint32LE(header, uncompressedSize, 22);
|
|
1213
|
+
writeUint16LE(header, nameBytes.length, 26);
|
|
1214
|
+
writeUint16LE(header, 0, 28);
|
|
1215
|
+
nameBytes.copy(header, 30);
|
|
1216
|
+
records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
|
|
1217
|
+
chunks.push(header, compressed);
|
|
1218
|
+
offset += header.length + compressed.length;
|
|
1219
|
+
}
|
|
1220
|
+
const centralDirStart = offset;
|
|
1221
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1222
|
+
const entry = entries[i];
|
|
1223
|
+
const rec = records[i];
|
|
1224
|
+
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
1225
|
+
const cdh = Buffer.alloc(46 + nameBytes.length);
|
|
1226
|
+
writeUint32LE(cdh, 33639248, 0);
|
|
1227
|
+
writeUint16LE(cdh, 20, 4);
|
|
1228
|
+
writeUint16LE(cdh, 20, 6);
|
|
1229
|
+
writeUint16LE(cdh, 0, 8);
|
|
1230
|
+
writeUint16LE(cdh, 8, 10);
|
|
1231
|
+
writeUint16LE(cdh, time, 12);
|
|
1232
|
+
writeUint16LE(cdh, date, 14);
|
|
1233
|
+
writeUint32LE(cdh, rec.crc, 16);
|
|
1234
|
+
writeUint32LE(cdh, rec.compressedSize, 20);
|
|
1235
|
+
writeUint32LE(cdh, rec.uncompressedSize, 24);
|
|
1236
|
+
writeUint16LE(cdh, nameBytes.length, 28);
|
|
1237
|
+
writeUint16LE(cdh, 0, 30);
|
|
1238
|
+
writeUint16LE(cdh, 0, 32);
|
|
1239
|
+
writeUint16LE(cdh, 0, 34);
|
|
1240
|
+
writeUint16LE(cdh, 0, 36);
|
|
1241
|
+
writeUint32LE(cdh, 0, 38);
|
|
1242
|
+
writeUint32LE(cdh, rec.offset, 42);
|
|
1243
|
+
nameBytes.copy(cdh, 46);
|
|
1244
|
+
chunks.push(cdh);
|
|
1245
|
+
offset += cdh.length;
|
|
1246
|
+
}
|
|
1247
|
+
const centralDirSize = offset - centralDirStart;
|
|
1248
|
+
const eocd = Buffer.alloc(22);
|
|
1249
|
+
writeUint32LE(eocd, 101010256, 0);
|
|
1250
|
+
writeUint16LE(eocd, 0, 4);
|
|
1251
|
+
writeUint16LE(eocd, 0, 6);
|
|
1252
|
+
writeUint16LE(eocd, entries.length, 8);
|
|
1253
|
+
writeUint16LE(eocd, entries.length, 10);
|
|
1254
|
+
writeUint32LE(eocd, centralDirSize, 12);
|
|
1255
|
+
writeUint32LE(eocd, centralDirStart, 16);
|
|
1256
|
+
writeUint16LE(eocd, 0, 20);
|
|
1257
|
+
chunks.push(eocd);
|
|
1258
|
+
return Buffer.concat(chunks);
|
|
1259
|
+
}
|
|
1260
|
+
function safeUnzip(zipPath, destDir) {
|
|
1261
|
+
const absDestDir = resolve(destDir);
|
|
1262
|
+
const listing = execSync(`unzip -l "${zipPath}"`, { encoding: "utf-8" });
|
|
1263
|
+
const lines = listing.split("\n");
|
|
1264
|
+
for (const line of lines) {
|
|
1265
|
+
const match = line.match(/^\s*\d+\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+(.+)$/);
|
|
1266
|
+
if (!match) continue;
|
|
1267
|
+
const entryPath = match[1].trim();
|
|
1268
|
+
if (!entryPath) continue;
|
|
1269
|
+
if (entryPath.startsWith("/")) {
|
|
1270
|
+
throw new Error(`Blocked: ZIP contains absolute path entry: ${entryPath}`);
|
|
1271
|
+
}
|
|
1272
|
+
if (entryPath.includes("..")) {
|
|
1273
|
+
throw new Error(`Blocked: ZIP contains path traversal entry: ${entryPath}`);
|
|
1274
|
+
}
|
|
1275
|
+
const resolved = resolve(absDestDir, entryPath);
|
|
1276
|
+
const rel = relative(absDestDir, resolved);
|
|
1277
|
+
if (rel.startsWith("..") || resolve(resolved) !== resolved.replace(/\/$/, "")) {
|
|
1278
|
+
if (rel.startsWith("..")) {
|
|
1279
|
+
throw new Error(`Blocked: ZIP entry escapes target directory: ${entryPath}`);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
execSync(`unzip -o -q "${zipPath}" -d "${destDir}"`);
|
|
1284
|
+
}
|
|
1285
|
+
function extractZipBuffer(buf) {
|
|
1286
|
+
const entries = [];
|
|
1287
|
+
let eocdOffset = -1;
|
|
1288
|
+
for (let i = buf.length - 22; i >= 0; i--) {
|
|
1289
|
+
if (buf.readUInt32LE(i) === 101010256) {
|
|
1290
|
+
eocdOffset = i;
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
if (eocdOffset === -1) {
|
|
1295
|
+
throw new Error("Invalid ZIP: EOCD not found");
|
|
1296
|
+
}
|
|
1297
|
+
const entryCount = buf.readUInt16LE(eocdOffset + 10);
|
|
1298
|
+
const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
1299
|
+
let offset = centralDirOffset;
|
|
1300
|
+
for (let i = 0; i < entryCount; i++) {
|
|
1301
|
+
if (buf.readUInt32LE(offset) !== 33639248) {
|
|
1302
|
+
throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
|
|
1303
|
+
}
|
|
1304
|
+
const compressionMethod = buf.readUInt16LE(offset + 10);
|
|
1305
|
+
const compressedSize = buf.readUInt32LE(offset + 20);
|
|
1306
|
+
const uncompressedSize = buf.readUInt32LE(offset + 24);
|
|
1307
|
+
const nameLen = buf.readUInt16LE(offset + 28);
|
|
1308
|
+
const extraLen = buf.readUInt16LE(offset + 30);
|
|
1309
|
+
const commentLen = buf.readUInt16LE(offset + 32);
|
|
1310
|
+
const localHeaderOffset = buf.readUInt32LE(offset + 42);
|
|
1311
|
+
const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
|
|
1312
|
+
if (!name.endsWith("/")) {
|
|
1313
|
+
const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
|
|
1314
|
+
const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
|
|
1315
|
+
const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
1316
|
+
const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
|
|
1317
|
+
let data;
|
|
1318
|
+
if (compressionMethod === 0) {
|
|
1319
|
+
data = Buffer.from(compressedData);
|
|
1320
|
+
} else if (compressionMethod === 8) {
|
|
1321
|
+
data = inflateRawSync(compressedData);
|
|
1322
|
+
} else {
|
|
1323
|
+
throw new Error(`Unsupported compression method: ${compressionMethod}`);
|
|
1324
|
+
}
|
|
1325
|
+
if (data.length !== uncompressedSize) {
|
|
1326
|
+
throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
|
|
1327
|
+
}
|
|
1328
|
+
entries.push({ path: name, data });
|
|
1329
|
+
}
|
|
1330
|
+
offset += 46 + nameLen + extraLen + commentLen;
|
|
1331
|
+
}
|
|
1332
|
+
return entries;
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
// src/bridge/manager.ts
|
|
1156
1336
|
var DUPLICATE_REQUEST_TTL_MS = 10 * 6e4;
|
|
1157
1337
|
var SESSION_SWEEP_INTERVAL_MS = 6e4;
|
|
1158
1338
|
var DEFAULT_SESSION_IDLE_TTL_MS = 10 * 6e4;
|
|
1159
1339
|
var MIN_SESSION_IDLE_TTL_MS = 6e4;
|
|
1340
|
+
var TRANSFER_ACTIVE_TTL_MS = 5 * 6e4;
|
|
1341
|
+
var TRANSFER_DORMANT_TTL_MS = 60 * 6e4;
|
|
1160
1342
|
function resolveSessionIdleTtlMs() {
|
|
1161
1343
|
const raw = process.env.AGENT_BRIDGE_SESSION_IDLE_TTL_MS;
|
|
1162
1344
|
if (!raw) {
|
|
@@ -1190,6 +1372,10 @@ var BridgeManager = class {
|
|
|
1190
1372
|
pendingTransfers = /* @__PURE__ */ new Map();
|
|
1191
1373
|
/** Pending WebRTC file uploads (caller→agent): transfer_id → FileUploadReceiver + cleanup timer */
|
|
1192
1374
|
pendingUploads = /* @__PURE__ */ new Map();
|
|
1375
|
+
/** Dormant transfers: ZIP evicted from memory, cached on disk for up to 1h */
|
|
1376
|
+
dormantTransfers = /* @__PURE__ */ new Map();
|
|
1377
|
+
/** Signals queued while a dormant transfer is being revived (prevents signal loss during async disk read) */
|
|
1378
|
+
revivingTransfers = /* @__PURE__ */ new Map();
|
|
1193
1379
|
constructor(opts) {
|
|
1194
1380
|
this.wsClient = opts.wsClient;
|
|
1195
1381
|
this.adapter = opts.adapter;
|
|
@@ -1202,6 +1388,7 @@ var BridgeManager = class {
|
|
|
1202
1388
|
this.pruneIdleSessions();
|
|
1203
1389
|
}, SESSION_SWEEP_INTERVAL_MS);
|
|
1204
1390
|
this.cleanupTimer.unref?.();
|
|
1391
|
+
void this.cleanupOrphanedTransferCache();
|
|
1205
1392
|
log.info(`Bridge manager started with ${this.adapter.displayName} adapter`);
|
|
1206
1393
|
}
|
|
1207
1394
|
stop() {
|
|
@@ -1617,11 +1804,27 @@ var BridgeManager = class {
|
|
|
1617
1804
|
};
|
|
1618
1805
|
this.wsClient.send(rtcSignal);
|
|
1619
1806
|
});
|
|
1807
|
+
const zipPath = this.transferCachePath(offer.transfer_id);
|
|
1808
|
+
void (async () => {
|
|
1809
|
+
try {
|
|
1810
|
+
await mkdirAsync(this.transferCacheDir(), { recursive: true });
|
|
1811
|
+
await writeFileAsync(zipPath, zipBuffer);
|
|
1812
|
+
log.debug(`ZIP cached to disk: transfer=${offer.transfer_id.slice(0, 8)}... path=${zipPath}`);
|
|
1813
|
+
} catch (err) {
|
|
1814
|
+
log.warn(`Failed to cache ZIP to disk: ${err}`);
|
|
1815
|
+
}
|
|
1816
|
+
})();
|
|
1620
1817
|
const timer = setTimeout(() => {
|
|
1621
1818
|
sender.close();
|
|
1622
1819
|
this.pendingTransfers.delete(offer.transfer_id);
|
|
1623
|
-
|
|
1624
|
-
|
|
1820
|
+
const dormantTimer = setTimeout(() => {
|
|
1821
|
+
this.cleanupDormantTransfer(offer.transfer_id);
|
|
1822
|
+
log.debug(`Dormant expired, disk cache removed: transfer=${offer.transfer_id.slice(0, 8)}...`);
|
|
1823
|
+
}, TRANSFER_DORMANT_TTL_MS);
|
|
1824
|
+
dormantTimer.unref?.();
|
|
1825
|
+
this.dormantTransfers.set(offer.transfer_id, { zipPath, offer, timer: dormantTimer });
|
|
1826
|
+
log.info(`Transfer transitioned to dormant: transfer=${offer.transfer_id.slice(0, 8)}...`);
|
|
1827
|
+
}, TRANSFER_ACTIVE_TTL_MS);
|
|
1625
1828
|
timer.unref?.();
|
|
1626
1829
|
this.pendingTransfers.set(offer.transfer_id, { sender, timer });
|
|
1627
1830
|
log.info(`WebRTC transfer registered: transfer=${offer.transfer_id.slice(0, 8)}... (${(zipBuffer.length / 1024).toFixed(1)} KB in memory)`);
|
|
@@ -1636,6 +1839,10 @@ var BridgeManager = class {
|
|
|
1636
1839
|
});
|
|
1637
1840
|
return;
|
|
1638
1841
|
}
|
|
1842
|
+
if (this.dormantTransfers.has(msg.transfer_id) || this.revivingTransfers.has(msg.transfer_id)) {
|
|
1843
|
+
void this.handleDormantSignal(msg);
|
|
1844
|
+
return;
|
|
1845
|
+
}
|
|
1639
1846
|
const uploadEntry = this.pendingUploads.get(msg.transfer_id);
|
|
1640
1847
|
if (uploadEntry) {
|
|
1641
1848
|
void uploadEntry.receiver.handleSignal({
|
|
@@ -1677,14 +1884,14 @@ var BridgeManager = class {
|
|
|
1677
1884
|
const zipPath = join3(workspaceDir, ".upload.zip");
|
|
1678
1885
|
writeFileSync2(zipPath, zipBuffer);
|
|
1679
1886
|
try {
|
|
1680
|
-
|
|
1887
|
+
safeUnzip(zipPath, workspaceDir);
|
|
1681
1888
|
try {
|
|
1682
|
-
|
|
1889
|
+
execSync2(`rm "${zipPath}"`);
|
|
1683
1890
|
} catch {
|
|
1684
1891
|
}
|
|
1685
1892
|
log.info(`[WebRTC] Upload: ${offer.file_count} file(s) extracted to ${workspaceDir}`);
|
|
1686
|
-
} catch {
|
|
1687
|
-
log.warn(`[WebRTC] Upload: Failed to extract ZIP. Saved to: ${zipPath}`);
|
|
1893
|
+
} catch (unzipErr) {
|
|
1894
|
+
log.warn(`[WebRTC] Upload: Failed to extract ZIP: ${unzipErr.message}. Saved to: ${zipPath}`);
|
|
1688
1895
|
}
|
|
1689
1896
|
} catch (err) {
|
|
1690
1897
|
log.error(`[WebRTC] Upload extraction failed: ${err}`);
|
|
@@ -1712,6 +1919,121 @@ var BridgeManager = class {
|
|
|
1712
1919
|
entry.sender.close();
|
|
1713
1920
|
}
|
|
1714
1921
|
this.pendingTransfers.clear();
|
|
1922
|
+
for (const [id] of this.dormantTransfers) {
|
|
1923
|
+
this.cleanupDormantTransfer(id);
|
|
1924
|
+
}
|
|
1925
|
+
this.revivingTransfers.clear();
|
|
1926
|
+
}
|
|
1927
|
+
// ========================================================
|
|
1928
|
+
// Dormant transfer: disk cache + revival
|
|
1929
|
+
// ========================================================
|
|
1930
|
+
transferCacheDir() {
|
|
1931
|
+
return join3(homedir2(), ".agent-mesh", "transfers");
|
|
1932
|
+
}
|
|
1933
|
+
transferCachePath(transferId) {
|
|
1934
|
+
return join3(this.transferCacheDir(), `${transferId}.zip`);
|
|
1935
|
+
}
|
|
1936
|
+
cleanupDormantTransfer(transferId) {
|
|
1937
|
+
const entry = this.dormantTransfers.get(transferId);
|
|
1938
|
+
if (!entry) return;
|
|
1939
|
+
clearTimeout(entry.timer);
|
|
1940
|
+
this.dormantTransfers.delete(transferId);
|
|
1941
|
+
void unlink(entry.zipPath).catch(() => {
|
|
1942
|
+
});
|
|
1943
|
+
}
|
|
1944
|
+
async reviveDormantTransfer(dormant) {
|
|
1945
|
+
const zipBuffer = await readFileAsync(dormant.zipPath);
|
|
1946
|
+
if (zipBuffer.length !== dormant.offer.zip_size) {
|
|
1947
|
+
throw new Error(`ZIP size mismatch: expected ${dormant.offer.zip_size}, got ${zipBuffer.length}`);
|
|
1948
|
+
}
|
|
1949
|
+
const sender = new FileSender(dormant.offer.transfer_id, zipBuffer);
|
|
1950
|
+
sender.onSignal((signal) => {
|
|
1951
|
+
const entry = this.pendingTransfers.get(dormant.offer.transfer_id);
|
|
1952
|
+
if (!entry) return;
|
|
1953
|
+
const rtcSignal = {
|
|
1954
|
+
type: "rtc_signal",
|
|
1955
|
+
transfer_id: dormant.offer.transfer_id,
|
|
1956
|
+
target_agent_id: entry.targetAgentId || "",
|
|
1957
|
+
signal_type: signal.signal_type,
|
|
1958
|
+
payload: signal.payload
|
|
1959
|
+
};
|
|
1960
|
+
this.wsClient.send(rtcSignal);
|
|
1961
|
+
});
|
|
1962
|
+
const timer = setTimeout(() => {
|
|
1963
|
+
sender.close();
|
|
1964
|
+
this.pendingTransfers.delete(dormant.offer.transfer_id);
|
|
1965
|
+
void unlink(dormant.zipPath).catch(() => {
|
|
1966
|
+
});
|
|
1967
|
+
log.debug(`Revived transfer expired: transfer=${dormant.offer.transfer_id.slice(0, 8)}...`);
|
|
1968
|
+
}, TRANSFER_ACTIVE_TTL_MS);
|
|
1969
|
+
timer.unref?.();
|
|
1970
|
+
this.pendingTransfers.set(dormant.offer.transfer_id, { sender, timer });
|
|
1971
|
+
log.info(`Revived from disk cache: transfer=${dormant.offer.transfer_id.slice(0, 8)}... (${(zipBuffer.length / 1024).toFixed(1)} KB)`);
|
|
1972
|
+
return sender;
|
|
1973
|
+
}
|
|
1974
|
+
async handleDormantSignal(msg) {
|
|
1975
|
+
const transferId = msg.transfer_id;
|
|
1976
|
+
const alreadyRevived = this.pendingTransfers.get(transferId);
|
|
1977
|
+
if (alreadyRevived) {
|
|
1978
|
+
alreadyRevived.targetAgentId = msg.from_agent_id;
|
|
1979
|
+
void alreadyRevived.sender.handleSignal({
|
|
1980
|
+
signal_type: msg.signal_type,
|
|
1981
|
+
payload: msg.payload
|
|
1982
|
+
});
|
|
1983
|
+
return;
|
|
1984
|
+
}
|
|
1985
|
+
const queue = this.revivingTransfers.get(transferId);
|
|
1986
|
+
if (queue) {
|
|
1987
|
+
queue.push(msg);
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
const dormant = this.dormantTransfers.get(transferId);
|
|
1991
|
+
if (!dormant) return;
|
|
1992
|
+
this.revivingTransfers.set(transferId, []);
|
|
1993
|
+
clearTimeout(dormant.timer);
|
|
1994
|
+
this.dormantTransfers.delete(transferId);
|
|
1995
|
+
try {
|
|
1996
|
+
const sender = await this.reviveDormantTransfer(dormant);
|
|
1997
|
+
const entry = this.pendingTransfers.get(transferId);
|
|
1998
|
+
if (entry) {
|
|
1999
|
+
entry.targetAgentId = msg.from_agent_id;
|
|
2000
|
+
}
|
|
2001
|
+
void sender.handleSignal({
|
|
2002
|
+
signal_type: msg.signal_type,
|
|
2003
|
+
payload: msg.payload
|
|
2004
|
+
});
|
|
2005
|
+
const queued = this.revivingTransfers.get(transferId) || [];
|
|
2006
|
+
this.revivingTransfers.delete(transferId);
|
|
2007
|
+
for (const queuedMsg of queued) {
|
|
2008
|
+
void sender.handleSignal({
|
|
2009
|
+
signal_type: queuedMsg.signal_type,
|
|
2010
|
+
payload: queuedMsg.payload
|
|
2011
|
+
});
|
|
2012
|
+
}
|
|
2013
|
+
} catch (err) {
|
|
2014
|
+
log.warn(`Failed to revive dormant transfer ${transferId.slice(0, 8)}...: ${err}`);
|
|
2015
|
+
this.revivingTransfers.delete(transferId);
|
|
2016
|
+
void unlink(dormant.zipPath).catch(() => {
|
|
2017
|
+
});
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
async cleanupOrphanedTransferCache() {
|
|
2021
|
+
try {
|
|
2022
|
+
const dir = this.transferCacheDir();
|
|
2023
|
+
let files;
|
|
2024
|
+
try {
|
|
2025
|
+
files = readdirSync(dir);
|
|
2026
|
+
} catch {
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
const zipFiles = files.filter((f) => f.endsWith(".zip"));
|
|
2030
|
+
if (zipFiles.length === 0) return;
|
|
2031
|
+
await Promise.all(zipFiles.map((f) => unlink(join3(dir, f)).catch(() => {
|
|
2032
|
+
})));
|
|
2033
|
+
log.info(`Cleaned ${zipFiles.length} orphaned transfer cache file(s)`);
|
|
2034
|
+
} catch (err) {
|
|
2035
|
+
log.debug(`Transfer cache cleanup failed: ${err}`);
|
|
2036
|
+
}
|
|
1715
2037
|
}
|
|
1716
2038
|
updateSessionCount() {
|
|
1717
2039
|
this.wsClient.setActiveSessions(this.pool.size);
|
|
@@ -1726,7 +2048,7 @@ var AgentAdapter = class {
|
|
|
1726
2048
|
import { spawn } from "child_process";
|
|
1727
2049
|
|
|
1728
2050
|
// src/utils/sandbox.ts
|
|
1729
|
-
import { execSync as
|
|
2051
|
+
import { execSync as execSync3 } from "child_process";
|
|
1730
2052
|
import { join as join4 } from "path";
|
|
1731
2053
|
var SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
|
|
1732
2054
|
var SENSITIVE_PATHS = [
|
|
@@ -1785,7 +2107,7 @@ var sandboxManager = null;
|
|
|
1785
2107
|
var sandboxInitialized = false;
|
|
1786
2108
|
async function importSandboxManager() {
|
|
1787
2109
|
try {
|
|
1788
|
-
const globalRoot =
|
|
2110
|
+
const globalRoot = execSync3("npm root -g", { encoding: "utf-8" }).trim();
|
|
1789
2111
|
const srtPath = join4(globalRoot, "@anthropic-ai/sandbox-runtime/dist/index.js");
|
|
1790
2112
|
const mod = await import(srtPath);
|
|
1791
2113
|
return mod.SandboxManager;
|
|
@@ -1874,7 +2196,7 @@ function buildCommandString(command, args) {
|
|
|
1874
2196
|
function installSandboxRuntime() {
|
|
1875
2197
|
log.info(`Installing ${SRT_PACKAGE}...`);
|
|
1876
2198
|
try {
|
|
1877
|
-
|
|
2199
|
+
execSync3(`npm install -g ${SRT_PACKAGE}`, { stdio: "inherit" });
|
|
1878
2200
|
log.success(`${SRT_PACKAGE} installed successfully`);
|
|
1879
2201
|
return true;
|
|
1880
2202
|
} catch {
|
|
@@ -1983,20 +2305,20 @@ function which(command) {
|
|
|
1983
2305
|
if (!ALLOWED_COMMANDS.test(command)) {
|
|
1984
2306
|
return Promise.resolve(null);
|
|
1985
2307
|
}
|
|
1986
|
-
return new Promise((
|
|
2308
|
+
return new Promise((resolve3) => {
|
|
1987
2309
|
execFile("which", [command], async (err, stdout) => {
|
|
1988
2310
|
if (!err && stdout.trim()) {
|
|
1989
|
-
|
|
2311
|
+
resolve3(stdout.trim());
|
|
1990
2312
|
return;
|
|
1991
2313
|
}
|
|
1992
|
-
|
|
2314
|
+
resolve3(await resolveFallbackPath(command));
|
|
1993
2315
|
});
|
|
1994
2316
|
});
|
|
1995
2317
|
}
|
|
1996
2318
|
|
|
1997
2319
|
// src/utils/client-workspace.ts
|
|
1998
|
-
import { mkdirSync as mkdirSync4, readdirSync, symlinkSync, existsSync as existsSync3, lstatSync } from "fs";
|
|
1999
|
-
import { join as join5, relative } from "path";
|
|
2320
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, symlinkSync, existsSync as existsSync3, lstatSync } from "fs";
|
|
2321
|
+
import { join as join5, relative as relative2 } from "path";
|
|
2000
2322
|
var SYMLINK_ALLOW = /* @__PURE__ */ new Set([
|
|
2001
2323
|
"CLAUDE.md",
|
|
2002
2324
|
".claude",
|
|
@@ -2028,7 +2350,7 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2028
2350
|
const wsDir = join5(projectPath, ".bridge-clients", clientId);
|
|
2029
2351
|
const isNew = !existsSync3(wsDir);
|
|
2030
2352
|
mkdirSync4(wsDir, { recursive: true });
|
|
2031
|
-
const entries =
|
|
2353
|
+
const entries = readdirSync2(projectPath, { withFileTypes: true });
|
|
2032
2354
|
for (const entry of entries) {
|
|
2033
2355
|
if (!shouldInclude(entry.name)) continue;
|
|
2034
2356
|
const link = join5(wsDir, entry.name);
|
|
@@ -2038,7 +2360,7 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2038
2360
|
} catch {
|
|
2039
2361
|
}
|
|
2040
2362
|
const target = join5(projectPath, entry.name);
|
|
2041
|
-
const relTarget =
|
|
2363
|
+
const relTarget = relative2(wsDir, target);
|
|
2042
2364
|
try {
|
|
2043
2365
|
symlinkSync(relTarget, link);
|
|
2044
2366
|
} catch (err) {
|
|
@@ -2053,11 +2375,11 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
2053
2375
|
|
|
2054
2376
|
// src/adapters/claude.ts
|
|
2055
2377
|
import { writeFile, mkdir, stat as stat2 } from "fs/promises";
|
|
2056
|
-
import { join as join7, relative as
|
|
2378
|
+
import { join as join7, relative as relative4, basename } from "path";
|
|
2057
2379
|
|
|
2058
2380
|
// src/utils/auto-upload.ts
|
|
2059
2381
|
import { readdir, readFile, stat } from "fs/promises";
|
|
2060
|
-
import { join as join6, relative as
|
|
2382
|
+
import { join as join6, relative as relative3 } from "path";
|
|
2061
2383
|
var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
|
|
2062
2384
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
2063
2385
|
".git",
|
|
@@ -2113,153 +2435,6 @@ async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
|
2113
2435
|
return files;
|
|
2114
2436
|
}
|
|
2115
2437
|
|
|
2116
|
-
// src/utils/zip.ts
|
|
2117
|
-
import { deflateRawSync, inflateRawSync } from "zlib";
|
|
2118
|
-
function dosTime(date) {
|
|
2119
|
-
return {
|
|
2120
|
-
time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
|
|
2121
|
-
date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
|
|
2122
|
-
};
|
|
2123
|
-
}
|
|
2124
|
-
function crc32(buf) {
|
|
2125
|
-
let crc = 4294967295;
|
|
2126
|
-
for (let i = 0; i < buf.length; i++) {
|
|
2127
|
-
crc ^= buf[i];
|
|
2128
|
-
for (let j = 0; j < 8; j++) {
|
|
2129
|
-
crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
|
|
2130
|
-
}
|
|
2131
|
-
}
|
|
2132
|
-
return (crc ^ 4294967295) >>> 0;
|
|
2133
|
-
}
|
|
2134
|
-
function writeUint16LE(buf, val, offset) {
|
|
2135
|
-
buf[offset] = val & 255;
|
|
2136
|
-
buf[offset + 1] = val >>> 8 & 255;
|
|
2137
|
-
}
|
|
2138
|
-
function writeUint32LE(buf, val, offset) {
|
|
2139
|
-
buf[offset] = val & 255;
|
|
2140
|
-
buf[offset + 1] = val >>> 8 & 255;
|
|
2141
|
-
buf[offset + 2] = val >>> 16 & 255;
|
|
2142
|
-
buf[offset + 3] = val >>> 24 & 255;
|
|
2143
|
-
}
|
|
2144
|
-
function createZipBuffer(entries) {
|
|
2145
|
-
const now = /* @__PURE__ */ new Date();
|
|
2146
|
-
const { time, date } = dosTime(now);
|
|
2147
|
-
const records = [];
|
|
2148
|
-
const chunks = [];
|
|
2149
|
-
let offset = 0;
|
|
2150
|
-
for (const entry of entries) {
|
|
2151
|
-
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
2152
|
-
const crc = crc32(entry.data);
|
|
2153
|
-
const compressed = deflateRawSync(entry.data, { level: 6 });
|
|
2154
|
-
const compressedSize = compressed.length;
|
|
2155
|
-
const uncompressedSize = entry.data.length;
|
|
2156
|
-
const header = Buffer.alloc(30 + nameBytes.length);
|
|
2157
|
-
writeUint32LE(header, 67324752, 0);
|
|
2158
|
-
writeUint16LE(header, 20, 4);
|
|
2159
|
-
writeUint16LE(header, 0, 6);
|
|
2160
|
-
writeUint16LE(header, 8, 8);
|
|
2161
|
-
writeUint16LE(header, time, 10);
|
|
2162
|
-
writeUint16LE(header, date, 12);
|
|
2163
|
-
writeUint32LE(header, crc, 14);
|
|
2164
|
-
writeUint32LE(header, compressedSize, 18);
|
|
2165
|
-
writeUint32LE(header, uncompressedSize, 22);
|
|
2166
|
-
writeUint16LE(header, nameBytes.length, 26);
|
|
2167
|
-
writeUint16LE(header, 0, 28);
|
|
2168
|
-
nameBytes.copy(header, 30);
|
|
2169
|
-
records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
|
|
2170
|
-
chunks.push(header, compressed);
|
|
2171
|
-
offset += header.length + compressed.length;
|
|
2172
|
-
}
|
|
2173
|
-
const centralDirStart = offset;
|
|
2174
|
-
for (let i = 0; i < entries.length; i++) {
|
|
2175
|
-
const entry = entries[i];
|
|
2176
|
-
const rec = records[i];
|
|
2177
|
-
const nameBytes = Buffer.from(entry.path, "utf-8");
|
|
2178
|
-
const cdh = Buffer.alloc(46 + nameBytes.length);
|
|
2179
|
-
writeUint32LE(cdh, 33639248, 0);
|
|
2180
|
-
writeUint16LE(cdh, 20, 4);
|
|
2181
|
-
writeUint16LE(cdh, 20, 6);
|
|
2182
|
-
writeUint16LE(cdh, 0, 8);
|
|
2183
|
-
writeUint16LE(cdh, 8, 10);
|
|
2184
|
-
writeUint16LE(cdh, time, 12);
|
|
2185
|
-
writeUint16LE(cdh, date, 14);
|
|
2186
|
-
writeUint32LE(cdh, rec.crc, 16);
|
|
2187
|
-
writeUint32LE(cdh, rec.compressedSize, 20);
|
|
2188
|
-
writeUint32LE(cdh, rec.uncompressedSize, 24);
|
|
2189
|
-
writeUint16LE(cdh, nameBytes.length, 28);
|
|
2190
|
-
writeUint16LE(cdh, 0, 30);
|
|
2191
|
-
writeUint16LE(cdh, 0, 32);
|
|
2192
|
-
writeUint16LE(cdh, 0, 34);
|
|
2193
|
-
writeUint16LE(cdh, 0, 36);
|
|
2194
|
-
writeUint32LE(cdh, 0, 38);
|
|
2195
|
-
writeUint32LE(cdh, rec.offset, 42);
|
|
2196
|
-
nameBytes.copy(cdh, 46);
|
|
2197
|
-
chunks.push(cdh);
|
|
2198
|
-
offset += cdh.length;
|
|
2199
|
-
}
|
|
2200
|
-
const centralDirSize = offset - centralDirStart;
|
|
2201
|
-
const eocd = Buffer.alloc(22);
|
|
2202
|
-
writeUint32LE(eocd, 101010256, 0);
|
|
2203
|
-
writeUint16LE(eocd, 0, 4);
|
|
2204
|
-
writeUint16LE(eocd, 0, 6);
|
|
2205
|
-
writeUint16LE(eocd, entries.length, 8);
|
|
2206
|
-
writeUint16LE(eocd, entries.length, 10);
|
|
2207
|
-
writeUint32LE(eocd, centralDirSize, 12);
|
|
2208
|
-
writeUint32LE(eocd, centralDirStart, 16);
|
|
2209
|
-
writeUint16LE(eocd, 0, 20);
|
|
2210
|
-
chunks.push(eocd);
|
|
2211
|
-
return Buffer.concat(chunks);
|
|
2212
|
-
}
|
|
2213
|
-
function extractZipBuffer(buf) {
|
|
2214
|
-
const entries = [];
|
|
2215
|
-
let eocdOffset = -1;
|
|
2216
|
-
for (let i = buf.length - 22; i >= 0; i--) {
|
|
2217
|
-
if (buf.readUInt32LE(i) === 101010256) {
|
|
2218
|
-
eocdOffset = i;
|
|
2219
|
-
break;
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
if (eocdOffset === -1) {
|
|
2223
|
-
throw new Error("Invalid ZIP: EOCD not found");
|
|
2224
|
-
}
|
|
2225
|
-
const entryCount = buf.readUInt16LE(eocdOffset + 10);
|
|
2226
|
-
const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
|
|
2227
|
-
let offset = centralDirOffset;
|
|
2228
|
-
for (let i = 0; i < entryCount; i++) {
|
|
2229
|
-
if (buf.readUInt32LE(offset) !== 33639248) {
|
|
2230
|
-
throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
|
|
2231
|
-
}
|
|
2232
|
-
const compressionMethod = buf.readUInt16LE(offset + 10);
|
|
2233
|
-
const compressedSize = buf.readUInt32LE(offset + 20);
|
|
2234
|
-
const uncompressedSize = buf.readUInt32LE(offset + 24);
|
|
2235
|
-
const nameLen = buf.readUInt16LE(offset + 28);
|
|
2236
|
-
const extraLen = buf.readUInt16LE(offset + 30);
|
|
2237
|
-
const commentLen = buf.readUInt16LE(offset + 32);
|
|
2238
|
-
const localHeaderOffset = buf.readUInt32LE(offset + 42);
|
|
2239
|
-
const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
|
|
2240
|
-
if (!name.endsWith("/")) {
|
|
2241
|
-
const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
|
|
2242
|
-
const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
|
|
2243
|
-
const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
|
|
2244
|
-
const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
|
|
2245
|
-
let data;
|
|
2246
|
-
if (compressionMethod === 0) {
|
|
2247
|
-
data = Buffer.from(compressedData);
|
|
2248
|
-
} else if (compressionMethod === 8) {
|
|
2249
|
-
data = inflateRawSync(compressedData);
|
|
2250
|
-
} else {
|
|
2251
|
-
throw new Error(`Unsupported compression method: ${compressionMethod}`);
|
|
2252
|
-
}
|
|
2253
|
-
if (data.length !== uncompressedSize) {
|
|
2254
|
-
throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
|
|
2255
|
-
}
|
|
2256
|
-
entries.push({ path: name, data });
|
|
2257
|
-
}
|
|
2258
|
-
offset += 46 + nameLen + extraLen + commentLen;
|
|
2259
|
-
}
|
|
2260
|
-
return entries;
|
|
2261
|
-
}
|
|
2262
|
-
|
|
2263
2438
|
// src/adapters/claude.ts
|
|
2264
2439
|
var DEFAULT_IDLE_TIMEOUT = 30 * 60 * 1e3;
|
|
2265
2440
|
var MIN_IDLE_TIMEOUT = 60 * 1e3;
|
|
@@ -2422,7 +2597,7 @@ var ClaudeSession = class {
|
|
|
2422
2597
|
const entries = [];
|
|
2423
2598
|
let totalBytes = 0;
|
|
2424
2599
|
for (const absPath of files) {
|
|
2425
|
-
const relPath =
|
|
2600
|
+
const relPath = relative4(workspaceRoot, absPath).replace(/\\/g, "/");
|
|
2426
2601
|
if (!relPath || relPath.startsWith("..")) continue;
|
|
2427
2602
|
try {
|
|
2428
2603
|
const fileStat = await stat2(absPath);
|
|
@@ -2711,7 +2886,7 @@ function logWorkspaceHint(slug, projectPath) {
|
|
|
2711
2886
|
console.log(` ${GRAY}${getWorkspaceHint()}${RESET}`);
|
|
2712
2887
|
}
|
|
2713
2888
|
function sleep2(ms) {
|
|
2714
|
-
return new Promise((
|
|
2889
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
2715
2890
|
}
|
|
2716
2891
|
function createAdapter(type, config) {
|
|
2717
2892
|
switch (type) {
|
|
@@ -2982,7 +3157,7 @@ async function pollForToken(baseUrl, deviceCode, expiresIn, interval) {
|
|
|
2982
3157
|
const deadline = Date.now() + expiresIn * 1e3;
|
|
2983
3158
|
let pollMs = Math.max(interval * 1e3, POLL_INTERVAL_MS);
|
|
2984
3159
|
while (Date.now() < deadline) {
|
|
2985
|
-
await new Promise((
|
|
3160
|
+
await new Promise((resolve3) => setTimeout(resolve3, pollMs));
|
|
2986
3161
|
const res = await fetch(`${baseUrl}/api/auth/device/token`, {
|
|
2987
3162
|
method: "POST",
|
|
2988
3163
|
headers: { "Content-Type": "application/json" },
|
|
@@ -3109,7 +3284,7 @@ function registerStatusCommand(program2) {
|
|
|
3109
3284
|
|
|
3110
3285
|
// src/commands/start.ts
|
|
3111
3286
|
function sleep3(ms) {
|
|
3112
|
-
return new Promise((
|
|
3287
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3113
3288
|
}
|
|
3114
3289
|
function registerStartCommand(program2) {
|
|
3115
3290
|
program2.command("start [name]").description("Start agent(s) in the background").option("--all", "Start all registered agents").action(async (name, opts) => {
|
|
@@ -3204,7 +3379,7 @@ function registerStopCommand(program2) {
|
|
|
3204
3379
|
|
|
3205
3380
|
// src/commands/restart.ts
|
|
3206
3381
|
function sleep4(ms) {
|
|
3207
|
-
return new Promise((
|
|
3382
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3208
3383
|
}
|
|
3209
3384
|
function registerRestartCommand(program2) {
|
|
3210
3385
|
program2.command("restart [name]").description("Restart agent(s)").option("--all", "Restart all registered agents").action(async (name, opts) => {
|
|
@@ -3291,10 +3466,10 @@ import { createInterface as createInterface2 } from "readline";
|
|
|
3291
3466
|
import { unlinkSync } from "fs";
|
|
3292
3467
|
function confirm(prompt) {
|
|
3293
3468
|
const rl = createInterface2({ input: process.stdin, output: process.stderr });
|
|
3294
|
-
return new Promise((
|
|
3469
|
+
return new Promise((resolve3) => {
|
|
3295
3470
|
rl.question(prompt, (answer) => {
|
|
3296
3471
|
rl.close();
|
|
3297
|
-
|
|
3472
|
+
resolve3(answer.trim().toLowerCase() === "y");
|
|
3298
3473
|
});
|
|
3299
3474
|
});
|
|
3300
3475
|
}
|
|
@@ -3347,7 +3522,7 @@ function registerOpenCommand(program2) {
|
|
|
3347
3522
|
import { writeFileSync as writeFileSync3, existsSync as existsSync5, mkdirSync as mkdirSync5 } from "fs";
|
|
3348
3523
|
import { join as join8 } from "path";
|
|
3349
3524
|
import { homedir as homedir5 } from "os";
|
|
3350
|
-
import { execSync as
|
|
3525
|
+
import { execSync as execSync4 } from "child_process";
|
|
3351
3526
|
var LABEL = "com.agents-hot.agent-mesh";
|
|
3352
3527
|
var PLIST_DIR = join8(homedir5(), "Library", "LaunchAgents");
|
|
3353
3528
|
var PLIST_PATH = join8(PLIST_DIR, `${LABEL}.plist`);
|
|
@@ -3413,17 +3588,17 @@ function registerInstallCommand(program2) {
|
|
|
3413
3588
|
}
|
|
3414
3589
|
if (existsSync5(PLIST_PATH)) {
|
|
3415
3590
|
try {
|
|
3416
|
-
|
|
3591
|
+
execSync4(`launchctl bootout gui/$(id -u) "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
3417
3592
|
} catch {
|
|
3418
3593
|
}
|
|
3419
3594
|
}
|
|
3420
3595
|
const plist = generatePlist(node, script);
|
|
3421
3596
|
writeFileSync3(PLIST_PATH, plist, { encoding: "utf-8" });
|
|
3422
3597
|
try {
|
|
3423
|
-
|
|
3598
|
+
execSync4(`launchctl bootstrap gui/$(id -u) "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
3424
3599
|
} catch {
|
|
3425
3600
|
try {
|
|
3426
|
-
|
|
3601
|
+
execSync4(`launchctl load "${PLIST_PATH}"`, { stdio: "pipe" });
|
|
3427
3602
|
} catch (err) {
|
|
3428
3603
|
log.error(`Failed to load LaunchAgent: ${err}`);
|
|
3429
3604
|
log.info(`Plist written to ${PLIST_PATH} \u2014 you can load it manually.`);
|
|
@@ -3447,7 +3622,7 @@ function registerInstallCommand(program2) {
|
|
|
3447
3622
|
import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
3448
3623
|
import { join as join9 } from "path";
|
|
3449
3624
|
import { homedir as homedir6 } from "os";
|
|
3450
|
-
import { execSync as
|
|
3625
|
+
import { execSync as execSync5 } from "child_process";
|
|
3451
3626
|
var LABEL2 = "com.agents-hot.agent-mesh";
|
|
3452
3627
|
var PLIST_PATH2 = join9(homedir6(), "Library", "LaunchAgents", `${LABEL2}.plist`);
|
|
3453
3628
|
function registerUninstallCommand(program2) {
|
|
@@ -3463,10 +3638,10 @@ function registerUninstallCommand(program2) {
|
|
|
3463
3638
|
return;
|
|
3464
3639
|
}
|
|
3465
3640
|
try {
|
|
3466
|
-
|
|
3641
|
+
execSync5(`launchctl bootout gui/$(id -u) "${PLIST_PATH2}" 2>/dev/null`, { stdio: "ignore" });
|
|
3467
3642
|
} catch {
|
|
3468
3643
|
try {
|
|
3469
|
-
|
|
3644
|
+
execSync5(`launchctl unload "${PLIST_PATH2}" 2>/dev/null`, { stdio: "ignore" });
|
|
3470
3645
|
} catch {
|
|
3471
3646
|
}
|
|
3472
3647
|
}
|
|
@@ -3667,10 +3842,10 @@ function parseVisibilityOrExit(input) {
|
|
|
3667
3842
|
}
|
|
3668
3843
|
function readLine(prompt) {
|
|
3669
3844
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
3670
|
-
return new Promise((
|
|
3845
|
+
return new Promise((resolve3) => {
|
|
3671
3846
|
rl.question(prompt, (answer) => {
|
|
3672
3847
|
rl.close();
|
|
3673
|
-
|
|
3848
|
+
resolve3(answer.trim());
|
|
3674
3849
|
});
|
|
3675
3850
|
});
|
|
3676
3851
|
}
|
|
@@ -3863,7 +4038,7 @@ function registerAgentsCommand(program2) {
|
|
|
3863
4038
|
import { createInterface as createInterface4 } from "readline";
|
|
3864
4039
|
import { existsSync as existsSync7, readFileSync as readFileSync2, mkdirSync as mkdirSync6, copyFileSync as copyFileSync2 } from "fs";
|
|
3865
4040
|
import { basename as basename2, join as join10 } from "path";
|
|
3866
|
-
import { execSync as
|
|
4041
|
+
import { execSync as execSync6 } from "child_process";
|
|
3867
4042
|
import { createHash as createHash2, randomUUID } from "crypto";
|
|
3868
4043
|
import { tmpdir } from "os";
|
|
3869
4044
|
|
|
@@ -3895,10 +4070,10 @@ function prepareUploadFile(filePath) {
|
|
|
3895
4070
|
const tempFile = join10(tempDir, fileName);
|
|
3896
4071
|
copyFileSync2(filePath, tempFile);
|
|
3897
4072
|
const zipPath = join10(tempDir, "upload.zip");
|
|
3898
|
-
|
|
4073
|
+
execSync6(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
|
|
3899
4074
|
const zipBuffer = readFileSync2(zipPath);
|
|
3900
4075
|
try {
|
|
3901
|
-
|
|
4076
|
+
execSync6(`rm -rf "${tempDir}"`);
|
|
3902
4077
|
} catch {
|
|
3903
4078
|
}
|
|
3904
4079
|
const zipSha256 = createHash2("sha256").update(zipBuffer).digest("hex");
|
|
@@ -3914,7 +4089,7 @@ function prepareUploadFile(filePath) {
|
|
|
3914
4089
|
};
|
|
3915
4090
|
}
|
|
3916
4091
|
function sleep5(ms) {
|
|
3917
|
-
return new Promise((
|
|
4092
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
3918
4093
|
}
|
|
3919
4094
|
async function chatWebrtcUpload(agentId, offer, zipBuffer, token, baseUrl) {
|
|
3920
4095
|
log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
|
|
@@ -4273,8 +4448,8 @@ function registerChatCommand(program2) {
|
|
|
4273
4448
|
}
|
|
4274
4449
|
|
|
4275
4450
|
// src/commands/skills.ts
|
|
4276
|
-
import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink } from "fs/promises";
|
|
4277
|
-
import { join as join12, resolve, relative as
|
|
4451
|
+
import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink as unlink2 } from "fs/promises";
|
|
4452
|
+
import { join as join12, resolve as resolve2, relative as relative5 } from "path";
|
|
4278
4453
|
|
|
4279
4454
|
// src/utils/skill-parser.ts
|
|
4280
4455
|
import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
|
|
@@ -4448,7 +4623,7 @@ function outputError(error, message, hint) {
|
|
|
4448
4623
|
process.exit(1);
|
|
4449
4624
|
}
|
|
4450
4625
|
function resolveSkillDir(pathArg) {
|
|
4451
|
-
return pathArg ?
|
|
4626
|
+
return pathArg ? resolve2(pathArg) : process.cwd();
|
|
4452
4627
|
}
|
|
4453
4628
|
function parseSkillRef(ref) {
|
|
4454
4629
|
if (!ref.includes("/")) {
|
|
@@ -4464,7 +4639,7 @@ function skillApiPath(authorLogin, slug) {
|
|
|
4464
4639
|
return `/api/skills/${encodeURIComponent(authorLogin)}/${encodeURIComponent(slug)}`;
|
|
4465
4640
|
}
|
|
4466
4641
|
async function resolveSkillsRootAsync(pathArg) {
|
|
4467
|
-
const projectRoot = pathArg ?
|
|
4642
|
+
const projectRoot = pathArg ? resolve2(pathArg) : process.cwd();
|
|
4468
4643
|
const skillsDir = join12(projectRoot, ".agents", "skills");
|
|
4469
4644
|
const claudeSkillsDir = join12(projectRoot, ".claude", "skills");
|
|
4470
4645
|
return { projectRoot, skillsDir, claudeSkillsDir };
|
|
@@ -4473,7 +4648,7 @@ async function ensureClaudeSymlink(claudeSkillsDir, slug) {
|
|
|
4473
4648
|
await mkdir2(claudeSkillsDir, { recursive: true });
|
|
4474
4649
|
const linkPath = join12(claudeSkillsDir, slug);
|
|
4475
4650
|
try {
|
|
4476
|
-
await
|
|
4651
|
+
await unlink2(linkPath);
|
|
4477
4652
|
} catch {
|
|
4478
4653
|
}
|
|
4479
4654
|
try {
|
|
@@ -4486,7 +4661,7 @@ async function collectPackFiles(dir, manifest) {
|
|
|
4486
4661
|
const results = [];
|
|
4487
4662
|
const all = await walkDir(dir);
|
|
4488
4663
|
for (const f of all) {
|
|
4489
|
-
const rel =
|
|
4664
|
+
const rel = relative5(dir, f);
|
|
4490
4665
|
results.push(rel);
|
|
4491
4666
|
}
|
|
4492
4667
|
const mainFile = manifest.main || "SKILL.md";
|
|
@@ -4960,7 +5135,7 @@ function registerSkillsCommand(program2) {
|
|
|
4960
5135
|
}
|
|
4961
5136
|
await rm(targetDir, { recursive: true, force: true });
|
|
4962
5137
|
try {
|
|
4963
|
-
await
|
|
5138
|
+
await unlink2(join12(claudeSkillsDir, slug));
|
|
4964
5139
|
} catch {
|
|
4965
5140
|
}
|
|
4966
5141
|
slog.success(`Removed skill: ${slug}`);
|
|
@@ -5098,7 +5273,7 @@ function registerDiscoverCommand(program2) {
|
|
|
5098
5273
|
|
|
5099
5274
|
// src/commands/call.ts
|
|
5100
5275
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7, copyFileSync as copyFileSync3, existsSync as existsSync8 } from "fs";
|
|
5101
|
-
import { execSync as
|
|
5276
|
+
import { execSync as execSync7 } from "child_process";
|
|
5102
5277
|
import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
|
|
5103
5278
|
import { join as join13, basename as basename3 } from "path";
|
|
5104
5279
|
import { tmpdir as tmpdir2 } from "os";
|
|
@@ -5123,7 +5298,7 @@ async function submitRating(baseUrl, token, agentId, callId, rating) {
|
|
|
5123
5298
|
}
|
|
5124
5299
|
}
|
|
5125
5300
|
function sleep6(ms) {
|
|
5126
|
-
return new Promise((
|
|
5301
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
5127
5302
|
}
|
|
5128
5303
|
function handleError2(err) {
|
|
5129
5304
|
if (err instanceof PlatformApiError) {
|
|
@@ -5198,13 +5373,13 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
|
|
|
5198
5373
|
const zipPath = join13(outputDir, ".transfer.zip");
|
|
5199
5374
|
writeFileSync4(zipPath, zipBuffer);
|
|
5200
5375
|
try {
|
|
5201
|
-
|
|
5376
|
+
safeUnzip(zipPath, outputDir);
|
|
5202
5377
|
try {
|
|
5203
|
-
|
|
5378
|
+
execSync7(`rm "${zipPath}"`);
|
|
5204
5379
|
} catch {
|
|
5205
5380
|
}
|
|
5206
|
-
} catch {
|
|
5207
|
-
log.warn(`Failed to extract ZIP. Saved to: ${zipPath}`);
|
|
5381
|
+
} catch (unzipErr) {
|
|
5382
|
+
log.warn(`Failed to extract ZIP: ${unzipErr.message}. Saved to: ${zipPath}`);
|
|
5208
5383
|
return;
|
|
5209
5384
|
}
|
|
5210
5385
|
if (json) {
|
|
@@ -5236,10 +5411,10 @@ function prepareFileForUpload(filePath) {
|
|
|
5236
5411
|
const tempFile = join13(tempDir, fileName);
|
|
5237
5412
|
copyFileSync3(filePath, tempFile);
|
|
5238
5413
|
const zipPath = join13(tempDir, "upload.zip");
|
|
5239
|
-
|
|
5414
|
+
execSync7(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
|
|
5240
5415
|
const zipBuffer = readFileSync3(zipPath);
|
|
5241
5416
|
try {
|
|
5242
|
-
|
|
5417
|
+
execSync7(`rm -rf "${tempDir}"`);
|
|
5243
5418
|
} catch {
|
|
5244
5419
|
}
|
|
5245
5420
|
const zipSha256 = createHash3("sha256").update(zipBuffer).digest("hex");
|