agent-room 0.2.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/README.md +446 -0
- package/dist/core/connection-manager.d.ts +66 -0
- package/dist/core/connection-manager.d.ts.map +1 -0
- package/dist/core/connection-manager.js +205 -0
- package/dist/core/connection-manager.js.map +1 -0
- package/dist/core/logger.d.ts +99 -0
- package/dist/core/logger.d.ts.map +1 -0
- package/dist/core/logger.js +204 -0
- package/dist/core/logger.js.map +1 -0
- package/dist/core/message-buffer.d.ts +42 -0
- package/dist/core/message-buffer.d.ts.map +1 -0
- package/dist/core/message-buffer.js +99 -0
- package/dist/core/message-buffer.js.map +1 -0
- package/dist/core/notification-engine.d.ts +38 -0
- package/dist/core/notification-engine.d.ts.map +1 -0
- package/dist/core/notification-engine.js +78 -0
- package/dist/core/notification-engine.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/dist/protocols/adapter-interface.d.ts +6 -0
- package/dist/protocols/adapter-interface.d.ts.map +1 -0
- package/dist/protocols/adapter-interface.js +6 -0
- package/dist/protocols/adapter-interface.js.map +1 -0
- package/dist/protocols/sse-adapter.d.ts +26 -0
- package/dist/protocols/sse-adapter.d.ts.map +1 -0
- package/dist/protocols/sse-adapter.js +138 -0
- package/dist/protocols/sse-adapter.js.map +1 -0
- package/dist/protocols/ws-adapter.d.ts +26 -0
- package/dist/protocols/ws-adapter.d.ts.map +1 -0
- package/dist/protocols/ws-adapter.js +154 -0
- package/dist/protocols/ws-adapter.js.map +1 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +903 -0
- package/dist/server.js.map +1 -0
- package/dist/service/cli.d.ts +29 -0
- package/dist/service/cli.d.ts.map +1 -0
- package/dist/service/cli.js +479 -0
- package/dist/service/cli.js.map +1 -0
- package/dist/service/http-api.d.ts +29 -0
- package/dist/service/http-api.d.ts.map +1 -0
- package/dist/service/http-api.js +122 -0
- package/dist/service/http-api.js.map +1 -0
- package/dist/service/index.d.ts +22 -0
- package/dist/service/index.d.ts.map +1 -0
- package/dist/service/index.js +80 -0
- package/dist/service/index.js.map +1 -0
- package/dist/service/protocol.d.ts +46 -0
- package/dist/service/protocol.d.ts.map +1 -0
- package/dist/service/protocol.js +81 -0
- package/dist/service/protocol.js.map +1 -0
- package/dist/service/room-manager.d.ts +88 -0
- package/dist/service/room-manager.d.ts.map +1 -0
- package/dist/service/room-manager.js +237 -0
- package/dist/service/room-manager.js.map +1 -0
- package/dist/service/test.d.ts +18 -0
- package/dist/service/test.d.ts.map +1 -0
- package/dist/service/test.js +260 -0
- package/dist/service/test.js.map +1 -0
- package/dist/service/user-manager.d.ts +68 -0
- package/dist/service/user-manager.d.ts.map +1 -0
- package/dist/service/user-manager.js +111 -0
- package/dist/service/user-manager.js.map +1 -0
- package/dist/service/ws-server.d.ts +34 -0
- package/dist/service/ws-server.d.ts.map +1 -0
- package/dist/service/ws-server.js +281 -0
- package/dist/service/ws-server.js.map +1 -0
- package/dist/test/echo-server.d.ts +12 -0
- package/dist/test/echo-server.d.ts.map +1 -0
- package/dist/test/echo-server.js +66 -0
- package/dist/test/echo-server.js.map +1 -0
- package/dist/test/integration-test.d.ts +13 -0
- package/dist/test/integration-test.d.ts.map +1 -0
- package/dist/test/integration-test.js +154 -0
- package/dist/test/integration-test.js.map +1 -0
- package/dist/test/reactive-test.d.ts +16 -0
- package/dist/test/reactive-test.d.ts.map +1 -0
- package/dist/test/reactive-test.js +128 -0
- package/dist/test/reactive-test.js.map +1 -0
- package/dist/test/send-hello.d.ts +2 -0
- package/dist/test/send-hello.d.ts.map +1 -0
- package/dist/test/send-hello.js +59 -0
- package/dist/test/send-hello.js.map +1 -0
- package/dist/test/service-mcp-test.d.ts +9 -0
- package/dist/test/service-mcp-test.d.ts.map +1 -0
- package/dist/test/service-mcp-test.js +127 -0
- package/dist/test/service-mcp-test.js.map +1 -0
- package/dist/types.d.ts +55 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +15 -0
- package/dist/types.js.map +1 -0
- package/package.json +76 -0
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
import { randomUUID } from "crypto";
|
|
3
|
+
import { WsAdapter } from "../protocols/ws-adapter.js";
|
|
4
|
+
import { SseAdapter } from "../protocols/sse-adapter.js";
|
|
5
|
+
import { Logger } from "./logger.js";
|
|
6
|
+
const log = Logger.create("connection-mgr");
|
|
7
|
+
/**
|
|
8
|
+
* Manages multiple concurrent stream connections, each identified by a channelId.
|
|
9
|
+
* Delegates actual protocol handling to WsAdapter / SseAdapter.
|
|
10
|
+
*/
|
|
11
|
+
export class ConnectionManager extends EventEmitter {
|
|
12
|
+
_connections = new Map();
|
|
13
|
+
// ─── Public API ────────────────────────────────────────────────────
|
|
14
|
+
/**
|
|
15
|
+
* Open a new stream connection.
|
|
16
|
+
* Returns the channelId (auto-generated if not provided).
|
|
17
|
+
*/
|
|
18
|
+
async connect(url, protocol, channelId, options) {
|
|
19
|
+
const id = channelId ?? randomUUID().slice(0, 8);
|
|
20
|
+
if (this._connections.has(id)) {
|
|
21
|
+
throw new Error(`Channel "${id}" already exists. Disconnect first or use a different ID.`);
|
|
22
|
+
}
|
|
23
|
+
const done = log.time("connect", { channelId: id, url, protocol });
|
|
24
|
+
const adapter = this._createAdapter(protocol);
|
|
25
|
+
const conn = {
|
|
26
|
+
channelId: id,
|
|
27
|
+
url,
|
|
28
|
+
protocol,
|
|
29
|
+
adapter,
|
|
30
|
+
messageCount: 0,
|
|
31
|
+
};
|
|
32
|
+
// Wire adapter events before connecting
|
|
33
|
+
adapter.on("connected", () => {
|
|
34
|
+
conn.connectedAt = new Date().toISOString();
|
|
35
|
+
this._emitStateChange(conn);
|
|
36
|
+
});
|
|
37
|
+
adapter.on("message", (data) => {
|
|
38
|
+
conn.messageCount++;
|
|
39
|
+
Logger.metrics.increment("messages.received");
|
|
40
|
+
this.emit("message", id, data);
|
|
41
|
+
this._emitStateChange(conn);
|
|
42
|
+
});
|
|
43
|
+
adapter.on("disconnected", (reason) => {
|
|
44
|
+
log.warn("channel disconnected", { channelId: id, reason });
|
|
45
|
+
Logger.metrics.increment("connections.disconnected");
|
|
46
|
+
this._emitStateChange(conn);
|
|
47
|
+
});
|
|
48
|
+
adapter.on("reconnecting", (attempt) => {
|
|
49
|
+
log.info("channel reconnecting", { channelId: id, attempt });
|
|
50
|
+
Logger.metrics.increment("connections.reconnect_attempts");
|
|
51
|
+
this._emitStateChange(conn);
|
|
52
|
+
});
|
|
53
|
+
adapter.on("error", (err) => {
|
|
54
|
+
log.error("channel error", { channelId: id }, err);
|
|
55
|
+
Logger.metrics.increment("errors.connection");
|
|
56
|
+
this._emitStateChange(conn);
|
|
57
|
+
});
|
|
58
|
+
this._connections.set(id, conn);
|
|
59
|
+
try {
|
|
60
|
+
await adapter.connect(url, options);
|
|
61
|
+
done();
|
|
62
|
+
Logger.metrics.increment("connections.opened");
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
// If initial connect fails, clean up
|
|
66
|
+
this._connections.delete(id);
|
|
67
|
+
log.error("connect failed", { channelId: id, url, protocol }, err);
|
|
68
|
+
Logger.metrics.increment("errors.connect_failed");
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
return id;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Disconnect a channel.
|
|
75
|
+
*/
|
|
76
|
+
async disconnect(channelId) {
|
|
77
|
+
const conn = this._connections.get(channelId);
|
|
78
|
+
if (!conn) {
|
|
79
|
+
throw new Error(`Channel "${channelId}" not found`);
|
|
80
|
+
}
|
|
81
|
+
log.debug("disconnecting channel", { channelId });
|
|
82
|
+
await conn.adapter.disconnect();
|
|
83
|
+
this._connections.delete(channelId);
|
|
84
|
+
Logger.metrics.increment("connections.closed");
|
|
85
|
+
log.info("channel disconnected (intentional)", { channelId });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Disconnect all channels.
|
|
89
|
+
*/
|
|
90
|
+
async disconnectAll() {
|
|
91
|
+
const ids = [...this._connections.keys()];
|
|
92
|
+
await Promise.allSettled(ids.map((id) => this.disconnect(id)));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Send data through a specific channel.
|
|
96
|
+
*/
|
|
97
|
+
async send(channelId, data) {
|
|
98
|
+
const conn = this._connections.get(channelId);
|
|
99
|
+
if (!conn) {
|
|
100
|
+
throw new Error(`Channel "${channelId}" not found`);
|
|
101
|
+
}
|
|
102
|
+
const done = log.time("send", { channelId });
|
|
103
|
+
try {
|
|
104
|
+
await conn.adapter.send(data);
|
|
105
|
+
done();
|
|
106
|
+
Logger.metrics.increment("messages.sent");
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
log.error("send failed", { channelId }, err);
|
|
110
|
+
Logger.metrics.increment("errors.send_failed");
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Get info for a specific channel.
|
|
116
|
+
*/
|
|
117
|
+
getConnectionInfo(channelId) {
|
|
118
|
+
const conn = this._connections.get(channelId);
|
|
119
|
+
if (!conn)
|
|
120
|
+
return undefined;
|
|
121
|
+
return this._toInfo(conn);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* List all active connections.
|
|
125
|
+
*/
|
|
126
|
+
listConnections() {
|
|
127
|
+
return [...this._connections.values()].map((c) => this._toInfo(c));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if a channel exists.
|
|
131
|
+
*/
|
|
132
|
+
has(channelId) {
|
|
133
|
+
return this._connections.has(channelId);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Wait for the next message on a channel, with optional substring filter.
|
|
137
|
+
* Returns a Promise that resolves with the raw message data, or rejects on timeout.
|
|
138
|
+
*
|
|
139
|
+
* @param channelId The channel to listen on
|
|
140
|
+
* @param timeoutMs Max time to wait (default 30000ms)
|
|
141
|
+
* @param filter Optional substring — only messages containing this string will resolve the promise
|
|
142
|
+
* @returns The raw message string that matched
|
|
143
|
+
*/
|
|
144
|
+
waitForMessage(channelId, timeoutMs = 30_000, filter) {
|
|
145
|
+
if (!this._connections.has(channelId)) {
|
|
146
|
+
return Promise.reject(new Error(`Channel "${channelId}" not found`));
|
|
147
|
+
}
|
|
148
|
+
return new Promise((resolve) => {
|
|
149
|
+
let settled = false;
|
|
150
|
+
const onMessage = (id, data) => {
|
|
151
|
+
if (settled)
|
|
152
|
+
return;
|
|
153
|
+
if (id !== channelId)
|
|
154
|
+
return;
|
|
155
|
+
if (filter && !data.includes(filter))
|
|
156
|
+
return;
|
|
157
|
+
settled = true;
|
|
158
|
+
clearTimeout(timer);
|
|
159
|
+
this.removeListener("message", onMessage);
|
|
160
|
+
resolve({ data, timedOut: false });
|
|
161
|
+
};
|
|
162
|
+
const timer = setTimeout(() => {
|
|
163
|
+
if (settled)
|
|
164
|
+
return;
|
|
165
|
+
settled = true;
|
|
166
|
+
this.removeListener("message", onMessage);
|
|
167
|
+
resolve({ data: null, timedOut: true });
|
|
168
|
+
}, timeoutMs);
|
|
169
|
+
// Use the base EventEmitter 'on' to avoid typed overload issues
|
|
170
|
+
super.on("message", onMessage);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
// ─── Internal ────────────────────────────────────────────────────────
|
|
174
|
+
_createAdapter(protocol) {
|
|
175
|
+
switch (protocol) {
|
|
176
|
+
case "ws":
|
|
177
|
+
return new WsAdapter();
|
|
178
|
+
case "sse":
|
|
179
|
+
return new SseAdapter();
|
|
180
|
+
default:
|
|
181
|
+
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
_toInfo(conn) {
|
|
185
|
+
return {
|
|
186
|
+
channelId: conn.channelId,
|
|
187
|
+
url: conn.url,
|
|
188
|
+
protocol: conn.protocol,
|
|
189
|
+
state: conn.adapter.state,
|
|
190
|
+
connectedAt: conn.connectedAt,
|
|
191
|
+
messageCount: conn.messageCount,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
_emitStateChange(conn) {
|
|
195
|
+
this.emit("stateChange", conn.channelId, this._toInfo(conn));
|
|
196
|
+
}
|
|
197
|
+
// Typed event helpers
|
|
198
|
+
on(event, listener) {
|
|
199
|
+
return super.on(event, listener);
|
|
200
|
+
}
|
|
201
|
+
emit(event, ...args) {
|
|
202
|
+
return super.emit(event, ...args);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=connection-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../../src/core/connection-manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logger — lightweight, zero-dependency logging for AgentRoom.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Log levels: debug, info, warn, error
|
|
6
|
+
* - Structured context (key-value pairs attached to every log line)
|
|
7
|
+
* - Performance timing via `time()` / `timeEnd()` (tracks duration in ms)
|
|
8
|
+
* - Error tracking with stack traces
|
|
9
|
+
* - Metrics: counters and histograms for aggregate stats
|
|
10
|
+
* - Outputs to stderr (stdout reserved for MCP protocol)
|
|
11
|
+
* - Configurable via LOG_LEVEL env var (default: "info")
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const log = Logger.create("ws-adapter");
|
|
15
|
+
* log.info("connected", { url, channelId });
|
|
16
|
+
* log.error("connection failed", { url, err });
|
|
17
|
+
*
|
|
18
|
+
* const done = log.time("connect");
|
|
19
|
+
* await doConnect();
|
|
20
|
+
* done(); // logs: [ws-adapter] connect completed in 42ms
|
|
21
|
+
*
|
|
22
|
+
* Logger.metrics.increment("messages.received");
|
|
23
|
+
* Logger.metrics.record("connect.duration_ms", 42);
|
|
24
|
+
* Logger.metrics.snapshot(); // returns all metrics
|
|
25
|
+
*/
|
|
26
|
+
export type LogLevel = "debug" | "info" | "warn" | "error";
|
|
27
|
+
export interface LogEntry {
|
|
28
|
+
timestamp: string;
|
|
29
|
+
level: LogLevel;
|
|
30
|
+
component: string;
|
|
31
|
+
message: string;
|
|
32
|
+
context?: Record<string, unknown>;
|
|
33
|
+
error?: {
|
|
34
|
+
message: string;
|
|
35
|
+
stack?: string;
|
|
36
|
+
};
|
|
37
|
+
duration_ms?: number;
|
|
38
|
+
}
|
|
39
|
+
export declare class MetricsCollector {
|
|
40
|
+
private _counters;
|
|
41
|
+
private _histograms;
|
|
42
|
+
private _startedAt;
|
|
43
|
+
/** Increment a counter by 1 (or a custom amount). */
|
|
44
|
+
increment(name: string, amount?: number): void;
|
|
45
|
+
/** Record a numeric sample into a histogram (e.g. latency, duration). */
|
|
46
|
+
record(name: string, value: number): void;
|
|
47
|
+
/** Get current value of a counter. */
|
|
48
|
+
getCounter(name: string): number;
|
|
49
|
+
/** Get summary statistics for a histogram. */
|
|
50
|
+
getHistogram(name: string): {
|
|
51
|
+
count: number;
|
|
52
|
+
min: number;
|
|
53
|
+
max: number;
|
|
54
|
+
avg: number;
|
|
55
|
+
p50: number;
|
|
56
|
+
p95: number;
|
|
57
|
+
p99: number;
|
|
58
|
+
} | null;
|
|
59
|
+
/** Return a full snapshot of all metrics. */
|
|
60
|
+
snapshot(): {
|
|
61
|
+
uptime_ms: number;
|
|
62
|
+
counters: Record<string, number>;
|
|
63
|
+
histograms: Record<string, ReturnType<MetricsCollector["getHistogram"]>>;
|
|
64
|
+
};
|
|
65
|
+
/** Reset all metrics. */
|
|
66
|
+
reset(): void;
|
|
67
|
+
}
|
|
68
|
+
export declare class Logger {
|
|
69
|
+
/** Shared metrics collector (singleton). */
|
|
70
|
+
static readonly metrics: MetricsCollector;
|
|
71
|
+
/** Current global log level. Override with LOG_LEVEL env var. */
|
|
72
|
+
private static _level;
|
|
73
|
+
/** JSON output mode (LOG_FORMAT=json). Default: human-readable. */
|
|
74
|
+
private static _jsonMode;
|
|
75
|
+
private _component;
|
|
76
|
+
private constructor();
|
|
77
|
+
/** Create a logger for a specific component. */
|
|
78
|
+
static create(component: string): Logger;
|
|
79
|
+
/** Set the global log level at runtime. */
|
|
80
|
+
static setLevel(level: LogLevel): void;
|
|
81
|
+
/** Get the global log level. */
|
|
82
|
+
static getLevel(): LogLevel;
|
|
83
|
+
debug(message: string, context?: Record<string, unknown>): void;
|
|
84
|
+
info(message: string, context?: Record<string, unknown>): void;
|
|
85
|
+
warn(message: string, context?: Record<string, unknown>): void;
|
|
86
|
+
error(message: string, context?: Record<string, unknown>, err?: Error | unknown): void;
|
|
87
|
+
/**
|
|
88
|
+
* Start a performance timer. Returns a `done()` function.
|
|
89
|
+
* When called, `done()` logs the elapsed time and records it in metrics.
|
|
90
|
+
*
|
|
91
|
+
* @param operation Name of the operation (e.g. "ws.connect", "room.join")
|
|
92
|
+
* @param context Optional extra context
|
|
93
|
+
* @returns A `done(extraContext?)` function
|
|
94
|
+
*/
|
|
95
|
+
time(operation: string, context?: Record<string, unknown>): (extraContext?: Record<string, unknown>) => number;
|
|
96
|
+
private _log;
|
|
97
|
+
private _formatHuman;
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured Logger — lightweight, zero-dependency logging for AgentRoom.
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Log levels: debug, info, warn, error
|
|
6
|
+
* - Structured context (key-value pairs attached to every log line)
|
|
7
|
+
* - Performance timing via `time()` / `timeEnd()` (tracks duration in ms)
|
|
8
|
+
* - Error tracking with stack traces
|
|
9
|
+
* - Metrics: counters and histograms for aggregate stats
|
|
10
|
+
* - Outputs to stderr (stdout reserved for MCP protocol)
|
|
11
|
+
* - Configurable via LOG_LEVEL env var (default: "info")
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* const log = Logger.create("ws-adapter");
|
|
15
|
+
* log.info("connected", { url, channelId });
|
|
16
|
+
* log.error("connection failed", { url, err });
|
|
17
|
+
*
|
|
18
|
+
* const done = log.time("connect");
|
|
19
|
+
* await doConnect();
|
|
20
|
+
* done(); // logs: [ws-adapter] connect completed in 42ms
|
|
21
|
+
*
|
|
22
|
+
* Logger.metrics.increment("messages.received");
|
|
23
|
+
* Logger.metrics.record("connect.duration_ms", 42);
|
|
24
|
+
* Logger.metrics.snapshot(); // returns all metrics
|
|
25
|
+
*/
|
|
26
|
+
// ─── Level Ordering ───────────────────────────────────────────────────
|
|
27
|
+
const LEVEL_ORDER = {
|
|
28
|
+
debug: 0,
|
|
29
|
+
info: 1,
|
|
30
|
+
warn: 2,
|
|
31
|
+
error: 3,
|
|
32
|
+
};
|
|
33
|
+
// ─── Metrics Collector ────────────────────────────────────────────────
|
|
34
|
+
export class MetricsCollector {
|
|
35
|
+
_counters = new Map();
|
|
36
|
+
_histograms = new Map();
|
|
37
|
+
_startedAt = Date.now();
|
|
38
|
+
/** Increment a counter by 1 (or a custom amount). */
|
|
39
|
+
increment(name, amount = 1) {
|
|
40
|
+
this._counters.set(name, (this._counters.get(name) ?? 0) + amount);
|
|
41
|
+
}
|
|
42
|
+
/** Record a numeric sample into a histogram (e.g. latency, duration). */
|
|
43
|
+
record(name, value) {
|
|
44
|
+
let samples = this._histograms.get(name);
|
|
45
|
+
if (!samples) {
|
|
46
|
+
samples = [];
|
|
47
|
+
this._histograms.set(name, samples);
|
|
48
|
+
}
|
|
49
|
+
samples.push(value);
|
|
50
|
+
// Keep last 1000 samples to bound memory
|
|
51
|
+
if (samples.length > 1000) {
|
|
52
|
+
samples.splice(0, samples.length - 1000);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/** Get current value of a counter. */
|
|
56
|
+
getCounter(name) {
|
|
57
|
+
return this._counters.get(name) ?? 0;
|
|
58
|
+
}
|
|
59
|
+
/** Get summary statistics for a histogram. */
|
|
60
|
+
getHistogram(name) {
|
|
61
|
+
const samples = this._histograms.get(name);
|
|
62
|
+
if (!samples || samples.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
const sorted = [...samples].sort((a, b) => a - b);
|
|
65
|
+
const count = sorted.length;
|
|
66
|
+
const sum = sorted.reduce((a, b) => a + b, 0);
|
|
67
|
+
return {
|
|
68
|
+
count,
|
|
69
|
+
min: sorted[0],
|
|
70
|
+
max: sorted[count - 1],
|
|
71
|
+
avg: Math.round((sum / count) * 100) / 100,
|
|
72
|
+
p50: sorted[Math.floor(count * 0.5)],
|
|
73
|
+
p95: sorted[Math.floor(count * 0.95)],
|
|
74
|
+
p99: sorted[Math.floor(count * 0.99)],
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Return a full snapshot of all metrics. */
|
|
78
|
+
snapshot() {
|
|
79
|
+
const counters = {};
|
|
80
|
+
for (const [k, v] of this._counters)
|
|
81
|
+
counters[k] = v;
|
|
82
|
+
const histograms = {};
|
|
83
|
+
for (const [k] of this._histograms)
|
|
84
|
+
histograms[k] = this.getHistogram(k);
|
|
85
|
+
return {
|
|
86
|
+
uptime_ms: Date.now() - this._startedAt,
|
|
87
|
+
counters,
|
|
88
|
+
histograms,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/** Reset all metrics. */
|
|
92
|
+
reset() {
|
|
93
|
+
this._counters.clear();
|
|
94
|
+
this._histograms.clear();
|
|
95
|
+
this._startedAt = Date.now();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// ─── Logger ───────────────────────────────────────────────────────────
|
|
99
|
+
export class Logger {
|
|
100
|
+
/** Shared metrics collector (singleton). */
|
|
101
|
+
static metrics = new MetricsCollector();
|
|
102
|
+
/** Current global log level. Override with LOG_LEVEL env var. */
|
|
103
|
+
static _level = process.env.LOG_LEVEL ?? "info";
|
|
104
|
+
/** JSON output mode (LOG_FORMAT=json). Default: human-readable. */
|
|
105
|
+
static _jsonMode = process.env.LOG_FORMAT === "json";
|
|
106
|
+
_component;
|
|
107
|
+
constructor(component) {
|
|
108
|
+
this._component = component;
|
|
109
|
+
}
|
|
110
|
+
/** Create a logger for a specific component. */
|
|
111
|
+
static create(component) {
|
|
112
|
+
return new Logger(component);
|
|
113
|
+
}
|
|
114
|
+
/** Set the global log level at runtime. */
|
|
115
|
+
static setLevel(level) {
|
|
116
|
+
Logger._level = level;
|
|
117
|
+
}
|
|
118
|
+
/** Get the global log level. */
|
|
119
|
+
static getLevel() {
|
|
120
|
+
return Logger._level;
|
|
121
|
+
}
|
|
122
|
+
// ─── Log Methods ──────────────────────────────────────────────────
|
|
123
|
+
debug(message, context) {
|
|
124
|
+
this._log("debug", message, context);
|
|
125
|
+
}
|
|
126
|
+
info(message, context) {
|
|
127
|
+
this._log("info", message, context);
|
|
128
|
+
}
|
|
129
|
+
warn(message, context) {
|
|
130
|
+
this._log("warn", message, context);
|
|
131
|
+
}
|
|
132
|
+
error(message, context, err) {
|
|
133
|
+
const entry = {};
|
|
134
|
+
if (err) {
|
|
135
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
136
|
+
entry.error = { message: e.message, stack: e.stack };
|
|
137
|
+
}
|
|
138
|
+
this._log("error", message, context, entry);
|
|
139
|
+
}
|
|
140
|
+
// ─── Performance Timing ───────────────────────────────────────────
|
|
141
|
+
/**
|
|
142
|
+
* Start a performance timer. Returns a `done()` function.
|
|
143
|
+
* When called, `done()` logs the elapsed time and records it in metrics.
|
|
144
|
+
*
|
|
145
|
+
* @param operation Name of the operation (e.g. "ws.connect", "room.join")
|
|
146
|
+
* @param context Optional extra context
|
|
147
|
+
* @returns A `done(extraContext?)` function
|
|
148
|
+
*/
|
|
149
|
+
time(operation, context) {
|
|
150
|
+
const start = performance.now();
|
|
151
|
+
return (extraContext) => {
|
|
152
|
+
const duration = Math.round((performance.now() - start) * 100) / 100;
|
|
153
|
+
const mergedContext = { ...context, ...extraContext };
|
|
154
|
+
this._log("info", `${operation} completed`, mergedContext, { duration_ms: duration });
|
|
155
|
+
// Record in metrics histogram
|
|
156
|
+
Logger.metrics.record(`${operation}.duration_ms`, duration);
|
|
157
|
+
return duration;
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
// ─── Internal ─────────────────────────────────────────────────────
|
|
161
|
+
_log(level, message, context, extra) {
|
|
162
|
+
if (LEVEL_ORDER[level] < LEVEL_ORDER[Logger._level])
|
|
163
|
+
return;
|
|
164
|
+
const entry = {
|
|
165
|
+
timestamp: new Date().toISOString(),
|
|
166
|
+
level,
|
|
167
|
+
component: this._component,
|
|
168
|
+
message,
|
|
169
|
+
...extra,
|
|
170
|
+
};
|
|
171
|
+
if (context && Object.keys(context).length > 0) {
|
|
172
|
+
entry.context = context;
|
|
173
|
+
}
|
|
174
|
+
if (Logger._jsonMode) {
|
|
175
|
+
process.stderr.write(JSON.stringify(entry) + "\n");
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
process.stderr.write(this._formatHuman(entry) + "\n");
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
_formatHuman(entry) {
|
|
182
|
+
const ts = entry.timestamp.slice(11, 23); // HH:mm:ss.SSS
|
|
183
|
+
const lvl = entry.level.toUpperCase().padEnd(5);
|
|
184
|
+
const comp = entry.component;
|
|
185
|
+
const dur = entry.duration_ms !== undefined ? ` (${entry.duration_ms}ms)` : "";
|
|
186
|
+
let line = `${ts} ${lvl} [${comp}] ${entry.message}${dur}`;
|
|
187
|
+
if (entry.context) {
|
|
188
|
+
const pairs = Object.entries(entry.context)
|
|
189
|
+
.map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`)
|
|
190
|
+
.join(" ");
|
|
191
|
+
line += ` | ${pairs}`;
|
|
192
|
+
}
|
|
193
|
+
if (entry.error) {
|
|
194
|
+
line += `\n ERROR: ${entry.error.message}`;
|
|
195
|
+
if (entry.error.stack && entry.level === "error") {
|
|
196
|
+
// Show first 3 stack frames
|
|
197
|
+
const frames = entry.error.stack.split("\n").slice(1, 4).map((f) => ` ${f.trim()}`).join("\n");
|
|
198
|
+
line += `\n${frames}`;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return line;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { StreamMessage } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Sliding-window buffer that stores the most recent N messages per channel.
|
|
4
|
+
* Automatically parses JSON payloads for structured display.
|
|
5
|
+
*/
|
|
6
|
+
export declare class MessageBuffer {
|
|
7
|
+
private _buffers;
|
|
8
|
+
private _maxSize;
|
|
9
|
+
constructor(maxSize?: number);
|
|
10
|
+
/**
|
|
11
|
+
* Add a raw message string to a channel's buffer.
|
|
12
|
+
* Returns the created StreamMessage.
|
|
13
|
+
*/
|
|
14
|
+
push(channelId: string, raw: string): StreamMessage;
|
|
15
|
+
/**
|
|
16
|
+
* Get the most recent `count` messages for a channel.
|
|
17
|
+
*/
|
|
18
|
+
getRecent(channelId: string, count?: number): StreamMessage[];
|
|
19
|
+
/**
|
|
20
|
+
* Get only the latest message for a channel.
|
|
21
|
+
*/
|
|
22
|
+
getLatest(channelId: string): StreamMessage | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Get all buffered messages for a channel.
|
|
25
|
+
*/
|
|
26
|
+
getAll(channelId: string): StreamMessage[];
|
|
27
|
+
/**
|
|
28
|
+
* Clear a channel's buffer.
|
|
29
|
+
*/
|
|
30
|
+
clear(channelId: string): void;
|
|
31
|
+
/**
|
|
32
|
+
* Clear all buffers.
|
|
33
|
+
*/
|
|
34
|
+
clearAll(): void;
|
|
35
|
+
/**
|
|
36
|
+
* Format messages for AI-readable display.
|
|
37
|
+
* JSON data is pretty-printed; raw strings are shown as-is.
|
|
38
|
+
*/
|
|
39
|
+
formatForDisplay(messages: StreamMessage[]): string;
|
|
40
|
+
private _tryParseJson;
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=message-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-buffer.d.ts","sourceRoot":"","sources":["../../src/core/message-buffer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAIjD;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,QAAQ,CAAS;gBAEb,OAAO,GAAE,MAAyB;IAI9C;;;OAGG;IACH,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa;IAwBnD;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE;IAO7D;;OAEG;IACH,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS;IAMvD;;OAEG;IACH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,aAAa,EAAE;IAI1C;;OAEG;IACH,KAAK,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAI9B;;OAEG;IACH,QAAQ,IAAI,IAAI;IAIhB;;;OAGG;IACH,gBAAgB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,MAAM;IAgBnD,OAAO,CAAC,aAAa;CAOtB"}
|