@annals/agent-mesh 0.17.9 → 0.18.1

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: mkdirSync7, writeFileSync: writeFileSync4 } = __require("fs");
1665
+ const { join: join13 } = __require("path");
1666
+ const { execSync: execSync6 } = __require("child_process");
1667
+ mkdirSync7(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, mkdirSync as mkdirSync5, copyFileSync as copyFileSync2 } 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,97 @@ 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
+ mkdirSync5(tempDir, { recursive: true });
3888
+ const tempFile = join9(tempDir, fileName);
3889
+ copyFileSync2(filePath, tempFile);
3890
+ const zipPath = join9(tempDir, "upload.zip");
3891
+ execSync4(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
3892
+ const zipBuffer = readFileSync2(zipPath);
3893
+ try {
3894
+ execSync4(`rm -rf "${tempDir}"`);
3895
+ } catch {
3896
+ }
3897
+ const zipSha256 = createHash2("sha256").update(zipBuffer).digest("hex");
3898
+ const transferId = randomUUID();
3899
+ return {
3900
+ offer: {
3901
+ transfer_id: transferId,
3902
+ zip_size: zipBuffer.length,
3903
+ zip_sha256: zipSha256,
3904
+ file_count: 1
3905
+ },
3906
+ zipBuffer: Buffer.from(zipBuffer)
3907
+ };
3908
+ }
3580
3909
  function sleep5(ms) {
3581
3910
  return new Promise((resolve2) => setTimeout(resolve2, ms));
3582
3911
  }
3912
+ async function chatWebrtcUpload(agentId, offer, zipBuffer, token, baseUrl) {
3913
+ log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
3914
+ const sender = new FileUploadSender(offer.transfer_id, zipBuffer);
3915
+ const exchangeSignals = async (signal) => {
3916
+ try {
3917
+ const res = await fetch(`${baseUrl}/api/agents/${agentId}/rtc-signal`, {
3918
+ method: "POST",
3919
+ headers: {
3920
+ Authorization: `Bearer ${token}`,
3921
+ "Content-Type": "application/json"
3922
+ },
3923
+ body: JSON.stringify({
3924
+ transfer_id: offer.transfer_id,
3925
+ signal_type: signal.signal_type,
3926
+ payload: signal.payload
3927
+ })
3928
+ });
3929
+ if (res.ok) {
3930
+ const { signals } = await res.json();
3931
+ for (const s of signals) {
3932
+ await sender.handleSignal(s);
3933
+ }
3934
+ }
3935
+ } catch {
3936
+ }
3937
+ };
3938
+ sender.onSignal(exchangeSignals);
3939
+ await sender.createOffer();
3940
+ const poll = async () => {
3941
+ for (let i = 0; i < 20; i++) {
3942
+ await sleep5(500);
3943
+ try {
3944
+ const res = await fetch(`${baseUrl}/api/agents/${agentId}/rtc-signal`, {
3945
+ method: "POST",
3946
+ headers: {
3947
+ Authorization: `Bearer ${token}`,
3948
+ "Content-Type": "application/json"
3949
+ },
3950
+ body: JSON.stringify({
3951
+ transfer_id: offer.transfer_id,
3952
+ signal_type: "poll",
3953
+ payload: ""
3954
+ })
3955
+ });
3956
+ if (res.ok) {
3957
+ const { signals } = await res.json();
3958
+ for (const s of signals) {
3959
+ await sender.handleSignal(s);
3960
+ }
3961
+ }
3962
+ } catch {
3963
+ }
3964
+ }
3965
+ };
3966
+ try {
3967
+ await Promise.all([sender.waitForCompletion(3e4), poll()]);
3968
+ log.success(`[WebRTC] File uploaded successfully`);
3969
+ } catch (err) {
3970
+ log.warn(`[WebRTC] Upload failed: ${err.message}`);
3971
+ } finally {
3972
+ sender.close();
3973
+ }
3974
+ }
3583
3975
  async function asyncChat(opts) {
3584
3976
  const res = await fetch(`${opts.baseUrl}/api/agents/${opts.agentId}/chat`, {
3585
3977
  method: "POST",
@@ -3587,7 +3979,11 @@ async function asyncChat(opts) {
3587
3979
  Authorization: `Bearer ${opts.token}`,
3588
3980
  "Content-Type": "application/json"
3589
3981
  },
3590
- body: JSON.stringify({ message: opts.message, mode: "async" }),
3982
+ body: JSON.stringify({
3983
+ message: opts.message,
3984
+ mode: "async",
3985
+ ...opts.fileUploadOffer ? { file_upload_offer: opts.fileUploadOffer } : {}
3986
+ }),
3591
3987
  signal: opts.signal
3592
3988
  });
3593
3989
  if (!res.ok) {
@@ -3655,7 +4051,10 @@ async function streamChat(opts) {
3655
4051
  Authorization: `Bearer ${opts.token}`,
3656
4052
  "Content-Type": "application/json"
3657
4053
  },
3658
- body: JSON.stringify({ message: opts.message }),
4054
+ body: JSON.stringify({
4055
+ message: opts.message,
4056
+ ...opts.fileUploadOffer ? { file_upload_offer: opts.fileUploadOffer } : {}
4057
+ }),
3659
4058
  signal: opts.signal
3660
4059
  });
3661
4060
  if (!res.ok) {
@@ -3789,8 +4188,10 @@ function registerChatCommand(program2) {
3789
4188
  process.exit(1);
3790
4189
  }
3791
4190
  log.banner(`Chat with ${agentName}`);
3792
- console.log(`${GRAY}Type your message and press Enter. Use /quit or Ctrl+C to exit.${RESET}
4191
+ console.log(`${GRAY}Type your message and press Enter. /upload <path> to send a file. /quit to exit.${RESET}
3793
4192
  `);
4193
+ let pendingUploadOffer;
4194
+ let pendingUploadZipBuffer;
3794
4195
  const rl = createInterface4({
3795
4196
  input: process.stdin,
3796
4197
  output: process.stdout,
@@ -3813,7 +4214,34 @@ function registerChatCommand(program2) {
3813
4214
  rl.close();
3814
4215
  return;
3815
4216
  }
4217
+ if (trimmed.startsWith("/upload ")) {
4218
+ const filePath = trimmed.slice(8).trim();
4219
+ if (!filePath) {
4220
+ log.error("Usage: /upload <path>");
4221
+ rl.prompt();
4222
+ return;
4223
+ }
4224
+ if (!existsSync7(filePath)) {
4225
+ log.error(`File not found: ${filePath}`);
4226
+ rl.prompt();
4227
+ return;
4228
+ }
4229
+ try {
4230
+ const prepared = prepareUploadFile(filePath);
4231
+ pendingUploadOffer = prepared.offer;
4232
+ pendingUploadZipBuffer = prepared.zipBuffer;
4233
+ log.info(`File staged: ${basename2(filePath)} (${(prepared.offer.zip_size / 1024).toFixed(1)} KB). Type a message to send with the file.`);
4234
+ } catch (err) {
4235
+ log.error(`Failed to prepare file: ${err.message}`);
4236
+ }
4237
+ rl.prompt();
4238
+ return;
4239
+ }
3816
4240
  console.log("");
4241
+ const uploadOffer = pendingUploadOffer;
4242
+ const uploadZipBuffer = pendingUploadZipBuffer;
4243
+ pendingUploadOffer = void 0;
4244
+ pendingUploadZipBuffer = void 0;
3817
4245
  try {
3818
4246
  await streamChat({
3819
4247
  agentId,
@@ -3821,8 +4249,12 @@ function registerChatCommand(program2) {
3821
4249
  token,
3822
4250
  baseUrl: opts.baseUrl,
3823
4251
  showThinking: opts.thinking,
3824
- mode
4252
+ mode,
4253
+ fileUploadOffer: uploadOffer
3825
4254
  });
4255
+ if (uploadOffer && uploadZipBuffer) {
4256
+ await chatWebrtcUpload(agentId, uploadOffer, uploadZipBuffer, token, opts.baseUrl);
4257
+ }
3826
4258
  } catch (err) {
3827
4259
  if (abortController.signal.aborted) return;
3828
4260
  log.error(err.message);
@@ -3835,11 +4267,11 @@ function registerChatCommand(program2) {
3835
4267
 
3836
4268
  // src/commands/skills.ts
3837
4269
  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";
4270
+ import { join as join11, resolve, relative as relative4 } from "path";
3839
4271
 
3840
4272
  // src/utils/skill-parser.ts
3841
4273
  import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
3842
- import { join as join9 } from "path";
4274
+ import { join as join10 } from "path";
3843
4275
  function parseSkillMd(raw) {
3844
4276
  const trimmed = raw.trimStart();
3845
4277
  if (!trimmed.startsWith("---")) {
@@ -3921,7 +4353,7 @@ function parseSkillMd(raw) {
3921
4353
  return { frontmatter, content };
3922
4354
  }
3923
4355
  async function loadSkillManifest(dir) {
3924
- const skillMdPath = join9(dir, "SKILL.md");
4356
+ const skillMdPath = join10(dir, "SKILL.md");
3925
4357
  try {
3926
4358
  const raw = await readFile2(skillMdPath, "utf-8");
3927
4359
  const { frontmatter } = parseSkillMd(raw);
@@ -4026,13 +4458,13 @@ function skillApiPath(authorLogin, slug) {
4026
4458
  }
4027
4459
  async function resolveSkillsRootAsync(pathArg) {
4028
4460
  const projectRoot = pathArg ? resolve(pathArg) : process.cwd();
4029
- const skillsDir = join10(projectRoot, ".agents", "skills");
4030
- const claudeSkillsDir = join10(projectRoot, ".claude", "skills");
4461
+ const skillsDir = join11(projectRoot, ".agents", "skills");
4462
+ const claudeSkillsDir = join11(projectRoot, ".claude", "skills");
4031
4463
  return { projectRoot, skillsDir, claudeSkillsDir };
4032
4464
  }
4033
4465
  async function ensureClaudeSymlink(claudeSkillsDir, slug) {
4034
4466
  await mkdir2(claudeSkillsDir, { recursive: true });
4035
- const linkPath = join10(claudeSkillsDir, slug);
4467
+ const linkPath = join11(claudeSkillsDir, slug);
4036
4468
  try {
4037
4469
  await unlink(linkPath);
4038
4470
  } catch {
@@ -4052,7 +4484,7 @@ async function collectPackFiles(dir, manifest) {
4052
4484
  }
4053
4485
  const mainFile = manifest.main || "SKILL.md";
4054
4486
  if (!results.includes(mainFile)) {
4055
- const mainPath = join10(dir, mainFile);
4487
+ const mainPath = join11(dir, mainFile);
4056
4488
  if (await pathExists(mainPath)) {
4057
4489
  results.unshift(mainFile);
4058
4490
  }
@@ -4069,7 +4501,7 @@ async function walkDir(dir) {
4069
4501
  }
4070
4502
  for (const entry of entries) {
4071
4503
  if (entry.isSymbolicLink()) continue;
4072
- const fullPath = join10(dir, entry.name);
4504
+ const fullPath = join11(dir, entry.name);
4073
4505
  if (entry.isDirectory()) {
4074
4506
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
4075
4507
  const sub = await walkDir(fullPath);
@@ -4087,7 +4519,7 @@ async function packSkill(dir, manifest) {
4087
4519
  }
4088
4520
  const entries = [];
4089
4521
  for (const relPath of fileList) {
4090
- const absPath = join10(dir, relPath);
4522
+ const absPath = join11(dir, relPath);
4091
4523
  try {
4092
4524
  const data = await readFile3(absPath);
4093
4525
  entries.push({ path: relPath.replace(/\\/g, "/"), data });
@@ -4121,7 +4553,7 @@ function bumpVersion(current, bump) {
4121
4553
  }
4122
4554
  async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4123
4555
  const meta = await client.get(skillApiPath(authorLogin, slug));
4124
- const targetDir = join10(skillsDir, slug);
4556
+ const targetDir = join11(skillsDir, slug);
4125
4557
  await mkdir2(targetDir, { recursive: true });
4126
4558
  if (meta.has_files) {
4127
4559
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/download`);
@@ -4129,8 +4561,8 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4129
4561
  const buf = Buffer.from(arrayBuf);
4130
4562
  const entries = extractZipBuffer(buf);
4131
4563
  for (const entry of entries) {
4132
- const filePath = join10(targetDir, entry.path);
4133
- const dir = join10(filePath, "..");
4564
+ const filePath = join11(targetDir, entry.path);
4565
+ const dir = join11(filePath, "..");
4134
4566
  await mkdir2(dir, { recursive: true });
4135
4567
  await writeFile3(filePath, entry.data);
4136
4568
  }
@@ -4143,7 +4575,7 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4143
4575
  } else {
4144
4576
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/raw`);
4145
4577
  const content = await res.text();
4146
- await writeFile3(join10(targetDir, "SKILL.md"), content);
4578
+ await writeFile3(join11(targetDir, "SKILL.md"), content);
4147
4579
  return {
4148
4580
  slug,
4149
4581
  name: meta.name,
@@ -4172,7 +4604,7 @@ function registerSkillsCommand(program2) {
4172
4604
  try {
4173
4605
  const dir = resolveSkillDir(pathArg);
4174
4606
  await mkdir2(dir, { recursive: true });
4175
- const skillMdPath = join10(dir, "SKILL.md");
4607
+ const skillMdPath = join11(dir, "SKILL.md");
4176
4608
  if (await pathExists(skillMdPath)) {
4177
4609
  const raw = await readFile3(skillMdPath, "utf-8");
4178
4610
  const { frontmatter } = parseSkillMd(raw);
@@ -4201,7 +4633,7 @@ function registerSkillsCommand(program2) {
4201
4633
  const dir = resolveSkillDir(pathArg);
4202
4634
  const manifest = await loadSkillManifest(dir);
4203
4635
  const result = await packSkill(dir, manifest);
4204
- const outPath = join10(dir, result.filename);
4636
+ const outPath = join11(dir, result.filename);
4205
4637
  await writeFile3(outPath, result.buffer);
4206
4638
  slog.info(`Packed ${result.files.length} files \u2192 ${result.filename} (${result.size} bytes)`);
4207
4639
  outputJson({
@@ -4247,7 +4679,7 @@ function registerSkillsCommand(program2) {
4247
4679
  if (opts.name) manifest.name = opts.name;
4248
4680
  if (opts.version) manifest.version = opts.version;
4249
4681
  if (opts.private !== void 0) manifest.private = opts.private;
4250
- content = await readFile3(join10(dir, manifest.main || "SKILL.md"), "utf-8");
4682
+ content = await readFile3(join11(dir, manifest.main || "SKILL.md"), "utf-8");
4251
4683
  packResult = await packSkill(dir, manifest);
4252
4684
  slog.info(`Packed ${packResult.files.length} files (${packResult.size} bytes)`);
4253
4685
  }
@@ -4384,7 +4816,7 @@ function registerSkillsCommand(program2) {
4384
4816
  skills.command("version <bump> [path]").description("Bump skill version (patch | minor | major | x.y.z)").action(async (bump, pathArg) => {
4385
4817
  try {
4386
4818
  const dir = resolveSkillDir(pathArg);
4387
- const skillMdPath = join10(dir, "SKILL.md");
4819
+ const skillMdPath = join11(dir, "SKILL.md");
4388
4820
  if (!await pathExists(skillMdPath)) {
4389
4821
  outputError("not_found", "No SKILL.md found. Run `agent-mesh skills init` first.");
4390
4822
  }
@@ -4404,7 +4836,7 @@ function registerSkillsCommand(program2) {
4404
4836
  try {
4405
4837
  const { authorLogin, slug } = parseSkillRef(ref);
4406
4838
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4407
- const targetDir = join10(skillsDir, slug);
4839
+ const targetDir = join11(skillsDir, slug);
4408
4840
  if (await pathExists(targetDir)) {
4409
4841
  if (!opts.force) {
4410
4842
  outputError("already_installed", `Skill "${slug}" is already installed at ${targetDir}. Use --force to overwrite.`);
@@ -4443,11 +4875,11 @@ function registerSkillsCommand(program2) {
4443
4875
  const failed = [];
4444
4876
  if (ref) {
4445
4877
  const { authorLogin, slug } = parseSkillRef(ref);
4446
- const targetDir = join10(skillsDir, slug);
4878
+ const targetDir = join11(skillsDir, slug);
4447
4879
  if (!await pathExists(targetDir)) {
4448
4880
  outputError("not_installed", `Skill "${slug}" is not installed. Use "skills install ${ref}" first.`);
4449
4881
  }
4450
- const skillMdPath = join10(targetDir, "SKILL.md");
4882
+ const skillMdPath = join11(targetDir, "SKILL.md");
4451
4883
  let localVersion = "0.0.0";
4452
4884
  if (await pathExists(skillMdPath)) {
4453
4885
  const raw = await readFile3(skillMdPath, "utf-8");
@@ -4474,7 +4906,7 @@ function registerSkillsCommand(program2) {
4474
4906
  for (const entry of entries) {
4475
4907
  if (!entry.isDirectory()) continue;
4476
4908
  const slug = entry.name;
4477
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4909
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4478
4910
  if (!await pathExists(skillMdPath)) {
4479
4911
  skipped.push({ slug, reason: "no_skill_md" });
4480
4912
  continue;
@@ -4494,7 +4926,7 @@ function registerSkillsCommand(program2) {
4494
4926
  skipped.push({ slug, reason: "up_to_date" });
4495
4927
  } else {
4496
4928
  slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
4497
- await rm(join10(skillsDir, slug), { recursive: true, force: true });
4929
+ await rm(join11(skillsDir, slug), { recursive: true, force: true });
4498
4930
  await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
4499
4931
  updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
4500
4932
  }
@@ -4515,13 +4947,13 @@ function registerSkillsCommand(program2) {
4515
4947
  skills.command("remove <slug> [path]").description("Remove a locally installed skill").action(async (slug, pathArg) => {
4516
4948
  try {
4517
4949
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4518
- const targetDir = join10(skillsDir, slug);
4950
+ const targetDir = join11(skillsDir, slug);
4519
4951
  if (!await pathExists(targetDir)) {
4520
4952
  outputError("not_installed", `Skill "${slug}" is not installed at ${targetDir}`);
4521
4953
  }
4522
4954
  await rm(targetDir, { recursive: true, force: true });
4523
4955
  try {
4524
- await unlink(join10(claudeSkillsDir, slug));
4956
+ await unlink(join11(claudeSkillsDir, slug));
4525
4957
  } catch {
4526
4958
  }
4527
4959
  slog.success(`Removed skill: ${slug}`);
@@ -4546,7 +4978,7 @@ function registerSkillsCommand(program2) {
4546
4978
  for (const entry of entries) {
4547
4979
  if (!entry.isDirectory()) continue;
4548
4980
  const slug = entry.name;
4549
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4981
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4550
4982
  if (!await pathExists(skillMdPath)) continue;
4551
4983
  const raw = await readFile3(skillMdPath, "utf-8");
4552
4984
  const { frontmatter } = parseSkillMd(raw);
@@ -4658,9 +5090,11 @@ function registerDiscoverCommand(program2) {
4658
5090
  }
4659
5091
 
4660
5092
  // 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";
5093
+ import { readFileSync as readFileSync3, writeFileSync as writeFileSync3, mkdirSync as mkdirSync6, copyFileSync as copyFileSync3 } from "fs";
5094
+ import { execSync as execSync5 } from "child_process";
5095
+ import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
5096
+ import { join as join12, basename as basename3 } from "path";
5097
+ import { tmpdir as tmpdir2 } from "os";
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`, {
@@ -4753,13 +5187,13 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4753
5187
  receiver.waitForCompletion(3e4),
4754
5188
  poll()
4755
5189
  ]);
4756
- mkdirSync5(outputDir, { recursive: true });
4757
- const zipPath = join11(outputDir, ".transfer.zip");
5190
+ mkdirSync6(outputDir, { recursive: true });
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,113 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4788
5222
  receiver.close();
4789
5223
  }
4790
5224
  }
5225
+ function prepareFileForUpload(filePath) {
5226
+ const fileName = basename3(filePath);
5227
+ const tempDir = join12(tmpdir2(), `upload-${Date.now()}`);
5228
+ mkdirSync6(tempDir, { recursive: true });
5229
+ const tempFile = join12(tempDir, fileName);
5230
+ copyFileSync3(filePath, tempFile);
5231
+ const zipPath = join12(tempDir, "upload.zip");
5232
+ execSync5(`cd "${tempDir}" && zip -q "${zipPath}" "${fileName}"`);
5233
+ const zipBuffer = readFileSync3(zipPath);
5234
+ try {
5235
+ execSync5(`rm -rf "${tempDir}"`);
5236
+ } catch {
5237
+ }
5238
+ const zipSha256 = createHash3("sha256").update(zipBuffer).digest("hex");
5239
+ const transferId = randomUUID2();
5240
+ return {
5241
+ offer: {
5242
+ transfer_id: transferId,
5243
+ zip_size: zipBuffer.length,
5244
+ zip_sha256: zipSha256,
5245
+ file_count: 1
5246
+ },
5247
+ zipBuffer: Buffer.from(zipBuffer)
5248
+ };
5249
+ }
5250
+ async function webrtcUpload(agentId, offer, zipBuffer, token, json) {
5251
+ if (!json) {
5252
+ log.info(`[WebRTC] Uploading file (${(offer.zip_size / 1024).toFixed(1)} KB)...`);
5253
+ }
5254
+ const sender = new FileUploadSender(offer.transfer_id, zipBuffer);
5255
+ const exchangeSignals = async (signal) => {
5256
+ try {
5257
+ const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${agentId}/rtc-signal`, {
5258
+ method: "POST",
5259
+ headers: {
5260
+ Authorization: `Bearer ${token}`,
5261
+ "Content-Type": "application/json"
5262
+ },
5263
+ body: JSON.stringify({
5264
+ transfer_id: offer.transfer_id,
5265
+ signal_type: signal.signal_type,
5266
+ payload: signal.payload
5267
+ })
5268
+ });
5269
+ if (res.ok) {
5270
+ const { signals } = await res.json();
5271
+ for (const s of signals) {
5272
+ await sender.handleSignal(s);
5273
+ }
5274
+ }
5275
+ } catch {
5276
+ }
5277
+ };
5278
+ sender.onSignal(exchangeSignals);
5279
+ await sender.createOffer();
5280
+ const poll = async () => {
5281
+ for (let i = 0; i < 20; i++) {
5282
+ await sleep6(500);
5283
+ try {
5284
+ const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${agentId}/rtc-signal`, {
5285
+ method: "POST",
5286
+ headers: {
5287
+ Authorization: `Bearer ${token}`,
5288
+ "Content-Type": "application/json"
5289
+ },
5290
+ body: JSON.stringify({
5291
+ transfer_id: offer.transfer_id,
5292
+ signal_type: "poll",
5293
+ payload: ""
5294
+ })
5295
+ });
5296
+ if (res.ok) {
5297
+ const { signals } = await res.json();
5298
+ for (const s of signals) {
5299
+ await sender.handleSignal(s);
5300
+ }
5301
+ }
5302
+ } catch {
5303
+ }
5304
+ }
5305
+ };
5306
+ try {
5307
+ await Promise.all([
5308
+ sender.waitForCompletion(3e4),
5309
+ poll()
5310
+ ]);
5311
+ if (json) {
5312
+ console.log(JSON.stringify({
5313
+ type: "files_uploaded",
5314
+ file_count: offer.file_count,
5315
+ zip_size: offer.zip_size,
5316
+ sha256: offer.zip_sha256
5317
+ }));
5318
+ } else {
5319
+ log.success(`[WebRTC] File uploaded successfully`);
5320
+ }
5321
+ } catch (err) {
5322
+ const msg = err.message;
5323
+ if (json) {
5324
+ console.log(JSON.stringify({ type: "file_upload_failed", error: msg }));
5325
+ } else {
5326
+ log.warn(`[WebRTC] File upload failed: ${msg}`);
5327
+ }
5328
+ } finally {
5329
+ sender.close();
5330
+ }
5331
+ }
4791
5332
  async function asyncCall(opts) {
4792
5333
  const selfAgentId = process.env.AGENT_BRIDGE_AGENT_ID;
4793
5334
  const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${opts.id}/call`, {
@@ -4800,7 +5341,8 @@ async function asyncCall(opts) {
4800
5341
  body: JSON.stringify({
4801
5342
  task_description: opts.taskDescription,
4802
5343
  mode: "async",
4803
- ...opts.withFiles ? { with_files: true } : {}
5344
+ ...opts.withFiles ? { with_files: true } : {},
5345
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4804
5346
  }),
4805
5347
  signal: opts.signal
4806
5348
  });
@@ -4880,7 +5422,7 @@ async function asyncCall(opts) {
4880
5422
  if (!opts.json) log.info(`Saved to ${opts.outputFile}`);
4881
5423
  }
4882
5424
  if (offer && opts.withFiles) {
4883
- const outputDir = opts.outputFile ? join11(opts.outputFile, "..", "files") : join11(process.cwd(), "agent-output");
5425
+ const outputDir = opts.outputFile ? join12(opts.outputFile, "..", "files") : join12(process.cwd(), "agent-output");
4884
5426
  await webrtcDownload(opts.id, offer, opts.token, outputDir, opts.json);
4885
5427
  }
4886
5428
  if (!opts.json) {
@@ -4919,7 +5461,8 @@ async function streamCall(opts) {
4919
5461
  },
4920
5462
  body: JSON.stringify({
4921
5463
  task_description: opts.taskDescription,
4922
- ...opts.withFiles ? { with_files: true } : {}
5464
+ ...opts.withFiles ? { with_files: true } : {},
5465
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4923
5466
  }),
4924
5467
  signal: opts.signal
4925
5468
  });
@@ -5054,7 +5597,7 @@ Error: ${event.message}
5054
5597
  }
5055
5598
  if (fileOffer && opts.withFiles) {
5056
5599
  console.log("");
5057
- const outputDir = opts.outputFile ? join11(opts.outputFile, "..", "files") : join11(process.cwd(), "agent-output");
5600
+ const outputDir = opts.outputFile ? join12(opts.outputFile, "..", "files") : join12(process.cwd(), "agent-output");
5058
5601
  await webrtcDownload(opts.id, fileOffer, opts.token, outputDir, opts.json);
5059
5602
  }
5060
5603
  if (!opts.json) {
@@ -5070,7 +5613,7 @@ Error: ${event.message}
5070
5613
  return { callId, ...sessionKey ? { sessionKey } : {} };
5071
5614
  }
5072
5615
  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) => {
5616
+ 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
5617
  try {
5075
5618
  const token = loadToken();
5076
5619
  if (!token) {
@@ -5081,13 +5624,25 @@ function registerCallCommand(program2) {
5081
5624
  const { id, name } = await resolveAgentId(agentInput, client);
5082
5625
  let taskDescription = opts.task;
5083
5626
  if (opts.inputFile) {
5084
- const content = readFileSync2(opts.inputFile, "utf-8");
5627
+ const content = readFileSync3(opts.inputFile, "utf-8");
5085
5628
  taskDescription = `${taskDescription}
5086
5629
 
5087
5630
  ---
5088
5631
 
5089
5632
  ${content}`;
5090
5633
  }
5634
+ let uploadOffer;
5635
+ let uploadZipBuffer;
5636
+ if (opts.uploadFile) {
5637
+ const { existsSync: existsSync8 } = __require("fs");
5638
+ if (!existsSync8(opts.uploadFile)) {
5639
+ log.error(`File not found: ${opts.uploadFile}`);
5640
+ process.exit(1);
5641
+ }
5642
+ const prepared = prepareFileForUpload(opts.uploadFile);
5643
+ uploadOffer = prepared.offer;
5644
+ uploadZipBuffer = prepared.zipBuffer;
5645
+ }
5091
5646
  const timeoutMs = parseInt(opts.timeout || "300", 10) * 1e3;
5092
5647
  const abortController = new AbortController();
5093
5648
  const timer = setTimeout(() => abortController.abort(), timeoutMs);
@@ -5100,13 +5655,20 @@ ${content}`;
5100
5655
  json: opts.json,
5101
5656
  outputFile: opts.outputFile,
5102
5657
  signal: abortController.signal,
5103
- withFiles: opts.withFiles
5658
+ withFiles: opts.withFiles,
5659
+ uploadOffer
5104
5660
  };
5105
5661
  let result;
5106
5662
  if (opts.stream) {
5663
+ if (uploadOffer && uploadZipBuffer) {
5664
+ void webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5665
+ }
5107
5666
  result = await streamCall(callOpts);
5108
5667
  } else {
5109
5668
  result = await asyncCall(callOpts);
5669
+ if (uploadOffer && uploadZipBuffer) {
5670
+ await webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5671
+ }
5110
5672
  }
5111
5673
  clearTimeout(timer);
5112
5674
  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.1",
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",