@xopcai/xopc 0.0.92 → 0.0.94
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/agents-OqhbJkMf.js +222 -0
- package/dist/gateway/static/root/assets/apps-page-OHXW9XP8.js +1 -0
- package/dist/gateway/static/root/assets/channels-settings-4N2R-jof.js +1 -0
- package/dist/gateway/static/root/assets/{channels-status-swr-XzddfJW2.js → channels-status-swr-Bv6f9kDq.js} +1 -1
- package/dist/gateway/static/root/assets/{cron-api--I8LJ44S.js → cron-api-BtaQaHJq.js} +1 -1
- package/dist/gateway/static/root/assets/cron-page-Dah32HJK.js +1 -0
- package/dist/gateway/static/root/assets/{dist-CYgHMQO0.js → dist-BJfD9Qvs.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-6cRP0nA9.js → extension-debug-page-DnYuMzmH.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DpwIkspI.js → extension-page-CJfc-6XV.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DYbnQUxH.js → extension-settings-page-BxdfYQMG.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-DTN0w7rV.js → fetch-B0aeeY0q.js} +1 -1
- package/dist/gateway/static/root/assets/{field-primitives-CslW6HwD.js → field-primitives-DOLHwowi.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-config-api-2UiKevxG.js → heartbeat-config-api-Bj2INAf5.js} +1 -1
- package/dist/gateway/static/root/assets/index-Bj_l8QDp.css +1 -0
- package/dist/gateway/static/root/assets/{index-DnevRVa6.js → index-DuQ1XPoA.js} +99 -98
- package/dist/gateway/static/root/assets/logs-page-AsOgLNJE.js +2 -0
- package/dist/gateway/static/root/assets/{note-detail-page-DvW2qg4i.js → note-detail-page-24J4mVP-.js} +53 -53
- package/dist/gateway/static/root/assets/{note-time-BEiibLJv.js → note-time-JBszYV3s.js} +1 -1
- package/dist/gateway/static/root/assets/notes-page-BApAirFB.js +1 -0
- package/dist/gateway/static/root/assets/sessions-page-DX9huWsA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-advanced-gate-BctKqHcf.js → settings-advanced-gate-DWvhsTuz.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-QJh5ruel.js → settings-form-section-CxMjaMiy.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-4VmUTzQs.js +3 -0
- package/dist/gateway/static/root/assets/{share-preview-page-DBsvvbmD.js → share-preview-page-IX0TJvRd.js} +1 -1
- package/dist/gateway/static/root/assets/skills-page-CGKGKfwe.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-ht5iswWS.js → theme-store-Cg_SuBw0.js} +1 -1
- package/dist/gateway/static/root/assets/url-BHHmdJYc.js +3 -0
- package/dist/gateway/static/root/assets/{utils-DhPv9xoB.js → utils-BmlcxR2j.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-DaGm2N4J.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-page.utils-CJqnPWkW.js → workflow-page.utils-D0vsIGHD.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BFCrD3nw.js +27 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/inbound/turn-dispatcher.d.ts +1 -0
- package/dist/src/agent/inbound/turn-dispatcher.js +3 -0
- package/dist/src/agent/inbound/turn-dispatcher.js.map +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.js +1 -1
- package/dist/src/agent/lifecycle/handlers/compaction.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js +1 -1
- package/dist/src/agent/mcp/bundle-mcp-materialize.js.map +1 -1
- package/dist/src/agent/mcp/bundle-mcp-runtime.js +17 -4
- package/dist/src/agent/mcp/bundle-mcp-runtime.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport-config.js +10 -3
- package/dist/src/agent/mcp/mcp-transport-config.js.map +1 -1
- package/dist/src/agent/mcp/mcp-transport.js +1 -1
- package/dist/src/agent/mcp/mcp-transport.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.d.ts +1 -0
- package/dist/src/agent/service/process-direct-streaming.js +15 -12
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.d.ts +4 -2
- package/dist/src/agent/service.js +20 -4
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/service.types.d.ts +3 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js +1 -1
- package/dist/src/agent/tools/browser/tool/browser-use-tool.js.map +1 -1
- package/dist/src/agent/tools/search/registry.js +1 -1
- package/dist/src/agent/tools/search/registry.js.map +1 -1
- package/dist/src/agent/tools/session-search-tool.js +1 -1
- package/dist/src/agent/tools/session-search-tool.js.map +1 -1
- package/dist/src/agent/tools/workflow-tool.js +1 -1
- package/dist/src/agent/tools/workflow-tool.js.map +1 -1
- package/dist/src/agent/workflow/progress-broker.js +1 -1
- package/dist/src/agent/workflow/progress-broker.js.map +1 -1
- package/dist/src/agent/workflow/subagent-runner.js +1 -1
- package/dist/src/agent/workflow/subagent-runner.js.map +1 -1
- package/dist/src/channels/pipeline.js +3 -2
- package/dist/src/channels/pipeline.js.map +1 -1
- package/dist/src/cli/cli-log-level-preset.d.ts +1 -1
- package/dist/src/cli/cli-log-level-preset.js +2 -2
- package/dist/src/cli/cli-log-level-preset.js.map +1 -1
- package/dist/src/cli/commands/logs.js +3 -3
- package/dist/src/cli/commands/logs.js.map +1 -1
- package/dist/src/cron/executor.js +7 -4
- package/dist/src/cron/executor.js.map +1 -1
- package/dist/src/gateway/hono/app.js +4 -1
- package/dist/src/gateway/hono/app.js.map +1 -1
- package/dist/src/gateway/hono/lib/route-logger.d.ts +6 -0
- package/dist/src/gateway/hono/lib/route-logger.js +31 -0
- package/dist/src/gateway/hono/lib/route-logger.js.map +1 -0
- package/dist/src/gateway/hono/middleware/auth.js +16 -3
- package/dist/src/gateway/hono/middleware/auth.js.map +1 -1
- package/dist/src/gateway/hono/middleware/logger.js +1 -1
- package/dist/src/gateway/hono/middleware/logger.js.map +1 -1
- package/dist/src/gateway/hono/middleware/route-errors.d.ts +5 -0
- package/dist/src/gateway/hono/middleware/route-errors.js +27 -0
- package/dist/src/gateway/hono/middleware/route-errors.js.map +1 -0
- package/dist/src/gateway/hono/routes/agent-stream.js +6 -0
- package/dist/src/gateway/hono/routes/agent-stream.js.map +1 -1
- package/dist/src/gateway/hono/routes/browser-install.js +2 -4
- package/dist/src/gateway/hono/routes/browser-install.js.map +1 -1
- package/dist/src/gateway/hono/routes/config.js +25 -11
- package/dist/src/gateway/hono/routes/config.js.map +1 -1
- package/dist/src/gateway/hono/routes/cron.js +5 -0
- package/dist/src/gateway/hono/routes/cron.js.map +1 -1
- package/dist/src/gateway/hono/routes/host-fs.js +2 -4
- package/dist/src/gateway/hono/routes/host-fs.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +14 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-fallback.js +3 -0
- package/dist/src/gateway/hono/routes/lazy-fallback.js.map +1 -1
- package/dist/src/gateway/hono/routes/logs.js +39 -7
- package/dist/src/gateway/hono/routes/logs.js.map +1 -1
- package/dist/src/gateway/hono/routes/mcp.d.ts +3 -0
- package/dist/src/gateway/hono/routes/mcp.js +107 -0
- package/dist/src/gateway/hono/routes/mcp.js.map +1 -0
- package/dist/src/gateway/hono/routes/notes.js +105 -1
- package/dist/src/gateway/hono/routes/notes.js.map +1 -1
- package/dist/src/gateway/hono/routes/sessions.js +6 -0
- package/dist/src/gateway/hono/routes/sessions.js.map +1 -1
- package/dist/src/gateway/hono/routes/update.js +2 -4
- package/dist/src/gateway/hono/routes/update.js.map +1 -1
- package/dist/src/gateway/hono/routes/voice.js +2 -4
- package/dist/src/gateway/hono/routes/voice.js.map +1 -1
- package/dist/src/gateway/hono/routes/workspace.js +2 -4
- package/dist/src/gateway/hono/routes/workspace.js.map +1 -1
- package/dist/src/gateway/hono/sse.js +9 -2
- package/dist/src/gateway/hono/sse.js.map +1 -1
- package/dist/src/gateway/host.d.ts +2 -0
- package/dist/src/gateway/host.js +6 -3
- package/dist/src/gateway/host.js.map +1 -1
- package/dist/src/gateway/service/agent-runner.js +1 -1
- package/dist/src/gateway/service/agent-runner.js.map +1 -1
- package/dist/src/gateway/service/config-coordinator.js +14 -6
- package/dist/src/gateway/service/config-coordinator.js.map +1 -1
- package/dist/src/gateway/service/marketplace-service.js +1 -1
- package/dist/src/gateway/service/marketplace-service.js.map +1 -1
- package/dist/src/gateway/service/run-gateway-agent.js +22 -5
- package/dist/src/gateway/service/run-gateway-agent.js.map +1 -1
- package/dist/src/gateway/service/sse-hub.js +1 -1
- package/dist/src/gateway/service/sse-hub.js.map +1 -1
- package/dist/src/gateway/service.js +12 -5
- package/dist/src/gateway/service.js.map +1 -1
- package/dist/src/mcp/channel-bridge.js +26 -2
- package/dist/src/mcp/channel-bridge.js.map +1 -1
- package/dist/src/mcp/gateway-http-client.js +24 -2
- package/dist/src/mcp/gateway-http-client.js.map +1 -1
- package/dist/src/notes/service.d.ts +13 -1
- package/dist/src/notes/service.js +237 -0
- package/dist/src/notes/service.js.map +1 -1
- package/dist/src/notes/store.d.ts +3 -0
- package/dist/src/notes/store.js +6 -2
- package/dist/src/notes/store.js.map +1 -1
- package/dist/src/notes/types.d.ts +31 -0
- package/dist/src/session/config-store.js +10 -4
- package/dist/src/session/config-store.js.map +1 -1
- package/dist/src/session/index.d.ts +1 -1
- package/dist/src/session/index.js +2 -2
- package/dist/src/session/manager.js +8 -1
- package/dist/src/session/manager.js.map +1 -1
- package/dist/src/session/session-title.d.ts +19 -3
- package/dist/src/session/session-title.js +82 -7
- package/dist/src/session/session-title.js.map +1 -1
- package/dist/src/utils/index.js +4 -4
- package/dist/src/utils/logger/config.js +2 -6
- package/dist/src/utils/logger/config.js.map +1 -1
- package/dist/src/utils/logger/context.d.ts +3 -22
- package/dist/src/utils/logger/context.js +4 -32
- package/dist/src/utils/logger/context.js.map +1 -1
- package/dist/src/utils/logger/index.d.ts +4 -7
- package/dist/src/utils/logger/index.js +9 -28
- package/dist/src/utils/logger/index.js.map +1 -1
- package/dist/src/utils/logger/log-store.d.ts +14 -32
- package/dist/src/utils/logger/log-store.js +67 -118
- package/dist/src/utils/logger/log-store.js.map +1 -1
- package/dist/src/utils/logger/log-stream.d.ts +5 -70
- package/dist/src/utils/logger/log-stream.js +67 -178
- package/dist/src/utils/logger/log-stream.js.map +1 -1
- package/dist/src/utils/logger/pino-record.d.ts +8 -0
- package/dist/src/utils/logger/pino-record.js +83 -0
- package/dist/src/utils/logger/pino-record.js.map +1 -0
- package/dist/src/utils/logger/stats.d.ts +1 -1
- package/dist/src/utils/logger/stats.js +2 -2
- package/dist/src/utils/logger/stats.js.map +1 -1
- package/dist/src/utils/logger/streams.js +18 -0
- package/dist/src/utils/logger/streams.js.map +1 -1
- package/dist/src/utils/logger/types.d.ts +0 -9
- package/dist/src/utils/logger/types.js.map +1 -1
- package/dist/src/utils/logger.js +4 -4
- package/package.json +6 -1
- package/dist/gateway/static/root/assets/agents-uwPn7ZW9.js +0 -222
- package/dist/gateway/static/root/assets/apps-page-CWKdhSPU.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-hEhW7Mbk.js +0 -1
- package/dist/gateway/static/root/assets/cron-page-B0kvgZGR.js +0 -1
- package/dist/gateway/static/root/assets/index-BUKUv7QW.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-sOP4TXJ4.js +0 -1
- package/dist/gateway/static/root/assets/notes-page-BFQaquHU.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-CptjDKAX.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-V3p-hISB.js +0 -2
- package/dist/gateway/static/root/assets/skills-page-q2zPUJAR.js +0 -2
- package/dist/gateway/static/root/assets/url-CWWpfkq1.js +0 -3
- package/dist/gateway/static/root/assets/voice-api-key-field-DLSKUipa.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-DRRQ1A0l.js +0 -27
|
@@ -1,207 +1,96 @@
|
|
|
1
|
+
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
1
2
|
//#region src/utils/logger/log-stream.ts
|
|
2
|
-
const subscribers = /* @__PURE__ */ new Set();
|
|
3
|
-
Number.MAX_VALUE;
|
|
4
|
-
/**
|
|
5
|
-
* Subscribe to log events
|
|
6
|
-
*
|
|
7
|
-
* @param subscriber - Callback function to receive log entries
|
|
8
|
-
* @returns Unsubscribe function
|
|
9
|
-
*/
|
|
10
3
|
function subscribeToLogs(subscriber) {
|
|
11
4
|
subscribers.add(subscriber);
|
|
12
5
|
return () => subscribers.delete(subscriber);
|
|
13
6
|
}
|
|
14
|
-
/**
|
|
15
|
-
* Get number of active subscribers
|
|
16
|
-
*/
|
|
17
7
|
function getSubscriberCount() {
|
|
18
8
|
return subscribers.size;
|
|
19
9
|
}
|
|
20
|
-
/**
|
|
21
|
-
* Check if any subscribers are active
|
|
22
|
-
*/
|
|
23
10
|
function hasSubscribers() {
|
|
24
11
|
return subscribers.size > 0;
|
|
25
12
|
}
|
|
26
|
-
/**
|
|
27
|
-
* Emit a log entry to all subscribers
|
|
28
|
-
*
|
|
29
|
-
* @internal - Called by the logger integration
|
|
30
|
-
*/
|
|
13
|
+
/** @internal Called from Pino live emit stream */
|
|
31
14
|
function emitLogEntry(entry) {
|
|
32
15
|
if (subscribers.size === 0) return;
|
|
33
16
|
for (const subscriber of subscribers) try {
|
|
34
17
|
subscriber(entry);
|
|
35
18
|
} catch {}
|
|
36
19
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
level,
|
|
46
|
-
message,
|
|
47
|
-
...context
|
|
48
|
-
};
|
|
20
|
+
function parseAllowedLevels(levelsParam) {
|
|
21
|
+
if (!levelsParam) return [
|
|
22
|
+
"info",
|
|
23
|
+
"warn",
|
|
24
|
+
"error",
|
|
25
|
+
"fatal"
|
|
26
|
+
];
|
|
27
|
+
return levelsParam.split(",").filter((l) => VALID_LEVELS.includes(l));
|
|
49
28
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
];
|
|
74
|
-
const allowedLevels = levelsParam ? levelsParam.split(",").filter((l) => validLevels.includes(l)) : [
|
|
75
|
-
"info",
|
|
76
|
-
"warn",
|
|
77
|
-
"error",
|
|
78
|
-
"fatal"
|
|
79
|
-
];
|
|
80
|
-
const stream = new ReadableStream({ start(controller) {
|
|
81
|
-
const encoder = new TextEncoder();
|
|
82
|
-
const sendEvent = (data) => {
|
|
83
|
-
const message = `data: ${JSON.stringify(data)}\n\n`;
|
|
84
|
-
controller.enqueue(encoder.encode(message));
|
|
85
|
-
};
|
|
86
|
-
sendEvent({
|
|
87
|
-
type: "connected",
|
|
88
|
-
message: "Log stream started"
|
|
89
|
-
});
|
|
90
|
-
const unsubscribe = subscribeToLogs((entry) => {
|
|
91
|
-
if (!allowedLevels.includes(entry.level)) return;
|
|
92
|
-
if (moduleFilter && entry.module !== moduleFilter && entry.prefix !== moduleFilter) return;
|
|
93
|
-
sendEvent(entry);
|
|
94
|
-
});
|
|
95
|
-
req.signal.addEventListener("abort", () => {
|
|
96
|
-
unsubscribe();
|
|
97
|
-
try {
|
|
98
|
-
controller.close();
|
|
99
|
-
} catch {}
|
|
100
|
-
});
|
|
101
|
-
const heartbeat = setInterval(() => {
|
|
102
|
-
try {
|
|
103
|
-
sendEvent({
|
|
104
|
-
type: "heartbeat",
|
|
105
|
-
subscribers: getSubscriberCount()
|
|
106
|
-
});
|
|
107
|
-
} catch {
|
|
108
|
-
clearInterval(heartbeat);
|
|
109
|
-
}
|
|
110
|
-
}, 3e4);
|
|
111
|
-
req.signal.addEventListener("abort", () => {
|
|
29
|
+
function createLogSseResponse(req, allowedLevels, moduleFilter) {
|
|
30
|
+
const stream = new ReadableStream({ start(controller) {
|
|
31
|
+
const encoder = new TextEncoder();
|
|
32
|
+
const sendEvent = (data) => {
|
|
33
|
+
const message = `data: ${JSON.stringify(data)}\n\n`;
|
|
34
|
+
controller.enqueue(encoder.encode(message));
|
|
35
|
+
};
|
|
36
|
+
sendEvent({
|
|
37
|
+
type: "connected",
|
|
38
|
+
message: "Log stream started"
|
|
39
|
+
});
|
|
40
|
+
const unsubscribe = subscribeToLogs((entry) => {
|
|
41
|
+
if (!allowedLevels.includes(entry.level)) return;
|
|
42
|
+
if (moduleFilter && entry.module !== moduleFilter) return;
|
|
43
|
+
sendEvent(entry);
|
|
44
|
+
});
|
|
45
|
+
const heartbeat = setInterval(() => {
|
|
46
|
+
try {
|
|
47
|
+
sendEvent({
|
|
48
|
+
type: "heartbeat",
|
|
49
|
+
subscribers: getSubscriberCount()
|
|
50
|
+
});
|
|
51
|
+
} catch {
|
|
112
52
|
clearInterval(heartbeat);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
53
|
+
}
|
|
54
|
+
}, 3e4);
|
|
55
|
+
req.signal.addEventListener("abort", () => {
|
|
56
|
+
unsubscribe();
|
|
57
|
+
clearInterval(heartbeat);
|
|
58
|
+
try {
|
|
59
|
+
controller.close();
|
|
60
|
+
} catch {}
|
|
61
|
+
});
|
|
62
|
+
} });
|
|
63
|
+
return new Response(stream, { headers: {
|
|
64
|
+
"Content-Type": "text/event-stream",
|
|
65
|
+
"Cache-Control": "no-cache",
|
|
66
|
+
Connection: "keep-alive",
|
|
67
|
+
"X-Accel-Buffering": "no"
|
|
68
|
+
} });
|
|
122
69
|
}
|
|
123
|
-
/**
|
|
124
|
-
* Create a Hono-compatible SSE handler for log streaming
|
|
125
|
-
*
|
|
126
|
-
* Usage:
|
|
127
|
-
* ```typescript
|
|
128
|
-
* import { createLogSSEHandler } from './log-stream.js';
|
|
129
|
-
*
|
|
130
|
-
* app.get('/api/logs/stream', createLogSSEHandler());
|
|
131
|
-
* ```
|
|
132
|
-
*/
|
|
70
|
+
/** Hono handler for `GET /api/logs/stream`. */
|
|
133
71
|
function createLogSSEHandler() {
|
|
134
72
|
return async (c) => {
|
|
135
73
|
const url = new URL(c.req.url);
|
|
136
|
-
const
|
|
74
|
+
const allowedLevels = parseAllowedLevels(url.searchParams.get("levels"));
|
|
137
75
|
const moduleFilter = url.searchParams.get("module");
|
|
138
|
-
|
|
139
|
-
"trace",
|
|
140
|
-
"debug",
|
|
141
|
-
"info",
|
|
142
|
-
"warn",
|
|
143
|
-
"error",
|
|
144
|
-
"fatal",
|
|
145
|
-
"silent"
|
|
146
|
-
];
|
|
147
|
-
const allowedLevels = levelsParam ? levelsParam.split(",").filter((l) => validLevels.includes(l)) : [
|
|
148
|
-
"info",
|
|
149
|
-
"warn",
|
|
150
|
-
"error",
|
|
151
|
-
"fatal"
|
|
152
|
-
];
|
|
153
|
-
const stream = new ReadableStream({ start(controller) {
|
|
154
|
-
const encoder = new TextEncoder();
|
|
155
|
-
const sendEvent = (data) => {
|
|
156
|
-
const message = `data: ${JSON.stringify(data)}\n\n`;
|
|
157
|
-
controller.enqueue(encoder.encode(message));
|
|
158
|
-
};
|
|
159
|
-
sendEvent({
|
|
160
|
-
type: "connected",
|
|
161
|
-
message: "Log stream started"
|
|
162
|
-
});
|
|
163
|
-
const unsubscribe = subscribeToLogs((entry) => {
|
|
164
|
-
if (!allowedLevels.includes(entry.level)) return;
|
|
165
|
-
if (moduleFilter && entry.module !== moduleFilter && entry.prefix !== moduleFilter) return;
|
|
166
|
-
sendEvent(entry);
|
|
167
|
-
});
|
|
168
|
-
c.req.raw.signal.addEventListener("abort", () => {
|
|
169
|
-
unsubscribe();
|
|
170
|
-
try {
|
|
171
|
-
controller.close();
|
|
172
|
-
} catch {}
|
|
173
|
-
});
|
|
174
|
-
const heartbeat = setInterval(() => {
|
|
175
|
-
try {
|
|
176
|
-
sendEvent({
|
|
177
|
-
type: "heartbeat",
|
|
178
|
-
subscribers: getSubscriberCount()
|
|
179
|
-
});
|
|
180
|
-
} catch {
|
|
181
|
-
clearInterval(heartbeat);
|
|
182
|
-
}
|
|
183
|
-
}, 3e4);
|
|
184
|
-
c.req.raw.signal.addEventListener("abort", () => {
|
|
185
|
-
clearInterval(heartbeat);
|
|
186
|
-
});
|
|
187
|
-
} });
|
|
188
|
-
return new Response(stream, { headers: {
|
|
189
|
-
"Content-Type": "text/event-stream",
|
|
190
|
-
"Cache-Control": "no-cache",
|
|
191
|
-
"Connection": "keep-alive"
|
|
192
|
-
} });
|
|
76
|
+
return createLogSseResponse(c.req.raw, allowedLevels, moduleFilter);
|
|
193
77
|
};
|
|
194
78
|
}
|
|
195
|
-
var
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
79
|
+
var subscribers, VALID_LEVELS;
|
|
80
|
+
var init_log_stream = __esmMin((() => {
|
|
81
|
+
subscribers = /* @__PURE__ */ new Set();
|
|
82
|
+
VALID_LEVELS = [
|
|
83
|
+
"trace",
|
|
84
|
+
"debug",
|
|
85
|
+
"info",
|
|
86
|
+
"warn",
|
|
87
|
+
"error",
|
|
88
|
+
"fatal",
|
|
89
|
+
"silent"
|
|
90
|
+
];
|
|
91
|
+
}));
|
|
204
92
|
//#endregion
|
|
205
|
-
|
|
93
|
+
init_log_stream();
|
|
94
|
+
export { createLogSSEHandler, emitLogEntry, hasSubscribers, init_log_stream };
|
|
206
95
|
|
|
207
96
|
//# sourceMappingURL=log-stream.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"log-stream.js","names":[],"sources":["../../../../src/utils/logger/log-stream.ts"],"sourcesContent":["/**\n * Log Streaming Module - SSE Real-time Log Delivery\n * \n * Provides Server-Sent Events (SSE) for real-time log streaming:\n * - Subscribe to log events\n * - Filter by level and module\n * - Automatic cleanup on disconnect\n */\n\nimport type { LogLevel, LogEntry } from './types.js';\nexport type { LogLevel, LogEntry };\n\nexport interface LogStreamOptions {\n minLevel?: LogLevel;\n module?: string;\n levels?: LogLevel[];\n}\n\n// =============================================================================\n// Internal State\n// =============================================================================\n\ntype LogSubscriber = (entry: LogEntry) => void;\nconst subscribers = new Set<LogSubscriber>();\n\n// Log level numeric values for comparison (reserved for future use)\nconst _LOG_LEVELS: Record<LogLevel, number> = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n silent: Number.MAX_VALUE,\n};\n\n// =============================================================================\n// Public API\n// =============================================================================\n\n/**\n * Subscribe to log events\n * \n * @param subscriber - Callback function to receive log entries\n * @returns Unsubscribe function\n */\nfunction subscribeToLogs(subscriber: LogSubscriber): () => void {\n subscribers.add(subscriber);\n return () => subscribers.delete(subscriber);\n}\n\n/**\n * Get number of active subscribers\n */\nfunction getSubscriberCount(): number {\n return subscribers.size;\n}\n\n/**\n * Check if any subscribers are active\n */\nfunction hasSubscribers(): boolean {\n return subscribers.size > 0;\n}\n\n/**\n * Emit a log entry to all subscribers\n * \n * @internal - Called by the logger integration\n */\nfunction emitLogEntry(entry: LogEntry): void {\n if (subscribers.size === 0) return;\n \n for (const subscriber of subscribers) {\n try {\n subscriber(entry);\n } catch {\n // Ignore subscriber errors\n }\n }\n}\n\n/**\n * Create a log entry from raw data\n * \n * @internal\n */\nfunction createLogEntry(\n level: LogLevel,\n message: string,\n context?: Record<string, unknown>\n): LogEntry {\n return {\n timestamp: new Date().toISOString(),\n level,\n message,\n ...context,\n };\n}\n\n// =============================================================================\n// HTTP SSE Handler\n// =============================================================================\n\n/**\n * Create an SSE handler for log streaming\n * \n * Usage:\n * ```typescript\n * import { createLogStreamHandler } from './log-stream.js';\n * \n * app.get('/api/logs/stream', createLogStreamHandler());\n * ```\n */\nfunction createLogStreamHandler() {\n return (req: Request): Response => {\n const url = new URL(req.url);\n const levelsParam = url.searchParams.get('levels');\n const moduleFilter = url.searchParams.get('module');\n \n // Parse levels\n const validLevels: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'silent'];\n const allowedLevels: LogLevel[] = levelsParam \n ? levelsParam.split(',').filter((l): l is LogLevel => validLevels.includes(l as LogLevel))\n : ['info', 'warn', 'error', 'fatal'];\n \n const stream = new ReadableStream({\n start(controller) {\n const encoder = new TextEncoder();\n \n const sendEvent = (data: unknown) => {\n const message = `data: ${JSON.stringify(data)}\\n\\n`;\n controller.enqueue(encoder.encode(message));\n };\n\n // Send connection message\n sendEvent({ type: 'connected', message: 'Log stream started' });\n\n // Subscribe to logs\n const unsubscribe = subscribeToLogs((entry) => {\n // Filter by level\n if (!allowedLevels.includes(entry.level)) return;\n \n // Filter by module\n if (moduleFilter && entry.module !== moduleFilter && entry.prefix !== moduleFilter) return;\n \n sendEvent(entry);\n });\n\n // Handle disconnect\n req.signal.addEventListener('abort', () => {\n unsubscribe();\n try {\n controller.close();\n } catch {\n // Already closed\n }\n });\n\n // Send heartbeat every 30 seconds\n const heartbeat = setInterval(() => {\n try {\n sendEvent({ type: 'heartbeat', subscribers: getSubscriberCount() });\n } catch {\n clearInterval(heartbeat);\n }\n }, 30000);\n\n req.signal.addEventListener('abort', () => {\n clearInterval(heartbeat);\n });\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n };\n}\n\n// =============================================================================\n// Utility: Create SSE handler compatible with Hono\n// =============================================================================\n\n/**\n * Create a Hono-compatible SSE handler for log streaming\n * \n * Usage:\n * ```typescript\n * import { createLogSSEHandler } from './log-stream.js';\n * \n * app.get('/api/logs/stream', createLogSSEHandler());\n * ```\n */\nexport function createLogSSEHandler(): (c: { req: { raw: Request; url: string } }) => Promise<Response> {\n return async (c: { req: { raw: Request; url: string } }) => {\n const url = new URL(c.req.url);\n const levelsParam = url.searchParams.get('levels');\n const moduleFilter = url.searchParams.get('module');\n \n const validLevels: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'silent'];\n const allowedLevels: LogLevel[] = levelsParam\n ? levelsParam.split(',').filter((l): l is LogLevel => validLevels.includes(l as LogLevel))\n : ['info', 'warn', 'error', 'fatal'];\n \n const stream = new ReadableStream({\n start(controller) {\n const encoder = new TextEncoder();\n \n const sendEvent = (data: unknown) => {\n const message = `data: ${JSON.stringify(data)}\\n\\n`;\n controller.enqueue(encoder.encode(message));\n };\n\n // Send connection message\n sendEvent({ type: 'connected', message: 'Log stream started' });\n\n // Subscribe to logs\n const unsubscribe = subscribeToLogs((entry) => {\n if (!allowedLevels.includes(entry.level)) return;\n if (moduleFilter && entry.module !== moduleFilter && entry.prefix !== moduleFilter) return;\n \n sendEvent(entry);\n });\n\n // Handle disconnect\n c.req.raw.signal.addEventListener('abort', () => {\n unsubscribe();\n try {\n controller.close();\n } catch {\n // Already closed\n }\n });\n\n // Heartbeat\n const heartbeat = setInterval(() => {\n try {\n sendEvent({ type: 'heartbeat', subscribers: getSubscriberCount() });\n } catch {\n clearInterval(heartbeat);\n }\n }, 30000);\n\n c.req.raw.signal.addEventListener('abort', () => {\n clearInterval(heartbeat);\n });\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n 'Connection': 'keep-alive',\n },\n });\n };\n}\n\nexport default {\n subscribeToLogs,\n getSubscriberCount,\n hasSubscribers,\n emitLogEntry,\n createLogEntry,\n createLogStreamHandler,\n createLogSSEHandler,\n};\n"],"mappings":";AAuBA,MAAM,8BAAc,IAAI,KAAoB;AAUlC,OAAO;;;;;;;AAajB,SAAS,gBAAgB,YAAuC;AAC9D,aAAY,IAAI,WAAW;AAC3B,cAAa,YAAY,OAAO,WAAW;;;;;AAM7C,SAAS,qBAA6B;AACpC,QAAO,YAAY;;;;;AAMrB,SAAS,iBAA0B;AACjC,QAAO,YAAY,OAAO;;;;;;;AAQ5B,SAAS,aAAa,OAAuB;AAC3C,KAAI,YAAY,SAAS,EAAG;AAE5B,MAAK,MAAM,cAAc,YACvB,KAAI;AACF,aAAW,MAAM;SACX;;;;;;;AAWZ,SAAS,eACP,OACA,SACA,SACU;AACV,QAAO;EACL,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC;EACA;EACA,GAAG;EACJ;;;;;;;;;;;;AAiBH,SAAS,yBAAyB;AAChC,SAAQ,QAA2B;EACjC,MAAM,MAAM,IAAI,IAAI,IAAI,IAAI;EAC5B,MAAM,cAAc,IAAI,aAAa,IAAI,SAAS;EAClD,MAAM,eAAe,IAAI,aAAa,IAAI,SAAS;EAGnD,MAAM,cAA0B;GAAC;GAAS;GAAS;GAAQ;GAAQ;GAAS;GAAS;GAAS;EAC9F,MAAM,gBAA4B,cAC9B,YAAY,MAAM,IAAI,CAAC,QAAQ,MAAqB,YAAY,SAAS,EAAc,CAAC,GACxF;GAAC;GAAQ;GAAQ;GAAS;GAAQ;EAEtC,MAAM,SAAS,IAAI,eAAe,EAChC,MAAM,YAAY;GAChB,MAAM,UAAU,IAAI,aAAa;GAEjC,MAAM,aAAa,SAAkB;IACnC,MAAM,UAAU,SAAS,KAAK,UAAU,KAAK,CAAC;AAC9C,eAAW,QAAQ,QAAQ,OAAO,QAAQ,CAAC;;AAI7C,aAAU;IAAE,MAAM;IAAa,SAAS;IAAsB,CAAC;GAG/D,MAAM,cAAc,iBAAiB,UAAU;AAE7C,QAAI,CAAC,cAAc,SAAS,MAAM,MAAM,CAAE;AAG1C,QAAI,gBAAgB,MAAM,WAAW,gBAAgB,MAAM,WAAW,aAAc;AAEpF,cAAU,MAAM;KAChB;AAGF,OAAI,OAAO,iBAAiB,eAAe;AACzC,iBAAa;AACb,QAAI;AACF,gBAAW,OAAO;YACZ;KAGR;GAGF,MAAM,YAAY,kBAAkB;AAClC,QAAI;AACF,eAAU;MAAE,MAAM;MAAa,aAAa,oBAAoB;MAAE,CAAC;YAC7D;AACN,mBAAc,UAAU;;MAEzB,IAAM;AAET,OAAI,OAAO,iBAAiB,eAAe;AACzC,kBAAc,UAAU;KACxB;KAEL,CAAC;AAEF,SAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GACjB,cAAc;GACd,qBAAqB;GACtB,EACF,CAAC;;;;;;;;;;;;;AAkBN,SAAgB,sBAAwF;AACtG,QAAO,OAAO,MAA8C;EAC1D,MAAM,MAAM,IAAI,IAAI,EAAE,IAAI,IAAI;EAC9B,MAAM,cAAc,IAAI,aAAa,IAAI,SAAS;EAClD,MAAM,eAAe,IAAI,aAAa,IAAI,SAAS;EAEnD,MAAM,cAA0B;GAAC;GAAS;GAAS;GAAQ;GAAQ;GAAS;GAAS;GAAS;EAC9F,MAAM,gBAA4B,cAC9B,YAAY,MAAM,IAAI,CAAC,QAAQ,MAAqB,YAAY,SAAS,EAAc,CAAC,GACxF;GAAC;GAAQ;GAAQ;GAAS;GAAQ;EAEtC,MAAM,SAAS,IAAI,eAAe,EAChC,MAAM,YAAY;GAChB,MAAM,UAAU,IAAI,aAAa;GAEjC,MAAM,aAAa,SAAkB;IACnC,MAAM,UAAU,SAAS,KAAK,UAAU,KAAK,CAAC;AAC9C,eAAW,QAAQ,QAAQ,OAAO,QAAQ,CAAC;;AAI7C,aAAU;IAAE,MAAM;IAAa,SAAS;IAAsB,CAAC;GAG/D,MAAM,cAAc,iBAAiB,UAAU;AAC7C,QAAI,CAAC,cAAc,SAAS,MAAM,MAAM,CAAE;AAC1C,QAAI,gBAAgB,MAAM,WAAW,gBAAgB,MAAM,WAAW,aAAc;AAEpF,cAAU,MAAM;KAChB;AAGF,KAAE,IAAI,IAAI,OAAO,iBAAiB,eAAe;AAC/C,iBAAa;AACb,QAAI;AACF,gBAAW,OAAO;YACZ;KAGR;GAGF,MAAM,YAAY,kBAAkB;AAClC,QAAI;AACF,eAAU;MAAE,MAAM;MAAa,aAAa,oBAAoB;MAAE,CAAC;YAC7D;AACN,mBAAc,UAAU;;MAEzB,IAAM;AAET,KAAE,IAAI,IAAI,OAAO,iBAAiB,eAAe;AAC/C,kBAAc,UAAU;KACxB;KAEL,CAAC;AAEF,SAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS;GACP,gBAAgB;GAChB,iBAAiB;GACjB,cAAc;GACf,EACF,CAAC;;;AAIN,IAAA,qBAAe;CACb;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
|
|
1
|
+
{"version":3,"file":"log-stream.js","names":[],"sources":["../../../../src/utils/logger/log-stream.ts"],"sourcesContent":["/**\n * Log Streaming Module - SSE real-time log delivery\n */\n\nimport type { LogLevel, LogEntry } from './types.js';\nexport type { LogLevel, LogEntry };\n\ntype LogSubscriber = (entry: LogEntry) => void;\nconst subscribers = new Set<LogSubscriber>();\n\nconst VALID_LEVELS: LogLevel[] = ['trace', 'debug', 'info', 'warn', 'error', 'fatal', 'silent'];\n\nfunction subscribeToLogs(subscriber: LogSubscriber): () => void {\n subscribers.add(subscriber);\n return () => subscribers.delete(subscriber);\n}\n\nfunction getSubscriberCount(): number {\n return subscribers.size;\n}\n\nexport function hasSubscribers(): boolean {\n return subscribers.size > 0;\n}\n\n/** @internal Called from Pino live emit stream */\nexport function emitLogEntry(entry: LogEntry): void {\n if (subscribers.size === 0) return;\n\n for (const subscriber of subscribers) {\n try {\n subscriber(entry);\n } catch {\n /* ignore subscriber errors */\n }\n }\n}\n\nfunction parseAllowedLevels(levelsParam: string | null): LogLevel[] {\n if (!levelsParam) {\n return ['info', 'warn', 'error', 'fatal'];\n }\n return levelsParam\n .split(',')\n .filter((l): l is LogLevel => VALID_LEVELS.includes(l as LogLevel));\n}\n\nfunction createLogSseResponse(req: Request, allowedLevels: LogLevel[], moduleFilter: string | null): Response {\n const stream = new ReadableStream({\n start(controller) {\n const encoder = new TextEncoder();\n\n const sendEvent = (data: unknown) => {\n const message = `data: ${JSON.stringify(data)}\\n\\n`;\n controller.enqueue(encoder.encode(message));\n };\n\n sendEvent({ type: 'connected', message: 'Log stream started' });\n\n const unsubscribe = subscribeToLogs((entry) => {\n if (!allowedLevels.includes(entry.level)) return;\n if (moduleFilter && entry.module !== moduleFilter) return;\n sendEvent(entry);\n });\n\n const heartbeat = setInterval(() => {\n try {\n sendEvent({ type: 'heartbeat', subscribers: getSubscriberCount() });\n } catch {\n clearInterval(heartbeat);\n }\n }, 30000);\n\n req.signal.addEventListener('abort', () => {\n unsubscribe();\n clearInterval(heartbeat);\n try {\n controller.close();\n } catch {\n /* already closed */\n }\n });\n },\n });\n\n return new Response(stream, {\n headers: {\n 'Content-Type': 'text/event-stream',\n 'Cache-Control': 'no-cache',\n Connection: 'keep-alive',\n 'X-Accel-Buffering': 'no',\n },\n });\n}\n\n/** Hono handler for `GET /api/logs/stream`. */\nexport function createLogSSEHandler(): (c: { req: { raw: Request; url: string } }) => Promise<Response> {\n return async (c) => {\n const url = new URL(c.req.url);\n const allowedLevels = parseAllowedLevels(url.searchParams.get('levels'));\n const moduleFilter = url.searchParams.get('module');\n return createLogSseResponse(c.req.raw, allowedLevels, moduleFilter);\n };\n}\n"],"mappings":";;AAYA,SAAS,gBAAgB,YAAuC;AAC9D,aAAY,IAAI,WAAW;AAC3B,cAAa,YAAY,OAAO,WAAW;;AAG7C,SAAS,qBAA6B;AACpC,QAAO,YAAY;;AAGrB,SAAgB,iBAA0B;AACxC,QAAO,YAAY,OAAO;;;AAI5B,SAAgB,aAAa,OAAuB;AAClD,KAAI,YAAY,SAAS,EAAG;AAE5B,MAAK,MAAM,cAAc,YACvB,KAAI;AACF,aAAW,MAAM;SACX;;AAMZ,SAAS,mBAAmB,aAAwC;AAClE,KAAI,CAAC,YACH,QAAO;EAAC;EAAQ;EAAQ;EAAS;EAAQ;AAE3C,QAAO,YACJ,MAAM,IAAI,CACV,QAAQ,MAAqB,aAAa,SAAS,EAAc,CAAC;;AAGvE,SAAS,qBAAqB,KAAc,eAA2B,cAAuC;CAC5G,MAAM,SAAS,IAAI,eAAe,EAChC,MAAM,YAAY;EAChB,MAAM,UAAU,IAAI,aAAa;EAEjC,MAAM,aAAa,SAAkB;GACnC,MAAM,UAAU,SAAS,KAAK,UAAU,KAAK,CAAC;AAC9C,cAAW,QAAQ,QAAQ,OAAO,QAAQ,CAAC;;AAG7C,YAAU;GAAE,MAAM;GAAa,SAAS;GAAsB,CAAC;EAE/D,MAAM,cAAc,iBAAiB,UAAU;AAC7C,OAAI,CAAC,cAAc,SAAS,MAAM,MAAM,CAAE;AAC1C,OAAI,gBAAgB,MAAM,WAAW,aAAc;AACnD,aAAU,MAAM;IAChB;EAEF,MAAM,YAAY,kBAAkB;AAClC,OAAI;AACF,cAAU;KAAE,MAAM;KAAa,aAAa,oBAAoB;KAAE,CAAC;WAC7D;AACN,kBAAc,UAAU;;KAEzB,IAAM;AAET,MAAI,OAAO,iBAAiB,eAAe;AACzC,gBAAa;AACb,iBAAc,UAAU;AACxB,OAAI;AACF,eAAW,OAAO;WACZ;IAGR;IAEL,CAAC;AAEF,QAAO,IAAI,SAAS,QAAQ,EAC1B,SAAS;EACP,gBAAgB;EAChB,iBAAiB;EACjB,YAAY;EACZ,qBAAqB;EACtB,EACF,CAAC;;;AAIJ,SAAgB,sBAAwF;AACtG,QAAO,OAAO,MAAM;EAClB,MAAM,MAAM,IAAI,IAAI,EAAE,IAAI,IAAI;EAC9B,MAAM,gBAAgB,mBAAmB,IAAI,aAAa,IAAI,SAAS,CAAC;EACxE,MAAM,eAAe,IAAI,aAAa,IAAI,SAAS;AACnD,SAAO,qBAAqB,EAAE,IAAI,KAAK,eAAe,aAAa;;;;;AA7FjE,+BAAc,IAAI,KAAoB;AAEtC,gBAA2B;EAAC;EAAS;EAAS;EAAQ;EAAQ;EAAS;EAAS;EAAS"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Pino JSON record → LogEntry conversion for file query and live SSE.
|
|
3
|
+
*/
|
|
4
|
+
import type { LogEntry } from './types.js';
|
|
5
|
+
/** Convert a parsed Pino JSON object into a normalized LogEntry. */
|
|
6
|
+
export declare function pinoRecordToLogEntry(parsed: Record<string, unknown>): LogEntry;
|
|
7
|
+
/** Build searchable text from a log entry (query + client-side filter). */
|
|
8
|
+
export declare function logEntrySearchText(entry: LogEntry): string;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
//#region src/utils/logger/pino-record.ts
|
|
3
|
+
function resolveLevel(parsed) {
|
|
4
|
+
if (typeof parsed.level !== "string") return "info";
|
|
5
|
+
const lv = parsed.level.toLowerCase();
|
|
6
|
+
return VALID_LEVELS.has(lv) ? lv : "info";
|
|
7
|
+
}
|
|
8
|
+
/** Convert a parsed Pino JSON object into a normalized LogEntry. */
|
|
9
|
+
function pinoRecordToLogEntry(parsed) {
|
|
10
|
+
const reserved = new Set([
|
|
11
|
+
"time",
|
|
12
|
+
"level",
|
|
13
|
+
"msg",
|
|
14
|
+
"pid",
|
|
15
|
+
"host",
|
|
16
|
+
"service",
|
|
17
|
+
"version",
|
|
18
|
+
"module",
|
|
19
|
+
"extension",
|
|
20
|
+
"requestId",
|
|
21
|
+
"sessionId",
|
|
22
|
+
"userId",
|
|
23
|
+
"correlationId"
|
|
24
|
+
]);
|
|
25
|
+
const meta = {};
|
|
26
|
+
for (const [key, value] of Object.entries(parsed)) if (!reserved.has(key)) meta[key] = value;
|
|
27
|
+
const entry = {
|
|
28
|
+
timestamp: String(parsed.time ?? (/* @__PURE__ */ new Date()).toISOString()),
|
|
29
|
+
level: resolveLevel(parsed),
|
|
30
|
+
message: String(parsed.msg ?? ""),
|
|
31
|
+
module: typeof parsed.module === "string" ? parsed.module : void 0,
|
|
32
|
+
service: typeof parsed.service === "string" ? parsed.service : void 0,
|
|
33
|
+
extension: typeof parsed.extension === "string" ? parsed.extension : void 0,
|
|
34
|
+
requestId: typeof parsed.requestId === "string" ? parsed.requestId : void 0,
|
|
35
|
+
sessionId: typeof parsed.sessionId === "string" ? parsed.sessionId : void 0,
|
|
36
|
+
userId: typeof parsed.userId === "string" ? parsed.userId : void 0,
|
|
37
|
+
correlationId: typeof parsed.correlationId === "string" ? parsed.correlationId : void 0
|
|
38
|
+
};
|
|
39
|
+
if (Object.keys(meta).length > 0) entry.meta = meta;
|
|
40
|
+
return entry;
|
|
41
|
+
}
|
|
42
|
+
/** Build searchable text from a log entry (query + client-side filter). */
|
|
43
|
+
function logEntrySearchText(entry) {
|
|
44
|
+
const parts = [
|
|
45
|
+
entry.message,
|
|
46
|
+
entry.module,
|
|
47
|
+
entry.service,
|
|
48
|
+
entry.extension,
|
|
49
|
+
entry.requestId,
|
|
50
|
+
entry.sessionId,
|
|
51
|
+
typeof entry.phase === "string" ? entry.phase : void 0,
|
|
52
|
+
typeof entry.errorMessage === "string" ? entry.errorMessage : void 0
|
|
53
|
+
];
|
|
54
|
+
const meta = entry.meta && typeof entry.meta === "object" ? entry.meta : void 0;
|
|
55
|
+
const err = entry.err ?? meta?.err;
|
|
56
|
+
if (err && typeof err === "object" && err !== null) {
|
|
57
|
+
const e = err;
|
|
58
|
+
if (typeof e.message === "string") parts.push(e.message);
|
|
59
|
+
if (typeof e.name === "string") parts.push(e.name);
|
|
60
|
+
if (typeof e.stack === "string") parts.push(e.stack);
|
|
61
|
+
}
|
|
62
|
+
if (entry.meta && typeof entry.meta === "object") for (const [key, value] of Object.entries(entry.meta)) {
|
|
63
|
+
if (key === "err") continue;
|
|
64
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") parts.push(String(value));
|
|
65
|
+
}
|
|
66
|
+
return parts.filter(Boolean).map(String).join(" ").toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
var VALID_LEVELS;
|
|
69
|
+
var init_pino_record = __esmMin((() => {
|
|
70
|
+
VALID_LEVELS = new Set([
|
|
71
|
+
"trace",
|
|
72
|
+
"debug",
|
|
73
|
+
"info",
|
|
74
|
+
"warn",
|
|
75
|
+
"error",
|
|
76
|
+
"fatal"
|
|
77
|
+
]);
|
|
78
|
+
}));
|
|
79
|
+
//#endregion
|
|
80
|
+
init_pino_record();
|
|
81
|
+
export { init_pino_record, logEntrySearchText, pinoRecordToLogEntry };
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=pino-record.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pino-record.js","names":[],"sources":["../../../../src/utils/logger/pino-record.ts"],"sourcesContent":["/**\n * Shared Pino JSON record → LogEntry conversion for file query and live SSE.\n */\n\nimport type { LogEntry, LogLevel } from './types.js';\n\nconst VALID_LEVELS = new Set<LogLevel>(['trace', 'debug', 'info', 'warn', 'error', 'fatal']);\n\nfunction resolveLevel(parsed: Record<string, unknown>): LogLevel {\n if (typeof parsed.level !== 'string') {\n return 'info';\n }\n const lv = parsed.level.toLowerCase() as LogLevel;\n return VALID_LEVELS.has(lv) ? lv : 'info';\n}\n\n/** Convert a parsed Pino JSON object into a normalized LogEntry. */\nexport function pinoRecordToLogEntry(parsed: Record<string, unknown>): LogEntry {\n const reserved = new Set([\n 'time',\n 'level',\n 'msg',\n 'pid',\n 'host',\n 'service',\n 'version',\n 'module',\n 'extension',\n 'requestId',\n 'sessionId',\n 'userId',\n 'correlationId',\n ]);\n\n const meta: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(parsed)) {\n if (!reserved.has(key)) {\n meta[key] = value;\n }\n }\n\n const entry: LogEntry = {\n timestamp: String(parsed.time ?? new Date().toISOString()),\n level: resolveLevel(parsed),\n message: String(parsed.msg ?? ''),\n module: typeof parsed.module === 'string' ? parsed.module : undefined,\n service: typeof parsed.service === 'string' ? parsed.service : undefined,\n extension: typeof parsed.extension === 'string' ? parsed.extension : undefined,\n requestId: typeof parsed.requestId === 'string' ? parsed.requestId : undefined,\n sessionId: typeof parsed.sessionId === 'string' ? parsed.sessionId : undefined,\n userId: typeof parsed.userId === 'string' ? parsed.userId : undefined,\n correlationId: typeof parsed.correlationId === 'string' ? parsed.correlationId : undefined,\n };\n\n if (Object.keys(meta).length > 0) {\n entry.meta = meta;\n }\n\n return entry;\n}\n\n/** Build searchable text from a log entry (query + client-side filter). */\nexport function logEntrySearchText(entry: LogEntry): string {\n const parts: string[] = [\n entry.message,\n entry.module,\n entry.service,\n entry.extension,\n entry.requestId,\n entry.sessionId,\n typeof entry.phase === 'string' ? entry.phase : undefined,\n typeof entry.errorMessage === 'string' ? entry.errorMessage : undefined,\n ];\n\n const meta =\n entry.meta && typeof entry.meta === 'object'\n ? (entry.meta as Record<string, unknown>)\n : undefined;\n const err = entry.err ?? meta?.err;\n if (err && typeof err === 'object' && err !== null) {\n const e = err as Record<string, unknown>;\n if (typeof e.message === 'string') parts.push(e.message);\n if (typeof e.name === 'string') parts.push(e.name);\n if (typeof e.stack === 'string') parts.push(e.stack);\n }\n\n if (entry.meta && typeof entry.meta === 'object') {\n for (const [key, value] of Object.entries(entry.meta)) {\n if (key === 'err') continue;\n if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {\n parts.push(String(value));\n }\n }\n }\n\n return parts.filter(Boolean).map(String).join(' ').toLowerCase();\n}\n"],"mappings":";;AAQA,SAAS,aAAa,QAA2C;AAC/D,KAAI,OAAO,OAAO,UAAU,SAC1B,QAAO;CAET,MAAM,KAAK,OAAO,MAAM,aAAa;AACrC,QAAO,aAAa,IAAI,GAAG,GAAG,KAAK;;;AAIrC,SAAgB,qBAAqB,QAA2C;CAC9E,MAAM,WAAW,IAAI,IAAI;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAEF,MAAM,OAAgC,EAAE;AACxC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,CAC/C,KAAI,CAAC,SAAS,IAAI,IAAI,CACpB,MAAK,OAAO;CAIhB,MAAM,QAAkB;EACtB,WAAW,OAAO,OAAO,yBAAQ,IAAI,MAAM,EAAC,aAAa,CAAC;EAC1D,OAAO,aAAa,OAAO;EAC3B,SAAS,OAAO,OAAO,OAAO,GAAG;EACjC,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,KAAA;EAC5D,SAAS,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU,KAAA;EAC/D,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY,KAAA;EACrE,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY,KAAA;EACrE,WAAW,OAAO,OAAO,cAAc,WAAW,OAAO,YAAY,KAAA;EACrE,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS,KAAA;EAC5D,eAAe,OAAO,OAAO,kBAAkB,WAAW,OAAO,gBAAgB,KAAA;EAClF;AAED,KAAI,OAAO,KAAK,KAAK,CAAC,SAAS,EAC7B,OAAM,OAAO;AAGf,QAAO;;;AAIT,SAAgB,mBAAmB,OAAyB;CAC1D,MAAM,QAAkB;EACtB,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,KAAA;EAChD,OAAO,MAAM,iBAAiB,WAAW,MAAM,eAAe,KAAA;EAC/D;CAED,MAAM,OACJ,MAAM,QAAQ,OAAO,MAAM,SAAS,WAC/B,MAAM,OACP,KAAA;CACN,MAAM,MAAM,MAAM,OAAO,MAAM;AAC/B,KAAI,OAAO,OAAO,QAAQ,YAAY,QAAQ,MAAM;EAClD,MAAM,IAAI;AACV,MAAI,OAAO,EAAE,YAAY,SAAU,OAAM,KAAK,EAAE,QAAQ;AACxD,MAAI,OAAO,EAAE,SAAS,SAAU,OAAM,KAAK,EAAE,KAAK;AAClD,MAAI,OAAO,EAAE,UAAU,SAAU,OAAM,KAAK,EAAE,MAAM;;AAGtD,KAAI,MAAM,QAAQ,OAAO,MAAM,SAAS,SACtC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,KAAK,EAAE;AACrD,MAAI,QAAQ,MAAO;AACnB,MAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,UAC7E,OAAM,KAAK,OAAO,MAAM,CAAC;;AAK/B,QAAO,MAAM,OAAO,QAAQ,CAAC,IAAI,OAAO,CAAC,KAAK,IAAI,CAAC,aAAa;;;;AAzF5D,gBAAe,IAAI,IAAc;EAAC;EAAS;EAAS;EAAQ;EAAQ;EAAS;EAAQ,CAAC"}
|
|
@@ -10,7 +10,7 @@ export declare function incrementStats(level: LogLevel, module?: string): void;
|
|
|
10
10
|
/**
|
|
11
11
|
* Get current statistics
|
|
12
12
|
*/
|
|
13
|
-
export declare function
|
|
13
|
+
export declare function getRuntimeLogStats(): {
|
|
14
14
|
byLevel: {
|
|
15
15
|
error: number;
|
|
16
16
|
debug: number;
|
|
@@ -19,7 +19,7 @@ function incrementStats(level, module) {
|
|
|
19
19
|
/**
|
|
20
20
|
* Get current statistics
|
|
21
21
|
*/
|
|
22
|
-
function
|
|
22
|
+
function getRuntimeLogStats() {
|
|
23
23
|
return {
|
|
24
24
|
byLevel: { ...stats.byLevel },
|
|
25
25
|
byModule: Object.fromEntries(stats.byModule),
|
|
@@ -65,6 +65,6 @@ var init_stats = __esmMin((() => {
|
|
|
65
65
|
}));
|
|
66
66
|
//#endregion
|
|
67
67
|
init_stats();
|
|
68
|
-
export {
|
|
68
|
+
export { getRuntimeLogStats, incrementStats, init_stats, resetStats };
|
|
69
69
|
|
|
70
70
|
//# sourceMappingURL=stats.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.js","names":[],"sources":["../../../../src/utils/logger/stats.ts"],"sourcesContent":["/**\n * Log Statistics\n * Track log counts by level and module\n */\n\nimport type { LogLevel } from './types.js';\n\ninterface StatsData {\n byLevel: Record<LogLevel, number>;\n byModule: Map<string, number>;\n errorsLast24h: number;\n lastErrorTime: number;\n startTime: number;\n}\n\nconst stats: StatsData = {\n byLevel: { trace: 0, debug: 0, info: 0, warn: 0, error: 0, fatal: 0, silent: 0 },\n byModule: new Map(),\n errorsLast24h: 0,\n lastErrorTime: 0,\n startTime: Date.now(),\n};\n\n/**\n * Increment statistics for a log entry\n */\nexport function incrementStats(level: LogLevel, module?: string): void {\n stats.byLevel[level]++;\n \n if (module) {\n const current = stats.byModule.get(module) || 0;\n stats.byModule.set(module, current + 1);\n }\n \n if (level === 'error' || level === 'fatal') {\n const now = Date.now();\n if (now - stats.lastErrorTime > 24 * 60 * 60 * 1000) {\n stats.errorsLast24h = 0;\n }\n stats.errorsLast24h++;\n stats.lastErrorTime = now;\n }\n}\n\n/**\n * Get current statistics\n */\nexport function
|
|
1
|
+
{"version":3,"file":"stats.js","names":[],"sources":["../../../../src/utils/logger/stats.ts"],"sourcesContent":["/**\n * Log Statistics\n * Track log counts by level and module\n */\n\nimport type { LogLevel } from './types.js';\n\ninterface StatsData {\n byLevel: Record<LogLevel, number>;\n byModule: Map<string, number>;\n errorsLast24h: number;\n lastErrorTime: number;\n startTime: number;\n}\n\nconst stats: StatsData = {\n byLevel: { trace: 0, debug: 0, info: 0, warn: 0, error: 0, fatal: 0, silent: 0 },\n byModule: new Map(),\n errorsLast24h: 0,\n lastErrorTime: 0,\n startTime: Date.now(),\n};\n\n/**\n * Increment statistics for a log entry\n */\nexport function incrementStats(level: LogLevel, module?: string): void {\n stats.byLevel[level]++;\n \n if (module) {\n const current = stats.byModule.get(module) || 0;\n stats.byModule.set(module, current + 1);\n }\n \n if (level === 'error' || level === 'fatal') {\n const now = Date.now();\n if (now - stats.lastErrorTime > 24 * 60 * 60 * 1000) {\n stats.errorsLast24h = 0;\n }\n stats.errorsLast24h++;\n stats.lastErrorTime = now;\n }\n}\n\n/**\n * Get current statistics\n */\nexport function getRuntimeLogStats() {\n return {\n byLevel: { ...stats.byLevel },\n byModule: Object.fromEntries(stats.byModule),\n errorsLast24h: stats.errorsLast24h,\n uptimeMs: Date.now() - stats.startTime,\n };\n}\n\n/**\n * Reset statistics (for testing)\n */\nexport function resetStats(): void {\n stats.byLevel = { trace: 0, debug: 0, info: 0, warn: 0, error: 0, fatal: 0, silent: 0 };\n stats.byModule.clear();\n stats.errorsLast24h = 0;\n stats.lastErrorTime = 0;\n stats.startTime = Date.now();\n}\n"],"mappings":";;;;;AA0BA,SAAgB,eAAe,OAAiB,QAAuB;AACrE,OAAM,QAAQ;AAEd,KAAI,QAAQ;EACV,MAAM,UAAU,MAAM,SAAS,IAAI,OAAO,IAAI;AAC9C,QAAM,SAAS,IAAI,QAAQ,UAAU,EAAE;;AAGzC,KAAI,UAAU,WAAW,UAAU,SAAS;EAC1C,MAAM,MAAM,KAAK,KAAK;AACtB,MAAI,MAAM,MAAM,gBAAgB,OAAU,KAAK,IAC7C,OAAM,gBAAgB;AAExB,QAAM;AACN,QAAM,gBAAgB;;;;;;AAO1B,SAAgB,qBAAqB;AACnC,QAAO;EACL,SAAS,EAAE,GAAG,MAAM,SAAS;EAC7B,UAAU,OAAO,YAAY,MAAM,SAAS;EAC5C,eAAe,MAAM;EACrB,UAAU,KAAK,KAAK,GAAG,MAAM;EAC9B;;;;;AAMH,SAAgB,aAAmB;AACjC,OAAM,UAAU;EAAE,OAAO;EAAG,OAAO;EAAG,MAAM;EAAG,MAAM;EAAG,OAAO;EAAG,OAAO;EAAG,QAAQ;EAAG;AACvF,OAAM,SAAS,OAAO;AACtB,OAAM,gBAAgB;AACtB,OAAM,gBAAgB;AACtB,OAAM,YAAY,KAAK,KAAK;;;;AAjDxB,SAAmB;EACvB,SAAS;GAAE,OAAO;GAAG,OAAO;GAAG,MAAM;GAAG,MAAM;GAAG,OAAO;GAAG,OAAO;GAAG,QAAQ;GAAG;EAChF,0BAAU,IAAI,KAAK;EACnB,eAAe;EACf,eAAe;EACf,WAAW,KAAK,KAAK;EACtB"}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { __esmMin } from "../../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { config, getLogDir, init_config } from "./config.js";
|
|
3
|
+
import { emitLogEntry, hasSubscribers, init_log_stream } from "./log-stream.js";
|
|
4
|
+
import { init_pino_record, pinoRecordToLogEntry } from "./pino-record.js";
|
|
3
5
|
import path from "path";
|
|
4
6
|
import { createWriteStream, existsSync, mkdirSync } from "fs";
|
|
7
|
+
import { Writable } from "stream";
|
|
5
8
|
//#region src/utils/logger/streams.ts
|
|
6
9
|
/**
|
|
7
10
|
* Log Streams
|
|
@@ -29,12 +32,25 @@ function createLogStream(filePath) {
|
|
|
29
32
|
encoding: "utf-8"
|
|
30
33
|
});
|
|
31
34
|
}
|
|
35
|
+
/** Push parsed Pino lines to live SSE subscribers when any are connected. */
|
|
36
|
+
function createLiveEmitStream() {
|
|
37
|
+
return new Writable({ write(chunk, _encoding, callback) {
|
|
38
|
+
if (hasSubscribers()) try {
|
|
39
|
+
emitLogEntry(pinoRecordToLogEntry(JSON.parse(chunk.toString())));
|
|
40
|
+
} catch {}
|
|
41
|
+
callback();
|
|
42
|
+
} });
|
|
43
|
+
}
|
|
32
44
|
/**
|
|
33
45
|
* Initialize and get all output streams
|
|
34
46
|
*/
|
|
35
47
|
function initializeStreams() {
|
|
36
48
|
ensureLogDir();
|
|
37
49
|
const streams = [];
|
|
50
|
+
streams.push({
|
|
51
|
+
stream: createLiveEmitStream(),
|
|
52
|
+
level: "trace"
|
|
53
|
+
});
|
|
38
54
|
if (config.consoleOutput) streams.push({
|
|
39
55
|
stream: process.stdout,
|
|
40
56
|
level: config.level
|
|
@@ -76,6 +92,8 @@ async function closeStreams() {
|
|
|
76
92
|
var fileStreams;
|
|
77
93
|
var init_streams = __esmMin((() => {
|
|
78
94
|
init_config();
|
|
95
|
+
init_log_stream();
|
|
96
|
+
init_pino_record();
|
|
79
97
|
fileStreams = [];
|
|
80
98
|
}));
|
|
81
99
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streams.js","names":[],"sources":["../../../../src/utils/logger/streams.ts"],"sourcesContent":["/**\n * Log Streams\n * Output stream management (console, file, error file)\n */\n\nimport { createWriteStream } from 'fs';\nimport { existsSync, mkdirSync } from 'fs';\nimport path from 'path';\nimport type { DestinationStream } from 'pino';\nimport type { LogLevel } from './types.js';\nimport { config, getLogDir } from './config.js';\n\n// Store file stream references for later cleanup\nconst fileStreams: ReturnType<typeof createWriteStream>[] = [];\n\n/**\n * Get log file path for a specific date and type\n */\nexport function getLogPath(type: 'app' | 'error' | 'audit' | 'access' = 'app', date: Date = new Date()): string {\n const dateStr = date.toISOString().split('T')[0];\n return path.join(getLogDir(), `${type}-${dateStr}.log`);\n}\n\n/**\n * Ensure log directory exists\n */\nfunction ensureLogDir(): void {\n if (!existsSync(config.logDir)) {\n mkdirSync(config.logDir, { recursive: true });\n }\n}\n\n/**\n * Create log stream\n */\nfunction createLogStream(filePath: string): ReturnType<typeof createWriteStream> {\n return createWriteStream(filePath, { flags: 'a', encoding: 'utf-8' });\n}\n\n/**\n * Initialize and get all output streams\n */\nexport function initializeStreams(): Array<{ stream: DestinationStream | NodeJS.WriteStream; level: LogLevel }> {\n ensureLogDir();\n \n const streams: Array<{ stream: DestinationStream | NodeJS.WriteStream; level: LogLevel }> = [];\n\n if (config.consoleOutput) {\n streams.push({\n stream: process.stdout as unknown as DestinationStream,\n level: config.level,\n });\n }\n\n if (config.fileOutput) {\n const appStream = createLogStream(getLogPath('app'));\n fileStreams.push(appStream);\n streams.push({\n stream: appStream as unknown as DestinationStream,\n level: config.level,\n });\n }\n\n if (config.errorFileOutput) {\n const errorStream = createLogStream(getLogPath('error'));\n fileStreams.push(errorStream);\n streams.push({\n stream: errorStream as unknown as DestinationStream,\n level: 'error',\n });\n }\n\n return streams;\n}\n\n/**\n * Get all file streams for cleanup\n */\nexport function getFileStreams(): ReturnType<typeof createWriteStream>[] {\n return fileStreams;\n}\n\n/**\n * Close all file streams gracefully\n */\nexport async function closeStreams(): Promise<void> {\n await Promise.all(\n fileStreams.map(\n (stream) =>\n new Promise<void>((resolve) => {\n if (stream.writable) {\n stream.end(() => resolve());\n } else {\n resolve();\n }\n }),\n ),\n );\n fileStreams.length = 0;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"streams.js","names":[],"sources":["../../../../src/utils/logger/streams.ts"],"sourcesContent":["/**\n * Log Streams\n * Output stream management (console, file, error file)\n */\n\nimport { createWriteStream } from 'fs';\nimport { existsSync, mkdirSync } from 'fs';\nimport path from 'path';\nimport { Writable } from 'stream';\nimport type { DestinationStream } from 'pino';\nimport type { LogLevel } from './types.js';\nimport { config, getLogDir } from './config.js';\nimport { emitLogEntry, hasSubscribers } from './log-stream.js';\nimport { pinoRecordToLogEntry } from './pino-record.js';\n\n// Store file stream references for later cleanup\nconst fileStreams: ReturnType<typeof createWriteStream>[] = [];\n\n/**\n * Get log file path for a specific date and type\n */\nexport function getLogPath(type: 'app' | 'error' | 'audit' | 'access' = 'app', date: Date = new Date()): string {\n const dateStr = date.toISOString().split('T')[0];\n return path.join(getLogDir(), `${type}-${dateStr}.log`);\n}\n\n/**\n * Ensure log directory exists\n */\nfunction ensureLogDir(): void {\n if (!existsSync(config.logDir)) {\n mkdirSync(config.logDir, { recursive: true });\n }\n}\n\n/**\n * Create log stream\n */\nfunction createLogStream(filePath: string): ReturnType<typeof createWriteStream> {\n return createWriteStream(filePath, { flags: 'a', encoding: 'utf-8' });\n}\n\n/** Push parsed Pino lines to live SSE subscribers when any are connected. */\nfunction createLiveEmitStream(): DestinationStream {\n return new Writable({\n write(chunk, _encoding, callback) {\n if (hasSubscribers()) {\n try {\n const parsed = JSON.parse(chunk.toString()) as Record<string, unknown>;\n emitLogEntry(pinoRecordToLogEntry(parsed));\n } catch {\n /* ignore non-JSON lines (e.g. pretty console) */\n }\n }\n callback();\n },\n }) as unknown as DestinationStream;\n}\n\n/**\n * Initialize and get all output streams\n */\nexport function initializeStreams(): Array<{ stream: DestinationStream | NodeJS.WriteStream; level: LogLevel }> {\n ensureLogDir();\n \n const streams: Array<{ stream: DestinationStream | NodeJS.WriteStream; level: LogLevel }> = [];\n\n streams.push({\n stream: createLiveEmitStream(),\n level: 'trace',\n });\n\n if (config.consoleOutput) {\n streams.push({\n stream: process.stdout as unknown as DestinationStream,\n level: config.level,\n });\n }\n\n if (config.fileOutput) {\n const appStream = createLogStream(getLogPath('app'));\n fileStreams.push(appStream);\n streams.push({\n stream: appStream as unknown as DestinationStream,\n level: config.level,\n });\n }\n\n if (config.errorFileOutput) {\n const errorStream = createLogStream(getLogPath('error'));\n fileStreams.push(errorStream);\n streams.push({\n stream: errorStream as unknown as DestinationStream,\n level: 'error',\n });\n }\n\n return streams;\n}\n\n/**\n * Get all file streams for cleanup\n */\nexport function getFileStreams(): ReturnType<typeof createWriteStream>[] {\n return fileStreams;\n}\n\n/**\n * Close all file streams gracefully\n */\nexport async function closeStreams(): Promise<void> {\n await Promise.all(\n fileStreams.map(\n (stream) =>\n new Promise<void>((resolve) => {\n if (stream.writable) {\n stream.end(() => resolve());\n } else {\n resolve();\n }\n }),\n ),\n );\n fileStreams.length = 0;\n}\n"],"mappings":";;;;;;;;;;;;;;;AAqBA,SAAgB,WAAW,OAA6C,OAAO,uBAAa,IAAI,MAAM,EAAU;CAC9G,MAAM,UAAU,KAAK,aAAa,CAAC,MAAM,IAAI,CAAC;AAC9C,QAAO,KAAK,KAAK,WAAW,EAAE,GAAG,KAAK,GAAG,QAAQ,MAAM;;;;;AAMzD,SAAS,eAAqB;AAC5B,KAAI,CAAC,WAAW,OAAO,OAAO,CAC5B,WAAU,OAAO,QAAQ,EAAE,WAAW,MAAM,CAAC;;;;;AAOjD,SAAS,gBAAgB,UAAwD;AAC/E,QAAO,kBAAkB,UAAU;EAAE,OAAO;EAAK,UAAU;EAAS,CAAC;;;AAIvE,SAAS,uBAA0C;AACjD,QAAO,IAAI,SAAS,EAClB,MAAM,OAAO,WAAW,UAAU;AAChC,MAAI,gBAAgB,CAClB,KAAI;AAEF,gBAAa,qBADE,KAAK,MAAM,MAAM,UAAU,CACF,CAAC,CAAC;UACpC;AAIV,YAAU;IAEb,CAAC;;;;;AAMJ,SAAgB,oBAAgG;AAC9G,eAAc;CAEd,MAAM,UAAsF,EAAE;AAE9F,SAAQ,KAAK;EACX,QAAQ,sBAAsB;EAC9B,OAAO;EACR,CAAC;AAEF,KAAI,OAAO,cACT,SAAQ,KAAK;EACX,QAAQ,QAAQ;EAChB,OAAO,OAAO;EACf,CAAC;AAGJ,KAAI,OAAO,YAAY;EACrB,MAAM,YAAY,gBAAgB,WAAW,MAAM,CAAC;AACpD,cAAY,KAAK,UAAU;AAC3B,UAAQ,KAAK;GACX,QAAQ;GACR,OAAO,OAAO;GACf,CAAC;;AAGJ,KAAI,OAAO,iBAAiB;EAC1B,MAAM,cAAc,gBAAgB,WAAW,QAAQ,CAAC;AACxD,cAAY,KAAK,YAAY;AAC7B,UAAQ,KAAK;GACX,QAAQ;GACR,OAAO;GACR,CAAC;;AAGJ,QAAO;;;;;AAMT,SAAgB,iBAAyD;AACvE,QAAO;;;;;AAMT,eAAsB,eAA8B;AAClD,OAAM,QAAQ,IACZ,YAAY,KACT,WACC,IAAI,SAAe,YAAY;AAC7B,MAAI,OAAO,SACT,QAAO,UAAU,SAAS,CAAC;MAE3B,UAAS;GAEX,CACL,CACF;AACD,aAAY,SAAS;;;;cAhHyB;kBACe;mBACP;AAGlD,eAAsD,EAAE"}
|
|
@@ -12,7 +12,6 @@ export interface LogEntry {
|
|
|
12
12
|
level: LogLevel;
|
|
13
13
|
message: string;
|
|
14
14
|
module?: string;
|
|
15
|
-
prefix?: string;
|
|
16
15
|
service?: string;
|
|
17
16
|
extension?: string;
|
|
18
17
|
requestId?: string;
|
|
@@ -66,10 +65,6 @@ export interface ContextualLogger extends Logger {
|
|
|
66
65
|
* Set default context for all subsequent logs
|
|
67
66
|
*/
|
|
68
67
|
withContext(context: LogContext): ContextualLogger;
|
|
69
|
-
/**
|
|
70
|
-
* Create a child logger with additional context
|
|
71
|
-
*/
|
|
72
|
-
childContext(context: LogContext): ContextualLogger;
|
|
73
68
|
}
|
|
74
69
|
/**
|
|
75
70
|
* Logger configuration options
|
|
@@ -91,10 +86,6 @@ export interface LoggerConfig {
|
|
|
91
86
|
maxFileSizeMB: number;
|
|
92
87
|
/** Enable pretty print for console (dev only) */
|
|
93
88
|
prettyPrint: boolean;
|
|
94
|
-
/** Enable async logging */
|
|
95
|
-
async: boolean;
|
|
96
|
-
/** Sample rate for debug logs (0-1, 1 = all) */
|
|
97
|
-
debugSampleRate?: number;
|
|
98
89
|
}
|
|
99
90
|
/**
|
|
100
91
|
* Log file metadata
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/utils/logger/types.ts"],"sourcesContent":["/**\n * Logger Types\n * \n * Centralized type definitions for the logging system.\n */\n\nimport type { Logger } from 'pino';\n\n/**\n * Basic log entry structure\n */\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n module?: string;\n
|
|
1
|
+
{"version":3,"file":"types.js","names":[],"sources":["../../../../src/utils/logger/types.ts"],"sourcesContent":["/**\n * Logger Types\n * \n * Centralized type definitions for the logging system.\n */\n\nimport type { Logger } from 'pino';\n\n/**\n * Basic log entry structure\n */\nexport interface LogEntry {\n timestamp: string;\n level: LogLevel;\n message: string;\n module?: string;\n service?: string;\n extension?: string;\n requestId?: string;\n sessionId?: string;\n userId?: string;\n [key: string]: unknown;\n}\n\n/**\n * Log levels in order of severity\n */\nexport const LogLevel = {\n TRACE: 'trace',\n DEBUG: 'debug',\n INFO: 'info',\n WARN: 'warn',\n ERROR: 'error',\n FATAL: 'fatal',\n SILENT: 'silent',\n} as const;\n\nexport type LogLevel = typeof LogLevel[keyof typeof LogLevel];\n\n/**\n * Log level numeric values (for comparison)\n */\nexport const LogLevelValue: Record<LogLevel, number> = {\n trace: 10,\n debug: 20,\n info: 30,\n warn: 40,\n error: 50,\n fatal: 60,\n silent: Number.MAX_VALUE,\n};\n\n/**\n * Common log context fields\n */\nexport interface LogContext {\n /** Request/operation ID for tracing */\n requestId?: string;\n /** Session ID for user tracking */\n sessionId?: string;\n /** Cross-service trace id when distinct from requestId */\n correlationId?: string;\n /** User ID */\n userId?: string;\n /** Module/component name */\n module?: string;\n /** Extension name */\n extension?: string;\n /** Service name */\n service?: string;\n /** Additional context */\n [key: string]: unknown;\n}\n\n/**\n * Extended logger with context support\n */\nexport interface ContextualLogger extends Logger {\n /**\n * Set default context for all subsequent logs\n */\n withContext(context: LogContext): ContextualLogger;\n}\n\n/**\n * Logger configuration options\n */\nexport interface LoggerConfig {\n /** Minimum log level */\n level: LogLevel;\n /** Log directory */\n logDir: string;\n /** Enable console output */\n consoleOutput: boolean;\n /** Enable file output */\n fileOutput: boolean;\n /** Enable error-only file output */\n errorFileOutput: boolean;\n /** Log rotation: max days to keep */\n retentionDays: number;\n /** Log rotation: max file size in MB */\n maxFileSizeMB: number;\n /** Enable pretty print for console (dev only) */\n prettyPrint: boolean;\n}\n\n/**\n * Log file metadata\n */\nexport interface LogFileMeta {\n name: string;\n path: string;\n size: number;\n created: string;\n modified: string;\n type: 'app' | 'error' | 'audit' | 'access';\n lines?: number;\n}\n\n/**\n * Log query options\n */\nexport interface LogQuery {\n /** Filter by log levels */\n levels?: LogLevel[];\n /** Start timestamp (ISO 8601) */\n from?: string;\n /** End timestamp (ISO 8601) */\n to?: string;\n /** Keyword search */\n q?: string;\n /** Filter by module */\n module?: string;\n /** Filter by extension */\n extension?: string;\n /** Filter by service */\n service?: string;\n /** Filter by request ID */\n requestId?: string;\n /** Filter by session ID */\n sessionId?: string;\n /** Pagination offset */\n offset?: number;\n /** Pagination limit */\n limit?: number;\n /** Sort order: 'asc' | 'desc' */\n order?: 'asc' | 'desc';\n}\n\n/**\n * Log statistics\n */\nexport interface LogStats {\n byLevel: Record<LogLevel, number>;\n}\n\n/**\n * Log rotation result\n */\nexport interface RotationResult {\n rotated: number;\n deleted: number;\n compressed: number;\n errors: string[];\n}\n"],"mappings":";;;;AA2BA,MAAa,WAAW;CACtB,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ;CACT;;;;AAOD,MAAa,gBAA0C;CACrD,OAAO;CACP,OAAO;CACP,MAAM;CACN,MAAM;CACN,OAAO;CACP,OAAO;CACP,QAAQ,OAAO;CAChB"}
|
package/dist/src/utils/logger.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { getLogDir, getLoggerConfig } from "./logger/config.js";
|
|
3
|
-
import {
|
|
3
|
+
import { getRuntimeLogStats } from "./logger/stats.js";
|
|
4
4
|
import { flushAndClose, isLoggerShuttingDown, setShuttingDown } from "./logger/shutdown.js";
|
|
5
5
|
import { cleanOldLogs, rotateLogs } from "./logger/rotation.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getAsyncLogContext, inboundCorrelationMetadataFromAsyncLogContext, runWithLogContext, updateAsyncLogContext } from "./logger/context.js";
|
|
7
7
|
import { isLogRedactionEnabled, redactObject, redactPemBlock, redactSecret, redactSensitiveInfo } from "./logger/redact.js";
|
|
8
8
|
import { exportLog, flushExporters, getExporters, initializeExporters } from "./logger/exporters.js";
|
|
9
9
|
import { configureAuditLog, getAuditConfig, logAuditEvent, logAuthEvent, logConfigChange, logDataAccess, logPermissionChange } from "./logger/audit.js";
|
|
10
|
-
import { Pino as pino, baseLogger as logger, createExtensionLogger, createLogger, createModuleLogger,
|
|
10
|
+
import { Pino as pino, baseLogger as logger, createExtensionLogger, createLogger, createModuleLogger, createServiceLogger, getLogLevel, init_logger as init_logger$1, isLevelEnabled, setLogLevel, withLogLevel } from "./logger/index.js";
|
|
11
11
|
//#region src/utils/logger.ts
|
|
12
12
|
var init_logger = __esmMin((() => {
|
|
13
13
|
init_logger$1();
|
|
14
14
|
}));
|
|
15
15
|
//#endregion
|
|
16
16
|
init_logger();
|
|
17
|
-
export { pino as Pino, logger as baseLogger, logger, cleanOldLogs,
|
|
17
|
+
export { pino as Pino, logger as baseLogger, logger, cleanOldLogs, configureAuditLog, createExtensionLogger, createLogger, createModuleLogger, createServiceLogger, exportLog, flushAndClose, flushExporters, getAsyncLogContext, getAuditConfig, getExporters, getLogDir, getLogLevel, getLoggerConfig, getRuntimeLogStats, inboundCorrelationMetadataFromAsyncLogContext, init_logger, initializeExporters, isLevelEnabled, isLogRedactionEnabled, isLoggerShuttingDown, logAuditEvent, logAuthEvent, logConfigChange, logDataAccess, logPermissionChange, redactObject, redactPemBlock, redactSecret, redactSensitiveInfo, rotateLogs, runWithLogContext, setLogLevel, setShuttingDown, updateAsyncLogContext, withLogLevel };
|
|
18
18
|
|
|
19
19
|
//# sourceMappingURL=logger.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xopcai/xopc",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.94",
|
|
4
4
|
"description": "The OPC workstation that grows with you: AI assistant for One Person Companies — CLI, gateway, multi-channel (Telegram/WeChat), 20+ LLM providers via pi-ai, extensions and skills.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/src/index.js",
|
|
@@ -87,6 +87,7 @@
|
|
|
87
87
|
"node-edge-tts": "^1.2.10",
|
|
88
88
|
"openai": "^6.39.1",
|
|
89
89
|
"pino": "^10.3.1",
|
|
90
|
+
"pino-pretty": "^13.1.3",
|
|
90
91
|
"playwright-core": "^1.60.0",
|
|
91
92
|
"proper-lockfile": "^4.1.2",
|
|
92
93
|
"semver": "^7.8.1",
|
|
@@ -133,6 +134,10 @@
|
|
|
133
134
|
"build:types": "tsc -p tsconfig.dts.json",
|
|
134
135
|
"build:ci": "pnpm run generate:bundled && pnpm run build:node && pnpm run build:web",
|
|
135
136
|
"build": "pnpm run generate:bundled && pnpm run build:node && pnpm run build:types && pnpm run build:web",
|
|
137
|
+
"docker:build": "sh -c 'docker buildx version >/dev/null 2>&1 || { echo \"Docker buildx is required. Install it with: brew install docker-buildx && mkdir -p ~/.docker/cli-plugins && ln -sf $(brew --prefix)/opt/docker-buildx/bin/docker-buildx ~/.docker/cli-plugins/docker-buildx\" >&2; exit 1; }; DOCKER_BUILDKIT=1 docker build --build-arg XOPC_APT_MIRROR=\"${XOPC_APT_MIRROR:-}\" -t xopc:local .'",
|
|
138
|
+
"docker:run": "docker run --rm -it -p 18790:18790 -v ${HOME}/.xopc:/home/node/.xopc xopc:local",
|
|
139
|
+
"docker:compose:up": "sh -c 'if docker compose version >/dev/null 2>&1; then DOCKER_BUILDKIT=1 docker compose up --build xopc-gateway; else COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker-compose up --build xopc-gateway; fi'",
|
|
140
|
+
"docker:compose:down": "sh -c 'if docker compose version >/dev/null 2>&1; then docker compose down; else docker-compose down; fi'",
|
|
136
141
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
137
142
|
"build:web": "pnpm -C web run build",
|
|
138
143
|
"dev:web": "pnpm -C web run dev",
|