@annals/agent-mesh 0.17.8 → 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
  }
@@ -1515,11 +1814,11 @@ async function initSandbox(agentType) {
1515
1814
  const filesystem = getSandboxPreset(agentType);
1516
1815
  try {
1517
1816
  await mgr.initialize({
1518
- network: { allowedDomains: ["placeholder.example.com"], deniedDomains: [] },
1817
+ network: { allowedDomains: ["placeholder.example.com"], deniedDomains: [], allowLocalBinding: true },
1519
1818
  filesystem
1520
1819
  });
1521
1820
  mgr.updateConfig({
1522
- network: { deniedDomains: [] },
1821
+ network: { allowedDomains: [], deniedDomains: [], allowLocalBinding: true },
1523
1822
  filesystem
1524
1823
  });
1525
1824
  sandboxManager = mgr;
@@ -1534,7 +1833,9 @@ async function initSandbox(agentType) {
1534
1833
  async function wrapWithSandbox(command, filesystemOverride) {
1535
1834
  if (!sandboxInitialized || !sandboxManager) return null;
1536
1835
  if (filesystemOverride) {
1836
+ const currentConfig = sandboxManager.getConfig();
1537
1837
  sandboxManager.updateConfig({
1838
+ network: currentConfig?.network ?? { allowedDomains: [], deniedDomains: [], allowLocalBinding: true },
1538
1839
  filesystem: filesystemOverride
1539
1840
  });
1540
1841
  }
@@ -1548,6 +1849,7 @@ async function wrapWithSandbox(command, filesystemOverride) {
1548
1849
  async function resetSandbox() {
1549
1850
  if (sandboxManager) {
1550
1851
  try {
1852
+ sandboxManager.cleanupAfterCommand?.();
1551
1853
  await sandboxManager.reset();
1552
1854
  } catch {
1553
1855
  }
@@ -2474,7 +2776,7 @@ function registerConnectCommand(program2) {
2474
2776
  log.error(`Failed to start. Check logs: ${getLogPath(slug)}`);
2475
2777
  process.exit(1);
2476
2778
  }
2477
- const { ListTUI } = await import("./list-ROLJARYB.js");
2779
+ const { ListTUI } = await import("./list-RV7HOA77.js");
2478
2780
  const tui = new ListTUI();
2479
2781
  await tui.run();
2480
2782
  return;
@@ -3331,7 +3633,6 @@ var SUPPORTED_VISIBILITIES = ["public", "private"];
3331
3633
  function normalizeAgentType(input) {
3332
3634
  if (!input) return null;
3333
3635
  const normalized = input.trim().toLowerCase();
3334
- if (normalized === "claude-code" || normalized === "claudecode") return "claude";
3335
3636
  if (SUPPORTED_AGENT_TYPES.includes(normalized)) {
3336
3637
  return normalized;
3337
3638
  }
@@ -3340,7 +3641,7 @@ function normalizeAgentType(input) {
3340
3641
  function parseAgentTypeOrExit(input) {
3341
3642
  const agentType = normalizeAgentType(input);
3342
3643
  if (agentType) return agentType;
3343
- log.error(`Invalid agent type: ${input}. Supported: ${SUPPORTED_AGENT_TYPES.join(", ")} (alias: claude-code).`);
3644
+ log.error(`Invalid agent type: ${input}. Supported: ${SUPPORTED_AGENT_TYPES.join(", ")}.`);
3344
3645
  process.exit(1);
3345
3646
  }
3346
3647
  function normalizeVisibility(input) {
@@ -3553,6 +3854,11 @@ function registerAgentsCommand(program2) {
3553
3854
 
3554
3855
  // src/commands/chat.ts
3555
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";
3556
3862
 
3557
3863
  // src/utils/sse-parser.ts
3558
3864
  function parseSseChunk(raw, carry) {
@@ -3575,9 +3881,99 @@ function parseSseChunk(raw, carry) {
3575
3881
 
3576
3882
  // src/commands/chat.ts
3577
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
+ }
3578
3911
  function sleep5(ms) {
3579
3912
  return new Promise((resolve2) => setTimeout(resolve2, ms));
3580
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
+ }
3581
3977
  async function asyncChat(opts) {
3582
3978
  const res = await fetch(`${opts.baseUrl}/api/agents/${opts.agentId}/chat`, {
3583
3979
  method: "POST",
@@ -3585,7 +3981,11 @@ async function asyncChat(opts) {
3585
3981
  Authorization: `Bearer ${opts.token}`,
3586
3982
  "Content-Type": "application/json"
3587
3983
  },
3588
- 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
+ }),
3589
3989
  signal: opts.signal
3590
3990
  });
3591
3991
  if (!res.ok) {
@@ -3653,7 +4053,10 @@ async function streamChat(opts) {
3653
4053
  Authorization: `Bearer ${opts.token}`,
3654
4054
  "Content-Type": "application/json"
3655
4055
  },
3656
- body: JSON.stringify({ message: opts.message }),
4056
+ body: JSON.stringify({
4057
+ message: opts.message,
4058
+ ...opts.fileUploadOffer ? { file_upload_offer: opts.fileUploadOffer } : {}
4059
+ }),
3657
4060
  signal: opts.signal
3658
4061
  });
3659
4062
  if (!res.ok) {
@@ -3787,8 +4190,10 @@ function registerChatCommand(program2) {
3787
4190
  process.exit(1);
3788
4191
  }
3789
4192
  log.banner(`Chat with ${agentName}`);
3790
- 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}
3791
4194
  `);
4195
+ let pendingUploadOffer;
4196
+ let pendingUploadZipBuffer;
3792
4197
  const rl = createInterface4({
3793
4198
  input: process.stdin,
3794
4199
  output: process.stdout,
@@ -3811,7 +4216,34 @@ function registerChatCommand(program2) {
3811
4216
  rl.close();
3812
4217
  return;
3813
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
+ }
3814
4242
  console.log("");
4243
+ const uploadOffer = pendingUploadOffer;
4244
+ const uploadZipBuffer = pendingUploadZipBuffer;
4245
+ pendingUploadOffer = void 0;
4246
+ pendingUploadZipBuffer = void 0;
3815
4247
  try {
3816
4248
  await streamChat({
3817
4249
  agentId,
@@ -3819,8 +4251,12 @@ function registerChatCommand(program2) {
3819
4251
  token,
3820
4252
  baseUrl: opts.baseUrl,
3821
4253
  showThinking: opts.thinking,
3822
- mode
4254
+ mode,
4255
+ fileUploadOffer: uploadOffer
3823
4256
  });
4257
+ if (uploadOffer && uploadZipBuffer) {
4258
+ await chatWebrtcUpload(agentId, uploadOffer, uploadZipBuffer, token, opts.baseUrl);
4259
+ }
3824
4260
  } catch (err) {
3825
4261
  if (abortController.signal.aborted) return;
3826
4262
  log.error(err.message);
@@ -3833,11 +4269,11 @@ function registerChatCommand(program2) {
3833
4269
 
3834
4270
  // src/commands/skills.ts
3835
4271
  import { readFile as readFile3, writeFile as writeFile3, readdir as readdir2, mkdir as mkdir2, rm, symlink, unlink } from "fs/promises";
3836
- import { join as join10, resolve, relative as relative4 } from "path";
4272
+ import { join as join11, resolve, relative as relative4 } from "path";
3837
4273
 
3838
4274
  // src/utils/skill-parser.ts
3839
4275
  import { readFile as readFile2, writeFile as writeFile2, stat as stat3 } from "fs/promises";
3840
- import { join as join9 } from "path";
4276
+ import { join as join10 } from "path";
3841
4277
  function parseSkillMd(raw) {
3842
4278
  const trimmed = raw.trimStart();
3843
4279
  if (!trimmed.startsWith("---")) {
@@ -3919,7 +4355,7 @@ function parseSkillMd(raw) {
3919
4355
  return { frontmatter, content };
3920
4356
  }
3921
4357
  async function loadSkillManifest(dir) {
3922
- const skillMdPath = join9(dir, "SKILL.md");
4358
+ const skillMdPath = join10(dir, "SKILL.md");
3923
4359
  try {
3924
4360
  const raw = await readFile2(skillMdPath, "utf-8");
3925
4361
  const { frontmatter } = parseSkillMd(raw);
@@ -4024,13 +4460,13 @@ function skillApiPath(authorLogin, slug) {
4024
4460
  }
4025
4461
  async function resolveSkillsRootAsync(pathArg) {
4026
4462
  const projectRoot = pathArg ? resolve(pathArg) : process.cwd();
4027
- const skillsDir = join10(projectRoot, ".agents", "skills");
4028
- const claudeSkillsDir = join10(projectRoot, ".claude", "skills");
4463
+ const skillsDir = join11(projectRoot, ".agents", "skills");
4464
+ const claudeSkillsDir = join11(projectRoot, ".claude", "skills");
4029
4465
  return { projectRoot, skillsDir, claudeSkillsDir };
4030
4466
  }
4031
4467
  async function ensureClaudeSymlink(claudeSkillsDir, slug) {
4032
4468
  await mkdir2(claudeSkillsDir, { recursive: true });
4033
- const linkPath = join10(claudeSkillsDir, slug);
4469
+ const linkPath = join11(claudeSkillsDir, slug);
4034
4470
  try {
4035
4471
  await unlink(linkPath);
4036
4472
  } catch {
@@ -4050,7 +4486,7 @@ async function collectPackFiles(dir, manifest) {
4050
4486
  }
4051
4487
  const mainFile = manifest.main || "SKILL.md";
4052
4488
  if (!results.includes(mainFile)) {
4053
- const mainPath = join10(dir, mainFile);
4489
+ const mainPath = join11(dir, mainFile);
4054
4490
  if (await pathExists(mainPath)) {
4055
4491
  results.unshift(mainFile);
4056
4492
  }
@@ -4067,7 +4503,7 @@ async function walkDir(dir) {
4067
4503
  }
4068
4504
  for (const entry of entries) {
4069
4505
  if (entry.isSymbolicLink()) continue;
4070
- const fullPath = join10(dir, entry.name);
4506
+ const fullPath = join11(dir, entry.name);
4071
4507
  if (entry.isDirectory()) {
4072
4508
  if (SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
4073
4509
  const sub = await walkDir(fullPath);
@@ -4085,7 +4521,7 @@ async function packSkill(dir, manifest) {
4085
4521
  }
4086
4522
  const entries = [];
4087
4523
  for (const relPath of fileList) {
4088
- const absPath = join10(dir, relPath);
4524
+ const absPath = join11(dir, relPath);
4089
4525
  try {
4090
4526
  const data = await readFile3(absPath);
4091
4527
  entries.push({ path: relPath.replace(/\\/g, "/"), data });
@@ -4119,7 +4555,7 @@ function bumpVersion(current, bump) {
4119
4555
  }
4120
4556
  async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4121
4557
  const meta = await client.get(skillApiPath(authorLogin, slug));
4122
- const targetDir = join10(skillsDir, slug);
4558
+ const targetDir = join11(skillsDir, slug);
4123
4559
  await mkdir2(targetDir, { recursive: true });
4124
4560
  if (meta.has_files) {
4125
4561
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/download`);
@@ -4127,8 +4563,8 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4127
4563
  const buf = Buffer.from(arrayBuf);
4128
4564
  const entries = extractZipBuffer(buf);
4129
4565
  for (const entry of entries) {
4130
- const filePath = join10(targetDir, entry.path);
4131
- const dir = join10(filePath, "..");
4566
+ const filePath = join11(targetDir, entry.path);
4567
+ const dir = join11(filePath, "..");
4132
4568
  await mkdir2(dir, { recursive: true });
4133
4569
  await writeFile3(filePath, entry.data);
4134
4570
  }
@@ -4141,7 +4577,7 @@ async function downloadAndInstallSkill(client, authorLogin, slug, skillsDir) {
4141
4577
  } else {
4142
4578
  const res = await client.getRaw(`${skillApiPath(authorLogin, slug)}/raw`);
4143
4579
  const content = await res.text();
4144
- await writeFile3(join10(targetDir, "SKILL.md"), content);
4580
+ await writeFile3(join11(targetDir, "SKILL.md"), content);
4145
4581
  return {
4146
4582
  slug,
4147
4583
  name: meta.name,
@@ -4170,7 +4606,7 @@ function registerSkillsCommand(program2) {
4170
4606
  try {
4171
4607
  const dir = resolveSkillDir(pathArg);
4172
4608
  await mkdir2(dir, { recursive: true });
4173
- const skillMdPath = join10(dir, "SKILL.md");
4609
+ const skillMdPath = join11(dir, "SKILL.md");
4174
4610
  if (await pathExists(skillMdPath)) {
4175
4611
  const raw = await readFile3(skillMdPath, "utf-8");
4176
4612
  const { frontmatter } = parseSkillMd(raw);
@@ -4199,7 +4635,7 @@ function registerSkillsCommand(program2) {
4199
4635
  const dir = resolveSkillDir(pathArg);
4200
4636
  const manifest = await loadSkillManifest(dir);
4201
4637
  const result = await packSkill(dir, manifest);
4202
- const outPath = join10(dir, result.filename);
4638
+ const outPath = join11(dir, result.filename);
4203
4639
  await writeFile3(outPath, result.buffer);
4204
4640
  slog.info(`Packed ${result.files.length} files \u2192 ${result.filename} (${result.size} bytes)`);
4205
4641
  outputJson({
@@ -4245,7 +4681,7 @@ function registerSkillsCommand(program2) {
4245
4681
  if (opts.name) manifest.name = opts.name;
4246
4682
  if (opts.version) manifest.version = opts.version;
4247
4683
  if (opts.private !== void 0) manifest.private = opts.private;
4248
- content = await readFile3(join10(dir, manifest.main || "SKILL.md"), "utf-8");
4684
+ content = await readFile3(join11(dir, manifest.main || "SKILL.md"), "utf-8");
4249
4685
  packResult = await packSkill(dir, manifest);
4250
4686
  slog.info(`Packed ${packResult.files.length} files (${packResult.size} bytes)`);
4251
4687
  }
@@ -4382,7 +4818,7 @@ function registerSkillsCommand(program2) {
4382
4818
  skills.command("version <bump> [path]").description("Bump skill version (patch | minor | major | x.y.z)").action(async (bump, pathArg) => {
4383
4819
  try {
4384
4820
  const dir = resolveSkillDir(pathArg);
4385
- const skillMdPath = join10(dir, "SKILL.md");
4821
+ const skillMdPath = join11(dir, "SKILL.md");
4386
4822
  if (!await pathExists(skillMdPath)) {
4387
4823
  outputError("not_found", "No SKILL.md found. Run `agent-mesh skills init` first.");
4388
4824
  }
@@ -4402,7 +4838,7 @@ function registerSkillsCommand(program2) {
4402
4838
  try {
4403
4839
  const { authorLogin, slug } = parseSkillRef(ref);
4404
4840
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4405
- const targetDir = join10(skillsDir, slug);
4841
+ const targetDir = join11(skillsDir, slug);
4406
4842
  if (await pathExists(targetDir)) {
4407
4843
  if (!opts.force) {
4408
4844
  outputError("already_installed", `Skill "${slug}" is already installed at ${targetDir}. Use --force to overwrite.`);
@@ -4441,11 +4877,11 @@ function registerSkillsCommand(program2) {
4441
4877
  const failed = [];
4442
4878
  if (ref) {
4443
4879
  const { authorLogin, slug } = parseSkillRef(ref);
4444
- const targetDir = join10(skillsDir, slug);
4880
+ const targetDir = join11(skillsDir, slug);
4445
4881
  if (!await pathExists(targetDir)) {
4446
4882
  outputError("not_installed", `Skill "${slug}" is not installed. Use "skills install ${ref}" first.`);
4447
4883
  }
4448
- const skillMdPath = join10(targetDir, "SKILL.md");
4884
+ const skillMdPath = join11(targetDir, "SKILL.md");
4449
4885
  let localVersion = "0.0.0";
4450
4886
  if (await pathExists(skillMdPath)) {
4451
4887
  const raw = await readFile3(skillMdPath, "utf-8");
@@ -4472,7 +4908,7 @@ function registerSkillsCommand(program2) {
4472
4908
  for (const entry of entries) {
4473
4909
  if (!entry.isDirectory()) continue;
4474
4910
  const slug = entry.name;
4475
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4911
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4476
4912
  if (!await pathExists(skillMdPath)) {
4477
4913
  skipped.push({ slug, reason: "no_skill_md" });
4478
4914
  continue;
@@ -4492,7 +4928,7 @@ function registerSkillsCommand(program2) {
4492
4928
  skipped.push({ slug, reason: "up_to_date" });
4493
4929
  } else {
4494
4930
  slog.info(`Updating ${slug}: v${localVersion} \u2192 v${remoteVersion}...`);
4495
- await rm(join10(skillsDir, slug), { recursive: true, force: true });
4931
+ await rm(join11(skillsDir, slug), { recursive: true, force: true });
4496
4932
  await downloadAndInstallSkill(client, authorLogin, slug, skillsDir);
4497
4933
  updated.push({ slug, name: remote.name, old_version: localVersion, new_version: remoteVersion });
4498
4934
  }
@@ -4513,13 +4949,13 @@ function registerSkillsCommand(program2) {
4513
4949
  skills.command("remove <slug> [path]").description("Remove a locally installed skill").action(async (slug, pathArg) => {
4514
4950
  try {
4515
4951
  const { skillsDir, claudeSkillsDir } = await resolveSkillsRootAsync(pathArg);
4516
- const targetDir = join10(skillsDir, slug);
4952
+ const targetDir = join11(skillsDir, slug);
4517
4953
  if (!await pathExists(targetDir)) {
4518
4954
  outputError("not_installed", `Skill "${slug}" is not installed at ${targetDir}`);
4519
4955
  }
4520
4956
  await rm(targetDir, { recursive: true, force: true });
4521
4957
  try {
4522
- await unlink(join10(claudeSkillsDir, slug));
4958
+ await unlink(join11(claudeSkillsDir, slug));
4523
4959
  } catch {
4524
4960
  }
4525
4961
  slog.success(`Removed skill: ${slug}`);
@@ -4544,7 +4980,7 @@ function registerSkillsCommand(program2) {
4544
4980
  for (const entry of entries) {
4545
4981
  if (!entry.isDirectory()) continue;
4546
4982
  const slug = entry.name;
4547
- const skillMdPath = join10(skillsDir, slug, "SKILL.md");
4983
+ const skillMdPath = join11(skillsDir, slug, "SKILL.md");
4548
4984
  if (!await pathExists(skillMdPath)) continue;
4549
4985
  const raw = await readFile3(skillMdPath, "utf-8");
4550
4986
  const { frontmatter } = parseSkillMd(raw);
@@ -4656,9 +5092,9 @@ function registerDiscoverCommand(program2) {
4656
5092
  }
4657
5093
 
4658
5094
  // src/commands/call.ts
4659
- import { readFileSync as readFileSync2, writeFileSync as writeFileSync3, mkdirSync as mkdirSync5 } from "fs";
4660
- import { execSync as execSync4 } from "child_process";
4661
- 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";
4662
5098
  var DEFAULT_BASE_URL4 = "https://agents.hot";
4663
5099
  async function submitRating(baseUrl, token, agentId, callId, rating) {
4664
5100
  const res = await fetch(`${baseUrl}/api/agents/${agentId}/rate`, {
@@ -4752,12 +5188,12 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4752
5188
  poll()
4753
5189
  ]);
4754
5190
  mkdirSync5(outputDir, { recursive: true });
4755
- const zipPath = join11(outputDir, ".transfer.zip");
5191
+ const zipPath = join12(outputDir, ".transfer.zip");
4756
5192
  writeFileSync3(zipPath, zipBuffer);
4757
5193
  try {
4758
- execSync4(`unzip -o -q "${zipPath}" -d "${outputDir}"`);
5194
+ execSync5(`unzip -o -q "${zipPath}" -d "${outputDir}"`);
4759
5195
  try {
4760
- execSync4(`rm "${zipPath}"`);
5196
+ execSync5(`rm "${zipPath}"`);
4761
5197
  } catch {
4762
5198
  }
4763
5199
  } catch {
@@ -4786,6 +5222,120 @@ async function webrtcDownload(agentId, offer, token, outputDir, json) {
4786
5222
  receiver.close();
4787
5223
  }
4788
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
+ }
4789
5339
  async function asyncCall(opts) {
4790
5340
  const selfAgentId = process.env.AGENT_BRIDGE_AGENT_ID;
4791
5341
  const res = await fetch(`${DEFAULT_BASE_URL4}/api/agents/${opts.id}/call`, {
@@ -4798,7 +5348,8 @@ async function asyncCall(opts) {
4798
5348
  body: JSON.stringify({
4799
5349
  task_description: opts.taskDescription,
4800
5350
  mode: "async",
4801
- ...opts.withFiles ? { with_files: true } : {}
5351
+ ...opts.withFiles ? { with_files: true } : {},
5352
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4802
5353
  }),
4803
5354
  signal: opts.signal
4804
5355
  });
@@ -4878,7 +5429,7 @@ async function asyncCall(opts) {
4878
5429
  if (!opts.json) log.info(`Saved to ${opts.outputFile}`);
4879
5430
  }
4880
5431
  if (offer && opts.withFiles) {
4881
- 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");
4882
5433
  await webrtcDownload(opts.id, offer, opts.token, outputDir, opts.json);
4883
5434
  }
4884
5435
  if (!opts.json) {
@@ -4917,7 +5468,8 @@ async function streamCall(opts) {
4917
5468
  },
4918
5469
  body: JSON.stringify({
4919
5470
  task_description: opts.taskDescription,
4920
- ...opts.withFiles ? { with_files: true } : {}
5471
+ ...opts.withFiles ? { with_files: true } : {},
5472
+ ...opts.uploadOffer ? { file_upload_offer: opts.uploadOffer } : {}
4921
5473
  }),
4922
5474
  signal: opts.signal
4923
5475
  });
@@ -5052,7 +5604,7 @@ Error: ${event.message}
5052
5604
  }
5053
5605
  if (fileOffer && opts.withFiles) {
5054
5606
  console.log("");
5055
- 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");
5056
5608
  await webrtcDownload(opts.id, fileOffer, opts.token, outputDir, opts.json);
5057
5609
  }
5058
5610
  if (!opts.json) {
@@ -5068,7 +5620,7 @@ Error: ${event.message}
5068
5620
  return { callId, ...sessionKey ? { sessionKey } : {} };
5069
5621
  }
5070
5622
  function registerCallCommand(program2) {
5071
- 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) => {
5072
5624
  try {
5073
5625
  const token = loadToken();
5074
5626
  if (!token) {
@@ -5079,13 +5631,25 @@ function registerCallCommand(program2) {
5079
5631
  const { id, name } = await resolveAgentId(agentInput, client);
5080
5632
  let taskDescription = opts.task;
5081
5633
  if (opts.inputFile) {
5082
- const content = readFileSync2(opts.inputFile, "utf-8");
5634
+ const content = readFileSync3(opts.inputFile, "utf-8");
5083
5635
  taskDescription = `${taskDescription}
5084
5636
 
5085
5637
  ---
5086
5638
 
5087
5639
  ${content}`;
5088
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
+ }
5089
5653
  const timeoutMs = parseInt(opts.timeout || "300", 10) * 1e3;
5090
5654
  const abortController = new AbortController();
5091
5655
  const timer = setTimeout(() => abortController.abort(), timeoutMs);
@@ -5098,13 +5662,20 @@ ${content}`;
5098
5662
  json: opts.json,
5099
5663
  outputFile: opts.outputFile,
5100
5664
  signal: abortController.signal,
5101
- withFiles: opts.withFiles
5665
+ withFiles: opts.withFiles,
5666
+ uploadOffer
5102
5667
  };
5103
5668
  let result;
5104
5669
  if (opts.stream) {
5670
+ if (uploadOffer && uploadZipBuffer) {
5671
+ void webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5672
+ }
5105
5673
  result = await streamCall(callOpts);
5106
5674
  } else {
5107
5675
  result = await asyncCall(callOpts);
5676
+ if (uploadOffer && uploadZipBuffer) {
5677
+ await webrtcUpload(id, uploadOffer, uploadZipBuffer, token, opts.json);
5678
+ }
5108
5679
  }
5109
5680
  clearTimeout(timer);
5110
5681
  if (opts.rate && result.callId) {
@@ -5356,9 +5927,9 @@ function registerSubscribeCommand(program2) {
5356
5927
 
5357
5928
  // src/commands/register.ts
5358
5929
  var DEFAULT_BASE_URL5 = "https://agents.hot";
5359
- var VALID_AGENT_TYPES = ["claude", "claude-code", "cursor", "windsurf", "custom"];
5930
+ var VALID_AGENT_TYPES = ["claude"];
5360
5931
  function registerRegisterCommand(program2) {
5361
- program2.command("register").description("Register a new agent on the platform and get an API key").requiredOption("--name <name>", "Agent name (alphanumeric + hyphens, 3-64 chars)").option("--type <type>", "Agent type", "claude-code").option("--description <text>", "Agent description").option("--capabilities <list>", "Comma-separated capabilities").option("--base-url <url>", "Platform base URL", DEFAULT_BASE_URL5).action(async (opts) => {
5932
+ program2.command("register").description("Register a new agent on the platform and get an API key").requiredOption("--name <name>", "Agent name (alphanumeric + hyphens, 3-64 chars)").option("--type <type>", "Agent type", "claude").option("--description <text>", "Agent description").option("--capabilities <list>", "Comma-separated capabilities").option("--base-url <url>", "Platform base URL", DEFAULT_BASE_URL5).action(async (opts) => {
5362
5933
  if (!VALID_AGENT_TYPES.includes(opts.type)) {
5363
5934
  log.error(`Invalid agent type: ${opts.type}. Must be one of: ${VALID_AGENT_TYPES.join(", ")}`);
5364
5935
  process.exit(1);
@@ -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.8",
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",