agent-relay-server 0.4.14 → 0.4.16

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
@@ -111,7 +111,7 @@ The plugin or sidecar registers the agent and injects messaging context:
111
111
 
112
112
  ```
113
113
  Agent Relay active. Your agent ID: macmini2-cli-myproject-a1b2c3
114
- Relay URL: http://localhost:4850 | Server: 0.4.13 | Plugin: 0.4.13
114
+ Relay URL: http://localhost:4850 | Server: 0.4.16 | Plugin: 0.4.16
115
115
  Approval mode: open
116
116
  ```
117
117
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.4.14",
3
+ "version": "0.4.16",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
@@ -3,6 +3,7 @@
3
3
  const DEFAULT_COMPOSE = { from: "", to: "", body: "", channel: "", subject: "", claimable: false };
4
4
  const CLOSED_TASK_STATUSES = new Set(["done", "failed", "canceled"]);
5
5
  const STATUS_SORT_ORDER = { online: 0, idle: 1, busy: 2, offline: 3 };
6
+ const LIVE_REFRESH_MS = 5_000;
6
7
 
7
8
  function loadPref(key, fallback) {
8
9
  try {
@@ -32,6 +33,7 @@
32
33
  tasks: [],
33
34
  taskEvents: [],
34
35
  stats: {},
36
+ now: Date.now(),
35
37
  authToken: loadPref("authToken", ""),
36
38
 
37
39
  selectedAgent: "",
@@ -57,12 +59,19 @@
57
59
 
58
60
  chartInstances: {},
59
61
  _es: null,
60
- _statsTimer: null,
62
+ _clockTimer: null,
63
+ _refreshTimer: null,
64
+ _refreshInFlight: false,
61
65
  };
62
66
  }
63
67
 
64
68
  function watchPersistedPrefs(vm) {
65
69
  vm.$watch("showOffline", (value) => vm.save("showOffline", value));
70
+ vm.$watch("autoRefresh", (value) => {
71
+ vm.save("autoRefresh", value);
72
+ if (value) vm.startAutoRefresh();
73
+ else vm.stopAutoRefresh();
74
+ });
66
75
  vm.$watch("agentSort", (value) => vm.save("agentSort", value));
67
76
  vm.$watch("agentSortDir", (value) => vm.save("agentSortDir", value));
68
77
  vm.$watch("agentStatusFilter", (value) => vm.save("agentStatusFilter", value));
@@ -95,12 +104,31 @@
95
104
  else vm.tasks.unshift(task);
96
105
  }
97
106
 
107
+ function syncAgentStats(vm) {
108
+ vm.stats.agents = vm.agents.length;
109
+ vm.stats.online = vm.agents.filter((agent) => agent.status !== "offline").length;
110
+ }
111
+
112
+ function syncMessageStats(vm, msg) {
113
+ vm.stats.messages = (vm.stats.messages ?? 0) + 1;
114
+ const createdAt = new Date(msg.createdAt || Date.now()).getTime();
115
+ if (Number.isFinite(createdAt) && Date.now() - createdAt <= 86_400_000) {
116
+ vm.stats.messagesLast24h = (vm.stats.messagesLast24h ?? 0) + 1;
117
+ }
118
+ }
119
+
120
+ function refreshChartsIfVisible(vm) {
121
+ if (vm.view !== "analytics") return;
122
+ vm.$nextTick(() => vm.renderCharts());
123
+ }
124
+
98
125
  function createLifecycleMethods() {
99
126
  return {
100
127
  async init() {
101
128
  await this.refresh();
102
129
  this.connectSSE();
103
- this._statsTimer = setInterval(() => this.fetchStats(), 30_000);
130
+ this.startClock();
131
+ this.startAutoRefresh();
104
132
  watchPersistedPrefs(this);
105
133
  },
106
134
 
@@ -113,6 +141,25 @@
113
141
  if (view === "messages") this.fetchMessages();
114
142
  if (view === "tasks") this.fetchTasks();
115
143
  },
144
+
145
+ startClock() {
146
+ if (this._clockTimer) return;
147
+ this._clockTimer = setInterval(() => {
148
+ this.now = Date.now();
149
+ }, 1_000);
150
+ },
151
+
152
+ startAutoRefresh() {
153
+ this.stopAutoRefresh();
154
+ if (!this.autoRefresh) return;
155
+ this._refreshTimer = setInterval(() => this.refreshLiveData(), LIVE_REFRESH_MS);
156
+ },
157
+
158
+ stopAutoRefresh() {
159
+ if (!this._refreshTimer) return;
160
+ clearInterval(this._refreshTimer);
161
+ this._refreshTimer = null;
162
+ },
116
163
  };
117
164
  }
118
165
 
@@ -156,17 +203,22 @@
156
203
 
157
204
  vm.messages.push(msg);
158
205
  if (vm.messages.length > 200) vm.messages.shift();
159
- vm.stats.messages = (vm.stats.messages ?? 0) + 1;
206
+ syncMessageStats(vm, msg);
207
+ refreshChartsIfVisible(vm);
160
208
  }
161
209
 
162
210
  function handleAgentStatus(vm, agent) {
163
211
  upsertById(vm.agents, agent);
164
212
  vm.agentsById[agent.id] = agent;
213
+ syncAgentStats(vm);
214
+ refreshChartsIfVisible(vm);
165
215
  }
166
216
 
167
217
  function handleAgentRemoved(vm, data) {
168
218
  vm.agents = vm.agents.filter((agent) => agent.id !== data.id);
169
219
  delete vm.agentsById[data.id];
220
+ syncAgentStats(vm);
221
+ refreshChartsIfVisible(vm);
170
222
  }
171
223
 
172
224
  function handleMessageClaimed(vm, data) {
@@ -216,6 +268,17 @@
216
268
  await Promise.all([this.fetchStats(), this.fetchAgents(), this.fetchMessages(), this.fetchTasks()]);
217
269
  },
218
270
 
271
+ async refreshLiveData() {
272
+ if (this._refreshInFlight || this.authNeeded) return;
273
+ this._refreshInFlight = true;
274
+ try {
275
+ await this.refresh();
276
+ refreshChartsIfVisible(this);
277
+ } finally {
278
+ this._refreshInFlight = false;
279
+ }
280
+ },
281
+
219
282
  async fetchStats() {
220
283
  try {
221
284
  this.stats = await this.api("GET", "/stats");
@@ -392,7 +455,7 @@
392
455
  const lastSeenMs = new Date(agent.lastSeen).getTime();
393
456
  if (!Number.isFinite(lastSeenMs)) return "Trying to reconnect...";
394
457
 
395
- const ageSec = Math.max(0, (Date.now() - lastSeenMs) / 1000);
458
+ const ageSec = Math.max(0, ((this.now || Date.now()) - lastSeenMs) / 1000);
396
459
  return ageSec <= 45 ? "Starting up..." : "Trying to reconnect...";
397
460
  }
398
461
 
@@ -401,7 +464,7 @@
401
464
  const ts = new Date(iso).getTime();
402
465
  if (!Number.isFinite(ts)) return "";
403
466
 
404
- const diff = Math.max(0, (Date.now() - ts) / 1000);
467
+ const diff = Math.max(0, ((this.now || Date.now()) - ts) / 1000);
405
468
  if (diff < 60) return Math.floor(diff) + "s ago";
406
469
  if (diff < 3600) return Math.floor(diff / 60) + "m ago";
407
470
  if (diff < 86400) return Math.floor(diff / 3600) + "h ago";
package/public/index.html CHANGED
@@ -109,6 +109,12 @@
109
109
  <input type="checkbox" class="form-check-input" x-model="showOffline">
110
110
  <span class="form-check-label small">Show offline</span>
111
111
  </label>
112
+ </div>
113
+ <div class="d-flex align-items-center gap-2 mb-2">
114
+ <label class="form-check form-switch mb-0">
115
+ <input type="checkbox" class="form-check-input" x-model="autoRefresh">
116
+ <span class="form-check-label small">Auto refresh</span>
117
+ </label>
112
118
  </div>
113
119
  <div class="text-muted small" x-show="stats.version" x-text="'v' + stats.version"></div>
114
120
  </div>
@@ -389,7 +395,7 @@
389
395
  <div class="ms-auto d-flex gap-2 align-items-center flex-wrap">
390
396
  <select class="form-select form-select-sm" style="width: auto; min-width: 160px" x-model="selectedAgent" @change="fetchMessages()">
391
397
  <option value="">All agents</option>
392
- <template x-for="a in agents" :key="a.id">
398
+ <template x-for="a in composeAgents" :key="a.id">
393
399
  <option :value="a.id" x-text="displayName(a) + ' [' + a.id.slice(-6) + ']'"></option>
394
400
  </template>
395
401
  </select>