somratpro Copilot commited on
Commit
e05bdf9
·
1 Parent(s): c53bf2f

feat: add plugin status retrieval and display in dashboard

Browse files
Files changed (1) hide show
  1. health-server.js +83 -4
health-server.js CHANGED
@@ -54,6 +54,56 @@ function getKeepaliveStatus() {
54
  return null;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  function getInviteUrl() {
58
  try {
59
  if (fs.existsSync(INVITE_URL_FILE)) {
@@ -138,6 +188,17 @@ function renderDashboard(data) {
138
  ? escapeHtml(data.sync.message)
139
  : "No status yet";
140
 
 
 
 
 
 
 
 
 
 
 
 
141
  const keepaliveConfigured = data.keepalive?.configured === true;
142
  const keepaliveStatus = String(
143
  data.keepalive?.status ||
@@ -156,6 +217,9 @@ function renderDashboard(data) {
156
 
157
  const inviteUrl = getInviteUrl();
158
 
 
 
 
159
  const tiles = [
160
  renderTile({
161
  title: "Paperclip Core",
@@ -183,6 +247,19 @@ function renderDashboard(data) {
183
  value: toneBadge(syncStatus.toUpperCase(), syncTone),
184
  detail: backupDetail,
185
  tone: syncTone,
 
 
 
 
 
 
 
 
 
 
 
 
 
186
  }),
187
  renderTile({
188
  title: "Keep Awake",
@@ -243,7 +320,7 @@ function renderDashboard(data) {
243
  <body>
244
  <main>
245
  <header>
246
- <h1>🧬 HuggingClip</h1>
247
  <div class="subtitle">Paperclip Orchestrator Dashboard</div>
248
  </header>
249
  ${
@@ -271,6 +348,7 @@ const server = http.createServer(async (req, res) => {
271
 
272
  if (pathname === "/health") {
273
  const appReady = await probeAppHealth();
 
274
  res.writeHead(appReady ? 200 : 503, { "Content-Type": "application/json" });
275
  return res.end(
276
  JSON.stringify({
@@ -278,12 +356,14 @@ const server = http.createServer(async (req, res) => {
278
  uptime: formatUptime(Date.now() - startTime),
279
  sync: getSyncStatus(),
280
  keepalive: getKeepaliveStatus(),
 
281
  }),
282
  );
283
  }
284
 
285
  if (pathname === "/" || pathname === "/dashboard") {
286
  const appReady = await probeAppHealth();
 
287
  res.writeHead(200, { "Content-Type": "text/html" });
288
  return res.end(
289
  renderDashboard({
@@ -291,6 +371,7 @@ const server = http.createServer(async (req, res) => {
291
  appReady,
292
  sync: getSyncStatus(),
293
  keepalive: getKeepaliveStatus(),
 
294
  }),
295
  );
296
  }
@@ -358,7 +439,5 @@ server.on("upgrade", (req, socket, head) => {
358
  server.timeout = 0;
359
  server.keepAliveTimeout = 65000;
360
  server.listen(PORT, "0.0.0.0", () =>
361
- console.log(
362
- `🧬 HuggingClip Dashboard on ${PORT} -> Paperclip on ${APP_PORT}`,
363
- ),
364
  );
 
54
  return null;
55
  }
56
 
57
+ async function getPluginStatus(timeoutMs = 1500) {
58
+ // Try several possible Paperclip API endpoints to list plugins/tools
59
+ const paths = ["/api/plugins", "/api/tools", "/api/plugins/list"];
60
+ for (const p of paths) {
61
+ try {
62
+ const result = await new Promise((resolve) => {
63
+ const req = http.get(
64
+ { hostname: APP_HOST, port: APP_PORT, path: p, timeout: timeoutMs },
65
+ (res) => {
66
+ let body = "";
67
+ res.on("data", (c) => (body += c));
68
+ res.on("end", () => resolve({ statusCode: res.statusCode, body }));
69
+ },
70
+ );
71
+ req.on("timeout", () => {
72
+ req.destroy();
73
+ resolve(null);
74
+ });
75
+ req.on("error", () => resolve(null));
76
+ });
77
+
78
+ if (!result || !result.body) continue;
79
+ if (result.statusCode >= 400) continue;
80
+
81
+ try {
82
+ const parsed = JSON.parse(result.body);
83
+ let list = [];
84
+ if (Array.isArray(parsed)) list = parsed;
85
+ else if (parsed.plugins && Array.isArray(parsed.plugins))
86
+ list = parsed.plugins;
87
+ else if (parsed.tools && Array.isArray(parsed.tools))
88
+ list = parsed.tools;
89
+ else if (parsed.items && Array.isArray(parsed.items))
90
+ list = parsed.items;
91
+
92
+ // Normalize list to array of names/ids
93
+ const names = list.map((it) =>
94
+ typeof it === "string" ? it : it.name || it.id || JSON.stringify(it),
95
+ );
96
+ return { count: names.length, list: names };
97
+ } catch (e) {
98
+ continue;
99
+ }
100
+ } catch (e) {
101
+ continue;
102
+ }
103
+ }
104
+ return null;
105
+ }
106
+
107
  function getInviteUrl() {
108
  try {
109
  if (fs.existsSync(INVITE_URL_FILE)) {
 
188
  ? escapeHtml(data.sync.message)
189
  : "No status yet";
190
 
191
+ // Show a concise status meta for the Backup tile (last sync time or sync count)
192
+ const backupMeta = (() => {
193
+ if (data.sync?.last_sync_time) {
194
+ return `Last sync: <code>${escapeHtml(data.sync.last_sync_time)}</code>`;
195
+ }
196
+ if (typeof data.sync?.sync_count === "number") {
197
+ return `Syncs: ${String(data.sync.sync_count)}`;
198
+ }
199
+ return "";
200
+ })();
201
+
202
  const keepaliveConfigured = data.keepalive?.configured === true;
203
  const keepaliveStatus = String(
204
  data.keepalive?.status ||
 
217
 
218
  const inviteUrl = getInviteUrl();
219
 
220
+ const pluginCount = Number(data.plugins?.count || 0);
221
+ const pluginList = Array.isArray(data.plugins?.list) ? data.plugins.list : [];
222
+
223
  const tiles = [
224
  renderTile({
225
  title: "Paperclip Core",
 
247
  value: toneBadge(syncStatus.toUpperCase(), syncTone),
248
  detail: backupDetail,
249
  tone: syncTone,
250
+ meta: backupMeta,
251
+ }),
252
+ renderTile({
253
+ title: "Plugins",
254
+ value: toneBadge(
255
+ pluginCount ? String(pluginCount) : "0",
256
+ pluginCount ? "ok" : "neutral",
257
+ ),
258
+ detail: pluginCount
259
+ ? escapeHtml(pluginList.slice(0, 6).join(", "))
260
+ : "No plugins loaded",
261
+ meta: pluginCount > 6 ? `+${pluginCount - 6} more` : "",
262
+ tone: pluginCount ? "ok" : "neutral",
263
  }),
264
  renderTile({
265
  title: "Keep Awake",
 
320
  <body>
321
  <main>
322
  <header>
323
+ <h1>HuggingClip</h1>
324
  <div class="subtitle">Paperclip Orchestrator Dashboard</div>
325
  </header>
326
  ${
 
348
 
349
  if (pathname === "/health") {
350
  const appReady = await probeAppHealth();
351
+ const plugins = await getPluginStatus();
352
  res.writeHead(appReady ? 200 : 503, { "Content-Type": "application/json" });
353
  return res.end(
354
  JSON.stringify({
 
356
  uptime: formatUptime(Date.now() - startTime),
357
  sync: getSyncStatus(),
358
  keepalive: getKeepaliveStatus(),
359
+ plugins: plugins,
360
  }),
361
  );
362
  }
363
 
364
  if (pathname === "/" || pathname === "/dashboard") {
365
  const appReady = await probeAppHealth();
366
+ const plugins = await getPluginStatus();
367
  res.writeHead(200, { "Content-Type": "text/html" });
368
  return res.end(
369
  renderDashboard({
 
371
  appReady,
372
  sync: getSyncStatus(),
373
  keepalive: getKeepaliveStatus(),
374
+ plugins: plugins,
375
  }),
376
  );
377
  }
 
439
  server.timeout = 0;
440
  server.keepAliveTimeout = 65000;
441
  server.listen(PORT, "0.0.0.0", () =>
442
+ console.log(`HuggingClip Dashboard on ${PORT} -> Paperclip on ${APP_PORT}`),
 
 
443
  );