mycomesh-relay 0.9.12 → 0.9.13

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.mjs +50 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mycomesh-relay",
3
- "version": "0.9.12",
3
+ "version": "0.9.13",
4
4
  "description": "Run an independent MycoMesh V11 Relay in Docker: no domain, no certificate authority, discoverable on-chain.",
5
5
  "repository": {
6
6
  "type": "git",
package/src/cli.mjs CHANGED
@@ -20,8 +20,10 @@ const USAGE = `Usage: mycomesh-relay <command> [options]
20
20
  register [--deposit UNITS] bind the signer on-chain, fund probes, and announce the Relay (with its
21
21
  certificate pin) in the on-chain Relay directory; testnet gas comes from the faucet
22
22
  start [--with-keeper] run the Relay on ports 10443 (HTTPS) and 10991 (Provider links)
23
- status | logs | stop inspect or stop it
23
+ status | logs | stop inspect or stop it (status: Providers linked, settlement queue, gas left)
24
24
  earnings | claim the Relay's share of fees and paying it out
25
+ receipts [--retry] receipts not settled yet or rejected, and why; --retry queues again
26
+ every rejected one still inside its deadline
25
27
  address print the owner address (fund it with a little ETH on mainnets)
26
28
 
27
29
  Options: --home DIR (default ~/.mycomesh/relay), --image REF, --network FILE,
@@ -59,6 +61,18 @@ function mycomesh(values, paths, args, extra = []) {
59
61
  values.image || IMAGE, ...args.map((arg) => (arg === "@network" ? net.inside : arg))]);
60
62
  }
61
63
 
64
+ /** The running Relay's /health, from this machine (its certificate is self-signed and pinned on-chain). */
65
+ function localHealth(port) {
66
+ return new Promise((resolvePromise) => {
67
+ const req = request({ host: "127.0.0.1", port: Number(port), path: "/health", rejectUnauthorized: false, timeout: 5000 }, (res) => {
68
+ let body = ""; res.on("data", (chunk) => { body += chunk; });
69
+ res.on("end", () => { try { resolvePromise(JSON.parse(body)); } catch { resolvePromise(null); } });
70
+ });
71
+ req.on("error", () => resolvePromise(null)); req.on("timeout", () => req.destroy());
72
+ req.end();
73
+ });
74
+ }
75
+
62
76
  function publicIp() {
63
77
  return new Promise((resolvePromise, reject) => {
64
78
  request("https://api.ipify.org", (res) => {
@@ -92,9 +106,10 @@ export async function main(argv = process.argv.slice(2), { stdout = process.stdo
92
106
  home: { type: "string" }, image: { type: "string" }, network: { type: "string" }, "public-ip": { type: "string" },
93
107
  deposit: { type: "string", default: "20000000" }, "http-port": { type: "string", default: "10443" },
94
108
  "link-port": { type: "string", default: "10991" }, "with-keeper": { type: "boolean" }, help: { type: "boolean" },
95
- version: { type: "boolean" },
109
+ version: { type: "boolean" }, retry: { type: "boolean" }, json: { type: "boolean" },
96
110
  },
97
111
  });
112
+ const human = !values.json && stdout === process.stdout && process.stdout.isTTY;
98
113
  const [command = "help"] = positionals;
99
114
  if (values.version) {
100
115
  stdout.write(`${JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8")).version}\n`);
@@ -155,7 +170,32 @@ export async function main(argv = process.argv.slice(2), { stdout = process.stdo
155
170
  const state = spawnSync("docker", ["ps", "-a", "--filter", `name=^${name}$`, "--format", "{{.Status}}"], { encoding: "utf8" }).stdout.trim();
156
171
  if (state) stdout.write(`${name}: ${state}\n`);
157
172
  }
158
- stdout.write(`${spawnSync("docker", ["logs", "--tail", "5", RELAY], { encoding: "utf8" }).stdout || ""}`);
173
+ const health = await localHealth(config.http_port || values["http-port"]);
174
+ if (!health) {
175
+ stdout.write(`${spawnSync("docker", ["logs", "--tail", "5", RELAY], { encoding: "utf8" }).stdout || ""}`);
176
+ return 2;
177
+ }
178
+ const queue = health.queue || {};
179
+ stdout.write(`Providers linked: ${health.providers}\n`
180
+ + `receipts: ${queue.queued || 0} waiting to settle, ${queue.settled || 0} in escrow, ${queue.released || 0} paid out, `
181
+ + `${queue.rejected || 0} rejected${queue.rejected ? " (mycomesh-relay receipts)" : ""}\n`);
182
+ const gas = health.submitter;
183
+ if (gas?.address) stdout.write(`settlement gas: ${gas.balance_eth} ETH in ${gas.address}${gas.low ? " LOW: fund it, or receipts stop settling" : ""}\n`);
184
+ if (health.settlement_worker?.error) stdout.write(`settlement worker: ${health.settlement_worker.error}\n`);
185
+ return 0;
186
+ }
187
+ if (command === "receipts") {
188
+ const raw = mycomesh(values, paths, ["relay", "receipts", "--network", "@network", "--data-dir", "/data/relay",
189
+ ...(values.retry ? ["--retry"] : [])]);
190
+ if (!human) { stdout.write(`${raw}\n`); return 0; }
191
+ const report = JSON.parse(raw);
192
+ stdout.write(`${JSON.stringify(report.counts)}\n`);
193
+ for (const r of report.receipts.slice(0, 30)) {
194
+ stdout.write(`${r.state.padEnd(8)} ${(r.fee / 1e6).toFixed(6)} tUSDC ${new Date(r.updated_at * 1000).toLocaleString()} `
195
+ + `${r.still_settleable ? "can still settle" : "expired"} ${(r.error || "").slice(0, 100)}\n`);
196
+ }
197
+ if (report.rejected_expired_fees) stdout.write(`lost to expiry: ${(report.rejected_expired_fees / 1e6).toFixed(6)} tUSDC\n`);
198
+ if (report.retried.length) stdout.write(`queued again: ${report.retried.length}\n`);
159
199
  return 0;
160
200
  }
161
201
  if (command === "logs") { docker(["logs", "-f", "--tail", "100", RELAY], { capture: false }); return 0; }
@@ -165,7 +205,13 @@ export async function main(argv = process.argv.slice(2), { stdout = process.stdo
165
205
  return 0;
166
206
  }
167
207
  if (command === "earnings") {
168
- stdout.write(`${mycomesh(values, paths, ["relay", "earnings", "--network", "@network", "--owner", config.owner])}\n`);
208
+ const raw = mycomesh(values, paths, ["relay", "earnings", "--network", "@network", "--owner", config.owner]);
209
+ if (!human) { stdout.write(`${raw}\n`); return 0; }
210
+ const e = JSON.parse(raw);
211
+ const relay = e.myco?.roles?.relay;
212
+ stdout.write(`claimable now ${(e.claimable / 1e6).toFixed(6)} tUSDC (the Relay's 5%, paid as receipts are released)\n`
213
+ + (relay ? `MYCO claimable ${(Number(BigInt(relay.claimable_wei) / 10n ** 14n) / 1e4).toFixed(4)} MYCO\n` : "")
214
+ + `owner gas ${(Number(e.eth_wei) / 1e18).toFixed(5)} ETH (pays settlement, release and claims)\n`);
169
215
  return 0;
170
216
  }
171
217
  if (command === "claim") {