@sleep2agi/commhub-server 0.5.1 → 0.5.2-preview.0
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/package.json +1 -1
- package/src/index.ts +35 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sleep2agi/commhub-server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.2-preview.0",
|
|
4
4
|
"description": "CommHub Server \u2014 AI Agent communication hub with MCP protocol, multi-network isolation, user auth, and 18 MCP tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
package/src/index.ts
CHANGED
|
@@ -18,6 +18,24 @@ const SERVER_VERSION = (() => {
|
|
|
18
18
|
} catch { return "?"; }
|
|
19
19
|
})();
|
|
20
20
|
|
|
21
|
+
// In-memory log ring buffer — last N lines streamed via /api/server-logs.
|
|
22
|
+
// Wraps console.log/info/warn/error so EVERY existing log call lands here
|
|
23
|
+
// without source changes. Dashboard tails this buffer for "hub server log
|
|
24
|
+
// view" feature.
|
|
25
|
+
const LOG_RING_CAP = Number(process.env.COMMHUB_LOG_RING || 500);
|
|
26
|
+
type LogEntry = { ts: string; level: "log" | "info" | "warn" | "error"; line: string };
|
|
27
|
+
const logRing: LogEntry[] = [];
|
|
28
|
+
const _origConsole = { log: console.log.bind(console), info: console.info.bind(console), warn: console.warn.bind(console), error: console.error.bind(console) };
|
|
29
|
+
function pushLog(level: LogEntry["level"], args: any[]) {
|
|
30
|
+
const line = args.map(a => typeof a === "string" ? a : (() => { try { return JSON.stringify(a); } catch { return String(a); } })()).join(" ");
|
|
31
|
+
logRing.push({ ts: new Date().toISOString(), level, line: line.slice(0, 4000) });
|
|
32
|
+
if (logRing.length > LOG_RING_CAP) logRing.splice(0, logRing.length - LOG_RING_CAP);
|
|
33
|
+
}
|
|
34
|
+
console.log = (...args: any[]) => { pushLog("log", args); _origConsole.log(...args); };
|
|
35
|
+
console.info = (...args: any[]) => { pushLog("info", args); _origConsole.info(...args); };
|
|
36
|
+
console.warn = (...args: any[]) => { pushLog("warn", args); _origConsole.warn(...args); };
|
|
37
|
+
console.error = (...args: any[]) => { pushLog("error", args); _origConsole.error(...args); };
|
|
38
|
+
|
|
21
39
|
// ── Rate limiter (in-memory, per IP) ──
|
|
22
40
|
const rateLimits = new Map<string, { count: number; resetAt: number }>();
|
|
23
41
|
function checkRateLimit(ip: string, maxPerMinute = 60): boolean {
|
|
@@ -838,6 +856,23 @@ Bun.serve({
|
|
|
838
856
|
}));
|
|
839
857
|
}
|
|
840
858
|
|
|
859
|
+
// ── REST: server log tail (in-memory ring buffer, last LOG_RING_CAP lines) ──
|
|
860
|
+
// Admin-only because logs may include user names + task content.
|
|
861
|
+
if (url.pathname === "/api/server-logs") {
|
|
862
|
+
const token = req.headers.get("Authorization")?.replace("Bearer ", "") || url.searchParams.get("token");
|
|
863
|
+
if (!token) return withCors(req, Response.json({ ok: false, error: "auth required" }, { status: 401 }));
|
|
864
|
+
const resolved = resolveToken(token);
|
|
865
|
+
if (!resolved) return withCors(req, Response.json({ ok: false, error: "invalid token" }, { status: 401 }));
|
|
866
|
+
if (resolved.user.role !== "admin") return withCors(req, Response.json({ ok: false, error: "admin only" }, { status: 403 }));
|
|
867
|
+
const limit = Math.min(Number(url.searchParams.get("limit")) || 200, LOG_RING_CAP);
|
|
868
|
+
const since = url.searchParams.get("since"); // ISO timestamp; only return logs newer
|
|
869
|
+
let entries = logRing.slice(-limit);
|
|
870
|
+
if (since) entries = entries.filter(e => e.ts > since);
|
|
871
|
+
// Newest first
|
|
872
|
+
entries = entries.slice().reverse();
|
|
873
|
+
return withCors(req, Response.json({ ok: true, logs: entries, capacity: LOG_RING_CAP }));
|
|
874
|
+
}
|
|
875
|
+
|
|
841
876
|
// ── REST: audit log (V3) ──
|
|
842
877
|
if (url.pathname === "/api/audit-log") {
|
|
843
878
|
const token = req.headers.get("Authorization")?.replace("Bearer ", "") || url.searchParams.get("token");
|