claude-flow-novice 1.6.1 → 1.6.3
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/.claude/agents/cfn-loop/product-owner.md +54 -4
- package/.claude/commands/cfn-claude-sync.md +303 -0
- package/.claude/commands/cfn-loop-epic.md +290 -0
- package/.claude/commands/cfn-loop-single.md +168 -0
- package/.claude/commands/cfn-loop-sprints.md +384 -0
- package/.claude/commands/cfn-loop.md +180 -0
- package/.claude/commands/metrics-summary.md +58 -0
- package/.claude/commands/parse-epic.md +357 -0
- package/.claude/settings.json +5 -4
- package/.claude/settings.local.json +9 -2
- package/.claude-flow-novice/.claude/agents/cfn-loop/product-owner.md +792 -0
- package/.claude-flow-novice/dist/mcp/server.js +21 -2
- package/.claude-flow-novice/dist/src/api/auth-service.js +84 -38
- package/.claude-flow-novice/dist/src/api/auth-service.js.map +1 -1
- package/.claude-flow-novice/dist/src/api/claude-client.js +138 -3
- package/.claude-flow-novice/dist/src/api/claude-client.js.map +1 -1
- package/.claude-flow-novice/dist/src/cfn-loop/phase-orchestrator-example.js +1 -1
- package/.claude-flow-novice/dist/src/cfn-loop/scope-control.js +247 -0
- package/.claude-flow-novice/dist/src/cfn-loop/scope-control.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js +32 -15
- package/.claude-flow-novice/dist/src/cli/commands/swarm.js.map +1 -1
- package/.claude-flow-novice/dist/src/cli/commands/transparency.js +455 -0
- package/.claude-flow-novice/dist/src/cli/commands/transparency.js.map +1 -0
- package/.claude-flow-novice/dist/src/cli/simple-commands/init/templates/CLAUDE.md +129 -13
- package/.claude-flow-novice/dist/src/components/visualizations/index.js +9 -0
- package/.claude-flow-novice/dist/src/components/visualizations/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js +462 -0
- package/.claude-flow-novice/dist/src/coordination/adapters/v1-coordinator-adapter.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/config-translator.js +248 -0
- package/.claude-flow-novice/dist/src/coordination/config-translator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js +287 -0
- package/.claude-flow-novice/dist/src/coordination/coordination-toggle.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/distributed-consensus.js +68 -9
- package/.claude-flow-novice/dist/src/coordination/distributed-consensus.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/feature-flags.js +166 -0
- package/.claude-flow-novice/dist/src/coordination/feature-flags.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/queen-agent.js +18 -4
- package/.claude-flow-novice/dist/src/coordination/queen-agent.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/role-assignment.js +6 -110
- package/.claude-flow-novice/dist/src/coordination/role-assignment.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/cache/artifact-cache-optimizer.js +632 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/artifact-cache-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/index.js +11 -0
- package/.claude-flow-novice/dist/src/coordination/v2/cache/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/checkpoints/checkpoint-compressor.js +318 -0
- package/.claude-flow-novice/dist/src/coordination/v2/checkpoints/checkpoint-compressor.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.example.js +364 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.js +492 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/cascading-shutdown.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/hierarchical-coordinator.js +786 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/hierarchical-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/parent-child-manager.js +342 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/parent-child-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/swarm-coordinator-v2.js +601 -0
- package/.claude-flow-novice/dist/src/coordination/v2/coordinators/swarm-coordinator-v2.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/help-request-metrics.js +211 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/help-request-metrics.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/index.js +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/message-broker.js +365 -6
- package/.claude-flow-novice/dist/src/coordination/v2/core/message-broker.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/resource-manager-safe.js +478 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/resource-manager-safe.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine-config.js +5 -2
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine-config.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine.js +189 -0
- package/.claude-flow-novice/dist/src/coordination/v2/core/state-machine.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/deadlock-detector.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/deadlock-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/index.js +9 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/resource-manager.js +669 -0
- package/.claude-flow-novice/dist/src/coordination/v2/deadlock/resource-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/artifact-storage.js +451 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/artifact-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/cycle-detector.js +271 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/cycle-detector.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-graph.js +335 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-graph.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-manager.js +439 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-manager.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-request.js +92 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/dependency-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/index.js +21 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/topological-sort.js +223 -0
- package/.claude-flow-novice/dist/src/coordination/v2/dependency/topological-sort.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-coordinator.js +436 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-matcher.js +278 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-matcher.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request-handler.js +317 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request.js +273 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/help-request.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/index.js +15 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/waiting-agent-pool.js +512 -0
- package/.claude-flow-novice/dist/src/coordination/v2/help-system/waiting-agent-pool.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/index.js +6 -0
- package/.claude-flow-novice/dist/src/coordination/v2/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/integration/help-deadlock-integration.js +557 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/help-deadlock-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.example.js +212 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.example.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.js +552 -0
- package/.claude-flow-novice/dist/src/coordination/v2/integration/message-bus-completion-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/dependency-storage.js +367 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/dependency-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/index.js +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/memory/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channel.js +371 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/dependency-channel.js +355 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/dependency-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/help-channel.js +424 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/help-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/index.js +16 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/state-channel.js +295 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/state-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/task-channel.js +411 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/channels/task-channel.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/index.js +14 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-bus.js +387 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-bus.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-persistence.js +589 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-persistence.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-router.js +444 -0
- package/.claude-flow-novice/dist/src/coordination/v2/messaging/message-router.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/checkpoint-manager.js +29 -8
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/checkpoint-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/help-coordinator.js +470 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/help-coordinator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/hierarchical-background-integration.js +450 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/hierarchical-background-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/index.js +5 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/index.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/multi-level-control.js +545 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/multi-level-control.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-controller.js +44 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-controller.js.map +1 -1
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-message-integration.js +415 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/query-message-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/session-pool-optimizer.js +615 -0
- package/.claude-flow-novice/dist/src/coordination/v2/sdk/session-pool-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/security/payload-validator.js +259 -0
- package/.claude-flow-novice/dist/src/coordination/v2/security/payload-validator.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/index.js +17 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-integration.js +357 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-system.js +679 -0
- package/.claude-flow-novice/dist/src/coordination/v2/transparency/transparency-system.js.map +1 -0
- package/.claude-flow-novice/dist/src/core/agent-manager.js +30 -0
- package/.claude-flow-novice/dist/src/core/agent-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/mcp/server.js +21 -2
- package/.claude-flow-novice/dist/src/mcp/server.js.map +1 -1
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js +719 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/apm-integration.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/datadog-collector.js +363 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/datadog-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/index.js +97 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/newrelic-collector.js +384 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/newrelic-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/performance-optimizer.js +612 -0
- package/.claude-flow-novice/dist/src/monitoring/apm/performance-optimizer.js.map +1 -0
- package/.claude-flow-novice/dist/src/monitoring/metrics-collector.js +282 -0
- package/.claude-flow-novice/dist/src/monitoring/metrics-collector.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/metrics-counter.js +268 -0
- package/.claude-flow-novice/dist/src/observability/metrics-counter.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/metrics-storage.js +265 -0
- package/.claude-flow-novice/dist/src/observability/metrics-storage.js.map +1 -0
- package/.claude-flow-novice/dist/src/observability/telemetry.js +26 -0
- package/.claude-flow-novice/dist/src/observability/telemetry.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/tiered-router.js +64 -10
- package/.claude-flow-novice/dist/src/providers/tiered-router.js.map +1 -1
- package/.claude-flow-novice/dist/src/providers/zai-provider.js +196 -97
- package/.claude-flow-novice/dist/src/providers/zai-provider.js.map +1 -1
- package/.claude-flow-novice/dist/src/slash-commands/cfn-claude-sync.js +533 -0
- package/.claude-flow-novice/dist/src/slash-commands/index.js +5 -0
- package/.claude-flow-novice/dist/src/slash-commands/metrics-summary-class.js +74 -0
- package/.claude-flow-novice/dist/src/slash-commands/metrics-summary.js +335 -0
- package/.claude-flow-novice/dist/src/slash-commands/register-all-commands.js +12 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression-demo.js +96 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression-demo.js.map +1 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression.js +406 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-compression.js.map +1 -0
- package/.claude-flow-novice/dist/src/verification/checkpoint-manager.js +35 -5
- package/.claude-flow-novice/dist/src/verification/checkpoint-manager.js.map +1 -1
- package/.claude-flow-novice/dist/src/web/api/apm-routes.js +355 -0
- package/.claude-flow-novice/dist/src/web/api/apm-routes.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/config/api-config.js +186 -0
- package/.claude-flow-novice/dist/src/web/api/config/api-config.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/auth.js +205 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/auth.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/cache.js +262 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/cache.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/error-handler.js +250 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/error-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/request-logger.js +217 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/request-logger.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/validation.js +325 -0
- package/.claude-flow-novice/dist/src/web/api/middleware/validation.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/events.js +465 -0
- package/.claude-flow-novice/dist/src/web/api/routes/events.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/hierarchy.js +302 -0
- package/.claude-flow-novice/dist/src/web/api/routes/hierarchy.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/index.js +14 -0
- package/.claude-flow-novice/dist/src/web/api/routes/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/metrics.js +561 -0
- package/.claude-flow-novice/dist/src/web/api/routes/metrics.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/routes/status.js +450 -0
- package/.claude-flow-novice/dist/src/web/api/routes/status.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/api/server.js +451 -0
- package/.claude-flow-novice/dist/src/web/api/server.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/hooks/useWebSocket.js +385 -0
- package/.claude-flow-novice/dist/src/web/dashboard/hooks/useWebSocket.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/index.js +87 -0
- package/.claude-flow-novice/dist/src/web/dashboard/index.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/dashboard/types.js +6 -0
- package/.claude-flow-novice/dist/src/web/dashboard/types.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/frontend/src/utils/security.js +425 -0
- package/.claude-flow-novice/dist/src/web/frontend/src/utils/security.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/security/security-middleware.js +379 -0
- package/.claude-flow-novice/dist/src/web/security/security-middleware.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/websocket/apm-websocket-handler.js +441 -0
- package/.claude-flow-novice/dist/src/web/websocket/apm-websocket-handler.js.map +1 -0
- package/.claude-flow-novice/dist/src/web/websocket/websocket-manager.js +255 -1
- package/.claude-flow-novice/dist/src/web/websocket/websocket-manager.js.map +1 -1
- package/.claude-flow-novice/metrics.db +0 -0
- package/.claude-flow-novice/metrics.db-shm +0 -0
- package/.claude-flow-novice/metrics.db-wal +0 -0
- package/AGENT_PERFORMANCE_GUIDELINES.md +88 -0
- package/CLAUDE.md +60 -3
- package/MEMORY_LEAK_ROOT_CAUSE.md +149 -0
- package/README.md +27 -0
- package/config/hooks/post-edit-pipeline.js +36 -2
- package/examples/metrics-counter-demo.ts +106 -0
- package/examples/persistent-metrics-demo.ts +83 -0
- package/examples/phase-5-multi-level-control.ts +282 -0
- package/examples/session-pool-optimizer-example.ts +311 -0
- package/package.json +18 -4
- package/scripts/check-routing-stats.cjs +122 -0
- package/scripts/monitor-loop.sh +65 -0
- package/scripts/monitor-memory.sh +47 -0
- package/scripts/monitor.py +43 -0
- package/scripts/pre-publish-validation.cjs +212 -0
- package/scripts/test-provider-routing.cjs +228 -0
- package/scripts/test-routing-telemetry.cjs +147 -0
- package/scripts/test-zai-10k.cjs +81 -0
- package/scripts/test-zai-api.cjs +191 -0
- package/scripts/test-zai-diagnostic.cjs +151 -0
- package/scripts/test-zai-final.cjs +128 -0
- package/scripts/test-zai-with-env.cjs +85 -0
- package/scripts/validate-coordination-cli.js +69 -0
- package/scripts/validate-coordination-toggle-integration.cjs +501 -0
- package/src/cli/simple-commands/init/templates/CLAUDE.md +29 -0
- package/src/observability/metrics-counter.ts +347 -0
- package/src/observability/metrics-storage.ts +356 -0
- package/src/observability/telemetry.ts +658 -0
- package/src/slash-commands/cfn-claude-sync.js +533 -0
- package/src/slash-commands/index.js +5 -0
- package/src/slash-commands/metrics-summary-class.js +74 -0
- package/src/slash-commands/metrics-summary.js +335 -0
- package/src/slash-commands/register-all-commands.js +12 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple Metrics Counter API
|
|
3
|
+
*
|
|
4
|
+
* Provides easy-to-use functions to increment metrics counters
|
|
5
|
+
* with optional tags for dimensional analysis.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getGlobalTelemetry } from './telemetry.js';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// Public API - Simple Counter Functions
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Increment a metric counter by 1 (or custom value)
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* // Simple counter
|
|
20
|
+
* incrementMetric('api.requests');
|
|
21
|
+
*
|
|
22
|
+
* // Counter with tags
|
|
23
|
+
* incrementMetric('api.requests', 1, {
|
|
24
|
+
* endpoint: '/users',
|
|
25
|
+
* method: 'GET',
|
|
26
|
+
* status: '200'
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Custom increment value
|
|
30
|
+
* incrementMetric('bytes.uploaded', 1024, { fileType: 'image' });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
export function incrementMetric(
|
|
34
|
+
metricName: string,
|
|
35
|
+
value: number = 1,
|
|
36
|
+
tags: Record<string, string> = {}
|
|
37
|
+
): void {
|
|
38
|
+
const telemetry = getGlobalTelemetry();
|
|
39
|
+
telemetry.recordCounter(metricName, value, tags);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Record a gauge value (point-in-time measurement)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Record current queue size
|
|
48
|
+
* recordGauge('queue.size', 42);
|
|
49
|
+
*
|
|
50
|
+
* // Record with context
|
|
51
|
+
* recordGauge('memory.usage', process.memoryUsage().heapUsed, {
|
|
52
|
+
* unit: 'bytes',
|
|
53
|
+
* process: 'worker-1'
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export function recordGauge(
|
|
58
|
+
metricName: string,
|
|
59
|
+
value: number,
|
|
60
|
+
tags: Record<string, string> = {}
|
|
61
|
+
): void {
|
|
62
|
+
const telemetry = getGlobalTelemetry();
|
|
63
|
+
telemetry.recordGauge(metricName, value, tags);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Record a timing/duration metric
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```typescript
|
|
71
|
+
* const start = Date.now();
|
|
72
|
+
* await doWork();
|
|
73
|
+
* recordTiming('task.duration', Date.now() - start, { taskType: 'export' });
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export function recordTiming(
|
|
77
|
+
metricName: string,
|
|
78
|
+
durationMs: number,
|
|
79
|
+
tags: Record<string, string> = {}
|
|
80
|
+
): void {
|
|
81
|
+
const telemetry = getGlobalTelemetry();
|
|
82
|
+
telemetry.recordTimer(metricName, durationMs, tags);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Wrapper to measure function execution time
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* const result = await measureExecution('api.fetch', async () => {
|
|
91
|
+
* return await fetch('https://api.example.com/data');
|
|
92
|
+
* }, { endpoint: '/data' });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export async function measureExecution<T>(
|
|
96
|
+
metricName: string,
|
|
97
|
+
fn: () => Promise<T>,
|
|
98
|
+
tags: Record<string, string> = {}
|
|
99
|
+
): Promise<T> {
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
try {
|
|
102
|
+
const result = await fn();
|
|
103
|
+
recordTiming(metricName, Date.now() - start, { ...tags, status: 'success' });
|
|
104
|
+
return result;
|
|
105
|
+
} catch (error) {
|
|
106
|
+
recordTiming(metricName, Date.now() - start, { ...tags, status: 'error' });
|
|
107
|
+
throw error;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Synchronous execution measurement
|
|
113
|
+
*/
|
|
114
|
+
export function measureExecutionSync<T>(
|
|
115
|
+
metricName: string,
|
|
116
|
+
fn: () => T,
|
|
117
|
+
tags: Record<string, string> = {}
|
|
118
|
+
): T {
|
|
119
|
+
const start = Date.now();
|
|
120
|
+
try {
|
|
121
|
+
const result = fn();
|
|
122
|
+
recordTiming(metricName, Date.now() - start, { ...tags, status: 'success' });
|
|
123
|
+
return result;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
recordTiming(metricName, Date.now() - start, { ...tags, status: 'error' });
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ============================================================================
|
|
131
|
+
// Pre-defined Domain Metrics (Convenience Functions)
|
|
132
|
+
// ============================================================================
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Track provider routing decisions
|
|
136
|
+
*/
|
|
137
|
+
export function trackProviderRouting(
|
|
138
|
+
provider: string,
|
|
139
|
+
tier: string,
|
|
140
|
+
agentType: string,
|
|
141
|
+
source: string
|
|
142
|
+
): void {
|
|
143
|
+
incrementMetric('provider.request', 1, {
|
|
144
|
+
provider,
|
|
145
|
+
tier,
|
|
146
|
+
agentType,
|
|
147
|
+
source,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Track agent spawns
|
|
153
|
+
*/
|
|
154
|
+
export function trackAgentSpawn(
|
|
155
|
+
agentType: string,
|
|
156
|
+
swarmId: string,
|
|
157
|
+
topology: string
|
|
158
|
+
): void {
|
|
159
|
+
incrementMetric('agent.spawned', 1, {
|
|
160
|
+
agentType,
|
|
161
|
+
swarmId,
|
|
162
|
+
topology,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Track agent completion
|
|
168
|
+
*/
|
|
169
|
+
export function trackAgentCompletion(
|
|
170
|
+
agentType: string,
|
|
171
|
+
success: boolean,
|
|
172
|
+
durationMs: number
|
|
173
|
+
): void {
|
|
174
|
+
incrementMetric('agent.completed', 1, {
|
|
175
|
+
agentType,
|
|
176
|
+
status: success ? 'success' : 'failure',
|
|
177
|
+
});
|
|
178
|
+
recordTiming('agent.duration', durationMs, {
|
|
179
|
+
agentType,
|
|
180
|
+
status: success ? 'success' : 'failure',
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Track task orchestration
|
|
186
|
+
*/
|
|
187
|
+
export function trackTaskOrchestration(
|
|
188
|
+
taskType: string,
|
|
189
|
+
strategy: string,
|
|
190
|
+
agentCount: number
|
|
191
|
+
): void {
|
|
192
|
+
incrementMetric('task.orchestrated', 1, {
|
|
193
|
+
taskType,
|
|
194
|
+
strategy,
|
|
195
|
+
});
|
|
196
|
+
recordGauge('task.agents', agentCount, {
|
|
197
|
+
taskType,
|
|
198
|
+
strategy,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Track swarm operations
|
|
204
|
+
*/
|
|
205
|
+
export function trackSwarmOperation(
|
|
206
|
+
operation: 'init' | 'spawn' | 'destroy',
|
|
207
|
+
topology: string,
|
|
208
|
+
agentCount?: number
|
|
209
|
+
): void {
|
|
210
|
+
incrementMetric('swarm.operation', 1, {
|
|
211
|
+
operation,
|
|
212
|
+
topology,
|
|
213
|
+
});
|
|
214
|
+
if (agentCount !== undefined) {
|
|
215
|
+
recordGauge('swarm.size', agentCount, { topology });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Track API calls (generic)
|
|
221
|
+
*/
|
|
222
|
+
export function trackAPICall(
|
|
223
|
+
endpoint: string,
|
|
224
|
+
method: string,
|
|
225
|
+
statusCode: number,
|
|
226
|
+
durationMs: number
|
|
227
|
+
): void {
|
|
228
|
+
incrementMetric('api.requests', 1, {
|
|
229
|
+
endpoint,
|
|
230
|
+
method,
|
|
231
|
+
status: statusCode.toString(),
|
|
232
|
+
});
|
|
233
|
+
recordTiming('api.duration', durationMs, {
|
|
234
|
+
endpoint,
|
|
235
|
+
method,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Track errors
|
|
241
|
+
*/
|
|
242
|
+
export function trackError(
|
|
243
|
+
errorType: string,
|
|
244
|
+
component: string,
|
|
245
|
+
severity: 'low' | 'medium' | 'high' | 'critical' = 'medium'
|
|
246
|
+
): void {
|
|
247
|
+
incrementMetric('errors.count', 1, {
|
|
248
|
+
errorType,
|
|
249
|
+
component,
|
|
250
|
+
severity,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Track memory usage
|
|
256
|
+
*/
|
|
257
|
+
export function trackMemoryUsage(component: string): void {
|
|
258
|
+
const usage = process.memoryUsage();
|
|
259
|
+
recordGauge('memory.heap.used', usage.heapUsed, { component, unit: 'bytes' });
|
|
260
|
+
recordGauge('memory.heap.total', usage.heapTotal, { component, unit: 'bytes' });
|
|
261
|
+
recordGauge('memory.rss', usage.rss, { component, unit: 'bytes' });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Track subscription usage
|
|
266
|
+
*/
|
|
267
|
+
export function trackSubscriptionUsage(
|
|
268
|
+
used: number,
|
|
269
|
+
limit: number,
|
|
270
|
+
remaining: number
|
|
271
|
+
): void {
|
|
272
|
+
recordGauge('subscription.usage', used, {
|
|
273
|
+
limit: limit.toString(),
|
|
274
|
+
remaining: remaining.toString(),
|
|
275
|
+
utilizationPct: ((used / limit) * 100).toFixed(1),
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ============================================================================
|
|
280
|
+
// Query Helpers
|
|
281
|
+
// ============================================================================
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Get current metric value (latest gauge or counter sum)
|
|
285
|
+
*/
|
|
286
|
+
export function getMetricValue(metricName: string): number {
|
|
287
|
+
const telemetry = getGlobalTelemetry();
|
|
288
|
+
const metrics = telemetry.getMetrics(metricName);
|
|
289
|
+
|
|
290
|
+
if (metrics.length === 0) return 0;
|
|
291
|
+
|
|
292
|
+
// For counters, sum all values
|
|
293
|
+
if (metrics[0].type === 'counter') {
|
|
294
|
+
return metrics.reduce((sum, m) => sum + m.value, 0);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// For gauges, return latest value
|
|
298
|
+
return metrics[metrics.length - 1].value;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Get metric breakdown by tag
|
|
303
|
+
*/
|
|
304
|
+
export function getMetricBreakdown(
|
|
305
|
+
metricName: string,
|
|
306
|
+
tagKey: string
|
|
307
|
+
): Record<string, number> {
|
|
308
|
+
const telemetry = getGlobalTelemetry();
|
|
309
|
+
const metrics = telemetry.getMetrics(metricName);
|
|
310
|
+
|
|
311
|
+
const breakdown: Record<string, number> = {};
|
|
312
|
+
|
|
313
|
+
metrics.forEach(metric => {
|
|
314
|
+
const tagValue = metric.tags[tagKey] || 'unknown';
|
|
315
|
+
breakdown[tagValue] = (breakdown[tagValue] || 0) + metric.value;
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
return breakdown;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// Exports
|
|
323
|
+
// ============================================================================
|
|
324
|
+
|
|
325
|
+
export default {
|
|
326
|
+
// Core functions
|
|
327
|
+
incrementMetric,
|
|
328
|
+
recordGauge,
|
|
329
|
+
recordTiming,
|
|
330
|
+
measureExecution,
|
|
331
|
+
measureExecutionSync,
|
|
332
|
+
|
|
333
|
+
// Domain-specific trackers
|
|
334
|
+
trackProviderRouting,
|
|
335
|
+
trackAgentSpawn,
|
|
336
|
+
trackAgentCompletion,
|
|
337
|
+
trackTaskOrchestration,
|
|
338
|
+
trackSwarmOperation,
|
|
339
|
+
trackAPICall,
|
|
340
|
+
trackError,
|
|
341
|
+
trackMemoryUsage,
|
|
342
|
+
trackSubscriptionUsage,
|
|
343
|
+
|
|
344
|
+
// Query helpers
|
|
345
|
+
getMetricValue,
|
|
346
|
+
getMetricBreakdown,
|
|
347
|
+
};
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistent Metrics Storage using SQLite
|
|
3
|
+
*
|
|
4
|
+
* Stores all metrics to a local SQLite database for:
|
|
5
|
+
* - Cross-process metric aggregation
|
|
6
|
+
* - Historical analysis
|
|
7
|
+
* - Metrics that survive restarts
|
|
8
|
+
* - Query capabilities across time ranges
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import Database from 'better-sqlite3';
|
|
12
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
13
|
+
import { dirname } from 'path';
|
|
14
|
+
import type { MetricPoint } from './telemetry.js';
|
|
15
|
+
|
|
16
|
+
export interface StoredMetric {
|
|
17
|
+
id: number;
|
|
18
|
+
name: string;
|
|
19
|
+
value: number;
|
|
20
|
+
type: 'counter' | 'gauge' | 'histogram' | 'timer';
|
|
21
|
+
timestamp: string; // ISO 8601
|
|
22
|
+
tags: string; // JSON stringified
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface MetricQuery {
|
|
26
|
+
name?: string;
|
|
27
|
+
type?: string;
|
|
28
|
+
startTime?: Date;
|
|
29
|
+
endTime?: Date;
|
|
30
|
+
tags?: Record<string, string>;
|
|
31
|
+
limit?: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface MetricSummary {
|
|
35
|
+
name: string;
|
|
36
|
+
type: string;
|
|
37
|
+
count: number;
|
|
38
|
+
sum: number;
|
|
39
|
+
avg: number;
|
|
40
|
+
min: number;
|
|
41
|
+
max: number;
|
|
42
|
+
tags: Record<string, Record<string, number>>; // tag -> value -> count
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export class MetricsStorage {
|
|
46
|
+
private db: Database.Database;
|
|
47
|
+
private insertStmt: Database.Statement;
|
|
48
|
+
private readonly dbPath: string;
|
|
49
|
+
|
|
50
|
+
constructor(dbPath: string = '.claude-flow-novice/metrics.db') {
|
|
51
|
+
this.dbPath = dbPath;
|
|
52
|
+
|
|
53
|
+
// Ensure directory exists
|
|
54
|
+
const dir = dirname(dbPath);
|
|
55
|
+
if (!existsSync(dir)) {
|
|
56
|
+
mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Initialize database
|
|
60
|
+
this.db = new Database(dbPath);
|
|
61
|
+
this.db.pragma('journal_mode = WAL'); // Write-Ahead Logging for better concurrency
|
|
62
|
+
|
|
63
|
+
// Create schema
|
|
64
|
+
this.createSchema();
|
|
65
|
+
|
|
66
|
+
// Prepare insert statement for performance
|
|
67
|
+
this.insertStmt = this.db.prepare(`
|
|
68
|
+
INSERT INTO metrics (name, value, type, timestamp, tags)
|
|
69
|
+
VALUES (?, ?, ?, ?, ?)
|
|
70
|
+
`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Create database schema
|
|
75
|
+
*/
|
|
76
|
+
private createSchema(): void {
|
|
77
|
+
this.db.exec(`
|
|
78
|
+
CREATE TABLE IF NOT EXISTS metrics (
|
|
79
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
80
|
+
name TEXT NOT NULL,
|
|
81
|
+
value REAL NOT NULL,
|
|
82
|
+
type TEXT NOT NULL CHECK(type IN ('counter', 'gauge', 'timer', 'histogram')),
|
|
83
|
+
timestamp TEXT NOT NULL,
|
|
84
|
+
tags TEXT NOT NULL DEFAULT '{}',
|
|
85
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
-- Indexes for fast queries
|
|
89
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_name ON metrics(name);
|
|
90
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_timestamp ON metrics(timestamp);
|
|
91
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_type ON metrics(type);
|
|
92
|
+
CREATE INDEX IF NOT EXISTS idx_metrics_name_timestamp ON metrics(name, timestamp);
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Store a metric point
|
|
98
|
+
*/
|
|
99
|
+
store(metric: MetricPoint): void {
|
|
100
|
+
this.insertStmt.run(
|
|
101
|
+
metric.name,
|
|
102
|
+
metric.value,
|
|
103
|
+
metric.type,
|
|
104
|
+
metric.timestamp.toISOString(),
|
|
105
|
+
JSON.stringify(metric.tags)
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Store multiple metrics in a transaction (faster)
|
|
111
|
+
*/
|
|
112
|
+
storeBatch(metrics: MetricPoint[]): void {
|
|
113
|
+
const insertMany = this.db.transaction((metricsArray: MetricPoint[]) => {
|
|
114
|
+
for (const metric of metricsArray) {
|
|
115
|
+
this.insertStmt.run(
|
|
116
|
+
metric.name,
|
|
117
|
+
metric.value,
|
|
118
|
+
metric.type,
|
|
119
|
+
metric.timestamp.toISOString(),
|
|
120
|
+
JSON.stringify(metric.tags)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
insertMany(metrics);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Query metrics with filters
|
|
130
|
+
*/
|
|
131
|
+
query(filter: MetricQuery = {}): StoredMetric[] {
|
|
132
|
+
let sql = 'SELECT * FROM metrics WHERE 1=1';
|
|
133
|
+
const params: any[] = [];
|
|
134
|
+
|
|
135
|
+
if (filter.name) {
|
|
136
|
+
sql += ' AND name = ?';
|
|
137
|
+
params.push(filter.name);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (filter.type) {
|
|
141
|
+
sql += ' AND type = ?';
|
|
142
|
+
params.push(filter.type);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (filter.startTime) {
|
|
146
|
+
sql += ' AND timestamp >= ?';
|
|
147
|
+
params.push(filter.startTime.toISOString());
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (filter.endTime) {
|
|
151
|
+
sql += ' AND timestamp <= ?';
|
|
152
|
+
params.push(filter.endTime.toISOString());
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
sql += ' ORDER BY timestamp DESC';
|
|
156
|
+
|
|
157
|
+
if (filter.limit) {
|
|
158
|
+
sql += ' LIMIT ?';
|
|
159
|
+
params.push(filter.limit);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const stmt = this.db.prepare(sql);
|
|
163
|
+
const rows = stmt.all(...params) as StoredMetric[];
|
|
164
|
+
|
|
165
|
+
// Filter by tags if specified
|
|
166
|
+
if (filter.tags) {
|
|
167
|
+
return rows.filter(row => {
|
|
168
|
+
const rowTags = JSON.parse(row.tags);
|
|
169
|
+
return Object.entries(filter.tags!).every(
|
|
170
|
+
([key, value]) => rowTags[key] === value
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return rows;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get total count for a counter metric
|
|
180
|
+
*/
|
|
181
|
+
getCounterTotal(name: string, tags?: Record<string, string>): number {
|
|
182
|
+
const metrics = this.query({ name, type: 'counter', tags });
|
|
183
|
+
return metrics.reduce((sum, m) => sum + m.value, 0);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get latest gauge value
|
|
188
|
+
*/
|
|
189
|
+
getLatestGauge(name: string, tags?: Record<string, string>): number | null {
|
|
190
|
+
const metrics = this.query({ name, type: 'gauge', tags, limit: 1 });
|
|
191
|
+
return metrics.length > 0 ? metrics[0].value : null;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Get metric summary with aggregations
|
|
196
|
+
*/
|
|
197
|
+
getSummary(name: string, startTime?: Date, endTime?: Date): MetricSummary | null {
|
|
198
|
+
const metrics = this.query({ name, startTime, endTime });
|
|
199
|
+
|
|
200
|
+
if (metrics.length === 0) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const values = metrics.map(m => m.value);
|
|
205
|
+
const tagBreakdown: Record<string, Record<string, number>> = {};
|
|
206
|
+
|
|
207
|
+
// Aggregate tags
|
|
208
|
+
metrics.forEach(metric => {
|
|
209
|
+
const tags = JSON.parse(metric.tags);
|
|
210
|
+
Object.entries(tags).forEach(([key, value]) => {
|
|
211
|
+
if (!tagBreakdown[key]) {
|
|
212
|
+
tagBreakdown[key] = {};
|
|
213
|
+
}
|
|
214
|
+
tagBreakdown[key][value as string] = (tagBreakdown[key][value as string] || 0) + metric.value;
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
name,
|
|
220
|
+
type: metrics[0].type,
|
|
221
|
+
count: metrics.length,
|
|
222
|
+
sum: values.reduce((a, b) => a + b, 0),
|
|
223
|
+
avg: values.reduce((a, b) => a + b, 0) / values.length,
|
|
224
|
+
min: Math.min(...values),
|
|
225
|
+
max: Math.max(...values),
|
|
226
|
+
tags: tagBreakdown,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Get breakdown by tag value
|
|
232
|
+
*/
|
|
233
|
+
getBreakdown(name: string, tagKey: string, startTime?: Date, endTime?: Date): Record<string, number> {
|
|
234
|
+
const metrics = this.query({ name, startTime, endTime });
|
|
235
|
+
const breakdown: Record<string, number> = {};
|
|
236
|
+
|
|
237
|
+
metrics.forEach(metric => {
|
|
238
|
+
const tags = JSON.parse(metric.tags);
|
|
239
|
+
const tagValue = tags[tagKey] || 'unknown';
|
|
240
|
+
breakdown[tagValue] = (breakdown[tagValue] || 0) + metric.value;
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return breakdown;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Get time-series data (metrics grouped by time buckets)
|
|
248
|
+
*/
|
|
249
|
+
getTimeSeries(
|
|
250
|
+
name: string,
|
|
251
|
+
bucketSize: '1min' | '5min' | '1hour' | '1day' = '5min',
|
|
252
|
+
startTime?: Date,
|
|
253
|
+
endTime?: Date
|
|
254
|
+
): Array<{ timestamp: string; value: number; count: number }> {
|
|
255
|
+
const bucketSeconds: Record<string, number> = {
|
|
256
|
+
'1min': 60,
|
|
257
|
+
'5min': 300,
|
|
258
|
+
'1hour': 3600,
|
|
259
|
+
'1day': 86400,
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const seconds = bucketSeconds[bucketSize];
|
|
263
|
+
const sql = `
|
|
264
|
+
SELECT
|
|
265
|
+
strftime('%Y-%m-%d %H:%M:%S',
|
|
266
|
+
datetime((strftime('%s', timestamp) / ${seconds}) * ${seconds}, 'unixepoch')
|
|
267
|
+
) as bucket,
|
|
268
|
+
SUM(value) as value,
|
|
269
|
+
COUNT(*) as count
|
|
270
|
+
FROM metrics
|
|
271
|
+
WHERE name = ?
|
|
272
|
+
${startTime ? 'AND timestamp >= ?' : ''}
|
|
273
|
+
${endTime ? 'AND timestamp <= ?' : ''}
|
|
274
|
+
GROUP BY bucket
|
|
275
|
+
ORDER BY bucket ASC
|
|
276
|
+
`;
|
|
277
|
+
|
|
278
|
+
const params = [name];
|
|
279
|
+
if (startTime) params.push(startTime.toISOString());
|
|
280
|
+
if (endTime) params.push(endTime.toISOString());
|
|
281
|
+
|
|
282
|
+
const stmt = this.db.prepare(sql);
|
|
283
|
+
return stmt.all(...params) as Array<{ timestamp: string; value: number; count: number }>;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Clean up old metrics (retention policy)
|
|
288
|
+
*/
|
|
289
|
+
cleanup(retentionDays: number = 30): number {
|
|
290
|
+
const cutoffDate = new Date();
|
|
291
|
+
cutoffDate.setDate(cutoffDate.getDate() - retentionDays);
|
|
292
|
+
|
|
293
|
+
const stmt = this.db.prepare('DELETE FROM metrics WHERE timestamp < ?');
|
|
294
|
+
const result = stmt.run(cutoffDate.toISOString());
|
|
295
|
+
|
|
296
|
+
return result.changes;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Get database statistics
|
|
301
|
+
*/
|
|
302
|
+
getStats(): {
|
|
303
|
+
totalMetrics: number;
|
|
304
|
+
uniqueNames: number;
|
|
305
|
+
oldestMetric: string | null;
|
|
306
|
+
newestMetric: string | null;
|
|
307
|
+
dbSizeMB: number;
|
|
308
|
+
} {
|
|
309
|
+
const totalMetrics = this.db.prepare('SELECT COUNT(*) as count FROM metrics').get() as { count: number };
|
|
310
|
+
const uniqueNames = this.db.prepare('SELECT COUNT(DISTINCT name) as count FROM metrics').get() as { count: number };
|
|
311
|
+
const oldest = this.db.prepare('SELECT MIN(timestamp) as ts FROM metrics').get() as { ts: string | null };
|
|
312
|
+
const newest = this.db.prepare('SELECT MAX(timestamp) as ts FROM metrics').get() as { ts: string | null };
|
|
313
|
+
|
|
314
|
+
// Get database file size
|
|
315
|
+
let dbSizeMB = 0;
|
|
316
|
+
try {
|
|
317
|
+
const fs = require('fs');
|
|
318
|
+
const stats = fs.statSync(this.dbPath);
|
|
319
|
+
dbSizeMB = stats.size / (1024 * 1024);
|
|
320
|
+
} catch (err) {
|
|
321
|
+
// Ignore if can't get file size
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
return {
|
|
325
|
+
totalMetrics: totalMetrics.count,
|
|
326
|
+
uniqueNames: uniqueNames.count,
|
|
327
|
+
oldestMetric: oldest.ts,
|
|
328
|
+
newestMetric: newest.ts,
|
|
329
|
+
dbSizeMB: Math.round(dbSizeMB * 100) / 100,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Close database connection
|
|
335
|
+
*/
|
|
336
|
+
close(): void {
|
|
337
|
+
this.db.close();
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// ============================================================================
|
|
342
|
+
// Singleton Instance
|
|
343
|
+
// ============================================================================
|
|
344
|
+
|
|
345
|
+
let globalStorage: MetricsStorage | undefined;
|
|
346
|
+
|
|
347
|
+
export function getGlobalMetricsStorage(): MetricsStorage {
|
|
348
|
+
if (!globalStorage) {
|
|
349
|
+
globalStorage = new MetricsStorage();
|
|
350
|
+
}
|
|
351
|
+
return globalStorage;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export function setGlobalMetricsStorage(storage: MetricsStorage): void {
|
|
355
|
+
globalStorage = storage;
|
|
356
|
+
}
|