@dollhousemcp/mcp-server 2.0.2 → 2.0.4
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/CHANGELOG.md +35 -0
- package/README.github.md +8 -33
- package/README.md +10 -8
- package/README.md.backup +10 -8
- package/README.npm.md +10 -8
- package/dist/constants/version.d.ts +3 -0
- package/dist/constants/version.d.ts.map +1 -0
- package/dist/constants/version.js +4 -0
- package/dist/generated/version.d.ts +2 -2
- package/dist/generated/version.js +3 -3
- package/dist/logging/sinks/SSELogSink.d.ts +35 -0
- package/dist/logging/sinks/SSELogSink.d.ts.map +1 -0
- package/dist/logging/sinks/SSELogSink.js +181 -0
- package/dist/logging/viewer/viewerHtml.d.ts +8 -0
- package/dist/logging/viewer/viewerHtml.d.ts.map +1 -0
- package/dist/logging/viewer/viewerHtml.js +204 -0
- package/dist/security/audit/config/suppressions.d.ts.map +1 -1
- package/dist/security/audit/config/suppressions.js +6 -1
- package/dist/seed-elements/memories/dollhousemcp-baseline-knowledge.yaml +149 -0
- package/dist/seed-elements/memories/how-to-create-custom-auto-load-memories.yaml +455 -0
- package/dist/seed-elements/memories/priority-best-practices-for-teams.yaml +542 -0
- package/dist/seed-elements/memories/token-estimation-guidelines.yaml +602 -0
- package/dist/web/public/app.js +29 -10
- package/dist/web/public/fonts/ibmplexmono--F63fjptAgt5VM-kVkqdyU8n1i8q131nj-o.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F63fjptAgt5VM-kVkqdyU8n1iAq131nj-otFQ.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F63fjptAgt5VM-kVkqdyU8n1iEq131nj-otFQ.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F63fjptAgt5VM-kVkqdyU8n1iIq131nj-otFQ.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F63fjptAgt5VM-kVkqdyU8n1isq131nj-otFQ.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F6qfjptAgt5VM-kVkqdyU8n3twJwl1FgsAXHNlYzg.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F6qfjptAgt5VM-kVkqdyU8n3twJwl5FgsAXHNlYzg.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F6qfjptAgt5VM-kVkqdyU8n3twJwl9FgsAXHNlYzg.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F6qfjptAgt5VM-kVkqdyU8n3twJwlBFgsAXHNk.woff2 +0 -0
- package/dist/web/public/fonts/ibmplexmono--F6qfjptAgt5VM-kVkqdyU8n3twJwlRFgsAXHNlYzg.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggOxSvfedN62Zw.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggSxSvfedN62Zw.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggexSvfedN4.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggixSvfedN62Zw.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggmxSvfedN62Zw.woff2 +0 -0
- package/dist/web/public/fonts/manrope-xn7gYHE41ni1AdIRggqxSvfedN62Zw.woff2 +0 -0
- package/dist/web/public/fonts/plusjakartasans-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko20yygg_vb.woff2 +0 -0
- package/dist/web/public/fonts/plusjakartasans-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko40yygg_vbd-E.woff2 +0 -0
- package/dist/web/public/fonts/plusjakartasans-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko50yygg_vbd-E.woff2 +0 -0
- package/dist/web/public/fonts/plusjakartasans-LDIoaomQNQcsA88c7O9yZ4KMCoOg4Ko70yygg_vbd-E.woff2 +0 -0
- package/dist/web/public/fonts.css +270 -0
- package/dist/web/public/index.html +365 -0
- package/dist/web/public/logs.css +472 -0
- package/dist/web/public/metrics.css +238 -0
- package/dist/web/public/permissions.css +364 -0
- package/dist/web/public/sessions.css +235 -0
- package/dist/web/public/setup.css +648 -0
- package/dist/web/public/setup.js +752 -0
- package/dist/web/public/styles.css +1717 -0
- package/dist/web/routes/setupRoutes.d.ts +18 -0
- package/dist/web/routes/setupRoutes.d.ts.map +1 -0
- package/dist/web/routes/setupRoutes.js +360 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +11 -1
- package/package.json +4 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.4] - 2026-04-02
|
|
4
|
+
|
|
5
|
+
### Hotfix: npm package missing web assets
|
|
6
|
+
|
|
7
|
+
- **Fix**: `package.json` `files` field now includes `dist/web/public/**` and `dist/seed-elements/**`
|
|
8
|
+
- v2.0.3 was missing `.html`, `.css`, and font files from the web console, causing `--web` mode to crash with `NotFoundError`
|
|
9
|
+
- Also missing seed element `.yaml` files, causing seed memory installation to fail
|
|
10
|
+
- Added package inclusion tests to prevent regression
|
|
11
|
+
|
|
12
|
+
## [2.0.3] - 2026-04-02
|
|
13
|
+
|
|
14
|
+
### Setup Tab — Interactive Installer
|
|
15
|
+
|
|
16
|
+
One command opens a browser-based setup wizard for installing DollhouseMCP on any MCP client:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npx @dollhousemcp/mcp-server@latest --web
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
#### Features
|
|
23
|
+
- **One-click install** for 9 platforms: Claude Desktop, Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Cline, LM Studio
|
|
24
|
+
- **Auto-updating vs Pinned version** toggle — all config snippets update dynamically
|
|
25
|
+
- **Installation detection** — scans existing client configs, shows green/amber state based on whether current settings match
|
|
26
|
+
- **Open config file** buttons — opens client config in the system default editor
|
|
27
|
+
- **Version-aware .mcpb download** — resolves correct versioned Desktop Extension from GitHub API
|
|
28
|
+
- **Install verification** — confirms config was written after install
|
|
29
|
+
- **Keyboard navigation** — arrow keys, Home, End for platform tabs
|
|
30
|
+
|
|
31
|
+
#### Technical
|
|
32
|
+
- Bundled `install-mcp` (MIT, by Dhravya Shah) as runtime dependency
|
|
33
|
+
- 7 of 9 platform panels generated from declarative PLATFORMS registry (zero HTML duplication)
|
|
34
|
+
- `UnicodeValidator.normalize()` on all server-side user input
|
|
35
|
+
- 171 tests including JSDOM DOM validation of generated panels
|
|
36
|
+
- All READMEs and guides updated with interactive setup one-liner
|
|
37
|
+
|
|
3
38
|
## [2.0.0] - 2026-04-01
|
|
4
39
|
|
|
5
40
|
DollhouseMCP v2.0.0 is the first stable release of the v2 line. This release brings MCP-AQL (Agent Query Language), Gatekeeper permission system, unified web console, multi-session support, comprehensive metrics, and 9000+ tests across unit, integration, security, and e2e suites.
|
package/README.github.md
CHANGED
|
@@ -337,49 +337,24 @@ Assistant: "I've saved 'Hard SciFi Writer' to your portfolio. You can activate i
|
|
|
337
337
|
|
|
338
338
|
## 📦 Installation
|
|
339
339
|
|
|
340
|
-
###
|
|
340
|
+
### Interactive Setup (Recommended)
|
|
341
341
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
<tr>
|
|
350
|
-
<td><strong>Local Install</strong><br>(Recommended)</td>
|
|
351
|
-
<td>Most users, multiple configs, customization</td>
|
|
352
|
-
<td>✅ Multiple setups<br>✅ Easy backup<br>✅ No permissions</td>
|
|
353
|
-
<td>❌ Longer path in config</td>
|
|
354
|
-
</tr>
|
|
355
|
-
<tr>
|
|
356
|
-
<td><strong>npx</strong></td>
|
|
357
|
-
<td>Quick testing, always latest</td>
|
|
358
|
-
<td>✅ No install<br>✅ Always updated</td>
|
|
359
|
-
<td>❌ Slower startup<br>❌ Needs internet</td>
|
|
360
|
-
</tr>
|
|
361
|
-
<tr>
|
|
362
|
-
<td><strong>Global Install</strong></td>
|
|
363
|
-
<td>Single shared instance</td>
|
|
364
|
-
<td>✅ Short command</td>
|
|
365
|
-
<td>❌ Only one version<br>❌ Needs sudo/admin</td>
|
|
366
|
-
</tr>
|
|
367
|
-
</table>
|
|
342
|
+
One command opens a browser-based setup wizard with one-click install for 9 MCP clients:
|
|
343
|
+
|
|
344
|
+
```bash
|
|
345
|
+
npx @dollhousemcp/mcp-server@latest --web
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Supports Claude Desktop, Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Cline, and LM Studio. Detects existing installations, auto-updating and pinned version options.
|
|
368
349
|
|
|
369
350
|
---
|
|
370
351
|
|
|
371
352
|
### Claude Code
|
|
372
353
|
|
|
373
|
-
All projects (recommended):
|
|
374
354
|
```bash
|
|
375
355
|
claude mcp add -s user dollhousemcp -- npx -y @dollhousemcp/mcp-server
|
|
376
356
|
```
|
|
377
357
|
|
|
378
|
-
Current project only:
|
|
379
|
-
```bash
|
|
380
|
-
claude mcp add dollhousemcp -- npx -y @dollhousemcp/mcp-server
|
|
381
|
-
```
|
|
382
|
-
|
|
383
358
|
---
|
|
384
359
|
|
|
385
360
|
### Method 1: Local Installation (Recommended)
|
package/README.md
CHANGED
|
@@ -58,23 +58,25 @@ Your **portfolio** (`~/.dollhouse/portfolio/`) is a local folder that holds all
|
|
|
58
58
|
|
|
59
59
|
DollhouseMCP installs on any MCP-compatible AI client — Claude Code, Claude Desktop, Cursor, Gemini, Codex, and local LLMs. Core element management (create, activate, search, browse) works across all platforms. Advanced features (Gatekeeper confirmation flows, agentic loop execution) have been tested extensively on Claude Code and should work on any client that supports standard MCP tool call/response patterns.
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
**Interactive Setup** (any platform):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx @dollhousemcp/mcp-server@latest --web
|
|
65
|
+
```
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
Opens a browser-based setup wizard with one-click install for Claude Desktop, Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Cline, and LM Studio. Detects existing installations, supports auto-updating and pinned versions.
|
|
64
68
|
|
|
65
69
|
**Claude Code** (one command):
|
|
66
70
|
|
|
67
|
-
All projects (recommended):
|
|
68
71
|
```bash
|
|
69
72
|
claude mcp add -s user dollhousemcp -- npx -y @dollhousemcp/mcp-server
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```
|
|
75
|
+
**Claude Desktop** (one-click install):
|
|
76
|
+
|
|
77
|
+
Download the [DollhouseMCP Desktop Extension](https://github.com/DollhouseMCP/mcp-server/releases/latest) (`.mcpb` file) and double-click it. Claude Desktop handles the rest — no terminal required.
|
|
76
78
|
|
|
77
|
-
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md)
|
|
79
|
+
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md) or run the interactive setup above.
|
|
78
80
|
|
|
79
81
|
Then start a conversation:
|
|
80
82
|
|
package/README.md.backup
CHANGED
|
@@ -58,23 +58,25 @@ Your **portfolio** (`~/.dollhouse/portfolio/`) is a local folder that holds all
|
|
|
58
58
|
|
|
59
59
|
DollhouseMCP installs on any MCP-compatible AI client — Claude Code, Claude Desktop, Cursor, Gemini, Codex, and local LLMs. Core element management (create, activate, search, browse) works across all platforms. Advanced features (Gatekeeper confirmation flows, agentic loop execution) have been tested extensively on Claude Code and should work on any client that supports standard MCP tool call/response patterns.
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
**Interactive Setup** (any platform):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx @dollhousemcp/mcp-server@latest --web
|
|
65
|
+
```
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
Opens a browser-based setup wizard with one-click install for Claude Desktop, Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Cline, and LM Studio. Detects existing installations, supports auto-updating and pinned versions.
|
|
64
68
|
|
|
65
69
|
**Claude Code** (one command):
|
|
66
70
|
|
|
67
|
-
All projects (recommended):
|
|
68
71
|
```bash
|
|
69
72
|
claude mcp add -s user dollhousemcp -- npx -y @dollhousemcp/mcp-server
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```
|
|
75
|
+
**Claude Desktop** (one-click install):
|
|
76
|
+
|
|
77
|
+
Download the [DollhouseMCP Desktop Extension](https://github.com/DollhouseMCP/mcp-server/releases/latest) (`.mcpb` file) and double-click it. Claude Desktop handles the rest — no terminal required.
|
|
76
78
|
|
|
77
|
-
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md)
|
|
79
|
+
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md) or run the interactive setup above.
|
|
78
80
|
|
|
79
81
|
Then start a conversation:
|
|
80
82
|
|
package/README.npm.md
CHANGED
|
@@ -58,23 +58,25 @@ Your **portfolio** (`~/.dollhouse/portfolio/`) is a local folder that holds all
|
|
|
58
58
|
|
|
59
59
|
DollhouseMCP installs on any MCP-compatible AI client — Claude Code, Claude Desktop, Cursor, Gemini, Codex, and local LLMs. Core element management (create, activate, search, browse) works across all platforms. Advanced features (Gatekeeper confirmation flows, agentic loop execution) have been tested extensively on Claude Code and should work on any client that supports standard MCP tool call/response patterns.
|
|
60
60
|
|
|
61
|
-
**
|
|
61
|
+
**Interactive Setup** (any platform):
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npx @dollhousemcp/mcp-server@latest --web
|
|
65
|
+
```
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
Opens a browser-based setup wizard with one-click install for Claude Desktop, Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Cline, and LM Studio. Detects existing installations, supports auto-updating and pinned versions.
|
|
64
68
|
|
|
65
69
|
**Claude Code** (one command):
|
|
66
70
|
|
|
67
|
-
All projects (recommended):
|
|
68
71
|
```bash
|
|
69
72
|
claude mcp add -s user dollhousemcp -- npx -y @dollhousemcp/mcp-server
|
|
70
73
|
```
|
|
71
74
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
```
|
|
75
|
+
**Claude Desktop** (one-click install):
|
|
76
|
+
|
|
77
|
+
Download the [DollhouseMCP Desktop Extension](https://github.com/DollhouseMCP/mcp-server/releases/latest) (`.mcpb` file) and double-click it. Claude Desktop handles the rest — no terminal required.
|
|
76
78
|
|
|
77
|
-
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md)
|
|
79
|
+
**Other platforms** — see the [Quick Start Guide](docs/guides/quick-start.md) or run the interactive setup above.
|
|
78
80
|
|
|
79
81
|
Then start a conversation:
|
|
80
82
|
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
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==
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Auto-generated file - DO NOT EDIT
|
|
3
3
|
* Generated at build time by scripts/generate-version.js
|
|
4
4
|
*/
|
|
5
|
-
export declare const PACKAGE_VERSION = "2.0.
|
|
6
|
-
export declare const BUILD_TIMESTAMP = "2026-04-
|
|
5
|
+
export declare const PACKAGE_VERSION = "2.0.4";
|
|
6
|
+
export declare const BUILD_TIMESTAMP = "2026-04-02T18:33:53.805Z";
|
|
7
7
|
export declare const BUILD_TYPE: 'npm' | 'git';
|
|
8
8
|
export declare const PACKAGE_NAME = "@dollhousemcp/mcp-server";
|
|
9
9
|
//# sourceMappingURL=version.d.ts.map
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Auto-generated file - DO NOT EDIT
|
|
3
3
|
* Generated at build time by scripts/generate-version.js
|
|
4
4
|
*/
|
|
5
|
-
export const PACKAGE_VERSION = '2.0.
|
|
6
|
-
export const BUILD_TIMESTAMP = '2026-04-
|
|
5
|
+
export const PACKAGE_VERSION = '2.0.4';
|
|
6
|
+
export const BUILD_TIMESTAMP = '2026-04-02T18:33:53.805Z';
|
|
7
7
|
export const BUILD_TYPE = 'npm';
|
|
8
8
|
export const PACKAGE_NAME = '@dollhousemcp/mcp-server';
|
|
9
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmVyc2lvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9nZW5lcmF0ZWQvdmVyc2lvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7O0dBR0c7QUFFSCxNQUFNLENBQUMsTUFBTSxlQUFlLEdBQUcsT0FBTyxDQUFDO0FBQ3ZDLE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRywwQkFBMEIsQ0FBQztBQUMxRCxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQWtCLEtBQUssQ0FBQztBQUMvQyxNQUFNLENBQUMsTUFBTSxZQUFZLEdBQUcsMEJBQTBCLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvKipcbiAqIEF1dG8tZ2VuZXJhdGVkIGZpbGUgLSBETyBOT1QgRURJVFxuICogR2VuZXJhdGVkIGF0IGJ1aWxkIHRpbWUgYnkgc2NyaXB0cy9nZW5lcmF0ZS12ZXJzaW9uLmpzXG4gKi9cblxuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfVkVSU0lPTiA9ICcyLjAuNCc7XG5leHBvcnQgY29uc3QgQlVJTERfVElNRVNUQU1QID0gJzIwMjYtMDQtMDJUMTg6MzM6NTMuODA1Wic7XG5leHBvcnQgY29uc3QgQlVJTERfVFlQRTogJ25wbScgfCAnZ2l0JyA9ICducG0nO1xuZXhwb3J0IGNvbnN0IFBBQ0tBR0VfTkFNRSA9ICdAZG9sbGhvdXNlbWNwL21jcC1zZXJ2ZXInO1xuIl19
|
|
@@ -0,0 +1,35 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|
|
@@ -0,0 +1,181 @@
|
|
|
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=
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
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"}
|