remote-agents 0.1.11 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +5 -0
  2. package/install.js +71 -3
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -141,6 +141,10 @@ remote-agents install --room dev --token <secret> --relay wss://<your-relay-host
141
141
  ```bash
142
142
  remote-agents-relay --bind 0.0.0.0:8080
143
143
  # agents/MCP then use relay_url = ws://<host>:8080
144
+ # optional: --token <secret> to gate room access at the relay;
145
+ # --idle-timeout-secs <n> to reap silently-dead sockets (default 90, 0 disables)
146
+ # monitoring: GET /health, /api/rooms (all active rooms + counts),
147
+ # /api/room/:room (one room's agents)
144
148
  ```
145
149
 
146
150
  **Cloudflare Worker:**
@@ -232,6 +236,7 @@ the registered entry.
232
236
  | `list_agents` | List agents connected to the relay room |
233
237
  | `fleet_exec` / `fleet_read` / `fleet_write` / `fleet_git` / `fleet_search` | Run an operation across the fleet — `target = all \| tag1,tag2 \| os:<family>` |
234
238
  | `file_search` / `file_stat` / `send_file` / `transfer_get` | Find files on a host, and move a file host→host (UDP, SHA-256 verified) |
239
+ | `tunnel_start` / `tunnel_list` / `tunnel_stop` | Expose a host's local port at a public `*.trycloudflare.com` URL via a Cloudflare quick tunnel (`cloudflared` auto-downloaded; Edit/Bypass) |
235
240
  | `mapreduce` | Distributed map/reduce over the fleet (shell map/reduce functions) |
236
241
 
237
242
  Each agent advertises platform metadata (OS family, distro, kernel, shell) and
package/install.js CHANGED
@@ -5,6 +5,7 @@
5
5
  const fs = require("fs");
6
6
  const path = require("path");
7
7
  const https = require("https");
8
+ const crypto = require("crypto");
8
9
  const { version } = require("./package.json");
9
10
 
10
11
  const REPO = "47-ronn/tunshell_mcp_agents";
@@ -42,6 +43,66 @@ function download(url, dest, redirects = 0) {
42
43
 
43
44
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
44
45
 
46
+ // GET a small text resource into memory (follows redirects). Used for the
47
+ // checksum sidecar.
48
+ function httpGetText(url, redirects = 0) {
49
+ return new Promise((resolve, reject) => {
50
+ if (redirects > 10) return reject(new Error("too many redirects"));
51
+ https
52
+ .get(url, { headers: { "User-Agent": "remote-agents-installer" } }, (res) => {
53
+ if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
54
+ res.resume();
55
+ return resolve(httpGetText(res.headers.location, redirects + 1));
56
+ }
57
+ if (res.statusCode !== 200) {
58
+ res.resume();
59
+ return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
60
+ }
61
+ let body = "";
62
+ res.setEncoding("utf8");
63
+ res.on("data", (c) => (body += c));
64
+ res.on("end", () => resolve(body));
65
+ })
66
+ .on("error", reject);
67
+ });
68
+ }
69
+
70
+ // SHA-256 of a file as lowercase hex.
71
+ function sha256File(file) {
72
+ return new Promise((resolve, reject) => {
73
+ const h = crypto.createHash("sha256");
74
+ const s = fs.createReadStream(file);
75
+ s.on("data", (d) => h.update(d));
76
+ s.on("end", () => resolve(h.digest("hex")));
77
+ s.on("error", reject);
78
+ });
79
+ }
80
+
81
+ // Verify `dest` against the `<url>.sha256` sidecar published with the release.
82
+ // Throws on mismatch (a corrupt/tampered download). If the sidecar is absent
83
+ // (older releases) or malformed, skips verification and returns false — so the
84
+ // installer stays compatible with releases that predate checksums.
85
+ // `fetchText`/`hashFile` are injectable for tests.
86
+ async function verifyChecksum(url, dest, fetchText = httpGetText, hashFile = sha256File) {
87
+ let doc;
88
+ try {
89
+ doc = await fetchText(`${url}.sha256`);
90
+ } catch {
91
+ console.warn("remote-agents: no checksum published for this release; skipping verification");
92
+ return false;
93
+ }
94
+ const expected = (doc.trim().split(/\s+/)[0] || "").toLowerCase();
95
+ if (!/^[0-9a-f]{64}$/.test(expected)) {
96
+ console.warn("remote-agents: malformed checksum; skipping verification");
97
+ return false;
98
+ }
99
+ const actual = (await hashFile(dest)).toLowerCase();
100
+ if (actual !== expected) {
101
+ throw new Error(`checksum mismatch (expected ${expected}, got ${actual})`);
102
+ }
103
+ return true;
104
+ }
105
+
45
106
  // Retry transient download failures (flaky networks / CDN hiccups) with a small
46
107
  // linear backoff. `doDownload`/`wait` are injectable for testing.
47
108
  async function downloadWithRetry(url, dest, attempts = 3, doDownload = download, wait = sleep) {
@@ -87,14 +148,21 @@ async function main() {
87
148
  console.log(`remote-agents: downloading ${asset} (v${version})…`);
88
149
  try {
89
150
  await downloadWithRetry(url, dest);
90
- if (!t.exe) fs.chmodSync(dest, 0o755);
91
- console.log(`remote-agents: installed ${dest}`);
92
151
  } catch (e) {
93
152
  console.error(`remote-agents: failed to download binary: ${e.message}`);
94
153
  console.error(` URL: ${url}`);
95
154
  console.error(` Ensure a release tagged v${version} exists with that asset.`);
96
155
  process.exit(1);
97
156
  }
157
+ try {
158
+ await verifyChecksum(url, dest);
159
+ } catch (e) {
160
+ try { fs.unlinkSync(dest); } catch {}
161
+ console.error(`remote-agents: ${e.message}; the download was corrupt or tampered.`);
162
+ process.exit(1);
163
+ }
164
+ if (!t.exe) fs.chmodSync(dest, 0o755);
165
+ console.log(`remote-agents: installed ${dest}`);
98
166
  }
99
167
 
100
168
  // Only download when run as the postinstall script, not when require()'d by tests.
@@ -102,4 +170,4 @@ if (require.main === module) {
102
170
  main();
103
171
  }
104
172
 
105
- module.exports = { target, downloadWithRetry };
173
+ module.exports = { target, downloadWithRetry, verifyChecksum, sha256File };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remote-agents",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "mcpName": "io.github.47-ronn/remote-agents",
5
5
  "description": "Unified MCP server for controlling fleets of remote machines through AI agents (Claude, opencode)",
6
6
  "keywords": [