specweave 0.18.0 → 0.20.0
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.md +229 -1817
- package/README.md +68 -0
- package/bin/specweave.js +62 -6
- package/dist/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +108 -0
- package/dist/locales/en/cli.json +287 -0
- package/dist/locales/en/errors.json +7 -0
- package/dist/locales/en/templates.json +6 -0
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +41 -0
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +108 -0
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +108 -0
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +108 -0
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +108 -0
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +269 -0
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +108 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +3 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts +21 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js +162 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts +25 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +112 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js +301 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +3 -3
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +70 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +258 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +51 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +2 -2
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +20 -5
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +87 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +412 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +64 -13
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts +78 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js +237 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts +97 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js +301 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +386 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +28 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts +57 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +248 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts +82 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js +13 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts +49 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js +316 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts +70 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js +23 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts +85 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js +281 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts +75 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js +238 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js.map +1 -0
- package/dist/spec-parser.js +629 -0
- package/dist/src/cli/commands/import-docs.js +4 -4
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/init-multiproject.js +17 -18
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +107 -3
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.js +8 -4
- package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
- package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
- package/dist/src/cli/commands/switch-project.js +9 -26
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-content.js +3 -0
- package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
- package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
- package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
- package/dist/src/core/deduplication/command-deduplicator.js +254 -0
- package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
- package/dist/src/core/increment/active-increment-manager.d.ts +42 -15
- package/dist/src/core/increment/active-increment-manager.d.ts.map +1 -1
- package/dist/src/core/increment/active-increment-manager.js +113 -46
- package/dist/src/core/increment/active-increment-manager.js.map +1 -1
- package/dist/src/core/increment/conflict-resolver.d.ts +40 -0
- package/dist/src/core/increment/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/increment/conflict-resolver.js +219 -0
- package/dist/src/core/increment/conflict-resolver.js.map +1 -0
- package/dist/src/core/increment/discipline-checker.d.ts.map +1 -1
- package/dist/src/core/increment/discipline-checker.js +7 -1
- package/dist/src/core/increment/discipline-checker.js.map +1 -1
- package/dist/src/core/increment/duplicate-detector.d.ts +52 -0
- package/dist/src/core/increment/duplicate-detector.d.ts.map +1 -0
- package/dist/src/core/increment/duplicate-detector.js +276 -0
- package/dist/src/core/increment/duplicate-detector.js.map +1 -0
- package/dist/src/core/increment/increment-archiver.d.ts +90 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -0
- package/dist/src/core/increment/increment-archiver.js +368 -0
- package/dist/src/core/increment/increment-archiver.js.map +1 -0
- package/dist/src/core/increment/increment-reopener.d.ts +165 -0
- package/dist/src/core/increment/increment-reopener.d.ts.map +1 -0
- package/dist/src/core/increment/increment-reopener.js +390 -0
- package/dist/src/core/increment/increment-reopener.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +26 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +143 -5
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/recent-work-scanner.d.ts +121 -0
- package/dist/src/core/increment/recent-work-scanner.d.ts.map +1 -0
- package/dist/src/core/increment/recent-work-scanner.js +303 -0
- package/dist/src/core/increment/recent-work-scanner.js.map +1 -0
- package/dist/src/core/increment/types.d.ts +1 -0
- package/dist/src/core/increment/types.d.ts.map +1 -1
- package/dist/src/core/increment-utils.d.ts +112 -0
- package/dist/src/core/increment-utils.d.ts.map +1 -0
- package/dist/src/core/increment-utils.js +210 -0
- package/dist/src/core/increment-utils.js.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts +65 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js +175 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts +130 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.js +549 -0
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts +81 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.js +339 -0
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +144 -83
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +488 -270
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/index.d.ts +6 -0
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +6 -0
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/project-detector.d.ts +6 -0
- package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
- package/dist/src/core/living-docs/project-detector.js +35 -1
- package/dist/src/core/living-docs/project-detector.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +100 -26
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.js +1275 -258
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts +109 -0
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js +221 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +143 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -1
- package/dist/src/core/project-manager.d.ts +2 -17
- package/dist/src/core/project-manager.d.ts.map +1 -1
- package/dist/src/core/project-manager.js +68 -48
- package/dist/src/core/project-manager.js.map +1 -1
- package/dist/src/core/spec-content-sync.d.ts +1 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts +32 -54
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.js +142 -138
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +153 -0
- package/dist/src/core/sync/performance-optimizer.d.ts.map +1 -0
- package/dist/src/core/sync/performance-optimizer.js +220 -0
- package/dist/src/core/sync/performance-optimizer.js.map +1 -0
- package/dist/src/core/sync/retry-handler.d.ts +98 -0
- package/dist/src/core/sync/retry-handler.d.ts.map +1 -0
- package/dist/src/core/sync/retry-handler.js +196 -0
- package/dist/src/core/sync/retry-handler.js.map +1 -0
- package/dist/src/core/sync/spec-content-sync.d.ts +88 -0
- package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/sync/spec-content-sync.js +5 -0
- package/dist/src/core/sync/spec-content-sync.js.map +1 -0
- package/dist/src/core/sync/types.d.ts +52 -0
- package/dist/src/core/sync/types.d.ts.map +1 -0
- package/dist/src/core/sync/types.js +5 -0
- package/dist/src/core/sync/types.js.map +1 -0
- package/dist/src/core/types/config.d.ts +125 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +25 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +10 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/increment-metadata.js +10 -1
- package/dist/src/core/types/increment-metadata.js.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/plugins/specweave/COMMANDS.md +13 -4
- package/plugins/specweave/agents/pm/AGENT.md +159 -12
- package/plugins/specweave/commands/specweave-abandon.md +22 -20
- package/plugins/specweave/commands/specweave-archive-features.md +121 -0
- package/plugins/specweave/commands/specweave-archive-increments.md +82 -0
- package/plugins/specweave/commands/specweave-archive.md +363 -0
- package/plugins/specweave/commands/specweave-backlog.md +211 -0
- package/plugins/specweave/commands/specweave-fix-duplicates.md +517 -0
- package/plugins/specweave/commands/specweave-increment.md +4 -3
- package/plugins/specweave/commands/specweave-progress.md +176 -27
- package/plugins/specweave/commands/specweave-reopen.md +391 -0
- package/plugins/specweave/commands/specweave-restore-feature.md +90 -0
- package/plugins/specweave/commands/specweave-restore.md +309 -0
- package/plugins/specweave/commands/specweave-resume.md +51 -23
- package/plugins/specweave/commands/specweave-status.md +41 -7
- package/plugins/specweave/commands/specweave-sync-specs.md +425 -0
- package/plugins/specweave/commands/specweave.md +70 -405
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/post-increment-planning.sh +26 -2
- package/plugins/specweave/hooks/post-task-completion.sh +39 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +83 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
- package/plugins/specweave/lib/hooks/sync-living-docs.js +2 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +4 -0
- package/plugins/specweave/lib/hooks/update-ac-status.js +102 -0
- package/plugins/specweave/lib/hooks/update-ac-status.ts +192 -0
- package/plugins/specweave/skills/archive-increments/SKILL.md +198 -0
- package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +14 -0
- package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +244 -0
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-confluent/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-confluent/README.md +375 -0
- package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +306 -0
- package/plugins/specweave-confluent/skills/confluent-kafka-connect/SKILL.md +453 -0
- package/plugins/specweave-confluent/skills/confluent-ksqldb/SKILL.md +470 -0
- package/plugins/specweave-confluent/skills/confluent-schema-registry/SKILL.md +316 -0
- package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +2 -2
- package/plugins/specweave-github/agents/user-story-updater/AGENT.md +148 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +1 -1
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +156 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh +42 -9
- package/plugins/specweave-github/lib/completion-calculator.js +262 -0
- package/plugins/specweave-github/lib/completion-calculator.ts +434 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/plugins/specweave-github/lib/duplicate-detector.ts +4 -4
- package/plugins/specweave-github/lib/epic-content-builder.js +265 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +376 -0
- package/plugins/specweave-github/lib/github-client-v2.js +49 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +59 -0
- package/plugins/specweave-github/lib/github-epic-sync.js +23 -24
- package/plugins/specweave-github/lib/github-epic-sync.ts +30 -5
- package/plugins/specweave-github/lib/github-feature-sync.js +381 -0
- package/plugins/specweave-github/lib/github-feature-sync.ts +568 -0
- package/plugins/specweave-github/lib/github-spec-content-sync.js +40 -10
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +82 -14
- package/plugins/specweave-github/lib/progress-comment-builder.js +229 -0
- package/plugins/specweave-github/lib/progress-comment-builder.ts +324 -0
- package/plugins/specweave-github/lib/user-story-content-builder.js +299 -0
- package/plugins/specweave-github/lib/user-story-content-builder.ts +413 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.js +344 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.ts +543 -0
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +189 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/{enhanced-jira-sync.ts.disabled → enhanced-jira-sync.ts} +26 -52
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +26 -0
- package/plugins/specweave-kafka/IMPLEMENTATION-COMPLETE.md +483 -0
- package/plugins/specweave-kafka/README.md +242 -0
- package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +235 -0
- package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +209 -0
- package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +266 -0
- package/plugins/specweave-kafka/commands/deploy.md +99 -0
- package/plugins/specweave-kafka/commands/dev-env.md +176 -0
- package/plugins/specweave-kafka/commands/mcp-configure.md +101 -0
- package/plugins/specweave-kafka/commands/monitor-setup.md +96 -0
- package/plugins/specweave-kafka/docker/kafka-local/docker-compose.yml +187 -0
- package/plugins/specweave-kafka/docker/redpanda/docker-compose.yml +199 -0
- package/plugins/specweave-kafka/docker/templates/consumer-nodejs.js +225 -0
- package/plugins/specweave-kafka/docker/templates/consumer-python.py +220 -0
- package/plugins/specweave-kafka/docker/templates/producer-nodejs.js +168 -0
- package/plugins/specweave-kafka/docker/templates/producer-python.py +167 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.js +438 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.ts +541 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.js +47 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.ts +343 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +258 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.ts +298 -0
- package/plugins/specweave-kafka/lib/cli/types.js +10 -0
- package/plugins/specweave-kafka/lib/cli/types.ts +92 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.js +305 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.ts +528 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.js +114 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.ts +195 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.js +210 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.ts +338 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.js +60 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.ts +130 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.js +143 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.ts +290 -0
- package/plugins/specweave-kafka/lib/mcp/detector.js +298 -0
- package/plugins/specweave-kafka/lib/mcp/detector.ts +352 -0
- package/plugins/specweave-kafka/lib/mcp/types.js +21 -0
- package/plugins/specweave-kafka/lib/mcp/types.ts +77 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.js +193 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.ts +362 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.js +188 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.ts +359 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.js +195 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.ts +380 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.js +209 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.ts +358 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.js +354 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.ts +563 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.js +259 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.ts +516 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.js +233 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.ts +423 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.js +266 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.ts +445 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.js +312 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.ts +561 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.js +289 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.ts +607 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.js +264 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.ts +498 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.js +263 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.ts +549 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.js +205 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.ts +399 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.js +249 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.ts +427 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.js +252 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.ts +494 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.js +203 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.ts +469 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.js +419 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.ts +564 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.js +329 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.ts +473 -0
- package/plugins/specweave-kafka/lib/utils/sizing.js +221 -0
- package/plugins/specweave-kafka/lib/utils/sizing.ts +374 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-broker-metrics.json +628 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-cluster-overview.json +564 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-consumer-lag.json +509 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-jvm-metrics.json +674 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-topic-metrics.json +578 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/dashboards/kafka.yml +17 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-alerts.yml +415 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-jmx-exporter.yml +256 -0
- package/plugins/specweave-kafka/package.json +41 -0
- package/plugins/specweave-kafka/skills/kafka-architecture/SKILL.md +647 -0
- package/plugins/specweave-kafka/skills/kafka-cli-tools/SKILL.md +433 -0
- package/plugins/specweave-kafka/skills/kafka-iac-deployment/SKILL.md +449 -0
- package/plugins/specweave-kafka/skills/kafka-kubernetes/SKILL.md +667 -0
- package/plugins/specweave-kafka/skills/kafka-mcp-integration/SKILL.md +273 -0
- package/plugins/specweave-kafka/skills/kafka-observability/SKILL.md +576 -0
- package/plugins/specweave-kafka/templates/config/broker-production.properties +254 -0
- package/plugins/specweave-kafka/templates/config/consumer-low-latency.properties +112 -0
- package/plugins/specweave-kafka/templates/config/producer-high-throughput.properties +120 -0
- package/plugins/specweave-kafka/templates/migration/mirrormaker2-config.properties +234 -0
- package/plugins/specweave-kafka/templates/monitoring/grafana/multi-cluster-dashboard.json +686 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/main.tf +347 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/outputs.tf +107 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/templates/kafka-broker-init.sh.tpl +216 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/variables.tf +156 -0
- package/plugins/specweave-kafka/terraform/aws-msk/main.tf +362 -0
- package/plugins/specweave-kafka/terraform/aws-msk/outputs.tf +93 -0
- package/plugins/specweave-kafka/terraform/aws-msk/templates/server.properties.tpl +32 -0
- package/plugins/specweave-kafka/terraform/aws-msk/variables.tf +235 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/main.tf +281 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/outputs.tf +118 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/variables.tf +148 -0
- package/plugins/specweave-kafka/tsconfig.json +21 -0
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-kafka-streams/README.md +310 -0
- package/plugins/specweave-kafka-streams/skills/kafka-streams-topology/SKILL.md +539 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-n8n/README.md +354 -0
- package/plugins/specweave-n8n/skills/n8n-kafka-workflows/SKILL.md +504 -0
- package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
- package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
- package/src/templates/AGENTS.md.template +601 -7
- package/src/templates/CLAUDE.md.template +188 -88
- package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +0 -255
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +0 -248
- package/plugins/specweave-github/commands/specweave-github-sync-from.md +0 -147
- package/plugins/specweave-github/commands/specweave-github-sync-spec.md +0 -208
- package/plugins/specweave-github/commands/specweave-github-sync-tasks.md +0 -530
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +0 -267
- package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +0 -240
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cluster Switcher
|
|
3
|
+
*
|
|
4
|
+
* Provides context switching between multiple Kafka clusters
|
|
5
|
+
*
|
|
6
|
+
* @module cluster-switcher
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Kafka, Admin, Producer, Consumer } from 'kafkajs';
|
|
10
|
+
import { ClusterConfigManager, ClusterConfig } from './cluster-config-manager';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Cluster Context
|
|
14
|
+
*
|
|
15
|
+
* Active connections to a Kafka cluster
|
|
16
|
+
*/
|
|
17
|
+
export interface ClusterContext {
|
|
18
|
+
/** Cluster ID */
|
|
19
|
+
clusterId: string;
|
|
20
|
+
/** Cluster configuration */
|
|
21
|
+
config: ClusterConfig;
|
|
22
|
+
/** Kafka client */
|
|
23
|
+
kafka: Kafka;
|
|
24
|
+
/** Admin client (lazy initialized) */
|
|
25
|
+
admin?: Admin;
|
|
26
|
+
/** Producer (lazy initialized) */
|
|
27
|
+
producer?: Producer;
|
|
28
|
+
/** Consumers (lazy initialized) */
|
|
29
|
+
consumers: Map<string, Consumer>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Cluster Switcher
|
|
34
|
+
*
|
|
35
|
+
* Manages active connections to multiple Kafka clusters with context switching
|
|
36
|
+
*/
|
|
37
|
+
export class ClusterSwitcher {
|
|
38
|
+
private configManager: ClusterConfigManager;
|
|
39
|
+
private contexts: Map<string, ClusterContext> = new Map();
|
|
40
|
+
private activeContext: ClusterContext | null = null;
|
|
41
|
+
|
|
42
|
+
constructor(configManager?: ClusterConfigManager) {
|
|
43
|
+
this.configManager = configManager || new ClusterConfigManager();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get or create cluster context
|
|
48
|
+
*/
|
|
49
|
+
private async getContext(clusterId: string): Promise<ClusterContext> {
|
|
50
|
+
// Return cached context if exists
|
|
51
|
+
if (this.contexts.has(clusterId)) {
|
|
52
|
+
return this.contexts.get(clusterId)!;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Get cluster config
|
|
56
|
+
const config = this.configManager.getCluster(clusterId);
|
|
57
|
+
if (!config) {
|
|
58
|
+
throw new Error(`Cluster "${clusterId}" not found`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Create Kafka client
|
|
62
|
+
const kafkaConfig = this.buildKafkaConfig(config);
|
|
63
|
+
const kafka = new Kafka(kafkaConfig);
|
|
64
|
+
|
|
65
|
+
// Create context
|
|
66
|
+
const context: ClusterContext = {
|
|
67
|
+
clusterId,
|
|
68
|
+
config,
|
|
69
|
+
kafka,
|
|
70
|
+
consumers: new Map(),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Cache context
|
|
74
|
+
this.contexts.set(clusterId, context);
|
|
75
|
+
|
|
76
|
+
return context;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Build kafkajs configuration from cluster config
|
|
81
|
+
*/
|
|
82
|
+
private buildKafkaConfig(config: ClusterConfig): any {
|
|
83
|
+
const kafkaConfig: any = {
|
|
84
|
+
clientId: `kafka-client-${config.id}`,
|
|
85
|
+
brokers: config.bootstrapServers,
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// Security configuration
|
|
89
|
+
if (config.securityProtocol !== 'PLAINTEXT') {
|
|
90
|
+
if (config.securityProtocol === 'SSL' || config.securityProtocol === 'SASL_SSL') {
|
|
91
|
+
kafkaConfig.ssl = { rejectUnauthorized: true };
|
|
92
|
+
// Note: In production, load SSL certs from files
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (config.securityProtocol.startsWith('SASL')) {
|
|
96
|
+
kafkaConfig.sasl = {
|
|
97
|
+
mechanism: config.saslMechanism || 'PLAIN',
|
|
98
|
+
username: config.saslUsername || '',
|
|
99
|
+
password: config.saslPassword || '',
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return kafkaConfig;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Switch to a different cluster
|
|
109
|
+
*/
|
|
110
|
+
async switch(clusterId: string): Promise<void> {
|
|
111
|
+
console.log(`Switching to cluster: ${clusterId}`);
|
|
112
|
+
|
|
113
|
+
// Get or create context
|
|
114
|
+
const context = await this.getContext(clusterId);
|
|
115
|
+
|
|
116
|
+
// Disconnect previous context (optional - keep connections alive for faster switching)
|
|
117
|
+
// if (this.activeContext && this.activeContext.clusterId !== clusterId) {
|
|
118
|
+
// await this.disconnectContext(this.activeContext);
|
|
119
|
+
// }
|
|
120
|
+
|
|
121
|
+
// Set active context
|
|
122
|
+
this.activeContext = context;
|
|
123
|
+
|
|
124
|
+
// Update config manager active cluster
|
|
125
|
+
this.configManager.setActiveCluster(clusterId);
|
|
126
|
+
|
|
127
|
+
console.log(`Now connected to cluster: ${context.config.name} (${context.config.environment})`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Get active cluster ID
|
|
132
|
+
*/
|
|
133
|
+
getActiveClusterId(): string | null {
|
|
134
|
+
return this.activeContext?.clusterId || null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Get active cluster configuration
|
|
139
|
+
*/
|
|
140
|
+
getActiveCluster(): ClusterConfig | null {
|
|
141
|
+
return this.activeContext?.config || null;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get Admin client for active cluster
|
|
146
|
+
*/
|
|
147
|
+
async getAdmin(): Promise<Admin> {
|
|
148
|
+
if (!this.activeContext) {
|
|
149
|
+
throw new Error('No active cluster. Use switch() first.');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Lazy initialize admin
|
|
153
|
+
if (!this.activeContext.admin) {
|
|
154
|
+
this.activeContext.admin = this.activeContext.kafka.admin();
|
|
155
|
+
await this.activeContext.admin.connect();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return this.activeContext.admin;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Get Producer for active cluster
|
|
163
|
+
*/
|
|
164
|
+
async getProducer(): Promise<Producer> {
|
|
165
|
+
if (!this.activeContext) {
|
|
166
|
+
throw new Error('No active cluster. Use switch() first.');
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Lazy initialize producer
|
|
170
|
+
if (!this.activeContext.producer) {
|
|
171
|
+
this.activeContext.producer = this.activeContext.kafka.producer();
|
|
172
|
+
await this.activeContext.producer.connect();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return this.activeContext.producer;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Get or create Consumer for active cluster
|
|
180
|
+
*/
|
|
181
|
+
async getConsumer(groupId: string): Promise<Consumer> {
|
|
182
|
+
if (!this.activeContext) {
|
|
183
|
+
throw new Error('No active cluster. Use switch() first.');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Return existing consumer
|
|
187
|
+
if (this.activeContext.consumers.has(groupId)) {
|
|
188
|
+
return this.activeContext.consumers.get(groupId)!;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Create new consumer
|
|
192
|
+
const consumer = this.activeContext.kafka.consumer({ groupId });
|
|
193
|
+
await consumer.connect();
|
|
194
|
+
|
|
195
|
+
// Cache consumer
|
|
196
|
+
this.activeContext.consumers.set(groupId, consumer);
|
|
197
|
+
|
|
198
|
+
return consumer;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Disconnect all connections for a specific cluster
|
|
203
|
+
*/
|
|
204
|
+
private async disconnectContext(context: ClusterContext): Promise<void> {
|
|
205
|
+
console.log(`Disconnecting cluster: ${context.clusterId}`);
|
|
206
|
+
|
|
207
|
+
// Disconnect admin
|
|
208
|
+
if (context.admin) {
|
|
209
|
+
await context.admin.disconnect();
|
|
210
|
+
context.admin = undefined;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Disconnect producer
|
|
214
|
+
if (context.producer) {
|
|
215
|
+
await context.producer.disconnect();
|
|
216
|
+
context.producer = undefined;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Disconnect all consumers
|
|
220
|
+
for (const [groupId, consumer] of context.consumers.entries()) {
|
|
221
|
+
await consumer.disconnect();
|
|
222
|
+
}
|
|
223
|
+
context.consumers.clear();
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Disconnect all clusters
|
|
228
|
+
*/
|
|
229
|
+
async disconnectAll(): Promise<void> {
|
|
230
|
+
for (const context of this.contexts.values()) {
|
|
231
|
+
await this.disconnectContext(context);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
this.contexts.clear();
|
|
235
|
+
this.activeContext = null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* List all available clusters
|
|
240
|
+
*/
|
|
241
|
+
listClusters(): ClusterConfig[] {
|
|
242
|
+
return this.configManager.getAllClusters();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Get cluster health status
|
|
247
|
+
*/
|
|
248
|
+
async getClusterHealth(clusterId?: string): Promise<{
|
|
249
|
+
clusterId: string;
|
|
250
|
+
healthy: boolean;
|
|
251
|
+
error?: string;
|
|
252
|
+
}> {
|
|
253
|
+
const targetClusterId = clusterId || this.activeContext?.clusterId;
|
|
254
|
+
if (!targetClusterId) {
|
|
255
|
+
throw new Error('No cluster specified and no active cluster');
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
const context = await this.getContext(targetClusterId);
|
|
260
|
+
const admin = context.kafka.admin();
|
|
261
|
+
await admin.connect();
|
|
262
|
+
|
|
263
|
+
// Simple health check: list topics
|
|
264
|
+
await admin.listTopics();
|
|
265
|
+
|
|
266
|
+
await admin.disconnect();
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
clusterId: targetClusterId,
|
|
270
|
+
healthy: true,
|
|
271
|
+
};
|
|
272
|
+
} catch (error) {
|
|
273
|
+
return {
|
|
274
|
+
clusterId: targetClusterId,
|
|
275
|
+
healthy: false,
|
|
276
|
+
error: (error as Error).message,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Execute operation on specific cluster (without switching context)
|
|
283
|
+
*/
|
|
284
|
+
async executeOn<T>(
|
|
285
|
+
clusterId: string,
|
|
286
|
+
operation: (kafka: Kafka) => Promise<T>
|
|
287
|
+
): Promise<T> {
|
|
288
|
+
const context = await this.getContext(clusterId);
|
|
289
|
+
return operation(context.kafka);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Example Usage: Basic Context Switching
|
|
295
|
+
*
|
|
296
|
+
* ```typescript
|
|
297
|
+
* const switcher = new ClusterSwitcher();
|
|
298
|
+
*
|
|
299
|
+
* // Switch to dev cluster
|
|
300
|
+
* await switcher.switch('dev');
|
|
301
|
+
* const producer = await switcher.getProducer();
|
|
302
|
+
* await producer.send({
|
|
303
|
+
* topic: 'test-topic',
|
|
304
|
+
* messages: [{ value: 'Hello from dev!' }],
|
|
305
|
+
* });
|
|
306
|
+
*
|
|
307
|
+
* // Switch to prod cluster
|
|
308
|
+
* await switcher.switch('prod');
|
|
309
|
+
* const consumer = await switcher.getConsumer('my-consumer-group');
|
|
310
|
+
* await consumer.subscribe({ topic: 'events' });
|
|
311
|
+
* await consumer.run({
|
|
312
|
+
* eachMessage: async ({ message }) => {
|
|
313
|
+
* console.log(`Prod event: ${message.value?.toString()}`);
|
|
314
|
+
* },
|
|
315
|
+
* });
|
|
316
|
+
* ```
|
|
317
|
+
*/
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Example Usage: Execute on Specific Cluster
|
|
321
|
+
*
|
|
322
|
+
* ```typescript
|
|
323
|
+
* const switcher = new ClusterSwitcher();
|
|
324
|
+
*
|
|
325
|
+
* // Active cluster: dev
|
|
326
|
+
* await switcher.switch('dev');
|
|
327
|
+
*
|
|
328
|
+
* // Execute operation on prod without switching
|
|
329
|
+
* await switcher.executeOn('prod', async (kafka) => {
|
|
330
|
+
* const admin = kafka.admin();
|
|
331
|
+
* await admin.connect();
|
|
332
|
+
* const topics = await admin.listTopics();
|
|
333
|
+
* await admin.disconnect();
|
|
334
|
+
* console.log(`Prod topics: ${topics.join(', ')}`);
|
|
335
|
+
* });
|
|
336
|
+
*
|
|
337
|
+
* // Active cluster is still dev
|
|
338
|
+
* console.log(`Active: ${switcher.getActiveClusterId()}`); // "dev"
|
|
339
|
+
* ```
|
|
340
|
+
*/
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Example Usage: Multi-Cluster Health Check
|
|
344
|
+
*
|
|
345
|
+
* ```typescript
|
|
346
|
+
* const switcher = new ClusterSwitcher();
|
|
347
|
+
* const clusters = switcher.listClusters();
|
|
348
|
+
*
|
|
349
|
+
* for (const cluster of clusters) {
|
|
350
|
+
* const health = await switcher.getClusterHealth(cluster.id);
|
|
351
|
+
* console.log(`${cluster.name}: ${health.healthy ? 'UP' : 'DOWN'}`);
|
|
352
|
+
* if (!health.healthy) {
|
|
353
|
+
* console.error(` Error: ${health.error}`);
|
|
354
|
+
* }
|
|
355
|
+
* }
|
|
356
|
+
* ```
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
export default ClusterSwitcher;
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
class HealthAggregator {
|
|
2
|
+
constructor(switcher) {
|
|
3
|
+
this.switcher = switcher;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Collect health metrics for a single cluster
|
|
7
|
+
*/
|
|
8
|
+
async collectClusterHealth(clusterId) {
|
|
9
|
+
try {
|
|
10
|
+
const clusters = this.switcher.listClusters();
|
|
11
|
+
const clusterConfig = clusters.find((c) => c.id === clusterId);
|
|
12
|
+
if (!clusterConfig) {
|
|
13
|
+
throw new Error(`Cluster "${clusterId}" not found`);
|
|
14
|
+
}
|
|
15
|
+
const metrics = await this.switcher.executeOn(clusterId, async (kafka) => {
|
|
16
|
+
const admin = kafka.admin();
|
|
17
|
+
await admin.connect();
|
|
18
|
+
try {
|
|
19
|
+
const cluster = await admin.describeCluster();
|
|
20
|
+
const topics = await admin.listTopics();
|
|
21
|
+
const groups = await admin.listGroups();
|
|
22
|
+
const metadata = await admin.fetchTopicMetadata({ topics });
|
|
23
|
+
let totalPartitions = 0;
|
|
24
|
+
let underReplicatedPartitions = 0;
|
|
25
|
+
let offlinePartitions = 0;
|
|
26
|
+
for (const topic of metadata.topics) {
|
|
27
|
+
for (const partition of topic.partitions) {
|
|
28
|
+
totalPartitions++;
|
|
29
|
+
if (partition.isr.length < partition.replicas.length) {
|
|
30
|
+
underReplicatedPartitions++;
|
|
31
|
+
}
|
|
32
|
+
if (partition.isr.length === 0) {
|
|
33
|
+
offlinePartitions++;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
clusterId,
|
|
39
|
+
clusterName: clusterConfig.name,
|
|
40
|
+
environment: clusterConfig.environment,
|
|
41
|
+
status: this.determineStatus(underReplicatedPartitions, offlinePartitions),
|
|
42
|
+
brokerCount: cluster.brokers.length,
|
|
43
|
+
onlineBrokerCount: cluster.brokers.length,
|
|
44
|
+
// All in describeCluster are online
|
|
45
|
+
topicCount: topics.length,
|
|
46
|
+
partitionCount: totalPartitions,
|
|
47
|
+
underReplicatedPartitions,
|
|
48
|
+
offlinePartitions,
|
|
49
|
+
consumerGroupCount: groups.groups.length,
|
|
50
|
+
controllerId: cluster.controller ? Number(cluster.controller) : void 0,
|
|
51
|
+
lastCheck: /* @__PURE__ */ new Date()
|
|
52
|
+
};
|
|
53
|
+
} finally {
|
|
54
|
+
await admin.disconnect();
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return metrics;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
const clusters = this.switcher.listClusters();
|
|
60
|
+
const clusterConfig = clusters.find((c) => c.id === clusterId);
|
|
61
|
+
return {
|
|
62
|
+
clusterId,
|
|
63
|
+
clusterName: clusterConfig.name,
|
|
64
|
+
environment: clusterConfig.environment,
|
|
65
|
+
status: "down",
|
|
66
|
+
brokerCount: 0,
|
|
67
|
+
onlineBrokerCount: 0,
|
|
68
|
+
topicCount: 0,
|
|
69
|
+
partitionCount: 0,
|
|
70
|
+
underReplicatedPartitions: 0,
|
|
71
|
+
offlinePartitions: 0,
|
|
72
|
+
consumerGroupCount: 0,
|
|
73
|
+
lastCheck: /* @__PURE__ */ new Date(),
|
|
74
|
+
error: error.message
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Determine cluster status based on metrics
|
|
80
|
+
*/
|
|
81
|
+
determineStatus(underReplicatedPartitions, offlinePartitions) {
|
|
82
|
+
if (offlinePartitions > 0) {
|
|
83
|
+
return "down";
|
|
84
|
+
}
|
|
85
|
+
if (underReplicatedPartitions > 0) {
|
|
86
|
+
return "degraded";
|
|
87
|
+
}
|
|
88
|
+
return "healthy";
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Collect health metrics for all clusters
|
|
92
|
+
*/
|
|
93
|
+
async collectAllClusters() {
|
|
94
|
+
const clusters = this.switcher.listClusters();
|
|
95
|
+
const promises = clusters.map((c) => this.collectClusterHealth(c.id));
|
|
96
|
+
return Promise.all(promises);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Aggregate health across all clusters
|
|
100
|
+
*/
|
|
101
|
+
async aggregateHealth() {
|
|
102
|
+
const clusterMetrics = await this.collectAllClusters();
|
|
103
|
+
let totalBrokers = 0;
|
|
104
|
+
let totalTopics = 0;
|
|
105
|
+
let totalPartitions = 0;
|
|
106
|
+
let totalUnderReplicatedPartitions = 0;
|
|
107
|
+
let healthyClusters = 0;
|
|
108
|
+
let degradedClusters = 0;
|
|
109
|
+
let downClusters = 0;
|
|
110
|
+
for (const metrics of clusterMetrics) {
|
|
111
|
+
totalBrokers += metrics.brokerCount;
|
|
112
|
+
totalTopics += metrics.topicCount;
|
|
113
|
+
totalPartitions += metrics.partitionCount;
|
|
114
|
+
totalUnderReplicatedPartitions += metrics.underReplicatedPartitions;
|
|
115
|
+
if (metrics.status === "healthy") healthyClusters++;
|
|
116
|
+
else if (metrics.status === "degraded") degradedClusters++;
|
|
117
|
+
else if (metrics.status === "down") downClusters++;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
totalClusters: clusterMetrics.length,
|
|
121
|
+
healthyClusters,
|
|
122
|
+
degradedClusters,
|
|
123
|
+
downClusters,
|
|
124
|
+
totalBrokers,
|
|
125
|
+
totalTopics,
|
|
126
|
+
totalPartitions,
|
|
127
|
+
totalUnderReplicatedPartitions,
|
|
128
|
+
clusterMetrics,
|
|
129
|
+
lastUpdate: /* @__PURE__ */ new Date()
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Get health summary as formatted text
|
|
134
|
+
*/
|
|
135
|
+
async getHealthSummaryText() {
|
|
136
|
+
const summary = await this.aggregateHealth();
|
|
137
|
+
const lines = [];
|
|
138
|
+
lines.push("=== Kafka Multi-Cluster Health Summary ===");
|
|
139
|
+
lines.push("");
|
|
140
|
+
lines.push(`Total Clusters: ${summary.totalClusters}`);
|
|
141
|
+
lines.push(` \u2713 Healthy: ${summary.healthyClusters}`);
|
|
142
|
+
lines.push(` \u26A0 Degraded: ${summary.degradedClusters}`);
|
|
143
|
+
lines.push(` \u2717 Down: ${summary.downClusters}`);
|
|
144
|
+
lines.push("");
|
|
145
|
+
lines.push(`Total Brokers: ${summary.totalBrokers}`);
|
|
146
|
+
lines.push(`Total Topics: ${summary.totalTopics}`);
|
|
147
|
+
lines.push(`Total Partitions: ${summary.totalPartitions}`);
|
|
148
|
+
if (summary.totalUnderReplicatedPartitions > 0) {
|
|
149
|
+
lines.push(`\u26A0 Under-Replicated Partitions: ${summary.totalUnderReplicatedPartitions}`);
|
|
150
|
+
}
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push("=== Per-Cluster Status ===");
|
|
153
|
+
for (const cluster of summary.clusterMetrics) {
|
|
154
|
+
const icon = cluster.status === "healthy" ? "\u2713" : cluster.status === "degraded" ? "\u26A0" : "\u2717";
|
|
155
|
+
lines.push("");
|
|
156
|
+
lines.push(
|
|
157
|
+
`${icon} ${cluster.clusterName} (${cluster.environment}) - ${cluster.status.toUpperCase()}`
|
|
158
|
+
);
|
|
159
|
+
lines.push(` Brokers: ${cluster.onlineBrokerCount}/${cluster.brokerCount}`);
|
|
160
|
+
lines.push(` Topics: ${cluster.topicCount}`);
|
|
161
|
+
lines.push(` Partitions: ${cluster.partitionCount}`);
|
|
162
|
+
if (cluster.underReplicatedPartitions > 0) {
|
|
163
|
+
lines.push(` \u26A0 Under-Replicated: ${cluster.underReplicatedPartitions}`);
|
|
164
|
+
}
|
|
165
|
+
if (cluster.offlinePartitions > 0) {
|
|
166
|
+
lines.push(` \u2717 Offline: ${cluster.offlinePartitions}`);
|
|
167
|
+
}
|
|
168
|
+
if (cluster.error) {
|
|
169
|
+
lines.push(` Error: ${cluster.error}`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
lines.push("");
|
|
173
|
+
lines.push(`Last Updated: ${summary.lastUpdate.toISOString()}`);
|
|
174
|
+
return lines.join("\n");
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Check if any cluster is unhealthy
|
|
178
|
+
*/
|
|
179
|
+
async hasUnhealthyClusters() {
|
|
180
|
+
const summary = await this.aggregateHealth();
|
|
181
|
+
return summary.degradedClusters > 0 || summary.downClusters > 0;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get list of unhealthy clusters
|
|
185
|
+
*/
|
|
186
|
+
async getUnhealthyClusters() {
|
|
187
|
+
const clusterMetrics = await this.collectAllClusters();
|
|
188
|
+
return clusterMetrics.filter((c) => c.status !== "healthy");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
var health_aggregator_default = HealthAggregator;
|
|
192
|
+
export {
|
|
193
|
+
HealthAggregator,
|
|
194
|
+
health_aggregator_default as default
|
|
195
|
+
};
|