specweave 0.28.68 → 0.29.1
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 +3 -2
- package/README.md +19 -2
- package/dist/src/cli/commands/discrepancies.d.ts +89 -0
- package/dist/src/cli/commands/discrepancies.d.ts.map +1 -0
- package/dist/src/cli/commands/discrepancies.js +385 -0
- package/dist/src/cli/commands/discrepancies.js.map +1 -0
- package/dist/src/cli/commands/notifications.d.ts +70 -0
- package/dist/src/cli/commands/notifications.d.ts.map +1 -0
- package/dist/src/cli/commands/notifications.js +236 -0
- package/dist/src/cli/commands/notifications.js.map +1 -0
- package/dist/src/cli/commands/sync-logs.d.ts +54 -0
- package/dist/src/cli/commands/sync-logs.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-logs.js +240 -0
- package/dist/src/cli/commands/sync-logs.js.map +1 -0
- package/dist/src/cli/commands/sync-monitor.d.ts +42 -0
- package/dist/src/cli/commands/sync-monitor.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-monitor.js +191 -0
- package/dist/src/cli/commands/sync-monitor.js.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts +45 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js +431 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -0
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/workers/brownfield-worker.d.ts +66 -0
- package/dist/src/cli/workers/brownfield-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/brownfield-worker.js +417 -0
- package/dist/src/cli/workers/brownfield-worker.js.map +1 -0
- package/dist/src/core/background/brownfield-launcher.d.ts +86 -0
- package/dist/src/core/background/brownfield-launcher.d.ts.map +1 -0
- package/dist/src/core/background/brownfield-launcher.js +295 -0
- package/dist/src/core/background/brownfield-launcher.js.map +1 -0
- package/dist/src/core/background/index.d.ts +2 -0
- package/dist/src/core/background/index.d.ts.map +1 -1
- package/dist/src/core/background/index.js +2 -0
- package/dist/src/core/background/index.js.map +1 -1
- package/dist/src/core/background/types.d.ts +23 -2
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/config/index.d.ts +1 -0
- package/dist/src/core/config/index.d.ts.map +1 -1
- package/dist/src/core/config/index.js +1 -0
- package/dist/src/core/config/index.js.map +1 -1
- package/dist/src/core/config/types.d.ts +6 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/dashboard/dashboard-data.d.ts +156 -0
- package/dist/src/core/dashboard/dashboard-data.d.ts.map +1 -0
- package/dist/src/core/dashboard/dashboard-data.js +191 -0
- package/dist/src/core/dashboard/dashboard-data.js.map +1 -0
- package/dist/src/core/dashboard/index.d.ts +9 -0
- package/dist/src/core/dashboard/index.d.ts.map +1 -0
- package/dist/src/core/dashboard/index.js +9 -0
- package/dist/src/core/dashboard/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts +77 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +286 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts +8 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.js +8 -0
- package/dist/src/core/discrepancy/analyzers/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts +96 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js +247 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts +88 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.js +520 -0
- package/dist/src/core/discrepancy/brownfield-manager.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts +174 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.js +11 -0
- package/dist/src/core/discrepancy/brownfield-types.js.map +1 -0
- package/dist/src/core/discrepancy/detector.d.ts +92 -0
- package/dist/src/core/discrepancy/detector.d.ts.map +1 -0
- package/dist/src/core/discrepancy/detector.js +346 -0
- package/dist/src/core/discrepancy/detector.js.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts +51 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.js +234 -0
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -0
- package/dist/src/core/discrepancy/index.d.ts +18 -0
- package/dist/src/core/discrepancy/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/index.js +24 -0
- package/dist/src/core/discrepancy/index.js.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts +81 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.js +289 -0
- package/dist/src/core/discrepancy/severity-classifier.js.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts +74 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.js +213 -0
- package/dist/src/core/discrepancy/spec-parser.js.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts +77 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.js +323 -0
- package/dist/src/core/discrepancy/update-recommender.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -16
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +31 -112
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/logs/index.d.ts +10 -0
- package/dist/src/core/logs/index.d.ts.map +1 -0
- package/dist/src/core/logs/index.js +10 -0
- package/dist/src/core/logs/index.js.map +1 -0
- package/dist/src/core/logs/log-aggregator.d.ts +130 -0
- package/dist/src/core/logs/log-aggregator.d.ts.map +1 -0
- package/dist/src/core/logs/log-aggregator.js +206 -0
- package/dist/src/core/logs/log-aggregator.js.map +1 -0
- package/dist/src/core/logs/log-exporter.d.ts +81 -0
- package/dist/src/core/logs/log-exporter.d.ts.map +1 -0
- package/dist/src/core/logs/log-exporter.js +141 -0
- package/dist/src/core/logs/log-exporter.js.map +1 -0
- package/dist/src/core/notifications/command-integration.d.ts +82 -0
- package/dist/src/core/notifications/command-integration.d.ts.map +1 -0
- package/dist/src/core/notifications/command-integration.js +80 -0
- package/dist/src/core/notifications/command-integration.js.map +1 -0
- package/dist/src/core/notifications/index.d.ts +12 -0
- package/dist/src/core/notifications/index.d.ts.map +1 -0
- package/dist/src/core/notifications/index.js +12 -0
- package/dist/src/core/notifications/index.js.map +1 -0
- package/dist/src/core/notifications/notification-display.d.ts +70 -0
- package/dist/src/core/notifications/notification-display.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-display.js +177 -0
- package/dist/src/core/notifications/notification-display.js.map +1 -0
- package/dist/src/core/notifications/notification-manager.d.ts +126 -0
- package/dist/src/core/notifications/notification-manager.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-manager.js +287 -0
- package/dist/src/core/notifications/notification-manager.js.map +1 -0
- package/dist/src/core/notifications/notification-types.d.ts +159 -0
- package/dist/src/core/notifications/notification-types.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-types.js +93 -0
- package/dist/src/core/notifications/notification-types.js.map +1 -0
- package/dist/src/core/scheduler/index.d.ts +11 -0
- package/dist/src/core/scheduler/index.d.ts.map +1 -0
- package/dist/src/core/scheduler/index.js +11 -0
- package/dist/src/core/scheduler/index.js.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts +179 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.js +282 -0
- package/dist/src/core/scheduler/job-scheduler.js.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts +83 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.js +180 -0
- package/dist/src/core/scheduler/schedule-persistence.js.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts +188 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.js +182 -0
- package/dist/src/core/scheduler/scheduled-job.js.map +1 -0
- package/dist/src/core/sync/permission-enforcer.d.ts +206 -0
- package/dist/src/core/sync/permission-enforcer.d.ts.map +1 -0
- package/dist/src/core/sync/permission-enforcer.js +268 -0
- package/dist/src/core/sync/permission-enforcer.js.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts +217 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.js +327 -0
- package/dist/src/core/sync/sync-audit-logger.js.map +1 -0
- package/dist/src/core/sync/sync-interceptor.d.ts +190 -0
- package/dist/src/core/sync/sync-interceptor.d.ts.map +1 -0
- package/dist/src/core/sync/sync-interceptor.js +224 -0
- package/dist/src/core/sync/sync-interceptor.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +5 -2
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/sync-config.d.ts +267 -0
- package/dist/src/core/types/sync-config.d.ts.map +1 -0
- package/dist/src/core/types/sync-config.js +304 -0
- package/dist/src/core/types/sync-config.js.map +1 -0
- package/dist/src/hooks/index.d.ts +11 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +11 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/platform.d.ts +125 -0
- package/dist/src/hooks/platform.d.ts.map +1 -0
- package/dist/src/hooks/platform.js +325 -0
- package/dist/src/hooks/platform.js.map +1 -0
- package/dist/src/hooks/processor.d.ts +20 -0
- package/dist/src/hooks/processor.d.ts.map +1 -0
- package/dist/src/hooks/processor.js +317 -0
- package/dist/src/hooks/processor.js.map +1 -0
- package/dist/src/hooks/scheduler-startup.d.ts +19 -0
- package/dist/src/hooks/scheduler-startup.d.ts.map +1 -0
- package/dist/src/hooks/scheduler-startup.js +92 -0
- package/dist/src/hooks/scheduler-startup.js.map +1 -0
- package/dist/src/hooks/session-start.d.ts +16 -0
- package/dist/src/hooks/session-start.d.ts.map +1 -0
- package/dist/src/hooks/session-start.js +92 -0
- package/dist/src/hooks/session-start.js.map +1 -0
- package/dist/src/importers/duplicate-detector.d.ts +13 -2
- package/dist/src/importers/duplicate-detector.d.ts.map +1 -1
- package/dist/src/importers/duplicate-detector.js +21 -2
- package/dist/src/importers/duplicate-detector.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +41 -2
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +225 -38
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +7 -0
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +30 -4
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
- package/dist/src/sync/ado-sync-wrapper.d.ts +137 -0
- package/dist/src/sync/ado-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/ado-sync-wrapper.js +148 -0
- package/dist/src/sync/ado-sync-wrapper.js.map +1 -0
- package/dist/src/sync/github-sync-wrapper.d.ts +195 -0
- package/dist/src/sync/github-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/github-sync-wrapper.js +220 -0
- package/dist/src/sync/github-sync-wrapper.js.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts +155 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.js +175 -0
- package/dist/src/sync/jira-sync-wrapper.js.map +1 -0
- package/dist/src/utils/feature-id-derivation.d.ts +58 -0
- package/dist/src/utils/feature-id-derivation.d.ts.map +1 -0
- package/dist/src/utils/feature-id-derivation.js +77 -0
- package/dist/src/utils/feature-id-derivation.js.map +1 -0
- package/package.json +3 -1
- package/plugins/specweave/commands/specweave-discrepancies.md +141 -0
- package/plugins/specweave/commands/specweave-discrepancy-to-increment.md +160 -0
- package/plugins/specweave/commands/specweave-jobs.md +45 -2
- package/plugins/specweave/commands/specweave-notifications.md +92 -0
- package/plugins/specweave/commands/specweave-sync-logs.md +131 -0
- package/plugins/specweave/commands/specweave-sync-monitor.md +57 -0
- package/plugins/specweave/hooks/hooks.json +3 -3
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +72 -0
- package/plugins/specweave/hooks/universal/dispatcher.mjs +246 -0
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -0
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +14 -5
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +5 -2
- package/plugins/specweave/skills/discrepancy-viewer.md +154 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +46 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +69 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +0 -26
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +0 -249
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +0 -28
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -156
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts +0 -119
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.js +0 -359
- package/dist/src/core/sync/bidirectional-engine.js.map +0 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +0 -66
- package/dist/src/core/sync/conflict-resolver.d.ts.map +0 -1
- package/dist/src/core/sync/conflict-resolver.js +0 -108
- package/dist/src/core/sync/conflict-resolver.js.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts +0 -55
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.js +0 -203
- package/dist/src/core/sync/enhanced-content-builder.js.map +0 -1
- package/dist/src/core/sync/folder-mapper.d.ts +0 -71
- package/dist/src/core/sync/folder-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/folder-mapper.js +0 -203
- package/dist/src/core/sync/folder-mapper.js.map +0 -1
- package/dist/src/core/sync/label-detector.d.ts +0 -66
- package/dist/src/core/sync/label-detector.d.ts.map +0 -1
- package/dist/src/core/sync/label-detector.js +0 -224
- package/dist/src/core/sync/label-detector.js.map +0 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +0 -153
- package/dist/src/core/sync/performance-optimizer.d.ts.map +0 -1
- package/dist/src/core/sync/performance-optimizer.js +0 -220
- package/dist/src/core/sync/performance-optimizer.js.map +0 -1
- package/dist/src/core/sync/profile-selector.d.ts +0 -52
- package/dist/src/core/sync/profile-selector.d.ts.map +0 -1
- package/dist/src/core/sync/profile-selector.js +0 -179
- package/dist/src/core/sync/profile-selector.js.map +0 -1
- package/dist/src/core/sync/profile-validator.d.ts +0 -52
- package/dist/src/core/sync/profile-validator.d.ts.map +0 -1
- package/dist/src/core/sync/profile-validator.js +0 -170
- package/dist/src/core/sync/profile-validator.js.map +0 -1
- package/dist/src/core/sync/rate-limiter.d.ts +0 -116
- package/dist/src/core/sync/rate-limiter.d.ts.map +0 -1
- package/dist/src/core/sync/rate-limiter.js +0 -308
- package/dist/src/core/sync/rate-limiter.js.map +0 -1
- package/dist/src/core/sync/retry-handler.d.ts +0 -98
- package/dist/src/core/sync/retry-handler.d.ts.map +0 -1
- package/dist/src/core/sync/retry-handler.js +0 -196
- package/dist/src/core/sync/retry-handler.js.map +0 -1
- package/dist/src/core/sync/retry-logic.d.ts +0 -64
- package/dist/src/core/sync/retry-logic.d.ts.map +0 -1
- package/dist/src/core/sync/retry-logic.js +0 -165
- package/dist/src/core/sync/retry-logic.js.map +0 -1
- package/dist/src/core/sync/status-cache.d.ts +0 -91
- package/dist/src/core/sync/status-cache.d.ts.map +0 -1
- package/dist/src/core/sync/status-cache.js +0 -140
- package/dist/src/core/sync/status-cache.js.map +0 -1
- package/dist/src/core/sync/status-mapper.d.ts +0 -69
- package/dist/src/core/sync/status-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/status-mapper.js +0 -90
- package/dist/src/core/sync/status-mapper.js.map +0 -1
- package/dist/src/core/sync/status-sync-engine.d.ts +0 -162
- package/dist/src/core/sync/status-sync-engine.d.ts.map +0 -1
- package/dist/src/core/sync/status-sync-engine.js +0 -347
- package/dist/src/core/sync/status-sync-engine.js.map +0 -1
- package/dist/src/core/sync/sync-event-logger.d.ts +0 -113
- package/dist/src/core/sync/sync-event-logger.d.ts.map +0 -1
- package/dist/src/core/sync/sync-event-logger.js +0 -141
- package/dist/src/core/sync/sync-event-logger.js.map +0 -1
- package/dist/src/core/sync/time-range-selector.d.ts +0 -48
- package/dist/src/core/sync/time-range-selector.d.ts.map +0 -1
- package/dist/src/core/sync/time-range-selector.js +0 -224
- package/dist/src/core/sync/time-range-selector.js.map +0 -1
- package/dist/src/core/sync/types.d.ts +0 -52
- package/dist/src/core/sync/types.d.ts.map +0 -1
- package/dist/src/core/sync/types.js +0 -5
- package/dist/src/core/sync/types.js.map +0 -1
- package/dist/src/core/sync/workflow-detector.d.ts +0 -95
- package/dist/src/core/sync/workflow-detector.d.ts.map +0 -1
- package/dist/src/core/sync/workflow-detector.js +0 -175
- package/dist/src/core/sync/workflow-detector.js.map +0 -1
- package/plugins/specweave-github/lib/enhanced-github-sync.js +0 -220
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +0 -322
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -134
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +0 -196
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Audit Logger
|
|
3
|
+
*
|
|
4
|
+
* Logs all sync operations for compliance and debugging.
|
|
5
|
+
* Uses JSONL format for efficient appending and querying.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - JSONL format (one JSON object per line)
|
|
9
|
+
* - Log rotation (new file when > 100MB or new day)
|
|
10
|
+
* - Configurable retention
|
|
11
|
+
*
|
|
12
|
+
* @module core/sync/sync-audit-logger
|
|
13
|
+
*/
|
|
14
|
+
import { SyncPlatform } from '../types/sync-config.js';
|
|
15
|
+
import { SyncOperation } from './permission-enforcer.js';
|
|
16
|
+
import { Logger } from '../../utils/logger.js';
|
|
17
|
+
/**
|
|
18
|
+
* Audit log entry - single sync operation record
|
|
19
|
+
*/
|
|
20
|
+
export interface AuditLogEntry {
|
|
21
|
+
/**
|
|
22
|
+
* ISO timestamp of the operation
|
|
23
|
+
*/
|
|
24
|
+
timestamp: string;
|
|
25
|
+
/**
|
|
26
|
+
* Target platform
|
|
27
|
+
*/
|
|
28
|
+
platform: SyncPlatform;
|
|
29
|
+
/**
|
|
30
|
+
* Operation type
|
|
31
|
+
*/
|
|
32
|
+
operation: SyncOperation;
|
|
33
|
+
/**
|
|
34
|
+
* Item ID being operated on
|
|
35
|
+
*/
|
|
36
|
+
itemId: string;
|
|
37
|
+
/**
|
|
38
|
+
* Operation result
|
|
39
|
+
*/
|
|
40
|
+
result: 'success' | 'denied' | 'error';
|
|
41
|
+
/**
|
|
42
|
+
* Reason for denial (if denied)
|
|
43
|
+
*/
|
|
44
|
+
reason?: string;
|
|
45
|
+
/**
|
|
46
|
+
* Error message (if error)
|
|
47
|
+
*/
|
|
48
|
+
error?: string;
|
|
49
|
+
/**
|
|
50
|
+
* Duration of the operation in milliseconds
|
|
51
|
+
*/
|
|
52
|
+
durationMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* Additional context
|
|
55
|
+
*/
|
|
56
|
+
context?: Record<string, unknown>;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Audit logger options
|
|
60
|
+
*/
|
|
61
|
+
export interface SyncAuditLoggerOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Path to .specweave directory
|
|
64
|
+
*/
|
|
65
|
+
specweavePath?: string;
|
|
66
|
+
/**
|
|
67
|
+
* Logger instance
|
|
68
|
+
*/
|
|
69
|
+
logger?: Logger;
|
|
70
|
+
/**
|
|
71
|
+
* Max file size in bytes before rotation
|
|
72
|
+
* @default 104857600 (100MB)
|
|
73
|
+
*/
|
|
74
|
+
maxFileSizeBytes?: number;
|
|
75
|
+
/**
|
|
76
|
+
* Retention period in days
|
|
77
|
+
* @default 30
|
|
78
|
+
*/
|
|
79
|
+
retentionDays?: number;
|
|
80
|
+
/**
|
|
81
|
+
* Enable logging (can be disabled for testing)
|
|
82
|
+
* @default true
|
|
83
|
+
*/
|
|
84
|
+
enabled?: boolean;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* SyncAuditLogger - Comprehensive audit logging for sync operations
|
|
88
|
+
*
|
|
89
|
+
* All sync attempts are logged including:
|
|
90
|
+
* - Successful operations
|
|
91
|
+
* - Permission denials
|
|
92
|
+
* - Errors during execution
|
|
93
|
+
*
|
|
94
|
+
* Logs are stored in JSONL format for easy parsing:
|
|
95
|
+
* `.specweave/logs/sync/audit-YYYY-MM-DD.jsonl`
|
|
96
|
+
*
|
|
97
|
+
* Usage:
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const logger = new SyncAuditLogger({ specweavePath });
|
|
100
|
+
*
|
|
101
|
+
* await logger.logSuccess('github', 'upsert-internal', 'FS-001', 150);
|
|
102
|
+
* await logger.logDenied('jira', 'upsert-external', 'EXT-123', 'External updates disabled');
|
|
103
|
+
* await logger.logError('ado', 'update-status', 'US-456', new Error('Network timeout'));
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
export declare class SyncAuditLogger {
|
|
107
|
+
private readonly logDir;
|
|
108
|
+
private readonly logger;
|
|
109
|
+
private readonly maxFileSizeBytes;
|
|
110
|
+
private readonly retentionDays;
|
|
111
|
+
private readonly enabled;
|
|
112
|
+
private currentDate;
|
|
113
|
+
private currentFilePath;
|
|
114
|
+
constructor(options?: SyncAuditLoggerOptions);
|
|
115
|
+
/**
|
|
116
|
+
* Log a successful sync operation
|
|
117
|
+
*
|
|
118
|
+
* @param platform - Target platform
|
|
119
|
+
* @param operation - Operation type
|
|
120
|
+
* @param itemId - Item ID
|
|
121
|
+
* @param durationMs - Operation duration in milliseconds
|
|
122
|
+
* @param context - Optional additional context
|
|
123
|
+
*/
|
|
124
|
+
logSuccess(platform: SyncPlatform, operation: SyncOperation, itemId: string, durationMs?: number, context?: Record<string, unknown>): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Log a denied sync operation
|
|
127
|
+
*
|
|
128
|
+
* @param platform - Target platform
|
|
129
|
+
* @param operation - Operation type
|
|
130
|
+
* @param itemId - Item ID
|
|
131
|
+
* @param reason - Denial reason
|
|
132
|
+
* @param context - Optional additional context
|
|
133
|
+
*/
|
|
134
|
+
logDenied(platform: SyncPlatform, operation: SyncOperation, itemId: string, reason: string, context?: Record<string, unknown>): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Log an error during sync operation
|
|
137
|
+
*
|
|
138
|
+
* @param platform - Target platform
|
|
139
|
+
* @param operation - Operation type
|
|
140
|
+
* @param itemId - Item ID
|
|
141
|
+
* @param error - Error that occurred
|
|
142
|
+
* @param context - Optional additional context
|
|
143
|
+
*/
|
|
144
|
+
logError(platform: SyncPlatform, operation: SyncOperation, itemId: string, error: Error, context?: Record<string, unknown>): Promise<void>;
|
|
145
|
+
/**
|
|
146
|
+
* Get log file path for a specific date
|
|
147
|
+
*
|
|
148
|
+
* @param date - Date to get log path for (defaults to today)
|
|
149
|
+
*/
|
|
150
|
+
getLogPath(date?: Date): string;
|
|
151
|
+
/**
|
|
152
|
+
* Get all log files in the log directory
|
|
153
|
+
*
|
|
154
|
+
* @returns Array of log file paths sorted by date (newest first)
|
|
155
|
+
*/
|
|
156
|
+
getLogFiles(): Promise<string[]>;
|
|
157
|
+
/**
|
|
158
|
+
* Read log entries from a file
|
|
159
|
+
*
|
|
160
|
+
* @param filePath - Path to log file
|
|
161
|
+
* @param limit - Maximum entries to return
|
|
162
|
+
* @param filter - Optional filter function
|
|
163
|
+
*/
|
|
164
|
+
readEntries(filePath: string, limit?: number, filter?: (entry: AuditLogEntry) => boolean): Promise<AuditLogEntry[]>;
|
|
165
|
+
/**
|
|
166
|
+
* Get recent log entries across all log files
|
|
167
|
+
*
|
|
168
|
+
* @param limit - Maximum entries to return
|
|
169
|
+
* @param filter - Optional filter function
|
|
170
|
+
*/
|
|
171
|
+
getRecentEntries(limit?: number, filter?: (entry: AuditLogEntry) => boolean): Promise<AuditLogEntry[]>;
|
|
172
|
+
/**
|
|
173
|
+
* Get entries for a specific platform
|
|
174
|
+
*
|
|
175
|
+
* @param platform - Platform to filter by
|
|
176
|
+
* @param limit - Maximum entries
|
|
177
|
+
*/
|
|
178
|
+
getEntriesForPlatform(platform: SyncPlatform, limit?: number): Promise<AuditLogEntry[]>;
|
|
179
|
+
/**
|
|
180
|
+
* Get all denied entries (for monitoring)
|
|
181
|
+
*
|
|
182
|
+
* @param limit - Maximum entries
|
|
183
|
+
*/
|
|
184
|
+
getDeniedEntries(limit?: number): Promise<AuditLogEntry[]>;
|
|
185
|
+
/**
|
|
186
|
+
* Get all error entries (for debugging)
|
|
187
|
+
*
|
|
188
|
+
* @param limit - Maximum entries
|
|
189
|
+
*/
|
|
190
|
+
getErrorEntries(limit?: number): Promise<AuditLogEntry[]>;
|
|
191
|
+
/**
|
|
192
|
+
* Get statistics for a time period
|
|
193
|
+
*
|
|
194
|
+
* @param since - Start date (defaults to 24 hours ago)
|
|
195
|
+
*/
|
|
196
|
+
getStats(since?: Date): Promise<{
|
|
197
|
+
total: number;
|
|
198
|
+
success: number;
|
|
199
|
+
denied: number;
|
|
200
|
+
errors: number;
|
|
201
|
+
byPlatform: Record<SyncPlatform, number>;
|
|
202
|
+
avgDurationMs: number;
|
|
203
|
+
}>;
|
|
204
|
+
/**
|
|
205
|
+
* Clean up old log files based on retention policy
|
|
206
|
+
*/
|
|
207
|
+
cleanup(): Promise<number>;
|
|
208
|
+
/**
|
|
209
|
+
* Write an entry to the log file
|
|
210
|
+
*/
|
|
211
|
+
private log;
|
|
212
|
+
/**
|
|
213
|
+
* Get current file path, rotating if needed
|
|
214
|
+
*/
|
|
215
|
+
private getOrRotateFilePath;
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=sync-audit-logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-audit-logger.d.ts","sourceRoot":"","sources":["../../../../src/core/sync/sync-audit-logger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Audit Logger
|
|
3
|
+
*
|
|
4
|
+
* Logs all sync operations for compliance and debugging.
|
|
5
|
+
* Uses JSONL format for efficient appending and querying.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - JSONL format (one JSON object per line)
|
|
9
|
+
* - Log rotation (new file when > 100MB or new day)
|
|
10
|
+
* - Configurable retention
|
|
11
|
+
*
|
|
12
|
+
* @module core/sync/sync-audit-logger
|
|
13
|
+
*/
|
|
14
|
+
import { promises as fs } from 'fs';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { consoleLogger } from '../../utils/logger.js';
|
|
17
|
+
/**
|
|
18
|
+
* SyncAuditLogger - Comprehensive audit logging for sync operations
|
|
19
|
+
*
|
|
20
|
+
* All sync attempts are logged including:
|
|
21
|
+
* - Successful operations
|
|
22
|
+
* - Permission denials
|
|
23
|
+
* - Errors during execution
|
|
24
|
+
*
|
|
25
|
+
* Logs are stored in JSONL format for easy parsing:
|
|
26
|
+
* `.specweave/logs/sync/audit-YYYY-MM-DD.jsonl`
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const logger = new SyncAuditLogger({ specweavePath });
|
|
31
|
+
*
|
|
32
|
+
* await logger.logSuccess('github', 'upsert-internal', 'FS-001', 150);
|
|
33
|
+
* await logger.logDenied('jira', 'upsert-external', 'EXT-123', 'External updates disabled');
|
|
34
|
+
* await logger.logError('ado', 'update-status', 'US-456', new Error('Network timeout'));
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export class SyncAuditLogger {
|
|
38
|
+
constructor(options = {}) {
|
|
39
|
+
// Cache current date to detect day changes
|
|
40
|
+
this.currentDate = '';
|
|
41
|
+
this.currentFilePath = '';
|
|
42
|
+
const specweavePath = options.specweavePath ?? path.join(process.cwd(), '.specweave');
|
|
43
|
+
this.logDir = path.join(specweavePath, 'logs', 'sync');
|
|
44
|
+
this.logger = options.logger ?? consoleLogger;
|
|
45
|
+
this.maxFileSizeBytes = options.maxFileSizeBytes ?? 100 * 1024 * 1024; // 100MB
|
|
46
|
+
this.retentionDays = options.retentionDays ?? 30;
|
|
47
|
+
this.enabled = options.enabled ?? true;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Log a successful sync operation
|
|
51
|
+
*
|
|
52
|
+
* @param platform - Target platform
|
|
53
|
+
* @param operation - Operation type
|
|
54
|
+
* @param itemId - Item ID
|
|
55
|
+
* @param durationMs - Operation duration in milliseconds
|
|
56
|
+
* @param context - Optional additional context
|
|
57
|
+
*/
|
|
58
|
+
async logSuccess(platform, operation, itemId, durationMs, context) {
|
|
59
|
+
await this.log({
|
|
60
|
+
timestamp: new Date().toISOString(),
|
|
61
|
+
platform,
|
|
62
|
+
operation,
|
|
63
|
+
itemId,
|
|
64
|
+
result: 'success',
|
|
65
|
+
durationMs,
|
|
66
|
+
context,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Log a denied sync operation
|
|
71
|
+
*
|
|
72
|
+
* @param platform - Target platform
|
|
73
|
+
* @param operation - Operation type
|
|
74
|
+
* @param itemId - Item ID
|
|
75
|
+
* @param reason - Denial reason
|
|
76
|
+
* @param context - Optional additional context
|
|
77
|
+
*/
|
|
78
|
+
async logDenied(platform, operation, itemId, reason, context) {
|
|
79
|
+
await this.log({
|
|
80
|
+
timestamp: new Date().toISOString(),
|
|
81
|
+
platform,
|
|
82
|
+
operation,
|
|
83
|
+
itemId,
|
|
84
|
+
result: 'denied',
|
|
85
|
+
reason,
|
|
86
|
+
context,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Log an error during sync operation
|
|
91
|
+
*
|
|
92
|
+
* @param platform - Target platform
|
|
93
|
+
* @param operation - Operation type
|
|
94
|
+
* @param itemId - Item ID
|
|
95
|
+
* @param error - Error that occurred
|
|
96
|
+
* @param context - Optional additional context
|
|
97
|
+
*/
|
|
98
|
+
async logError(platform, operation, itemId, error, context) {
|
|
99
|
+
await this.log({
|
|
100
|
+
timestamp: new Date().toISOString(),
|
|
101
|
+
platform,
|
|
102
|
+
operation,
|
|
103
|
+
itemId,
|
|
104
|
+
result: 'error',
|
|
105
|
+
error: error.message,
|
|
106
|
+
context: {
|
|
107
|
+
...context,
|
|
108
|
+
stack: error.stack,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Get log file path for a specific date
|
|
114
|
+
*
|
|
115
|
+
* @param date - Date to get log path for (defaults to today)
|
|
116
|
+
*/
|
|
117
|
+
getLogPath(date) {
|
|
118
|
+
const d = date ?? new Date();
|
|
119
|
+
const dateStr = d.toISOString().split('T')[0]; // YYYY-MM-DD
|
|
120
|
+
return path.join(this.logDir, `audit-${dateStr}.jsonl`);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Get all log files in the log directory
|
|
124
|
+
*
|
|
125
|
+
* @returns Array of log file paths sorted by date (newest first)
|
|
126
|
+
*/
|
|
127
|
+
async getLogFiles() {
|
|
128
|
+
try {
|
|
129
|
+
const files = await fs.readdir(this.logDir);
|
|
130
|
+
return files
|
|
131
|
+
.filter((f) => f.startsWith('audit-') && f.endsWith('.jsonl'))
|
|
132
|
+
.map((f) => path.join(this.logDir, f))
|
|
133
|
+
.sort()
|
|
134
|
+
.reverse();
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Read log entries from a file
|
|
142
|
+
*
|
|
143
|
+
* @param filePath - Path to log file
|
|
144
|
+
* @param limit - Maximum entries to return
|
|
145
|
+
* @param filter - Optional filter function
|
|
146
|
+
*/
|
|
147
|
+
async readEntries(filePath, limit, filter) {
|
|
148
|
+
try {
|
|
149
|
+
const content = await fs.readFile(filePath, 'utf-8');
|
|
150
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
151
|
+
let entries = lines.map((line) => JSON.parse(line));
|
|
152
|
+
if (filter) {
|
|
153
|
+
entries = entries.filter(filter);
|
|
154
|
+
}
|
|
155
|
+
if (limit) {
|
|
156
|
+
entries = entries.slice(-limit); // Last N entries
|
|
157
|
+
}
|
|
158
|
+
return entries;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get recent log entries across all log files
|
|
166
|
+
*
|
|
167
|
+
* @param limit - Maximum entries to return
|
|
168
|
+
* @param filter - Optional filter function
|
|
169
|
+
*/
|
|
170
|
+
async getRecentEntries(limit = 100, filter) {
|
|
171
|
+
const files = await this.getLogFiles();
|
|
172
|
+
const entries = [];
|
|
173
|
+
for (const file of files) {
|
|
174
|
+
const fileEntries = await this.readEntries(file, undefined, filter);
|
|
175
|
+
entries.push(...fileEntries);
|
|
176
|
+
if (entries.length >= limit) {
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return entries.slice(-limit);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Get entries for a specific platform
|
|
184
|
+
*
|
|
185
|
+
* @param platform - Platform to filter by
|
|
186
|
+
* @param limit - Maximum entries
|
|
187
|
+
*/
|
|
188
|
+
async getEntriesForPlatform(platform, limit = 50) {
|
|
189
|
+
return this.getRecentEntries(limit, (e) => e.platform === platform);
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Get all denied entries (for monitoring)
|
|
193
|
+
*
|
|
194
|
+
* @param limit - Maximum entries
|
|
195
|
+
*/
|
|
196
|
+
async getDeniedEntries(limit = 50) {
|
|
197
|
+
return this.getRecentEntries(limit, (e) => e.result === 'denied');
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Get all error entries (for debugging)
|
|
201
|
+
*
|
|
202
|
+
* @param limit - Maximum entries
|
|
203
|
+
*/
|
|
204
|
+
async getErrorEntries(limit = 50) {
|
|
205
|
+
return this.getRecentEntries(limit, (e) => e.result === 'error');
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Get statistics for a time period
|
|
209
|
+
*
|
|
210
|
+
* @param since - Start date (defaults to 24 hours ago)
|
|
211
|
+
*/
|
|
212
|
+
async getStats(since) {
|
|
213
|
+
const sinceDate = since ?? new Date(Date.now() - 24 * 60 * 60 * 1000);
|
|
214
|
+
const sinceStr = sinceDate.toISOString();
|
|
215
|
+
const entries = await this.getRecentEntries(10000, (e) => e.timestamp >= sinceStr);
|
|
216
|
+
const stats = {
|
|
217
|
+
total: entries.length,
|
|
218
|
+
success: 0,
|
|
219
|
+
denied: 0,
|
|
220
|
+
errors: 0,
|
|
221
|
+
byPlatform: { github: 0, jira: 0, ado: 0 },
|
|
222
|
+
avgDurationMs: 0,
|
|
223
|
+
};
|
|
224
|
+
let totalDuration = 0;
|
|
225
|
+
let durationCount = 0;
|
|
226
|
+
for (const entry of entries) {
|
|
227
|
+
if (entry.result === 'success')
|
|
228
|
+
stats.success++;
|
|
229
|
+
else if (entry.result === 'denied')
|
|
230
|
+
stats.denied++;
|
|
231
|
+
else if (entry.result === 'error')
|
|
232
|
+
stats.errors++;
|
|
233
|
+
stats.byPlatform[entry.platform]++;
|
|
234
|
+
if (entry.durationMs) {
|
|
235
|
+
totalDuration += entry.durationMs;
|
|
236
|
+
durationCount++;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
stats.avgDurationMs = durationCount > 0 ? Math.round(totalDuration / durationCount) : 0;
|
|
240
|
+
return stats;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Clean up old log files based on retention policy
|
|
244
|
+
*/
|
|
245
|
+
async cleanup() {
|
|
246
|
+
const cutoffDate = new Date();
|
|
247
|
+
cutoffDate.setDate(cutoffDate.getDate() - this.retentionDays);
|
|
248
|
+
const cutoffStr = cutoffDate.toISOString().split('T')[0];
|
|
249
|
+
const files = await this.getLogFiles();
|
|
250
|
+
let deletedCount = 0;
|
|
251
|
+
for (const file of files) {
|
|
252
|
+
// Extract date from filename: audit-YYYY-MM-DD.jsonl
|
|
253
|
+
const match = path.basename(file).match(/audit-(\d{4}-\d{2}-\d{2})\.jsonl/);
|
|
254
|
+
if (match && match[1] < cutoffStr) {
|
|
255
|
+
try {
|
|
256
|
+
await fs.unlink(file);
|
|
257
|
+
deletedCount++;
|
|
258
|
+
this.logger.debug(`Deleted old audit log: ${path.basename(file)}`);
|
|
259
|
+
}
|
|
260
|
+
catch (error) {
|
|
261
|
+
this.logger.debug(`Failed to delete audit log: ${file}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return deletedCount;
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Write an entry to the log file
|
|
269
|
+
*/
|
|
270
|
+
async log(entry) {
|
|
271
|
+
if (!this.enabled) {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
// Ensure directory exists
|
|
276
|
+
await fs.mkdir(this.logDir, { recursive: true });
|
|
277
|
+
// Get current file path (may rotate on new day)
|
|
278
|
+
const filePath = await this.getOrRotateFilePath();
|
|
279
|
+
// Append entry as JSONL
|
|
280
|
+
const line = JSON.stringify(entry) + '\n';
|
|
281
|
+
await fs.appendFile(filePath, line, 'utf-8');
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
// Don't fail the sync operation if logging fails
|
|
285
|
+
this.logger.debug(`Failed to write audit log: ${error}`);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* Get current file path, rotating if needed
|
|
290
|
+
*/
|
|
291
|
+
async getOrRotateFilePath() {
|
|
292
|
+
const today = new Date().toISOString().split('T')[0];
|
|
293
|
+
// Check if we need a new file (new day)
|
|
294
|
+
if (today !== this.currentDate) {
|
|
295
|
+
this.currentDate = today;
|
|
296
|
+
this.currentFilePath = this.getLogPath();
|
|
297
|
+
}
|
|
298
|
+
// Check file size for rotation
|
|
299
|
+
if (this.currentFilePath) {
|
|
300
|
+
try {
|
|
301
|
+
const stats = await fs.stat(this.currentFilePath);
|
|
302
|
+
if (stats.size >= this.maxFileSizeBytes) {
|
|
303
|
+
// Rotate: add sequence number
|
|
304
|
+
const basePath = this.currentFilePath.replace('.jsonl', '');
|
|
305
|
+
let seq = 1;
|
|
306
|
+
while (true) {
|
|
307
|
+
const rotatedPath = `${basePath}-${seq}.jsonl`;
|
|
308
|
+
try {
|
|
309
|
+
await fs.access(rotatedPath);
|
|
310
|
+
seq++;
|
|
311
|
+
}
|
|
312
|
+
catch {
|
|
313
|
+
// File doesn't exist, use this name
|
|
314
|
+
this.currentFilePath = rotatedPath;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
catch {
|
|
321
|
+
// File doesn't exist yet, that's fine
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return this.currentFilePath;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=sync-audit-logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-audit-logger.js","sourceRoot":"","sources":["../../../../src/core/sync/sync-audit-logger.ts"],"names":[],"mappings":"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"}
|