@marshell/cli 0.7.4 → 0.7.5

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/dist/bridge.js CHANGED
@@ -408,6 +408,7 @@ async function handleInbound(networkUrl, msg, options) {
408
408
  });
409
409
  if (pending) {
410
410
  await (0, pending_1.clearPending)(msg.from);
411
+ await (0, relay_state_1.clearWaitingReported)(msg.from);
411
412
  }
412
413
  await (0, relay_state_1.markRelayed)([msg.id]);
413
414
  }
package/dist/cli.js CHANGED
@@ -24,7 +24,7 @@ function printHelp() {
24
24
  " marshell inbox [--json] [--wait <seconds>] [--from <name>]",
25
25
  " marshell history [--with <name>] [--limit <n>] [--json]",
26
26
  " marshell listen [--json] [--notify <cmd>] (deliver-only listener)",
27
- " marshell relay cron [--include-untracked] [--json]",
27
+ " marshell relay cron [--quiet] [--include-untracked] [--json]",
28
28
  " marshell pending list|clear [--peer <name>] [--json]",
29
29
  " marshell --help",
30
30
  "",
@@ -370,13 +370,14 @@ async function cmdInbox(args) {
370
370
  async function cmdRelay(args) {
371
371
  const sub = args[0];
372
372
  if (sub !== "cron") {
373
- printError("Usage: marshell relay cron [--include-untracked] [--json]");
373
+ printError("Usage: marshell relay cron [--quiet] [--include-untracked] [--json]");
374
374
  }
375
375
  const json = hasFlag(args, "--json");
376
+ const quiet = hasFlag(args, "--quiet");
376
377
  const pendingOnly = !hasFlag(args, "--include-untracked");
377
378
  const config = await (0, config_1.readConfig)();
378
379
  const networkUrl = (0, config_1.getNetworkUrl)(config);
379
- const result = await (0, relay_cron_1.runRelayCron)({ networkUrl, pendingOnly });
380
+ const result = await (0, relay_cron_1.runRelayCron)({ networkUrl, pendingOnly, quiet });
380
381
  if (json) {
381
382
  printJson({
382
383
  ...result,
@@ -384,6 +385,9 @@ async function cmdRelay(args) {
384
385
  });
385
386
  return;
386
387
  }
388
+ if (!result.notify) {
389
+ return;
390
+ }
387
391
  if (result.relayed.length > 0) {
388
392
  if (result.pending_count === 0 && !pendingOnly) {
389
393
  process.stdout.write(`${result.relayed.length} new inbound message(s):\n\n`);
@@ -391,7 +395,9 @@ async function cmdRelay(args) {
391
395
  process.stdout.write(`${(0, relay_cron_1.formatRelayOutput)(result.relayed)}\n`);
392
396
  return;
393
397
  }
394
- process.stdout.write(result.message ?? "Nothing new to relay.\n");
398
+ if (result.message) {
399
+ process.stdout.write(`${result.message}\n`);
400
+ }
395
401
  }
396
402
  async function cmdPending(args) {
397
403
  const json = hasFlag(args, "--json");
@@ -17,6 +17,7 @@ function formatRelayOutput(items) {
17
17
  return items.map(formatItem).join("\n\n---\n\n");
18
18
  }
19
19
  async function runRelayCron(options) {
20
+ const quiet = options.quiet ?? false;
20
21
  const pending = await (0, pending_1.listPending)();
21
22
  const pendingMap = new Map(pending.map((p) => [p.peer.toLowerCase(), p]));
22
23
  if (options.pendingOnly && pending.length === 0) {
@@ -24,7 +25,10 @@ async function runRelayCron(options) {
24
25
  pending_count: 0,
25
26
  relayed: [],
26
27
  skipped_relayed: 0,
27
- message: "No pending tracked sends — list is empty.",
28
+ message: quiet
29
+ ? undefined
30
+ : "No pending tracked sends — list is empty.",
31
+ notify: !quiet,
28
32
  };
29
33
  }
30
34
  const inbox = await (0, network_1.fetchInbox)(options.networkUrl, { peek: true });
@@ -55,17 +59,46 @@ async function runRelayCron(options) {
55
59
  }
56
60
  for (const peer of peersToClear) {
57
61
  await (0, pending_1.clearPending)(peer);
62
+ await (0, relay_state_1.clearWaitingReported)(peer);
63
+ }
64
+ if (items.length > 0) {
65
+ return {
66
+ pending_count: pending.length,
67
+ relayed: items,
68
+ skipped_relayed: skippedRelayed,
69
+ notify: true,
70
+ };
58
71
  }
59
72
  let message;
60
- if (items.length === 0 && pending.length > 0) {
61
- const names = pending.map((p) => p.peer).join(", ");
62
- message = `Waiting for replies from: ${names}`;
73
+ let notify = !quiet;
74
+ if (pending.length > 0) {
75
+ const waitingPeers = [];
76
+ for (const entry of pending) {
77
+ const shouldReport = await (0, relay_state_1.shouldReportWaiting)(entry.peer, entry.sentAt);
78
+ if (shouldReport) {
79
+ waitingPeers.push(entry.peer);
80
+ await (0, relay_state_1.markWaitingReported)(entry.peer, entry.sentAt);
81
+ }
82
+ }
83
+ if (waitingPeers.length > 0) {
84
+ message = `Waiting for replies from: ${waitingPeers.join(", ")}`;
85
+ notify = true;
86
+ }
87
+ else if (quiet) {
88
+ message = undefined;
89
+ notify = false;
90
+ }
91
+ else {
92
+ message = `Still waiting for: ${pending.map((p) => p.peer).join(", ")}`;
93
+ notify = true;
94
+ }
63
95
  }
64
96
  return {
65
97
  pending_count: pending.length,
66
- relayed: items,
98
+ relayed: [],
67
99
  skipped_relayed: skippedRelayed,
68
100
  message,
101
+ notify,
69
102
  };
70
103
  }
71
104
  function buildItem(msg, pending, kind) {
@@ -2,6 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.filterUnrelayed = filterUnrelayed;
4
4
  exports.markRelayed = markRelayed;
5
+ exports.shouldReportWaiting = shouldReportWaiting;
6
+ exports.markWaitingReported = markWaitingReported;
7
+ exports.clearWaitingReported = clearWaitingReported;
5
8
  const promises_1 = require("node:fs/promises");
6
9
  const node_path_1 = require("node:path");
7
10
  const node_os_1 = require("node:os");
@@ -9,21 +12,36 @@ const MAX_IDS = 500;
9
12
  function statePath() {
10
13
  return (0, node_path_1.join)((0, node_os_1.homedir)(), ".marshell", "relay-state.json");
11
14
  }
15
+ function emptyState() {
16
+ return {
17
+ relayedIds: [],
18
+ waitingReported: {},
19
+ updatedAt: new Date().toISOString(),
20
+ };
21
+ }
12
22
  async function readState() {
13
23
  try {
14
24
  const raw = await (0, promises_1.readFile)(statePath(), "utf8");
15
- return JSON.parse(raw);
25
+ const parsed = JSON.parse(raw);
26
+ return {
27
+ relayedIds: parsed.relayedIds ?? [],
28
+ waitingReported: parsed.waitingReported ?? {},
29
+ updatedAt: parsed.updatedAt ?? new Date().toISOString(),
30
+ };
16
31
  }
17
32
  catch (error) {
18
33
  const maybeCode = error.code;
19
34
  if (maybeCode === "ENOENT") {
20
- return { relayedIds: [], updatedAt: new Date().toISOString() };
35
+ return emptyState();
21
36
  }
22
37
  throw error;
23
38
  }
24
39
  }
25
40
  async function writeState(state) {
26
41
  await (0, promises_1.mkdir)((0, node_path_1.join)((0, node_os_1.homedir)(), ".marshell"), { recursive: true });
42
+ await writeStateFile(state);
43
+ }
44
+ async function writeStateFile(state) {
27
45
  await (0, promises_1.writeFile)(statePath(), `${JSON.stringify(state, null, 2)}\n`, "utf8");
28
46
  }
29
47
  async function filterUnrelayed(messages) {
@@ -37,8 +55,37 @@ async function markRelayed(ids) {
37
55
  const state = await readState();
38
56
  const merged = [...new Set([...state.relayedIds, ...ids])];
39
57
  const trimmed = merged.length > MAX_IDS ? merged.slice(merged.length - MAX_IDS) : merged;
40
- await writeState({
58
+ await writeStateFile({
59
+ ...state,
41
60
  relayedIds: trimmed,
42
61
  updatedAt: new Date().toISOString(),
43
62
  });
44
63
  }
64
+ /** True if we should tell the human we're waiting on this peer (once per pending). */
65
+ async function shouldReportWaiting(peer, pendingSentAt) {
66
+ const state = await readState();
67
+ const key = peer.toLowerCase();
68
+ return state.waitingReported[key] !== pendingSentAt;
69
+ }
70
+ async function markWaitingReported(peer, pendingSentAt) {
71
+ const state = await readState();
72
+ const key = peer.toLowerCase();
73
+ await writeStateFile({
74
+ ...state,
75
+ waitingReported: { ...state.waitingReported, [key]: pendingSentAt },
76
+ updatedAt: new Date().toISOString(),
77
+ });
78
+ }
79
+ async function clearWaitingReported(peer) {
80
+ const state = await readState();
81
+ const key = peer.toLowerCase();
82
+ if (!(key in state.waitingReported))
83
+ return;
84
+ const next = { ...state.waitingReported };
85
+ delete next[key];
86
+ await writeStateFile({
87
+ ...state,
88
+ waitingReported: next,
89
+ updatedAt: new Date().toISOString(),
90
+ });
91
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@marshell/cli",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Marshell CLI — join a subnet, discover agents, send messages",
5
5
  "license": "MIT",
6
6
  "type": "commonjs",