@documentation-robotics/cli 0.1.0 → 0.1.7
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/LICENSE +21 -0
- package/README.md +466 -693
- package/dist/analysis/data-model-analyzer.js +189 -0
- package/dist/analysis/data-model-analyzer.js.map +7 -0
- package/dist/analysis/index.js +3 -0
- package/dist/analysis/index.js.map +7 -0
- package/dist/analysis/relationship-classifier.js +181 -0
- package/dist/analysis/relationship-classifier.js.map +7 -0
- package/dist/analyzers/base-analyzer.js +1 -0
- package/dist/analyzers/base-analyzer.js.map +7 -0
- package/dist/analyzers/call-graph-utils.js +39 -0
- package/dist/analyzers/call-graph-utils.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +1205 -0
- package/dist/analyzers/cbm-analyzer.js.map +7 -0
- package/dist/analyzers/heuristic-utils.js +55 -0
- package/dist/analyzers/heuristic-utils.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +146 -0
- package/dist/analyzers/mapping-loader.js.map +7 -0
- package/dist/analyzers/registry.js +124 -0
- package/dist/analyzers/registry.js.map +7 -0
- package/dist/analyzers/session-state.js +176 -0
- package/dist/analyzers/session-state.js.map +7 -0
- package/dist/analyzers/stdio-client.js +208 -0
- package/dist/analyzers/stdio-client.js.map +7 -0
- package/dist/analyzers/types.js +15 -0
- package/dist/analyzers/types.js.map +7 -0
- package/dist/analyzers/verify-engine.js +209 -0
- package/dist/analyzers/verify-engine.js.map +7 -0
- package/dist/analyzers/verify-ignore.js +130 -0
- package/dist/analyzers/verify-ignore.js.map +7 -0
- package/dist/analyzers/version.js +8 -0
- package/dist/analyzers/version.js.map +7 -0
- package/dist/audit/ai/runner.js +41 -0
- package/dist/audit/ai/runner.js.map +7 -0
- package/dist/audit/differential-analyzer.js +183 -0
- package/dist/audit/differential-analyzer.js.map +7 -0
- package/dist/audit/index.js +32 -0
- package/dist/audit/index.js.map +7 -0
- package/dist/audit/nodes/ai/evaluator.js +49 -0
- package/dist/audit/nodes/ai/evaluator.js.map +7 -0
- package/dist/audit/nodes/ai/parser.js +55 -0
- package/dist/audit/nodes/ai/parser.js.map +7 -0
- package/dist/audit/nodes/ai/prompts.js +103 -0
- package/dist/audit/nodes/ai/prompts.js.map +7 -0
- package/dist/audit/nodes/analysis/node-completeness-checker.js +32 -0
- package/dist/audit/nodes/analysis/node-completeness-checker.js.map +7 -0
- package/dist/audit/nodes/analysis/node-definition-analyzer.js +47 -0
- package/dist/audit/nodes/analysis/node-definition-analyzer.js.map +7 -0
- package/dist/audit/nodes/analysis/node-overlap-detector.js +79 -0
- package/dist/audit/nodes/analysis/node-overlap-detector.js.map +7 -0
- package/dist/audit/nodes/model/orchestrator.js +82 -0
- package/dist/audit/nodes/model/orchestrator.js.map +7 -0
- package/dist/audit/nodes/spec/loader.js +116 -0
- package/dist/audit/nodes/spec/loader.js.map +7 -0
- package/dist/audit/nodes/spec/orchestrator.js +68 -0
- package/dist/audit/nodes/spec/orchestrator.js.map +7 -0
- package/dist/audit/nodes/types.js +1 -0
- package/dist/audit/nodes/types.js.map +7 -0
- package/dist/audit/pipeline/index.js +5 -0
- package/dist/audit/pipeline/index.js.map +7 -0
- package/dist/audit/pipeline/pipeline-orchestrator.js +218 -0
- package/dist/audit/pipeline/pipeline-orchestrator.js.map +7 -0
- package/dist/audit/relationships/ai/evaluator.js +235 -0
- package/dist/audit/relationships/ai/evaluator.js.map +7 -0
- package/dist/audit/relationships/ai/parser.js +197 -0
- package/dist/audit/relationships/ai/parser.js.map +7 -0
- package/dist/audit/relationships/ai/prompts.js +274 -0
- package/dist/audit/relationships/ai/prompts.js.map +7 -0
- package/dist/audit/relationships/analysis/balance-assessor.js +115 -0
- package/dist/audit/relationships/analysis/balance-assessor.js.map +7 -0
- package/dist/audit/relationships/analysis/coverage-analyzer.js +99 -0
- package/dist/audit/relationships/analysis/coverage-analyzer.js.map +7 -0
- package/dist/audit/relationships/analysis/duplicate-detector.js +95 -0
- package/dist/audit/relationships/analysis/duplicate-detector.js.map +7 -0
- package/dist/audit/relationships/analysis/gap-analyzer.js +99 -0
- package/dist/audit/relationships/analysis/gap-analyzer.js.map +7 -0
- package/dist/audit/relationships/analysis/layer-templates.js +294 -0
- package/dist/audit/relationships/analysis/layer-templates.js.map +7 -0
- package/dist/audit/relationships/graph/connectivity.js +129 -0
- package/dist/audit/relationships/graph/connectivity.js.map +7 -0
- package/dist/audit/relationships/graph/relationship-graph.js +179 -0
- package/dist/audit/relationships/graph/relationship-graph.js.map +7 -0
- package/dist/audit/relationships/model/balance-assessor.js +49 -0
- package/dist/audit/relationships/model/balance-assessor.js.map +7 -0
- package/dist/audit/relationships/model/coverage-analyzer.js +34 -0
- package/dist/audit/relationships/model/coverage-analyzer.js.map +7 -0
- package/dist/audit/relationships/model/duplicate-detector.js +68 -0
- package/dist/audit/relationships/model/duplicate-detector.js.map +7 -0
- package/dist/audit/relationships/model/gap-analyzer.js +41 -0
- package/dist/audit/relationships/model/gap-analyzer.js.map +7 -0
- package/dist/audit/relationships/model/orchestrator.js +126 -0
- package/dist/audit/relationships/model/orchestrator.js.map +7 -0
- package/dist/audit/relationships/spec/orchestrator.js +157 -0
- package/dist/audit/relationships/spec/orchestrator.js.map +7 -0
- package/dist/audit/reports/index.js +5 -0
- package/dist/audit/reports/index.js.map +7 -0
- package/dist/audit/reports/report-generator.js +118 -0
- package/dist/audit/reports/report-generator.js.map +7 -0
- package/dist/audit/snapshot-storage.js +163 -0
- package/dist/audit/snapshot-storage.js.map +7 -0
- package/dist/audit/types.js +21 -0
- package/dist/audit/types.js.map +7 -0
- package/dist/cli.js +223 -92
- package/dist/cli.js.map +3 -3
- package/dist/coding-agents/agents/claude-code.js +147 -0
- package/dist/coding-agents/agents/claude-code.js.map +7 -0
- package/dist/coding-agents/agents/index.js +5 -0
- package/dist/coding-agents/agents/index.js.map +7 -0
- package/dist/coding-agents/agents/types.js +1 -0
- package/dist/coding-agents/agents/types.js.map +7 -0
- package/dist/coding-agents/base-chat-client.js +45 -0
- package/dist/coding-agents/base-chat-client.js.map +7 -0
- package/dist/coding-agents/chat-utils.js +23 -0
- package/dist/coding-agents/chat-utils.js.map +7 -0
- package/dist/coding-agents/claude-code-client.js +246 -0
- package/dist/coding-agents/claude-code-client.js.map +7 -0
- package/dist/coding-agents/copilot-client.js +209 -0
- package/dist/coding-agents/copilot-client.js.map +7 -0
- package/dist/commands/add.js +95 -33
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +511 -0
- package/dist/commands/analyzer.js.map +7 -0
- package/dist/commands/audit-diff.js +49 -0
- package/dist/commands/audit-diff.js.map +7 -0
- package/dist/commands/audit-snapshots.js +62 -0
- package/dist/commands/audit-snapshots.js.map +7 -0
- package/dist/commands/audit.js +81 -0
- package/dist/commands/audit.js.map +7 -0
- package/dist/commands/catalog.js +248 -0
- package/dist/commands/catalog.js.map +7 -0
- package/dist/commands/changeset.js +577 -66
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/chat.js +106 -83
- package/dist/commands/chat.js.map +2 -2
- package/dist/commands/claude.js +74 -0
- package/dist/commands/claude.js.map +7 -0
- package/dist/commands/conformance.js +71 -68
- package/dist/commands/conformance.js.map +2 -2
- package/dist/commands/copilot.js +75 -0
- package/dist/commands/copilot.js.map +7 -0
- package/dist/commands/delete.js +125 -11
- package/dist/commands/delete.js.map +3 -3
- package/dist/commands/discover-logic.js +44 -0
- package/dist/commands/discover-logic.js.map +7 -0
- package/dist/commands/docs.js +87 -0
- package/dist/commands/docs.js.map +7 -0
- package/dist/commands/element.js +14 -20
- package/dist/commands/element.js.map +2 -2
- package/dist/commands/export.js +13 -3
- package/dist/commands/export.js.map +2 -2
- package/dist/commands/import.js +51 -0
- package/dist/commands/import.js.map +7 -0
- package/dist/commands/info.js +30 -24
- package/dist/commands/info.js.map +2 -2
- package/dist/commands/init.js +27 -11
- package/dist/commands/init.js.map +2 -2
- package/dist/commands/list.js +63 -13
- package/dist/commands/list.js.map +3 -3
- package/dist/commands/relationship.js +225 -37
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/report.js +54 -0
- package/dist/commands/report.js.map +7 -0
- package/dist/commands/reports.js +45 -0
- package/dist/commands/reports.js.map +7 -0
- package/dist/commands/schema.js +135 -0
- package/dist/commands/schema.js.map +7 -0
- package/dist/commands/search.js +40 -12
- package/dist/commands/search.js.map +2 -2
- package/dist/commands/show.js +58 -27
- package/dist/commands/show.js.map +2 -2
- package/dist/commands/stats.js +35 -0
- package/dist/commands/stats.js.map +7 -0
- package/dist/commands/trace.js +39 -16
- package/dist/commands/trace.js.map +2 -2
- package/dist/commands/update.js +141 -14
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +210 -46
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/validate.js +142 -23
- package/dist/commands/validate.js.map +3 -3
- package/dist/commands/version.js +8 -12
- package/dist/commands/version.js.map +2 -2
- package/dist/commands/visualize.js +162 -25
- package/dist/commands/visualize.js.map +2 -2
- package/dist/core/base-snapshot-manager.js +84 -0
- package/dist/core/base-snapshot-manager.js.map +7 -0
- package/dist/core/changeset-exporter.js +305 -0
- package/dist/core/changeset-exporter.js.map +7 -0
- package/dist/core/changeset-validator.js +41 -0
- package/dist/core/changeset-validator.js.map +7 -0
- package/dist/core/changeset.js +88 -173
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/dependency-tracker.js +4 -2
- package/dist/core/dependency-tracker.js.map +2 -2
- package/dist/core/element.js +98 -27
- package/dist/core/element.js.map +2 -2
- package/dist/core/golden-copy-cache.js +290 -0
- package/dist/core/golden-copy-cache.js.map +7 -0
- package/dist/core/graph-model.js +266 -0
- package/dist/core/graph-model.js.map +7 -0
- package/dist/core/layer.js +159 -16
- package/dist/core/layer.js.map +2 -2
- package/dist/core/layers.js +32 -0
- package/dist/core/layers.js.map +7 -0
- package/dist/core/manifest.js +32 -11
- package/dist/core/manifest.js.map +2 -2
- package/dist/core/migration-registry.js +56 -1
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model.js +424 -217
- package/dist/core/model.js.map +3 -3
- package/dist/core/mutation-handler.js +179 -0
- package/dist/core/mutation-handler.js.map +7 -0
- package/dist/core/reference-registry.js +29 -21
- package/dist/core/reference-registry.js.map +2 -2
- package/dist/core/relationship-catalog.js +189 -0
- package/dist/core/relationship-catalog.js.map +7 -0
- package/dist/core/relationship-registry.js.map +2 -2
- package/dist/core/relationships.js.map +1 -1
- package/dist/core/report-data-model.js +285 -0
- package/dist/core/report-data-model.js.map +7 -0
- package/dist/core/spec-data-service.js +185 -0
- package/dist/core/spec-data-service.js.map +7 -0
- package/dist/core/spec-loader-types.js +1 -0
- package/dist/core/spec-loader-types.js.map +7 -0
- package/dist/core/spec-loader.js +234 -0
- package/dist/core/spec-loader.js.map +7 -0
- package/dist/core/staged-changeset-storage.js +282 -0
- package/dist/core/staged-changeset-storage.js.map +7 -0
- package/dist/core/staging-area.js +1002 -0
- package/dist/core/staging-area.js.map +7 -0
- package/dist/core/stats-collector.js +169 -0
- package/dist/core/stats-collector.js.map +7 -0
- package/dist/core/virtual-projection.js +443 -0
- package/dist/core/virtual-projection.js.map +7 -0
- package/dist/export/archimate-exporter.js +99 -60
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/export/audit-formatters.js +479 -0
- package/dist/export/audit-formatters.js.map +7 -0
- package/dist/export/diff-formatters.js +99 -0
- package/dist/export/diff-formatters.js.map +7 -0
- package/dist/export/export-manager.js.map +2 -2
- package/dist/export/graphml-exporter.js +44 -27
- package/dist/export/graphml-exporter.js.map +2 -2
- package/dist/export/index.js +4 -0
- package/dist/export/index.js.map +2 -2
- package/dist/export/json-schema-exporter.js +55 -42
- package/dist/export/json-schema-exporter.js.map +2 -2
- package/dist/export/markdown-exporter.js +92 -70
- package/dist/export/markdown-exporter.js.map +2 -2
- package/dist/export/markdown-generator.js +248 -0
- package/dist/export/markdown-generator.js.map +7 -0
- package/dist/export/markdown-utils.js +30 -0
- package/dist/export/markdown-utils.js.map +7 -0
- package/dist/export/mermaid-markdown-exporter.js +37 -0
- package/dist/export/mermaid-markdown-exporter.js.map +7 -0
- package/dist/export/openapi-exporter.js +119 -71
- package/dist/export/openapi-exporter.js.map +2 -2
- package/dist/export/plantuml-exporter.js +104 -39
- package/dist/export/plantuml-exporter.js.map +2 -2
- package/dist/export/report-formatters.js +111 -0
- package/dist/export/report-formatters.js.map +7 -0
- package/dist/export/stats-formatters.js +98 -0
- package/dist/export/stats-formatters.js.map +7 -0
- package/dist/export/types.js +2 -14
- package/dist/export/types.js.map +2 -2
- package/dist/export/verify-formatters.js +153 -0
- package/dist/export/verify-formatters.js.map +7 -0
- package/dist/generated/compiled-validators.js +17 -0
- package/dist/generated/compiled-validators.js.map +7 -0
- package/dist/generated/index.js +31 -0
- package/dist/generated/index.js.map +7 -0
- package/dist/generated/layer-registry.js +190 -0
- package/dist/generated/layer-registry.js.map +7 -0
- package/dist/generated/layer-types.js +7 -0
- package/dist/generated/layer-types.js.map +7 -0
- package/dist/generated/node-types.js +1719 -0
- package/dist/generated/node-types.js.map +7 -0
- package/dist/generated/relationship-index.js +14552 -0
- package/dist/generated/relationship-index.js.map +7 -0
- package/dist/import/archimate-importer.js +166 -0
- package/dist/import/archimate-importer.js.map +7 -0
- package/dist/import/index.js +47 -0
- package/dist/import/index.js.map +7 -0
- package/dist/import/openapi-importer.js +143 -0
- package/dist/import/openapi-importer.js.map +7 -0
- package/dist/import/types.js +1 -0
- package/dist/import/types.js.map +7 -0
- package/dist/integrations/base-manager.js +311 -0
- package/dist/integrations/base-manager.js.map +7 -0
- package/dist/integrations/claude-manager.js +360 -0
- package/dist/integrations/claude-manager.js.map +7 -0
- package/dist/integrations/claude_code/agents/dr-advisor.md +253 -0
- package/dist/integrations/claude_code/agents/dr-architect.md +278 -0
- package/dist/integrations/claude_code/agents/dr-extractor.md +286 -0
- package/dist/integrations/claude_code/commands/dr-changeset.md +657 -0
- package/dist/integrations/claude_code/commands/dr-design.md +373 -0
- package/dist/integrations/claude_code/commands/dr-info.md +132 -0
- package/dist/integrations/claude_code/commands/dr-init.md +251 -0
- package/dist/integrations/claude_code/commands/dr-map.md +936 -0
- package/dist/integrations/claude_code/commands/dr-model.md +493 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +814 -0
- package/dist/integrations/claude_code/commands/dr-sync.md +279 -0
- package/dist/integrations/claude_code/commands/dr-validate.md +654 -0
- package/dist/integrations/claude_code/commands/dr-verify.md +559 -0
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +903 -0
- package/dist/integrations/claude_code/docs/QUICK_START.md +271 -0
- package/dist/integrations/claude_code/docs/README.md +347 -0
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +550 -0
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +620 -0
- package/dist/integrations/claude_code/skills/dr_03_security_layer/SKILL.md +642 -0
- package/dist/integrations/claude_code/skills/dr_04_application_layer/SKILL.md +911 -0
- package/dist/integrations/claude_code/skills/dr_05_technology_layer/SKILL.md +836 -0
- package/dist/integrations/claude_code/skills/dr_06_api_layer/SKILL.md +933 -0
- package/dist/integrations/claude_code/skills/dr_07_data_model_layer/SKILL.md +210 -0
- package/dist/integrations/claude_code/skills/dr_08_dataset_layer/SKILL.md +319 -0
- package/dist/integrations/claude_code/skills/dr_09_ux_layer/SKILL.md +319 -0
- package/dist/integrations/claude_code/skills/dr_10_navigation_layer/SKILL.md +318 -0
- package/dist/integrations/claude_code/skills/dr_11_apm_layer/SKILL.md +273 -0
- package/dist/integrations/claude_code/skills/dr_12_testing_layer/SKILL.md +273 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +187 -0
- package/dist/integrations/claude_code/skills/dr_codebase_memory/SKILL.md +590 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +295 -0
- package/dist/integrations/copilot-manager.js +328 -0
- package/dist/integrations/copilot-manager.js.map +7 -0
- package/dist/integrations/github_copilot/agents/dr-advisor.md +243 -0
- package/dist/integrations/github_copilot/agents/dr-architect.md +1567 -0
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +491 -0
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +539 -0
- package/dist/integrations/github_copilot/skills/dr_03_security_layer/SKILL.md +667 -0
- package/dist/integrations/github_copilot/skills/dr_04_application_layer/SKILL.md +670 -0
- package/dist/integrations/github_copilot/skills/dr_05_technology_layer/SKILL.md +660 -0
- package/dist/integrations/github_copilot/skills/dr_06_api_layer/SKILL.md +855 -0
- package/dist/integrations/github_copilot/skills/dr_07_data_model_layer/SKILL.md +171 -0
- package/dist/integrations/github_copilot/skills/dr_08_dataset_layer/SKILL.md +216 -0
- package/dist/integrations/github_copilot/skills/dr_09_ux_layer/SKILL.md +230 -0
- package/dist/integrations/github_copilot/skills/dr_10_navigation_layer/SKILL.md +228 -0
- package/dist/integrations/github_copilot/skills/dr_11_apm_layer/SKILL.md +234 -0
- package/dist/integrations/github_copilot/skills/dr_12_testing_layer/SKILL.md +230 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +187 -0
- package/dist/integrations/hash-utils.js +51 -0
- package/dist/integrations/hash-utils.js.map +7 -0
- package/dist/integrations/types.js +16 -0
- package/dist/integrations/types.js.map +7 -0
- package/dist/reports/model-layer-report-generator.js +278 -0
- package/dist/reports/model-layer-report-generator.js.map +7 -0
- package/dist/reports/model-report-data.js +57 -0
- package/dist/reports/model-report-data.js.map +7 -0
- package/dist/reports/model-report-orchestrator.js +103 -0
- package/dist/reports/model-report-orchestrator.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +595 -0
- package/dist/schemas/bundled/analyzers/manifest.json +9 -0
- package/dist/schemas/bundled/api.json +3411 -0
- package/dist/schemas/bundled/apm.json +2371 -0
- package/dist/schemas/bundled/application.json +1253 -0
- package/dist/schemas/bundled/base.json +1622 -0
- package/dist/schemas/bundled/business.json +1279 -0
- package/dist/schemas/bundled/data-model.json +1298 -0
- package/dist/schemas/bundled/data-store.json +2025 -0
- package/dist/schemas/bundled/manifest.json +93 -0
- package/dist/schemas/bundled/motivation.json +1360 -0
- package/dist/schemas/bundled/navigation.json +1874 -0
- package/dist/schemas/bundled/security.json +3488 -0
- package/dist/schemas/bundled/technology.json +2067 -0
- package/dist/schemas/bundled/testing.json +3120 -0
- package/dist/schemas/bundled/ux.json +3592 -0
- package/dist/server/schemas.js +202 -0
- package/dist/server/schemas.js.map +7 -0
- package/dist/server/server.js +1336 -252
- package/dist/server/server.js.map +3 -3
- package/dist/server/telemetry-middleware.js +22 -15
- package/dist/server/telemetry-middleware.js.map +2 -2
- package/dist/server-entry.js +65 -0
- package/dist/server-entry.js.map +7 -0
- package/dist/telemetry/config.js +89 -0
- package/dist/telemetry/config.js.map +7 -0
- package/dist/telemetry/console-interceptor.js.map +1 -1
- package/dist/telemetry/detect.js +24 -0
- package/dist/telemetry/detect.js.map +7 -0
- package/dist/telemetry/fetch-otlp-exporter.js +112 -0
- package/dist/telemetry/fetch-otlp-exporter.js.map +7 -0
- package/dist/telemetry/index.js +153 -3
- package/dist/telemetry/index.js.map +2 -2
- package/dist/telemetry/resilient-exporter.js +22 -8
- package/dist/telemetry/resilient-exporter.js.map +2 -2
- package/dist/telemetry/resilient-log-exporter.js +17 -8
- package/dist/telemetry/resilient-log-exporter.js.map +2 -2
- package/dist/telemetry/test-instrumentation.js.map +2 -2
- package/dist/types/index.js +84 -0
- package/dist/types/index.js.map +3 -3
- package/dist/types/source-reference.js +1 -0
- package/dist/types/source-reference.js.map +7 -0
- package/dist/utils/chat-logger.js +292 -0
- package/dist/utils/chat-logger.js.map +7 -0
- package/dist/utils/claude-stream.js +8 -0
- package/dist/utils/claude-stream.js.map +7 -0
- package/dist/utils/date-utils.js +7 -0
- package/dist/utils/date-utils.js.map +7 -0
- package/dist/utils/element-utils.js.map +1 -1
- package/dist/utils/errors.js +88 -15
- package/dist/utils/errors.js.map +3 -3
- package/dist/utils/export-python-annotations.js +82 -0
- package/dist/utils/export-python-annotations.js.map +7 -0
- package/dist/utils/file-io.js +7 -2
- package/dist/utils/file-io.js.map +2 -2
- package/dist/utils/file-lock.js +156 -0
- package/dist/utils/file-lock.js.map +7 -0
- package/dist/utils/id-generator.js +18 -0
- package/dist/utils/id-generator.js.map +7 -0
- package/dist/utils/layer-name-formatter.js +16 -0
- package/dist/utils/layer-name-formatter.js.map +7 -0
- package/dist/utils/markdown-anchor.js +7 -0
- package/dist/utils/markdown-anchor.js.map +7 -0
- package/dist/utils/markdown-link.js +12 -0
- package/dist/utils/markdown-link.js.map +7 -0
- package/dist/utils/markdown-table.js +17 -0
- package/dist/utils/markdown-table.js.map +7 -0
- package/dist/utils/mermaid-utils.js +11 -0
- package/dist/utils/mermaid-utils.js.map +7 -0
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/source-reference.js +52 -0
- package/dist/utils/source-reference.js.map +7 -0
- package/dist/utils/spec-installer.js +40 -53
- package/dist/utils/spec-installer.js.map +2 -2
- package/dist/utils/spec-version.js +5 -1
- package/dist/utils/spec-version.js.map +2 -2
- package/dist/utils/string-utils.js +12 -0
- package/dist/utils/string-utils.js.map +7 -0
- package/dist/utils/table-formatting.js +19 -0
- package/dist/utils/table-formatting.js.map +7 -0
- package/dist/validators/index.js +4 -0
- package/dist/validators/index.js.map +2 -2
- package/dist/validators/naming-validator.js +20 -23
- package/dist/validators/naming-validator.js.map +2 -2
- package/dist/validators/reference-validator.js +23 -38
- package/dist/validators/reference-validator.js.map +2 -2
- package/dist/validators/relationship-schema-validator.js +243 -0
- package/dist/validators/relationship-schema-validator.js.map +7 -0
- package/dist/validators/schema-validator.js +168 -68
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/semantic-validator.js +36 -60
- package/dist/validators/semantic-validator.js.map +2 -2
- package/dist/validators/types.js.map +2 -2
- package/dist/validators/validation-formatter.js +159 -0
- package/dist/validators/validation-formatter.js.map +7 -0
- package/dist/validators/validator.js +24 -38
- package/dist/validators/validator.js.map +2 -2
- package/dist/viewer/README.md +139 -0
- package/dist/viewer/assets/main-CY0Xufuj.css +1 -0
- package/dist/viewer/assets/main-DtgErnwF.js +81 -0
- package/dist/viewer/assets/vendor-CxKvDp2x.js +17 -0
- package/dist/viewer/favicon.svg +11 -0
- package/dist/viewer/fonts/inter/Inter-Black.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Bold.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-ExtraBold.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Light.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Medium.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-Regular.woff2 +0 -0
- package/dist/viewer/fonts/inter/Inter-SemiBold.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Medium.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-Regular.woff2 +0 -0
- package/dist/viewer/fonts/jetbrains-mono/JetBrainsMono-SemiBold.woff2 +0 -0
- package/dist/viewer/index.html +15 -0
- package/dist/viewer/manifest.json +99 -0
- package/dist/viewer/version.json +1 -0
- package/package.json +42 -16
- package/PYTHON_CLI_PARITY.md +0 -265
- package/PYTHON_CLI_STRUCTURE_UPDATE.md +0 -246
- package/biome.json +0 -42
- package/bunfig.toml +0 -3
- package/dist/ai/claude-client.js +0 -71
- package/dist/ai/claude-client.js.map +0 -7
- package/dist/ai/context-provider.js +0 -129
- package/dist/ai/context-provider.js.map +0 -7
- package/dist/ai/tools.js +0 -231
- package/dist/ai/tools.js.map +0 -7
- package/dist/commands/migrate.js +0 -36
- package/dist/commands/migrate.js.map +0 -7
- package/dist/commands/project.js +0 -74
- package/dist/commands/project.js.map +0 -7
- package/dist/core/active-changeset.js +0 -52
- package/dist/core/active-changeset.js.map +0 -7
- package/dist/core/projection-engine.js +0 -288
- package/dist/core/projection-engine.js.map +0 -7
- package/dist/schemas/bundled/01-motivation-layer.schema.json +0 -942
- package/dist/schemas/bundled/02-business-layer.schema.json +0 -1293
- package/dist/schemas/bundled/03-security-layer.schema.json +0 -2177
- package/dist/schemas/bundled/04-application-layer.schema.json +0 -1328
- package/dist/schemas/bundled/05-technology-layer.schema.json +0 -1465
- package/dist/schemas/bundled/06-api-layer.schema.json +0 -1743
- package/dist/schemas/bundled/07-data-model-layer.schema.json +0 -1253
- package/dist/schemas/bundled/08-datastore-layer.schema.json +0 -892
- package/dist/schemas/bundled/09-ux-layer.schema.json +0 -1506
- package/dist/schemas/bundled/10-navigation-layer.schema.json +0 -843
- package/dist/schemas/bundled/11-apm-observability-layer.schema.json +0 -1430
- package/dist/schemas/bundled/12-testing-layer.schema.json +0 -1091
- package/dist/schemas/bundled/common/layer-extensions.schema.json +0 -266
- package/dist/schemas/bundled/common/predicates.schema.json +0 -493
- package/dist/schemas/bundled/common/relationships.schema.json +0 -791
- package/dist/schemas/bundled/common/source-references.schema.json +0 -71
- package/dist/schemas/bundled/link-registry.json +0 -1839
- package/dist/schemas/bundled/link-registry.schema.json +0 -339
- package/dist/schemas/bundled/relationship-catalog.json +0 -1302
- package/dist/schemas/bundled/relationship-type.schema.json +0 -221
- package/dist/utils/model-path.js +0 -22
- package/dist/utils/model-path.js.map +0 -7
- package/docs/TEST-INSTRUMENTATION.md +0 -405
- package/docs/telemetry.md +0 -427
- package/test-debug.js +0 -33
- package/test-special.mjs +0 -46
- package/test_runner.ts +0 -7
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/server/server.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Visualization Server\n * HTTP server with WebSocket support for real-time model updates\n */\n\nimport { Hono } from 'hono';\nimport { upgradeWebSocket, websocket } from 'hono/bun';\nimport { cors } from 'hono/cors';\nimport { serve } from 'bun';\nimport { Model } from '../core/model.js';\nimport { Element } from '../core/element.js';\nimport { telemetryMiddleware } from './telemetry-middleware.js';\n\ninterface WSMessage {\n type: 'subscribe' | 'annotate' | 'ping';\n topics?: string[];\n annotation?: {\n elementId: string;\n author: string;\n text: string;\n timestamp: string;\n };\n jsonrpc?: string; // For JSON-RPC 2.0 messages\n method?: string;\n params?: any;\n id?: string | number;\n result?: any;\n error?: {\n code: number;\n message: string;\n data?: any;\n };\n}\n\ninterface ClientAnnotation {\n id: string;\n elementId: string;\n author: string;\n content: string;\n createdAt: string;\n updatedAt?: string;\n tags?: string[];\n}\n\ninterface Changeset {\n metadata: {\n id: string;\n name: string;\n description?: string;\n type: 'feature' | 'bugfix' | 'exploration';\n status: 'active' | 'applied' | 'abandoned';\n created_at: string;\n updated_at?: string;\n workflow?: string;\n summary: {\n elements_added: number;\n elements_updated: number;\n elements_deleted: number;\n };\n };\n changes: {\n version: string;\n changes: Array<{\n timestamp: string;\n operation: 'add' | 'update' | 'delete';\n element_id: string;\n layer: string;\n element_type: string;\n data?: any;\n before?: any;\n after?: any;\n }>;\n };\n}\n\n// Type for WebSocket context from Hono/Bun\ntype HonoWSContext = any;\n\nexport interface VisualizationServerOptions {\n authEnabled?: boolean;\n authToken?: string;\n}\n\n/**\n * Visualization Server class\n */\nexport class VisualizationServer {\n private app: Hono;\n private model: Model;\n private _server?: ReturnType<typeof serve>;\n private clients: Set<HonoWSContext> = new Set();\n private watcher?: any;\n private annotations: Map<string, ClientAnnotation> = new Map(); // annotationId -> annotation\n private annotationsByElement: Map<string, Set<string>> = new Map(); // elementId -> Set<annotationId>\n private changesets: Map<string, Changeset> = new Map(); // changesetId -> changeset\n private authToken: string;\n private authEnabled: boolean = true; // Enabled by default for security\n private activeChatProcesses: Map<string, any> = new Map(); // conversationId -> Bun.spawn process\n private chatConversationCounter: number = 0;\n\n constructor(model: Model, options?: VisualizationServerOptions) {\n this.app = new Hono();\n this.model = model;\n\n // Auth configuration (CLI options override environment variables)\n this.authEnabled = options?.authEnabled ?? (process.env.DR_AUTH_ENABLED !== 'false');\n this.authToken = options?.authToken || process.env.DR_AUTH_TOKEN || this.generateAuthToken();\n\n // Add CORS middleware\n this.app.use('/*', cors());\n\n // Add telemetry middleware to instrument all HTTP requests\n this.app.use('/*', telemetryMiddleware);\n\n // Add authentication middleware (except for health endpoint and root)\n this.app.use('/api/*', async (c, next) => {\n if (!this.authEnabled) {\n return next();\n }\n\n // Check for token in Authorization header or query parameter\n const authHeader = c.req.header('Authorization');\n const queryToken = c.req.query('token');\n\n let providedToken: string | null = null;\n\n if (authHeader && authHeader.startsWith('Bearer ')) {\n providedToken = authHeader.substring(7);\n } else if (queryToken) {\n providedToken = queryToken;\n }\n\n if (!providedToken) {\n return c.json({ error: 'Authentication required. Please provide a valid token.' }, 401);\n }\n\n if (providedToken !== this.authToken) {\n return c.json({ error: 'Invalid authentication token' }, 403);\n }\n\n return next();\n });\n\n this.setupRoutes();\n\n // Log auth token if enabled\n if (this.authEnabled && process.env.VERBOSE) {\n console.log(`[Auth] Authentication enabled. Token: ${this.authToken}`);\n }\n }\n\n /**\n * Generate a random auth token\n */\n private generateAuthToken(): string {\n return `dr-${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;\n }\n\n /**\n * Setup HTTP routes and WebSocket handler\n */\n private setupRoutes(): void {\n // Static viewer HTML at root\n this.app.get('/', (c) => {\n return c.html(this.getViewerHTML());\n });\n\n // Health check endpoint\n this.app.get('/health', (c) => {\n return c.json({\n status: 'ok',\n version: '0.1.0'\n });\n });\n\n // REST API endpoints\n\n // Get full model\n this.app.get('/api/model', async (c) => {\n try {\n const modelData = await this.serializeModel();\n return c.json(modelData);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get specific layer\n this.app.get('/api/layers/:name', async (c) => {\n try {\n const layerName = c.req.param('name');\n const layer = await this.model.getLayer(layerName);\n\n if (!layer) {\n return c.json({ error: 'Layer not found' }, 404);\n }\n\n const elements = layer\n .listElements()\n .map((e) => e.toJSON());\n\n return c.json({\n name: layerName,\n elements,\n elementCount: elements.length,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get specific element\n this.app.get('/api/elements/:id', async (c) => {\n try {\n const elementId = c.req.param('id');\n const element = await this.findElement(elementId);\n\n if (!element) {\n return c.json({ error: 'Element not found' }, 404);\n }\n\n return c.json({\n ...element.toJSON(),\n annotations: this.annotations.get(elementId) || [],\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get JSON Schema specifications\n this.app.get('/api/spec', async (c) => {\n try {\n const schemas = await this.loadSchemas();\n return c.json({\n version: '0.1.0',\n type: 'schema-collection',\n description: 'JSON Schema definitions from dr CLI',\n source: 'dr-cli',\n schemas,\n schemaCount: Object.keys(schemas).length,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get link registry\n this.app.get('/api/link-registry', async (c) => {\n try {\n const linkRegistry = await this.loadLinkRegistry();\n return c.json(linkRegistry);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 404);\n }\n });\n\n // Annotations API (spec-compliant routes)\n\n // Get all annotations (optionally filtered by elementId)\n this.app.get('/api/annotations', (c) => {\n const elementId = c.req.query('elementId');\n\n if (elementId) {\n // Filter by element\n const annotationIds = this.annotationsByElement.get(elementId) || new Set();\n const annotations = Array.from(annotationIds)\n .map(id => this.annotations.get(id))\n .filter((a): a is ClientAnnotation => a !== undefined);\n\n return c.json({ annotations });\n } else {\n // Return all annotations\n const annotations = Array.from(this.annotations.values());\n return c.json({ annotations });\n }\n });\n\n // Create annotation\n this.app.post('/api/annotations', async (c) => {\n try {\n const body = await c.req.json();\n\n // Validate required fields\n if (!body.elementId || !body.content || !body.author) {\n return c.json({\n error: 'Missing required fields: elementId, content, author'\n }, 400);\n }\n\n // Verify element exists\n const element = await this.findElement(body.elementId);\n if (!element) {\n return c.json({ error: 'Element not found' }, 404);\n }\n\n // Create annotation\n const annotation: ClientAnnotation = {\n id: this.generateAnnotationId(),\n elementId: String(body.elementId),\n author: String(body.author),\n content: String(body.content),\n createdAt: new Date().toISOString(),\n tags: body.tags || [],\n };\n\n // Store annotation\n this.annotations.set(annotation.id, annotation);\n if (!this.annotationsByElement.has(annotation.elementId)) {\n this.annotationsByElement.set(annotation.elementId, new Set());\n }\n this.annotationsByElement.get(annotation.elementId)!.add(annotation.id);\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: 'annotation.added',\n annotationId: annotation.id,\n elementId: annotation.elementId,\n timestamp: annotation.createdAt,\n });\n\n return c.json(annotation, 201);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Update annotation\n this.app.put('/api/annotations/:annotationId', async (c) => {\n try {\n const annotationId = c.req.param('annotationId');\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: 'Annotation not found' }, 404);\n }\n\n const body = await c.req.json();\n\n // Update fields\n if (body.content !== undefined) {\n annotation.content = String(body.content);\n }\n if (body.tags !== undefined) {\n annotation.tags = body.tags;\n }\n annotation.updatedAt = new Date().toISOString();\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: 'annotation.updated',\n annotationId: annotation.id,\n timestamp: annotation.updatedAt,\n });\n\n return c.json(annotation);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Delete annotation\n this.app.delete('/api/annotations/:annotationId', async (c) => {\n try {\n const annotationId = c.req.param('annotationId');\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: 'Annotation not found' }, 404);\n }\n\n // Remove from storage\n this.annotations.delete(annotationId);\n const elementAnnotations = this.annotationsByElement.get(annotation.elementId);\n if (elementAnnotations) {\n elementAnnotations.delete(annotationId);\n }\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: 'annotation.deleted',\n annotationId,\n timestamp: new Date().toISOString(),\n });\n\n return c.body(null, 204);\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Changesets API\n\n // Get all changesets\n this.app.get('/api/changesets', (c) => {\n const changesets: Record<string, any> = {};\n\n for (const [id, changeset] of this.changesets) {\n changesets[id] = {\n name: changeset.metadata.name,\n status: changeset.metadata.status,\n type: changeset.metadata.type,\n created_at: changeset.metadata.created_at,\n elements_count: changeset.changes.changes.length,\n };\n }\n\n return c.json({\n version: '1.0.0',\n changesets,\n });\n });\n\n // Get specific changeset\n this.app.get('/api/changesets/:changesetId', (c) => {\n const changesetId = c.req.param('changesetId');\n const changeset = this.changesets.get(changesetId);\n\n if (!changeset) {\n return c.json({ error: 'Changeset not found' }, 404);\n }\n\n return c.json(changeset);\n });\n\n // Deprecated: Get annotations for an element (use /api/annotations?elementId=xyz instead)\n this.app.get('/api/elements/:id/annotations', (c) => {\n const elementId = c.req.param('id');\n const annotationIds = this.annotationsByElement.get(elementId) || new Set();\n const annotations = Array.from(annotationIds)\n .map(id => this.annotations.get(id))\n .filter((a): a is ClientAnnotation => a !== undefined);\n\n return c.json({ elementId, annotations });\n });\n\n // Deprecated: Post annotation to element (use POST /api/annotations instead)\n this.app.post('/api/elements/:id/annotations', async (c) => {\n try {\n const elementId = c.req.param('id');\n\n // Verify element exists\n const element = await this.findElement(elementId);\n if (!element) {\n return c.json({ error: 'Element not found' }, 404);\n }\n\n const body = await c.req.json();\n\n // Support both 'text' and 'content' for backward compatibility\n const content = (body.content || body.text) && String(body.content || body.text).trim();\n if (!content) {\n return c.json({ error: 'Annotation content cannot be empty' }, 400);\n }\n\n const author = body.author ? String(body.author).trim() : 'Anonymous';\n\n const annotation: ClientAnnotation = {\n id: this.generateAnnotationId(),\n elementId,\n author,\n content,\n createdAt: new Date().toISOString(),\n tags: body.tags || [],\n };\n\n // Store annotation\n this.annotations.set(annotation.id, annotation);\n if (!this.annotationsByElement.has(elementId)) {\n this.annotationsByElement.set(elementId, new Set());\n }\n this.annotationsByElement.get(elementId)!.add(annotation.id);\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: 'annotation.added',\n annotationId: annotation.id,\n elementId: annotation.elementId,\n timestamp: annotation.createdAt,\n });\n\n return c.json({ success: true, annotation });\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // WebSocket endpoint\n this.app.get(\n '/ws',\n async (c, next) => {\n // Check WebSocket auth if enabled\n if (this.authEnabled) {\n const queryToken = c.req.query('token');\n\n if (!queryToken) {\n return c.json({ error: 'Authentication required. Provide token as query parameter: ?token=YOUR_TOKEN' }, 401);\n }\n\n if (queryToken !== this.authToken) {\n return c.json({ error: 'Invalid authentication token' }, 403);\n }\n }\n\n return next();\n },\n upgradeWebSocket(() => ({\n onOpen: (_evt, ws) => {\n this.clients.add(ws);\n\n // Send connected message per spec\n ws.send(JSON.stringify({\n type: 'connected',\n version: '0.1.0',\n timestamp: new Date().toISOString(),\n }));\n\n if (process.env.VERBOSE) {\n console.log(`[WebSocket] Client connected (total: ${this.clients.size})`);\n }\n },\n\n onClose: (_evt, ws) => {\n this.clients.delete(ws);\n if (process.env.VERBOSE) {\n console.log(\n `[WebSocket] Client disconnected (total: ${this.clients.size})`\n );\n }\n },\n\n onMessage: async (message, ws) => {\n try {\n // Extract the actual message data\n // In Hono/Bun, message is a MessageEvent with a data property\n const rawData = (message as any).data ?? message;\n\n // Handle different message types (string, Buffer, ArrayBuffer)\n let msgStr: string;\n if (typeof rawData === 'string') {\n msgStr = rawData;\n } else if (rawData instanceof Buffer || rawData instanceof Uint8Array) {\n msgStr = new TextDecoder().decode(rawData);\n } else {\n msgStr = String(rawData);\n }\n const data = JSON.parse(msgStr) as WSMessage;\n\n // Check if it's a JSON-RPC message\n if (data.jsonrpc === '2.0') {\n await this.handleJSONRPCMessage(ws, data);\n } else {\n await this.handleWSMessage(ws, data);\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n if (process.env.DEBUG) {\n console.error(`[WebSocket] Error handling message: ${errorMsg}`);\n }\n ws.send(\n JSON.stringify({\n type: 'error',\n message: errorMsg,\n })\n );\n }\n },\n\n onError: (error) => {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`[WebSocket] Error: ${message}`);\n },\n }))\n );\n }\n\n /**\n * Serialize model to JSON for client consumption\n */\n private async serializeModel(): Promise<any> {\n const layers: any = {};\n\n for (const [name, layer] of this.model.layers) {\n const elements = layer\n .listElements()\n .map((e) => {\n const annotationIds = this.annotationsByElement.get(e.id) || new Set();\n const annotations = Array.from(annotationIds)\n .map(id => this.annotations.get(id))\n .filter((a): a is ClientAnnotation => a !== undefined);\n\n return {\n ...e.toJSON(),\n annotations,\n };\n });\n\n layers[name] = {\n name,\n elements,\n elementCount: elements.length,\n };\n }\n\n return {\n manifest: this.model.manifest.toJSON(),\n layers,\n totalElements: Array.from(this.model.layers.values()).reduce(\n (sum, layer) => sum + layer.listElements().length,\n 0\n ),\n };\n }\n\n /**\n * Generate unique annotation ID\n */\n private generateAnnotationId(): string {\n return `ann-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n }\n\n /**\n * Broadcast message to all connected WebSocket clients\n */\n private async broadcastMessage(message: any): Promise<void> {\n const messageStr = JSON.stringify(message);\n\n let failureCount = 0;\n for (const client of this.clients) {\n try {\n client.send(messageStr);\n } catch (error) {\n failureCount++;\n const msg = error instanceof Error ? error.message : String(error);\n if (process.env.VERBOSE) {\n console.warn(`[WebSocket] Failed to send message to client: ${msg}`);\n }\n }\n }\n\n if (failureCount > 0 && process.env.VERBOSE) {\n console.warn(`[WebSocket] Failed to send message to ${failureCount}/${this.clients.size} clients`);\n }\n }\n\n /**\n * Find an element across all layers\n */\n private async findElement(id: string): Promise<Element | null> {\n for (const layer of this.model.layers.values()) {\n const element = layer.getElement(id);\n if (element) return element;\n }\n return null;\n }\n\n /**\n * Handle WebSocket messages\n */\n private async handleWSMessage(ws: HonoWSContext, data: WSMessage): Promise<void> {\n switch (data.type) {\n case 'subscribe':\n // Send subscribed confirmation\n const topics = data.topics || ['model', 'annotations'];\n ws.send(JSON.stringify({\n type: 'subscribed',\n topics,\n timestamp: new Date().toISOString(),\n }));\n\n // Send initial model state\n const modelData = await this.serializeModel();\n ws.send(\n JSON.stringify({\n type: 'model',\n data: modelData,\n timestamp: new Date().toISOString(),\n })\n );\n break;\n\n case 'ping':\n // Respond with pong\n ws.send(JSON.stringify({\n type: 'pong',\n timestamp: new Date().toISOString(),\n }));\n break;\n\n case 'annotate':\n if (data.annotation) {\n await this.broadcastMessage({\n type: 'annotation',\n data: data.annotation,\n });\n }\n break;\n\n default:\n ws.send(\n JSON.stringify({\n type: 'error',\n message: `Unknown message type: ${data.type}`,\n })\n );\n }\n }\n\n /**\n * Handle JSON-RPC 2.0 messages for chat functionality\n */\n private async handleJSONRPCMessage(ws: HonoWSContext, data: WSMessage): Promise<void> {\n const { method, params, id } = data;\n\n // Helper to send JSON-RPC response\n const sendResponse = (result: any) => {\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n result,\n id,\n }));\n };\n\n // Helper to send JSON-RPC error\n const sendError = (code: number, message: string, data?: any) => {\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n error: { code, message, data },\n id,\n }));\n };\n\n try {\n switch (method) {\n case 'chat.status':\n // Check if Claude Code CLI is available\n const claudeAvailable = await this.checkClaudeCodeCLI();\n sendResponse({\n sdk_available: claudeAvailable,\n sdk_version: claudeAvailable ? 'claude-code-cli' : null,\n error_message: claudeAvailable ? null : 'Claude Code CLI not found in PATH'\n });\n break;\n\n case 'chat.send':\n // Validate params\n if (!params || !params.message) {\n sendError(-32602, 'Message cannot be empty');\n return;\n }\n\n // Check if Claude Code is available\n if (!await this.checkClaudeCodeCLI()) {\n sendError(-32001, 'Claude Code CLI not available. Install Claude Code to enable chat.');\n return;\n }\n\n // Generate conversation ID\n const conversationId = `conv-${++this.chatConversationCounter}-${Date.now()}`;\n\n // Launch Claude Code with DR chat agent\n await this.launchClaudeCodeChat(ws, conversationId, params.message, id);\n break;\n\n case 'chat.cancel':\n // Find and cancel any active conversation\n let cancelled = false;\n let cancelledConvId = null;\n\n for (const [convId, process] of this.activeChatProcesses) {\n try {\n process.kill();\n this.activeChatProcesses.delete(convId);\n cancelled = true;\n cancelledConvId = convId;\n break;\n } catch (error) {\n // Process already terminated\n }\n }\n\n sendResponse({\n cancelled,\n conversation_id: cancelledConvId\n });\n break;\n\n default:\n sendError(-32601, `Method not found: ${method}`);\n }\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n sendError(-32603, 'Internal error', errorMsg);\n }\n }\n\n /**\n * Check if Claude Code CLI is available\n */\n private async checkClaudeCodeCLI(): Promise<boolean> {\n try {\n const result = Bun.spawnSync({\n cmd: ['which', 'claude'],\n stdout: 'pipe',\n stderr: 'pipe',\n });\n return result.exitCode === 0;\n } catch {\n return false;\n }\n }\n\n /**\n * Launch Claude Code CLI with dr-architect agent and stream responses\n */\n private async launchClaudeCodeChat(\n ws: HonoWSContext,\n conversationId: string,\n message: string,\n requestId: string | number | undefined\n ): Promise<void> {\n try {\n // Launch claude with dr-architect agent for comprehensive DR expertise\n const proc = Bun.spawn({\n cmd: [\n 'claude',\n '--agent', 'dr-architect',\n '--print',\n '--dangerously-skip-permissions',\n '--verbose',\n '--output-format', 'stream-json',\n ],\n cwd: this.model.rootPath,\n stdin: 'pipe',\n stdout: 'pipe',\n stderr: 'pipe',\n });\n\n // Store active process\n this.activeChatProcesses.set(conversationId, proc);\n\n // Send user message via stdin (like Python prototype)\n proc.stdin?.write(new TextEncoder().encode(message));\n proc.stdin?.end();\n\n // Stream stdout (JSON events)\n const stdoutReader = proc.stdout.getReader();\n const decoder = new TextDecoder();\n\n const streamOutput = async () => {\n let accumulatedText = '';\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await stdoutReader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n\n // Process complete lines (JSON events)\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n try {\n const event = JSON.parse(line);\n\n if (event.type === 'assistant') {\n // Extract content blocks from assistant message\n const content = event.message?.content || [];\n for (const block of content) {\n if (block.type === 'text') {\n accumulatedText += block.text;\n // Send text chunk notification\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n method: 'chat.response.chunk',\n params: {\n conversation_id: conversationId,\n content: block.text,\n is_final: false,\n timestamp: new Date().toISOString(),\n },\n }));\n } else if (block.type === 'tool_use') {\n // Send tool invocation notification\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n method: 'chat.tool.invoke',\n params: {\n conversation_id: conversationId,\n tool_name: block.name,\n tool_input: block.input,\n timestamp: new Date().toISOString(),\n },\n }));\n }\n }\n } else if (event.type === 'result') {\n // Tool result\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n method: 'chat.tool.result',\n params: {\n conversation_id: conversationId,\n result: event.result,\n timestamp: new Date().toISOString(),\n },\n }));\n }\n } catch {\n // Non-JSON line, send as raw text chunk\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n method: 'chat.response.chunk',\n params: {\n conversation_id: conversationId,\n content: line + '\\n',\n is_final: false,\n timestamp: new Date().toISOString(),\n },\n }));\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n method: 'chat.response.chunk',\n params: {\n conversation_id: conversationId,\n content: buffer,\n is_final: true,\n timestamp: new Date().toISOString(),\n },\n }));\n }\n\n // Wait for process to complete\n const exitCode = await proc.exited;\n\n // Clean up\n this.activeChatProcesses.delete(conversationId);\n\n // Send completion response\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n result: {\n conversation_id: conversationId,\n status: exitCode === 0 ? 'complete' : 'error',\n exit_code: exitCode,\n full_response: accumulatedText,\n timestamp: new Date().toISOString(),\n },\n id: requestId,\n }));\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: `Chat failed: ${errorMsg}`,\n },\n id: requestId,\n }));\n\n this.activeChatProcesses.delete(conversationId);\n\n try {\n proc.kill();\n } catch {\n // Process may already be terminated\n }\n }\n };\n\n // Start streaming in background\n streamOutput();\n } catch (error) {\n const errorMsg = error instanceof Error ? error.message : String(error);\n\n ws.send(JSON.stringify({\n jsonrpc: '2.0',\n error: {\n code: -32603,\n message: `Failed to launch Claude Code: ${errorMsg}`,\n },\n id: requestId,\n }));\n }\n }\n\n\n /**\n * Setup file watcher for model changes\n */\n private setupFileWatcher(): void {\n const drPath = `${this.model.rootPath}/.dr`;\n\n // Use Bun's global watch API (cast to any due to type definitions)\n const bunWatch = (globalThis as any).Bun?.watch;\n if (!bunWatch) {\n console.warn('[Watcher] Bun.watch not available, file watching disabled');\n return;\n }\n\n this.watcher = bunWatch(drPath, {\n recursive: true,\n onChange: async (_event: string, path: string) => {\n if (process.env.VERBOSE) {\n console.log(`[Watcher] File changed: ${path}`);\n }\n\n try {\n // Reload model\n this.model = await Model.load(this.model.rootPath, { lazyLoad: false });\n\n // Broadcast update to all clients\n const modelData = await this.serializeModel();\n const message = JSON.stringify({\n type: 'model.updated',\n data: modelData,\n timestamp: new Date().toISOString(),\n });\n\n for (const client of this.clients) {\n try {\n client.send(message);\n } catch (error) {\n if (process.env.DEBUG) {\n const msg = error instanceof Error ? error.message : String(error);\n console.debug(`[Watcher] Failed to send update: ${msg}`);\n }\n }\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n console.error(`[Watcher] Failed to reload model: ${message}`);\n }\n },\n });\n }\n\n /**\n * Load all JSON schemas from bundled directory\n */\n private async loadSchemas(): Promise<Record<string, any>> {\n const schemasPath = new URL('../schemas/bundled/', import.meta.url).pathname;\n const schemas: Record<string, any> = {};\n\n try {\n const fs = await import('fs/promises');\n const path = await import('path');\n\n const fileList = await fs.readdir(schemasPath);\n\n for (const file of fileList) {\n if (file.endsWith('.schema.json') || file.endsWith('.json')) {\n const filePath = path.join(schemasPath, file);\n const content = await fs.readFile(filePath, 'utf-8');\n schemas[file] = JSON.parse(content);\n }\n }\n\n return schemas;\n } catch (error) {\n console.error('Failed to load schemas:', error);\n throw new Error('Failed to load schema files');\n }\n }\n\n /**\n * Load link registry from bundled directory\n */\n private async loadLinkRegistry(): Promise<any> {\n const linkRegistryPath = new URL('../schemas/bundled/link-registry.json', import.meta.url).pathname;\n\n try {\n const fs = await import('fs/promises');\n const content = await fs.readFile(linkRegistryPath, 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n console.error('Failed to load link registry:', error);\n throw new Error('Link registry not found');\n }\n }\n\n /**\n * Get viewer HTML\n */\n private getViewerHTML(): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Documentation Robotics Viewer</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n background: #f5f5f5;\n color: #333;\n }\n\n header {\n background: white;\n border-bottom: 1px solid #e0e0e0;\n padding: 20px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .header-content {\n max-width: 1400px;\n margin: 0 auto;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n h1 {\n font-size: 24px;\n font-weight: 600;\n }\n\n .status-badge {\n display: inline-block;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .status-badge.connected {\n background: #4caf50;\n color: white;\n }\n\n .status-badge.disconnected {\n background: #f44336;\n color: white;\n }\n\n .container {\n max-width: 1400px;\n margin: 0 auto;\n padding: 20px;\n }\n\n .content {\n display: grid;\n grid-template-columns: 300px 1fr;\n gap: 20px;\n height: calc(100vh - 100px);\n }\n\n .sidebar {\n background: white;\n border-radius: 4px;\n overflow-y: auto;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n padding: 16px;\n }\n\n .main-panel {\n background: white;\n border-radius: 4px;\n overflow-y: auto;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n padding: 20px;\n }\n\n .layer-group {\n margin-bottom: 16px;\n }\n\n .layer-header {\n font-weight: 600;\n padding: 8px 0;\n border-bottom: 2px solid #e0e0e0;\n margin-bottom: 8px;\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n }\n\n .layer-header:hover {\n color: #2196F3;\n }\n\n .layer-header::before {\n content: '\u25B6';\n margin-right: 8px;\n display: inline-block;\n transition: transform 0.2s;\n }\n\n .layer-header.expanded::before {\n transform: rotate(90deg);\n }\n\n .layer-elements {\n margin-left: 16px;\n display: none;\n }\n\n .layer-elements.expanded {\n display: block;\n }\n\n .element-item {\n padding: 8px;\n margin-bottom: 4px;\n background: #f9f9f9;\n border-left: 3px solid #e0e0e0;\n cursor: pointer;\n border-radius: 2px;\n transition: all 0.2s;\n font-size: 13px;\n }\n\n .element-item:hover {\n background: #f0f0f0;\n border-left-color: #2196F3;\n }\n\n .element-item.selected {\n background: #e3f2fd;\n border-left-color: #2196F3;\n font-weight: 500;\n }\n\n .element-details {\n min-height: 300px;\n }\n\n .detail-header {\n border-bottom: 2px solid #e0e0e0;\n padding-bottom: 12px;\n margin-bottom: 16px;\n }\n\n .detail-header h2 {\n font-size: 18px;\n margin-bottom: 4px;\n }\n\n .detail-header .element-type {\n font-size: 12px;\n color: #999;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .detail-section {\n margin-bottom: 16px;\n }\n\n .detail-section h3 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n margin-bottom: 8px;\n letter-spacing: 0.5px;\n }\n\n .detail-value {\n font-size: 14px;\n line-height: 1.6;\n color: #333;\n word-break: break-word;\n }\n\n .annotations-list {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 8px;\n }\n\n .annotation-item {\n padding: 8px;\n margin-bottom: 8px;\n background: #fffde7;\n border-left: 3px solid #fbc02d;\n border-radius: 2px;\n font-size: 12px;\n }\n\n .annotation-item .author {\n font-weight: 600;\n color: #666;\n }\n\n .annotation-item .text {\n margin: 4px 0 0 0;\n color: #333;\n }\n\n .annotation-item .timestamp {\n font-size: 11px;\n color: #999;\n margin-top: 4px;\n }\n\n .annotation-form {\n display: grid;\n gap: 8px;\n margin-top: 12px;\n }\n\n .annotation-form input,\n .annotation-form textarea {\n padding: 8px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-family: inherit;\n font-size: 12px;\n }\n\n .annotation-form textarea {\n resize: vertical;\n min-height: 60px;\n }\n\n .annotation-form button {\n padding: 8px 12px;\n background: #2196F3;\n color: white;\n border: none;\n border-radius: 4px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .annotation-form button:hover {\n background: #1976D2;\n }\n\n .loading {\n text-align: center;\n padding: 40px;\n color: #999;\n }\n\n .loading::after {\n content: '';\n display: inline-block;\n width: 16px;\n height: 16px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2196F3;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-left: 8px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .stats {\n font-size: 12px;\n color: #999;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid #e0e0e0;\n }\n </style>\n</head>\n<body>\n <header>\n <div class=\"header-content\">\n <div>\n <h1>\uD83D\uDCDA Documentation Robotics Viewer</h1>\n </div>\n <div class=\"status-badge connected\" id=\"status\">\u25CF Connected</div>\n </div>\n </header>\n\n <div class=\"container\">\n <div class=\"content\">\n <div class=\"sidebar\">\n <div class=\"stats\" id=\"stats\">Loading...</div>\n <div id=\"model-tree\">\n <div class=\"loading\">Loading model...</div>\n </div>\n </div>\n\n <div class=\"main-panel\">\n <div id=\"element-details\">\n <div class=\"loading\">Select an element to view details</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n let currentModel = null;\n let selectedElementId = null;\n\n const statusEl = document.getElementById('status');\n const modelTreeEl = document.getElementById('model-tree');\n const detailsEl = document.getElementById('element-details');\n const statsEl = document.getElementById('stats');\n\n /**\n * Escape HTML special characters to prevent XSS\n * @param {string} text - Text to escape\n * @returns {string} HTML-escaped text\n */\n function escapeHtml(text) {\n if (!text) return '';\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n const ws = new WebSocket(\\`ws://\\${window.location.host}/ws\\`);\n\n ws.addEventListener('open', () => {\n console.log('WebSocket connected');\n updateStatus(true);\n ws.send(JSON.stringify({ type: 'subscribe' }));\n });\n\n ws.addEventListener('message', (event) => {\n try {\n const message = JSON.parse(event.data);\n\n if (message.type === 'model' || message.type === 'model-update') {\n currentModel = message.data;\n renderModel();\n } else if (message.type === 'annotation') {\n // Update annotations for the element\n renderSelectedElement();\n } else if (message.type === 'error') {\n console.error('WebSocket error:', message.message);\n }\n } catch (error) {\n console.error('Failed to parse message:', error);\n }\n });\n\n ws.addEventListener('close', () => {\n console.log('WebSocket disconnected');\n updateStatus(false);\n });\n\n ws.addEventListener('error', (error) => {\n console.error('WebSocket error:', error);\n updateStatus(false);\n });\n\n function updateStatus(connected) {\n if (connected) {\n statusEl.className = 'status-badge connected';\n statusEl.textContent = '\u25CF Connected';\n } else {\n statusEl.className = 'status-badge disconnected';\n statusEl.textContent = '\u25CF Disconnected';\n }\n }\n\n function renderModel() {\n if (!currentModel) return;\n\n // Update stats\n const layerCount = Object.keys(currentModel.layers).length;\n const elementCount = currentModel.totalElements || 0;\n statsEl.textContent = \\`\\${layerCount} layers \u00B7 \\${elementCount} elements\\`;\n\n // Render layers and elements\n modelTreeEl.innerHTML = '';\n const layers = Object.entries(currentModel.layers)\n .sort((a, b) => a[0].localeCompare(b[0]));\n\n for (const [layerName, layerData] of layers) {\n const layerDiv = document.createElement('div');\n layerDiv.className = 'layer-group';\n\n const headerDiv = document.createElement('div');\n headerDiv.className = 'layer-header';\n headerDiv.textContent = layerName;\n headerDiv.addEventListener('click', () => {\n headerDiv.classList.toggle('expanded');\n elementsDiv.classList.toggle('expanded');\n });\n\n const elementsDiv = document.createElement('div');\n elementsDiv.className = 'layer-elements';\n\n for (const element of layerData.elements) {\n const elemDiv = document.createElement('div');\n elemDiv.className = 'element-item';\n if (element.id === selectedElementId) {\n elemDiv.classList.add('selected');\n }\n elemDiv.innerHTML = \\`\\${escapeHtml(element.id)}<br><span style=\"font-size: 11px; color: #999;\">\\${escapeHtml(element.name)}</span>\\`;\n elemDiv.addEventListener('click', () => {\n selectedElementId = element.id;\n document.querySelectorAll('.element-item').forEach(el => el.classList.remove('selected'));\n elemDiv.classList.add('selected');\n renderSelectedElement();\n });\n\n elementsDiv.appendChild(elemDiv);\n }\n\n layerDiv.appendChild(headerDiv);\n layerDiv.appendChild(elementsDiv);\n modelTreeEl.appendChild(layerDiv);\n }\n }\n\n function renderSelectedElement() {\n if (!selectedElementId || !currentModel) {\n detailsEl.innerHTML = '<div class=\"loading\">Select an element to view details</div>';\n return;\n }\n\n // Find element in model\n let element = null;\n for (const layer of Object.values(currentModel.layers)) {\n element = layer.elements.find(e => e.id === selectedElementId);\n if (element) break;\n }\n\n if (!element) {\n detailsEl.innerHTML = '<div class=\"loading\">Element not found</div>';\n return;\n }\n\n let html = \\`\n <div class=\"detail-header\">\n <div class=\"element-type\">\\${escapeHtml(element.type)}</div>\n <h2>\\${escapeHtml(element.name)}</h2>\n <div style=\"font-size: 12px; color: #666; margin-top: 4px;\"><code>\\${escapeHtml(element.id)}</code></div>\n </div>\n \\`;\n\n if (element.description) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Description</h3>\n <div class=\"detail-value\">\\${escapeHtml(element.description)}</div>\n </div>\n \\`;\n }\n\n if (element.properties && Object.keys(element.properties).length > 0) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Properties</h3>\n <div class=\"detail-value\"><pre>\\${escapeHtml(JSON.stringify(element.properties, null, 2))}</pre></div>\n </div>\n \\`;\n }\n\n if (element.annotations && element.annotations.length > 0) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Annotations</h3>\n <div class=\"annotations-list\">\n \\${element.annotations.map(ann => \\`\n <div class=\"annotation-item\">\n <div class=\"author\">\\${escapeHtml(ann.author)}</div>\n <div class=\"text\">\\${escapeHtml(ann.text)}</div>\n <div class=\"timestamp\">\\${new Date(ann.timestamp).toLocaleString()}</div>\n </div>\n \\`).join('')}\n </div>\n </div>\n \\`;\n }\n\n html += \\`\n <div class=\"detail-section\">\n <h3>Add Annotation</h3>\n <form class=\"annotation-form\" onsubmit=\"addAnnotation(event, '\\${escapeHtml(element.id)}')\">\n <input type=\"text\" placeholder=\"Author name\" id=\"ann-author\" required>\n <textarea placeholder=\"Annotation text\" id=\"ann-text\" required></textarea>\n <button type=\"submit\">Add Annotation</button>\n </form>\n </div>\n \\`;\n\n detailsEl.innerHTML = html;\n }\n\n function addAnnotation(event, elementId) {\n event.preventDefault();\n\n const author = document.getElementById('ann-author').value;\n const text = document.getElementById('ann-text').value;\n\n if (!author || !text) return;\n\n // Send to server\n fetch(\\`/api/elements/\\${elementId}/annotations\\`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ author, text })\n })\n .then(r => r.json())\n .then(data => {\n if (data.success) {\n document.getElementById('ann-author').value = '';\n document.getElementById('ann-text').value = '';\n }\n })\n .catch(err => console.error('Failed to add annotation:', err));\n }\n </script>\n</body>\n</html>`;\n }\n\n /**\n * Start the server\n */\n async start(port: number = 8080): Promise<void> {\n this.setupFileWatcher();\n\n this._server = serve({\n port,\n fetch: this.app.fetch,\n websocket, // Add WebSocket handler from hono/bun\n });\n\n console.log(\n `\u2713 Visualization server running at http://localhost:${port}`\n );\n }\n\n /**\n * Get the current auth token\n */\n getAuthToken(): string {\n return this.authToken;\n }\n\n /**\n * Check if authentication is enabled\n */\n isAuthEnabled(): boolean {\n return this.authEnabled;\n }\n\n /**\n * Stop the server\n */\n stop(): void {\n // Close all WebSocket connections\n for (const client of this.clients) {\n try {\n client.close();\n } catch (error) {\n // Ignore errors on close\n }\n }\n this.clients.clear();\n\n // Stop file watcher\n if (this.watcher) {\n this.watcher.close?.();\n }\n\n // Stop the server\n if (this._server) {\n this._server.stop();\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["data", "process"]
|
|
4
|
+
"sourcesContent": ["/**\n * Visualization Server\n * HTTP server with WebSocket support for real-time model updates\n */\n\nimport { OpenAPIHono, createRoute, z } from \"@hono/zod-openapi\";\nimport { cors } from \"hono/cors\";\nimport { swaggerUI } from \"@hono/swagger-ui\";\nimport { randomBytes } from \"crypto\";\nimport { createRequire } from \"module\";\nimport { fileURLToPath } from \"url\";\nimport path from \"path\";\n\n// hono/bun imports are conditional - they're only available in Bun runtime\n// For Node.js/tsx environments (e.g., generating OpenAPI spec), these are lazily imported\n// These are conditionally loaded in loadBunAdapters() and remain undefined in non-Bun runtimes\n// Bun-specific WebSocket upgrade middleware from hono/bun\n// Only available in Bun runtime; undefined in Node.js/tsx\nlet upgradeWebSocket: any;\n// Bun-specific WebSocket middleware from hono/bun\n// Only available in Bun runtime; undefined in Node.js/tsx\nlet websocket: any;\n\n/**\n * NOTE: Module-scoped WebSocket adapter state\n *\n * upgradeWebSocket and websocket are declared as module-level let variables and mutated\n * in the constructor via loadBunAdapters(). This creates intentional coupling between\n * VisualizationServer instances: they all share the same adapter references.\n *\n * DESIGN RATIONALE:\n * - These adapters are loaded from the Bun runtime at module-load time and are immutable\n * after initialization. The Bun runtime itself is global state.\n * - In practice, all VisualizationServer instances will call loadBunAdapters() with the same\n * inputs and will get the same results (or the same errors).\n * - Creating separate instance-level copies would add unnecessary memory overhead without\n * providing benefits, since the adapters cannot be meaningfully different per instance.\n * - The coupling is acceptable because:\n * 1. These are runtime environment adapters, not request-specific state\n * 2. They are immutable after initialization (set once in first constructor call)\n * 3. Tests and production code all use a single VisualizationServer instance per process\n *\n * ALTERNATIVE APPROACHES (rejected):\n * - Instance properties: Would duplicate adapter references unnecessarily\n * - Lazy initialization in methods: Would cause per-request overhead\n * - Singleton pattern: Would require additional infrastructure without clear benefits\n *\n * If future requirements demand multiple independent VisualizationServer instances\n * with different WebSocket configurations, this design can be revisited.\n */\nconst require = createRequire(import.meta.url);\nimport { Model } from \"../core/model.js\";\nimport { Element } from \"../core/element.js\";\nimport { Changeset as StagedChangeset } from \"../core/changeset.js\";\nimport { StagedChangesetStorage } from \"../core/staged-changeset-storage.js\";\nimport { telemetryMiddleware } from \"./telemetry-middleware.js\";\nimport { BaseChatClient } from \"../coding-agents/base-chat-client.js\";\nimport { ClaudeCodeClient } from \"../coding-agents/claude-code-client.js\";\nimport { CopilotClient } from \"../coding-agents/copilot-client.js\";\nimport { detectAvailableClients, selectChatClient } from \"../coding-agents/chat-utils.js\";\nimport { getErrorMessage } from \"../utils/errors.js\";\nimport {\n AnnotationCreateSchema,\n AnnotationUpdateSchema,\n AnnotationReplyCreateSchema,\n LayerNameSchema,\n IdSchema,\n AnnotationFilterSchema,\n ErrorResponseSchema,\n HealthResponseSchema,\n AnnotationSchema,\n AnnotationReplySchema,\n AnnotationsListSchema,\n ChangesetsListSchema,\n ChangesetDetailSchema,\n ModelResponseSchema,\n LayerResponseSchema,\n SpecResponseSchema,\n AnnotationRepliesSchema,\n WSMessageSchema,\n SimpleWSMessageSchema,\n JSONRPCRequestSchema,\n JSONRPCResponseSchema,\n} from \"./schemas.js\";\n\n/**\n * JSON-RPC 2.0 Error Codes\n * Subset of JSON-RPC 2.0 error codes used in this implementation.\n * Standard JSON-RPC 2.0 also defines PARSE_ERROR (-32700) and INVALID_REQUEST (-32600)\n * for protocol-level errors not applicable to WebSocket message handling.\n */\nconst JSONRPC_ERRORS = {\n // Standard JSON-RPC errors\n INVALID_PARAMS: -32602, // Invalid method parameters\n METHOD_NOT_FOUND: -32601, // Method does not exist\n INTERNAL_ERROR: -32603, // Internal server error\n\n // Custom application errors\n NO_CLIENT_AVAILABLE: -32001, // No chat client available\n} as const;\n\n/**\n * Type Casting Note\n *\n * Type casts (`as any`) are used in this file due to @hono/zod-openapi v1.2.1\n * type inference limitations with async middleware handlers. The middleware\n * properly validates and transforms request/response data at runtime via Zod,\n * so the type assertions are safe despite bypassing TypeScript's type checker.\n *\n * TODO: Remove these casts after @hono/zod-openapi improves async handler typing\n * Upstream tracking: https://github.com/honojs/hono/issues/3524\n * Target version: @hono/zod-openapi v2.0.0+ (when async handler typing is improved)\n *\n * Affected endpoints:\n * - Annotation creation (POST /api/annotations)\n * - Annotation retrieval (GET /api/annotations/:id)\n * - Annotation updates (PATCH /api/annotations/:id)\n * - Annotation replacement (PUT /api/annotations/:id)\n * - Annotation deletion (DELETE /api/annotations/:id)\n * - Layer retrieval (GET /api/layers/:layerName)\n * - Schema retrieval (GET /api/spec)\n */\n\n// WebSocket message types derived from Zod schemas for type safety and runtime validation\ntype SimpleWSMessage = z.infer<typeof SimpleWSMessageSchema>;\ntype JSONRPCRequest = z.infer<typeof JSONRPCRequestSchema>;\ntype JSONRPCResponse = z.infer<typeof JSONRPCResponseSchema>;\ntype WSMessage = z.infer<typeof WSMessageSchema>;\n\ntype AnnotationReply = z.infer<typeof AnnotationReplySchema>;\n\n// Derive ClientAnnotation type from AnnotationSchema with proper serialization\ntype ClientAnnotation = z.infer<typeof AnnotationSchema>;\n\n// Type for WebSocket context from Hono/Bun\n// Defines the interface for WebSocket operations within the Hono/Bun environment\ninterface HonoWSContext {\n send(source: string | ArrayBuffer | Uint8Array | ArrayBufferView, options?: { compress?: boolean }): void;\n close(code?: number, reason?: string): void;\n}\n\nexport interface VisualizationServerOptions {\n authEnabled?: boolean;\n authToken?: string;\n withDanger?: boolean;\n viewerPath?: string;\n}\n\n/**\n * Visualization Server class\n */\nexport class VisualizationServer {\n private app: OpenAPIHono;\n private model: Model;\n private _server?: any; // Bun.serve return type, loaded dynamically\n private clients: Set<HonoWSContext> = new Set();\n private watcher?: any;\n private annotations: Map<string, ClientAnnotation> = new Map(); // annotationId -> annotation\n private annotationsByElement: Map<string, Set<string>> = new Map(); // elementId -> Set<annotationId>\n private replies: Map<string, AnnotationReply[]> = new Map(); // annotationId -> replies[]\n private changesets: Map<string, StagedChangeset> = new Map(); // changesetId -> changeset\n private changesetStorage?: StagedChangesetStorage;\n private authToken: string;\n private authEnabled: boolean = true;\n private withDanger: boolean = false; // Danger mode disabled by default\n private viewerPath?: string; // Optional custom viewer path\n private activeChatProcesses: Map<string, any> = new Map(); // conversationId -> Bun.spawn process\n private chatConversationCounter: number = 0;\n private selectedChatClient?: BaseChatClient; // Selected chat client for server\n private chatInitializationError?: Error; // Store initialization error for status endpoint\n\n /**\n * Valid spec node IDs per layer\n * Dynamically loaded from compiled spec (spec/dist/{layer}.json at module load time)\n * Used to validate and resolve viewer spec node IDs\n * Prevents silent drift when node types are added to spec/schemas/nodes/\n */\n private static readonly VALID_SPEC_NODE_IDS: Record<string, string[]> = VisualizationServer.loadValidSpecNodeIds();\n\n /**\n * Load valid spec node IDs dynamically from bundled spec files\n * Extracts node_types arrays from each layer's compiled schema file\n * This ensures the list is always in sync with the spec build output\n * If any file cannot be loaded, falls back to a minimal safe set\n */\n private static loadValidSpecNodeIds(): Record<string, string[]> {\n const result: Record<string, string[]> = {};\n const requiredLayers = [\n \"motivation\",\n \"business\",\n \"security\",\n \"application\",\n \"technology\",\n \"api\",\n \"data-model\",\n \"data-store\",\n \"ux\",\n \"navigation\",\n \"apm\",\n \"testing\",\n ];\n\n // Get the directory of this file to build absolute path to bundled schemas\n const __dirname = path.dirname(fileURLToPath(import.meta.url));\n const bundledDir = path.join(__dirname, \"..\", \"schemas\", \"bundled\");\n\n for (const layer of requiredLayers) {\n try {\n // Build absolute path to the layer's compiled spec file\n const specFilePath = path.join(bundledDir, `${layer}.json`);\n\n // Use require() to synchronously load bundled spec file\n // These JSON files are built by \"npm run build\" from spec source\n const spec = require(specFilePath) as {\n layer?: { node_types?: string[] };\n specVersion?: string;\n };\n\n // Extract node_types array from layer object in the compiled spec\n const nodeTypes = spec.layer?.node_types;\n if (Array.isArray(nodeTypes) && nodeTypes.length > 0) {\n result[layer] = nodeTypes;\n } else {\n console.warn(\n `[SPEC-LOAD-001] Layer ${layer} has no node_types in spec file`\n );\n result[layer] = [];\n }\n } catch (error) {\n console.warn(\n `[SPEC-LOAD-002] Failed to load spec node IDs for layer ${layer}: ${getErrorMessage(error)}`\n );\n // Use empty array as fallback - will be caught by usage code\n result[layer] = [];\n }\n }\n\n return result;\n }\n\n /**\n * Fallback spec node IDs used when type extraction from elementId fails\n * These are used when no valid spec node ID can be extracted from the elementId\n * or found through other resolution strategies\n */\n private static readonly LAYER_FALLBACK: Record<string, string> = {\n security: \"business.businessprocess\",\n technology: \"application.applicationservice\",\n api: \"business.businessprocess\",\n \"data-model\": \"data-store.database\",\n \"data-store\": \"data-store.database\",\n ux: \"application.applicationcomponent\",\n navigation: \"application.applicationcomponent\",\n apm: \"application.applicationservice\",\n testing: \"business.businessprocess\",\n };\n\n /**\n * All valid spec node IDs across all layers (cached Set for O(1) lookup)\n */\n private static readonly ALL_VALID_IDS: Set<string> = (() => {\n const ids = new Set<string>();\n Object.values(VisualizationServer.VALID_SPEC_NODE_IDS).forEach((layerIds) => {\n layerIds.forEach((id) => ids.add(id));\n });\n return ids;\n })();\n\n constructor(model: Model, options?: VisualizationServerOptions) {\n this.app = new OpenAPIHono();\n this.model = model;\n\n // Load Bun-specific imports if available (for WebSocket support in Bun runtime)\n this.loadBunAdapters();\n\n // Auth configuration (CLI options override environment variables)\n this.authEnabled = options?.authEnabled ?? process.env.DR_AUTH_ENABLED !== \"false\";\n this.authToken = options?.authToken || process.env.DR_AUTH_TOKEN || this.generateAuthToken();\n this.withDanger = options?.withDanger || false;\n // Use explicit viewerPath if provided, otherwise fall back to the bundled viewer\n this.viewerPath = options?.viewerPath ||\n new URL(\"../viewer\", import.meta.url).pathname;\n\n // Validate cross-field configuration\n if (!this.authEnabled && options?.authToken) {\n console.warn(\n \"[VisualizationServer] Warning: authToken provided but authEnabled is false. \" +\n \"The token will be generated but not used for authentication. \" +\n \"Set authEnabled: true to enable authentication.\"\n );\n }\n\n // Add CORS middleware\n this.app.use(\"/*\", cors());\n\n // Add telemetry middleware to instrument all HTTP requests\n this.app.use(\"/*\", telemetryMiddleware);\n\n // Add authentication middleware (except for health endpoint and root)\n this.app.use(\"/api/*\", async (c, next) => {\n if (!this.authEnabled) {\n return next();\n }\n\n // Check for token in Authorization header or query parameter\n const authHeader = c.req.header(\"Authorization\");\n const queryToken = c.req.query(\"token\");\n\n let providedToken: string | null = null;\n\n if (authHeader && authHeader.startsWith(\"Bearer \")) {\n providedToken = authHeader.substring(7);\n } else if (queryToken) {\n providedToken = queryToken;\n }\n\n if (!providedToken) {\n return c.json({ error: \"Authentication required. Please provide a valid token.\" }, 401);\n }\n\n if (providedToken !== this.authToken) {\n return c.json({ error: \"Invalid authentication token\" }, 403);\n }\n\n return next();\n });\n\n this.setupRoutes();\n\n // Log auth token if enabled\n if (this.authEnabled && process.env.VERBOSE) {\n console.log(`[Auth] Authentication enabled. Token: ${this.authToken}`);\n }\n\n // Initialize chat clients asynchronously\n this.initializeChatClients().catch((error) => {\n this.chatInitializationError = error instanceof Error ? error : new Error(String(error));\n console.error(\"[Chat] Failed to initialize chat clients:\", error);\n });\n }\n\n /**\n * Load Bun-specific WebSocket adapters if available\n * This is called in the constructor to support WebSocket functionality in Bun runtime\n * In Node.js/tsx environments (e.g., generating OpenAPI spec), these imports are skipped\n */\n private loadBunAdapters(): void {\n // Check if we're running in Bun runtime by checking for Bun global\n if (typeof (global as any).Bun !== \"undefined\") {\n try {\n // Use dynamic import for Bun-specific adapters\n // This is safe to call only in Bun runtime\n const honoModule = require(\"hono/bun\") as any;\n upgradeWebSocket = honoModule.upgradeWebSocket;\n websocket = honoModule.websocket;\n } catch (error) {\n // Log error details for production debugging (not just VERBOSE mode)\n const message = error instanceof Error ? error.message : String(error);\n console.warn(\n `[Server] WARNING: Could not load WebSocket adapters from hono/bun: ${message}. ` +\n `WebSocket endpoints (/ws) will not be available. ` +\n `Please ensure 'hono' is installed and the Bun runtime is properly configured.`\n );\n if (process.env.VERBOSE && error instanceof Error && error.stack) {\n console.debug(\"[Server] Stack trace:\", error.stack);\n }\n }\n }\n }\n\n /**\n * Detect and initialize available chat clients\n */\n private async initializeChatClients(): Promise<void> {\n const clients = await detectAvailableClients();\n\n // Select the chat client from available clients\n // Note: Chat client preference is no longer persisted in manifest\n this.selectedChatClient = selectChatClient(clients, null);\n\n if (this.selectedChatClient && process.env.VERBOSE) {\n console.log(`[Chat] Using chat client: ${this.selectedChatClient.getClientName()}`);\n } else if (clients.length === 0 && process.env.VERBOSE) {\n console.log(\"[Chat] No chat clients available\");\n }\n }\n\n /**\n * Generate a random auth token\n */\n private generateAuthToken(): string {\n return `dr-${randomBytes(24).toString('base64url')}`;\n }\n\n /**\n * Shared annotation update logic for PUT/PATCH handlers\n * Performs partial updates with atomic operations\n */\n private async updateAnnotation(\n annotationId: string,\n body: z.infer<typeof AnnotationUpdateSchema>\n ): Promise<ClientAnnotation | null> {\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return null;\n }\n\n // Create new annotation object with updates (atomic update, no direct mutation)\n // This prevents race conditions if the annotation is read during WebSocket broadcast\n const updatedAnnotation: ClientAnnotation = {\n ...annotation,\n content: body.content ?? annotation.content,\n tags: body.tags ?? annotation.tags,\n resolved: body.resolved ?? annotation.resolved,\n updatedAt: new Date().toISOString(),\n };\n\n // Store the updated annotation atomically\n this.annotations.set(annotationId, updatedAnnotation);\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: \"annotation.updated\",\n annotationId: updatedAnnotation.id,\n timestamp: updatedAnnotation.updatedAt,\n });\n\n return updatedAnnotation;\n }\n\n /**\n * Setup HTTP routes and WebSocket handler\n */\n private setupRoutes(): void {\n // Register bearerAuth security scheme for protected /api/* routes\n this.app.openAPIRegistry.registerComponent('securitySchemes', 'bearerAuth', {\n type: 'http',\n scheme: 'bearer',\n description: 'API token (shown at server startup, pass as Authorization: Bearer <token>)',\n });\n\n // Static viewer HTML at root\n this.app.get(\"/\", (c) => {\n if (this.viewerPath) {\n // Serve custom viewer index.html\n return this.serveCustomViewer(\"index.html\");\n }\n return c.html(this.getViewerHTML());\n });\n\n // Serve static files from custom viewer path if provided\n // Note: This catch-all route must skip API/WS routes by passing to next handler\n if (this.viewerPath) {\n this.app.get(\"/*\", async (c, next) => {\n const requestPath = c.req.path;\n if (process.env.VERBOSE) console.log(`[ROUTE] Catch-all matched: ${requestPath}`);\n\n // Skip API routes and WebSocket - let them be handled by their specific routes\n if (requestPath.startsWith(\"/api/\") || requestPath === \"/ws\" || requestPath === \"/health\" || requestPath === \"/api-spec.yaml\" || requestPath === \"/api-docs\") {\n if (process.env.VERBOSE) console.log(`[ROUTE] Catch-all delegating to next handler for: ${requestPath}`);\n return next(); // Pass to next handler instead of returning 404\n }\n\n if (process.env.VERBOSE) console.log(`[ROUTE] Catch-all serving custom viewer file: ${requestPath}`);\n return this.serveCustomViewer(requestPath.substring(1));\n });\n }\n\n // Health check endpoint\n const healthRoute = createRoute({\n method: 'get',\n path: '/health',\n tags: ['Health'],\n summary: 'Health check',\n description: 'Check if the server is running and healthy',\n responses: {\n 200: {\n description: 'Server is healthy',\n content: {\n 'application/json': {\n schema: HealthResponseSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(healthRoute, (c) => {\n const response: any = {\n status: this.chatInitializationError ? \"degraded\" : \"ok\",\n version: \"0.1.0\",\n };\n\n // Include warnings if any services failed to initialize\n if (this.chatInitializationError) {\n response.warnings = [\n `Chat service unavailable: ${getErrorMessage(this.chatInitializationError)}`\n ];\n }\n\n return c.json(response);\n });\n\n // Get full model\n const getModelRoute = createRoute({\n method: 'get',\n path: '/api/model',\n tags: ['Model'],\n summary: 'Get full model',\n description: 'Retrieve the complete architecture model across all layers',\n security: [{ bearerAuth: [] }],\n responses: {\n 200: {\n description: 'Model data retrieved successfully',\n content: {\n 'application/json': {\n schema: ModelResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(getModelRoute, async (c) => {\n if (process.env.VERBOSE) console.log(`[ROUTE] /api/model handler called`);\n try {\n if (process.env.VERBOSE) console.log(`[ROUTE] /api/model serializing model...`);\n const modelData = await this.serializeModel();\n if (process.env.VERBOSE)\n console.log(\n `[ROUTE] /api/model returning ${Object.keys(modelData.layers || {}).length} layers`\n );\n return c.json(modelData);\n } catch (error) {\n const message = getErrorMessage(error);\n console.error(`[ROUTE] /api/model error: ${message}`);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get specific layer\n const getLayerRoute = createRoute({\n method: 'get',\n path: '/api/layers/:layerName',\n tags: ['Model'],\n summary: 'Get layer',\n description: 'Retrieve a specific layer with all its elements',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ layerName: LayerNameSchema }),\n },\n responses: {\n 200: {\n description: 'Layer retrieved successfully',\n content: {\n 'application/json': {\n schema: LayerResponseSchema,\n },\n },\n },\n 404: {\n description: 'Layer not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(getLayerRoute, (async (c: any) => {\n try {\n const { layerName } = c.req.valid(\"param\");\n const layer = await this.model.getLayer(layerName);\n\n if (!layer) {\n return c.json({ error: \"Layer not found\" }, 404);\n }\n\n const elements = layer.listElements().map((e) => e.toJSON());\n\n return c.json({\n name: layerName,\n elements,\n elementCount: elements.length,\n });\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n }) as any);\n\n // Get JSON Schema specifications\n const getSpecRoute = createRoute({\n method: 'get',\n path: '/api/spec',\n tags: ['Schema'],\n summary: 'Get JSON schemas',\n description: 'Retrieve all JSON Schema definitions used by the system',\n security: [{ bearerAuth: [] }],\n responses: {\n 200: {\n description: 'Schemas retrieved successfully',\n content: {\n 'application/json': {\n schema: SpecResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(getSpecRoute, (async (c: any) => {\n try {\n const schemas = await this.loadSchemas();\n return c.json({\n version: \"0.1.0\",\n type: \"schema-collection\",\n description: \"JSON Schema definitions from dr CLI\",\n source: \"dr-cli\",\n schemas,\n schemaCount: Object.keys(schemas).length,\n });\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n }) as any);\n\n // Annotations API (spec-compliant routes)\n\n // Get all annotations (optionally filtered by elementId)\n const getAnnotationsRoute = createRoute({\n method: 'get',\n path: '/api/annotations',\n tags: ['Annotations'],\n summary: 'Get annotations',\n description: 'Retrieve all annotations, optionally filtered by element',\n security: [{ bearerAuth: [] }],\n request: {\n query: AnnotationFilterSchema,\n },\n responses: {\n 200: {\n description: 'Annotations retrieved successfully',\n content: {\n 'application/json': {\n schema: AnnotationsListSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(getAnnotationsRoute, (c) => {\n const query = c.req.valid(\"query\");\n\n const elementId = query.elementId;\n\n if (elementId) {\n // Filter by element\n const annotationIds = this.annotationsByElement.get(elementId) || new Set();\n const annotations = Array.from(annotationIds)\n .map((id) => this.annotations.get(id))\n .filter((a): a is ClientAnnotation => a !== undefined);\n\n return c.json({ annotations });\n } else {\n // Return all annotations\n const annotations = Array.from(this.annotations.values());\n return c.json({ annotations });\n }\n });\n\n // Create annotation\n const createAnnotationRoute = createRoute({\n method: 'post',\n path: '/api/annotations',\n tags: ['Annotations'],\n summary: 'Create annotation',\n description: 'Create a new annotation on a model element',\n security: [{ bearerAuth: [] }],\n request: {\n body: {\n content: {\n 'application/json': {\n schema: AnnotationCreateSchema,\n },\n },\n },\n },\n responses: {\n 201: {\n description: 'Annotation created successfully',\n content: {\n 'application/json': {\n schema: AnnotationSchema,\n },\n },\n },\n 400: {\n description: 'Invalid request body',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 404: {\n description: 'Element not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(createAnnotationRoute, async (c) => {\n try {\n const body = c.req.valid(\"json\");\n\n // Verify element exists\n const element = await this.findElement(body.elementId);\n if (!element) {\n return c.json({ error: \"Element not found\" }, 404);\n }\n\n // Create annotation\n const annotation: ClientAnnotation = {\n id: this.generateAnnotationId(),\n elementId: body.elementId,\n author: body.author,\n content: body.content,\n createdAt: new Date().toISOString(),\n tags: body.tags,\n resolved: false, // Default to unresolved\n };\n\n // Store annotation\n this.annotations.set(annotation.id, annotation);\n if (!this.annotationsByElement.has(annotation.elementId)) {\n this.annotationsByElement.set(annotation.elementId, new Set());\n }\n this.annotationsByElement.get(annotation.elementId)!.add(annotation.id);\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: \"annotation.added\",\n annotationId: annotation.id,\n elementId: annotation.elementId,\n timestamp: annotation.createdAt,\n });\n\n return c.json(annotation, 201);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Get individual annotation by ID\n const getAnnotationRoute = createRoute({\n method: 'get',\n path: '/api/annotations/:annotationId',\n tags: ['Annotations'],\n summary: 'Get annotation by ID',\n description: 'Retrieve a specific annotation by its ID',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n },\n responses: {\n 200: {\n description: 'Annotation retrieved successfully',\n content: {\n 'application/json': {\n schema: AnnotationSchema,\n },\n },\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(getAnnotationRoute, (async (c: any) => {\n try {\n const { annotationId } = c.req.valid(\"param\");\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n return c.json(annotation);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n }) as any);\n\n // Update annotation (PUT - partial update for backwards compatibility)\n // NOTE: DESIGN RATIONALE - Non-standard PUT behavior:\n // This endpoint performs PARTIAL updates (idempotent) like PATCH, not full replacement\n // like standard HTTP PUT. This is a known deviation from HTTP semantics, chosen for\n // backwards compatibility with existing API consumers. New code should prefer PATCH\n // for partial updates. If full replacement semantics are needed in future, a new\n // endpoint should be created rather than changing this one's behavior.\n const putAnnotationRoute = createRoute({\n method: 'put',\n path: '/api/annotations/:annotationId',\n tags: ['Annotations'],\n summary: 'Update annotation (partial)',\n description: 'Update an existing annotation with partial fields (NOTE: partial updates, use PATCH for recommended behavior)',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n body: {\n content: {\n 'application/json': {\n schema: AnnotationUpdateSchema,\n },\n },\n },\n },\n responses: {\n 200: {\n description: 'Annotation updated successfully',\n content: {\n 'application/json': {\n schema: AnnotationSchema,\n },\n },\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(putAnnotationRoute, (async (c: any) => {\n try {\n const { annotationId } = c.req.valid(\"param\");\n const body = c.req.valid(\"json\");\n\n const updatedAnnotation = await this.updateAnnotation(annotationId, body);\n\n if (!updatedAnnotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n return c.json(updatedAnnotation);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n }) as any);\n\n // Update annotation (PATCH)\n const patchAnnotationRoute = createRoute({\n method: 'patch',\n path: '/api/annotations/:annotationId',\n tags: ['Annotations'],\n summary: 'Partially update annotation',\n description: 'Partially update an existing annotation (recommended over PUT)',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n body: {\n content: {\n 'application/json': {\n schema: AnnotationUpdateSchema,\n },\n },\n },\n },\n responses: {\n 200: {\n description: 'Annotation updated successfully',\n content: {\n 'application/json': {\n schema: AnnotationSchema,\n },\n },\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(patchAnnotationRoute, (async (c: any) => {\n try {\n const { annotationId } = c.req.valid(\"param\");\n const body = c.req.valid(\"json\");\n\n const updatedAnnotation = await this.updateAnnotation(annotationId, body);\n\n if (!updatedAnnotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n return c.json(updatedAnnotation);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n }) as any);\n\n // Delete annotation\n const deleteAnnotationRoute = createRoute({\n method: 'delete',\n path: '/api/annotations/:annotationId',\n tags: ['Annotations'],\n summary: 'Delete annotation',\n description: 'Delete an existing annotation',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n },\n responses: {\n 204: {\n description: 'Annotation deleted successfully',\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(deleteAnnotationRoute, async (c) => {\n try {\n const { annotationId } = c.req.valid(\"param\");\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n // Remove from storage\n this.annotations.delete(annotationId);\n this.replies.delete(annotationId); // Clean up replies\n const elementAnnotations = this.annotationsByElement.get(annotation.elementId);\n if (elementAnnotations) {\n elementAnnotations.delete(annotationId);\n }\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: \"annotation.deleted\",\n annotationId,\n timestamp: new Date().toISOString(),\n });\n\n return c.body(null, 204);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // GET annotation replies\n const getAnnotationRepliesRoute = createRoute({\n method: 'get',\n path: '/api/annotations/:annotationId/replies',\n tags: ['Annotations'],\n summary: 'Get annotation replies',\n description: 'Retrieve all replies to an annotation',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n },\n responses: {\n 200: {\n description: 'Replies retrieved successfully',\n content: {\n 'application/json': {\n schema: AnnotationRepliesSchema,\n },\n },\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(getAnnotationRepliesRoute, ((c: any) => {\n const { annotationId } = c.req.valid(\"param\");\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n const replies = this.replies.get(annotationId) || [];\n return c.json({ replies });\n }) as any);\n\n // POST annotation reply\n const createAnnotationReplyRoute = createRoute({\n method: 'post',\n path: '/api/annotations/:annotationId/replies',\n tags: ['Annotations'],\n summary: 'Create annotation reply',\n description: 'Add a reply to an annotation',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ annotationId: IdSchema }),\n body: {\n content: {\n 'application/json': {\n schema: AnnotationReplyCreateSchema,\n },\n },\n },\n },\n responses: {\n 201: {\n description: 'Reply created successfully',\n content: {\n 'application/json': {\n schema: AnnotationReplySchema,\n },\n },\n },\n 404: {\n description: 'Annotation not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n 500: {\n description: 'Server error',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(createAnnotationReplyRoute, async (c) => {\n try {\n const { annotationId } = c.req.valid(\"param\");\n const annotation = this.annotations.get(annotationId);\n\n if (!annotation) {\n return c.json({ error: \"Annotation not found\" }, 404);\n }\n\n const body = c.req.valid(\"json\");\n\n const reply: AnnotationReply = {\n id: this.generateReplyId(),\n author: body.author,\n content: body.content,\n createdAt: new Date().toISOString(),\n };\n\n // Store reply\n if (!this.replies.has(annotationId)) {\n this.replies.set(annotationId, []);\n }\n this.replies.get(annotationId)!.push(reply);\n\n // Broadcast to all clients\n await this.broadcastMessage({\n type: \"annotation.reply.added\",\n annotationId,\n replyId: reply.id,\n timestamp: reply.createdAt,\n });\n\n return c.json(reply, 201);\n } catch (error) {\n const message = getErrorMessage(error);\n return c.json({ error: message }, 500);\n }\n });\n\n // Changesets API\n\n // Get all changesets\n const getChangesetsRoute = createRoute({\n method: 'get',\n path: '/api/changesets',\n tags: ['Changesets'],\n summary: 'Get changesets',\n description: 'Retrieve a list of all changesets',\n security: [{ bearerAuth: [] }],\n responses: {\n 200: {\n description: 'Changesets retrieved successfully',\n content: {\n 'application/json': {\n schema: ChangesetsListSchema,\n },\n },\n },\n },\n });\n\n this.app.openapi(getChangesetsRoute, (c) => {\n const changesets: Record<string, any> = {};\n\n for (const [id, changeset] of this.changesets) {\n changesets[id] = {\n name: changeset.name,\n status: changeset.status,\n created: changeset.created,\n changes_count: changeset.changes.length,\n };\n }\n\n return c.json({\n version: \"1.0.0\",\n changesets,\n });\n });\n\n // Get specific changeset\n const getChangesetRoute = createRoute({\n method: 'get',\n path: '/api/changesets/:changesetId',\n tags: ['Changesets'],\n summary: 'Get changeset',\n description: 'Retrieve a specific changeset with all its changes',\n security: [{ bearerAuth: [] }],\n request: {\n params: z.object({ changesetId: IdSchema }),\n },\n responses: {\n 200: {\n description: 'Changeset retrieved successfully',\n content: {\n 'application/json': {\n schema: ChangesetDetailSchema,\n },\n },\n },\n 404: {\n description: 'Changeset not found',\n content: {\n 'application/json': {\n schema: ErrorResponseSchema,\n },\n },\n },\n },\n });\n\n // \u2705 Type cast required: See Type Casting Note at top of file\n this.app.openapi(getChangesetRoute, ((c: any) => {\n const { changesetId } = c.req.valid(\"param\");\n const changeset = this.changesets.get(changesetId);\n\n if (!changeset) {\n return c.json({ error: \"Changeset not found\" }, 404);\n }\n\n return c.json({\n id: changeset.id,\n name: changeset.name,\n description: changeset.description,\n status: changeset.status,\n created: changeset.created,\n modified: changeset.modified,\n baseSnapshot: changeset.baseSnapshot,\n stats: changeset.stats,\n changes: changeset.changes,\n });\n }) as any);\n\n // OpenAPI documentation endpoint\n // Note: @hono/zod-openapi always generates 3.1.0, so we specify it here\n // but the version is normalized to 3.0.3 in the generate-openapi.ts script\n this.app.doc('/api-spec.yaml', {\n openapi: '3.1.0',\n info: {\n title: 'Documentation Robotics Visualization Server API',\n version: '0.1.0',\n description: 'API specification for the DR CLI visualization server',\n contact: {\n name: 'Documentation Robotics',\n url: 'https://github.com/tinkermonkey/documentation_robotics',\n },\n license: {\n name: 'ISC',\n },\n },\n servers: [\n { url: 'http://localhost:8080', description: 'Local development server' },\n ],\n tags: [\n { name: 'Health', description: 'Server health and status' },\n { name: 'Schema', description: 'JSON Schema specifications' },\n { name: 'Model', description: 'Architecture model data' },\n { name: 'Changesets', description: 'Model changesets and history' },\n { name: 'Annotations', description: 'User annotations on model elements' },\n { name: 'WebSocket', description: 'Real-time updates via WebSocket' },\n ],\n });\n\n // Swagger UI for interactive API exploration\n this.app.get('/api-docs', swaggerUI({ url: '/api-spec.yaml' }));\n\n // WebSocket endpoint\n // Only register if WebSocket adapter is available (Bun runtime)\n if (upgradeWebSocket) {\n this.app.get(\n \"/ws\",\n async (c, next) => {\n // Check WebSocket auth if enabled\n if (this.authEnabled) {\n // Accept token from multiple sources to support diverse client environments:\n // 1. Query parameter: ?token=<token>\n // Why: Works in all browsers. Simple and universally compatible.\n // 2. Sec-WebSocket-Protocol header: token, <actual-token>\n // Why: Browsers cannot set custom HTTP headers during WebSocket handshake\n // for security reasons. The WebSocket spec allows subprotocol negotiation\n // as an alternative auth mechanism. Format: [\"token\", \"YOUR_TOKEN\"]\n // 3. Authorization header: Bearer <token>\n // Why: Standard OAuth2/JWT pattern for non-browser clients (Node.js, cURL, etc)\n // that can set arbitrary headers. Browsers cannot use this for WebSocket due\n // to HTTP header restrictions during handshake.\n\n const queryToken = c.req.query(\"token\");\n\n // Check Sec-WebSocket-Protocol header (browser-compatible method)\n // Format: \"token, <actual-token>\" or \"token,<actual-token>\"\n const wsProtocol = c.req.header(\"Sec-WebSocket-Protocol\");\n let protocolToken: string | null = null;\n if (wsProtocol) {\n const protocols = wsProtocol.split(\",\").map((p) => p.trim());\n // Look for protocol pair: ['token', '<actual-token>']\n const tokenIndex = protocols.indexOf(\"token\");\n if (tokenIndex !== -1 && protocols.length > tokenIndex + 1) {\n protocolToken = protocols[tokenIndex + 1];\n }\n }\n\n // Check Authorization header (non-browser clients)\n const authHeader = c.req.header(\"Authorization\");\n const bearerToken = authHeader?.startsWith(\"Bearer \") ? authHeader.substring(7) : null;\n\n const token = queryToken || protocolToken || bearerToken;\n\n if (!token) {\n return c.json(\n {\n error:\n 'Authentication required. Provide token via: 1) Query param (?token=YOUR_TOKEN), 2) Sec-WebSocket-Protocol header (browser: new WebSocket(url, [\"token\", \"YOUR_TOKEN\"])), or 3) Authorization header (Bearer YOUR_TOKEN)',\n },\n 401\n );\n }\n\n if (token !== this.authToken) {\n return c.json({ error: \"Invalid authentication token\" }, 403);\n }\n\n // If token came from Sec-WebSocket-Protocol, we need to respond with the protocol\n // This is required by the WebSocket spec for subprotocol negotiation\n if (protocolToken) {\n c.header(\"Sec-WebSocket-Protocol\", \"token\");\n }\n }\n\n return next();\n },\n upgradeWebSocket(() => ({\n onOpen: async (_evt: any, ws: HonoWSContext) => {\n // Telemetry: Track WebSocket connection\n await this.recordWebSocketEvent(\"ws.connection.open\", {\n \"ws.client_count\": this.clients.size + 1,\n });\n\n this.clients.add(ws);\n\n // Send connected message per spec\n ws.send(\n JSON.stringify({\n type: \"connected\",\n version: \"0.1.0\",\n timestamp: new Date().toISOString(),\n })\n );\n\n if (process.env.VERBOSE) {\n console.log(`[WebSocket] Client connected (total: ${this.clients.size})`);\n }\n },\n\n onClose: async (_evt: any, ws: HonoWSContext) => {\n // Telemetry: Track WebSocket disconnection\n await this.recordWebSocketEvent(\"ws.connection.close\", {\n \"ws.client_count\": this.clients.size - 1,\n });\n\n this.clients.delete(ws);\n if (process.env.VERBOSE) {\n console.log(`[WebSocket] Client disconnected (total: ${this.clients.size})`);\n }\n },\n\n onMessage: async (message: any, ws: HonoWSContext) => {\n const messageStartTime = Date.now();\n let messageType = \"unknown\";\n\n try {\n // Extract the actual message data\n // In Hono/Bun, message is a MessageEvent with a data property\n const rawData = (message as any).data ?? message;\n\n // Handle different message types (string, Buffer, ArrayBuffer)\n let msgStr: string;\n if (typeof rawData === \"string\") {\n msgStr = rawData;\n } else if (rawData instanceof Buffer || rawData instanceof Uint8Array) {\n msgStr = new TextDecoder().decode(rawData);\n } else {\n msgStr = String(rawData);\n }\n const rawMessage = JSON.parse(msgStr);\n\n // Runtime validation of WebSocket message format\n const validationResult = WSMessageSchema.safeParse(rawMessage);\n if (!validationResult.success) {\n const errorMsg = validationResult.error.issues.map((e) => `${e.path.join('.')}: ${e.message}`).join('; ');\n // Always log validation errors for production debugging\n console.error(\"Invalid WebSocket message format:\", errorMsg);\n ws.send(JSON.stringify({ error: \"Invalid message format\", details: errorMsg }));\n return;\n }\n\n const data = validationResult.data as WSMessage;\n\n // Type-safe discriminated union handling\n if ('jsonrpc' in data && data.jsonrpc === '2.0') {\n // Check if it's a request (has method) or response (has result/error)\n if ('method' in data && typeof data.method === 'string') {\n messageType = (data as JSONRPCRequest).method;\n } else {\n messageType = (data as JSONRPCResponse).id?.toString() || \"jsonrpc-response\";\n }\n } else if ('type' in data) {\n messageType = (data as SimpleWSMessage).type;\n } else {\n messageType = \"unknown\";\n }\n\n // Telemetry: Track message processing\n await this.recordWebSocketEvent(\"ws.message.received\", {\n \"ws.message.type\": messageType,\n \"ws.message.size_bytes\": msgStr.length,\n });\n\n // Check if it's a JSON-RPC message\n if ('jsonrpc' in data && data.jsonrpc === \"2.0\") {\n await this.handleJSONRPCMessage(ws, data);\n } else {\n await this.handleWSMessage(ws, data);\n }\n\n // Telemetry: Track successful message processing\n const durationMs = Date.now() - messageStartTime;\n await this.recordWebSocketEvent(\"ws.message.processed\", {\n \"ws.message.type\": messageType,\n \"ws.message.duration_ms\": durationMs,\n \"ws.message.status\": \"success\",\n });\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n const durationMs = Date.now() - messageStartTime;\n\n // Telemetry: Track message processing error\n await this.recordWebSocketEvent(\"ws.message.error\", {\n \"ws.message.type\": messageType,\n \"ws.message.duration_ms\": durationMs,\n \"ws.message.status\": \"error\",\n \"error.message\": errorMsg,\n });\n\n // Always log message handling errors for operational visibility\n console.warn(\n `[WebSocket] Error handling ${messageType} message (${durationMs}ms): ${errorMsg}`\n );\n ws.send(\n JSON.stringify({\n type: \"error\",\n message: errorMsg,\n })\n );\n }\n },\n\n onError: async (error: any) => {\n const message = getErrorMessage(error);\n\n // Telemetry: Track WebSocket error\n await this.recordWebSocketEvent(\"ws.error\", {\n \"error.type\": error instanceof Error ? error.constructor.name : \"unknown\",\n \"error.message\": message,\n });\n\n // Always log WebSocket errors with full context including stack trace for debugging\n console.error(\"[WebSocket] Error:\", error);\n },\n }))\n );\n }\n }\n\n /**\n * Get OpenAPI specification document\n * Provides a stable public API for OpenAPI spec generation.\n *\n * BEHAVIOR: Returns an OpenAPI spec with the version specified in `config.openapi`.\n * The returned document will have whatever OpenAPI version was passed in the config.\n *\n * NOTE: Hono's getOpenAPI31Document() passes the config directly through and\n * uses the specified version in the output. There is no forced version conversion.\n */\n public getOpenAPIDocument(config: {\n openapi: string;\n info: {\n title: string;\n version: string;\n description?: string;\n contact?: { name?: string; url?: string };\n license?: { name: string };\n };\n servers?: Array<{ url: string; description?: string }>;\n }): any {\n return this.app.getOpenAPI31Document(config);\n }\n\n /**\n * Serialize model to graph format for client consumption.\n *\n * Returns { nodes, links } where:\n * - nodes: flat array of all elements across all layers\n * - links: intra-layer relationships (carry layer_id) and cross-layer\n * references (carry source_layer_id + target_layer_id)\n *\n * Links are deduplicated: if the same relationship appears on multiple\n * elements (e.g. stored on both source and target), only one entry is kept.\n */\n private async serializeModel(): Promise<any> {\n const nodes: any[] = [];\n const linksMap = new Map<string, any>();\n\n // Build element-id \u2192 layer_id lookup so cross-layer reference links can\n // carry accurate source_layer_id / target_layer_id even when the\n // referenced element lives in a different layer.\n const elementLayerMap = new Map<string, string>();\n for (const [layerName, layer] of this.model.layers) {\n for (const e of layer.listElements()) {\n const layerId = e.layer_id || layerName;\n if (e.id) elementLayerMap.set(e.id, layerId);\n // Also index by path so references that use dot-separated slugs resolve correctly.\n // For new-format elements: e.id is a UUID, e.path is the dot-separated slug.\n // For old-format elements: e.id === e.path (both the slug), so no duplicate work.\n if (e.path && e.path !== e.id) elementLayerMap.set(e.path, layerId);\n }\n }\n\n for (const [layerName, layer] of this.model.layers) {\n for (const e of layer.listElements()) {\n const layerId = e.layer_id || layerName;\n\n // Resolve a viewer-compatible spec_node_id so every element renders\n // correctly in the v0.3.0 viewer regardless of which layer it belongs to.\n const viewerSpecNodeId = this.resolveViewerSpecNodeId(e.id, layerId, e.type, e.spec_node_id);\n\n // Node \u2014 all model fields for complete client-side access\n const node: any = {\n id: e.id,\n spec_node_id: viewerSpecNodeId,\n type: e.type,\n layer_id: layerId,\n name: e.name,\n };\n if (e.description) node.description = e.description;\n if (e.attributes && Object.keys(e.attributes).length > 0) node.attributes = e.attributes;\n if (e.source_reference) node.source_reference = e.source_reference;\n if (e.metadata) node.metadata = e.metadata;\n\n nodes.push(node);\n\n // Intra-layer links from relationships\n for (const rel of e.relationships) {\n const linkId = `rel:${rel.source}:${rel.target}:${rel.predicate}`;\n if (!linksMap.has(linkId)) {\n linksMap.set(linkId, {\n id: linkId,\n source: rel.source,\n target: rel.target,\n type: rel.predicate,\n layer_id: layerId,\n });\n }\n }\n\n // Cross-layer links from references\n for (const ref of e.references) {\n const linkId = `ref:${ref.source}:${ref.target}:${ref.type}`;\n if (!linksMap.has(linkId)) {\n linksMap.set(linkId, {\n id: linkId,\n source: ref.source,\n target: ref.target,\n type: ref.type,\n source_layer_id: elementLayerMap.get(ref.source) || layerId,\n target_layer_id: elementLayerMap.get(ref.target) || 'unknown',\n });\n }\n }\n }\n }\n\n // Central relationships.yaml links (added via `dr relationship add`)\n // These are NOT attached to individual elements, so the per-element loop\n // above misses them entirely. Emit them here using the same linksMap so\n // duplicates are naturally de-duplicated.\n for (const rel of this.model.relationships.getAll()) {\n const linkId = `rel:${rel.source}:${rel.target}:${rel.predicate}`;\n if (!linksMap.has(linkId)) {\n const layerId = rel.layer || elementLayerMap.get(rel.source);\n if (!layerId) continue; // Skip \u2014 source element not found and no layer field set\n const link: any = {\n id: linkId,\n source: rel.source,\n target: rel.target,\n type: rel.predicate,\n };\n // Cross-layer relationships set targetLayer; intra-layer relationships do not.\n // Both paths are reachable through normal CLI usage via `dr relationship add`.\n if (rel.targetLayer) {\n link.source_layer_id = layerId;\n link.target_layer_id = rel.targetLayer;\n } else {\n link.layer_id = layerId;\n }\n linksMap.set(linkId, link);\n }\n }\n\n return {\n nodes,\n links: Array.from(linksMap.values()),\n };\n }\n\n /**\n * Resolve viewer spec node ID with fallback logic\n * Handles multiple resolution strategies:\n * 1. Valid pass-through: Return specNodeId if it's already a valid spec node ID\n * 2. Dot-format extraction: Extract type from elementId in \"layer.type.name\" format and construct candidate\n * 3. Hyphen-format extraction: Extract type from elementId in \"layer-type-name\" format and construct candidate\n * 4. Type-parameter fallback: If extracted type differs from type parameter, retry with type parameter\n * 5. Layer-specific fallback: Use LAYER_FALLBACK map for layers with limited viewer support\n *\n * @param elementId - The element ID (can be in dot-format or hyphen-format)\n * @param layer - The layer name (canonical form)\n * @param type - The element type field\n * @param specNodeId - The spec node ID if already known\n * @returns The resolved spec node ID string\n */\n private resolveViewerSpecNodeId(\n elementId: string,\n layer: string,\n type: string,\n specNodeId: string\n ): string {\n // Strategy 1: Valid pass-through - return if specNodeId is valid (anywhere in the model)\n if (specNodeId && VisualizationServer.ALL_VALID_IDS.has(specNodeId)) {\n return specNodeId;\n }\n\n // Strategy 2 & 3: Extract type from elementId and construct candidate\n // Only extract if elementId is not empty\n let extractedType: string | null = null;\n\n if (elementId) {\n // Try dot-format extraction first (layer.type.name)\n if (elementId.includes(\".\")) {\n const parts = elementId.split(\".\");\n if (parts.length >= 2) {\n extractedType = parts[1]; // Second segment is the type\n }\n }\n\n // If no dot-format extraction, try hyphen-format (layer-type-name)\n if (!extractedType && elementId.includes(\"-\")) {\n // For multi-word layers like \"data-model\" or \"data-store\", handle specially\n if (layer === \"data-model\" || layer === \"data-store\") {\n // These layers have hyphens in their names\n // For \"data-model-entity-users\", we need to extract after \"data-model-\"\n const expectedPrefix = layer + \"-\";\n if (elementId.startsWith(expectedPrefix)) {\n const afterPrefix = elementId.substring(expectedPrefix.length);\n const parts = afterPrefix.split(\"-\");\n if (parts.length >= 1) {\n extractedType = parts[0]; // First segment after layer prefix is the type\n }\n }\n } else {\n // For single-word layers, split by hyphen and take second part\n const parts = elementId.split(\"-\");\n if (parts.length >= 2) {\n // Second segment is the type (first is layer)\n extractedType = parts[1];\n }\n }\n }\n }\n\n // If we extracted a type, try to construct a valid candidate\n if (extractedType) {\n const candidate = `${layer}.${extractedType}`;\n const layerIds = VisualizationServer.VALID_SPEC_NODE_IDS[layer] || [];\n if (layerIds.includes(candidate)) {\n return candidate;\n }\n }\n\n // Strategy 4: If type param was provided, try with provided type\n // This applies when:\n // - extractedType is null (e.g., unparseable elementId) but type is valid\n // - extractedType differs from type (prefer extracted over provided)\n if (type && (!extractedType || extractedType !== type)) {\n const candidate = `${layer}.${type}`;\n const layerIds = VisualizationServer.VALID_SPEC_NODE_IDS[layer] || [];\n if (layerIds.includes(candidate)) {\n return candidate;\n }\n }\n\n // Strategy 5: Fall back to layer-specific fallback\n if (layer in VisualizationServer.LAYER_FALLBACK) {\n return VisualizationServer.LAYER_FALLBACK[layer];\n }\n\n // Default fallback for unknown layers\n return \"data-store.database\";\n }\n\n /**\n * Generate unique annotation ID\n * Uses cryptographically secure random bytes for collision-resistant IDs\n */\n private generateAnnotationId(): string {\n return `ann-${Date.now()}-${randomBytes(4).toString('hex')}`;\n }\n\n /**\n * Generate unique reply ID\n * Uses cryptographically secure random bytes for collision-resistant IDs\n */\n private generateReplyId(): string {\n return `reply-${Date.now()}-${randomBytes(4).toString('hex')}`;\n }\n\n /**\n * Broadcast message to all connected WebSocket clients\n */\n private async broadcastMessage(message: any): Promise<void> {\n const messageStr = JSON.stringify(message);\n const messageType = message.type || \"unknown\";\n\n // Telemetry: Track broadcast start\n await this.recordWebSocketEvent(\"ws.broadcast.start\", {\n \"ws.broadcast.type\": messageType,\n \"ws.broadcast.client_count\": this.clients.size,\n \"ws.broadcast.message_size_bytes\": messageStr.length,\n });\n\n let failureCount = 0;\n const failedClients: HonoWSContext[] = [];\n\n for (const client of this.clients) {\n try {\n client.send(messageStr);\n } catch (error) {\n failureCount++;\n failedClients.push(client);\n const msg = getErrorMessage(error);\n // Always log broadcast failures for operational visibility in production\n console.warn(`[WebSocket] Failed to send message to client: ${msg}`);\n }\n }\n\n // Remove dead clients from the active client set\n for (const client of failedClients) {\n this.clients.delete(client);\n }\n\n // Telemetry: Track broadcast completion\n await this.recordWebSocketEvent(\"ws.broadcast.complete\", {\n \"ws.broadcast.type\": messageType,\n \"ws.broadcast.success_count\": this.clients.size - failureCount,\n \"ws.broadcast.failure_count\": failureCount,\n });\n\n // Always warn about significant broadcast failures (>10% failure rate)\n if (failureCount > 0) {\n const failureRate = this.clients.size > 0 ? (failureCount / this.clients.size) * 100 : 0;\n if (failureRate >= 10 || failureCount > 5) {\n console.warn(\n `[WebSocket] Broadcast completed with ${failureCount}/${this.clients.size} client failures (${failureRate.toFixed(1)}% failure rate)`\n );\n }\n }\n }\n\n /**\n * Record WebSocket telemetry event\n * Creates spans for WebSocket operations when telemetry is enabled\n */\n private async recordWebSocketEvent(\n eventName: string,\n attributes: Record<string, any>\n ): Promise<void> {\n // Check if telemetry is enabled (compile-time constant set by esbuild)\n // @ts-ignore - TELEMETRY_ENABLED is a global constant defined at build time\n const isTelemetryEnabled = typeof TELEMETRY_ENABLED !== \"undefined\" ? TELEMETRY_ENABLED : false;\n\n if (!isTelemetryEnabled) {\n return;\n }\n\n try {\n // Dynamic import for tree-shaking\n const { startSpan, endSpan } = await import(\"../telemetry/index.js\");\n\n // Create span for WebSocket event\n const span = startSpan(eventName, attributes);\n\n // End span immediately (WebSocket events are typically instantaneous)\n endSpan(span);\n } catch (error) {\n // Log telemetry failures to enable production debugging\n // Always log errors (not just in DEBUG) to catch silent failures\n console.warn(`[Telemetry] Failed to record WebSocket event: ${getErrorMessage(error)}`);\n }\n }\n\n /**\n * Find an element across all layers\n */\n private async findElement(id: string): Promise<Element | null> {\n for (const layer of this.model.layers.values()) {\n const element = layer.getElement(id);\n if (element) return element;\n }\n return null;\n }\n\n /**\n * Handle WebSocket messages\n */\n private async handleWSMessage(ws: HonoWSContext, data: WSMessage): Promise<void> {\n const simpleMsg = data as SimpleWSMessage;\n switch (simpleMsg.type) {\n case \"subscribe\":\n // Send subscribed confirmation\n const topics = simpleMsg.topics || [\"model\", \"annotations\"];\n ws.send(\n JSON.stringify({\n type: \"subscribed\",\n topics,\n timestamp: new Date().toISOString(),\n })\n );\n\n // Send initial model state\n const modelData = await this.serializeModel();\n ws.send(\n JSON.stringify({\n type: \"model\",\n data: modelData,\n timestamp: new Date().toISOString(),\n })\n );\n break;\n\n case \"ping\":\n // Respond with pong\n ws.send(\n JSON.stringify({\n type: \"pong\",\n timestamp: new Date().toISOString(),\n })\n );\n break;\n\n case \"annotate\":\n if (simpleMsg.annotation) {\n await this.broadcastMessage({\n type: \"annotation\",\n data: simpleMsg.annotation,\n });\n }\n break;\n\n default:\n ws.send(\n JSON.stringify({\n type: \"error\",\n message: `Unknown message type: ${simpleMsg.type}`,\n })\n );\n }\n }\n\n /**\n * Handle JSON-RPC 2.0 messages for chat functionality\n */\n private async handleJSONRPCMessage(ws: HonoWSContext, data: WSMessage): Promise<void> {\n // Only requests have a method; responses have result/error\n if (!('method' in data)) {\n // This is a response, not something we handle in this function\n // Log unexpected protocol patterns for operational visibility\n const responseType = ('result' in data) ? 'success' : ('error' in data) ? 'error' : 'unknown';\n const msgId = ('id' in data) ? data.id : 'unknown';\n console.warn(\n `[WebSocket] Received JSON-RPC ${responseType} response from client (ID: ${msgId}), ignoring`\n );\n return;\n }\n const rpcMsg = data as JSONRPCRequest;\n const { method, params, id } = rpcMsg;\n\n // Helper to send JSON-RPC response\n const sendResponse = (result: any) => {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n result,\n id,\n })\n );\n };\n\n // Helper to send JSON-RPC error\n const sendError = (code: number, message: string, data?: any) => {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: { code, message, data },\n id,\n })\n );\n };\n\n try {\n switch (method) {\n case \"chat.status\":\n // Check if any chat client is available\n const hasClient = this.selectedChatClient !== undefined;\n\n // Determine error message: initialization error takes precedence\n let statusError: string | null = null;\n if (this.chatInitializationError) {\n statusError = `Chat initialization failed: ${this.chatInitializationError.message}`;\n } else if (!hasClient) {\n statusError = \"No chat client available. Install Claude Code or GitHub Copilot.\";\n }\n\n sendResponse({\n sdk_available: hasClient,\n sdk_version: hasClient ? this.selectedChatClient!.getClientName() : null,\n error_message: statusError,\n });\n break;\n\n case \"chat.send\":\n // Validate params\n if (!params || typeof params !== 'object' || !('message' in params) || typeof (params as any).message !== 'string' || (params as any).message.trim() === '') {\n sendError(JSONRPC_ERRORS.INVALID_PARAMS, \"Message cannot be empty\");\n return;\n }\n\n // Check if chat client is available\n if (!this.selectedChatClient) {\n sendError(\n JSONRPC_ERRORS.NO_CLIENT_AVAILABLE,\n \"No chat client available. Install Claude Code or GitHub Copilot to enable chat.\"\n );\n return;\n }\n\n // Generate conversation ID\n const conversationId = `conv-${++this.chatConversationCounter}-${Date.now()}`;\n\n // Launch chat with selected client\n await this.launchChat(ws, conversationId, (params as any).message, id);\n break;\n\n case \"chat.cancel\":\n // Find and cancel any active conversation\n let cancelled = false;\n let cancelledConvId = null;\n\n for (const [convId, process] of this.activeChatProcesses) {\n try {\n process.kill();\n this.activeChatProcesses.delete(convId);\n cancelled = true;\n cancelledConvId = convId;\n break;\n } catch (error) {\n console.warn(`[Chat] Failed to cancel process for conversation ${convId}: ${getErrorMessage(error)}`);\n }\n }\n\n sendResponse({\n cancelled,\n conversation_id: cancelledConvId,\n });\n break;\n\n default:\n sendError(JSONRPC_ERRORS.METHOD_NOT_FOUND, `Method not found: ${method}`);\n }\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n sendError(JSONRPC_ERRORS.INTERNAL_ERROR, \"Internal error\", errorMsg);\n }\n }\n\n /**\n * Launch chat with selected client and stream responses via WebSocket\n */\n private async launchChat(\n ws: HonoWSContext,\n conversationId: string,\n message: string,\n requestId: string | number | undefined\n ): Promise<void> {\n if (!this.selectedChatClient) {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.NO_CLIENT_AVAILABLE,\n message: \"No chat client available\",\n },\n id: requestId,\n })\n );\n return;\n }\n\n // Route to appropriate client-specific handler\n if (this.selectedChatClient instanceof ClaudeCodeClient) {\n await this.launchClaudeCodeChat(ws, conversationId, message, requestId);\n } else if (this.selectedChatClient instanceof CopilotClient) {\n await this.launchCopilotChat(ws, conversationId, message, requestId);\n } else {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.NO_CLIENT_AVAILABLE,\n message: \"Unknown chat client type\",\n },\n id: requestId,\n })\n );\n }\n }\n\n /**\n * Launch Claude Code CLI with dr-architect agent and stream responses\n */\n private async launchClaudeCodeChat(\n ws: HonoWSContext,\n conversationId: string,\n message: string,\n requestId: string | number | undefined\n ): Promise<void> {\n try {\n // Build command arguments\n const cmd = [\"claude\", \"--agent\", \"dr-architect\", \"--print\"];\n\n // Add dangerously-skip-permissions flag if withDanger is enabled\n if (this.withDanger) {\n cmd.push(\"--dangerously-skip-permissions\");\n }\n\n cmd.push(\"--verbose\", \"--output-format\", \"stream-json\");\n\n // Launch claude with dr-architect agent for comprehensive DR expertise\n const proc = Bun.spawn({\n cmd,\n cwd: this.model.rootPath,\n stdin: \"pipe\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n\n // Store active process\n this.activeChatProcesses.set(conversationId, proc);\n\n // Send user message via stdin (like Python prototype)\n proc.stdin?.write(new TextEncoder().encode(message));\n proc.stdin?.end();\n\n // Stream stdout (JSON events)\n const stdoutReader = proc.stdout.getReader();\n const decoder = new TextDecoder();\n\n const streamOutput = async () => {\n let accumulatedText = \"\";\n let buffer = \"\";\n\n try {\n while (true) {\n const { done, value } = await stdoutReader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n buffer += chunk;\n\n // Process complete lines (JSON events)\n const lines = buffer.split(\"\\n\");\n buffer = lines.pop() || \"\";\n\n for (const line of lines) {\n if (!line.trim()) continue;\n\n try {\n const event = JSON.parse(line);\n\n if (event.type === \"assistant\") {\n // Extract content blocks from assistant message\n const content = event.message?.content || [];\n for (const block of content) {\n if (block.type === \"text\") {\n accumulatedText += block.text;\n // Send text chunk notification\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.response.chunk\",\n params: {\n conversation_id: conversationId,\n content: block.text,\n is_final: false,\n timestamp: new Date().toISOString(),\n },\n })\n );\n } else if (block.type === \"tool_use\") {\n // Send tool invocation notification\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.tool.invoke\",\n params: {\n conversation_id: conversationId,\n tool_name: block.name,\n tool_input: block.input,\n timestamp: new Date().toISOString(),\n },\n })\n );\n }\n }\n } else if (event.type === \"result\") {\n // Tool result\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.tool.result\",\n params: {\n conversation_id: conversationId,\n result: event.result,\n timestamp: new Date().toISOString(),\n },\n })\n );\n }\n } catch (parseError) {\n // Non-JSON line, send as raw text chunk\n if (process.env.DEBUG) {\n console.debug(`[Chat] Failed to parse JSON chunk: ${getErrorMessage(parseError)}`);\n }\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.response.chunk\",\n params: {\n conversation_id: conversationId,\n content: line + \"\\n\",\n is_final: false,\n timestamp: new Date().toISOString(),\n },\n })\n );\n }\n }\n }\n\n // Process remaining buffer\n if (buffer.trim()) {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.response.chunk\",\n params: {\n conversation_id: conversationId,\n content: buffer,\n is_final: true,\n timestamp: new Date().toISOString(),\n },\n })\n );\n }\n\n // Wait for process to complete\n const exitCode = await proc.exited;\n\n // Clean up\n this.activeChatProcesses.delete(conversationId);\n\n // Send completion response\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n result: {\n conversation_id: conversationId,\n status: exitCode === 0 ? \"complete\" : \"error\",\n exit_code: exitCode,\n full_response: accumulatedText,\n timestamp: new Date().toISOString(),\n },\n id: requestId,\n })\n );\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Chat failed: ${errorMsg}`,\n },\n id: requestId,\n })\n );\n\n this.activeChatProcesses.delete(conversationId);\n\n try {\n await this.killProcessWithRetry(proc, conversationId, \"Claude Code\");\n } catch (error) {\n console.error(`[Claude Code] Failed to kill process for conversation ${conversationId}: ${getErrorMessage(error)}`);\n }\n }\n };\n\n // Start streaming in background\n streamOutput().catch((err) => {\n const errorMsg = getErrorMessage(err);\n console.error(`[Claude Code] Stream error: ${errorMsg}`);\n\n // Notify client of stream failure via WebSocket\n try {\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Stream error: ${errorMsg}`,\n },\n id: requestId,\n })\n );\n } catch (sendError) {\n console.error(`[Claude Code] Failed to send stream error notification: ${getErrorMessage(sendError)}`);\n }\n });\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Failed to launch Claude Code: ${errorMsg}`,\n },\n id: requestId,\n })\n );\n }\n }\n\n /**\n * Launch GitHub Copilot CLI and stream responses via WebSocket\n */\n private async launchCopilotChat(\n ws: HonoWSContext,\n conversationId: string,\n message: string,\n requestId: string | number | undefined\n ): Promise<void> {\n try {\n // Determine which command to use (gh copilot or standalone copilot)\n let cmd: string[];\n\n // Check if gh CLI with copilot extension is available\n const ghResult = Bun.spawnSync({\n cmd: [\"gh\", \"copilot\", \"--version\"],\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n\n if (ghResult.exitCode === 0) {\n cmd = [\"gh\", \"copilot\", \"explain\"];\n\n // Add allow-all-tools flag if withDanger is enabled\n if (this.withDanger) {\n cmd.push(\"--allow-all-tools\");\n }\n\n cmd.push(message);\n } else {\n // Try standalone copilot\n cmd = [\"copilot\", \"explain\"];\n\n // Add allow-all-tools flag if withDanger is enabled\n if (this.withDanger) {\n cmd.push(\"--allow-all-tools\");\n }\n\n cmd.push(message);\n }\n\n // Launch GitHub Copilot\n const proc = Bun.spawn({\n cmd,\n cwd: this.model.rootPath,\n stdin: \"pipe\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n });\n\n // Store active process\n this.activeChatProcesses.set(conversationId, proc);\n\n // GitHub Copilot outputs plain text/markdown (not JSON)\n const stdoutReader = proc.stdout.getReader();\n const decoder = new TextDecoder();\n\n const streamOutput = async () => {\n let accumulatedText = \"\";\n\n try {\n while (true) {\n const { done, value } = await stdoutReader.read();\n if (done) break;\n\n const chunk = decoder.decode(value, { stream: true });\n accumulatedText += chunk;\n\n // Send text chunk as it arrives\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n method: \"chat.response.chunk\",\n params: {\n conversation_id: conversationId,\n content: chunk,\n is_final: false,\n timestamp: new Date().toISOString(),\n },\n })\n );\n }\n\n // Wait for process to complete\n const exitCode = await proc.exited;\n\n // Clean up\n this.activeChatProcesses.delete(conversationId);\n\n // Send completion response\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n result: {\n conversation_id: conversationId,\n status: exitCode === 0 ? \"complete\" : \"error\",\n exit_code: exitCode,\n full_response: accumulatedText,\n timestamp: new Date().toISOString(),\n },\n id: requestId,\n })\n );\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Chat failed: ${errorMsg}`,\n },\n id: requestId,\n })\n );\n\n this.activeChatProcesses.delete(conversationId);\n\n try {\n await this.killProcessWithRetry(proc, conversationId, \"Copilot\");\n } catch (error) {\n console.error(`[Copilot] Failed to kill process for conversation ${conversationId}: ${getErrorMessage(error)}`);\n }\n }\n };\n\n // Start streaming in background\n streamOutput().catch((err) => {\n console.error(`[Copilot] Stream error: ${getErrorMessage(err)}`);\n });\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n\n ws.send(\n JSON.stringify({\n jsonrpc: \"2.0\",\n error: {\n code: JSONRPC_ERRORS.INTERNAL_ERROR,\n message: `Failed to launch GitHub Copilot: ${errorMsg}`,\n },\n id: requestId,\n })\n );\n }\n }\n\n /**\n * Setup file watcher for model changes\n */\n private setupFileWatcher(): void {\n const modelPath = `${this.model.rootPath}/documentation-robotics/model`;\n\n // Use Bun's global watch API (cast to any due to type definitions)\n const bunWatch = (globalThis as any).Bun?.watch;\n if (!bunWatch) {\n console.warn(\"[Watcher] Bun.watch not available, file watching disabled\");\n return;\n }\n\n try {\n this.watcher = bunWatch(modelPath, {\n recursive: true,\n onChange: async (_event: string, path: string) => {\n if (process.env.VERBOSE) {\n console.log(`[Watcher] File changed: ${path}`);\n }\n\n try {\n // Reload model and changesets\n this.model = await Model.load(this.model.rootPath, { lazyLoad: false });\n await this.loadChangesetsFromDisk();\n\n // Broadcast update to all clients\n const modelData = await this.serializeModel();\n const message = JSON.stringify({\n type: \"model.updated\",\n data: modelData,\n timestamp: new Date().toISOString(),\n });\n\n for (const client of this.clients) {\n try {\n client.send(message);\n } catch (error) {\n const msg = getErrorMessage(error);\n console.warn(`[Watcher] Failed to send update: ${msg}`);\n }\n }\n } catch (error) {\n const message = getErrorMessage(error);\n console.error(`[Watcher] Failed to reload model: ${message}`);\n }\n },\n });\n } catch (err) {\n console.warn(`[Watcher] Could not watch ${modelPath}: ${err}`);\n }\n }\n\n /**\n * Load all JSON schemas from bundled directory (recursive)\n * Loads schemas from all subdirectories: base/, nodes/, relationships/, etc.\n */\n private async loadSchemas(): Promise<Record<string, any>> {\n const schemasPath = new URL(\"../schemas/bundled/\", import.meta.url).pathname;\n const schemas: Record<string, any> = {};\n\n try {\n const fs = await import(\"fs/promises\");\n const path = await import(\"path\");\n\n // Recursive function to walk through directories\n const walkDirectory = async (dir: string, prefix: string = \"\"): Promise<void> => {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const schemaKey = prefix ? `${prefix}/${entry.name}` : entry.name;\n\n if (entry.isDirectory()) {\n // Recursively walk subdirectories\n await walkDirectory(fullPath, schemaKey);\n } else if (entry.isFile() && (entry.name.endsWith(\".schema.json\") || entry.name.endsWith(\".json\"))) {\n // Load JSON schema files\n const content = await fs.readFile(fullPath, \"utf-8\");\n try {\n schemas[schemaKey] = JSON.parse(content);\n } catch (parseError) {\n const parseErrorMsg = parseError instanceof Error ? parseError.message : String(parseError);\n throw new Error(`Invalid JSON in schema file ${fullPath}: ${parseErrorMsg}`);\n }\n }\n }\n };\n\n await walkDirectory(schemasPath);\n\n return schemas;\n } catch (error) {\n const errorMsg = getErrorMessage(error);\n console.error(\"Failed to load schemas:\", error);\n throw new Error(`Failed to load schema files: ${errorMsg}`);\n }\n }\n\n /**\n * Get viewer HTML\n */\n private getViewerHTML(): string {\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>Documentation Robotics Viewer</title>\n <style>\n * {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n }\n\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;\n background: #f5f5f5;\n color: #333;\n }\n\n header {\n background: white;\n border-bottom: 1px solid #e0e0e0;\n padding: 20px;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n }\n\n .header-content {\n max-width: 1400px;\n margin: 0 auto;\n display: flex;\n justify-content: space-between;\n align-items: center;\n }\n\n h1 {\n font-size: 24px;\n font-weight: 600;\n }\n\n .status-badge {\n display: inline-block;\n padding: 6px 12px;\n border-radius: 4px;\n font-size: 12px;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .status-badge.connected {\n background: #4caf50;\n color: white;\n }\n\n .status-badge.disconnected {\n background: #f44336;\n color: white;\n }\n\n .container {\n max-width: 1400px;\n margin: 0 auto;\n padding: 20px;\n }\n\n .content {\n display: grid;\n grid-template-columns: 300px 1fr;\n gap: 20px;\n height: calc(100vh - 100px);\n }\n\n .sidebar {\n background: white;\n border-radius: 4px;\n overflow-y: auto;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n padding: 16px;\n }\n\n .main-panel {\n background: white;\n border-radius: 4px;\n overflow-y: auto;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n padding: 20px;\n }\n\n .layer-group {\n margin-bottom: 16px;\n }\n\n .layer-header {\n font-weight: 600;\n padding: 8px 0;\n border-bottom: 2px solid #e0e0e0;\n margin-bottom: 8px;\n cursor: pointer;\n user-select: none;\n display: flex;\n align-items: center;\n }\n\n .layer-header:hover {\n color: #2196F3;\n }\n\n .layer-header::before {\n content: '\u25B6';\n margin-right: 8px;\n display: inline-block;\n transition: transform 0.2s;\n }\n\n .layer-header.expanded::before {\n transform: rotate(90deg);\n }\n\n .layer-elements {\n margin-left: 16px;\n display: none;\n }\n\n .layer-elements.expanded {\n display: block;\n }\n\n .element-item {\n padding: 8px;\n margin-bottom: 4px;\n background: #f9f9f9;\n border-left: 3px solid #e0e0e0;\n cursor: pointer;\n border-radius: 2px;\n transition: all 0.2s;\n font-size: 13px;\n }\n\n .element-item:hover {\n background: #f0f0f0;\n border-left-color: #2196F3;\n }\n\n .element-item.selected {\n background: #e3f2fd;\n border-left-color: #2196F3;\n font-weight: 500;\n }\n\n .element-details {\n min-height: 300px;\n }\n\n .detail-header {\n border-bottom: 2px solid #e0e0e0;\n padding-bottom: 12px;\n margin-bottom: 16px;\n }\n\n .detail-header h2 {\n font-size: 18px;\n margin-bottom: 4px;\n }\n\n .detail-header .element-type {\n font-size: 12px;\n color: #999;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n }\n\n .detail-section {\n margin-bottom: 16px;\n }\n\n .detail-section h3 {\n font-size: 13px;\n font-weight: 600;\n text-transform: uppercase;\n color: #999;\n margin-bottom: 8px;\n letter-spacing: 0.5px;\n }\n\n .detail-value {\n font-size: 14px;\n line-height: 1.6;\n color: #333;\n word-break: break-word;\n }\n\n .annotations-list {\n max-height: 200px;\n overflow-y: auto;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n padding: 8px;\n }\n\n .annotation-item {\n padding: 8px;\n margin-bottom: 8px;\n background: #fffde7;\n border-left: 3px solid #fbc02d;\n border-radius: 2px;\n font-size: 12px;\n }\n\n .annotation-item .author {\n font-weight: 600;\n color: #666;\n }\n\n .annotation-item .text {\n margin: 4px 0 0 0;\n color: #333;\n }\n\n .annotation-item .timestamp {\n font-size: 11px;\n color: #999;\n margin-top: 4px;\n }\n\n .annotation-form {\n display: grid;\n gap: 8px;\n margin-top: 12px;\n }\n\n .annotation-form input,\n .annotation-form textarea {\n padding: 8px;\n border: 1px solid #e0e0e0;\n border-radius: 4px;\n font-family: inherit;\n font-size: 12px;\n }\n\n .annotation-form textarea {\n resize: vertical;\n min-height: 60px;\n }\n\n .annotation-form button {\n padding: 8px 12px;\n background: #2196F3;\n color: white;\n border: none;\n border-radius: 4px;\n font-weight: 500;\n cursor: pointer;\n transition: background 0.2s;\n }\n\n .annotation-form button:hover {\n background: #1976D2;\n }\n\n .loading {\n text-align: center;\n padding: 40px;\n color: #999;\n }\n\n .loading::after {\n content: '';\n display: inline-block;\n width: 16px;\n height: 16px;\n border: 2px solid #f3f3f3;\n border-top: 2px solid #2196F3;\n border-radius: 50%;\n animation: spin 1s linear infinite;\n margin-left: 8px;\n }\n\n @keyframes spin {\n 0% { transform: rotate(0deg); }\n 100% { transform: rotate(360deg); }\n }\n\n .stats {\n font-size: 12px;\n color: #999;\n margin-bottom: 16px;\n padding-bottom: 12px;\n border-bottom: 1px solid #e0e0e0;\n }\n </style>\n</head>\n<body>\n <header>\n <div class=\"header-content\">\n <div>\n <h1>\uD83D\uDCDA Documentation Robotics Viewer</h1>\n </div>\n <div class=\"status-badge connected\" id=\"status\">\u25CF Connected</div>\n </div>\n </header>\n\n <div class=\"container\">\n <div class=\"content\">\n <div class=\"sidebar\">\n <div class=\"stats\" id=\"stats\">Loading...</div>\n <div id=\"model-tree\">\n <div class=\"loading\">Loading model...</div>\n </div>\n </div>\n\n <div class=\"main-panel\">\n <div id=\"element-details\">\n <div class=\"loading\">Select an element to view details</div>\n </div>\n </div>\n </div>\n </div>\n\n <script>\n let currentModel = null;\n let selectedElementId = null;\n\n const statusEl = document.getElementById('status');\n const modelTreeEl = document.getElementById('model-tree');\n const detailsEl = document.getElementById('element-details');\n const statsEl = document.getElementById('stats');\n\n /**\n * Escape HTML special characters to prevent XSS\n * @param {string} text - Text to escape\n * @returns {string} HTML-escaped text\n */\n function escapeHtml(text) {\n if (!text) return '';\n const div = document.createElement('div');\n div.textContent = text;\n return div.innerHTML;\n }\n\n // Extract token from URL query parameters if present\n const urlParams = new URLSearchParams(window.location.search);\n const token = urlParams.get('token');\n\n // Log token status for debugging\n if (!token) {\n console.warn('[WebSocket] No authentication token found in URL. If authentication is enabled, connection will fail.');\n console.warn('[WebSocket] Expected URL format: http://localhost:PORT?token=YOUR_TOKEN');\n } else {\n console.log('[WebSocket] Found authentication token in URL');\n }\n\n // Build WebSocket URL with token if available\n let wsUrl = \\`ws://\\${window.location.host}/ws\\`;\n if (token) {\n wsUrl += \\`?token=\\${encodeURIComponent(token)}\\`;\n }\n\n console.log('[WebSocket] Connecting to:', wsUrl.replace(/token=[^&]+/, 'token=***'));\n\n const ws = new WebSocket(wsUrl);\n\n ws.addEventListener('open', () => {\n console.log('WebSocket connected');\n updateStatus(true);\n ws.send(JSON.stringify({ type: 'subscribe' }));\n });\n\n ws.addEventListener('message', (event) => {\n try {\n const message = JSON.parse(event.data);\n\n if (message.type === 'model' || message.type === 'model-update') {\n currentModel = message.data;\n renderModel();\n } else if (message.type === 'annotation') {\n // Update annotations for the element\n renderSelectedElement();\n } else if (message.type === 'error') {\n console.error('WebSocket error:', message.message);\n }\n } catch (error) {\n console.error('Failed to parse message:', error);\n }\n });\n\n ws.addEventListener('close', (event) => {\n console.log('WebSocket disconnected', event.code, event.reason);\n updateStatus(false);\n\n // If closed immediately with auth error codes, show helpful message\n if (!token && (event.code === 1002 || event.code === 1008 || event.code === 1006)) {\n showAuthError();\n }\n });\n\n ws.addEventListener('error', (error) => {\n console.error('WebSocket error:', error);\n updateStatus(false);\n\n // If we don't have a token, likely an auth issue\n if (!token) {\n showAuthError();\n }\n });\n\n function showAuthError() {\n const errorDiv = document.createElement('div');\n errorDiv.style.cssText = 'position: fixed; top: 80px; left: 50%; transform: translateX(-50%); background: #f44336; color: white; padding: 16px 24px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.3); z-index: 10000; max-width: 500px; text-align: center;';\n errorDiv.innerHTML = \\`\n <strong>\u26A0\uFE0F Authentication Required</strong><br>\n <span style=\"font-size: 14px; margin-top: 8px; display: inline-block;\">\n Please use the full URL with authentication token from the terminal.\n </span>\n \\`;\n document.body.appendChild(errorDiv);\n\n // Auto-dismiss after 10 seconds\n setTimeout(() => {\n errorDiv.style.transition = 'opacity 0.5s';\n errorDiv.style.opacity = '0';\n setTimeout(() => errorDiv.remove(), 500);\n }, 10000);\n }\n\n function updateStatus(connected) {\n if (connected) {\n statusEl.className = 'status-badge connected';\n statusEl.textContent = '\u25CF Connected';\n } else {\n statusEl.className = 'status-badge disconnected';\n statusEl.textContent = '\u25CF Disconnected';\n }\n }\n\n function renderModel() {\n if (!currentModel) return;\n\n // Update stats\n const layerCount = Object.keys(currentModel.layers).length;\n const elementCount = currentModel.totalElements || 0;\n statsEl.textContent = \\`\\${layerCount} layers \u00B7 \\${elementCount} elements\\`;\n\n // Render layers and elements\n modelTreeEl.innerHTML = '';\n const layers = Object.entries(currentModel.layers)\n .sort((a, b) => a[0].localeCompare(b[0]));\n\n for (const [layerName, layerData] of layers) {\n const layerDiv = document.createElement('div');\n layerDiv.className = 'layer-group';\n\n const headerDiv = document.createElement('div');\n headerDiv.className = 'layer-header';\n headerDiv.textContent = layerName;\n headerDiv.addEventListener('click', () => {\n headerDiv.classList.toggle('expanded');\n elementsDiv.classList.toggle('expanded');\n });\n\n const elementsDiv = document.createElement('div');\n elementsDiv.className = 'layer-elements';\n\n for (const element of layerData.elements) {\n const elemDiv = document.createElement('div');\n elemDiv.className = 'element-item';\n const elementSlug = element.path || element.id;\n if (elementSlug === selectedElementId) {\n elemDiv.classList.add('selected');\n }\n elemDiv.innerHTML = \\`\\${escapeHtml(elementSlug)}<br><span style=\"font-size: 11px; color: #999;\">\\${escapeHtml(element.name)}</span>\\`;\n elemDiv.addEventListener('click', () => {\n selectedElementId = elementSlug;\n document.querySelectorAll('.element-item').forEach(el => el.classList.remove('selected'));\n elemDiv.classList.add('selected');\n renderSelectedElement();\n });\n\n elementsDiv.appendChild(elemDiv);\n }\n\n layerDiv.appendChild(headerDiv);\n layerDiv.appendChild(elementsDiv);\n modelTreeEl.appendChild(layerDiv);\n }\n }\n\n function renderSelectedElement() {\n if (!selectedElementId || !currentModel) {\n detailsEl.innerHTML = '<div class=\"loading\">Select an element to view details</div>';\n return;\n }\n\n // Find element in model\n let element = null;\n for (const layer of Object.values(currentModel.layers)) {\n element = layer.elements.find(e => (e.path || e.id) === selectedElementId);\n if (element) break;\n }\n\n if (!element) {\n detailsEl.innerHTML = '<div class=\"loading\">Element not found</div>';\n return;\n }\n\n let html = \\`\n <div class=\"detail-header\">\n <div class=\"element-type\">\\${escapeHtml(element.type)}</div>\n <h2>\\${escapeHtml(element.name)}</h2>\n <div style=\"font-size: 12px; color: #666; margin-top: 4px;\"><code>\\${escapeHtml(element.path || element.id)}</code></div>\n </div>\n \\`;\n\n if (element.description) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Description</h3>\n <div class=\"detail-value\">\\${escapeHtml(element.description)}</div>\n </div>\n \\`;\n }\n\n if (element.properties && Object.keys(element.properties).length > 0) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Properties</h3>\n <div class=\"detail-value\"><pre>\\${escapeHtml(JSON.stringify(element.properties, null, 2))}</pre></div>\n </div>\n \\`;\n }\n\n if (element.annotations && element.annotations.length > 0) {\n html += \\`\n <div class=\"detail-section\">\n <h3>Annotations</h3>\n <div class=\"annotations-list\">\n \\${element.annotations.map(ann => \\`\n <div class=\"annotation-item\">\n <div class=\"author\">\\${escapeHtml(ann.author)}</div>\n <div class=\"text\">\\${escapeHtml(ann.text)}</div>\n <div class=\"timestamp\">\\${new Date(ann.timestamp).toLocaleString()}</div>\n </div>\n \\`).join('')}\n </div>\n </div>\n \\`;\n }\n\n html += \\`\n <div class=\"detail-section\">\n <h3>Add Annotation</h3>\n <form class=\"annotation-form\" onsubmit=\"addAnnotation(event, '\\${escapeHtml(element.path || element.id)}')\">\n <input type=\"text\" placeholder=\"Author name\" id=\"ann-author\" required>\n <textarea placeholder=\"Annotation text\" id=\"ann-text\" required></textarea>\n <button type=\"submit\">Add Annotation</button>\n </form>\n <div id=\"ann-error\" style=\"margin-top: 8px; min-height: 1.2em;\"></div>\n </div>\n \\`;\n\n detailsEl.innerHTML = html;\n }\n\n function addAnnotation(event, elementId) {\n event.preventDefault();\n\n const author = document.getElementById('ann-author').value;\n const text = document.getElementById('ann-text').value;\n\n if (!author || !text) return;\n\n // Send to server using new annotation API\n fetch('/api/annotations', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ elementId, author, content: text })\n })\n .then(r => r.json().then(data => ({ ok: r.ok, data })))\n .then(({ ok, data }) => {\n if (ok && data && data.id) {\n document.getElementById('ann-author').value = '';\n document.getElementById('ann-text').value = '';\n const errorDiv = document.getElementById('ann-error');\n if (errorDiv) errorDiv.textContent = '';\n } else if (!ok) {\n const errorMsg = (data && data.error) ? data.error : 'Failed to create annotation';\n const errorDiv = document.getElementById('ann-error');\n if (errorDiv) {\n errorDiv.textContent = 'Error: ' + errorMsg;\n errorDiv.style.color = '#d32f2f';\n errorDiv.style.marginTop = '8px';\n }\n }\n })\n .catch(err => {\n const errorDiv = document.getElementById('ann-error');\n if (errorDiv) {\n errorDiv.textContent = 'Error: Failed to add annotation: ' + err.message;\n errorDiv.style.color = '#d32f2f';\n errorDiv.style.marginTop = '8px';\n }\n });\n }\n </script>\n</body>\n</html>`;\n }\n\n /**\n * Serve a file from the custom viewer path\n */\n private async serveCustomViewer(filePath: string): Promise<Response> {\n if (process.env.VERBOSE) console.log(`[VIEWER] serveCustomViewer called for: ${filePath}`);\n\n if (!this.viewerPath) {\n console.error(`[VIEWER] Custom viewer path not configured`);\n return new Response(\"Custom viewer path not configured\", { status: 500 });\n }\n\n try {\n const path = await import(\"path\");\n const fs = await import(\"fs/promises\");\n\n // Resolve absolute path and prevent directory traversal\n const fullPath = path.resolve(this.viewerPath, filePath);\n if (process.env.VERBOSE) console.log(`[VIEWER] Resolved path: ${fullPath}`);\n\n // Security check: ensure the resolved path is within viewerPath\n if (!fullPath.startsWith(path.resolve(this.viewerPath))) {\n console.error(`[VIEWER] Security check failed - path outside viewer directory`);\n return new Response(\"Forbidden\", { status: 403 });\n }\n\n // Read file\n if (process.env.VERBOSE) console.log(`[VIEWER] Reading file: ${fullPath}`);\n const content = await fs.readFile(fullPath);\n\n // Determine content type\n const ext = path.extname(fullPath).toLowerCase();\n const contentTypes: Record<string, string> = {\n \".html\": \"text/html\",\n \".css\": \"text/css\",\n \".js\": \"application/javascript\",\n \".json\": \"application/json\",\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".svg\": \"image/svg+xml\",\n \".ico\": \"image/x-icon\",\n \".woff\": \"font/woff\",\n \".woff2\": \"font/woff2\",\n \".ttf\": \"font/ttf\",\n \".eot\": \"application/vnd.ms-fontobject\",\n };\n\n const contentType = contentTypes[ext] || \"application/octet-stream\";\n\n if (process.env.VERBOSE)\n console.log(`[VIEWER] Successfully read file, returning with Content-Type: ${contentType}`);\n return new Response(content, {\n status: 200,\n headers: {\n \"Content-Type\": contentType,\n },\n });\n } catch (error) {\n if ((error as any).code === \"ENOENT\") {\n console.error(`[VIEWER] File not found: ${filePath}`);\n return new Response(\"File not found\", { status: 404 });\n }\n console.error(`[VIEWER] Error serving custom viewer file ${filePath}:`, error);\n return new Response(\"Internal server error\", { status: 500 });\n }\n }\n\n /**\n * Load all changesets from disk into the in-memory map\n */\n private async loadChangesetsFromDisk(): Promise<void> {\n if (!this.changesetStorage) return;\n try {\n const changesets = await this.changesetStorage.list();\n this.changesets.clear();\n for (const cs of changesets) {\n this.changesets.set(cs.id, cs);\n }\n if (process.env.VERBOSE) {\n console.log(`[Changesets] Loaded ${changesets.length} changeset(s) from disk`);\n }\n } catch (err) {\n console.warn(`[Changesets] Warning: Failed to load changesets from disk: ${err}`);\n }\n }\n\n /**\n * Start the server\n */\n async start(port: number = 8080): Promise<void> {\n // Initialize changeset storage and load from disk\n this.changesetStorage = new StagedChangesetStorage(this.model.rootPath);\n await this.loadChangesetsFromDisk();\n\n this.setupFileWatcher();\n\n // Dynamically import serve from bun to allow usage with non-Bun runtimes\n const { serve } = await import(\"bun\");\n\n this._server = serve({\n port,\n fetch: this.app.fetch,\n websocket, // Add WebSocket handler from hono/bun\n });\n\n console.log(`\u2713 Visualization server running at http://localhost:${port}`);\n }\n\n /**\n * Get the current auth token\n */\n getAuthToken(): string {\n return this.authToken;\n }\n\n /**\n * Check if authentication is enabled\n */\n isAuthEnabled(): boolean {\n return this.authEnabled;\n }\n\n /**\n * Kill a process with retry logic and exponential backoff\n * Attempts graceful SIGTERM first, then force SIGKILL if needed\n */\n private async killProcessWithRetry(\n proc: any,\n conversationId: string,\n label: string\n ): Promise<void> {\n const maxRetries = 3;\n let attempt = 0;\n const baseDelay = 100; // milliseconds\n\n while (attempt < maxRetries) {\n try {\n if (proc.killed) {\n // Process already terminated\n return;\n }\n\n // Try graceful termination with SIGTERM\n proc.kill(\"SIGTERM\");\n\n // Wait for process to exit gracefully\n await new Promise((resolve) => setTimeout(resolve, 500));\n\n if (proc.killed) {\n return;\n }\n } catch (error) {\n // Continue to retry\n }\n\n attempt++;\n if (attempt < maxRetries) {\n // Exponential backoff: 100ms, 200ms, 400ms\n const delay = baseDelay * Math.pow(2, attempt - 1);\n await new Promise((resolve) => setTimeout(resolve, delay));\n }\n }\n\n // Force kill if graceful shutdown failed\n try {\n if (!proc.killed) {\n proc.kill(\"SIGKILL\");\n console.warn(\n `[${label}] Process for conversation ${conversationId} did not respond to SIGTERM, force killed with SIGKILL`\n );\n }\n } catch (error) {\n throw new Error(\n `Failed to kill process: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Stop the server\n */\n stop(): void {\n // Close all WebSocket connections\n for (const client of this.clients) {\n try {\n client.close();\n } catch (error) {\n // Log unexpected errors regardless of VERBOSE flag for operational visibility\n const errorMessage = error instanceof Error ? error.message : String(error);\n if (!errorMessage.includes('already closed')) {\n console.warn(`[SERVER] Unexpected error closing WebSocket client: ${errorMessage}`);\n }\n }\n }\n this.clients.clear();\n\n // Stop file watcher\n if (this.watcher) {\n this.watcher.close?.();\n }\n\n // Stop the server\n if (this._server) {\n this._server.stop();\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["require", "data", "process", "error", "path"]
|
|
7
7
|
}
|
|
@@ -1,22 +1,29 @@
|
|
|
1
1
|
const isTelemetryEnabled = !1;
|
|
2
2
|
async function telemetryMiddleware(c, next) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
const method = c.req.method, path = c.req.path, url = c.req.url, isDebug = process.env.DEBUG || process.env.VERBOSE;
|
|
4
|
+
return isDebug && console.log(`[HTTP] ${method} ${path} (full URL: ${url})`), isDebug && console.log("[Telemetry] Skipping span creation - TELEMETRY_ENABLED is false"), next();
|
|
5
|
+
const { startActiveSpan } = await import("../telemetry/index.js"), route = c.req.routePath || path;
|
|
6
|
+
return isDebug && console.log("[Telemetry] Creating active span for http.server.request"), await startActiveSpan("http.server.request", async (span) => {
|
|
7
|
+
const requestStartTime = Date.now();
|
|
8
|
+
span.setAttributes({
|
|
9
|
+
"http.method": method,
|
|
10
|
+
"http.route": route,
|
|
11
|
+
"http.url": url,
|
|
12
|
+
"http.target": path,
|
|
13
|
+
"http.user_agent": c.req.header("user-agent") || "",
|
|
14
|
+
"http.scheme": new URL(url).protocol.replace(":", "")
|
|
15
|
+
}), isDebug && console.log("[Telemetry] Active span created successfully");
|
|
16
|
+
try {
|
|
17
|
+
await next();
|
|
6
18
|
const statusCode = c.res.status, durationMs = Date.now() - requestStartTime;
|
|
7
|
-
span.setAttribute("http.status_code", statusCode), span.setAttribute("http.duration_ms", durationMs), statusCode >= 500 ? span.setStatus({ code: 2, message: "Server Error" }) : statusCode >= 400 ? span.setStatus({ code: 2, message: "Client Error" }) : span.setStatus({ code: 1 })
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"http.target": path,
|
|
11
|
-
"http.status_code": statusCode,
|
|
12
|
-
duration_ms: durationMs
|
|
13
|
-
});
|
|
19
|
+
span.setAttribute("http.status_code", statusCode), span.setAttribute("http.duration_ms", durationMs), statusCode >= 500 ? span.setStatus({ code: 2, message: "Server Error" }) : statusCode >= 400 ? span.setStatus({ code: 2, message: "Client Error" }) : span.setStatus({ code: 1 });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throw span.recordException(error), span.setStatus({ code: 2, message: error.message }), error;
|
|
14
22
|
}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
23
|
+
}, {
|
|
24
|
+
"http.method": method,
|
|
25
|
+
"http.route": route
|
|
26
|
+
});
|
|
20
27
|
}
|
|
21
28
|
export {
|
|
22
29
|
telemetryMiddleware
|