remote-agents 0.1.10 → 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.
- package/README.md +5 -0
- package/install.js +71 -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