@dollhousemcp/mcp-server 2.0.10 → 2.0.11

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.
Files changed (36) hide show
  1. package/dist/auto-dollhouse/portDiscovery.d.ts +23 -0
  2. package/dist/auto-dollhouse/portDiscovery.d.ts.map +1 -0
  3. package/dist/auto-dollhouse/portDiscovery.js +77 -0
  4. package/dist/cli/console-token.d.ts +18 -0
  5. package/dist/cli/console-token.d.ts.map +1 -0
  6. package/dist/cli/console-token.js +187 -0
  7. package/dist/generated/version.d.ts +2 -2
  8. package/dist/generated/version.js +3 -3
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +24 -5
  11. package/dist/web/console/consoleToken.d.ts +403 -0
  12. package/dist/web/console/consoleToken.d.ts.map +1 -0
  13. package/dist/web/console/consoleToken.js +930 -0
  14. package/dist/web/middleware/authMiddleware.d.ts +64 -0
  15. package/dist/web/middleware/authMiddleware.d.ts.map +1 -0
  16. package/dist/web/middleware/authMiddleware.js +174 -0
  17. package/dist/web/routes/consoleRouteHelpers.d.ts +33 -0
  18. package/dist/web/routes/consoleRouteHelpers.d.ts.map +1 -0
  19. package/dist/web/routes/consoleRouteHelpers.js +60 -0
  20. package/dist/web/routes/tokenRoutes.d.ts +37 -0
  21. package/dist/web/routes/tokenRoutes.d.ts.map +1 -0
  22. package/dist/web/routes/tokenRoutes.js +95 -0
  23. package/dist/web/routes/totpRoutes.d.ts +45 -0
  24. package/dist/web/routes/totpRoutes.d.ts.map +1 -0
  25. package/dist/web/routes/totpRoutes.js +187 -0
  26. package/package.json +1 -1
  27. package/server.json +2 -2
  28. package/dist/constants/version.d.ts +0 -3
  29. package/dist/constants/version.d.ts.map +0 -1
  30. package/dist/constants/version.js +0 -4
  31. package/dist/logging/sinks/SSELogSink.d.ts +0 -35
  32. package/dist/logging/sinks/SSELogSink.d.ts.map +0 -1
  33. package/dist/logging/sinks/SSELogSink.js +0 -181
  34. package/dist/logging/viewer/viewerHtml.d.ts +0 -8
  35. package/dist/logging/viewer/viewerHtml.d.ts.map +0 -1
  36. package/dist/logging/viewer/viewerHtml.js +0 -204
@@ -0,0 +1,187 @@
1
+ /**
2
+ * TOTP (authenticator) enrollment HTTP routes — Phase 2 of #1780 (#1794).
3
+ *
4
+ * Provides:
5
+ * - GET /api/console/totp/status — enrollment state (no secrets)
6
+ * - POST /api/console/totp/enroll/begin — generate secret, return QR + otpauth URI
7
+ * - POST /api/console/totp/enroll/confirm — verify code, persist, return backup codes (once)
8
+ * - POST /api/console/totp/disable — verify code, clear enrollment
9
+ *
10
+ * Security model:
11
+ * - All endpoints require a valid existing console token. The caller must
12
+ * prove they already hold the token before they can enroll a second
13
+ * factor — otherwise an attacker with local port access could pre-enroll
14
+ * their own authenticator and lock the legitimate user out.
15
+ * - Enforcement happens via an always-on `createAuthMiddleware` instance
16
+ * mounted at the top of this router, independent of the global
17
+ * DOLLHOUSE_WEB_AUTH_ENABLED flag.
18
+ * - Backup codes are returned in plaintext exactly once (confirm response)
19
+ * and only their sha256 hashes are retained by the store.
20
+ * - A sliding-window rate limit throttles confirm/disable attempts on a
21
+ * per-IP basis so a bad actor with a live session can't brute-force a
22
+ * TOTP window by flooding requests.
23
+ *
24
+ * @since v2.1.0 — Issue #1794
25
+ */
26
+ import express, { Router } from 'express';
27
+ import QRCode from 'qrcode';
28
+ import { TotpError } from '../console/consoleToken.js';
29
+ import { createAuthMiddleware } from '../middleware/authMiddleware.js';
30
+ import { SlidingWindowRateLimiter } from '../../utils/SlidingWindowRateLimiter.js';
31
+ import { httpStatusForStoreError, sendStoreError, getNormalizedStringField } from './consoleRouteHelpers.js';
32
+ import { logger } from '../../utils/logger.js';
33
+ /** JSON body size limit — TOTP requests are tiny, cap hard. */
34
+ const BODY_LIMIT = '1kb';
35
+ /**
36
+ * Default rate limit for code-verification endpoints: 10 attempts per minute.
37
+ * TOTP codes are 6 digits (1-in-10^6 guess rate) and the ±30s window gives
38
+ * an attacker up to 3 valid codes per minute. 10 attempts caps brute-force
39
+ * success probability per minute at ~3e-5 even before network latency.
40
+ *
41
+ * Limiters are per-endpoint (confirm vs disable) because they protect
42
+ * different secrets: confirm tests the in-memory pending enrollment secret
43
+ * (bounded lifetime, attacker must also know the pendingId), disable tests
44
+ * the persisted enrollment secret (long-lived). A single shared limiter
45
+ * would let traffic on one endpoint exhaust budget on the other.
46
+ *
47
+ * Limiters are global (not per-IP) because the server binds to 127.0.0.1
48
+ * only — every request comes from the same loopback address, so keying on
49
+ * IP would collapse to a single bucket anyway.
50
+ *
51
+ * Tests construct a fresh router per `buildApp`, which yields fresh
52
+ * limiters, so no cross-test pollution.
53
+ */
54
+ const DEFAULT_RATE_LIMIT_MAX = 10;
55
+ const DEFAULT_RATE_LIMIT_WINDOW_MS = 60_000;
56
+ /**
57
+ * Render an otpauth URI as an SVG data URL suitable for direct embedding
58
+ * in an <img src> or background-image. Separated into a helper so the
59
+ * request handler stays readable.
60
+ */
61
+ async function renderQrDataUrl(otpauthUri) {
62
+ // errorCorrectionLevel 'M' is the default and balances size vs robustness.
63
+ // We emit SVG (not PNG) because it scales to any container size and is
64
+ // smaller on the wire for this particular payload.
65
+ const svg = await QRCode.toString(otpauthUri, { type: 'svg', margin: 1 });
66
+ return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
67
+ }
68
+ /**
69
+ * Build the Express router exposing TOTP endpoints. The returned router
70
+ * should be mounted at `/api/console/totp`; the caller does not need to
71
+ * add additional auth middleware — this router enforces its own auth
72
+ * regardless of the global feature flag.
73
+ */
74
+ export function createTotpRoutes(options) {
75
+ const { store } = options;
76
+ const router = Router();
77
+ const jsonParser = express.json({ limit: BODY_LIMIT, type: 'application/json' });
78
+ // Fresh per-endpoint limiters per router instance. Separate buckets for
79
+ // confirm vs disable — they protect different secrets, so traffic on one
80
+ // should not exhaust the budget of the other. Fresh instances per call
81
+ // keep tests isolated.
82
+ const rateLimitMax = options.rateLimitMax ?? DEFAULT_RATE_LIMIT_MAX;
83
+ const rateLimitWindowMs = options.rateLimitWindowMs ?? DEFAULT_RATE_LIMIT_WINDOW_MS;
84
+ const confirmLimiter = new SlidingWindowRateLimiter(rateLimitMax, rateLimitWindowMs);
85
+ const disableLimiter = new SlidingWindowRateLimiter(rateLimitMax, rateLimitWindowMs);
86
+ // Always-on auth — the global feature flag does not apply here. Even
87
+ // during Phase 1 rollout (flag off), the TOTP endpoints must verify that
88
+ // the caller already holds the console token before letting them enroll
89
+ // a second factor. Otherwise an attacker with local port access could
90
+ // enroll their own authenticator and lock the real user out.
91
+ const auth = createAuthMiddleware({
92
+ store,
93
+ enabled: true,
94
+ label: 'totp',
95
+ });
96
+ router.use(auth);
97
+ /** GET /status — enrollment state (no secret material). */
98
+ router.get('/status', (_req, res) => {
99
+ res.json(store.getTotpStatus());
100
+ });
101
+ /** POST /enroll/begin — generate pending secret, return QR + URI. */
102
+ router.post('/enroll/begin', jsonParser, async (req, res) => {
103
+ // Optional label override — lets the UI label the authenticator entry
104
+ // differently if the user has renamed the console token.
105
+ const label = getNormalizedStringField(req.body, 'label') ?? undefined;
106
+ try {
107
+ const begin = store.beginTotpEnrollment(label);
108
+ const qrSvgDataUrl = await renderQrDataUrl(begin.otpauthUri);
109
+ res.json({
110
+ pendingId: begin.pendingId,
111
+ secret: begin.secret,
112
+ otpauthUri: begin.otpauthUri,
113
+ qrSvgDataUrl,
114
+ expiresAt: begin.expiresAt,
115
+ });
116
+ }
117
+ catch (err) {
118
+ const message = err instanceof Error ? err.message : 'Enrollment could not be started';
119
+ logger.debug(`[TOTP] begin failed: ${message}`);
120
+ if (err instanceof TotpError) {
121
+ sendStoreError(res, httpStatusForStoreError(err.code), err.code, err.message);
122
+ }
123
+ else {
124
+ sendStoreError(res, 500, 'INTERNAL', message);
125
+ }
126
+ }
127
+ });
128
+ /** POST /enroll/confirm — verify code, persist, return backup codes (once). */
129
+ router.post('/enroll/confirm', jsonParser, async (req, res) => {
130
+ if (!confirmLimiter.tryAcquire()) {
131
+ sendStoreError(res, 429, 'RATE_LIMITED', 'Too many confirmation attempts — slow down');
132
+ return;
133
+ }
134
+ const pendingId = getNormalizedStringField(req.body, 'pendingId');
135
+ const code = getNormalizedStringField(req.body, 'code');
136
+ if (!pendingId || !code) {
137
+ sendStoreError(res, 400, 'MISSING_FIELDS', 'pendingId and code are required');
138
+ return;
139
+ }
140
+ try {
141
+ const result = await store.confirmTotpEnrollment(pendingId, code);
142
+ res.json({
143
+ enrolled: true,
144
+ enrolledAt: result.enrolledAt,
145
+ backupCodes: result.backupCodes,
146
+ });
147
+ }
148
+ catch (err) {
149
+ const message = err instanceof Error ? err.message : 'Confirmation failed';
150
+ logger.debug(`[TOTP] confirm failed: ${message}`);
151
+ if (err instanceof TotpError) {
152
+ sendStoreError(res, httpStatusForStoreError(err.code), err.code, err.message);
153
+ }
154
+ else {
155
+ sendStoreError(res, 500, 'INTERNAL', message);
156
+ }
157
+ }
158
+ });
159
+ /** POST /disable — verify code, clear enrollment. */
160
+ router.post('/disable', jsonParser, async (req, res) => {
161
+ if (!disableLimiter.tryAcquire()) {
162
+ sendStoreError(res, 429, 'RATE_LIMITED', 'Too many disable attempts — slow down');
163
+ return;
164
+ }
165
+ const code = getNormalizedStringField(req.body, 'code');
166
+ if (!code) {
167
+ sendStoreError(res, 400, 'MISSING_FIELDS', 'code is required');
168
+ return;
169
+ }
170
+ try {
171
+ await store.disableTotp(code);
172
+ res.json({ enrolled: false });
173
+ }
174
+ catch (err) {
175
+ const message = err instanceof Error ? err.message : 'Disable failed';
176
+ logger.debug(`[TOTP] disable failed: ${message}`);
177
+ if (err instanceof TotpError) {
178
+ sendStoreError(res, httpStatusForStoreError(err.code), err.code, err.message);
179
+ }
180
+ else {
181
+ sendStoreError(res, 500, 'INTERNAL', message);
182
+ }
183
+ }
184
+ });
185
+ return router;
186
+ }
187
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidG90cFJvdXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy93ZWIvcm91dGVzL3RvdHBSb3V0ZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXdCRztBQUVILE9BQU8sT0FBTyxFQUFFLEVBQUUsTUFBTSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRTFDLE9BQU8sTUFBTSxNQUFNLFFBQVEsQ0FBQztBQUM1QixPQUFPLEVBQUUsU0FBUyxFQUEwQixNQUFNLDRCQUE0QixDQUFDO0FBQy9FLE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3ZFLE9BQU8sRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHlDQUF5QyxDQUFDO0FBQ25GLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxjQUFjLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUM3RyxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFL0MsK0RBQStEO0FBQy9ELE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQztBQUV6Qjs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBa0JHO0FBQ0gsTUFBTSxzQkFBc0IsR0FBRyxFQUFFLENBQUM7QUFDbEMsTUFBTSw0QkFBNEIsR0FBRyxNQUFNLENBQUM7QUFFNUM7Ozs7R0FJRztBQUNILEtBQUssVUFBVSxlQUFlLENBQUMsVUFBa0I7SUFDL0MsMkVBQTJFO0lBQzNFLHVFQUF1RTtJQUN2RSxtREFBbUQ7SUFDbkQsTUFBTSxHQUFHLEdBQUcsTUFBTSxNQUFNLENBQUMsUUFBUSxDQUFDLFVBQVUsRUFBRSxFQUFFLElBQUksRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7SUFDMUUsT0FBTywyQkFBMkIsa0JBQWtCLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztBQUM5RCxDQUFDO0FBYUQ7Ozs7O0dBS0c7QUFDSCxN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LENBQUMsQ0FBQyxnQkFBZ0IsQ0FBQztZQUN0RSxNQUFNLENBQUMsS0FBSyxDQUFDLDBCQUEwQixPQUFPLEVBQUUsQ0FBQyxDQUFDO1lBQ2xELElBQUksR0FBRyxZQUFZLFNBQVMsRUFBRSxDQUFDO2dCQUM3QixjQUFjLENBQUMsR0FBRyxFQUFFLHVCQUF1QixDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxHQUFHLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztZQUNoRixDQUFDO2lCQUFNLENBQUM7Z0JBQ04sY0FBYyxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDO1lBQ2hELENBQUM7UUFDSCxDQUFDO0lBQ0gsQ0FBQyxDQUFDLENBQUM7SUFFSCxPQUFPLE1BQU0sQ0FBQztBQUNoQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBUT1RQIChhdXRoZW50aWNhdG9yKSBlbnJvbGxtZW50IEhUVFAgcm91dGVzIOKAlCBQaGFzZSAyIG9mICMxNzgwICgjMTc5NCkuXG4gKlxuICogUHJvdmlkZXM6XG4gKiAtIEdFVCAgICAvYXBpL2NvbnNvbGUvdG90cC9zdGF0dXMgICAgICAgIOKAlCBlbnJvbGxtZW50IHN0YXRlIChubyBzZWNyZXRzKVxuICogLSBQT1NUICAgL2FwaS9jb25zb2xlL3RvdHAvZW5yb2xsL2JlZ2luICDigJQgZ2VuZXJhdGUgc2VjcmV0LCByZXR1cm4gUVIgKyBvdHBhdXRoIFVSSVxuICogLSBQT1NUICAgL2FwaS9jb25zb2xlL3RvdHAvZW5yb2xsL2NvbmZpcm0g4oCUIHZlcmlmeSBjb2RlLCBwZXJzaXN0LCByZXR1cm4gYmFja3VwIGNvZGVzIChvbmNlKVxuICogLSBQT1NUICAgL2FwaS9jb25zb2xlL3RvdHAvZGlzYWJsZSAgICAgICDigJQgdmVyaWZ5IGNvZGUsIGNsZWFyIGVucm9sbG1lbnRcbiAqXG4gKiBTZWN1cml0eSBtb2RlbDpcbiAqIC0gQWxsIGVuZHBvaW50cyByZXF1aXJlIGEgdmFsaWQgZXhpc3RpbmcgY29uc29sZSB0b2tlbi4gVGhlIGNhbGxlciBtdXN0XG4gKiAgIHByb3ZlIHRoZXkgYWxyZWFkeSBob2xkIHRoZSB0b2tlbiBiZWZvcmUgdGhleSBjYW4gZW5yb2xsIGEgc2Vjb25kXG4gKiAgIGZhY3RvciDigJQgb3RoZXJ3aXNlIGFuIGF0dGFja2VyIHdpdGggbG9jYWwgcG9ydCBhY2Nlc3MgY291bGQgcHJlLWVucm9sbFxuICogICB0aGVpciBvd24gYXV0aGVudGljYXRvciBhbmQgbG9jayB0aGUgbGVnaXRpbWF0ZSB1c2VyIG91dC5cbiAqIC0gRW5mb3JjZW1lbnQgaGFwcGVucyB2aWEgYW4gYWx3YXlzLW9uIGBjcmVhdGVBdXRoTWlkZGxld2FyZWAgaW5zdGFuY2VcbiAqICAgbW91bnRlZCBhdCB0aGUgdG9wIG9mIHRoaXMgcm91dGVyLCBpbmRlcGVuZGVudCBvZiB0aGUgZ2xvYmFsXG4gKiAgIERPTExIT1VTRV9XRUJfQVVUSF9FTkFCTEVEIGZsYWcuXG4gKiAtIEJhY2t1cCBjb2RlcyBhcmUgcmV0dXJuZWQgaW4gcGxhaW50ZXh0IGV4YWN0bHkgb25jZSAoY29uZmlybSByZXNwb25zZSlcbiAqICAgYW5kIG9ubHkgdGhlaXIgc2hhMjU2IGhhc2hlcyBhcmUgcmV0YWluZWQgYnkgdGhlIHN0b3JlLlxuICogLSBBIHNsaWRpbmctd2luZG93IHJhdGUgbGltaXQgdGhyb3R0bGVzIGNvbmZpcm0vZGlzYWJsZSBhdHRlbXB0cyBvbiBhXG4gKiAgIHBlci1JUCBiYXNpcyBzbyBhIGJhZCBhY3RvciB3aXRoIGEgbGl2ZSBzZXNzaW9uIGNhbid0IGJydXRlLWZvcmNlIGFcbiAqICAgVE9UUCB3aW5kb3cgYnkgZmxvb2RpbmcgcmVxdWVzdHMuXG4gKlxuICogQHNpbmNlIHYyLjEuMCDigJQgSXNzdWUgIzE3OTRcbiAqL1xuXG5pbXBvcnQgZXhwcmVzcywgeyBSb3V0ZXIgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCB0eXBlIHsgUmVxdWVzdCwgUmVzcG9uc2UgfSBmcm9tICdleHByZXNzJztcbmltcG9ydCBRUkNvZGUgZnJvbSAncXJjb2RlJztcbmltcG9ydCB7IFRvdHBFcnJvciwgdHlwZSBDb25zb2xlVG9rZW5TdG9yZSB9IGZyb20gJy4uL2NvbnNvbGUvY29uc29sZVRva2VuLmpzJztcbmltcG9ydCB7IGNyZWF0ZUF1dGhNaWRkbGV3YXJlIH0gZnJvbSAnLi4vbWlkZGxld2FyZS9hdXRoTWlkZGxld2FyZS5qcyc7XG5pbXBvcnQgeyBTbGlkaW5nV2luZG93UmF0ZUxpbWl0ZXIgfSBmcm9tICcuLi8uLi91dGlscy9TbGlkaW5nV2luZG93UmF0ZUxpbWl0ZXIuanMnO1xuaW1wb3J0IHsgaHR0cFN0YXR1c0ZvclN0b3JlRXJyb3IsIHNlbmRTdG9yZUVycm9yLCBnZXROb3JtYWxpemVkU3RyaW5nRmllbGQgfSBmcm9tICcuL2NvbnNvbGVSb3V0ZUhlbHBlcnMuanMnO1xuaW1wb3J0IHsgbG9nZ2VyIH0gZnJvbSAnLi4vLi4vdXRpbHMvbG9nZ2VyLmpzJztcblxuLyoqIEpTT04gYm9keSBzaXplIGxpbWl0IOKAlCBUT1RQIHJlcXVlc3RzIGFyZSB0aW55LCBjYXAgaGFyZC4gKi9cbmNvbnN0IEJPRFlfTElNSVQgPSAnMWtiJztcblxuLyoqXG4gKiBEZWZhdWx0IHJhdGUgbGltaXQgZm9yIGNvZGUtdmVyaWZpY2F0aW9uIGVuZHBvaW50czogMTAgYXR0ZW1wdHMgcGVyIG1pbnV0ZS5cbiAqIFRPVFAgY29kZXMgYXJlIDYgZGlnaXRzICgxLWluLTEwXjYgZ3Vlc3MgcmF0ZSkgYW5kIHRoZSDCsTMwcyB3aW5kb3cgZ2l2ZXNcbiAqIGFuIGF0dGFja2VyIHVwIHRvIDMgdmFsaWQgY29kZXMgcGVyIG1pbnV0ZS4gMTAgYXR0ZW1wdHMgY2FwcyBicnV0ZS1mb3JjZVxuICogc3VjY2VzcyBwcm9iYWJpbGl0eSBwZXIgbWludXRlIGF0IH4zZS01IGV2ZW4gYmVmb3JlIG5ldHdvcmsgbGF0ZW5jeS5cbiAqXG4gKiBMaW1pdGVycyBhcmUgcGVyLWVuZHBvaW50IChjb25maXJtIHZzIGRpc2FibGUpIGJlY2F1c2UgdGhleSBwcm90ZWN0XG4gKiBkaWZmZXJlbnQgc2VjcmV0czogY29uZmlybSB0ZXN0cyB0aGUgaW4tbWVtb3J5IHBlbmRpbmcgZW5yb2xsbWVudCBzZWNyZXRcbiAqIChib3VuZGVkIGxpZmV0aW1lLCBhdHRhY2tlciBtdXN0IGFsc28ga25vdyB0aGUgcGVuZGluZ0lkKSwgZGlzYWJsZSB0ZXN0c1xuICogdGhlIHBlcnNpc3RlZCBlbnJvbGxtZW50IHNlY3JldCAobG9uZy1saXZlZCkuIEEgc2luZ2xlIHNoYXJlZCBsaW1pdGVyXG4gKiB3b3VsZCBsZXQgdHJhZmZpYyBvbiBvbmUgZW5kcG9pbnQgZXhoYXVzdCBidWRnZXQgb24gdGhlIG90aGVyLlxuICpcbiAqIExpbWl0ZXJzIGFyZSBnbG9iYWwgKG5vdCBwZXItSVApIGJlY2F1c2UgdGhlIHNlcnZlciBiaW5kcyB0byAxMjcuMC4wLjFcbiAqIG9ubHkg4oCUIGV2ZXJ5IHJlcXVlc3QgY29tZXMgZnJvbSB0aGUgc2FtZSBsb29wYmFjayBhZGRyZXNzLCBzbyBrZXlpbmcgb25cbiAqIElQIHdvdWxkIGNvbGxhcHNlIHRvIGEgc2luZ2xlIGJ1Y2tldCBhbnl3YXkuXG4gKlxuICogVGVzdHMgY29uc3RydWN0IGEgZnJlc2ggcm91dGVyIHBlciBgYnVpbGRBcHBgLCB3aGljaCB5aWVsZHMgZnJlc2hcbiAqIGxpbWl0ZXJzLCBzbyBubyBjcm9zcy10ZXN0IHBvbGx1dGlvbi5cbiAqL1xuY29uc3QgREVGQVVMVF9SQVRFX0xJTUlUX01BWCA9IDEwO1xuY29uc3QgREVGQVVMVF9SQVRFX0xJTUlUX1dJTkRPV19NUyA9IDYwXzAwMDtcblxuLyoqXG4gKiBSZW5kZXIgYW4gb3RwYXV0aCBVUkkgYXMgYW4gU1ZHIGRhdGEgVVJMIHN1aXRhYmxlIGZvciBkaXJlY3QgZW1iZWRkaW5nXG4gKiBpbiBhbiA8aW1nIHNyYz4gb3IgYmFja2dyb3VuZC1pbWFnZS4gU2VwYXJhdGVkIGludG8gYSBoZWxwZXIgc28gdGhlXG4gKiByZXF1ZXN0IGhhbmRsZXIgc3RheXMgcmVhZGFibGUuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIHJlbmRlclFyRGF0YVVybChvdHBhdXRoVXJpOiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuICAvLyBlcnJvckNvcnJlY3Rpb25MZXZlbCAnTScgaXMgdGhlIGRlZmF1bHQgYW5kIGJhbGFuY2VzIHNpemUgdnMgcm9idXN0bmVzcy5cbiAgLy8gV2UgZW1pdCBTVkcgKG5vdCBQTkcpIGJlY2F1c2UgaXQgc2NhbGVzIHRvIGFueSBjb250YWluZXIgc2l6ZSBhbmQgaXNcbiAgLy8gc21hbGxlciBvbiB0aGUgd2lyZSBmb3IgdGhpcyBwYXJ0aWN1bGFyIHBheWxvYWQuXG4gIGNvbnN0IHN2ZyA9IGF3YWl0IFFSQ29kZS50b1N0cmluZyhvdHBhdXRoVXJpLCB7IHR5cGU6ICdzdmcnLCBtYXJnaW46IDEgfSk7XG4gIHJldHVybiBgZGF0YTppbWFnZS9zdmcreG1sO3V0ZjgsJHtlbmNvZGVVUklDb21wb25lbnQoc3ZnKX1gO1xufVxuXG4vKipcbiAqIE9wdGlvbnMgZm9yIHRoZSBUT1RQIHJvdXRlcyBmYWN0b3J5LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRvdHBSb3V0ZXNPcHRpb25zIHtcbiAgc3RvcmU6IENvbnNvbGVUb2tlblN0b3JlO1xuICAvKiogTWF4aW11bSBjb2RlLXZlcmlmaWNhdGlvbiBhdHRlbXB0cyBwZXIgd2luZG93LiBEZWZhdWx0OiAxMC4gKi9cbiAgcmF0ZUxpbWl0TWF4PzogbnVtYmVyO1xuICAvKiogUmF0ZSBsaW1pdCB3aW5kb3cgaW4gbWlsbGlzZWNvbmRzLiBEZWZhdWx0OiA2MF8wMDAgKDEgbWludXRlKS4gKi9cbiAgcmF0ZUxpbWl0V2luZG93TXM/OiBudW1iZXI7XG59XG5cbi8qKlxuICogQnVpbGQgdGhlIEV4cHJlc3Mgcm91dGVyIGV4cG9zaW5nIFRPVFAgZW5kcG9pbnRzLiBUaGUgcmV0dXJuZWQgcm91dGVyXG4gKiBzaG91bGQgYmUgbW91bnRlZCBhdCBgL2FwaS9jb25zb2xlL3RvdHBgOyB0aGUgY2FsbGVyIGRvZXMgbm90IG5lZWQgdG9cbiAqIGFkZCBhZGRpdGlvbmFsIGF1dGggbWlkZGxld2FyZSDigJQgdGhpcyByb3V0ZXIgZW5mb3JjZXMgaXRzIG93biBhdXRoXG4gKiByZWdhcmRsZXNzIG9mIHRoZSBnbG9iYWwgZmVhdHVyZSBmbGFnLlxuICovXG5leHBvcnQgZnVuY3Rpb24gY3JlYXRlVG90cFJvdXRlcyhvcHRpb25zOiBUb3RwUm91dGVzT3B0aW9ucyk6IFJvdXRlciB7XG4gIGNvbnN0IHsgc3RvcmUgfSA9IG9wdGlvbnM7XG4gIGNvbnN0IHJvdXRlciA9IFJvdXRlcigpO1xuICBjb25zdCBqc29uUGFyc2VyID0gZXhwcmVzcy5qc29uKHsgbGltaXQ6IEJPRFlfTElNSVQsIHR5cGU6ICdhcHBsaWNhdGlvbi9qc29uJyB9KTtcbiAgLy8gRnJlc2ggcGVyLWVuZHBvaW50IGxpbWl0ZXJzIHBlciByb3V0ZXIgaW5zdGFuY2UuIFNlcGFyYXRlIGJ1Y2tldHMgZm9yXG4gIC8vIGNvbmZpcm0gdnMgZGlzYWJsZSDigJQgdGhleSBwcm90ZWN0IGRpZmZlcmVudCBzZWNyZXRzLCBzbyB0cmFmZmljIG9uIG9uZVxuICAvLyBzaG91bGQgbm90IGV4aGF1c3QgdGhlIGJ1ZGdldCBvZiB0aGUgb3RoZXIuIEZyZXNoIGluc3RhbmNlcyBwZXIgY2FsbFxuICAvLyBrZWVwIHRlc3RzIGlzb2xhdGVkLlxuICBjb25zdCByYXRlTGltaXRNYXggPSBvcHRpb25zLnJhdGVMaW1pdE1heCA/PyBERUZBVUxUX1JBVEVfTElNSVRfTUFYO1xuICBjb25zdCByYXRlTGltaXRXaW5kb3dNcyA9IG9wdGlvbnMucmF0ZUxpbWl0V2luZG93TXMgPz8gREVGQVVMVF9SQVRFX0xJTUlUX1dJTkRPV19NUztcbiAgY29uc3QgY29uZmlybUxpbWl0ZXIgPSBuZXcgU2xpZGluZ1dpbmRvd1JhdGVMaW1pdGVyKHJhdGVMaW1pdE1heCwgcmF0ZUxpbWl0V2luZG93TXMpO1xuICBjb25zdCBkaXNhYmxlTGltaXRlciA9IG5ldyBTbGlkaW5nV2luZG93UmF0ZUxpbWl0ZXIocmF0ZUxpbWl0TWF4LCByYXRlTGltaXRXaW5kb3dNcyk7XG5cbiAgLy8gQWx3YXlzLW9uIGF1dGgg4oCUIHRoZSBnbG9iYWwgZmVhdHVyZSBmbGFnIGRvZXMgbm90IGFwcGx5IGhlcmUuIEV2ZW5cbiAgLy8gZHVyaW5nIFBoYXNlIDEgcm9sbG91dCAoZmxhZyBvZmYpLCB0aGUgVE9UUCBlbmRwb2ludHMgbXVzdCB2ZXJpZnkgdGhhdFxuICAvLyB0aGUgY2FsbGVyIGFscmVhZHkgaG9sZHMgdGhlIGNvbnNvbGUgdG9rZW4gYmVmb3JlIGxldHRpbmcgdGhlbSBlbnJvbGxcbiAgLy8gYSBzZWNvbmQgZmFjdG9yLiBPdGhlcndpc2UgYW4gYXR0YWNrZXIgd2l0aCBsb2NhbCBwb3J0IGFjY2VzcyBjb3VsZFxuICAvLyBlbnJvbGwgdGhlaXIgb3duIGF1dGhlbnRpY2F0b3IgYW5kIGxvY2sgdGhlIHJlYWwgdXNlciBvdXQuXG4gIGNvbnN0IGF1dGggPSBjcmVhdGVBdXRoTWlkZGxld2FyZSh7XG4gICAgc3RvcmUsXG4gICAgZW5hYmxlZDogdHJ1ZSxcbiAgICBsYWJlbDogJ3RvdHAnLFxuICB9KTtcbiAgcm91dGVyLnVzZShhdXRoKTtcblxuICAvKiogR0VUIC9zdGF0dXMg4oCUIGVucm9sbG1lbnQgc3RhdGUgKG5vIHNlY3JldCBtYXRlcmlhbCkuICovXG4gIHJvdXRlci5nZXQoJy9zdGF0dXMnLCAoX3JlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICAgIHJlcy5qc29uKHN0b3JlLmdldFRvdHBTdGF0dXMoKSk7XG4gIH0pO1xuXG4gIC8qKiBQT1NUIC9lbnJvbGwvYmVnaW4g4oCUIGdlbmVyYXRlIHBlbmRpbmcgc2VjcmV0LCByZXR1cm4gUVIgKyBVUkkuICovXG4gIHJvdXRlci5wb3N0KCcvZW5yb2xsL2JlZ2luJywganNvblBhcnNlciwgYXN5bmMgKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICAgIC8vIE9wdGlvbmFsIGxhYmVsIG92ZXJyaWRlIOKAlCBsZXRzIHRoZSBVSSBsYWJlbCB0aGUgYXV0aGVudGljYXRvciBlbnRyeVxuICAgIC8vIGRpZmZlcmVudGx5IGlmIHRoZSB1c2VyIGhhcyByZW5hbWVkIHRoZSBjb25zb2xlIHRva2VuLlxuICAgIGNvbnN0IGxhYmVsID0gZ2V0Tm9ybWFsaXplZFN0cmluZ0ZpZWxkKHJlcS5ib2R5LCAnbGFiZWwnKSA/PyB1bmRlZmluZWQ7XG4gICAgdHJ5IHtcbiAgICAgIGNvbnN0IGJlZ2luID0gc3RvcmUuYmVnaW5Ub3RwRW5yb2xsbWVudChsYWJlbCk7XG4gICAgICBjb25zdCBxclN2Z0RhdGFVcmwgPSBhd2FpdCByZW5kZXJRckRhdGFVcmwoYmVnaW4ub3RwYXV0aFVyaSk7XG4gICAgICByZXMuanNvbih7XG4gICAgICAgIHBlbmRpbmdJZDogYmVnaW4ucGVuZGluZ0lkLFxuICAgICAgICBzZWNyZXQ6IGJlZ2luLnNlY3JldCxcbiAgICAgICAgb3RwYXV0aFVyaTogYmVnaW4ub3RwYXV0aFVyaSxcbiAgICAgICAgcXJTdmdEYXRhVXJsLFxuICAgICAgICBleHBpcmVzQXQ6IGJlZ2luLmV4cGlyZXNBdCxcbiAgICAgIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc3QgbWVzc2FnZSA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiAnRW5yb2xsbWVudCBjb3VsZCBub3QgYmUgc3RhcnRlZCc7XG4gICAgICBsb2dnZXIuZGVidWcoYFtUT1RQXSBiZWdpbiBmYWlsZWQ6ICR7bWVzc2FnZX1gKTtcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBUb3RwRXJyb3IpIHtcbiAgICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCBodHRwU3RhdHVzRm9yU3RvcmVFcnJvcihlcnIuY29kZSksIGVyci5jb2RlLCBlcnIubWVzc2FnZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZW5kU3RvcmVFcnJvcihyZXMsIDUwMCwgJ0lOVEVSTkFMJywgbWVzc2FnZSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICAvKiogUE9TVCAvZW5yb2xsL2NvbmZpcm0g4oCUIHZlcmlmeSBjb2RlLCBwZXJzaXN0LCByZXR1cm4gYmFja3VwIGNvZGVzIChvbmNlKS4gKi9cbiAgcm91dGVyLnBvc3QoJy9lbnJvbGwvY29uZmlybScsIGpzb25QYXJzZXIsIGFzeW5jIChyZXE6IFJlcXVlc3QsIHJlczogUmVzcG9uc2UpID0+IHtcbiAgICBpZiAoIWNvbmZpcm1MaW1pdGVyLnRyeUFjcXVpcmUoKSkge1xuICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCA0MjksICdSQVRFX0xJTUlURUQnLCAnVG9vIG1hbnkgY29uZmlybWF0aW9uIGF0dGVtcHRzIOKAlCBzbG93IGRvd24nKTtcbiAgICAgIHJldHVybjtcbiAgICB9XG4gICAgY29uc3QgcGVuZGluZ0lkID0gZ2V0Tm9ybWFsaXplZFN0cmluZ0ZpZWxkKHJlcS5ib2R5LCAncGVuZGluZ0lkJyk7XG4gICAgY29uc3QgY29kZSA9IGdldE5vcm1hbGl6ZWRTdHJpbmdGaWVsZChyZXEuYm9keSwgJ2NvZGUnKTtcbiAgICBpZiAoIXBlbmRpbmdJZCB8fCAhY29kZSkge1xuICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCA0MDAsICdNSVNTSU5HX0ZJRUxEUycsICdwZW5kaW5nSWQgYW5kIGNvZGUgYXJlIHJlcXVpcmVkJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBjb25zdCByZXN1bHQgPSBhd2FpdCBzdG9yZS5jb25maXJtVG90cEVucm9sbG1lbnQocGVuZGluZ0lkLCBjb2RlKTtcbiAgICAgIHJlcy5qc29uKHtcbiAgICAgICAgZW5yb2xsZWQ6IHRydWUsXG4gICAgICAgIGVucm9sbGVkQXQ6IHJlc3VsdC5lbnJvbGxlZEF0LFxuICAgICAgICBiYWNrdXBDb2RlczogcmVzdWx0LmJhY2t1cENvZGVzLFxuICAgICAgfSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICBjb25zdCBtZXNzYWdlID0gZXJyIGluc3RhbmNlb2YgRXJyb3IgPyBlcnIubWVzc2FnZSA6ICdDb25maXJtYXRpb24gZmFpbGVkJztcbiAgICAgIGxvZ2dlci5kZWJ1ZyhgW1RPVFBdIGNvbmZpcm0gZmFpbGVkOiAke21lc3NhZ2V9YCk7XG4gICAgICBpZiAoZXJyIGluc3RhbmNlb2YgVG90cEVycm9yKSB7XG4gICAgICAgIHNlbmRTdG9yZUVycm9yKHJlcywgaHR0cFN0YXR1c0ZvclN0b3JlRXJyb3IoZXJyLmNvZGUpLCBlcnIuY29kZSwgZXJyLm1lc3NhZ2UpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCA1MDAsICdJTlRFUk5BTCcsIG1lc3NhZ2UpO1xuICAgICAgfVxuICAgIH1cbiAgfSk7XG5cbiAgLyoqIFBPU1QgL2Rpc2FibGUg4oCUIHZlcmlmeSBjb2RlLCBjbGVhciBlbnJvbGxtZW50LiAqL1xuICByb3V0ZXIucG9zdCgnL2Rpc2FibGUnLCBqc29uUGFyc2VyLCBhc3luYyAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKSA9PiB7XG4gICAgaWYgKCFkaXNhYmxlTGltaXRlci50cnlBY3F1aXJlKCkpIHtcbiAgICAgIHNlbmRTdG9yZUVycm9yKHJlcywgNDI5LCAnUkFURV9MSU1JVEVEJywgJ1RvbyBtYW55IGRpc2FibGUgYXR0ZW1wdHMg4oCUIHNsb3cgZG93bicpO1xuICAgICAgcmV0dXJuO1xuICAgIH1cbiAgICBjb25zdCBjb2RlID0gZ2V0Tm9ybWFsaXplZFN0cmluZ0ZpZWxkKHJlcS5ib2R5LCAnY29kZScpO1xuICAgIGlmICghY29kZSkge1xuICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCA0MDAsICdNSVNTSU5HX0ZJRUxEUycsICdjb2RlIGlzIHJlcXVpcmVkJyk7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBzdG9yZS5kaXNhYmxlVG90cChjb2RlKTtcbiAgICAgIHJlcy5qc29uKHsgZW5yb2xsZWQ6IGZhbHNlIH0pO1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgY29uc3QgbWVzc2FnZSA9IGVyciBpbnN0YW5jZW9mIEVycm9yID8gZXJyLm1lc3NhZ2UgOiAnRGlzYWJsZSBmYWlsZWQnO1xuICAgICAgbG9nZ2VyLmRlYnVnKGBbVE9UUF0gZGlzYWJsZSBmYWlsZWQ6ICR7bWVzc2FnZX1gKTtcbiAgICAgIGlmIChlcnIgaW5zdGFuY2VvZiBUb3RwRXJyb3IpIHtcbiAgICAgICAgc2VuZFN0b3JlRXJyb3IocmVzLCBodHRwU3RhdHVzRm9yU3RvcmVFcnJvcihlcnIuY29kZSksIGVyci5jb2RlLCBlcnIubWVzc2FnZSk7XG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzZW5kU3RvcmVFcnJvcihyZXMsIDUwMCwgJ0lOVEVSTkFMJywgbWVzc2FnZSk7XG4gICAgICB9XG4gICAgfVxuICB9KTtcblxuICByZXR1cm4gcm91dGVyO1xufVxuIl19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dollhousemcp/mcp-server",
3
- "version": "2.0.10",
3
+ "version": "2.0.11",
4
4
  "description": "DollhouseMCP - A Model Context Protocol (MCP) server that enables dynamic AI persona management from markdown files, allowing Claude and other compatible AI assistants to activate and switch between different behavioral personas.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
package/server.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "io.github.DollhouseMCP/mcp-server",
4
4
  "title": "DollhouseMCP",
5
5
  "description": "OSS to create Personas, Skills, Templates, Agents, and Memories to customize your AI experience.",
6
- "version": "2.0.10",
6
+ "version": "2.0.11",
7
7
  "homepage": "https://dollhousemcp.com",
8
8
  "repository": {
9
9
  "type": "git",
@@ -29,7 +29,7 @@
29
29
  {
30
30
  "registryType": "npm",
31
31
  "identifier": "@dollhousemcp/mcp-server",
32
- "version": "2.0.10",
32
+ "version": "2.0.11",
33
33
  "transport": {
34
34
  "type": "stdio"
35
35
  }
@@ -1,3 +0,0 @@
1
- export declare const VERSION = "1.6.5";
2
- export declare const BUILD_DATE = "2025-08-26T15:30:22.187Z";
3
- //# sourceMappingURL=version.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/constants/version.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,OAAO,UAAU,CAAC;AAC/B,eAAO,MAAM,UAAU,6BAA6B,CAAC"}
@@ -1,4 +0,0 @@
1
- // Auto-generated version constant
2
- export const VERSION = "1.6.5";
3
- export const BUILD_DATE = "2025-08-26T15:30:22.187Z";
4
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25zdGFudHMvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxrQ0FBa0M7QUFDbEMsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQztBQUMvQixNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsMEJBQTBCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBBdXRvLWdlbmVyYXRlZCB2ZXJzaW9uIGNvbnN0YW50XG5leHBvcnQgY29uc3QgVkVSU0lPTiA9IFwiMS42LjVcIjtcbmV4cG9ydCBjb25zdCBCVUlMRF9EQVRFID0gXCIyMDI1LTA4LTI2VDE1OjMwOjIyLjE4N1pcIjtcbiJdfQ==
@@ -1,35 +0,0 @@
1
- /**
2
- * SSE-based real-time log viewer sink.
3
- *
4
- * Implements ILogSink and runs an opt-in Express HTTP server that:
5
- * - Serves a browser-based log viewer at GET /
6
- * - Streams log entries via SSE at GET /logs/stream
7
- * - Exposes a JSON query endpoint at GET /logs (delegates to MemoryLogSink)
8
- * - Provides a health endpoint at GET /health
9
- *
10
- * See docs/LOGGING-DESIGN.md §4.6 for the full design.
11
- */
12
- import type { ILogSink, UnifiedLogEntry } from '../types.js';
13
- import type { MemoryLogSink } from './MemoryLogSink.js';
14
- export interface SSELogSinkOptions {
15
- port: number;
16
- memorySink: MemoryLogSink;
17
- }
18
- export declare class SSELogSink implements ILogSink {
19
- private readonly app;
20
- private server;
21
- private readonly clients;
22
- private readonly memorySink;
23
- private readonly port;
24
- private readonly startTime;
25
- constructor(options: SSELogSinkOptions);
26
- write(entry: UnifiedLogEntry): void;
27
- flush(): Promise<void>;
28
- close(): Promise<void>;
29
- start(): Promise<void>;
30
- get clientCount(): number;
31
- getPort(): number;
32
- private setupRoutes;
33
- private matchesFilter;
34
- }
35
- //# sourceMappingURL=SSELogSink.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SSELogSink.d.ts","sourceRoot":"","sources":["../../../src/logging/sinks/SSELogSink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAyB,MAAM,aAAa,CAAC;AAEpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGxD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,CAAC;CAC3B;AAcD,qBAAa,UAAW,YAAW,QAAQ;IACzC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;IACjD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwB;IAChD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgB;IAC3C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;gBAE5B,OAAO,EAAE,iBAAiB;IAWtC,KAAK,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI;IAQ7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoBtB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAS5B,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,OAAO,IAAI,MAAM;IAWjB,OAAO,CAAC,WAAW;IA4FnB,OAAO,CAAC,aAAa;CAkBtB"}
@@ -1,181 +0,0 @@
1
- /**
2
- * SSE-based real-time log viewer sink.
3
- *
4
- * Implements ILogSink and runs an opt-in Express HTTP server that:
5
- * - Serves a browser-based log viewer at GET /
6
- * - Streams log entries via SSE at GET /logs/stream
7
- * - Exposes a JSON query endpoint at GET /logs (delegates to MemoryLogSink)
8
- * - Provides a health endpoint at GET /health
9
- *
10
- * See docs/LOGGING-DESIGN.md §4.6 for the full design.
11
- */
12
- import express from 'express';
13
- import { LOG_LEVEL_PRIORITY } from '../types.js';
14
- import { getViewerHtml } from '../viewer/viewerHtml.js';
15
- export class SSELogSink {
16
- app;
17
- server = null;
18
- clients = new Set();
19
- memorySink;
20
- port;
21
- startTime = Date.now();
22
- constructor(options) {
23
- this.port = options.port;
24
- this.memorySink = options.memorySink;
25
- this.app = express();
26
- this.setupRoutes();
27
- }
28
- // ---------------------------------------------------------------------------
29
- // ILogSink
30
- // ---------------------------------------------------------------------------
31
- write(entry) {
32
- for (const client of this.clients) {
33
- if (this.matchesFilter(entry, client.filter)) {
34
- client.res.write(`data: ${JSON.stringify(entry)}\n\n`);
35
- }
36
- }
37
- }
38
- async flush() {
39
- // No-op — SSE writes are immediate.
40
- }
41
- async close() {
42
- // End all client connections
43
- for (const client of this.clients) {
44
- client.res.end();
45
- }
46
- this.clients.clear();
47
- // Shut down HTTP server
48
- if (this.server) {
49
- await new Promise((resolve) => {
50
- this.server.close(() => resolve());
51
- });
52
- this.server = null;
53
- }
54
- }
55
- // ---------------------------------------------------------------------------
56
- // Lifecycle
57
- // ---------------------------------------------------------------------------
58
- async start() {
59
- return new Promise((resolve) => {
60
- this.server = this.app.listen(this.port, '127.0.0.1', () => {
61
- this.server.unref();
62
- resolve();
63
- });
64
- });
65
- }
66
- get clientCount() {
67
- return this.clients.size;
68
- }
69
- getPort() {
70
- if (!this.server)
71
- return this.port;
72
- const addr = this.server.address();
73
- if (addr && typeof addr === 'object')
74
- return addr.port;
75
- return this.port;
76
- }
77
- // ---------------------------------------------------------------------------
78
- // Routes
79
- // ---------------------------------------------------------------------------
80
- setupRoutes() {
81
- // Viewer HTML
82
- this.app.get('/', (_req, res) => {
83
- const actualPort = this.getPort();
84
- res.type('html').send(getViewerHtml(actualPort));
85
- });
86
- // SSE stream
87
- this.app.get('/logs/stream', (req, res) => {
88
- res.writeHead(200, {
89
- 'Content-Type': 'text/event-stream',
90
- 'Cache-Control': 'no-cache',
91
- 'Connection': 'keep-alive',
92
- });
93
- res.write(':connected\n\n');
94
- const filter = {};
95
- if (typeof req.query['category'] === 'string' && req.query['category']) {
96
- filter.category = req.query['category'];
97
- }
98
- if (typeof req.query['level'] === 'string' && req.query['level']) {
99
- filter.level = req.query['level'];
100
- }
101
- if (typeof req.query['source'] === 'string' && req.query['source']) {
102
- filter.source = req.query['source'];
103
- }
104
- if (typeof req.query['correlationId'] === 'string' && req.query['correlationId']) {
105
- filter.correlationId = req.query['correlationId'];
106
- }
107
- const client = { res, filter };
108
- this.clients.add(client);
109
- // Backfill recent history so the viewer shows context on connect
110
- const history = this.memorySink.query({ category: 'all', limit: 500 });
111
- // Send oldest-first so the viewer displays in chronological order
112
- const entries = history.entries.slice().reverse();
113
- for (const entry of entries) {
114
- res.write(`data: ${JSON.stringify(entry)}\n\n`);
115
- }
116
- req.on('close', () => {
117
- this.clients.delete(client);
118
- });
119
- });
120
- // JSON query (delegates to MemoryLogSink)
121
- this.app.get('/logs', (req, res) => {
122
- const options = {};
123
- if (typeof req.query['category'] === 'string' && req.query['category']) {
124
- options['category'] = req.query['category'];
125
- }
126
- if (typeof req.query['level'] === 'string' && req.query['level']) {
127
- options['level'] = req.query['level'];
128
- }
129
- if (typeof req.query['source'] === 'string' && req.query['source']) {
130
- options['source'] = req.query['source'];
131
- }
132
- if (typeof req.query['message'] === 'string' && req.query['message']) {
133
- options['message'] = req.query['message'];
134
- }
135
- if (typeof req.query['limit'] === 'string') {
136
- options['limit'] = parseInt(req.query['limit'], 10);
137
- }
138
- if (typeof req.query['offset'] === 'string') {
139
- options['offset'] = parseInt(req.query['offset'], 10);
140
- }
141
- if (typeof req.query['since'] === 'string' && req.query['since']) {
142
- options['since'] = req.query['since'];
143
- }
144
- if (typeof req.query['until'] === 'string' && req.query['until']) {
145
- options['until'] = req.query['until'];
146
- }
147
- const result = this.memorySink.query(options);
148
- res.json(result);
149
- });
150
- // Health
151
- this.app.get('/health', (_req, res) => {
152
- res.json({
153
- status: 'ok',
154
- clients: this.clientCount,
155
- uptime: Math.floor((Date.now() - this.startTime) / 1000),
156
- });
157
- });
158
- }
159
- // ---------------------------------------------------------------------------
160
- // Filter matching
161
- // ---------------------------------------------------------------------------
162
- matchesFilter(entry, filter) {
163
- if (filter.category && entry.category !== filter.category) {
164
- return false;
165
- }
166
- if (filter.level && LOG_LEVEL_PRIORITY[entry.level] < LOG_LEVEL_PRIORITY[filter.level]) {
167
- return false;
168
- }
169
- if (filter.source) {
170
- const needle = filter.source.toLowerCase();
171
- if (!entry.source.toLowerCase().includes(needle)) {
172
- return false;
173
- }
174
- }
175
- if (filter.correlationId && entry.correlationId !== filter.correlationId) {
176
- return false;
177
- }
178
- return true;
179
- }
180
- }
181
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU1NFTG9nU2luay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9sb2dnaW5nL3NpbmtzL1NTRUxvZ1NpbmsudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7Ozs7R0FVRztBQUVILE9BQU8sT0FBTyxNQUFNLFNBQVMsQ0FBQztBQUk5QixPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFFakQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBbUJ4RCxNQUFNLE9BQU8sVUFBVTtJQUNKLEdBQUcsQ0FBNkI7SUFDekMsTUFBTSxHQUFrQixJQUFJLENBQUM7SUFDcEIsT0FBTyxHQUFHLElBQUksR0FBRyxFQUFhLENBQUM7SUFDL0IsVUFBVSxDQUFnQjtJQUMxQixJQUFJLENBQVM7SUFDYixTQUFTLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO0lBRXhDLFlBQVksT0FBMEI7UUFDcEMsSUFBSSxDQUFDLElBQUksR0FBRyxPQUFPLENBQUMsSUFBSSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxVQUFVLEdBQUcsT0FBTyxDQUFDLFVBQVUsQ0FBQztRQUNyQyxJQUFJLENBQUMsR0FBRyxHQUFHLE9BQU8sRUFBRSxDQUFDO1FBQ3JCLElBQUksQ0FBQyxXQUFXLEVBQUUsQ0FBQztJQUNyQixDQUFDO0lBRUQsOEVBQThFO0lBQzlFLFdBQVc7SUFDWCw4RUFBOEU7SUFFOUUsS0FBSyxDQUFDLEtBQXNCO1FBQzFCLEtBQUssTUFBTSxNQUFNLElBQUksSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1lBQ2xDLElBQUksSUFBSSxDQUFDLGFBQWEsQ0FBQyxLQUFLLEVBQUUsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLENBQUM7Z0JBQzdDLE1BQU0sQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLFNBQVMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLENBQUM7WUFD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Q0FBQztZQUNmLENBQUM7UUFDSCxDQUFDO1FBQ0QsSUFBSSxNQUFNLENBQUMsYUFBYSxJQUFJLEtBQUssQ0FBQyxhQUFhLEtBQUssTUFBTSxDQUFDLGFBQWEsRUFBRSxDQUFDO1lBQ3pFLE9BQU8sS0FBSyxDQUFDO1FBQ2YsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDO0lBQ2QsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBTU0UtYmFzZWQgcmVhbC10aW1lIGxvZyB2aWV3ZXIgc2luay5cbiAqXG4gKiBJbXBsZW1lbnRzIElMb2dTaW5rIGFuZCBydW5zIGFuIG9wdC1pbiBFeHByZXNzIEhUVFAgc2VydmVyIHRoYXQ6XG4gKiAtIFNlcnZlcyBhIGJyb3dzZXItYmFzZWQgbG9nIHZpZXdlciBhdCBHRVQgL1xuICogLSBTdHJlYW1zIGxvZyBlbnRyaWVzIHZpYSBTU0UgYXQgR0VUIC9sb2dzL3N0cmVhbVxuICogLSBFeHBvc2VzIGEgSlNPTiBxdWVyeSBlbmRwb2ludCBhdCBHRVQgL2xvZ3MgKGRlbGVnYXRlcyB0byBNZW1vcnlMb2dTaW5rKVxuICogLSBQcm92aWRlcyBhIGhlYWx0aCBlbmRwb2ludCBhdCBHRVQgL2hlYWx0aFxuICpcbiAqIFNlZSBkb2NzL0xPR0dJTkctREVTSUdOLm1kIMKnNC42IGZvciB0aGUgZnVsbCBkZXNpZ24uXG4gKi9cblxuaW1wb3J0IGV4cHJlc3MgZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3QsIFJlc3BvbnNlIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgdHlwZSB7IFNlcnZlciB9IGZyb20gJ2h0dHAnO1xuaW1wb3J0IHR5cGUgeyBJTG9nU2luaywgVW5pZmllZExvZ0VudHJ5LCBMb2dDYXRlZ29yeSwgTG9nTGV2ZWwgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5pbXBvcnQgeyBMT0dfTEVWRUxfUFJJT1JJVFkgfSBmcm9tICcuLi90eXBlcy5qcyc7XG5pbXBvcnQgdHlwZSB7IE1lbW9yeUxvZ1NpbmsgfSBmcm9tICcuL01lbW9yeUxvZ1NpbmsuanMnO1xuaW1wb3J0IHsgZ2V0Vmlld2VySHRtbCB9IGZyb20gJy4uL3ZpZXdlci92aWV3ZXJIdG1sLmpzJztcblxuZXhwb3J0IGludGVyZmFjZSBTU0VMb2dTaW5rT3B0aW9ucyB7XG4gIHBvcnQ6IG51bWJlcjtcbiAgbWVtb3J5U2luazogTWVtb3J5TG9nU2luaztcbn1cblxuaW50ZXJmYWNlIFNTRUNsaWVudEZpbHRlciB7XG4gIGNhdGVnb3J5PzogTG9nQ2F0ZWdvcnk7XG4gIGxldmVsPzogTG9nTGV2ZWw7XG4gIHNvdXJjZT86IHN0cmluZztcbiAgY29ycmVsYXRpb25JZD86IHN0cmluZztcbn1cblxuaW50ZXJmYWNlIFNTRUNsaWVudCB7XG4gIHJlczogUmVzcG9uc2U7XG4gIGZpbHRlcjogU1NFQ2xpZW50RmlsdGVyO1xufVxuXG5leHBvcnQgY2xhc3MgU1NFTG9nU2luayBpbXBsZW1lbnRzIElMb2dTaW5rIHtcbiAgcHJpdmF0ZSByZWFkb25seSBhcHA6IFJldHVyblR5cGU8dHlwZW9mIGV4cHJlc3M+O1xuICBwcml2YXRlIHNlcnZlcjogU2VydmVyIHwgbnVsbCA9IG51bGw7XG4gIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50cyA9IG5ldyBTZXQ8U1NFQ2xpZW50PigpO1xuICBwcml2YXRlIHJlYWRvbmx5IG1lbW9yeVNpbms6IE1lbW9yeUxvZ1Npbms7XG4gIHByaXZhdGUgcmVhZG9ubHkgcG9ydDogbnVtYmVyO1xuICBwcml2YXRlIHJlYWRvbmx5IHN0YXJ0VGltZSA9IERhdGUubm93KCk7XG5cbiAgY29uc3RydWN0b3Iob3B0aW9uczogU1NFTG9nU2lua09wdGlvbnMpIHtcbiAgICB0aGlzLnBvcnQgPSBvcHRpb25zLnBvcnQ7XG4gICAgdGhpcy5tZW1vcnlTaW5rID0gb3B0aW9ucy5tZW1vcnlTaW5rO1xuICAgIHRoaXMuYXBwID0gZXhwcmVzcygpO1xuICAgIHRoaXMuc2V0dXBSb3V0ZXMoKTtcbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBJTG9nU2lua1xuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cblxuICB3cml0ZShlbnRyeTogVW5pZmllZExvZ0VudHJ5KTogdm9pZCB7XG4gICAgZm9yIChjb25zdCBjbGllbnQgb2YgdGhpcy5jbGllbnRzKSB7XG4gICAgICBpZiAodGhpcy5tYXRjaGVzRmlsdGVyKGVudHJ5LCBjbGllbnQuZmlsdGVyKSkge1xuICAgICAgICBjbGllbnQucmVzLndyaXRlKGBkYXRhOiAke0pTT04uc3RyaW5naWZ5KGVudHJ5KX1cXG5cXG5gKTtcbiAgICAgIH1cbiAgICB9XG4gIH1cblxuICBhc3luYyBmbHVzaCgpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBOby1vcCDigJQgU1NFIHdyaXRlcyBhcmUgaW1tZWRpYXRlLlxuICB9XG5cbiAgYXN5bmMgY2xvc2UoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgLy8gRW5kIGFsbCBjbGllbnQgY29ubmVjdGlvbnNcbiAgICBmb3IgKGNvbnN0IGNsaWVudCBvZiB0aGlzLmNsaWVudHMpIHtcbiAgICAgIGNsaWVudC5yZXMuZW5kKCk7XG4gICAgfVxuICAgIHRoaXMuY2xpZW50cy5jbGVhcigpO1xuXG4gICAgLy8gU2h1dCBkb3duIEhUVFAgc2VydmVyXG4gICAgaWYgKHRoaXMuc2VydmVyKSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4ge1xuICAgICAgICB0aGlzLnNlcnZlciEuY2xvc2UoKCkgPT4gcmVzb2x2ZSgpKTtcbiAgICAgIH0pO1xuICAgICAgdGhpcy5zZXJ2ZXIgPSBudWxsO1xuICAgIH1cbiAgfVxuXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuICAvLyBMaWZlY3ljbGVcbiAgLy8gLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tXG5cbiAgYXN5bmMgc3RhcnQoKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgICB0aGlzLnNlcnZlciA9IHRoaXMuYXBwLmxpc3Rlbih0aGlzLnBvcnQsICcxMjcuMC4wLjEnLCAoKSA9PiB7XG4gICAgICAgIHRoaXMuc2VydmVyIS51bnJlZigpO1xuICAgICAgICByZXNvbHZlKCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgfVxuXG4gIGdldCBjbGllbnRDb3VudCgpOiBudW1iZXIge1xuICAgIHJldHVybiB0aGlzLmNsaWVudHMuc2l6ZTtcbiAgfVxuXG4gIGdldFBvcnQoKTogbnVtYmVyIHtcbiAgICBpZiAoIXRoaXMuc2VydmVyKSByZXR1cm4gdGhpcy5wb3J0O1xuICAgIGNvbnN0IGFkZHIgPSB0aGlzLnNlcnZlci5hZGRyZXNzKCk7XG4gICAgaWYgKGFkZHIgJiYgdHlwZW9mIGFkZHIgPT09ICdvYmplY3QnKSByZXR1cm4gYWRkci5wb3J0O1xuICAgIHJldHVybiB0aGlzLnBvcnQ7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gUm91dGVzXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIHByaXZhdGUgc2V0dXBSb3V0ZXMoKTogdm9pZCB7XG4gICAgLy8gVmlld2VyIEhUTUxcbiAgICB0aGlzLmFwcC5nZXQoJy8nLCAoX3JlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICAgICAgY29uc3QgYWN0dWFsUG9ydCA9IHRoaXMuZ2V0UG9ydCgpO1xuICAgICAgcmVzLnR5cGUoJ2h0bWwnKS5zZW5kKGdldFZpZXdlckh0bWwoYWN0dWFsUG9ydCkpO1xuICAgIH0pO1xuXG4gICAgLy8gU1NFIHN0cmVhbVxuICAgIHRoaXMuYXBwLmdldCgnL2xvZ3Mvc3RyZWFtJywgKHJlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICAgICAgcmVzLndyaXRlSGVhZCgyMDAsIHtcbiAgICAgICAgJ0NvbnRlbnQtVHlwZSc6ICd0ZXh0L2V2ZW50LXN0cmVhbScsXG4gICAgICAgICdDYWNoZS1Db250cm9sJzogJ25vLWNhY2hlJyxcbiAgICAgICAgJ0Nvbm5lY3Rpb24nOiAna2VlcC1hbGl2ZScsXG4gICAgICB9KTtcbiAgICAgIHJlcy53cml0ZSgnOmNvbm5lY3RlZFxcblxcbicpO1xuXG4gICAgICBjb25zdCBmaWx0ZXI6IFNTRUNsaWVudEZpbHRlciA9IHt9O1xuICAgICAgaWYgKHR5cGVvZiByZXEucXVlcnlbJ2NhdGVnb3J5J10gPT09ICdzdHJpbmcnICYmIHJlcS5xdWVyeVsnY2F0ZWdvcnknXSkge1xuICAgICAgICBmaWx0ZXIuY2F0ZWdvcnkgPSByZXEucXVlcnlbJ2NhdGVnb3J5J10gYXMgTG9nQ2F0ZWdvcnk7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHJlcS5xdWVyeVsnbGV2ZWwnXSA9PT0gJ3N0cmluZycgJiYgcmVxLnF1ZXJ5WydsZXZlbCddKSB7XG4gICAgICAgIGZpbHRlci5sZXZlbCA9IHJlcS5xdWVyeVsnbGV2ZWwnXSBhcyBMb2dMZXZlbDtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcmVxLnF1ZXJ5Wydzb3VyY2UnXSA9PT0gJ3N0cmluZycgJiYgcmVxLnF1ZXJ5Wydzb3VyY2UnXSkge1xuICAgICAgICBmaWx0ZXIuc291cmNlID0gcmVxLnF1ZXJ5Wydzb3VyY2UnXTtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcmVxLnF1ZXJ5Wydjb3JyZWxhdGlvbklkJ10gPT09ICdzdHJpbmcnICYmIHJlcS5xdWVyeVsnY29ycmVsYXRpb25JZCddKSB7XG4gICAgICAgIGZpbHRlci5jb3JyZWxhdGlvbklkID0gcmVxLnF1ZXJ5Wydjb3JyZWxhdGlvbklkJ107XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IGNsaWVudDogU1NFQ2xpZW50ID0geyByZXMsIGZpbHRlciB9O1xuICAgICAgdGhpcy5jbGllbnRzLmFkZChjbGllbnQpO1xuXG4gICAgICAvLyBCYWNrZmlsbCByZWNlbnQgaGlzdG9yeSBzbyB0aGUgdmlld2VyIHNob3dzIGNvbnRleHQgb24gY29ubmVjdFxuICAgICAgY29uc3QgaGlzdG9yeSA9IHRoaXMubWVtb3J5U2luay5xdWVyeSh7IGNhdGVnb3J5OiAnYWxsJywgbGltaXQ6IDUwMCB9KTtcbiAgICAgIC8vIFNlbmQgb2xkZXN0LWZpcnN0IHNvIHRoZSB2aWV3ZXIgZGlzcGxheXMgaW4gY2hyb25vbG9naWNhbCBvcmRlclxuICAgICAgY29uc3QgZW50cmllcyA9IGhpc3RvcnkuZW50cmllcy5zbGljZSgpLnJldmVyc2UoKTtcbiAgICAgIGZvciAoY29uc3QgZW50cnkgb2YgZW50cmllcykge1xuICAgICAgICByZXMud3JpdGUoYGRhdGE6ICR7SlNPTi5zdHJpbmdpZnkoZW50cnkpfVxcblxcbmApO1xuICAgICAgfVxuXG4gICAgICByZXEub24oJ2Nsb3NlJywgKCkgPT4ge1xuICAgICAgICB0aGlzLmNsaWVudHMuZGVsZXRlKGNsaWVudCk7XG4gICAgICB9KTtcbiAgICB9KTtcblxuICAgIC8vIEpTT04gcXVlcnkgKGRlbGVnYXRlcyB0byBNZW1vcnlMb2dTaW5rKVxuICAgIHRoaXMuYXBwLmdldCgnL2xvZ3MnLCAocmVxOiBSZXF1ZXN0LCByZXM6IFJlc3BvbnNlKSA9PiB7XG4gICAgICBjb25zdCBvcHRpb25zOiBSZWNvcmQ8c3RyaW5nLCB1bmtub3duPiA9IHt9O1xuICAgICAgaWYgKHR5cGVvZiByZXEucXVlcnlbJ2NhdGVnb3J5J10gPT09ICdzdHJpbmcnICYmIHJlcS5xdWVyeVsnY2F0ZWdvcnknXSkge1xuICAgICAgICBvcHRpb25zWydjYXRlZ29yeSddID0gcmVxLnF1ZXJ5WydjYXRlZ29yeSddO1xuICAgICAgfVxuICAgICAgaWYgKHR5cGVvZiByZXEucXVlcnlbJ2xldmVsJ10gPT09ICdzdHJpbmcnICYmIHJlcS5xdWVyeVsnbGV2ZWwnXSkge1xuICAgICAgICBvcHRpb25zWydsZXZlbCddID0gcmVxLnF1ZXJ5WydsZXZlbCddO1xuICAgICAgfVxuICAgICAgaWYgKHR5cGVvZiByZXEucXVlcnlbJ3NvdXJjZSddID09PSAnc3RyaW5nJyAmJiByZXEucXVlcnlbJ3NvdXJjZSddKSB7XG4gICAgICAgIG9wdGlvbnNbJ3NvdXJjZSddID0gcmVxLnF1ZXJ5Wydzb3VyY2UnXTtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcmVxLnF1ZXJ5WydtZXNzYWdlJ10gPT09ICdzdHJpbmcnICYmIHJlcS5xdWVyeVsnbWVzc2FnZSddKSB7XG4gICAgICAgIG9wdGlvbnNbJ21lc3NhZ2UnXSA9IHJlcS5xdWVyeVsnbWVzc2FnZSddO1xuICAgICAgfVxuICAgICAgaWYgKHR5cGVvZiByZXEucXVlcnlbJ2xpbWl0J10gPT09ICdzdHJpbmcnKSB7XG4gICAgICAgIG9wdGlvbnNbJ2xpbWl0J10gPSBwYXJzZUludChyZXEucXVlcnlbJ2xpbWl0J10sIDEwKTtcbiAgICAgIH1cbiAgICAgIGlmICh0eXBlb2YgcmVxLnF1ZXJ5WydvZmZzZXQnXSA9PT0gJ3N0cmluZycpIHtcbiAgICAgICAgb3B0aW9uc1snb2Zmc2V0J10gPSBwYXJzZUludChyZXEucXVlcnlbJ29mZnNldCddLCAxMCk7XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHJlcS5xdWVyeVsnc2luY2UnXSA9PT0gJ3N0cmluZycgJiYgcmVxLnF1ZXJ5WydzaW5jZSddKSB7XG4gICAgICAgIG9wdGlvbnNbJ3NpbmNlJ10gPSByZXEucXVlcnlbJ3NpbmNlJ107XG4gICAgICB9XG4gICAgICBpZiAodHlwZW9mIHJlcS5xdWVyeVsndW50aWwnXSA9PT0gJ3N0cmluZycgJiYgcmVxLnF1ZXJ5Wyd1bnRpbCddKSB7XG4gICAgICAgIG9wdGlvbnNbJ3VudGlsJ10gPSByZXEucXVlcnlbJ3VudGlsJ107XG4gICAgICB9XG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IHRoaXMubWVtb3J5U2luay5xdWVyeShvcHRpb25zKTtcbiAgICAgIHJlcy5qc29uKHJlc3VsdCk7XG4gICAgfSk7XG5cbiAgICAvLyBIZWFsdGhcbiAgICB0aGlzLmFwcC5nZXQoJy9oZWFsdGgnLCAoX3JlcTogUmVxdWVzdCwgcmVzOiBSZXNwb25zZSkgPT4ge1xuICAgICAgcmVzLmpzb24oe1xuICAgICAgICBzdGF0dXM6ICdvaycsXG4gICAgICAgIGNsaWVudHM6IHRoaXMuY2xpZW50Q291bnQsXG4gICAgICAgIHVwdGltZTogTWF0aC5mbG9vcigoRGF0ZS5ub3coKSAtIHRoaXMuc3RhcnRUaW1lKSAvIDEwMDApLFxuICAgICAgfSk7XG4gICAgfSk7XG4gIH1cblxuICAvLyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS1cbiAgLy8gRmlsdGVyIG1hdGNoaW5nXG4gIC8vIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuXG4gIHByaXZhdGUgbWF0Y2hlc0ZpbHRlcihlbnRyeTogVW5pZmllZExvZ0VudHJ5LCBmaWx0ZXI6IFNTRUNsaWVudEZpbHRlcik6IGJvb2xlYW4ge1xuICAgIGlmIChmaWx0ZXIuY2F0ZWdvcnkgJiYgZW50cnkuY2F0ZWdvcnkgIT09IGZpbHRlci5jYXRlZ29yeSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoZmlsdGVyLmxldmVsICYmIExPR19MRVZFTF9QUklPUklUWVtlbnRyeS5sZXZlbF0gPCBMT0dfTEVWRUxfUFJJT1JJVFlbZmlsdGVyLmxldmVsXSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgICBpZiAoZmlsdGVyLnNvdXJjZSkge1xuICAgICAgY29uc3QgbmVlZGxlID0gZmlsdGVyLnNvdXJjZS50b0xvd2VyQ2FzZSgpO1xuICAgICAgaWYgKCFlbnRyeS5zb3VyY2UudG9Mb3dlckNhc2UoKS5pbmNsdWRlcyhuZWVkbGUpKSB7XG4gICAgICAgIHJldHVybiBmYWxzZTtcbiAgICAgIH1cbiAgICB9XG4gICAgaWYgKGZpbHRlci5jb3JyZWxhdGlvbklkICYmIGVudHJ5LmNvcnJlbGF0aW9uSWQgIT09IGZpbHRlci5jb3JyZWxhdGlvbklkKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICAgIHJldHVybiB0cnVlO1xuICB9XG59XG4iXX0=
@@ -1,8 +0,0 @@
1
- /**
2
- * Embedded HTML template for the DollhouseMCP Log Viewer.
3
- *
4
- * Returns a self-contained vanilla JS/CSS page that connects to the
5
- * SSELogSink's /logs/stream endpoint via EventSource. See docs/LOGGING-DESIGN.md §4.6.
6
- */
7
- export declare function getViewerHtml(port: number): string;
8
- //# sourceMappingURL=viewerHtml.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewerHtml.d.ts","sourceRoot":"","sources":["../../../src/logging/viewer/viewerHtml.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAoMlD"}