@annals/agent-mesh 0.16.13 → 0.17.2
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
CHANGED
|
@@ -33,7 +33,7 @@ import {
|
|
|
33
33
|
} from "./chunk-KEUGYA3L.js";
|
|
34
34
|
|
|
35
35
|
// src/index.ts
|
|
36
|
-
import { createRequire } from "module";
|
|
36
|
+
import { createRequire as createRequire2 } from "module";
|
|
37
37
|
import { program } from "commander";
|
|
38
38
|
|
|
39
39
|
// src/platform/auth.ts
|
|
@@ -269,6 +269,137 @@ var BridgeWSClient = class extends EventEmitter {
|
|
|
269
269
|
// src/bridge/manager.ts
|
|
270
270
|
import { BridgeErrorCode } from "@annals/bridge-protocol";
|
|
271
271
|
|
|
272
|
+
// src/utils/webrtc-transfer.ts
|
|
273
|
+
import { createHash } from "crypto";
|
|
274
|
+
import { createRequire } from "module";
|
|
275
|
+
import { existsSync, copyFileSync, mkdirSync } from "fs";
|
|
276
|
+
import { join, dirname } from "path";
|
|
277
|
+
var ICE_SERVERS = ["stun:stun.l.google.com:19302"];
|
|
278
|
+
var CHUNK_SIZE = 64 * 1024;
|
|
279
|
+
var ndcModule;
|
|
280
|
+
function ensurePrebuilt() {
|
|
281
|
+
try {
|
|
282
|
+
const require3 = createRequire(import.meta.url);
|
|
283
|
+
const ndcMain = require3.resolve("node-datachannel");
|
|
284
|
+
let ndcRoot = dirname(ndcMain);
|
|
285
|
+
for (let i = 0; i < 5; i++) {
|
|
286
|
+
if (existsSync(join(ndcRoot, "package.json"))) break;
|
|
287
|
+
ndcRoot = dirname(ndcRoot);
|
|
288
|
+
}
|
|
289
|
+
const target = join(ndcRoot, "build", "Release", "node_datachannel.node");
|
|
290
|
+
if (existsSync(target)) return true;
|
|
291
|
+
const platform = process.platform;
|
|
292
|
+
const arch = process.arch;
|
|
293
|
+
const ourPkgRoot = join(dirname(import.meta.url.replace("file://", "")), "..", "..");
|
|
294
|
+
const prebuiltSrc = join(ourPkgRoot, "prebuilds", `${platform}-${arch}`, "node_datachannel.node");
|
|
295
|
+
if (!existsSync(prebuiltSrc)) {
|
|
296
|
+
log.warn(`No prebuilt binary for ${platform}-${arch}`);
|
|
297
|
+
return false;
|
|
298
|
+
}
|
|
299
|
+
mkdirSync(join(ndcRoot, "build", "Release"), { recursive: true });
|
|
300
|
+
copyFileSync(prebuiltSrc, target);
|
|
301
|
+
log.info(`Installed node-datachannel prebuilt for ${platform}-${arch}`);
|
|
302
|
+
return true;
|
|
303
|
+
} catch {
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
async function loadNdc() {
|
|
308
|
+
if (ndcModule !== void 0) return ndcModule;
|
|
309
|
+
try {
|
|
310
|
+
ensurePrebuilt();
|
|
311
|
+
ndcModule = await import("node-datachannel");
|
|
312
|
+
return ndcModule;
|
|
313
|
+
} catch {
|
|
314
|
+
log.warn("node-datachannel not available \u2014 WebRTC file transfer disabled");
|
|
315
|
+
ndcModule = null;
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
var FileSender = class {
|
|
320
|
+
peer = null;
|
|
321
|
+
transferId;
|
|
322
|
+
zipBuffer;
|
|
323
|
+
pendingCandidates = [];
|
|
324
|
+
signalCallback = null;
|
|
325
|
+
closed = false;
|
|
326
|
+
constructor(transferId, zipBuffer) {
|
|
327
|
+
this.transferId = transferId;
|
|
328
|
+
this.zipBuffer = zipBuffer;
|
|
329
|
+
}
|
|
330
|
+
onSignal(cb) {
|
|
331
|
+
this.signalCallback = cb;
|
|
332
|
+
}
|
|
333
|
+
async handleSignal(signal) {
|
|
334
|
+
const ndc = await loadNdc();
|
|
335
|
+
if (!ndc || this.closed) return;
|
|
336
|
+
if (!this.peer) {
|
|
337
|
+
this.peer = new ndc.PeerConnection(`sender-${this.transferId}`, {
|
|
338
|
+
iceServers: ICE_SERVERS
|
|
339
|
+
});
|
|
340
|
+
this.peer.onLocalDescription((sdp, type) => {
|
|
341
|
+
this.signalCallback?.({ signal_type: type, payload: sdp });
|
|
342
|
+
});
|
|
343
|
+
this.peer.onLocalCandidate((candidate, mid) => {
|
|
344
|
+
this.signalCallback?.({
|
|
345
|
+
signal_type: "candidate",
|
|
346
|
+
payload: JSON.stringify({ candidate, mid })
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
this.peer.onDataChannel((dc) => {
|
|
350
|
+
dc.onOpen(() => {
|
|
351
|
+
void this.sendZip(dc);
|
|
352
|
+
});
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
if (signal.signal_type === "offer" || signal.signal_type === "answer") {
|
|
356
|
+
this.peer.setRemoteDescription(signal.payload, signal.signal_type);
|
|
357
|
+
for (const c of this.pendingCandidates) {
|
|
358
|
+
this.peer.addRemoteCandidate(c.candidate, c.mid);
|
|
359
|
+
}
|
|
360
|
+
this.pendingCandidates = [];
|
|
361
|
+
} else if (signal.signal_type === "candidate") {
|
|
362
|
+
const { candidate, mid } = JSON.parse(signal.payload);
|
|
363
|
+
if (this.peer.remoteDescription()) {
|
|
364
|
+
this.peer.addRemoteCandidate(candidate, mid);
|
|
365
|
+
} else {
|
|
366
|
+
this.pendingCandidates.push({ candidate, mid });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
async sendZip(dc) {
|
|
371
|
+
try {
|
|
372
|
+
dc.sendMessage(JSON.stringify({
|
|
373
|
+
type: "header",
|
|
374
|
+
transfer_id: this.transferId,
|
|
375
|
+
zip_size: this.zipBuffer.length
|
|
376
|
+
}));
|
|
377
|
+
let offset = 0;
|
|
378
|
+
while (offset < this.zipBuffer.length) {
|
|
379
|
+
const end = Math.min(offset + CHUNK_SIZE, this.zipBuffer.length);
|
|
380
|
+
const chunk = this.zipBuffer.subarray(offset, end);
|
|
381
|
+
dc.sendMessageBinary(chunk);
|
|
382
|
+
offset = end;
|
|
383
|
+
}
|
|
384
|
+
dc.sendMessage(JSON.stringify({ type: "complete" }));
|
|
385
|
+
log.info(`[WebRTC] Sent ${this.zipBuffer.length} bytes in ${Math.ceil(this.zipBuffer.length / CHUNK_SIZE)} chunks`);
|
|
386
|
+
} catch (err) {
|
|
387
|
+
log.error(`[WebRTC] Send failed: ${err}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
close() {
|
|
391
|
+
this.closed = true;
|
|
392
|
+
try {
|
|
393
|
+
this.peer?.close();
|
|
394
|
+
} catch {
|
|
395
|
+
}
|
|
396
|
+
this.peer = null;
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
function sha256Hex(data) {
|
|
400
|
+
return createHash("sha256").update(data).digest("hex");
|
|
401
|
+
}
|
|
402
|
+
|
|
272
403
|
// src/bridge/session-pool.ts
|
|
273
404
|
var SessionPool = class {
|
|
274
405
|
sessions = /* @__PURE__ */ new Map();
|
|
@@ -311,15 +442,15 @@ var SessionPool = class {
|
|
|
311
442
|
|
|
312
443
|
// src/utils/local-runtime-queue.ts
|
|
313
444
|
import {
|
|
314
|
-
existsSync,
|
|
315
|
-
mkdirSync,
|
|
445
|
+
existsSync as existsSync2,
|
|
446
|
+
mkdirSync as mkdirSync2,
|
|
316
447
|
readFileSync,
|
|
317
448
|
renameSync,
|
|
318
449
|
rmSync,
|
|
319
450
|
statSync,
|
|
320
451
|
writeFileSync
|
|
321
452
|
} from "fs";
|
|
322
|
-
import { join } from "path";
|
|
453
|
+
import { join as join2 } from "path";
|
|
323
454
|
import { homedir } from "os";
|
|
324
455
|
var DEFAULT_LOCK_STALE_MS = 3e4;
|
|
325
456
|
var DEFAULT_LOCK_WAIT_MS = 1e4;
|
|
@@ -376,10 +507,10 @@ var LocalRuntimeQueue = class {
|
|
|
376
507
|
leaseHeartbeatMs;
|
|
377
508
|
constructor(config, opts = {}) {
|
|
378
509
|
this.config = config;
|
|
379
|
-
const baseDir = opts.baseDir ||
|
|
380
|
-
this.runtimeRoot =
|
|
381
|
-
this.statePath =
|
|
382
|
-
this.lockPath =
|
|
510
|
+
const baseDir = opts.baseDir || join2(homedir(), ".agent-mesh");
|
|
511
|
+
this.runtimeRoot = join2(baseDir, "runtime");
|
|
512
|
+
this.statePath = join2(this.runtimeRoot, "queue-state.json");
|
|
513
|
+
this.lockPath = join2(this.runtimeRoot, "queue.lock");
|
|
383
514
|
this.lockStaleMs = opts.lockStaleMs ?? DEFAULT_LOCK_STALE_MS;
|
|
384
515
|
this.lockWaitMs = opts.lockWaitMs ?? DEFAULT_LOCK_WAIT_MS;
|
|
385
516
|
this.lockRetryMs = opts.lockRetryMs ?? DEFAULT_LOCK_RETRY_MS;
|
|
@@ -551,8 +682,8 @@ var LocalRuntimeQueue = class {
|
|
|
551
682
|
}
|
|
552
683
|
}
|
|
553
684
|
ensureRuntimeDir() {
|
|
554
|
-
if (!
|
|
555
|
-
|
|
685
|
+
if (!existsSync2(this.runtimeRoot)) {
|
|
686
|
+
mkdirSync2(this.runtimeRoot, { recursive: true, mode: 448 });
|
|
556
687
|
}
|
|
557
688
|
}
|
|
558
689
|
defaultState() {
|
|
@@ -607,7 +738,7 @@ var LocalRuntimeQueue = class {
|
|
|
607
738
|
const acquiredAt = Date.now();
|
|
608
739
|
while (true) {
|
|
609
740
|
try {
|
|
610
|
-
|
|
741
|
+
mkdirSync2(this.lockPath, { mode: 448 });
|
|
611
742
|
break;
|
|
612
743
|
} catch (err) {
|
|
613
744
|
const e = err;
|
|
@@ -688,6 +819,8 @@ var BridgeManager = class {
|
|
|
688
819
|
requestDispatches = /* @__PURE__ */ new Map();
|
|
689
820
|
cleanupTimer = null;
|
|
690
821
|
runtimeQueue;
|
|
822
|
+
/** Pending WebRTC file transfers: transfer_id → FileSender + cleanup timer */
|
|
823
|
+
pendingTransfers = /* @__PURE__ */ new Map();
|
|
691
824
|
constructor(opts) {
|
|
692
825
|
this.wsClient = opts.wsClient;
|
|
693
826
|
this.adapter = opts.adapter;
|
|
@@ -715,6 +848,7 @@ var BridgeManager = class {
|
|
|
715
848
|
this.wiredSessions.clear();
|
|
716
849
|
this.sessionLastSeenAt.clear();
|
|
717
850
|
this.cleanupRequestDispatches("shutdown");
|
|
851
|
+
this.cleanupPendingTransfers();
|
|
718
852
|
log.info("Bridge manager stopped");
|
|
719
853
|
}
|
|
720
854
|
/**
|
|
@@ -746,6 +880,9 @@ var BridgeManager = class {
|
|
|
746
880
|
break;
|
|
747
881
|
case "registered":
|
|
748
882
|
break;
|
|
883
|
+
case "rtc_signal_relay":
|
|
884
|
+
this.handleRtcSignalRelay(msg);
|
|
885
|
+
break;
|
|
749
886
|
default:
|
|
750
887
|
log.warn(`Unknown message type from worker: ${msg.type}`);
|
|
751
888
|
}
|
|
@@ -811,7 +948,7 @@ var BridgeManager = class {
|
|
|
811
948
|
}
|
|
812
949
|
async dispatchWithLocalQueue(opts) {
|
|
813
950
|
const { msg, handle, requestKey } = opts;
|
|
814
|
-
const { session_id, request_id, content, attachments,
|
|
951
|
+
const { session_id, request_id, content, attachments, client_id, with_files } = msg;
|
|
815
952
|
const state = this.requestDispatches.get(requestKey);
|
|
816
953
|
if (!state) return;
|
|
817
954
|
try {
|
|
@@ -830,9 +967,8 @@ var BridgeManager = class {
|
|
|
830
967
|
await this.releaseRequestLease(session_id, request_id, "cancel");
|
|
831
968
|
return;
|
|
832
969
|
}
|
|
833
|
-
const uploadCredentials = upload_url && upload_token ? { uploadUrl: upload_url, uploadToken: upload_token } : void 0;
|
|
834
970
|
try {
|
|
835
|
-
handle.send(content, attachments,
|
|
971
|
+
handle.send(content, attachments, client_id, with_files);
|
|
836
972
|
this.sessionLastSeenAt.set(session_id, Date.now());
|
|
837
973
|
} catch (err) {
|
|
838
974
|
log.error(`Failed to send to adapter: ${err}`);
|
|
@@ -893,20 +1029,24 @@ var BridgeManager = class {
|
|
|
893
1029
|
handle.onDone((payload) => {
|
|
894
1030
|
void this.releaseRequestLease(sessionId, requestRef.requestId, "done");
|
|
895
1031
|
const attachments = payload?.attachments;
|
|
896
|
-
const
|
|
1032
|
+
const fileTransferOffer = payload?.fileTransferOffer;
|
|
1033
|
+
const zipBuffer = payload?.zipBuffer;
|
|
1034
|
+
if (fileTransferOffer && zipBuffer) {
|
|
1035
|
+
this.registerPendingTransfer(fileTransferOffer, zipBuffer);
|
|
1036
|
+
}
|
|
897
1037
|
const done = {
|
|
898
1038
|
type: "done",
|
|
899
1039
|
session_id: sessionId,
|
|
900
1040
|
request_id: requestRef.requestId,
|
|
901
1041
|
...attachments && attachments.length > 0 && { attachments },
|
|
902
|
-
...
|
|
1042
|
+
...fileTransferOffer && { file_transfer_offer: fileTransferOffer },
|
|
903
1043
|
...fullResponseBuffer && { result: fullResponseBuffer }
|
|
904
1044
|
};
|
|
905
1045
|
this.trackRequest(sessionId, requestRef.requestId, "done");
|
|
906
1046
|
this.wsClient.send(done);
|
|
907
1047
|
fullResponseBuffer = "";
|
|
908
1048
|
this.sessionLastSeenAt.set(sessionId, Date.now());
|
|
909
|
-
const fileInfo =
|
|
1049
|
+
const fileInfo = fileTransferOffer ? ` (${fileTransferOffer.file_count} files, transfer=${fileTransferOffer.transfer_id.slice(0, 8)}...)` : "";
|
|
910
1050
|
log.info(`Request done: session=${sessionId.slice(0, 8)}... request=${requestRef.requestId.slice(0, 8)}...${fileInfo}`);
|
|
911
1051
|
});
|
|
912
1052
|
handle.onError((err) => {
|
|
@@ -1087,6 +1227,50 @@ var BridgeManager = class {
|
|
|
1087
1227
|
}
|
|
1088
1228
|
}
|
|
1089
1229
|
}
|
|
1230
|
+
// ========================================================
|
|
1231
|
+
// WebRTC signaling relay
|
|
1232
|
+
// ========================================================
|
|
1233
|
+
registerPendingTransfer(offer, zipBuffer) {
|
|
1234
|
+
const sender = new FileSender(offer.transfer_id, zipBuffer);
|
|
1235
|
+
sender.onSignal((signal) => {
|
|
1236
|
+
const entry = this.pendingTransfers.get(offer.transfer_id);
|
|
1237
|
+
if (!entry) return;
|
|
1238
|
+
const rtcSignal = {
|
|
1239
|
+
type: "rtc_signal",
|
|
1240
|
+
transfer_id: offer.transfer_id,
|
|
1241
|
+
target_agent_id: entry.targetAgentId || "",
|
|
1242
|
+
signal_type: signal.signal_type,
|
|
1243
|
+
payload: signal.payload
|
|
1244
|
+
};
|
|
1245
|
+
this.wsClient.send(rtcSignal);
|
|
1246
|
+
});
|
|
1247
|
+
const timer = setTimeout(() => {
|
|
1248
|
+
sender.close();
|
|
1249
|
+
this.pendingTransfers.delete(offer.transfer_id);
|
|
1250
|
+
log.debug(`Transfer ${offer.transfer_id.slice(0, 8)}... expired`);
|
|
1251
|
+
}, 5 * 6e4);
|
|
1252
|
+
timer.unref?.();
|
|
1253
|
+
this.pendingTransfers.set(offer.transfer_id, { sender, timer });
|
|
1254
|
+
}
|
|
1255
|
+
handleRtcSignalRelay(msg) {
|
|
1256
|
+
const entry = this.pendingTransfers.get(msg.transfer_id);
|
|
1257
|
+
if (!entry) {
|
|
1258
|
+
log.debug(`No pending transfer for ${msg.transfer_id.slice(0, 8)}...`);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
entry.targetAgentId = msg.from_agent_id;
|
|
1262
|
+
void entry.sender.handleSignal({
|
|
1263
|
+
signal_type: msg.signal_type,
|
|
1264
|
+
payload: msg.payload
|
|
1265
|
+
});
|
|
1266
|
+
}
|
|
1267
|
+
cleanupPendingTransfers() {
|
|
1268
|
+
for (const [id, entry] of this.pendingTransfers) {
|
|
1269
|
+
clearTimeout(entry.timer);
|
|
1270
|
+
entry.sender.close();
|
|
1271
|
+
}
|
|
1272
|
+
this.pendingTransfers.clear();
|
|
1273
|
+
}
|
|
1090
1274
|
updateSessionCount() {
|
|
1091
1275
|
this.wsClient.setActiveSessions(this.pool.size);
|
|
1092
1276
|
}
|
|
@@ -1101,7 +1285,7 @@ import { spawn } from "child_process";
|
|
|
1101
1285
|
|
|
1102
1286
|
// src/utils/sandbox.ts
|
|
1103
1287
|
import { execSync } from "child_process";
|
|
1104
|
-
import { join as
|
|
1288
|
+
import { join as join3 } from "path";
|
|
1105
1289
|
var SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
|
|
1106
1290
|
var SENSITIVE_PATHS = [
|
|
1107
1291
|
// SSH & crypto keys
|
|
@@ -1160,7 +1344,7 @@ var sandboxInitialized = false;
|
|
|
1160
1344
|
async function importSandboxManager() {
|
|
1161
1345
|
try {
|
|
1162
1346
|
const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
|
|
1163
|
-
const srtPath =
|
|
1347
|
+
const srtPath = join3(globalRoot, "@anthropic-ai/sandbox-runtime/dist/index.js");
|
|
1164
1348
|
const mod = await import(srtPath);
|
|
1165
1349
|
return mod.SandboxManager;
|
|
1166
1350
|
} catch {
|
|
@@ -1366,8 +1550,8 @@ function which(command) {
|
|
|
1366
1550
|
}
|
|
1367
1551
|
|
|
1368
1552
|
// src/utils/client-workspace.ts
|
|
1369
|
-
import { mkdirSync as
|
|
1370
|
-
import { join as
|
|
1553
|
+
import { mkdirSync as mkdirSync3, readdirSync, symlinkSync, existsSync as existsSync3, lstatSync } from "fs";
|
|
1554
|
+
import { join as join4, relative } from "path";
|
|
1371
1555
|
var SYMLINK_ALLOW = /* @__PURE__ */ new Set([
|
|
1372
1556
|
"CLAUDE.md",
|
|
1373
1557
|
".claude",
|
|
@@ -1396,19 +1580,19 @@ function shouldInclude(name) {
|
|
|
1396
1580
|
return false;
|
|
1397
1581
|
}
|
|
1398
1582
|
function createClientWorkspace(projectPath, clientId) {
|
|
1399
|
-
const wsDir =
|
|
1400
|
-
const isNew = !
|
|
1401
|
-
|
|
1583
|
+
const wsDir = join4(projectPath, ".bridge-clients", clientId);
|
|
1584
|
+
const isNew = !existsSync3(wsDir);
|
|
1585
|
+
mkdirSync3(wsDir, { recursive: true });
|
|
1402
1586
|
const entries = readdirSync(projectPath, { withFileTypes: true });
|
|
1403
1587
|
for (const entry of entries) {
|
|
1404
1588
|
if (!shouldInclude(entry.name)) continue;
|
|
1405
|
-
const link =
|
|
1589
|
+
const link = join4(wsDir, entry.name);
|
|
1406
1590
|
try {
|
|
1407
1591
|
lstatSync(link);
|
|
1408
1592
|
continue;
|
|
1409
1593
|
} catch {
|
|
1410
1594
|
}
|
|
1411
|
-
const target =
|
|
1595
|
+
const target = join4(projectPath, entry.name);
|
|
1412
1596
|
const relTarget = relative(wsDir, target);
|
|
1413
1597
|
try {
|
|
1414
1598
|
symlinkSync(relTarget, link);
|
|
@@ -1423,12 +1607,12 @@ function createClientWorkspace(projectPath, clientId) {
|
|
|
1423
1607
|
}
|
|
1424
1608
|
|
|
1425
1609
|
// src/adapters/claude.ts
|
|
1426
|
-
import {
|
|
1427
|
-
import { join as
|
|
1610
|
+
import { writeFile, mkdir, stat as stat2 } from "fs/promises";
|
|
1611
|
+
import { join as join6, relative as relative3, basename } from "path";
|
|
1428
1612
|
|
|
1429
1613
|
// src/utils/auto-upload.ts
|
|
1430
1614
|
import { readdir, readFile, stat } from "fs/promises";
|
|
1431
|
-
import { join as
|
|
1615
|
+
import { join as join5, relative as relative2 } from "path";
|
|
1432
1616
|
var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
|
|
1433
1617
|
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1434
1618
|
".git",
|
|
@@ -1440,23 +1624,6 @@ var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
|
1440
1624
|
"coverage",
|
|
1441
1625
|
".turbo"
|
|
1442
1626
|
]);
|
|
1443
|
-
var MIME_MAP = {
|
|
1444
|
-
md: "text/markdown",
|
|
1445
|
-
txt: "text/plain",
|
|
1446
|
-
json: "application/json",
|
|
1447
|
-
js: "text/javascript",
|
|
1448
|
-
ts: "text/typescript",
|
|
1449
|
-
py: "text/x-python",
|
|
1450
|
-
html: "text/html",
|
|
1451
|
-
css: "text/css",
|
|
1452
|
-
csv: "text/csv",
|
|
1453
|
-
png: "image/png",
|
|
1454
|
-
jpg: "image/jpeg",
|
|
1455
|
-
jpeg: "image/jpeg",
|
|
1456
|
-
gif: "image/gif",
|
|
1457
|
-
svg: "image/svg+xml",
|
|
1458
|
-
pdf: "application/pdf"
|
|
1459
|
-
};
|
|
1460
1627
|
async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
1461
1628
|
const files = [];
|
|
1462
1629
|
const walk = async (d) => {
|
|
@@ -1470,7 +1637,7 @@ async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
|
1470
1637
|
for (const entry of entries) {
|
|
1471
1638
|
if (files.length >= maxFiles) return;
|
|
1472
1639
|
if (entry.isSymbolicLink()) continue;
|
|
1473
|
-
const fullPath =
|
|
1640
|
+
const fullPath = join5(d, entry.name);
|
|
1474
1641
|
if (entry.isDirectory()) {
|
|
1475
1642
|
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1476
1643
|
await walk(fullPath);
|
|
@@ -1641,7 +1808,6 @@ var CLAUDE_RUNTIME_ALLOW_WRITE_PATHS = [
|
|
|
1641
1808
|
`${HOME_DIR}/.claude.json.tmp`,
|
|
1642
1809
|
`${HOME_DIR}/.local/state/claude`
|
|
1643
1810
|
];
|
|
1644
|
-
var MAX_UPLOAD_FILE_SIZE = 200 * 1024 * 1024;
|
|
1645
1811
|
var MAX_COLLECT_FILES = 5e3;
|
|
1646
1812
|
var DEFAULT_ZIP_MAX_BYTES = 200 * 1024 * 1024;
|
|
1647
1813
|
function resolveIdleTimeoutMs() {
|
|
@@ -1675,29 +1841,23 @@ var ClaudeSession = class {
|
|
|
1675
1841
|
activeToolName = null;
|
|
1676
1842
|
/** Track current content block type to distinguish thinking vs text deltas */
|
|
1677
1843
|
currentBlockType = null;
|
|
1678
|
-
/** Upload credentials provided by the platform for auto-uploading output files */
|
|
1679
|
-
uploadCredentials = null;
|
|
1680
1844
|
/** Per-client workspace path (symlink-based), set on each send() */
|
|
1681
1845
|
currentWorkspace;
|
|
1682
|
-
|
|
1846
|
+
/** Whether caller requested file transfer */
|
|
1847
|
+
withFiles = false;
|
|
1848
|
+
send(message, attachments, clientId, withFiles) {
|
|
1683
1849
|
this.resetIdleTimer();
|
|
1684
1850
|
this.doneFired = false;
|
|
1685
1851
|
this.chunksEmitted = false;
|
|
1686
1852
|
this.activeToolCallId = null;
|
|
1687
1853
|
this.activeToolName = null;
|
|
1688
1854
|
this.currentBlockType = null;
|
|
1689
|
-
|
|
1690
|
-
this.uploadCredentials = uploadCredentials;
|
|
1691
|
-
}
|
|
1855
|
+
this.withFiles = withFiles || false;
|
|
1692
1856
|
if (clientId && this.config.project) {
|
|
1693
1857
|
this.currentWorkspace = createClientWorkspace(this.config.project, clientId);
|
|
1694
1858
|
} else {
|
|
1695
1859
|
this.currentWorkspace = void 0;
|
|
1696
1860
|
}
|
|
1697
|
-
if (platformTask) {
|
|
1698
|
-
void this.runPlatformTask(platformTask);
|
|
1699
|
-
return;
|
|
1700
|
-
}
|
|
1701
1861
|
const args = ["-p", message, "--continue", "--output-format", "stream-json", "--verbose", "--include-partial-messages", "--dangerously-skip-permissions"];
|
|
1702
1862
|
void this.downloadAttachments(attachments).then(() => {
|
|
1703
1863
|
this.launchProcess(args);
|
|
@@ -1714,7 +1874,7 @@ var ClaudeSession = class {
|
|
|
1714
1874
|
await mkdir(workspaceRoot, { recursive: true });
|
|
1715
1875
|
for (const att of attachments) {
|
|
1716
1876
|
const safeName = basename(att.name).replace(/[^a-zA-Z0-9._-]/g, "_") || "attachment";
|
|
1717
|
-
const destPath =
|
|
1877
|
+
const destPath = join6(workspaceRoot, safeName);
|
|
1718
1878
|
try {
|
|
1719
1879
|
const res = await fetch(att.url);
|
|
1720
1880
|
if (!res.ok) {
|
|
@@ -1786,145 +1946,64 @@ var ClaudeSession = class {
|
|
|
1786
1946
|
getWorkspaceRoot() {
|
|
1787
1947
|
return this.currentWorkspace || this.config.project || process.cwd();
|
|
1788
1948
|
}
|
|
1789
|
-
normalizeRelativePath(inputPath) {
|
|
1790
|
-
const normalized = inputPath.replace(/\\/g, "/").replace(/^\/+/, "");
|
|
1791
|
-
if (!normalized || normalized.includes("\0")) return null;
|
|
1792
|
-
if (normalized.split("/").some((seg) => seg === "..")) return null;
|
|
1793
|
-
return normalized;
|
|
1794
|
-
}
|
|
1795
|
-
async runPlatformTask(task) {
|
|
1796
|
-
try {
|
|
1797
|
-
if (!this.uploadCredentials) {
|
|
1798
|
-
throw new Error("Missing upload credentials for platform task");
|
|
1799
|
-
}
|
|
1800
|
-
if (task.type === "upload_file") {
|
|
1801
|
-
await this.runUploadFileTask(task.path);
|
|
1802
|
-
} else if (task.type === "upload_all_zip") {
|
|
1803
|
-
await this.runUploadAllZipTask(task.zip_name, task.max_bytes);
|
|
1804
|
-
} else {
|
|
1805
|
-
throw new Error(`Unsupported platform task: ${task.type || "unknown"}`);
|
|
1806
|
-
}
|
|
1807
|
-
this.doneFired = true;
|
|
1808
|
-
} catch (error) {
|
|
1809
|
-
this.emitError(new Error(`Platform task failed: ${error instanceof Error ? error.message : String(error)}`));
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
async runUploadFileTask(path) {
|
|
1813
|
-
const workspaceRoot = this.getWorkspaceRoot();
|
|
1814
|
-
const relPath = this.normalizeRelativePath(path);
|
|
1815
|
-
if (!relPath) {
|
|
1816
|
-
throw new Error("Invalid file path");
|
|
1817
|
-
}
|
|
1818
|
-
const absPath = join5(workspaceRoot, relPath);
|
|
1819
|
-
const info = await stat2(absPath);
|
|
1820
|
-
if (!info.isFile()) {
|
|
1821
|
-
throw new Error("Path is not a regular file");
|
|
1822
|
-
}
|
|
1823
|
-
const buffer = await readFile2(absPath);
|
|
1824
|
-
if (buffer.length === 0 || buffer.length > MAX_UPLOAD_FILE_SIZE) {
|
|
1825
|
-
throw new Error(`File size out of bounds: ${buffer.length}`);
|
|
1826
|
-
}
|
|
1827
|
-
const uploaded = await this.uploadBuffer(relPath, buffer);
|
|
1828
|
-
for (const cb of this.doneCallbacks) cb({ attachments: [uploaded] });
|
|
1829
|
-
}
|
|
1830
|
-
async runUploadAllZipTask(zipName, maxBytes) {
|
|
1831
|
-
const workspaceRoot = this.getWorkspaceRoot();
|
|
1832
|
-
const files = await this.collectWorkspaceFiles(workspaceRoot);
|
|
1833
|
-
if (files.length === 0) {
|
|
1834
|
-
throw new Error("No files found");
|
|
1835
|
-
}
|
|
1836
|
-
const maxZipBytes = typeof maxBytes === "number" && Number.isFinite(maxBytes) && maxBytes > 0 ? Math.floor(maxBytes) : DEFAULT_ZIP_MAX_BYTES;
|
|
1837
|
-
const entries = [];
|
|
1838
|
-
let totalBytes = 0;
|
|
1839
|
-
for (const absPath of files) {
|
|
1840
|
-
this.resetIdleTimer();
|
|
1841
|
-
const relPath = relative3(workspaceRoot, absPath).replace(/\\/g, "/");
|
|
1842
|
-
if (!relPath || relPath.startsWith("..")) continue;
|
|
1843
|
-
const buffer = await readFile2(absPath);
|
|
1844
|
-
if (buffer.length === 0) continue;
|
|
1845
|
-
totalBytes += buffer.length;
|
|
1846
|
-
if (totalBytes > maxZipBytes) {
|
|
1847
|
-
throw new Error(`ZIP_TOO_LARGE:${totalBytes}`);
|
|
1848
|
-
}
|
|
1849
|
-
entries.push({ path: relPath, data: buffer });
|
|
1850
|
-
}
|
|
1851
|
-
const zipBuffer = createZipBuffer(entries);
|
|
1852
|
-
if (zipBuffer.length > maxZipBytes) {
|
|
1853
|
-
throw new Error(`ZIP_TOO_LARGE:${zipBuffer.length}`);
|
|
1854
|
-
}
|
|
1855
|
-
const safeZipName = this.normalizeRelativePath(zipName || "") || `workspace-${this.sessionId.slice(0, 8)}.zip`;
|
|
1856
|
-
const uploaded = await this.uploadBuffer(safeZipName.endsWith(".zip") ? safeZipName : `${safeZipName}.zip`, zipBuffer);
|
|
1857
|
-
for (const cb of this.doneCallbacks) cb({ attachments: [uploaded] });
|
|
1858
|
-
}
|
|
1859
|
-
async uploadBuffer(filename, buffer) {
|
|
1860
|
-
const creds = this.uploadCredentials;
|
|
1861
|
-
if (!creds) {
|
|
1862
|
-
throw new Error("Upload credentials missing");
|
|
1863
|
-
}
|
|
1864
|
-
const response = await fetch(creds.uploadUrl, {
|
|
1865
|
-
method: "POST",
|
|
1866
|
-
headers: {
|
|
1867
|
-
"X-Upload-Token": creds.uploadToken,
|
|
1868
|
-
"Content-Type": "application/json"
|
|
1869
|
-
},
|
|
1870
|
-
body: JSON.stringify({
|
|
1871
|
-
filename,
|
|
1872
|
-
content: buffer.toString("base64")
|
|
1873
|
-
})
|
|
1874
|
-
});
|
|
1875
|
-
if (!response.ok) {
|
|
1876
|
-
throw new Error(`Upload failed (${response.status}) for ${filename}`);
|
|
1877
|
-
}
|
|
1878
|
-
const payload = await response.json();
|
|
1879
|
-
if (typeof payload.url !== "string" || payload.url.length === 0) {
|
|
1880
|
-
throw new Error(`Upload response missing url for ${filename}`);
|
|
1881
|
-
}
|
|
1882
|
-
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
1883
|
-
return {
|
|
1884
|
-
name: filename,
|
|
1885
|
-
url: payload.url,
|
|
1886
|
-
type: MIME_MAP[ext] || "application/octet-stream"
|
|
1887
|
-
};
|
|
1888
|
-
}
|
|
1889
1949
|
async collectWorkspaceFiles(workspaceRoot) {
|
|
1890
1950
|
return collectRealFiles(workspaceRoot, MAX_COLLECT_FILES);
|
|
1891
1951
|
}
|
|
1892
|
-
|
|
1952
|
+
/**
|
|
1953
|
+
* Collect workspace files into a ZIP buffer + compute SHA-256.
|
|
1954
|
+
* Only called when with_files is true.
|
|
1955
|
+
*/
|
|
1956
|
+
async createWorkspaceZip(workspaceRoot) {
|
|
1893
1957
|
const files = await this.collectWorkspaceFiles(workspaceRoot);
|
|
1894
|
-
|
|
1958
|
+
if (files.length === 0) return null;
|
|
1959
|
+
const entries = [];
|
|
1960
|
+
let totalBytes = 0;
|
|
1895
1961
|
for (const absPath of files) {
|
|
1896
1962
|
const relPath = relative3(workspaceRoot, absPath).replace(/\\/g, "/");
|
|
1897
1963
|
if (!relPath || relPath.startsWith("..")) continue;
|
|
1898
1964
|
try {
|
|
1899
1965
|
const fileStat = await stat2(absPath);
|
|
1900
1966
|
if (!fileStat.isFile()) continue;
|
|
1901
|
-
const
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1967
|
+
const { readFile: readFile4 } = await import("fs/promises");
|
|
1968
|
+
const buffer = await readFile4(absPath);
|
|
1969
|
+
if (buffer.length === 0) continue;
|
|
1970
|
+
totalBytes += buffer.length;
|
|
1971
|
+
if (totalBytes > DEFAULT_ZIP_MAX_BYTES) {
|
|
1972
|
+
log.warn(`Workspace exceeds ${DEFAULT_ZIP_MAX_BYTES / 1024 / 1024}MB limit, truncating`);
|
|
1973
|
+
break;
|
|
1974
|
+
}
|
|
1975
|
+
entries.push({ path: relPath, data: buffer });
|
|
1908
1976
|
} catch {
|
|
1909
1977
|
}
|
|
1910
1978
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
1979
|
+
if (entries.length === 0) return null;
|
|
1980
|
+
const zipBuffer = createZipBuffer(entries);
|
|
1981
|
+
return { zipBuffer, fileCount: entries.length };
|
|
1913
1982
|
}
|
|
1914
1983
|
async finalizeDone(attachments) {
|
|
1915
|
-
const workspaceRoot = this.getWorkspaceRoot();
|
|
1916
|
-
let fileManifest;
|
|
1917
|
-
try {
|
|
1918
|
-
fileManifest = await this.collectWorkspaceManifest(workspaceRoot);
|
|
1919
|
-
} catch (error) {
|
|
1920
|
-
log.warn(`Manifest collection failed: ${error}`);
|
|
1921
|
-
}
|
|
1922
1984
|
const payload = {};
|
|
1923
1985
|
if (attachments && attachments.length > 0) {
|
|
1924
1986
|
payload.attachments = attachments;
|
|
1925
1987
|
}
|
|
1926
|
-
if (
|
|
1927
|
-
|
|
1988
|
+
if (this.withFiles) {
|
|
1989
|
+
const workspaceRoot = this.getWorkspaceRoot();
|
|
1990
|
+
try {
|
|
1991
|
+
const result = await this.createWorkspaceZip(workspaceRoot);
|
|
1992
|
+
if (result) {
|
|
1993
|
+
const transferId = crypto.randomUUID();
|
|
1994
|
+
const zipSha256 = sha256Hex(result.zipBuffer);
|
|
1995
|
+
payload.fileTransferOffer = {
|
|
1996
|
+
transfer_id: transferId,
|
|
1997
|
+
zip_size: result.zipBuffer.length,
|
|
1998
|
+
zip_sha256: zipSha256,
|
|
1999
|
+
file_count: result.fileCount
|
|
2000
|
+
};
|
|
2001
|
+
payload.zipBuffer = result.zipBuffer;
|
|
2002
|
+
log.info(`[WebRTC] ZIP ready: ${result.fileCount} files, ${result.zipBuffer.length} bytes, transfer=${transferId.slice(0, 8)}...`);
|
|
2003
|
+
}
|
|
2004
|
+
} catch (error) {
|
|
2005
|
+
log.warn(`ZIP creation failed: ${error}`);
|
|
2006
|
+
}
|
|
1928
2007
|
}
|
|
1929
2008
|
for (const cb of this.doneCallbacks) cb(payload);
|
|
1930
2009
|
}
|
|
@@ -2071,34 +2150,28 @@ var ClaudeSession = class {
|
|
|
2071
2150
|
return;
|
|
2072
2151
|
}
|
|
2073
2152
|
}
|
|
2074
|
-
stripWorkspacePaths(text) {
|
|
2075
|
-
const root = this.getWorkspaceRoot();
|
|
2076
|
-
if (!root) return text;
|
|
2077
|
-
const prefix = root.endsWith("/") ? root : root + "/";
|
|
2078
|
-
return text.replaceAll(prefix, "");
|
|
2079
|
-
}
|
|
2080
2153
|
emitChunk(text) {
|
|
2081
2154
|
this.chunksEmitted = true;
|
|
2082
|
-
for (const cb of this.chunkCallbacks) cb(
|
|
2155
|
+
for (const cb of this.chunkCallbacks) cb(text);
|
|
2083
2156
|
}
|
|
2084
2157
|
emitToolEvent(event) {
|
|
2085
|
-
for (const cb of this.toolCallbacks) cb(
|
|
2158
|
+
for (const cb of this.toolCallbacks) cb(event);
|
|
2086
2159
|
}
|
|
2087
2160
|
emitTextAsChunks(text) {
|
|
2088
|
-
const
|
|
2089
|
-
if (text.length <=
|
|
2161
|
+
const CHUNK_SIZE2 = 60;
|
|
2162
|
+
if (text.length <= CHUNK_SIZE2) {
|
|
2090
2163
|
this.emitChunk(text);
|
|
2091
2164
|
return;
|
|
2092
2165
|
}
|
|
2093
2166
|
let pos = 0;
|
|
2094
2167
|
while (pos < text.length) {
|
|
2095
|
-
let end = Math.min(pos +
|
|
2168
|
+
let end = Math.min(pos + CHUNK_SIZE2, text.length);
|
|
2096
2169
|
if (end < text.length) {
|
|
2097
2170
|
const slice = text.slice(pos, end + 20);
|
|
2098
2171
|
const breakPoints = ["\n", "\u3002", "\uFF01", "\uFF1F", ". ", "! ", "? ", "\uFF0C", ", ", " "];
|
|
2099
2172
|
for (const bp of breakPoints) {
|
|
2100
|
-
const idx = slice.indexOf(bp,
|
|
2101
|
-
if (idx >= 0 && idx <
|
|
2173
|
+
const idx = slice.indexOf(bp, CHUNK_SIZE2 - 20);
|
|
2174
|
+
if (idx >= 0 && idx < CHUNK_SIZE2 + 20) {
|
|
2102
2175
|
end = pos + idx + bp.length;
|
|
2103
2176
|
break;
|
|
2104
2177
|
}
|
|
@@ -2718,7 +2791,7 @@ function registerRestartCommand(program2) {
|
|
|
2718
2791
|
|
|
2719
2792
|
// src/commands/logs.ts
|
|
2720
2793
|
import { spawn as spawn2 } from "child_process";
|
|
2721
|
-
import { existsSync as
|
|
2794
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2722
2795
|
function registerLogsCommand(program2) {
|
|
2723
2796
|
program2.command("logs <name>").description("View agent logs (follows in real-time)").option("-n, --lines <number>", "Number of lines to show", "50").action((name, opts) => {
|
|
2724
2797
|
const entry = getAgent(name);
|
|
@@ -2727,7 +2800,7 @@ function registerLogsCommand(program2) {
|
|
|
2727
2800
|
process.exit(1);
|
|
2728
2801
|
}
|
|
2729
2802
|
const logPath = getLogPath(name);
|
|
2730
|
-
if (!
|
|
2803
|
+
if (!existsSync4(logPath)) {
|
|
2731
2804
|
log.error(`No log file found for "${name}". Has this agent been started before?`);
|
|
2732
2805
|
process.exit(1);
|
|
2733
2806
|
}
|
|
@@ -2808,14 +2881,14 @@ function registerOpenCommand(program2) {
|
|
|
2808
2881
|
}
|
|
2809
2882
|
|
|
2810
2883
|
// src/commands/install.ts
|
|
2811
|
-
import { writeFileSync as writeFileSync2, existsSync as
|
|
2812
|
-
import { join as
|
|
2884
|
+
import { writeFileSync as writeFileSync2, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
|
|
2885
|
+
import { join as join7 } from "path";
|
|
2813
2886
|
import { homedir as homedir4 } from "os";
|
|
2814
2887
|
import { execSync as execSync2 } from "child_process";
|
|
2815
2888
|
var LABEL = "com.agents-hot.agent-mesh";
|
|
2816
|
-
var PLIST_DIR =
|
|
2817
|
-
var PLIST_PATH =
|
|
2818
|
-
var LOG_PATH =
|
|
2889
|
+
var PLIST_DIR = join7(homedir4(), "Library", "LaunchAgents");
|
|
2890
|
+
var PLIST_PATH = join7(PLIST_DIR, `${LABEL}.plist`);
|
|
2891
|
+
var LOG_PATH = join7(homedir4(), ".agent-mesh", "logs", "launchd.log");
|
|
2819
2892
|
function detectPaths() {
|
|
2820
2893
|
return {
|
|
2821
2894
|
node: process.execPath,
|
|
@@ -2862,7 +2935,7 @@ function registerInstallCommand(program2) {
|
|
|
2862
2935
|
log.error("LaunchAgent is macOS only. On Linux, use systemd user service instead.");
|
|
2863
2936
|
process.exit(1);
|
|
2864
2937
|
}
|
|
2865
|
-
if (
|
|
2938
|
+
if (existsSync5(PLIST_PATH) && !opts.force) {
|
|
2866
2939
|
console.log(`
|
|
2867
2940
|
${YELLOW}\u2298${RESET} LaunchAgent already installed at:`);
|
|
2868
2941
|
console.log(` ${GRAY}${PLIST_PATH}${RESET}`);
|
|
@@ -2872,10 +2945,10 @@ function registerInstallCommand(program2) {
|
|
|
2872
2945
|
return;
|
|
2873
2946
|
}
|
|
2874
2947
|
const { node, script } = detectPaths();
|
|
2875
|
-
if (!
|
|
2876
|
-
|
|
2948
|
+
if (!existsSync5(PLIST_DIR)) {
|
|
2949
|
+
mkdirSync4(PLIST_DIR, { recursive: true });
|
|
2877
2950
|
}
|
|
2878
|
-
if (
|
|
2951
|
+
if (existsSync5(PLIST_PATH)) {
|
|
2879
2952
|
try {
|
|
2880
2953
|
execSync2(`launchctl bootout gui/$(id -u) "${PLIST_PATH}" 2>/dev/null`, { stdio: "ignore" });
|
|
2881
2954
|
} catch {
|
|
@@ -2908,19 +2981,19 @@ function registerInstallCommand(program2) {
|
|
|
2908
2981
|
}
|
|
2909
2982
|
|
|
2910
2983
|
// src/commands/uninstall.ts
|
|
2911
|
-
import { existsSync as
|
|
2912
|
-
import { join as
|
|
2984
|
+
import { existsSync as existsSync6, unlinkSync as unlinkSync2 } from "fs";
|
|
2985
|
+
import { join as join8 } from "path";
|
|
2913
2986
|
import { homedir as homedir5 } from "os";
|
|
2914
2987
|
import { execSync as execSync3 } from "child_process";
|
|
2915
2988
|
var LABEL2 = "com.agents-hot.agent-mesh";
|
|
2916
|
-
var PLIST_PATH2 =
|
|
2989
|
+
var PLIST_PATH2 = join8(homedir5(), "Library", "LaunchAgents", `${LABEL2}.plist`);
|
|
2917
2990
|
function registerUninstallCommand(program2) {
|
|
2918
2991
|
program2.command("uninstall").description("Remove macOS LaunchAgent (agents will no longer auto-start)").action(async () => {
|
|
2919
2992
|
if (process.platform !== "darwin") {
|
|
2920
2993
|
log.error("LaunchAgent is macOS only.");
|
|
2921
2994
|
process.exit(1);
|
|
2922
2995
|
}
|
|
2923
|
-
if (!
|
|
2996
|
+
if (!existsSync6(PLIST_PATH2)) {
|
|
2924
2997
|
console.log(`
|
|
2925
2998
|
${YELLOW}\u2298${RESET} No LaunchAgent found at ${GRAY}${PLIST_PATH2}${RESET}
|
|
2926
2999
|
`);
|
|
@@ -3399,8 +3472,8 @@ async function asyncChat(opts) {
|
|
|
3399
3472
|
`);
|
|
3400
3473
|
}
|
|
3401
3474
|
}
|
|
3402
|
-
if (task.
|
|
3403
|
-
process.stdout.write(`${GRAY}[
|
|
3475
|
+
if (task.file_transfer_offer) {
|
|
3476
|
+
process.stdout.write(`${GRAY}[files: ${task.file_transfer_offer.file_count} available via WebRTC]${RESET}
|
|
3404
3477
|
`);
|
|
3405
3478
|
}
|
|
3406
3479
|
return;
|
|
@@ -3605,12 +3678,12 @@ function registerChatCommand(program2) {
|
|
|
3605
3678
|
}
|
|
3606
3679
|
|
|
3607
3680
|
// src/commands/skills.ts
|
|
3608
|
-
import { readFile as
|
|
3609
|
-
import { join as
|
|
3681
|
+
import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink } from "fs/promises";
|
|
3682
|
+
import { join as join10, resolve, relative as relative4 } from "path";
|
|
3610
3683
|
|
|
3611
3684
|
// src/utils/skill-parser.ts
|
|
3612
|
-
import { readFile as
|
|
3613
|
-
import { join as
|
|
3685
|
+
import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
|
|
3686
|
+
import { join as join9 } from "path";
|
|
3614
3687
|
function parseSkillMd(raw) {
|
|
3615
3688
|
const trimmed = raw.trimStart();
|
|
3616
3689
|
if (!trimmed.startsWith("---")) {
|
|
@@ -3692,9 +3765,9 @@ function parseSkillMd(raw) {
|
|
|
3692
3765
|
return { frontmatter, content };
|
|
3693
3766
|
}
|
|
3694
3767
|
async function loadSkillManifest(dir) {
|
|
3695
|
-
const skillMdPath =
|
|
3768
|
+
const skillMdPath = join9(dir, "SKILL.md");
|
|
3696
3769
|
try {
|
|
3697
|
-
const raw = await
|
|
3770
|
+
const raw = await readFile2(skillMdPath, "utf-8");
|
|
3698
3771
|
const { frontmatter } = parseSkillMd(raw);
|
|
3699
3772
|
const name = frontmatter.name;
|
|
3700
3773
|
if (!name) {
|
|
@@ -3727,7 +3800,7 @@ async function pathExists(p) {
|
|
|
3727
3800
|
}
|
|
3728
3801
|
}
|
|
3729
3802
|
async function updateFrontmatterField(filePath, field, value) {
|
|
3730
|
-
const raw = await
|
|
3803
|
+
const raw = await readFile2(filePath, "utf-8");
|
|
3731
3804
|
const trimmed = raw.trimStart();
|
|
3732
3805
|
if (!trimmed.startsWith("---")) {
|
|
3733
3806
|
throw new Error("SKILL.md has no frontmatter block");
|
|
@@ -3797,13 +3870,13 @@ function skillApiPath(authorLogin, slug) {
|
|
|
3797
3870
|
}
|
|
3798
3871
|
async function resolveSkillsRootAsync(pathArg) {
|
|
3799
3872
|
const projectRoot = pathArg ? resolve(pathArg) : process.cwd();
|
|
3800
|
-
const skillsDir =
|
|
3801
|
-
const claudeSkillsDir =
|
|
3873
|
+
const skillsDir = join10(projectRoot, ".agents", "skills");
|
|
3874
|
+
const claudeSkillsDir = join10(projectRoot, ".claude", "skills");
|
|
3802
3875
|
return { projectRoot, skillsDir, claudeSkillsDir };
|
|
3803
3876
|
}
|
|
3804
3877
|
async function ensureClaudeSymlink(claudeSkillsDir, slug) {
|
|
3805
3878
|
await mkdir2(claudeSkillsDir, { recursive: true });
|
|
3806
|
-
const linkPath =
|
|
3879
|
+
const linkPath = join10(claudeSkillsDir, slug);
|
|
3807
3880
|
try {
|
|
3808
3881
|
await unlink(linkPath);
|
|
3809
3882
|
} catch {
|
|
@@ -3823,7 +3896,7 @@ async function collectPackFiles(dir, manifest) {
|
|
|
3823
3896
|
}
|
|
3824
3897
|
const mainFile = manifest.main || "SKILL.md";
|
|
3825
3898
|
if (!results.includes(mainFile)) {
|
|
3826
|
-
const mainPath =
|
|
3899
|
+
const mainPath = join10(dir, mainFile);
|
|
3827
3900
|
if (await pathExists(mainPath)) {
|
|
3828
3901
|
results.unshift(mainFile);
|
|
3829
3902
|
}
|
|
@@ -3840,7 +3913,7 @@ async function walkDir(dir) {
|
|
|
3840
3913
|
}
|
|
3841
3914
|
for (const entry of entries) {
|
|
3842
3915
|
if (entry.isSymbolicLink()) continue;
|
|
3843
|
-
const fullPath =
|
|
3916
|
+
const fullPath = join10(dir, entry.name);
|
|
3844
3917
|
if (entry.isDirectory()) {
|
|
3845
3918
|
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
3846
3919
|
const sub = await walkDir(fullPath);
|
|
@@ -3858,9 +3931,9 @@ async function packSkill(dir, manifest) {
|
|
|
3858
3931
|
}
|
|
3859
3932
|
const entries = [];
|
|
3860
3933
|
for (const relPath of fileList) {
|
|
3861
|
-
const absPath =
|
|
3934
|
+
const absPath = join10(dir, relPath);
|
|
3862
3935
|
try {
|
|
3863
|
-
const data = await
|
|
3936
|
+
const data = await readFile3(absPath);
|
|
3864
3937
|
entries.push({ path: relPath.replace(/\\/g, "/"), data });
|
|
3865
3938
|
} catch {
|
|
3866
3939
|
slog.warn(`Skipping unreadable file: ${relPath}`);
|
|
@@ -3892,7 +3965,7 @@ function bumpVersion(current, bump) {
|
|
|
3892
3965
|
}
|
|
3893
3966
|
async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
|
|
3894
3967
|
const meta = await client.get(skillApiPath(authorLogin, slug));
|
|
3895
|
-
const targetDir =
|
|
3968
|
+
const targetDir = join10(skillsDir, slug);
|
|
3896
3969
|
await mkdir2(targetDir, { recursive: true });
|
|
3897
3970
|
if (meta.has_files) {
|
|
3898
3971
|
const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/download`);
|
|
@@ -3900,8 +3973,8 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
|
|
|
3900
3973
|
const buf = Buffer.from(arrayBuf);
|
|
3901
3974
|
const entries = extractZipBuffer(buf);
|
|
3902
3975
|
for (const entry of entries) {
|
|
3903
|
-
const filePath =
|
|
3904
|
-
const dir =
|
|
3976
|
+
const filePath = join10(targetDir, entry.path);
|
|
3977
|
+
const dir = join10(filePath, "..");
|
|
3905
3978
|
await mkdir2(dir, { recursive: true });
|
|
3906
3979
|
await writeFile3(filePath, entry.data);
|
|
3907
3980
|
}
|
|
@@ -3914,7 +3987,7 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
|
|
|
3914
3987
|
} else {
|
|
3915
3988
|
const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/raw`);
|
|
3916
3989
|
const content = await res.text();
|
|
3917
|
-
await writeFile3(
|
|
3990
|
+
await writeFile3(join10(targetDir, "SKILL.md"), content);
|
|
3918
3991
|
return {
|
|
3919
3992
|
slug,
|
|
3920
3993
|
name: meta.name,
|
|
@@ -3943,9 +4016,9 @@ function registerSkillsCommand(program2) {
|
|
|
3943
4016
|
try {
|
|
3944
4017
|
const dir = resolveSkillDir(pathArg);
|
|
3945
4018
|
await mkdir2(dir, { recursive: true });
|
|
3946
|
-
const skillMdPath =
|
|
4019
|
+
const skillMdPath = join10(dir, "SKILL.md");
|
|
3947
4020
|
if (await pathExists(skillMdPath)) {
|
|
3948
|
-
const raw = await
|
|
4021
|
+
const raw = await readFile3(skillMdPath, "utf-8");
|
|
3949
4022
|
const { frontmatter } = parseSkillMd(raw);
|
|
3950
4023
|
if (frontmatter.name) {
|
|
3951
4024
|
slog.info(`SKILL.md already exists with name: ${frontmatter.name}`);
|
|
@@ -3972,7 +4045,7 @@ function registerSkillsCommand(program2) {
|
|
|
3972
4045
|
const dir = resolveSkillDir(pathArg);
|
|
3973
4046
|
const manifest = await loadSkillManifest(dir);
|
|
3974
4047
|
const result = await packSkill(dir, manifest);
|
|
3975
|
-
const outPath =
|
|
4048
|
+
const outPath = join10(dir, result.filename);
|
|
3976
4049
|
await writeFile3(outPath, result.buffer);
|
|
3977
4050
|
slog.info(`Packed ${result.files.length} files \u2192 ${result.filename} (${result.size} bytes)`);
|
|
3978
4051
|
outputJson({
|
|
@@ -4018,7 +4091,7 @@ function registerSkillsCommand(program2) {
|
|
|
4018
4091
|
if (opts.name) manifest.name = opts.name;
|
|
4019
4092
|
if (opts.version) manifest.version = opts.version;
|
|
4020
4093
|
if (opts.private !== void 0) manifest.private = opts.private;
|
|
4021
|
-
content = await
|
|
4094
|
+
content = await readFile3(join10(dir, manifest.main || "SKILL.md"), "utf-8");
|
|
4022
4095
|
packResult = await packSkill(dir, manifest);
|
|
4023
4096
|
slog.info(`Packed ${packResult.files.length} files (${packResult.size} bytes)`);
|
|
4024
4097
|
}
|
|
@@ -4155,11 +4228,11 @@ function registerSkillsCommand(program2) {
|
|
|
4155
4228
|
skills.command("version <bump> [path]").description("Bump skill version (patch | minor | major | x.y.z)").action(async (bump, pathArg) => {
|
|
4156
4229
|
try {
|
|
4157
4230
|
const dir = resolveSkillDir(pathArg);
|
|
4158
|
-
const skillMdPath =
|
|
4231
|
+
const skillMdPath = join10(dir, "SKILL.md");
|
|
4159
4232
|
if (!await pathExists(skillMdPath)) {
|
|
4160
4233
|
outputError("not_found", "No SKILL.md found. Run `agent-mesh skills init` first.");
|
|
4161
4234
|
}
|
|
4162
|
-
const raw = await
|
|
4235
|
+
const raw = await readFile3(skillMdPath, "utf-8");
|
|
4163
4236
|
const { frontmatter } = parseSkillMd(raw);
|
|
4164
4237
|
const oldVersion = frontmatter.version || "0.0.0";
|
|
4165
4238
|
const newVersion = bumpVersion(oldVersion, bump);
|
|
@@ -4175,7 +4248,7 @@ function registerSkillsCommand(program2) {
|
|
|
4175
4248
|
try {
|
|
4176
4249
|
const { authorLogin, slug } = parseSkillRef(ref);
|
|
4177
4250
|
const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
4178
|
-
const targetDir =
|
|
4251
|
+
const targetDir = join10(skillsDir, slug);
|
|
4179
4252
|
if (await pathExists(targetDir)) {
|
|
4180
4253
|
if (!opts.force) {
|
|
4181
4254
|
outputError("already_installed", `Skill "${slug}" is already installed at ${targetDir}. Use --force to overwrite.`);
|
|
@@ -4214,14 +4287,14 @@ function registerSkillsCommand(program2) {
|
|
|
4214
4287
|
const failed = [];
|
|
4215
4288
|
if (ref) {
|
|
4216
4289
|
const { authorLogin, slug } = parseSkillRef(ref);
|
|
4217
|
-
const targetDir =
|
|
4290
|
+
const targetDir = join10(skillsDir, slug);
|
|
4218
4291
|
if (!await pathExists(targetDir)) {
|
|
4219
4292
|
outputError("not_installed", `Skill "${slug}" is not installed. Use "skills install ${ref}" first.`);
|
|
4220
4293
|
}
|
|
4221
|
-
const skillMdPath =
|
|
4294
|
+
const skillMdPath = join10(targetDir, "SKILL.md");
|
|
4222
4295
|
let localVersion = "0.0.0";
|
|
4223
4296
|
if (await pathExists(skillMdPath)) {
|
|
4224
|
-
const raw = await
|
|
4297
|
+
const raw = await readFile3(skillMdPath, "utf-8");
|
|
4225
4298
|
const { frontmatter } = parseSkillMd(raw);
|
|
4226
4299
|
localVersion = frontmatter.version || "0.0.0";
|
|
4227
4300
|
}
|
|
@@ -4245,12 +4318,12 @@ function registerSkillsCommand(program2) {
|
|
|
4245
4318
|
for (const entry of entries) {
|
|
4246
4319
|
if (!entry.isDirectory()) continue;
|
|
4247
4320
|
const slug = entry.name;
|
|
4248
|
-
const skillMdPath =
|
|
4321
|
+
const skillMdPath = join10(skillsDir, slug, "SKILL.md");
|
|
4249
4322
|
if (!await pathExists(skillMdPath)) {
|
|
4250
4323
|
skipped.push({ slug, reason: "no_skill_md" });
|
|
4251
4324
|
continue;
|
|
4252
4325
|
}
|
|
4253
|
-
const raw = await
|
|
4326
|
+
const raw = await readFile3(skillMdPath, "utf-8");
|
|
4254
4327
|
const { frontmatter } = parseSkillMd(raw);
|
|
4255
4328
|
const localVersion = frontmatter.version || "0.0.0";
|
|
4256
4329
|
const authorLogin = frontmatter.author;
|
|
@@ -4265,7 +4338,7 @@ function registerSkillsCommand(program2) {
|
|
|
4265
4338
|
skipped.push({ slug, reason: "up_to_date" });
|
|
4266
4339
|
} else {
|
|
4267
4340
|
slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
|
|
4268
|
-
await rm(
|
|
4341
|
+
await rm(join10(skillsDir, slug), { recursive: true, force: true });
|
|
4269
4342
|
await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
|
|
4270
4343
|
updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
|
|
4271
4344
|
}
|
|
@@ -4286,13 +4359,13 @@ function registerSkillsCommand(program2) {
|
|
|
4286
4359
|
skills.command("remove <slug> [path]").description("Remove a locally installed skill").action(async (slug, pathArg) => {
|
|
4287
4360
|
try {
|
|
4288
4361
|
const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
|
|
4289
|
-
const targetDir =
|
|
4362
|
+
const targetDir = join10(skillsDir, slug);
|
|
4290
4363
|
if (!await pathExists(targetDir)) {
|
|
4291
4364
|
outputError("not_installed", `Skill "${slug}" is not installed at ${targetDir}`);
|
|
4292
4365
|
}
|
|
4293
4366
|
await rm(targetDir, { recursive: true, force: true });
|
|
4294
4367
|
try {
|
|
4295
|
-
await unlink(
|
|
4368
|
+
await unlink(join10(claudeSkillsDir, slug));
|
|
4296
4369
|
} catch {
|
|
4297
4370
|
}
|
|
4298
4371
|
slog.success(`Removed skill: ${slug}`);
|
|
@@ -4317,9 +4390,9 @@ function registerSkillsCommand(program2) {
|
|
|
4317
4390
|
for (const entry of entries) {
|
|
4318
4391
|
if (!entry.isDirectory()) continue;
|
|
4319
4392
|
const slug = entry.name;
|
|
4320
|
-
const skillMdPath =
|
|
4393
|
+
const skillMdPath = join10(skillsDir, slug, "SKILL.md");
|
|
4321
4394
|
if (!await pathExists(skillMdPath)) continue;
|
|
4322
|
-
const raw = await
|
|
4395
|
+
const raw = await readFile3(skillMdPath, "utf-8");
|
|
4323
4396
|
const { frontmatter } = parseSkillMd(raw);
|
|
4324
4397
|
const skillInfo = {
|
|
4325
4398
|
slug,
|
|
@@ -4470,7 +4543,11 @@ async function asyncCall(opts) {
|
|
|
4470
4543
|
"Content-Type": "application/json",
|
|
4471
4544
|
...selfAgentId ? { "X-Caller-Agent-Id": selfAgentId } : {}
|
|
4472
4545
|
},
|
|
4473
|
-
body: JSON.stringify({
|
|
4546
|
+
body: JSON.stringify({
|
|
4547
|
+
task_description: opts.taskDescription,
|
|
4548
|
+
mode: "async",
|
|
4549
|
+
...opts.withFiles ? { with_files: true } : {}
|
|
4550
|
+
}),
|
|
4474
4551
|
signal: opts.signal
|
|
4475
4552
|
});
|
|
4476
4553
|
if (!res.ok) {
|
|
@@ -4529,7 +4606,7 @@ async function asyncCall(opts) {
|
|
|
4529
4606
|
status: "completed",
|
|
4530
4607
|
result,
|
|
4531
4608
|
...task.attachments?.length ? { attachments: task.attachments } : {},
|
|
4532
|
-
...
|
|
4609
|
+
...task.file_transfer_offer ? { file_transfer_offer: task.file_transfer_offer } : {},
|
|
4533
4610
|
rate_hint: `POST /api/agents/${opts.id}/rate body: { call_id: "${call_id}", rating: 1-5 }`
|
|
4534
4611
|
}));
|
|
4535
4612
|
} else {
|
|
@@ -4539,9 +4616,9 @@ async function asyncCall(opts) {
|
|
|
4539
4616
|
log.info(` ${GRAY}File:${RESET} ${att.name} ${GRAY}${att.url}${RESET}`);
|
|
4540
4617
|
}
|
|
4541
4618
|
}
|
|
4542
|
-
const
|
|
4543
|
-
if (
|
|
4544
|
-
log.info(` ${GRAY}
|
|
4619
|
+
const offer = task.file_transfer_offer;
|
|
4620
|
+
if (offer) {
|
|
4621
|
+
log.info(` ${GRAY}Files:${RESET} ${offer.file_count} file(s) available via WebRTC`);
|
|
4545
4622
|
}
|
|
4546
4623
|
if (session_key) {
|
|
4547
4624
|
log.info(` ${GRAY}Session:${RESET} ${session_key}`);
|
|
@@ -4585,7 +4662,10 @@ async function streamCall(opts) {
|
|
|
4585
4662
|
Accept: "text/event-stream",
|
|
4586
4663
|
...selfAgentId ? { "X-Caller-Agent-Id": selfAgentId } : {}
|
|
4587
4664
|
},
|
|
4588
|
-
body: JSON.stringify({
|
|
4665
|
+
body: JSON.stringify({
|
|
4666
|
+
task_description: opts.taskDescription,
|
|
4667
|
+
...opts.withFiles ? { with_files: true } : {}
|
|
4668
|
+
}),
|
|
4589
4669
|
signal: opts.signal
|
|
4590
4670
|
});
|
|
4591
4671
|
if (!res.ok) {
|
|
@@ -4668,13 +4748,15 @@ async function streamCall(opts) {
|
|
|
4668
4748
|
outputBuffer += delta;
|
|
4669
4749
|
}
|
|
4670
4750
|
}
|
|
4671
|
-
} else if (event.type === "done"
|
|
4672
|
-
|
|
4673
|
-
|
|
4674
|
-
|
|
4751
|
+
} else if (event.type === "done") {
|
|
4752
|
+
if (event.attachments?.length) {
|
|
4753
|
+
console.log("");
|
|
4754
|
+
for (const att of event.attachments) {
|
|
4755
|
+
log.info(` ${GRAY}File:${RESET} ${att.name} ${GRAY}${att.url}${RESET}`);
|
|
4756
|
+
}
|
|
4675
4757
|
}
|
|
4676
|
-
if (
|
|
4677
|
-
log.info(` ${GRAY}
|
|
4758
|
+
if (event.file_transfer_offer) {
|
|
4759
|
+
log.info(` ${GRAY}Files:${RESET} ${event.file_transfer_offer.file_count} file(s) available via WebRTC`);
|
|
4678
4760
|
}
|
|
4679
4761
|
} else if (event.type === "error") {
|
|
4680
4762
|
process.stderr.write(`
|
|
@@ -4724,7 +4806,7 @@ Error: ${event.message}
|
|
|
4724
4806
|
return { callId, ...sessionKey ? { sessionKey } : {} };
|
|
4725
4807
|
}
|
|
4726
4808
|
function registerCallCommand(program2) {
|
|
4727
|
-
program2.command("call <agent>").description("Call an agent on the A2A network (default: async polling)").requiredOption("--task <description>", "Task description").option("--input-file <path>", "Read file and append to task description").option("--output-file <path>", "Save response text to file").option("--stream", "Use SSE streaming instead of async polling").option("--json", "Output JSONL events").option("--timeout <seconds>", "Timeout in seconds", "300").option("--rate <rating>", "Rate the agent after call (1-5)", parseInt).action(async (agentInput, opts) => {
|
|
4809
|
+
program2.command("call <agent>").description("Call an agent on the A2A network (default: async polling)").requiredOption("--task <description>", "Task description").option("--input-file <path>", "Read file and append to task description").option("--output-file <path>", "Save response text to file").option("--stream", "Use SSE streaming instead of async polling").option("--with-files", "Request file transfer via WebRTC after task completion").option("--json", "Output JSONL events").option("--timeout <seconds>", "Timeout in seconds", "300").option("--rate <rating>", "Rate the agent after call (1-5)", parseInt).action(async (agentInput, opts) => {
|
|
4728
4810
|
try {
|
|
4729
4811
|
const token = loadToken();
|
|
4730
4812
|
if (!token) {
|
|
@@ -4753,7 +4835,8 @@ ${content}`;
|
|
|
4753
4835
|
timeoutMs,
|
|
4754
4836
|
json: opts.json,
|
|
4755
4837
|
outputFile: opts.outputFile,
|
|
4756
|
-
signal: abortController.signal
|
|
4838
|
+
signal: abortController.signal,
|
|
4839
|
+
withFiles: opts.withFiles
|
|
4757
4840
|
};
|
|
4758
4841
|
let result;
|
|
4759
4842
|
if (opts.stream) {
|
|
@@ -5220,21 +5303,11 @@ function registerProfileCommand(program2) {
|
|
|
5220
5303
|
}
|
|
5221
5304
|
|
|
5222
5305
|
// src/commands/files.ts
|
|
5223
|
-
import { writeFileSync as writeFileSync4 } from "fs";
|
|
5224
|
-
import { basename as basename2 } from "path";
|
|
5225
5306
|
function formatBytes(bytes) {
|
|
5226
5307
|
if (bytes < 1024) return `${bytes}B`;
|
|
5227
5308
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
5228
5309
|
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
5229
5310
|
}
|
|
5230
|
-
function defaultOutputPath(filePath) {
|
|
5231
|
-
const name = basename2(filePath || "") || "file";
|
|
5232
|
-
return name;
|
|
5233
|
-
}
|
|
5234
|
-
function defaultZipOutputPath(sessionKey) {
|
|
5235
|
-
const suffix = sessionKey.split(":").at(-1) || "session";
|
|
5236
|
-
return `session-${suffix}.zip`;
|
|
5237
|
-
}
|
|
5238
5311
|
function handleError7(err) {
|
|
5239
5312
|
if (err instanceof PlatformApiError) {
|
|
5240
5313
|
log.error(err.message);
|
|
@@ -5247,16 +5320,8 @@ async function resolveTargetAgent(agentInput) {
|
|
|
5247
5320
|
const client = createClient();
|
|
5248
5321
|
return resolveAgentId(agentInput, client);
|
|
5249
5322
|
}
|
|
5250
|
-
async function downloadToFile(url, outputPath) {
|
|
5251
|
-
const res = await fetch(url);
|
|
5252
|
-
if (!res.ok) {
|
|
5253
|
-
throw new Error(`Download failed: HTTP ${res.status}`);
|
|
5254
|
-
}
|
|
5255
|
-
const buf = Buffer.from(await res.arrayBuffer());
|
|
5256
|
-
writeFileSync4(outputPath, buf);
|
|
5257
|
-
}
|
|
5258
5323
|
function registerFilesCommand(program2) {
|
|
5259
|
-
const files = program2.command("files").description("Session file
|
|
5324
|
+
const files = program2.command("files").description("Session file commands (WebRTC P2P transfer)");
|
|
5260
5325
|
files.command("list").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--json", "Output raw JSON").action(async (opts) => {
|
|
5261
5326
|
try {
|
|
5262
5327
|
const { id, name } = await resolveTargetAgent(opts.agent);
|
|
@@ -5279,90 +5344,7 @@ function registerFilesCommand(program2) {
|
|
|
5279
5344
|
console.log(` ${f.path} ${GRAY}${formatBytes(f.size)}${RESET}`);
|
|
5280
5345
|
}
|
|
5281
5346
|
console.log("");
|
|
5282
|
-
|
|
5283
|
-
handleError7(err);
|
|
5284
|
-
}
|
|
5285
|
-
});
|
|
5286
|
-
files.command("upload").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").requiredOption("--path <file_path>", "Relative file path in session workspace").option("--json", "Output raw JSON").action(async (opts) => {
|
|
5287
|
-
try {
|
|
5288
|
-
const { id } = await resolveTargetAgent(opts.agent);
|
|
5289
|
-
const client = createClient();
|
|
5290
|
-
const data = await client.post(`/api/agents/${id}/files/upload`, {
|
|
5291
|
-
session_key: opts.session,
|
|
5292
|
-
path: opts.path
|
|
5293
|
-
});
|
|
5294
|
-
if (opts.json) {
|
|
5295
|
-
console.log(JSON.stringify(data));
|
|
5296
|
-
return;
|
|
5297
|
-
}
|
|
5298
|
-
if (!data.file?.url) {
|
|
5299
|
-
throw new Error("No file URL returned");
|
|
5300
|
-
}
|
|
5301
|
-
log.success(`Uploaded ${opts.path}`);
|
|
5302
|
-
console.log(` ${GRAY}URL${RESET} ${data.file.url}`);
|
|
5303
|
-
} catch (err) {
|
|
5304
|
-
handleError7(err);
|
|
5305
|
-
}
|
|
5306
|
-
});
|
|
5307
|
-
files.command("upload-all").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--json", "Output raw JSON").action(async (opts) => {
|
|
5308
|
-
try {
|
|
5309
|
-
const { id } = await resolveTargetAgent(opts.agent);
|
|
5310
|
-
const client = createClient();
|
|
5311
|
-
const data = await client.post(`/api/agents/${id}/files/upload-all`, {
|
|
5312
|
-
session_key: opts.session
|
|
5313
|
-
});
|
|
5314
|
-
if (opts.json) {
|
|
5315
|
-
console.log(JSON.stringify(data));
|
|
5316
|
-
return;
|
|
5317
|
-
}
|
|
5318
|
-
if (!data.file?.url) {
|
|
5319
|
-
throw new Error("No ZIP URL returned");
|
|
5320
|
-
}
|
|
5321
|
-
log.success("Uploaded session ZIP");
|
|
5322
|
-
console.log(` ${GRAY}URL${RESET} ${data.file.url}`);
|
|
5323
|
-
} catch (err) {
|
|
5324
|
-
handleError7(err);
|
|
5325
|
-
}
|
|
5326
|
-
});
|
|
5327
|
-
files.command("download").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").requiredOption("--path <file_path>", "Relative file path in session workspace").option("--output <path>", "Local output path").option("--json", "Output raw JSON").action(async (opts) => {
|
|
5328
|
-
try {
|
|
5329
|
-
const { id } = await resolveTargetAgent(opts.agent);
|
|
5330
|
-
const client = createClient();
|
|
5331
|
-
const data = await client.post(`/api/agents/${id}/files/upload`, {
|
|
5332
|
-
session_key: opts.session,
|
|
5333
|
-
path: opts.path
|
|
5334
|
-
});
|
|
5335
|
-
const url = data.file?.url;
|
|
5336
|
-
if (!url) throw new Error("No file URL returned");
|
|
5337
|
-
const output = opts.output || defaultOutputPath(opts.path);
|
|
5338
|
-
await downloadToFile(url, output);
|
|
5339
|
-
if (opts.json) {
|
|
5340
|
-
console.log(JSON.stringify({ success: true, output, url }));
|
|
5341
|
-
return;
|
|
5342
|
-
}
|
|
5343
|
-
log.success(`Downloaded ${opts.path}`);
|
|
5344
|
-
console.log(` ${GRAY}Saved${RESET} ${output}`);
|
|
5345
|
-
} catch (err) {
|
|
5346
|
-
handleError7(err);
|
|
5347
|
-
}
|
|
5348
|
-
});
|
|
5349
|
-
files.command("download-all").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--output <path>", "Local output path").option("--json", "Output raw JSON").action(async (opts) => {
|
|
5350
|
-
try {
|
|
5351
|
-
const { id } = await resolveTargetAgent(opts.agent);
|
|
5352
|
-
const client = createClient();
|
|
5353
|
-
const data = await client.post(`/api/agents/${id}/files/upload-all`, {
|
|
5354
|
-
session_key: opts.session
|
|
5355
|
-
});
|
|
5356
|
-
const url = data.file?.url;
|
|
5357
|
-
if (!url) throw new Error("No ZIP URL returned");
|
|
5358
|
-
const output = opts.output || defaultZipOutputPath(opts.session);
|
|
5359
|
-
await downloadToFile(url, output);
|
|
5360
|
-
if (opts.json) {
|
|
5361
|
-
console.log(JSON.stringify({ success: true, output, url }));
|
|
5362
|
-
return;
|
|
5363
|
-
}
|
|
5364
|
-
log.success("Downloaded session ZIP");
|
|
5365
|
-
console.log(` ${GRAY}Saved${RESET} ${output}`);
|
|
5347
|
+
console.log(` ${GRAY}Use --with-files in call/chat to receive files via WebRTC P2P${RESET}`);
|
|
5366
5348
|
} catch (err) {
|
|
5367
5349
|
handleError7(err);
|
|
5368
5350
|
}
|
|
@@ -5372,12 +5354,9 @@ function registerFilesCommand(program2) {
|
|
|
5372
5354
|
command: "agent-mesh files",
|
|
5373
5355
|
docs: "https://agents.hot/docs/cli/files",
|
|
5374
5356
|
commands: [
|
|
5375
|
-
{ name: "list", required: ["--agent", "--session"], optional: ["--json"] }
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
{ name: "download", required: ["--agent", "--session", "--path"], optional: ["--output", "--json"] },
|
|
5379
|
-
{ name: "download-all", required: ["--agent", "--session"], optional: ["--output", "--json"] }
|
|
5380
|
-
]
|
|
5357
|
+
{ name: "list", required: ["--agent", "--session"], optional: ["--json"] }
|
|
5358
|
+
],
|
|
5359
|
+
notes: "File transfer now uses WebRTC P2P. Use --with-files flag in call/chat commands."
|
|
5381
5360
|
};
|
|
5382
5361
|
if (opts.json) {
|
|
5383
5362
|
console.log(JSON.stringify(reference));
|
|
@@ -5388,6 +5367,8 @@ function registerFilesCommand(program2) {
|
|
|
5388
5367
|
for (const item of reference.commands) {
|
|
5389
5368
|
console.log(` ${item.name}`);
|
|
5390
5369
|
}
|
|
5370
|
+
console.log("");
|
|
5371
|
+
console.log(` ${GRAY}${reference.notes}${RESET}`);
|
|
5391
5372
|
});
|
|
5392
5373
|
}
|
|
5393
5374
|
|
|
@@ -5506,7 +5487,7 @@ function maybeAutoUpgradeOnStartup(opts) {
|
|
|
5506
5487
|
}
|
|
5507
5488
|
|
|
5508
5489
|
// src/index.ts
|
|
5509
|
-
var require2 =
|
|
5490
|
+
var require2 = createRequire2(import.meta.url);
|
|
5510
5491
|
var { version } = require2("../package.json");
|
|
5511
5492
|
var autoUpgrade = maybeAutoUpgradeOnStartup({ currentVersion: version });
|
|
5512
5493
|
if (autoUpgrade.relaunched) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@annals/agent-mesh",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "CLI bridge connecting local AI agents to the Agents.Hot platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@annals/bridge-protocol": "^0.2.0",
|
|
12
12
|
"commander": "^13.0.0",
|
|
13
|
+
"node-datachannel": "^0.32.0",
|
|
13
14
|
"ws": "^8.18.0"
|
|
14
15
|
},
|
|
15
16
|
"devDependencies": {
|
|
@@ -18,7 +19,8 @@
|
|
|
18
19
|
"typescript": "^5.7.0"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
|
-
"dist"
|
|
22
|
+
"dist",
|
|
23
|
+
"prebuilds"
|
|
22
24
|
],
|
|
23
25
|
"license": "MIT",
|
|
24
26
|
"repository": {
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|