@annals/agent-mesh 0.16.11 → 0.16.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ The ticket is one-time use and expires in 15 minutes.
24
24
 
25
25
  | Agent | Status | How it connects |
26
26
  |-------|--------|-----------------|
27
- | [OpenClaw](https://github.com/nicepkg/openclaw) | Available | WebSocket to local gateway (Protocol v3) |
27
+ | [Claude Code](https://github.com/nicepkg/claude) | Available | WebSocket to local gateway (Protocol v3) |
28
28
  | [Claude Code](https://docs.anthropic.com/en/docs/claude-code) | Available | stdio (stream-json format) |
29
29
  | Codex CLI | Planned | — |
30
30
  | Gemini CLI | Planned | — |
@@ -36,8 +36,8 @@ agent-mesh connect [type] # Connect agent to platform
36
36
  --setup <url> # One-click setup from ticket URL
37
37
  --agent-id <id> # Agent UUID
38
38
  --project <path> # Project path (Claude adapter)
39
- --gateway-url <url> # OpenClaw gateway URL
40
- --gateway-token <token> # OpenClaw gateway token
39
+ --gateway-url <url> # Claude Code gateway URL
40
+ --gateway-token <token> # Claude Code gateway token
41
41
  --sandbox # Run inside sandbox (macOS, requires srt)
42
42
 
43
43
  agent-mesh login # Authenticate
@@ -49,7 +49,7 @@ agent-mesh status # Check connection
49
49
  ```
50
50
  Your machine Cloud Users
51
51
  ┌──────────────────┐ outbound ┌─────────────────────┐ ┌──────────┐
52
- OpenClaw │ WebSocket │ │ │ │
52
+ Claude Code │ WebSocket │ │ │ │
53
53
  │ Claude Code ├──────────────► │ bridge.agents.hot │ ◄── │ Platform │
54
54
  │ Codex (planned) │ (no inbound │ (Cloudflare Worker)│ │ IM bots │
55
55
  │ Gemini (planned) │ ports) │ │ │ API │
@@ -83,7 +83,7 @@ For Claude Code agents, any files the agent creates or modifies are automaticall
83
83
 
84
84
  ## 中文说明
85
85
 
86
- Agent Mesh CLI 把你本地的 AI Agent(OpenClaw、Claude Code 等)接入 [agents.hot](https://agents.hot) 平台。用户在网页聊天,你赚钱。Agent 始终运行在你自己的机器上,无需开放端口。
86
+ Agent Mesh CLI 把你本地的 AI Agent(Claude Code、Claude Code 等)接入 [agents.hot](https://agents.hot) 平台。用户在网页聊天,你赚钱。Agent 始终运行在你自己的机器上,无需开放端口。
87
87
 
88
88
  每个用户自动获得独立的工作目录(workspace 隔离),Claude Code 的输出文件会自动上传回平台。
89
89
 
@@ -40,6 +40,16 @@ function updateConfig(partial) {
40
40
  const existing = loadConfig();
41
41
  saveConfig({ ...existing, ...partial });
42
42
  }
43
+ function maybePrintDocsHint(docsUrl) {
44
+ const config = loadConfig();
45
+ if (config.docsHintShownAt) return;
46
+ process.stderr.write(`
47
+ [agent-mesh] Docs: ${docsUrl}
48
+
49
+ `);
50
+ config.docsHintShownAt = (/* @__PURE__ */ new Date()).toISOString();
51
+ saveConfig(config);
52
+ }
43
53
  function parsePositiveInt(raw) {
44
54
  if (typeof raw !== "number" || !Number.isFinite(raw)) return void 0;
45
55
  const n = Math.floor(raw);
@@ -944,6 +954,7 @@ export {
944
954
  DEFAULT_RUNTIME_CONFIG,
945
955
  loadConfig,
946
956
  updateConfig,
957
+ maybePrintDocsHint,
947
958
  resolveRuntimeConfig,
948
959
  getRuntimeConfig,
949
960
  updateRuntimeConfig,
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  isProcessAlive,
17
17
  listAgents,
18
18
  loadConfig,
19
+ maybePrintDocsHint,
19
20
  readPid,
20
21
  registerListCommand,
21
22
  removeAgent,
@@ -29,7 +30,7 @@ import {
29
30
  updateConfig,
30
31
  updateRuntimeConfig,
31
32
  writePid
32
- } from "./chunk-U32JDKSN.js";
33
+ } from "./chunk-KEUGYA3L.js";
33
34
 
34
35
  // src/index.ts
35
36
  import { createRequire } from "module";
@@ -810,7 +811,7 @@ var BridgeManager = class {
810
811
  }
811
812
  async dispatchWithLocalQueue(opts) {
812
813
  const { msg, handle, requestKey } = opts;
813
- const { session_id, request_id, content, attachments, upload_url, upload_token, client_id } = msg;
814
+ const { session_id, request_id, content, attachments, upload_url, upload_token, client_id, platform_task } = msg;
814
815
  const state = this.requestDispatches.get(requestKey);
815
816
  if (!state) return;
816
817
  try {
@@ -831,7 +832,7 @@ var BridgeManager = class {
831
832
  }
832
833
  const uploadCredentials = upload_url && upload_token ? { uploadUrl: upload_url, uploadToken: upload_token } : void 0;
833
834
  try {
834
- handle.send(content, attachments, uploadCredentials, client_id);
835
+ handle.send(content, attachments, uploadCredentials, client_id, platform_task);
835
836
  this.sessionLastSeenAt.set(session_id, Date.now());
836
837
  } catch (err) {
837
838
  log.error(`Failed to send to adapter: ${err}`);
@@ -889,13 +890,16 @@ var BridgeManager = class {
889
890
  this.wsClient.send(chunk);
890
891
  this.sessionLastSeenAt.set(sessionId, Date.now());
891
892
  });
892
- handle.onDone((attachments) => {
893
+ handle.onDone((payload) => {
893
894
  void this.releaseRequestLease(sessionId, requestRef.requestId, "done");
895
+ const attachments = payload?.attachments;
896
+ const fileManifest = payload?.fileManifest;
894
897
  const done = {
895
898
  type: "done",
896
899
  session_id: sessionId,
897
900
  request_id: requestRef.requestId,
898
901
  ...attachments && attachments.length > 0 && { attachments },
902
+ ...fileManifest && fileManifest.length > 0 && { file_manifest: fileManifest },
899
903
  ...fullResponseBuffer && { result: fullResponseBuffer }
900
904
  };
901
905
  this.trackRequest(sessionId, requestRef.requestId, "done");
@@ -1125,7 +1129,6 @@ var SENSITIVE_PATHS = [
1125
1129
  "~/.claude/ide",
1126
1130
  // IDE integration data
1127
1131
  // Other AI agent configs (contain API keys / tokens)
1128
- "~/.openclaw",
1129
1132
  // ~/.agent-mesh — fine-grained: block tokens/config, allow agent workspaces
1130
1133
  // NOT blocked: ~/.agent-mesh/agents/ (per-agent project workspaces used as cwd)
1131
1134
  "~/.agent-mesh/config.json",
@@ -1150,11 +1153,6 @@ var SANDBOX_PRESETS = {
1150
1153
  denyRead: [...SENSITIVE_PATHS],
1151
1154
  allowWrite: [".", "/tmp"],
1152
1155
  denyWrite: [".env", ".env.*"]
1153
- },
1154
- openclaw: {
1155
- denyRead: [...SENSITIVE_PATHS],
1156
- allowWrite: ["/tmp"],
1157
- denyWrite: [".env", ".env.*"]
1158
1156
  }
1159
1157
  };
1160
1158
  var sandboxManager = null;
@@ -1425,13 +1423,12 @@ function createClientWorkspace(projectPath, clientId) {
1425
1423
  }
1426
1424
 
1427
1425
  // src/adapters/claude.ts
1428
- import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
1426
+ import { readFile as readFile2, writeFile, mkdir, stat as stat2 } from "fs/promises";
1429
1427
  import { join as join5, relative as relative3, basename } from "path";
1430
1428
 
1431
1429
  // src/utils/auto-upload.ts
1432
1430
  import { readdir, readFile, stat } from "fs/promises";
1433
1431
  import { join as join4, relative as relative2 } from "path";
1434
- var MAX_AUTO_UPLOAD_FILES = 50;
1435
1432
  var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
1436
1433
  var SKIP_DIRS = /* @__PURE__ */ new Set([
1437
1434
  ".git",
@@ -1485,76 +1482,152 @@ async function collectRealFiles(dir, maxFiles = Infinity) {
1485
1482
  await walk(dir);
1486
1483
  return files;
1487
1484
  }
1488
- async function snapshotWorkspace(workspacePath) {
1489
- const snapshot = /* @__PURE__ */ new Map();
1490
- try {
1491
- const files = await collectRealFiles(workspacePath);
1492
- for (const filePath of files) {
1493
- try {
1494
- const s = await stat(filePath);
1495
- snapshot.set(filePath, { mtimeMs: s.mtimeMs, size: s.size });
1496
- } catch {
1497
- }
1485
+
1486
+ // src/utils/zip.ts
1487
+ import { deflateRawSync, inflateRawSync } from "zlib";
1488
+ function dosTime(date) {
1489
+ return {
1490
+ time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
1491
+ date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
1492
+ };
1493
+ }
1494
+ function crc32(buf) {
1495
+ let crc = 4294967295;
1496
+ for (let i = 0; i < buf.length; i++) {
1497
+ crc ^= buf[i];
1498
+ for (let j = 0; j < 8; j++) {
1499
+ crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
1498
1500
  }
1499
- log.debug(`Workspace snapshot: ${snapshot.size} files`);
1500
- } catch (err) {
1501
- log.debug(`Workspace snapshot failed: ${err}`);
1502
1501
  }
1503
- return snapshot;
1502
+ return (crc ^ 4294967295) >>> 0;
1504
1503
  }
1505
- async function diffAndUpload(params) {
1506
- const { workspace, snapshot, uploadUrl, uploadToken } = params;
1507
- const currentFiles = await collectRealFiles(workspace);
1508
- const newOrModified = [];
1509
- for (const filePath of currentFiles) {
1510
- try {
1511
- const s = await stat(filePath);
1512
- const prev = snapshot.get(filePath);
1513
- if (!prev || s.mtimeMs !== prev.mtimeMs || s.size !== prev.size) {
1514
- newOrModified.push(filePath);
1515
- }
1516
- } catch {
1504
+ function writeUint16LE(buf, val, offset) {
1505
+ buf[offset] = val & 255;
1506
+ buf[offset + 1] = val >>> 8 & 255;
1507
+ }
1508
+ function writeUint32LE(buf, val, offset) {
1509
+ buf[offset] = val & 255;
1510
+ buf[offset + 1] = val >>> 8 & 255;
1511
+ buf[offset + 2] = val >>> 16 & 255;
1512
+ buf[offset + 3] = val >>> 24 & 255;
1513
+ }
1514
+ function createZipBuffer(entries) {
1515
+ const now = /* @__PURE__ */ new Date();
1516
+ const { time, date } = dosTime(now);
1517
+ const records = [];
1518
+ const chunks = [];
1519
+ let offset = 0;
1520
+ for (const entry of entries) {
1521
+ const nameBytes = Buffer.from(entry.path, "utf-8");
1522
+ const crc = crc32(entry.data);
1523
+ const compressed = deflateRawSync(entry.data, { level: 6 });
1524
+ const compressedSize = compressed.length;
1525
+ const uncompressedSize = entry.data.length;
1526
+ const header = Buffer.alloc(30 + nameBytes.length);
1527
+ writeUint32LE(header, 67324752, 0);
1528
+ writeUint16LE(header, 20, 4);
1529
+ writeUint16LE(header, 0, 6);
1530
+ writeUint16LE(header, 8, 8);
1531
+ writeUint16LE(header, time, 10);
1532
+ writeUint16LE(header, date, 12);
1533
+ writeUint32LE(header, crc, 14);
1534
+ writeUint32LE(header, compressedSize, 18);
1535
+ writeUint32LE(header, uncompressedSize, 22);
1536
+ writeUint16LE(header, nameBytes.length, 26);
1537
+ writeUint16LE(header, 0, 28);
1538
+ nameBytes.copy(header, 30);
1539
+ records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
1540
+ chunks.push(header, compressed);
1541
+ offset += header.length + compressed.length;
1542
+ }
1543
+ const centralDirStart = offset;
1544
+ for (let i = 0; i < entries.length; i++) {
1545
+ const entry = entries[i];
1546
+ const rec = records[i];
1547
+ const nameBytes = Buffer.from(entry.path, "utf-8");
1548
+ const cdh = Buffer.alloc(46 + nameBytes.length);
1549
+ writeUint32LE(cdh, 33639248, 0);
1550
+ writeUint16LE(cdh, 20, 4);
1551
+ writeUint16LE(cdh, 20, 6);
1552
+ writeUint16LE(cdh, 0, 8);
1553
+ writeUint16LE(cdh, 8, 10);
1554
+ writeUint16LE(cdh, time, 12);
1555
+ writeUint16LE(cdh, date, 14);
1556
+ writeUint32LE(cdh, rec.crc, 16);
1557
+ writeUint32LE(cdh, rec.compressedSize, 20);
1558
+ writeUint32LE(cdh, rec.uncompressedSize, 24);
1559
+ writeUint16LE(cdh, nameBytes.length, 28);
1560
+ writeUint16LE(cdh, 0, 30);
1561
+ writeUint16LE(cdh, 0, 32);
1562
+ writeUint16LE(cdh, 0, 34);
1563
+ writeUint16LE(cdh, 0, 36);
1564
+ writeUint32LE(cdh, 0, 38);
1565
+ writeUint32LE(cdh, rec.offset, 42);
1566
+ nameBytes.copy(cdh, 46);
1567
+ chunks.push(cdh);
1568
+ offset += cdh.length;
1569
+ }
1570
+ const centralDirSize = offset - centralDirStart;
1571
+ const eocd = Buffer.alloc(22);
1572
+ writeUint32LE(eocd, 101010256, 0);
1573
+ writeUint16LE(eocd, 0, 4);
1574
+ writeUint16LE(eocd, 0, 6);
1575
+ writeUint16LE(eocd, entries.length, 8);
1576
+ writeUint16LE(eocd, entries.length, 10);
1577
+ writeUint32LE(eocd, centralDirSize, 12);
1578
+ writeUint32LE(eocd, centralDirStart, 16);
1579
+ writeUint16LE(eocd, 0, 20);
1580
+ chunks.push(eocd);
1581
+ return Buffer.concat(chunks);
1582
+ }
1583
+ function extractZipBuffer(buf) {
1584
+ const entries = [];
1585
+ let eocdOffset = -1;
1586
+ for (let i = buf.length - 22; i >= 0; i--) {
1587
+ if (buf.readUInt32LE(i) === 101010256) {
1588
+ eocdOffset = i;
1589
+ break;
1517
1590
  }
1518
1591
  }
1519
- if (newOrModified.length === 0) return [];
1520
- log.debug(`Workspace diff: ${newOrModified.length} new/modified file(s)`);
1521
- const attachments = [];
1522
- const filesToUpload = newOrModified.slice(0, MAX_AUTO_UPLOAD_FILES);
1523
- for (const absPath of filesToUpload) {
1524
- try {
1525
- const buffer = await readFile(absPath);
1526
- if (buffer.length === 0 || buffer.length > MAX_AUTO_UPLOAD_FILE_SIZE) continue;
1527
- const relPath = relative2(workspace, absPath).replace(/\\/g, "/");
1528
- const filename = relPath && !relPath.startsWith("..") ? relPath : absPath.split("/").pop() || "file";
1529
- const response = await fetch(uploadUrl, {
1530
- method: "POST",
1531
- headers: {
1532
- "X-Upload-Token": uploadToken,
1533
- "Content-Type": "application/json"
1534
- },
1535
- body: JSON.stringify({
1536
- filename,
1537
- content: buffer.toString("base64")
1538
- })
1539
- });
1540
- if (!response.ok) {
1541
- log.warn(`Auto-upload failed (${response.status}) for ${filename}`);
1542
- continue;
1592
+ if (eocdOffset === -1) {
1593
+ throw new Error("Invalid ZIP: EOCD not found");
1594
+ }
1595
+ const entryCount = buf.readUInt16LE(eocdOffset + 10);
1596
+ const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
1597
+ let offset = centralDirOffset;
1598
+ for (let i = 0; i < entryCount; i++) {
1599
+ if (buf.readUInt32LE(offset) !== 33639248) {
1600
+ throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
1601
+ }
1602
+ const compressionMethod = buf.readUInt16LE(offset + 10);
1603
+ const compressedSize = buf.readUInt32LE(offset + 20);
1604
+ const uncompressedSize = buf.readUInt32LE(offset + 24);
1605
+ const nameLen = buf.readUInt16LE(offset + 28);
1606
+ const extraLen = buf.readUInt16LE(offset + 30);
1607
+ const commentLen = buf.readUInt16LE(offset + 32);
1608
+ const localHeaderOffset = buf.readUInt32LE(offset + 42);
1609
+ const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
1610
+ if (!name.endsWith("/")) {
1611
+ const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
1612
+ const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
1613
+ const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
1614
+ const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
1615
+ let data;
1616
+ if (compressionMethod === 0) {
1617
+ data = Buffer.from(compressedData);
1618
+ } else if (compressionMethod === 8) {
1619
+ data = inflateRawSync(compressedData);
1620
+ } else {
1621
+ throw new Error(`Unsupported compression method: ${compressionMethod}`);
1543
1622
  }
1544
- const payload = await response.json();
1545
- if (typeof payload.url === "string" && payload.url.length > 0) {
1546
- const ext = filename.split(".").pop()?.toLowerCase() || "";
1547
- attachments.push({
1548
- name: filename,
1549
- url: payload.url,
1550
- type: MIME_MAP[ext] || "application/octet-stream"
1551
- });
1623
+ if (data.length !== uncompressedSize) {
1624
+ throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
1552
1625
  }
1553
- } catch (err) {
1554
- log.warn(`Auto-upload error for ${absPath}: ${err}`);
1626
+ entries.push({ path: name, data });
1555
1627
  }
1628
+ offset += 46 + nameLen + extraLen + commentLen;
1556
1629
  }
1557
- return attachments;
1630
+ return entries;
1558
1631
  }
1559
1632
 
1560
1633
  // src/adapters/claude.ts
@@ -1568,9 +1641,9 @@ var CLAUDE_RUNTIME_ALLOW_WRITE_PATHS = [
1568
1641
  `${HOME_DIR}/.claude.json.tmp`,
1569
1642
  `${HOME_DIR}/.local/state/claude`
1570
1643
  ];
1571
- var COLLECT_TASK_MARKER = "Collect files task (platform-issued):";
1572
- var MAX_UPLOAD_FILE_SIZE = 20 * 1024 * 1024;
1573
- var MAX_COLLECT_FILES = 1500;
1644
+ var MAX_UPLOAD_FILE_SIZE = 200 * 1024 * 1024;
1645
+ var MAX_COLLECT_FILES = 5e3;
1646
+ var DEFAULT_ZIP_MAX_BYTES = 200 * 1024 * 1024;
1574
1647
  function resolveIdleTimeoutMs() {
1575
1648
  const raw = process.env.AGENT_BRIDGE_CLAUDE_IDLE_TIMEOUT_MS;
1576
1649
  if (!raw) return DEFAULT_IDLE_TIMEOUT;
@@ -1606,9 +1679,7 @@ var ClaudeSession = class {
1606
1679
  uploadCredentials = null;
1607
1680
  /** Per-client workspace path (symlink-based), set on each send() */
1608
1681
  currentWorkspace;
1609
- /** Pre-message workspace file snapshot for diffing */
1610
- preMessageSnapshot = /* @__PURE__ */ new Map();
1611
- send(message, attachments, uploadCredentials, clientId) {
1682
+ send(message, attachments, uploadCredentials, clientId, platformTask) {
1612
1683
  this.resetIdleTimer();
1613
1684
  this.doneFired = false;
1614
1685
  this.chunksEmitted = false;
@@ -1623,13 +1694,12 @@ var ClaudeSession = class {
1623
1694
  } else {
1624
1695
  this.currentWorkspace = void 0;
1625
1696
  }
1626
- const collectTask = this.parseCollectWorkspaceTask(message);
1627
- if (collectTask) {
1628
- void this.runCollectWorkspaceTask(collectTask);
1697
+ if (platformTask) {
1698
+ void this.runPlatformTask(platformTask);
1629
1699
  return;
1630
1700
  }
1631
1701
  const args = ["-p", message, "--continue", "--output-format", "stream-json", "--verbose", "--include-partial-messages", "--dangerously-skip-permissions"];
1632
- void this.downloadAttachments(attachments).then(() => this.takeSnapshot()).then(() => {
1702
+ void this.downloadAttachments(attachments).then(() => {
1633
1703
  this.launchProcess(args);
1634
1704
  });
1635
1705
  }
@@ -1713,80 +1783,151 @@ var ClaudeSession = class {
1713
1783
  }
1714
1784
  });
1715
1785
  }
1716
- parseCollectWorkspaceTask(message) {
1717
- if (!message.includes("[PLATFORM TASK]") || !message.includes("[END PLATFORM TASK]")) {
1718
- return null;
1719
- }
1720
- if (!message.includes(COLLECT_TASK_MARKER)) {
1721
- return null;
1722
- }
1723
- const urlMatch = message.match(/UPLOAD_URL=(\S+)/);
1724
- const tokenMatch = message.match(/UPLOAD_TOKEN=(\S+)/);
1725
- if (!urlMatch || !tokenMatch) {
1726
- return null;
1727
- }
1728
- return {
1729
- uploadUrl: urlMatch[1].trim(),
1730
- uploadToken: tokenMatch[1].trim()
1731
- };
1786
+ getWorkspaceRoot() {
1787
+ return this.currentWorkspace || this.config.project || process.cwd();
1788
+ }
1789
+ normalizeRelativePath(inputPath) {
1790
+ const normalized = inputPath.replace(/\\/g, "/").replace(/^\/+/, "");
1791
+ if (!normalized || normalized.includes("\0")) return null;
1792
+ if (normalized.split("/").some((seg) => seg === "..")) return null;
1793
+ return normalized;
1732
1794
  }
1733
- async runCollectWorkspaceTask(task) {
1734
- const workspaceRoot = this.currentWorkspace || this.config.project || process.cwd();
1795
+ async runPlatformTask(task) {
1735
1796
  try {
1736
- const files = await this.collectWorkspaceFiles(workspaceRoot);
1737
- if (files.length === 0) {
1738
- this.emitChunk("NO_FILES_FOUND");
1739
- this.doneFired = true;
1740
- for (const cb of this.doneCallbacks) cb();
1741
- return;
1797
+ if (!this.uploadCredentials) {
1798
+ throw new Error("Missing upload credentials for platform task");
1742
1799
  }
1743
- const uploadedUrls = [];
1744
- for (const absPath of files) {
1745
- this.resetIdleTimer();
1746
- try {
1747
- const buffer = await readFile2(absPath);
1748
- if (buffer.length === 0 || buffer.length > MAX_UPLOAD_FILE_SIZE) {
1749
- continue;
1750
- }
1751
- const relPath = relative3(workspaceRoot, absPath).replace(/\\/g, "/");
1752
- const filename = relPath && !relPath.startsWith("..") ? relPath : absPath.split("/").pop() || "file";
1753
- const response = await fetch(task.uploadUrl, {
1754
- method: "POST",
1755
- headers: {
1756
- "X-Upload-Token": task.uploadToken,
1757
- "Content-Type": "application/json"
1758
- },
1759
- body: JSON.stringify({
1760
- filename,
1761
- content: buffer.toString("base64")
1762
- })
1763
- });
1764
- if (!response.ok) {
1765
- log.warn(`collect-files upload failed (${response.status}) for ${filename}`);
1766
- continue;
1767
- }
1768
- const payload = await response.json();
1769
- if (typeof payload.url === "string" && payload.url.length > 0) {
1770
- uploadedUrls.push(payload.url);
1771
- }
1772
- } catch (error) {
1773
- log.warn(`collect-files upload error for ${absPath}: ${error}`);
1774
- }
1775
- }
1776
- if (uploadedUrls.length === 0) {
1777
- this.emitChunk("COLLECT_FILES_FAILED");
1800
+ if (task.type === "upload_file") {
1801
+ await this.runUploadFileTask(task.path);
1802
+ } else if (task.type === "upload_all_zip") {
1803
+ await this.runUploadAllZipTask(task.zip_name, task.max_bytes);
1778
1804
  } else {
1779
- this.emitChunk(uploadedUrls.join("\n"));
1805
+ throw new Error(`Unsupported platform task: ${task.type || "unknown"}`);
1780
1806
  }
1781
1807
  this.doneFired = true;
1782
- for (const cb of this.doneCallbacks) cb();
1783
1808
  } catch (error) {
1784
- this.emitError(new Error(`Collect files task failed: ${error instanceof Error ? error.message : String(error)}`));
1809
+ this.emitError(new Error(`Platform task failed: ${error instanceof Error ? error.message : String(error)}`));
1810
+ }
1811
+ }
1812
+ async runUploadFileTask(path) {
1813
+ const workspaceRoot = this.getWorkspaceRoot();
1814
+ const relPath = this.normalizeRelativePath(path);
1815
+ if (!relPath) {
1816
+ throw new Error("Invalid file path");
1817
+ }
1818
+ const absPath = join5(workspaceRoot, relPath);
1819
+ const info = await stat2(absPath);
1820
+ if (!info.isFile()) {
1821
+ throw new Error("Path is not a regular file");
1822
+ }
1823
+ const buffer = await readFile2(absPath);
1824
+ if (buffer.length === 0 || buffer.length > MAX_UPLOAD_FILE_SIZE) {
1825
+ throw new Error(`File size out of bounds: ${buffer.length}`);
1826
+ }
1827
+ const uploaded = await this.uploadBuffer(relPath, buffer);
1828
+ for (const cb of this.doneCallbacks) cb({ attachments: [uploaded] });
1829
+ }
1830
+ async runUploadAllZipTask(zipName, maxBytes) {
1831
+ const workspaceRoot = this.getWorkspaceRoot();
1832
+ const files = await this.collectWorkspaceFiles(workspaceRoot);
1833
+ if (files.length === 0) {
1834
+ throw new Error("No files found");
1785
1835
  }
1836
+ const maxZipBytes = typeof maxBytes === "number" && Number.isFinite(maxBytes) && maxBytes > 0 ? Math.floor(maxBytes) : DEFAULT_ZIP_MAX_BYTES;
1837
+ const entries = [];
1838
+ let totalBytes = 0;
1839
+ for (const absPath of files) {
1840
+ this.resetIdleTimer();
1841
+ const relPath = relative3(workspaceRoot, absPath).replace(/\\/g, "/");
1842
+ if (!relPath || relPath.startsWith("..")) continue;
1843
+ const buffer = await readFile2(absPath);
1844
+ if (buffer.length === 0) continue;
1845
+ totalBytes += buffer.length;
1846
+ if (totalBytes > maxZipBytes) {
1847
+ throw new Error(`ZIP_TOO_LARGE:${totalBytes}`);
1848
+ }
1849
+ entries.push({ path: relPath, data: buffer });
1850
+ }
1851
+ const zipBuffer = createZipBuffer(entries);
1852
+ if (zipBuffer.length > maxZipBytes) {
1853
+ throw new Error(`ZIP_TOO_LARGE:${zipBuffer.length}`);
1854
+ }
1855
+ const safeZipName = this.normalizeRelativePath(zipName || "") || `workspace-${this.sessionId.slice(0, 8)}.zip`;
1856
+ const uploaded = await this.uploadBuffer(safeZipName.endsWith(".zip") ? safeZipName : `${safeZipName}.zip`, zipBuffer);
1857
+ for (const cb of this.doneCallbacks) cb({ attachments: [uploaded] });
1858
+ }
1859
+ async uploadBuffer(filename, buffer) {
1860
+ const creds = this.uploadCredentials;
1861
+ if (!creds) {
1862
+ throw new Error("Upload credentials missing");
1863
+ }
1864
+ const response = await fetch(creds.uploadUrl, {
1865
+ method: "POST",
1866
+ headers: {
1867
+ "X-Upload-Token": creds.uploadToken,
1868
+ "Content-Type": "application/json"
1869
+ },
1870
+ body: JSON.stringify({
1871
+ filename,
1872
+ content: buffer.toString("base64")
1873
+ })
1874
+ });
1875
+ if (!response.ok) {
1876
+ throw new Error(`Upload failed (${response.status}) for ${filename}`);
1877
+ }
1878
+ const payload = await response.json();
1879
+ if (typeof payload.url !== "string" || payload.url.length === 0) {
1880
+ throw new Error(`Upload response missing url for ${filename}`);
1881
+ }
1882
+ const ext = filename.split(".").pop()?.toLowerCase() || "";
1883
+ return {
1884
+ name: filename,
1885
+ url: payload.url,
1886
+ type: MIME_MAP[ext] || "application/octet-stream"
1887
+ };
1786
1888
  }
1787
1889
  async collectWorkspaceFiles(workspaceRoot) {
1788
1890
  return collectRealFiles(workspaceRoot, MAX_COLLECT_FILES);
1789
1891
  }
1892
+ async collectWorkspaceManifest(workspaceRoot) {
1893
+ const files = await this.collectWorkspaceFiles(workspaceRoot);
1894
+ const manifest = [];
1895
+ for (const absPath of files) {
1896
+ const relPath = relative3(workspaceRoot, absPath).replace(/\\/g, "/");
1897
+ if (!relPath || relPath.startsWith("..")) continue;
1898
+ try {
1899
+ const fileStat = await stat2(absPath);
1900
+ if (!fileStat.isFile()) continue;
1901
+ const ext = relPath.split(".").pop()?.toLowerCase() || "";
1902
+ manifest.push({
1903
+ path: relPath,
1904
+ size: fileStat.size,
1905
+ mtime_ms: Math.floor(fileStat.mtimeMs),
1906
+ type: MIME_MAP[ext] || "application/octet-stream"
1907
+ });
1908
+ } catch {
1909
+ }
1910
+ }
1911
+ manifest.sort((a, b) => a.path.localeCompare(b.path));
1912
+ return manifest;
1913
+ }
1914
+ async finalizeDone(attachments) {
1915
+ const workspaceRoot = this.getWorkspaceRoot();
1916
+ let fileManifest;
1917
+ try {
1918
+ fileManifest = await this.collectWorkspaceManifest(workspaceRoot);
1919
+ } catch (error) {
1920
+ log.warn(`Manifest collection failed: ${error}`);
1921
+ }
1922
+ const payload = {};
1923
+ if (attachments && attachments.length > 0) {
1924
+ payload.attachments = attachments;
1925
+ }
1926
+ if (fileManifest) {
1927
+ payload.fileManifest = fileManifest;
1928
+ }
1929
+ for (const cb of this.doneCallbacks) cb(payload);
1930
+ }
1790
1931
  onChunk(cb) {
1791
1932
  this.chunkCallbacks.push(cb);
1792
1933
  }
@@ -1921,47 +2062,15 @@ var ClaudeSession = class {
1921
2062
  }
1922
2063
  }
1923
2064
  this.doneFired = true;
1924
- void this.autoUploadAndDone();
2065
+ void this.finalizeDone();
1925
2066
  return;
1926
2067
  }
1927
2068
  if (event.type === "assistant" && event.subtype === "end") {
1928
2069
  this.doneFired = true;
1929
- for (const cb of this.doneCallbacks) cb();
2070
+ void this.finalizeDone();
1930
2071
  return;
1931
2072
  }
1932
2073
  }
1933
- /**
1934
- * Auto-upload new/modified files from workspace, then fire done callbacks.
1935
- */
1936
- async autoUploadAndDone() {
1937
- let attachments;
1938
- const workspaceRoot = this.currentWorkspace || this.config.project;
1939
- if (this.uploadCredentials && workspaceRoot) {
1940
- try {
1941
- attachments = await diffAndUpload({
1942
- workspace: workspaceRoot,
1943
- snapshot: this.preMessageSnapshot,
1944
- uploadUrl: this.uploadCredentials.uploadUrl,
1945
- uploadToken: this.uploadCredentials.uploadToken
1946
- });
1947
- if (attachments && attachments.length > 0) {
1948
- log.info(`Auto-uploaded ${attachments.length} file(s) from workspace`);
1949
- }
1950
- } catch (err) {
1951
- log.warn(`Auto-upload failed: ${err}`);
1952
- }
1953
- }
1954
- for (const cb of this.doneCallbacks) cb(attachments);
1955
- }
1956
- /**
1957
- * Snapshot all files in the workspace before Claude starts processing.
1958
- */
1959
- async takeSnapshot() {
1960
- this.preMessageSnapshot.clear();
1961
- const workspaceRoot = this.currentWorkspace || this.config.project;
1962
- if (!workspaceRoot) return;
1963
- this.preMessageSnapshot = await snapshotWorkspace(workspaceRoot);
1964
- }
1965
2074
  emitChunk(text) {
1966
2075
  this.chunksEmitted = true;
1967
2076
  for (const cb of this.chunkCallbacks) cb(text);
@@ -2132,7 +2241,7 @@ function registerConnectCommand(program2) {
2132
2241
  log.error(`Failed to start. Check logs: ${getLogPath(slug)}`);
2133
2242
  process.exit(1);
2134
2243
  }
2135
- const { ListTUI } = await import("./list-KMJ463VL.js");
2244
+ const { ListTUI } = await import("./list-ROLJARYB.js");
2136
2245
  const tui = new ListTUI();
2137
2246
  await tui.run();
2138
2247
  return;
@@ -3284,6 +3393,10 @@ async function asyncChat(opts) {
3284
3393
  `);
3285
3394
  }
3286
3395
  }
3396
+ if (task.file_manifest) {
3397
+ process.stdout.write(`${GRAY}[manifest: ${task.file_manifest.length} files]${RESET}
3398
+ `);
3399
+ }
3287
3400
  return;
3288
3401
  }
3289
3402
  if (task.status === "failed") {
@@ -3490,7 +3603,7 @@ import { readFile as readFile4, writeFile as writeFile3, readdir as readdir2, mk
3490
3603
  import { join as join9, resolve, relative as relative4 } from "path";
3491
3604
 
3492
3605
  // src/utils/skill-parser.ts
3493
- import { readFile as readFile3, writeFile as writeFile2, stat as stat2 } from "fs/promises";
3606
+ import { readFile as readFile3, writeFile as writeFile2, stat as stat3 } from "fs/promises";
3494
3607
  import { join as join8 } from "path";
3495
3608
  function parseSkillMd(raw) {
3496
3609
  const trimmed = raw.trimStart();
@@ -3601,7 +3714,7 @@ async function loadSkillManifest(dir) {
3601
3714
  }
3602
3715
  async function pathExists(p) {
3603
3716
  try {
3604
- await stat2(p);
3717
+ await stat3(p);
3605
3718
  return true;
3606
3719
  } catch {
3607
3720
  return false;
@@ -3632,153 +3745,6 @@ ${updated}${after}`);
3632
3745
  }
3633
3746
  }
3634
3747
 
3635
- // src/utils/zip.ts
3636
- import { deflateRawSync, inflateRawSync } from "zlib";
3637
- function dosTime(date) {
3638
- return {
3639
- time: date.getHours() << 11 | date.getMinutes() << 5 | date.getSeconds() >> 1,
3640
- date: date.getFullYear() - 1980 << 9 | date.getMonth() + 1 << 5 | date.getDate()
3641
- };
3642
- }
3643
- function crc32(buf) {
3644
- let crc = 4294967295;
3645
- for (let i = 0; i < buf.length; i++) {
3646
- crc ^= buf[i];
3647
- for (let j = 0; j < 8; j++) {
3648
- crc = crc >>> 1 ^ (crc & 1 ? 3988292384 : 0);
3649
- }
3650
- }
3651
- return (crc ^ 4294967295) >>> 0;
3652
- }
3653
- function writeUint16LE(buf, val, offset) {
3654
- buf[offset] = val & 255;
3655
- buf[offset + 1] = val >>> 8 & 255;
3656
- }
3657
- function writeUint32LE(buf, val, offset) {
3658
- buf[offset] = val & 255;
3659
- buf[offset + 1] = val >>> 8 & 255;
3660
- buf[offset + 2] = val >>> 16 & 255;
3661
- buf[offset + 3] = val >>> 24 & 255;
3662
- }
3663
- function createZipBuffer(entries) {
3664
- const now = /* @__PURE__ */ new Date();
3665
- const { time, date } = dosTime(now);
3666
- const records = [];
3667
- const chunks = [];
3668
- let offset = 0;
3669
- for (const entry of entries) {
3670
- const nameBytes = Buffer.from(entry.path, "utf-8");
3671
- const crc = crc32(entry.data);
3672
- const compressed = deflateRawSync(entry.data, { level: 6 });
3673
- const compressedSize = compressed.length;
3674
- const uncompressedSize = entry.data.length;
3675
- const header = Buffer.alloc(30 + nameBytes.length);
3676
- writeUint32LE(header, 67324752, 0);
3677
- writeUint16LE(header, 20, 4);
3678
- writeUint16LE(header, 0, 6);
3679
- writeUint16LE(header, 8, 8);
3680
- writeUint16LE(header, time, 10);
3681
- writeUint16LE(header, date, 12);
3682
- writeUint32LE(header, crc, 14);
3683
- writeUint32LE(header, compressedSize, 18);
3684
- writeUint32LE(header, uncompressedSize, 22);
3685
- writeUint16LE(header, nameBytes.length, 26);
3686
- writeUint16LE(header, 0, 28);
3687
- nameBytes.copy(header, 30);
3688
- records.push({ header, compressed, crc, compressedSize, uncompressedSize, offset });
3689
- chunks.push(header, compressed);
3690
- offset += header.length + compressed.length;
3691
- }
3692
- const centralDirStart = offset;
3693
- for (let i = 0; i < entries.length; i++) {
3694
- const entry = entries[i];
3695
- const rec = records[i];
3696
- const nameBytes = Buffer.from(entry.path, "utf-8");
3697
- const cdh = Buffer.alloc(46 + nameBytes.length);
3698
- writeUint32LE(cdh, 33639248, 0);
3699
- writeUint16LE(cdh, 20, 4);
3700
- writeUint16LE(cdh, 20, 6);
3701
- writeUint16LE(cdh, 0, 8);
3702
- writeUint16LE(cdh, 8, 10);
3703
- writeUint16LE(cdh, time, 12);
3704
- writeUint16LE(cdh, date, 14);
3705
- writeUint32LE(cdh, rec.crc, 16);
3706
- writeUint32LE(cdh, rec.compressedSize, 20);
3707
- writeUint32LE(cdh, rec.uncompressedSize, 24);
3708
- writeUint16LE(cdh, nameBytes.length, 28);
3709
- writeUint16LE(cdh, 0, 30);
3710
- writeUint16LE(cdh, 0, 32);
3711
- writeUint16LE(cdh, 0, 34);
3712
- writeUint16LE(cdh, 0, 36);
3713
- writeUint32LE(cdh, 0, 38);
3714
- writeUint32LE(cdh, rec.offset, 42);
3715
- nameBytes.copy(cdh, 46);
3716
- chunks.push(cdh);
3717
- offset += cdh.length;
3718
- }
3719
- const centralDirSize = offset - centralDirStart;
3720
- const eocd = Buffer.alloc(22);
3721
- writeUint32LE(eocd, 101010256, 0);
3722
- writeUint16LE(eocd, 0, 4);
3723
- writeUint16LE(eocd, 0, 6);
3724
- writeUint16LE(eocd, entries.length, 8);
3725
- writeUint16LE(eocd, entries.length, 10);
3726
- writeUint32LE(eocd, centralDirSize, 12);
3727
- writeUint32LE(eocd, centralDirStart, 16);
3728
- writeUint16LE(eocd, 0, 20);
3729
- chunks.push(eocd);
3730
- return Buffer.concat(chunks);
3731
- }
3732
- function extractZipBuffer(buf) {
3733
- const entries = [];
3734
- let eocdOffset = -1;
3735
- for (let i = buf.length - 22; i >= 0; i--) {
3736
- if (buf.readUInt32LE(i) === 101010256) {
3737
- eocdOffset = i;
3738
- break;
3739
- }
3740
- }
3741
- if (eocdOffset === -1) {
3742
- throw new Error("Invalid ZIP: EOCD not found");
3743
- }
3744
- const entryCount = buf.readUInt16LE(eocdOffset + 10);
3745
- const centralDirOffset = buf.readUInt32LE(eocdOffset + 16);
3746
- let offset = centralDirOffset;
3747
- for (let i = 0; i < entryCount; i++) {
3748
- if (buf.readUInt32LE(offset) !== 33639248) {
3749
- throw new Error(`Invalid ZIP: bad central directory signature at ${offset}`);
3750
- }
3751
- const compressionMethod = buf.readUInt16LE(offset + 10);
3752
- const compressedSize = buf.readUInt32LE(offset + 20);
3753
- const uncompressedSize = buf.readUInt32LE(offset + 24);
3754
- const nameLen = buf.readUInt16LE(offset + 28);
3755
- const extraLen = buf.readUInt16LE(offset + 30);
3756
- const commentLen = buf.readUInt16LE(offset + 32);
3757
- const localHeaderOffset = buf.readUInt32LE(offset + 42);
3758
- const name = buf.subarray(offset + 46, offset + 46 + nameLen).toString("utf-8");
3759
- if (!name.endsWith("/")) {
3760
- const localNameLen = buf.readUInt16LE(localHeaderOffset + 26);
3761
- const localExtraLen = buf.readUInt16LE(localHeaderOffset + 28);
3762
- const dataOffset = localHeaderOffset + 30 + localNameLen + localExtraLen;
3763
- const compressedData = buf.subarray(dataOffset, dataOffset + compressedSize);
3764
- let data;
3765
- if (compressionMethod === 0) {
3766
- data = Buffer.from(compressedData);
3767
- } else if (compressionMethod === 8) {
3768
- data = inflateRawSync(compressedData);
3769
- } else {
3770
- throw new Error(`Unsupported compression method: ${compressionMethod}`);
3771
- }
3772
- if (data.length !== uncompressedSize) {
3773
- throw new Error(`Size mismatch for ${name}: expected ${uncompressedSize}, got ${data.length}`);
3774
- }
3775
- entries.push({ path: name, data });
3776
- }
3777
- offset += 46 + nameLen + extraLen + commentLen;
3778
- }
3779
- return entries;
3780
- }
3781
-
3782
3748
  // src/commands/skills.ts
3783
3749
  var slog = {
3784
3750
  info: (msg) => {
@@ -4518,7 +4484,7 @@ async function asyncCall(opts) {
4518
4484
  }
4519
4485
  process.exit(1);
4520
4486
  }
4521
- const { request_id, call_id, status, error_message, error_code } = await res.json();
4487
+ const { request_id, call_id, session_key, status, error_message, error_code } = await res.json();
4522
4488
  if (status === "failed") {
4523
4489
  log.error(`Call failed: ${error_message || error_code}`);
4524
4490
  process.exit(1);
@@ -4553,9 +4519,11 @@ async function asyncCall(opts) {
4553
4519
  console.log(JSON.stringify({
4554
4520
  call_id,
4555
4521
  request_id,
4522
+ ...session_key ? { session_key } : {},
4556
4523
  status: "completed",
4557
4524
  result,
4558
4525
  ...task.attachments?.length ? { attachments: task.attachments } : {},
4526
+ ...Array.isArray(task.file_manifest) ? { file_manifest: task.file_manifest } : {},
4559
4527
  rate_hint: `POST /api/agents/${opts.id}/rate body: { call_id: "${call_id}", rating: 1-5 }`
4560
4528
  }));
4561
4529
  } else {
@@ -4565,6 +4533,13 @@ async function asyncCall(opts) {
4565
4533
  log.info(` ${GRAY}File:${RESET} ${att.name} ${GRAY}${att.url}${RESET}`);
4566
4534
  }
4567
4535
  }
4536
+ const manifest = task.file_manifest;
4537
+ if (Array.isArray(manifest)) {
4538
+ log.info(` ${GRAY}Manifest:${RESET} ${manifest.length} file(s)`);
4539
+ }
4540
+ if (session_key) {
4541
+ log.info(` ${GRAY}Session:${RESET} ${session_key}`);
4542
+ }
4568
4543
  }
4569
4544
  if (opts.outputFile && result) {
4570
4545
  writeFileSync3(opts.outputFile, result);
@@ -4573,7 +4548,7 @@ async function asyncCall(opts) {
4573
4548
  if (!opts.json) {
4574
4549
  log.info(`${GRAY}Rate this call: agent-mesh rate ${call_id} <1-5> --agent ${opts.id}${RESET}`);
4575
4550
  }
4576
- return { callId: call_id };
4551
+ return { callId: call_id, ...session_key ? { sessionKey: session_key } : {} };
4577
4552
  }
4578
4553
  if (task.status === "failed") {
4579
4554
  if (!opts.json) {
@@ -4637,7 +4612,7 @@ async function streamCall(opts) {
4637
4612
  console.log(` ${GRAY}Created${RESET} ${result.created_at}`);
4638
4613
  console.log("");
4639
4614
  }
4640
- return;
4615
+ return { callId: result.call_id, ...result.session_key ? { sessionKey: result.session_key } : {} };
4641
4616
  }
4642
4617
  if (!res.body) {
4643
4618
  log.error("Empty response body");
@@ -4653,6 +4628,7 @@ async function streamCall(opts) {
4653
4628
  let outputBuffer = "";
4654
4629
  let inThinkingBlock = false;
4655
4630
  let callId = res.headers.get("X-Call-Id") || "";
4631
+ let sessionKey = res.headers.get("X-Session-Key") || "";
4656
4632
  while (true) {
4657
4633
  const { done, value } = await reader.read();
4658
4634
  if (done) break;
@@ -4665,6 +4641,9 @@ async function streamCall(opts) {
4665
4641
  const event = JSON.parse(data);
4666
4642
  if (event.type === "start" && event.call_id) {
4667
4643
  callId = event.call_id;
4644
+ if (event.session_key) {
4645
+ sessionKey = event.session_key;
4646
+ }
4668
4647
  }
4669
4648
  if (opts.json) {
4670
4649
  console.log(JSON.stringify(event));
@@ -4688,6 +4667,9 @@ async function streamCall(opts) {
4688
4667
  for (const att of event.attachments) {
4689
4668
  log.info(` ${GRAY}File:${RESET} ${att.name} ${GRAY}${att.url}${RESET}`);
4690
4669
  }
4670
+ if (Array.isArray(event.file_manifest)) {
4671
+ log.info(` ${GRAY}Manifest:${RESET} ${event.file_manifest.length} file(s)`);
4672
+ }
4691
4673
  } else if (event.type === "error") {
4692
4674
  process.stderr.write(`
4693
4675
  Error: ${event.message}
@@ -4726,11 +4708,14 @@ Error: ${event.message}
4726
4708
  if (!opts.json) {
4727
4709
  console.log("\n");
4728
4710
  log.success("Call completed");
4711
+ if (sessionKey) {
4712
+ log.info(`${GRAY}Session:${RESET} ${sessionKey}`);
4713
+ }
4729
4714
  if (callId) {
4730
4715
  log.info(`${GRAY}Rate this call: agent-mesh rate ${callId} <1-5> --agent ${opts.id}${RESET}`);
4731
4716
  }
4732
4717
  }
4733
- return { callId };
4718
+ return { callId, ...sessionKey ? { sessionKey } : {} };
4734
4719
  }
4735
4720
  function registerCallCommand(program2) {
4736
4721
  program2.command("call <agent>").description("Call an agent on the A2A network (default: async polling)").requiredOption("--task <description>", "Task description").option("--input-file <path>", "Read file and append to task description").option("--output-file <path>", "Save response text to file").option("--stream", "Use SSE streaming instead of async polling").option("--json", "Output JSONL events").option("--timeout <seconds>", "Timeout in seconds", "300").option("--rate <rating>", "Rate the agent after call (1-5)", parseInt).action(async (agentInput, opts) => {
@@ -5020,7 +5005,7 @@ function registerSubscribeCommand(program2) {
5020
5005
 
5021
5006
  // src/commands/register.ts
5022
5007
  var DEFAULT_BASE_URL5 = "https://agents.hot";
5023
- var VALID_AGENT_TYPES = ["openclaw", "claude-code", "cursor", "windsurf", "custom"];
5008
+ var VALID_AGENT_TYPES = ["claude", "claude-code", "cursor", "windsurf", "custom"];
5024
5009
  function registerRegisterCommand(program2) {
5025
5010
  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) => {
5026
5011
  if (!VALID_AGENT_TYPES.includes(opts.type)) {
@@ -5228,6 +5213,178 @@ function registerProfileCommand(program2) {
5228
5213
  });
5229
5214
  }
5230
5215
 
5216
+ // src/commands/files.ts
5217
+ import { writeFileSync as writeFileSync4 } from "fs";
5218
+ import { basename as basename2 } from "path";
5219
+ function formatBytes(bytes) {
5220
+ if (bytes < 1024) return `${bytes}B`;
5221
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
5222
+ return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
5223
+ }
5224
+ function defaultOutputPath(filePath) {
5225
+ const name = basename2(filePath || "") || "file";
5226
+ return name;
5227
+ }
5228
+ function defaultZipOutputPath(sessionKey) {
5229
+ const suffix = sessionKey.split(":").at(-1) || "session";
5230
+ return `session-${suffix}.zip`;
5231
+ }
5232
+ function handleError7(err) {
5233
+ if (err instanceof PlatformApiError) {
5234
+ log.error(err.message);
5235
+ } else {
5236
+ log.error(err.message);
5237
+ }
5238
+ process.exit(1);
5239
+ }
5240
+ async function resolveTargetAgent(agentInput) {
5241
+ const client = createClient();
5242
+ return resolveAgentId(agentInput, client);
5243
+ }
5244
+ async function downloadToFile(url, outputPath) {
5245
+ const res = await fetch(url);
5246
+ if (!res.ok) {
5247
+ throw new Error(`Download failed: HTTP ${res.status}`);
5248
+ }
5249
+ const buf = Buffer.from(await res.arrayBuffer());
5250
+ writeFileSync4(outputPath, buf);
5251
+ }
5252
+ function registerFilesCommand(program2) {
5253
+ const files = program2.command("files").description("Session file manifest and on-demand upload/download commands");
5254
+ files.command("list").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--json", "Output raw JSON").action(async (opts) => {
5255
+ try {
5256
+ const { id, name } = await resolveTargetAgent(opts.agent);
5257
+ const client = createClient();
5258
+ const data = await client.get(
5259
+ `/api/agents/${id}/files?session_key=${encodeURIComponent(opts.session)}`
5260
+ );
5261
+ if (opts.json) {
5262
+ console.log(JSON.stringify(data));
5263
+ return;
5264
+ }
5265
+ log.banner(`Session Files \u2014 ${BOLD}${name}${RESET}`);
5266
+ console.log(` ${GRAY}Session${RESET} ${data.session_key || opts.session}`);
5267
+ console.log(` ${GRAY}Count${RESET} ${data.files.length}`);
5268
+ if (data.updated_at) {
5269
+ console.log(` ${GRAY}Updated${RESET} ${data.updated_at}`);
5270
+ }
5271
+ console.log("");
5272
+ for (const f of data.files) {
5273
+ console.log(` ${f.path} ${GRAY}${formatBytes(f.size)}${RESET}`);
5274
+ }
5275
+ console.log("");
5276
+ } catch (err) {
5277
+ handleError7(err);
5278
+ }
5279
+ });
5280
+ files.command("upload").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").requiredOption("--path <file_path>", "Relative file path in session workspace").option("--json", "Output raw JSON").action(async (opts) => {
5281
+ try {
5282
+ const { id } = await resolveTargetAgent(opts.agent);
5283
+ const client = createClient();
5284
+ const data = await client.post(`/api/agents/${id}/files/upload`, {
5285
+ session_key: opts.session,
5286
+ path: opts.path
5287
+ });
5288
+ if (opts.json) {
5289
+ console.log(JSON.stringify(data));
5290
+ return;
5291
+ }
5292
+ if (!data.file?.url) {
5293
+ throw new Error("No file URL returned");
5294
+ }
5295
+ log.success(`Uploaded ${opts.path}`);
5296
+ console.log(` ${GRAY}URL${RESET} ${data.file.url}`);
5297
+ } catch (err) {
5298
+ handleError7(err);
5299
+ }
5300
+ });
5301
+ files.command("upload-all").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--json", "Output raw JSON").action(async (opts) => {
5302
+ try {
5303
+ const { id } = await resolveTargetAgent(opts.agent);
5304
+ const client = createClient();
5305
+ const data = await client.post(`/api/agents/${id}/files/upload-all`, {
5306
+ session_key: opts.session
5307
+ });
5308
+ if (opts.json) {
5309
+ console.log(JSON.stringify(data));
5310
+ return;
5311
+ }
5312
+ if (!data.file?.url) {
5313
+ throw new Error("No ZIP URL returned");
5314
+ }
5315
+ log.success("Uploaded session ZIP");
5316
+ console.log(` ${GRAY}URL${RESET} ${data.file.url}`);
5317
+ } catch (err) {
5318
+ handleError7(err);
5319
+ }
5320
+ });
5321
+ files.command("download").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").requiredOption("--path <file_path>", "Relative file path in session workspace").option("--output <path>", "Local output path").option("--json", "Output raw JSON").action(async (opts) => {
5322
+ try {
5323
+ const { id } = await resolveTargetAgent(opts.agent);
5324
+ const client = createClient();
5325
+ const data = await client.post(`/api/agents/${id}/files/upload`, {
5326
+ session_key: opts.session,
5327
+ path: opts.path
5328
+ });
5329
+ const url = data.file?.url;
5330
+ if (!url) throw new Error("No file URL returned");
5331
+ const output = opts.output || defaultOutputPath(opts.path);
5332
+ await downloadToFile(url, output);
5333
+ if (opts.json) {
5334
+ console.log(JSON.stringify({ success: true, output, url }));
5335
+ return;
5336
+ }
5337
+ log.success(`Downloaded ${opts.path}`);
5338
+ console.log(` ${GRAY}Saved${RESET} ${output}`);
5339
+ } catch (err) {
5340
+ handleError7(err);
5341
+ }
5342
+ });
5343
+ files.command("download-all").requiredOption("--agent <agent>", "Target agent ID or name").requiredOption("--session <session_key>", "Session key").option("--output <path>", "Local output path").option("--json", "Output raw JSON").action(async (opts) => {
5344
+ try {
5345
+ const { id } = await resolveTargetAgent(opts.agent);
5346
+ const client = createClient();
5347
+ const data = await client.post(`/api/agents/${id}/files/upload-all`, {
5348
+ session_key: opts.session
5349
+ });
5350
+ const url = data.file?.url;
5351
+ if (!url) throw new Error("No ZIP URL returned");
5352
+ const output = opts.output || defaultZipOutputPath(opts.session);
5353
+ await downloadToFile(url, output);
5354
+ if (opts.json) {
5355
+ console.log(JSON.stringify({ success: true, output, url }));
5356
+ return;
5357
+ }
5358
+ log.success("Downloaded session ZIP");
5359
+ console.log(` ${GRAY}Saved${RESET} ${output}`);
5360
+ } catch (err) {
5361
+ handleError7(err);
5362
+ }
5363
+ });
5364
+ files.command("help").description("Show machine-readable files command reference").option("--json", "Output JSON format").action((opts) => {
5365
+ const reference = {
5366
+ command: "agent-mesh files",
5367
+ docs: "https://agents.hot/docs/cli/files",
5368
+ commands: [
5369
+ { name: "list", required: ["--agent", "--session"], optional: ["--json"] },
5370
+ { name: "upload", required: ["--agent", "--session", "--path"], optional: ["--json"] },
5371
+ { name: "upload-all", required: ["--agent", "--session"], optional: ["--json"] },
5372
+ { name: "download", required: ["--agent", "--session", "--path"], optional: ["--output", "--json"] },
5373
+ { name: "download-all", required: ["--agent", "--session"], optional: ["--output", "--json"] }
5374
+ ]
5375
+ };
5376
+ if (opts.json) {
5377
+ console.log(JSON.stringify(reference));
5378
+ return;
5379
+ }
5380
+ log.banner("Files Command Reference");
5381
+ console.log(` ${GRAY}Docs${RESET} ${reference.docs}`);
5382
+ for (const item of reference.commands) {
5383
+ console.log(` ${item.name}`);
5384
+ }
5385
+ });
5386
+ }
5387
+
5231
5388
  // src/utils/auto-updater.ts
5232
5389
  import { spawnSync } from "child_process";
5233
5390
  var AUTO_UPGRADE_ENV = "AGENT_MESH_AUTO_UPGRADE";
@@ -5353,6 +5510,14 @@ program.name("agent-mesh").description("Connect local AI agents to the Agents.Ho
5353
5510
  console.log(version);
5354
5511
  process.exit(0);
5355
5512
  });
5513
+ program.configureOutput({
5514
+ outputError: (str, write) => {
5515
+ write(str);
5516
+ if (str.trim().length > 0) {
5517
+ write("\nDocs: https://agents.hot/docs/cli\n");
5518
+ }
5519
+ }
5520
+ });
5356
5521
  registerConnectCommand(program);
5357
5522
  registerLoginCommand(program);
5358
5523
  registerStatusCommand(program);
@@ -5377,4 +5542,21 @@ registerRegisterCommand(program);
5377
5542
  registerRateCommand(program);
5378
5543
  registerRuntimeCommand(program);
5379
5544
  registerProfileCommand(program);
5545
+ registerFilesCommand(program);
5546
+ program.command("help").description("Show CLI help").option("--json", "Output machine-readable command reference").action((opts) => {
5547
+ if (opts.json) {
5548
+ const commands = program.commands.map((cmd) => cmd.name()).filter((name) => name !== "help");
5549
+ console.log(JSON.stringify({
5550
+ name: "agent-mesh",
5551
+ docs: "https://agents.hot/docs/cli",
5552
+ commands
5553
+ }));
5554
+ return;
5555
+ }
5556
+ program.outputHelp();
5557
+ });
5558
+ var wantsJsonOutput = process.argv.includes("--json");
5559
+ if (!wantsJsonOutput && !process.argv.includes("--version") && !process.argv.includes("-v")) {
5560
+ maybePrintDocsHint("https://agents.hot/docs/cli");
5561
+ }
5380
5562
  program.parse();
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  ListTUI,
4
4
  registerListCommand
5
- } from "./chunk-U32JDKSN.js";
5
+ } from "./chunk-KEUGYA3L.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.16.11",
3
+ "version": "0.16.12",
4
4
  "description": "CLI bridge connecting local AI agents to the Agents.Hot platform",
5
5
  "type": "module",
6
6
  "bin": {