@annals/agent-mesh 0.17.9 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
3
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
4
+ }) : x)(function(x) {
5
+ if (typeof require !== "undefined") return require.apply(this, arguments);
6
+ throw Error('Dynamic require of "' + x + '" is not supported');
7
+ });
2
8
 
3
9
  // src/commands/list.ts
4
10
  import { spawn as spawn2 } from "child_process";
@@ -951,6 +957,7 @@ function registerListCommand(program) {
951
957
  }
952
958
 
953
959
  export {
960
+ __require,
954
961
  DEFAULT_RUNTIME_CONFIG,
955
962
  loadConfig,
956
963
  updateConfig,
package/dist/index.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  GREEN,
7
7
  RESET,
8
8
  YELLOW,
9
+ __require,
9
10
  addAgent,
10
11
  findAgentByAgentId,
11
12
  getAgent,
@@ -30,7 +31,7 @@ import {
30
31
  updateConfig,
31
32
  updateRuntimeConfig,
32
33
  writePid
33
- } from "./chunk-KEUGYA3L.js";
34
+ } from "./chunk-5CMYB6XV.js";
34
35
 
35
36
  // src/index.ts
36
37
  import { createRequire as createRequire2 } from "module";
@@ -531,6 +532,225 @@ var FileReceiver = class {
531
532
  this.dc = null;
532
533
  }
533
534
  };
535
+ var FileUploadReceiver = class {
536
+ peer = null;
537
+ expectedSize;
538
+ expectedSha256;
539
+ chunks = [];
540
+ receivedBytes = 0;
541
+ resolveComplete = null;
542
+ rejectComplete = null;
543
+ signalCallback = null;
544
+ pendingCandidates = [];
545
+ closed = false;
546
+ constructor(expectedSize, expectedSha256) {
547
+ this.expectedSize = expectedSize;
548
+ this.expectedSha256 = expectedSha256;
549
+ }
550
+ onSignal(cb) {
551
+ this.signalCallback = cb;
552
+ }
553
+ async handleSignal(signal) {
554
+ const ndc = await loadNdc();
555
+ if (!ndc || this.closed) return;
556
+ if (!this.peer) {
557
+ this.peer = new ndc.PeerConnection(`upload-receiver-${Date.now()}`, {
558
+ iceServers: ICE_SERVERS
559
+ });
560
+ this.peer.onLocalDescription((sdp, type) => {
561
+ this.signalCallback?.({ signal_type: type, payload: sdp });
562
+ });
563
+ this.peer.onLocalCandidate((candidate, mid) => {
564
+ this.signalCallback?.({
565
+ signal_type: "candidate",
566
+ payload: JSON.stringify({ candidate, mid })
567
+ });
568
+ });
569
+ this.peer.onDataChannel((dc) => {
570
+ dc.onMessage((msg) => {
571
+ if (typeof msg === "string") {
572
+ try {
573
+ const ctrl = JSON.parse(msg);
574
+ if (ctrl.type === "complete") {
575
+ this.finalizeReceive();
576
+ }
577
+ } catch {
578
+ }
579
+ return;
580
+ }
581
+ const buf = Buffer.isBuffer(msg) ? msg : Buffer.from(msg);
582
+ this.chunks.push(buf);
583
+ this.receivedBytes += buf.length;
584
+ });
585
+ });
586
+ }
587
+ if (signal.signal_type === "offer" || signal.signal_type === "answer") {
588
+ this.peer.setRemoteDescription(signal.payload, signal.signal_type);
589
+ for (const c of this.pendingCandidates) {
590
+ this.peer.addRemoteCandidate(c.candidate, c.mid);
591
+ }
592
+ this.pendingCandidates = [];
593
+ } else if (signal.signal_type === "candidate") {
594
+ const { candidate, mid } = JSON.parse(signal.payload);
595
+ if (this.peer.remoteDescription()) {
596
+ this.peer.addRemoteCandidate(candidate, mid);
597
+ } else {
598
+ this.pendingCandidates.push({ candidate, mid });
599
+ }
600
+ }
601
+ }
602
+ waitForCompletion(timeoutMs = 3e4) {
603
+ return new Promise((resolve2, reject) => {
604
+ this.resolveComplete = resolve2;
605
+ this.rejectComplete = reject;
606
+ setTimeout(() => {
607
+ if (!this.closed) {
608
+ reject(new Error(`Upload receive timed out after ${timeoutMs}ms`));
609
+ this.close();
610
+ }
611
+ }, timeoutMs);
612
+ });
613
+ }
614
+ finalizeReceive() {
615
+ const zipBuffer = Buffer.concat(this.chunks);
616
+ const actualSha256 = createHash("sha256").update(zipBuffer).digest("hex");
617
+ if (zipBuffer.length !== this.expectedSize) {
618
+ this.rejectComplete?.(
619
+ new Error(`Size mismatch: expected ${this.expectedSize}, got ${zipBuffer.length}`)
620
+ );
621
+ this.close();
622
+ return;
623
+ }
624
+ if (actualSha256 !== this.expectedSha256) {
625
+ this.rejectComplete?.(
626
+ new Error(`SHA-256 mismatch: expected ${this.expectedSha256}, got ${actualSha256}`)
627
+ );
628
+ this.close();
629
+ return;
630
+ }
631
+ log.info(`[WebRTC] Upload received ${zipBuffer.length} bytes, SHA-256 verified`);
632
+ this.resolveComplete?.(zipBuffer);
633
+ this.close();
634
+ }
635
+ close() {
636
+ this.closed = true;
637
+ try {
638
+ this.peer?.close();
639
+ } catch {
640
+ }
641
+ this.peer = null;
642
+ }
643
+ };
644
+ var FileUploadSender = class {
645
+ peer = null;
646
+ dc = null;
647
+ transferId;
648
+ zipBuffer;
649
+ signalCallback = null;
650
+ pendingCandidates = [];
651
+ resolveComplete = null;
652
+ rejectComplete = null;
653
+ closed = false;
654
+ constructor(transferId, zipBuffer) {
655
+ this.transferId = transferId;
656
+ this.zipBuffer = zipBuffer;
657
+ }
658
+ onSignal(cb) {
659
+ this.signalCallback = cb;
660
+ }
661
+ async createOffer() {
662
+ const ndc = await loadNdc();
663
+ if (!ndc) return null;
664
+ this.peer = new ndc.PeerConnection("upload-sender", {
665
+ iceServers: ICE_SERVERS
666
+ });
667
+ return new Promise((resolve2) => {
668
+ this.peer.onLocalDescription((sdp, type) => {
669
+ if (type === "offer") {
670
+ resolve2(sdp);
671
+ }
672
+ this.signalCallback?.({ signal_type: type, payload: sdp });
673
+ });
674
+ this.peer.onLocalCandidate((candidate, mid) => {
675
+ this.signalCallback?.({
676
+ signal_type: "candidate",
677
+ payload: JSON.stringify({ candidate, mid })
678
+ });
679
+ });
680
+ this.dc = this.peer.createDataChannel("file-transfer");
681
+ this.dc.onOpen(() => {
682
+ void this.sendZip();
683
+ });
684
+ });
685
+ }
686
+ async handleSignal(signal) {
687
+ if (!this.peer || this.closed) return;
688
+ if (signal.signal_type === "answer" || signal.signal_type === "offer") {
689
+ this.peer.setRemoteDescription(signal.payload, signal.signal_type);
690
+ for (const c of this.pendingCandidates) {
691
+ this.peer.addRemoteCandidate(c.candidate, c.mid);
692
+ }
693
+ this.pendingCandidates = [];
694
+ } else if (signal.signal_type === "candidate") {
695
+ const { candidate, mid } = JSON.parse(signal.payload);
696
+ if (this.peer.remoteDescription()) {
697
+ this.peer.addRemoteCandidate(candidate, mid);
698
+ } else {
699
+ this.pendingCandidates.push({ candidate, mid });
700
+ }
701
+ }
702
+ }
703
+ waitForCompletion(timeoutMs = 3e4) {
704
+ return new Promise((resolve2, reject) => {
705
+ this.resolveComplete = resolve2;
706
+ this.rejectComplete = reject;
707
+ setTimeout(() => {
708
+ if (!this.closed) {
709
+ reject(new Error(`Upload send timed out after ${timeoutMs}ms`));
710
+ this.close();
711
+ }
712
+ }, timeoutMs);
713
+ });
714
+ }
715
+ async sendZip() {
716
+ if (!this.dc) return;
717
+ try {
718
+ this.dc.sendMessage(JSON.stringify({
719
+ type: "header",
720
+ transfer_id: this.transferId,
721
+ zip_size: this.zipBuffer.length
722
+ }));
723
+ let offset = 0;
724
+ while (offset < this.zipBuffer.length) {
725
+ const end = Math.min(offset + CHUNK_SIZE, this.zipBuffer.length);
726
+ const chunk = this.zipBuffer.subarray(offset, end);
727
+ this.dc.sendMessageBinary(chunk);
728
+ offset = end;
729
+ }
730
+ this.dc.sendMessage(JSON.stringify({ type: "complete" }));
731
+ log.info(`[WebRTC] Upload sent ${this.zipBuffer.length} bytes in ${Math.ceil(this.zipBuffer.length / CHUNK_SIZE)} chunks`);
732
+ this.resolveComplete?.();
733
+ this.close();
734
+ } catch (err) {
735
+ log.error(`[WebRTC] Upload send failed: ${err}`);
736
+ this.rejectComplete?.(err instanceof Error ? err : new Error(String(err)));
737
+ this.close();
738
+ }
739
+ }
740
+ close() {
741
+ this.closed = true;
742
+ try {
743
+ this.dc?.close();
744
+ } catch {
745
+ }
746
+ try {
747
+ this.peer?.close();
748
+ } catch {
749
+ }
750
+ this.peer = null;
751
+ this.dc = null;
752
+ }
753
+ };
534
754
  function sha256Hex(data) {
535
755
  return createHash("sha256").update(data).digest("hex");
536
756
  }
@@ -954,8 +1174,10 @@ var BridgeManager = class {
954
1174
  requestDispatches = /* @__PURE__ */ new Map();
955
1175
  cleanupTimer = null;
956
1176
  runtimeQueue;
957
- /** Pending WebRTC file transfers: transfer_id → FileSender + cleanup timer */
1177
+ /** Pending WebRTC file transfers (download): transfer_id → FileSender + cleanup timer */
958
1178
  pendingTransfers = /* @__PURE__ */ new Map();
1179
+ /** Pending WebRTC file uploads (caller→agent): transfer_id → FileUploadReceiver + cleanup timer */
1180
+ pendingUploads = /* @__PURE__ */ new Map();
959
1181
  constructor(opts) {
960
1182
  this.wsClient = opts.wsClient;
961
1183
  this.adapter = opts.adapter;
@@ -984,6 +1206,7 @@ var BridgeManager = class {
984
1206
  this.sessionLastSeenAt.clear();
985
1207
  this.cleanupRequestDispatches("shutdown");
986
1208
  this.cleanupPendingTransfers();
1209
+ this.cleanupPendingUploads();
987
1210
  log.info("Bridge manager stopped");
988
1211
  }
989
1212
  /**
@@ -1083,7 +1306,10 @@ var BridgeManager = class {
1083
1306
  }
1084
1307
  async dispatchWithLocalQueue(opts) {
1085
1308
  const { msg, handle, requestKey } = opts;
1086
- const { session_id, request_id, content, attachments, client_id, with_files } = msg;
1309
+ const { session_id, request_id, content, attachments, client_id, with_files, file_upload_offer } = msg;
1310
+ if (file_upload_offer) {
1311
+ this.registerPendingUpload(file_upload_offer, session_id, request_id);
1312
+ }
1087
1313
  const state = this.requestDispatches.get(requestKey);
1088
1314
  if (!state) return;
1089
1315
  try {
@@ -1389,19 +1615,92 @@ var BridgeManager = class {
1389
1615
  log.info(`WebRTC transfer registered: transfer=${offer.transfer_id.slice(0, 8)}... (${(zipBuffer.length / 1024).toFixed(1)} KB in memory)`);
1390
1616
  }
1391
1617
  handleRtcSignalRelay(msg) {
1392
- const entry = this.pendingTransfers.get(msg.transfer_id);
1393
- if (!entry) {
1394
- log.debug(`No pending transfer for ${msg.transfer_id.slice(0, 8)}...`);
1618
+ const downloadEntry = this.pendingTransfers.get(msg.transfer_id);
1619
+ if (downloadEntry) {
1620
+ downloadEntry.targetAgentId = msg.from_agent_id;
1621
+ void downloadEntry.sender.handleSignal({
1622
+ signal_type: msg.signal_type,
1623
+ payload: msg.payload
1624
+ });
1625
+ return;
1626
+ }
1627
+ const uploadEntry = this.pendingUploads.get(msg.transfer_id);
1628
+ if (uploadEntry) {
1629
+ void uploadEntry.receiver.handleSignal({
1630
+ signal_type: msg.signal_type,
1631
+ payload: msg.payload
1632
+ });
1395
1633
  return;
1396
1634
  }
1397
- entry.targetAgentId = msg.from_agent_id;
1398
- void entry.sender.handleSignal({
1399
- signal_type: msg.signal_type,
1400
- payload: msg.payload
1635
+ log.debug(`No pending transfer for ${msg.transfer_id.slice(0, 8)}...`);
1636
+ }
1637
+ // ========================================================
1638
+ // Upload (Caller → Agent) WebRTC signaling
1639
+ // ========================================================
1640
+ registerPendingUpload(offer, _sessionId, _requestId) {
1641
+ const receiver = new FileUploadReceiver(offer.zip_size, offer.zip_sha256);
1642
+ receiver.onSignal((signal) => {
1643
+ const rtcSignal = {
1644
+ type: "rtc_signal",
1645
+ transfer_id: offer.transfer_id,
1646
+ target_agent_id: "http-caller",
1647
+ signal_type: signal.signal_type,
1648
+ payload: signal.payload
1649
+ };
1650
+ this.wsClient.send(rtcSignal);
1401
1651
  });
1652
+ const timer = setTimeout(() => {
1653
+ receiver.close();
1654
+ this.pendingUploads.delete(offer.transfer_id);
1655
+ log.debug(`Upload transfer ${offer.transfer_id.slice(0, 8)}... expired`);
1656
+ }, 5 * 6e4);
1657
+ timer.unref?.();
1658
+ this.pendingUploads.set(offer.transfer_id, { receiver, timer });
1659
+ void receiver.waitForCompletion(5 * 6e4).then((zipBuffer) => {
1660
+ clearTimeout(timer);
1661
+ this.pendingUploads.delete(offer.transfer_id);
1662
+ const workspaceDir = this.resolveUploadWorkspace(_sessionId);
1663
+ try {
1664
+ const { mkdirSync: mkdirSync6, writeFileSync: writeFileSync4 } = __require("fs");
1665
+ const { join: join13 } = __require("path");
1666
+ const { execSync: execSync6 } = __require("child_process");
1667
+ mkdirSync6(workspaceDir, { recursive: true });
1668
+ const zipPath = join13(workspaceDir, ".upload.zip");
1669
+ writeFileSync4(zipPath, zipBuffer);
1670
+ try {
1671
+ execSync6(`unzip -o -q "${zipPath}" -d "${workspaceDir}"`);
1672
+ try {
1673
+ execSync6(`rm "${zipPath}"`);
1674
+ } catch {
1675
+ }
1676
+ log.info(`[WebRTC] Upload: ${offer.file_count} file(s) extracted to ${workspaceDir}`);
1677
+ } catch {
1678
+ log.warn(`[WebRTC] Upload: Failed to extract ZIP. Saved to: ${zipPath}`);
1679
+ }
1680
+ } catch (err) {
1681
+ log.error(`[WebRTC] Upload extraction failed: ${err}`);
1682
+ }
1683
+ }).catch((err) => {
1684
+ log.warn(`[WebRTC] Upload receive failed: ${err.message}`);
1685
+ this.pendingUploads.delete(offer.transfer_id);
1686
+ });
1687
+ log.info(`[WebRTC] Upload registered: transfer=${offer.transfer_id.slice(0, 8)}... (${offer.file_count} files, ${(offer.zip_size / 1024).toFixed(1)} KB)`);
1688
+ }
1689
+ resolveUploadWorkspace(sessionId) {
1690
+ const { join: join13 } = __require("path");
1691
+ const { homedir: homedir6 } = __require("os");
1692
+ const safeSessionId = sessionId.replace(/[^a-zA-Z0-9_:-]/g, "_").slice(0, 64);
1693
+ return join13(homedir6(), ".agent-mesh", "uploads", safeSessionId);
1694
+ }
1695
+ cleanupPendingUploads() {
1696
+ for (const [, entry] of this.pendingUploads) {
1697
+ clearTimeout(entry.timer);
1698
+ entry.receiver.close();
1699
+ }
1700
+ this.pendingUploads.clear();
1402
1701
  }
1403
1702
  cleanupPendingTransfers() {
1404
- for (const [id, entry] of this.pendingTransfers) {
1703
+ for (const [, entry] of this.pendingTransfers) {
1405
1704
  clearTimeout(entry.timer);
1406
1705
  entry.sender.close();
1407
1706
  }
@@ -2477,7 +2776,7 @@ function registerConnectCommand(program2) {
2477
2776
  log.error(`Failed to start. Check logs: ${getLogPath(slug)}`);
2478
2777
  process.exit(1);
2479
2778
  }
2480
- const { ListTUI } = await import("./list-ROLJARYB.js");
2779
+ const { ListTUI } = await import("./list-RV7HOA77.js");
2481
2780
  const tui = new ListTUI();
2482
2781
  await tui.run();
2483
2782
  return;
@@ -3555,6 +3854,11 @@ function registerAgentsCommand(program2) {
3555
3854
 
3556
3855
  // src/commands/chat.ts
3557
3856
  import { createInterface as createInterface4 } from "readline";
3857
+ import { existsSync as existsSync7, readFileSync as readFileSync2 } from "fs";
3858
+ import { basename as basename2, join as join9 } from "path";
3859
+ import { execSync as execSync4 } from "child_process";
3860
+ import { createHash as createHash2, randomUUID } from "crypto";
3861
+ import { tmpdir } from "os";
3558
3862
 
3559
3863
  // src/utils/sse-parser.ts
3560
3864
  function parseSseChunk(raw, carry) {
@@ -3577,9 +3881,99 @@ function parseSseChunk(raw, carry) {
3577
3881
 
3578
3882
  // src/commands/chat.ts
3579
3883
  var DEFAULT_BASE_URL3 = "https://agents.hot";
3884
+ function prepareUploadFile(filePath) {
3885
+ const fileName = basename2(filePath);
3886
+ const tempDir = join9(tmpdir(), `chat-upload-${Date.now()}`);
3887
+ const { mkdirSync: mkdirSync6 } = __require("fs");
3888
+ mkdirSync6(tempDir, { recursive: true });
3889
+ const tempFile = join9(tempDir, fileName);
3890
+ const { copyFileSync: copyFileSync2 } = __require("fs");
3891
+ copyFileSync2(filePath, tempFile);
3892
+ const zipPath = join9(tempDir, "upload.zip");
3893
+ execSync4(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
3894
+ const zipBuffer = readFileSync2(zipPath);
3895
+ try {
3896
+ execSync4(`rm -rf "${tempDir}"`);
3897
+ } catch {
3898
+ }
3899
+ const zipSha256 = createHash2("sha256").update(zipBuffer).digest("hex");
3900
+ const transferId = randomUUID();
3901
+ return {
3902
+ offer: {
3903
+ transfer_id: transferId,
3904
+ zip_size: zipBuffer.length,
3905
+ zip_sha256: zipSha256,
3906
+ file_count: 1
3907
+ },
3908
+ zipBuffer: Buffer.from(zipBuffer)
3909
+ };
3910
+ }
3580
3911
  function sleep5(ms) {
3581
3912
  return new Promise((resolve2) => setTimeout(resolve2, ms));
3582
3913
  }
3914
+ async function chatWebrtcUpload(agentId, offer, zipBuffer, token, baseUrl) {
3915
+ log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
3916
+ const sender = new FileUploadSender(offer.transfer_id, zipBuffer);
3917
+ const exchangeSignals = async (signal) => {
3918
+ try {
3919
+ const res = await fetch(`${baseUrl}/api/agents/${agentId}/rtc-signal`, {
3920
+ method: "POST",
3921
+ headers: {
3922
+ Authorization: `Bearer ${token}`,
3923
+ "Content-Type": "application/json"
3924
+ },
3925
+ body: JSON.stringify({
3926
+ transfer_id: offer.transfer_id,
3927
+ signal_type: signal.signal_type,
3928
+ payload: signal.payload
3929
+ })
3930
+ });
3931
+ if (res.ok) {
3932
+ const { signals } = await res.json();
3933
+ for (const s of signals) {
3934
+ await sender.handleSignal(s);
3935
+ }
3936
+ }
3937
+ } catch {
3938
+ }
3939
+ };
3940
+ sender.onSignal(exchangeSignals);
3941
+ await sender.createOffer();
3942
+ const poll = async () => {
3943
+ for (let i = 0; i < 20; i++) {
3944
+ await sleep5(500);
3945
+ try {
3946
+ const res = await fetch(`${baseUrl}/api/agents/${agentId}/rtc-signal`, {
3947
+ method: "POST",
3948
+ headers: {
3949
+ Authorization: `Bearer ${token}`,
3950
+ "Content-Type": "application/json"
3951
+ },
3952
+ body: JSON.stringify({
3953
+ transfer_id: offer.transfer_id,
3954
+ signal_type: "poll",
3955
+ payload: ""
3956
+ })
3957
+ });
3958
+ if (res.ok) {
3959
+ const { signals } = await res.json();
3960
+ for (const s of signals) {
3961
+ await sender.handleSignal(s);
3962
+ }
3963
+ }
3964
+ } catch {
3965
+ }
3966
+ }
3967
+ };
3968
+ try {
3969
+ await Promise.all([sender.waitForCompletion(3e4), poll()]);
3970
+ log.success(`[WebRTC] File uploaded successfully`);
3971
+ } catch (err) {
3972
+ log.warn(`[WebRTC] Upload failed: ${err.message}`);
3973
+ } finally {
3974
+ sender.close();
3975
+ }
3976
+ }
3583
3977
  async function asyncChat(opts) {
3584
3978
  const res = await fetch(`${opts.baseUrl}/api/agents/${opts.agentId}/chat`, {
3585
3979
  method: "POST",
@@ -3587,7 +3981,11 @@ async function asyncChat(opts) {
3587
3981
  Authorization: `Bearer ${opts.token}`,
3588
3982
  "Content-Type": "application/json"
3589
3983
  },
3590
- body: JSON.stringify({ message: opts.message, mode: "async" }),
3984
+ body: JSON.stringify({
3985
+ message: opts.message,
3986
+ mode: "async",
3987
+ ...opts.fileUploadOffer ? { file_upload_offer: opts.fileUploadOffer } : {}
3988
+ }),
3591
3989
  signal: opts.signal
3592
3990
  });
3593
3991
  if (!res.ok) {
@@ -3655,7 +4053,10 @@ async function streamChat(opts) {
3655
4053
  Authorization: `Bearer ${opts.token}`,
3656
4054
  "Content-Type": "application/json"
3657
4055
  },
3658
- body: JSON.stringify({ message: opts.message }),
4056
+ body: JSON.stringify({
4057
+ message: opts.message,
4058
+ ...opts.fileUploadOffer ? { file_upload_offer: opts.fileUploadOffer } : {}
4059
+ }),
3659
4060
  signal: opts.signal
3660
4061
  });
3661
4062
  if (!res.ok) {
@@ -3789,8 +4190,10 @@ function registerChatCommand(program2) {
3789
4190
  process.exit(1);
3790
4191
  }
3791
4192
  log.banner(`Chat with ${agentName}`);
3792
- console.log(`${GRAY}Type your message and press Enter. Use /quit or Ctrl+C to exit.${RESET}
4193
+ console.log(`${GRAY}Type your message and press Enter. /upload <path> to send a file. /quit to exit.${RESET}
3793
4194
  `);
4195
+ let pendingUploadOffer;
4196
+ let pendingUploadZipBuffer;
3794
4197
  const rl = createInterface4({
3795
4198
  input: process.stdin,
3796
4199
  output: process.stdout,
@@ -3813,7 +4216,34 @@ function registerChatCommand(program2) {
3813
4216
  rl.close();
3814
4217
  return;
3815
4218
  }
4219
+ if (trimmed.startsWith("/upload ")) {
4220
+ const filePath = trimmed.slice(8).trim();
4221
+ if (!filePath) {
4222
+ log.error("Usage: /upload <path>");
4223
+ rl.prompt();
4224
+ return;
4225
+ }
4226
+ if (!existsSync7(filePath)) {
4227
+ log.error(`File not found: ${filePath}`);
4228
+ rl.prompt();
4229
+ return;
4230
+ }
4231
+ try {
4232
+ const prepared = prepareUploadFile(filePath);
4233
+ pendingUploadOffer = prepared.offer;
4234
+ pendingUploadZipBuffer = prepared.zipBuffer;
4235
+ log.info(`File staged: ${basename2(filePath)} (${(prepared.offer.zip_size / 1024).toFixed(1)} KB). Type a message to send with the file.`);
4236
+ } catch (err) {
4237
+ log.error(`Failed to prepare file: ${err.message}`);
4238
+ }
4239
+ rl.prompt();
4240
+ return;
4241
+ }
3816
4242
  console.log("");
4243
+ const uploadOffer = pendingUploadOffer;
4244
+ const uploadZipBuffer = pendingUploadZipBuffer;
4245
+ pendingUploadOffer = void 0;
4246
+ pendingUploadZipBuffer = void 0;
3817
4247
  try {
3818
4248
  await streamChat({
3819
4249
  agentId,
@@ -3821,8 +4251,12 @@ function registerChatCommand(program2) {
3821
4251
  token,
3822
4252
  baseUrl: opts.baseUrl,
3823
4253
  showThinking: opts.thinking,
3824
- mode
4254
+ mode,
4255
+ fileUploadOffer: uploadOffer
3825
4256
  });
4257
+ if (uploadOffer && uploadZipBuffer) {
4258
+ await chatWebrtcUpload(agentId, uploadOffer, uploadZipBuffer, token, opts.baseUrl);
4259
+ }
3826
4260
  } catch (err) {
3827
4261
  if (abortController.signal.aborted) return;
3828
4262
  log.error(err.message);
@@ -3835,11 +4269,11 @@ function registerChatCommand(program2) {
3835
4269
 
3836
4270
  // src/commands/skills.ts
3837
4271
  import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink } from "fs/promises";
3838
- import { join as join10, resolve, relative as relative4 } from "path";
4272
+ import { join as join11, resolve, relative as relative4 } from "path";
3839
4273
 
3840
4274
  // src/utils/skill-parser.ts
3841
4275
  import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
3842
- import { join as join9 } from "path";
4276
+ import { join as join10 } from "path";
3843
4277
  function parseSkillMd(raw) {
3844
4278
  const trimmed = raw.trimStart();
3845
4279
  if (!trimmed.startsWith("---")) {
@@ -3921,7 +4355,7 @@ function parseSkillMd(raw) {
3921
4355
  return { frontmatter, content };
3922
4356
  }
3923
4357
  async function loadSkillManifest(dir) {
3924
- const skillMdPath = join9(dir, "SKILL.md");
4358
+ const skillMdPath = join10(dir, "SKILL.md");
3925
4359
  try {
3926
4360
  const raw = await readFile2(skillMdPath, "utf-8");
3927
4361
  const { frontmatter } = parseSkillMd(raw);
@@ -4026,13 +4460,13 @@ function skillApiPath(authorLogin, slug) {
4026
4460
  }
4027
4461
  async function resolveSkillsRootAsync(pathArg) {
4028
4462
  const projectRoot = pathArg ? resolve(pathArg) : process.cwd();
4029
- const skillsDir = join10(projectRoot, ".agents", "skills");
4030
- const claudeSkillsDir = join10(projectRoot, ".claude", "skills");
4463
+ const skillsDir = join11(projectRoot, ".agents", "skills");
4464
+ const claudeSkillsDir = join11(projectRoot, ".claude", "skills");
4031
4465
  return { projectRoot, skillsDir, claudeSkillsDir };
4032
4466
  }
4033
4467
  async function ensureClaudeSymlink(claudeSkillsDir, slug) {
4034
4468
  await mkdir2(claudeSkillsDir, { recursive: true });
4035
- const linkPath = join10(claudeSkillsDir, slug);
4469
+ const linkPath = join11(claudeSkillsDir, slug);
4036
4470
  try {
4037
4471
  await unlink(linkPath);
4038
4472
  } catch {
@@ -4052,7 +4486,7 @@ async function collectPackFiles(dir, manifest) {
4052
4486
  }
4053
4487
  const mainFile = manifest.main || "SKILL.md";
4054
4488
  if (!results.includes(mainFile)) {
4055
- const mainPath = join10(dir, mainFile);
4489
+ const mainPath = join11(dir, mainFile);
4056
4490
  if (await pathExists(mainPath)) {
4057
4491
  results.unshift(mainFile);
4058
4492
  }
@@ -4069,7 +4503,7 @@ async function walkDir(dir) {
4069
4503
  }
4070
4504
  for (const entry of entries) {
4071
4505
  if (entry.isSymbolicLink()) continue;
4072
- const fullPath = join10(dir, entry.name);
4506
+ const fullPath = join11(dir, entry.name);
4073
4507
  if (entry.isDirectory()) {
4074
4508
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
4075
4509
  const sub = await walkDir(fullPath);
@@ -4087,7 +4521,7 @@ async function packSkill(dir, manifest) {
4087
4521
  }
4088
4522
  const entries = [];
4089
4523
  for (const relPath of fileList) {
4090
- const absPath = join10(dir, relPath);
4524
+ const absPath = join11(dir, relPath);
4091
4525
  try {
4092
4526
  const data = await readFile3(absPath);
4093
4527
  entries.push({ path: relPath.replace(/\\/g, "/"), data });
@@ -4121,7 +4555,7 @@ function bumpVersion(current, bump) {
4121
4555
  }
4122
4556
  async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4123
4557
  const meta = await client.get(skillApiPath(authorLogin, slug));
4124
- const targetDir = join10(skillsDir, slug);
4558
+ const targetDir = join11(skillsDir, slug);
4125
4559
  await mkdir2(targetDir, { recursive: true });
4126
4560
  if (meta.has_files) {
4127
4561
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/download`);
@@ -4129,8 +4563,8 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4129
4563
  const buf = Buffer.from(arrayBuf);
4130
4564
  const entries = extractZipBuffer(buf);
4131
4565
  for (const entry of entries) {
4132
- const filePath = join10(targetDir, entry.path);
4133
- const dir = join10(filePath, "..");
4566
+ const filePath = join11(targetDir, entry.path);
4567
+ const dir = join11(filePath, "..");
4134
4568
  await mkdir2(dir, { recursive: true });
4135
4569
  await writeFile3(filePath, entry.data);
4136
4570
  }
@@ -4143,7 +4577,7 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4143
4577
  } else {
4144
4578
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/raw`);
4145
4579
  const content = await res.text();
4146
- await writeFile3(join10(targetDir, "SKILL.md"), content);
4580
+ await writeFile3(join11(targetDir, "SKILL.md"), content);
4147
4581
  return {
4148
4582
  slug,
4149
4583
  name: meta.name,
@@ -4172,7 +4606,7 @@ function registerSkillsCommand(program2) {
4172
4606
  try {
4173
4607
  const dir = resolveSkillDir(pathArg);
4174
4608
  await mkdir2(dir, { recursive: true });
4175
- const skillMdPath = join10(dir, "SKILL.md");
4609
+ const skillMdPath = join11(dir, "SKILL.md");
4176
4610
  if (await pathExists(skillMdPath)) {
4177
4611
  const raw = await readFile3(skillMdPath, "utf-8");
4178
4612
  const { frontmatter } = parseSkillMd(raw);
@@ -4201,7 +4635,7 @@ function registerSkillsCommand(program2) {
4201
4635
  const dir = resolveSkillDir(pathArg);
4202
4636
  const manifest = await loadSkillManifest(dir);
4203
4637
  const result = await packSkill(dir, manifest);
4204
- const outPath = join10(dir, result.filename);
4638
+ const outPath = join11(dir, result.filename);
4205
4639
  await writeFile3(outPath, result.buffer);
4206
4640
  slog.info(`Packed ${result.files.length} files \u2192 ${result.filename} (${result.size} bytes)`);
4207
4641
  outputJson({
@@ -4247,7 +4681,7 @@ function registerSkillsCommand(program2) {
4247
4681
  if (opts.name) manifest.name = opts.name;
4248
4682
  if (opts.version) manifest.version = opts.version;
4249
4683
  if (opts.private !== void 0) manifest.private = opts.private;
4250
- content = await readFile3(join10(dir, manifest.main || "SKILL.md"), "utf-8");
4684
+ content = await readFile3(join11(dir, manifest.main || "SKILL.md"), "utf-8");
4251
4685
  packResult = await packSkill(dir, manifest);
4252
4686
  slog.info(`Packed ${packResult.files.length} files (${packResult.size} bytes)`);
4253
4687
  }
@@ -4384,7 +4818,7 @@ function registerSkillsCommand(program2) {
4384
4818
  skills.command("version <bump> [path]").description("Bump skill version (patch | minor | major | x.y.z)").action(async (bump, pathArg) => {
4385
4819
  try {
4386
4820
  const dir = resolveSkillDir(pathArg);
4387
- const skillMdPath = join10(dir, "SKILL.md");
4821
+ const skillMdPath = join11(dir, "SKILL.md");
4388
4822
  if (!await pathExists(skillMdPath)) {
4389
4823
  outputError("not_found", "No SKILL.md found. Run `agent-mesh skills init` first.");
4390
4824
  }
@@ -4404,7 +4838,7 @@ function registerSkillsCommand(program2) {
4404
4838
  try {
4405
4839
  const { authorLogin, slug } = parseSkillRef(ref);
4406
4840
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4407
- const targetDir = join10(skillsDir, slug);
4841
+ const targetDir = join11(skillsDir, slug);
4408
4842
  if (await pathExists(targetDir)) {
4409
4843
  if (!opts.force) {
4410
4844
  outputError("already_installed", `Skill "${slug}" is already installed at ${targetDir}. Use --force to overwrite.`);
@@ -4443,11 +4877,11 @@ function registerSkillsCommand(program2) {
4443
4877
  const failed = [];
4444
4878
  if (ref) {
4445
4879
  const { authorLogin, slug } = parseSkillRef(ref);
4446
- const targetDir = join10(skillsDir, slug);
4880
+ const targetDir = join11(skillsDir, slug);
4447
4881
  if (!await pathExists(targetDir)) {
4448
4882
  outputError("not_installed", `Skill "${slug}" is not installed. Use "skills install ${ref}" first.`);
4449
4883
  }
4450
- const skillMdPath = join10(targetDir, "SKILL.md");
4884
+ const skillMdPath = join11(targetDir, "SKILL.md");
4451
4885
  let localVersion = "0.0.0";
4452
4886
  if (await pathExists(skillMdPath)) {
4453
4887
  const raw = await readFile3(skillMdPath, "utf-8");
@@ -4474,7 +4908,7 @@ function registerSkillsCommand(program2) {
4474
4908
  for (const entry of entries) {
4475
4909
  if (!entry.isDirectory()) continue;
4476
4910
  const slug = entry.name;
4477
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4911
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4478
4912
  if (!await pathExists(skillMdPath)) {
4479
4913
  skipped.push({ slug, reason: "no_skill_md" });
4480
4914
  continue;
@@ -4494,7 +4928,7 @@ function registerSkillsCommand(program2) {
4494
4928
  skipped.push({ slug, reason: "up_to_date" });
4495
4929
  } else {
4496
4930
  slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
4497
- await rm(join10(skillsDir, slug), { recursive: true, force: true });
4931
+ await rm(join11(skillsDir, slug), { recursive: true, force: true });
4498
4932
  await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
4499
4933
  updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
4500
4934
  }
@@ -4515,13 +4949,13 @@ function registerSkillsCommand(program2) {
4515
4949
  skills.command("remove <slug> [path]").description("Remove a locally installed skill").action(async (slug, pathArg) => {
4516
4950
  try {
4517
4951
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4518
- const targetDir = join10(skillsDir, slug);
4952
+ const targetDir = join11(skillsDir, slug);
4519
4953
  if (!await pathExists(targetDir)) {
4520
4954
  outputError("not_installed", `Skill "${slug}" is not installed at ${targetDir}`);
4521
4955
  }
4522
4956
  await rm(targetDir, { recursive: true, force: true });
4523
4957
  try {
4524
- await unlink(join10(claudeSkillsDir, slug));
4958
+ await unlink(join11(claudeSkillsDir, slug));
4525
4959
  } catch {
4526
4960
  }
4527
4961
  slog.success(`Removed skill: ${slug}`);
@@ -4546,7 +4980,7 @@ function registerSkillsCommand(program2) {
4546
4980
  for (const entry of entries) {
4547
4981
  if (!entry.isDirectory()) continue;
4548
4982
  const slug = entry.name;
4549
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4983
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4550
4984
  if (!await pathExists(skillMdPath)) continue;
4551
4985
  const raw = await readFile3(skillMdPath, "utf-8");
4552
4986
  const { frontmatter } = parseSkillMd(raw);
@@ -4658,9 +5092,9 @@ function registerDiscoverCommand(program2) {
4658
5092
  }
4659
5093
 
4660
5094
  // src/commands/call.ts
4661
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "fs";
4662
- import { execSync as execSync4 } from "child_process";
4663
- import { join as join11 } from "path";
5095
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "fs";
5096
+ import { execSync as execSync5 } from "child_process";
5097
+ import { join as join12 } from "path";
4664
5098
  var DEFAULT_BASE_URL4 = "https://agents.hot";
4665
5099
  async function submitRating(baseUrl, token, agentId, callId, rating) {
4666
5100
  const res = await fetch(`${baseUrl}/api/agents/${agentId}/rate`, {
@@ -4754,12 +5188,12 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4754
5188
  poll()
4755
5189
  ]);
4756
5190
  mkdirSync5(outputDir, { recursive: true });
4757
- const zipPath = join11(outputDir, ".transfer.zip");
5191
+ const zipPath = join12(outputDir, ".transfer.zip");
4758
5192
  writeFileSync3(zipPath, zipBuffer);
4759
5193
  try {
4760
- execSync4(`unzip -o -q "${zipPath}" -d "${outputDir}"`);
5194
+ execSync5(`unzip -o -q "${zipPath}" -d "${outputDir}"`);
4761
5195
  try {
4762
- execSync4(`rm "${zipPath}"`);
5196
+ execSync5(`rm "${zipPath}"`);
4763
5197
  } catch {
4764
5198
  }
4765
5199
  } catch {
@@ -4788,6 +5222,120 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4788
5222
  receiver.close();
4789
5223
  }
4790
5224
  }
5225
+ function prepareFileForUpload(filePath) {
5226
+ const { readFileSync: _readFileSync } = __require("fs");
5227
+ const { basename: basename3 } = __require("path");
5228
+ const { execSync: _execSync } = __require("child_process");
5229
+ const { createHash: createHash3 } = __require("crypto");
5230
+ const { tmpdir: tmpdir2 } = __require("os");
5231
+ const fileName = basename3(filePath);
5232
+ const tempDir = join12(tmpdir2(), `upload-${Date.now()}`);
5233
+ const { mkdirSync: _mkdirSync } = __require("fs");
5234
+ _mkdirSync(tempDir, { recursive: true });
5235
+ const tempFile = join12(tempDir, fileName);
5236
+ const { copyFileSync: _copyFileSync } = __require("fs");
5237
+ _copyFileSync(filePath, tempFile);
5238
+ const zipPath = join12(tempDir, "upload.zip");
5239
+ _execSync(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
5240
+ const zipBuffer = _readFileSync(zipPath);
5241
+ try {
5242
+ _execSync(`rm -rf "${tempDir}"`);
5243
+ } catch {
5244
+ }
5245
+ const zipSha256 = createHash3("sha256").update(zipBuffer).digest("hex");
5246
+ const transferId = __require("crypto").randomUUID();
5247
+ return {
5248
+ offer: {
5249
+ transfer_id: transferId,
5250
+ zip_size: zipBuffer.length,
5251
+ zip_sha256: zipSha256,
5252
+ file_count: 1
5253
+ },
5254
+ zipBuffer: Buffer.from(zipBuffer)
5255
+ };
5256
+ }
5257
+ async function webrtcUpload(agentId, offer, zipBuffer, token, json) {
5258
+ if (!json) {
5259
+ log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
5260
+ }
5261
+ const sender = new FileUploadSender(offer.transfer_id, zipBuffer);
5262
+ const exchangeSignals = async (signal) => {
5263
+ try {
5264
+ const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${agentId}/rtc-signal`, {
5265
+ method: "POST",
5266
+ headers: {
5267
+ Authorization: `Bearer ${token}`,
5268
+ "Content-Type": "application/json"
5269
+ },
5270
+ body: JSON.stringify({
5271
+ transfer_id: offer.transfer_id,
5272
+ signal_type: signal.signal_type,
5273
+ payload: signal.payload
5274
+ })
5275
+ });
5276
+ if (res.ok) {
5277
+ const { signals } = await res.json();
5278
+ for (const s of signals) {
5279
+ await sender.handleSignal(s);
5280
+ }
5281
+ }
5282
+ } catch {
5283
+ }
5284
+ };
5285
+ sender.onSignal(exchangeSignals);
5286
+ await sender.createOffer();
5287
+ const poll = async () => {
5288
+ for (let i = 0; i < 20; i++) {
5289
+ await sleep6(500);
5290
+ try {
5291
+ const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${agentId}/rtc-signal`, {
5292
+ method: "POST",
5293
+ headers: {
5294
+ Authorization: `Bearer ${token}`,
5295
+ "Content-Type": "application/json"
5296
+ },
5297
+ body: JSON.stringify({
5298
+ transfer_id: offer.transfer_id,
5299
+ signal_type: "poll",
5300
+ payload: ""
5301
+ })
5302
+ });
5303
+ if (res.ok) {
5304
+ const { signals } = await res.json();
5305
+ for (const s of signals) {
5306
+ await sender.handleSignal(s);
5307
+ }
5308
+ }
5309
+ } catch {
5310
+ }
5311
+ }
5312
+ };
5313
+ try {
5314
+ await Promise.all([
5315
+ sender.waitForCompletion(3e4),
5316
+ poll()
5317
+ ]);
5318
+ if (json) {
5319
+ console.log(JSON.stringify({
5320
+ type: "files_uploaded",
5321
+ file_count: offer.file_count,
5322
+ zip_size: offer.zip_size,
5323
+ sha256: offer.zip_sha256
5324
+ }));
5325
+ } else {
5326
+ log.success(`[WebRTC] File uploaded successfully`);
5327
+ }
5328
+ } catch (err) {
5329
+ const msg = err.message;
5330
+ if (json) {
5331
+ console.log(JSON.stringify({ type: "file_upload_failed", error: msg }));
5332
+ } else {
5333
+ log.warn(`[WebRTC] File upload failed: ${msg}`);
5334
+ }
5335
+ } finally {
5336
+ sender.close();
5337
+ }
5338
+ }
4791
5339
  async function asyncCall(opts) {
4792
5340
  const selfAgentId = process.env.AGENT_BRIDGE_AGENT_ID;
4793
5341
  const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${opts.id}/call`, {
@@ -4800,7 +5348,8 @@ async function asyncCall(opts) {
4800
5348
  body: JSON.stringify({
4801
5349
  task_description: opts.taskDescription,
4802
5350
  mode: "async",
4803
- ...opts.withFiles ? { with_files: true } : {}
5351
+ ...opts.withFiles ? { with_files: true } : {},
5352
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4804
5353
  }),
4805
5354
  signal: opts.signal
4806
5355
  });
@@ -4880,7 +5429,7 @@ async function asyncCall(opts) {
4880
5429
  if (!opts.json) log.info(`Saved to ${opts.outputFile}`);
4881
5430
  }
4882
5431
  if (offer && opts.withFiles) {
4883
- const outputDir = opts.outputFile ? join11(opts.outputFile, "..", "files") : join11(process.cwd(), "agent-output");
5432
+ const outputDir = opts.outputFile ? join12(opts.outputFile, "..", "files") : join12(process.cwd(), "agent-output");
4884
5433
  await webrtcDownload(opts.id, offer, opts.token, outputDir, opts.json);
4885
5434
  }
4886
5435
  if (!opts.json) {
@@ -4919,7 +5468,8 @@ async function streamCall(opts) {
4919
5468
  },
4920
5469
  body: JSON.stringify({
4921
5470
  task_description: opts.taskDescription,
4922
- ...opts.withFiles ? { with_files: true } : {}
5471
+ ...opts.withFiles ? { with_files: true } : {},
5472
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4923
5473
  }),
4924
5474
  signal: opts.signal
4925
5475
  });
@@ -5054,7 +5604,7 @@ Error: ${event.message}
5054
5604
  }
5055
5605
  if (fileOffer && opts.withFiles) {
5056
5606
  console.log("");
5057
- const outputDir = opts.outputFile ? join11(opts.outputFile, "..", "files") : join11(process.cwd(), "agent-output");
5607
+ const outputDir = opts.outputFile ? join12(opts.outputFile, "..", "files") : join12(process.cwd(), "agent-output");
5058
5608
  await webrtcDownload(opts.id, fileOffer, opts.token, outputDir, opts.json);
5059
5609
  }
5060
5610
  if (!opts.json) {
@@ -5070,7 +5620,7 @@ Error: ${event.message}
5070
5620
  return { callId, ...sessionKey ? { sessionKey } : {} };
5071
5621
  }
5072
5622
  function registerCallCommand(program2) {
5073
- 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) => {
5623
+ 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("--upload-file <path>", "Upload file to agent via WebRTC P2P").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) => {
5074
5624
  try {
5075
5625
  const token = loadToken();
5076
5626
  if (!token) {
@@ -5081,13 +5631,25 @@ function registerCallCommand(program2) {
5081
5631
  const { id, name } = await resolveAgentId(agentInput, client);
5082
5632
  let taskDescription = opts.task;
5083
5633
  if (opts.inputFile) {
5084
- const content = readFileSync2(opts.inputFile, "utf-8");
5634
+ const content = readFileSync3(opts.inputFile, "utf-8");
5085
5635
  taskDescription = `${taskDescription}
5086
5636
 
5087
5637
  ---
5088
5638
 
5089
5639
  ${content}`;
5090
5640
  }
5641
+ let uploadOffer;
5642
+ let uploadZipBuffer;
5643
+ if (opts.uploadFile) {
5644
+ const { existsSync: existsSync8 } = __require("fs");
5645
+ if (!existsSync8(opts.uploadFile)) {
5646
+ log.error(`File not found: ${opts.uploadFile}`);
5647
+ process.exit(1);
5648
+ }
5649
+ const prepared = prepareFileForUpload(opts.uploadFile);
5650
+ uploadOffer = prepared.offer;
5651
+ uploadZipBuffer = prepared.zipBuffer;
5652
+ }
5091
5653
  const timeoutMs = parseInt(opts.timeout || "300", 10) * 1e3;
5092
5654
  const abortController = new AbortController();
5093
5655
  const timer = setTimeout(() => abortController.abort(), timeoutMs);
@@ -5100,13 +5662,20 @@ ${content}`;
5100
5662
  json: opts.json,
5101
5663
  outputFile: opts.outputFile,
5102
5664
  signal: abortController.signal,
5103
- withFiles: opts.withFiles
5665
+ withFiles: opts.withFiles,
5666
+ uploadOffer
5104
5667
  };
5105
5668
  let result;
5106
5669
  if (opts.stream) {
5670
+ if (uploadOffer && uploadZipBuffer) {
5671
+ void webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5672
+ }
5107
5673
  result = await streamCall(callOpts);
5108
5674
  } else {
5109
5675
  result = await asyncCall(callOpts);
5676
+ if (uploadOffer && uploadZipBuffer) {
5677
+ await webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5678
+ }
5110
5679
  }
5111
5680
  clearTimeout(timer);
5112
5681
  if (opts.rate && result.callId) {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ListTUI,
4
4
  registerListCommand
5
- } from "./chunk-KEUGYA3L.js";
5
+ } from "./chunk-5CMYB6XV.js";
6
6
  export {
7
7
  ListTUI,
8
8
  registerListCommand
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@annals/agent-mesh",
3
- "version": "0.17.9",
3
+ "version": "0.18.0",
4
4
  "description": "CLI bridge connecting local AI agents to the Agents.Hot platform",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,7 @@
11
11
  "commander": "^13.0.0",
12
12
  "node-datachannel": "^0.32.0",
13
13
  "ws": "^8.18.0",
14
- "@annals/bridge-protocol": "^0.2.1"
14
+ "@annals/bridge-protocol": "^0.2.2"
15
15
  },
16
16
  "devDependencies": {
17
17
  "@types/ws": "^8.5.0",