@probelabs/visor 0.1.149-ee → 0.1.150
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/README.md +52 -1
- package/dist/909.index.js +27117 -0
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/docs/assistant-workflows.md +805 -0
- package/dist/docs/event-triggers.md +25 -0
- package/dist/docs/scheduler.md +156 -0
- package/dist/docs/slack-integration.md +48 -0
- package/dist/enterprise/scheduler/knex-store.d.ts +7 -1
- package/dist/enterprise/scheduler/knex-store.d.ts.map +1 -1
- package/dist/examples/code-talk-as-tool.yaml +76 -0
- package/dist/examples/code-talk-workflow.yaml +68 -0
- package/dist/examples/intent-router-workflow.yaml +66 -0
- package/dist/examples/slack-message-triggers.yaml +270 -0
- package/dist/generated/config-schema.d.ts +102 -7
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/generated/config-schema.json +116 -7
- package/dist/git-repository-analyzer.d.ts +8 -0
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/index.js +4051 -6453
- package/dist/output/traces/run-2026-03-03T20-21-24-501Z.ndjson +138 -0
- package/dist/output/traces/run-2026-03-03T20-22-08-701Z.ndjson +2197 -0
- package/dist/scheduler/message-trigger.d.ts +47 -0
- package/dist/scheduler/message-trigger.d.ts.map +1 -0
- package/dist/scheduler/schedule-store.d.ts +31 -1
- package/dist/scheduler/schedule-store.d.ts.map +1 -1
- package/dist/scheduler/schedule-tool.d.ts +17 -1
- package/dist/scheduler/schedule-tool.d.ts.map +1 -1
- package/dist/scheduler/store/sqlite-store.d.ts +7 -1
- package/dist/scheduler/store/sqlite-store.d.ts.map +1 -1
- package/dist/scheduler/store/types.d.ts +45 -0
- package/dist/scheduler/store/types.d.ts.map +1 -1
- package/dist/sdk/{check-provider-registry-LVLC4EPF.mjs → check-provider-registry-6D5CAX44.mjs} +6 -6
- package/dist/sdk/{check-provider-registry-TJAJVSMY.mjs → check-provider-registry-RRYBG6AU.mjs} +6 -6
- package/dist/sdk/check-provider-registry-YFC5KSJY.mjs +29 -0
- package/dist/sdk/{chunk-V6GI4U2M.mjs → chunk-AADKUA6L.mjs} +585 -29
- package/dist/sdk/{chunk-NYFTDVG5.mjs.map → chunk-AADKUA6L.mjs.map} +1 -1
- package/dist/sdk/{chunk-NYFTDVG5.mjs → chunk-CKDFGNF4.mjs} +586 -30
- package/dist/sdk/{chunk-V6GI4U2M.mjs.map → chunk-CKDFGNF4.mjs.map} +1 -1
- package/dist/sdk/{chunk-YYZAN5NK.mjs → chunk-FYK2DJK6.mjs} +106 -9
- package/dist/sdk/chunk-FYK2DJK6.mjs.map +1 -0
- package/dist/sdk/{chunk-RJLJUTSU.mjs → chunk-FZEQ744M.mjs} +2 -2
- package/dist/sdk/{chunk-CISJ6DJW.mjs → chunk-GIAN7HCT.mjs} +3 -3
- package/dist/sdk/chunk-GLROSEYJ.mjs +1502 -0
- package/dist/sdk/chunk-GLROSEYJ.mjs.map +1 -0
- package/dist/sdk/chunk-H4HFH7HH.mjs +443 -0
- package/dist/sdk/chunk-H4HFH7HH.mjs.map +1 -0
- package/dist/sdk/{chunk-62TNF5PJ.mjs → chunk-PETLPNRA.mjs} +2 -2
- package/dist/sdk/{chunk-62TNF5PJ.mjs.map → chunk-PETLPNRA.mjs.map} +1 -1
- package/dist/sdk/chunk-SWO4W57C.mjs +739 -0
- package/dist/sdk/chunk-SWO4W57C.mjs.map +1 -0
- package/dist/sdk/chunk-YY3KADY2.mjs +43715 -0
- package/dist/sdk/chunk-YY3KADY2.mjs.map +1 -0
- package/dist/sdk/{config-KQH254CA.mjs → config-MTEIGCOQ.mjs} +2 -2
- package/dist/sdk/{failure-condition-evaluator-IVCTD4BZ.mjs → failure-condition-evaluator-P6QUFLIN.mjs} +3 -3
- package/dist/sdk/failure-condition-evaluator-XV2ZMFFY.mjs +17 -0
- package/dist/sdk/{git-repository-analyzer-QFMW6WIS.mjs → git-repository-analyzer-TWNJUN42.mjs} +34 -3
- package/dist/sdk/git-repository-analyzer-TWNJUN42.mjs.map +1 -0
- package/dist/sdk/{github-frontend-DFT5G32K.mjs → github-frontend-A2R7D4N6.mjs} +3 -3
- package/dist/sdk/github-frontend-GSB2P5PE.mjs +1368 -0
- package/dist/sdk/github-frontend-GSB2P5PE.mjs.map +1 -0
- package/dist/sdk/{host-H7IX4GBK.mjs → host-CLPM2WVQ.mjs} +2 -2
- package/dist/sdk/{host-NZXGBBJI.mjs → host-MR7L57QI.mjs} +2 -2
- package/dist/sdk/{routing-LU5PAREW.mjs → routing-KLVK2MJZ.mjs} +4 -4
- package/dist/sdk/routing-V3MYZAOH.mjs +25 -0
- package/dist/sdk/{schedule-tool-4U32CSH6.mjs → schedule-tool-HYM55K3H.mjs} +6 -6
- package/dist/sdk/{schedule-tool-NX75VKGA.mjs → schedule-tool-NYLNJUEW.mjs} +6 -6
- package/dist/sdk/schedule-tool-PRXFAV4K.mjs +35 -0
- package/dist/sdk/{schedule-tool-handler-6S2DNP26.mjs → schedule-tool-handler-EFQIYD3W.mjs} +6 -6
- package/dist/sdk/{schedule-tool-handler-KKN7XJYT.mjs → schedule-tool-handler-HVWCEGQ6.mjs} +6 -6
- package/dist/sdk/schedule-tool-handler-HVWCEGQ6.mjs.map +1 -0
- package/dist/sdk/schedule-tool-handler-M5YI573R.mjs +39 -0
- package/dist/sdk/schedule-tool-handler-M5YI573R.mjs.map +1 -0
- package/dist/sdk/sdk.d.mts +38 -1
- package/dist/sdk/sdk.d.ts +38 -1
- package/dist/sdk/sdk.js +964 -1538
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +5 -5
- package/dist/sdk/{trace-helpers-6ROJR7N3.mjs → trace-helpers-6NSZBC35.mjs} +2 -2
- package/dist/sdk/trace-helpers-6NSZBC35.mjs.map +1 -0
- package/dist/sdk/trace-helpers-TORF3JD5.mjs +25 -0
- package/dist/sdk/trace-helpers-TORF3JD5.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-AGZ5JY2I.mjs → workflow-check-provider-3F6CBHVD.mjs} +6 -6
- package/dist/sdk/workflow-check-provider-3F6CBHVD.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-FDNGOBBG.mjs → workflow-check-provider-OXHMLICJ.mjs} +6 -6
- package/dist/sdk/workflow-check-provider-OXHMLICJ.mjs.map +1 -0
- package/dist/sdk/workflow-check-provider-XLH7N4FV.mjs +29 -0
- package/dist/sdk/workflow-check-provider-XLH7N4FV.mjs.map +1 -0
- package/dist/slack/socket-runner.d.ts +14 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
- package/dist/test-runner/fixture-loader.d.ts +1 -1
- package/dist/test-runner/fixture-loader.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/traces/run-2026-03-03T20-21-24-501Z.ndjson +138 -0
- package/dist/traces/run-2026-03-03T20-22-08-701Z.ndjson +2197 -0
- package/dist/types/config.d.ts +38 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/utils/workspace-manager.d.ts +4 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/dist/utils/worktree-manager.d.ts +15 -1
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/package.json +2 -2
- package/dist/sdk/chunk-YYZAN5NK.mjs.map +0 -1
- package/dist/sdk/git-repository-analyzer-QFMW6WIS.mjs.map +0 -1
- package/dist/sdk/knex-store-HPXJILBL.mjs +0 -411
- package/dist/sdk/knex-store-HPXJILBL.mjs.map +0 -1
- package/dist/sdk/loader-YSRMVXC3.mjs +0 -89
- package/dist/sdk/loader-YSRMVXC3.mjs.map +0 -1
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs +0 -655
- package/dist/sdk/opa-policy-engine-S2S2ULEI.mjs.map +0 -1
- package/dist/sdk/validator-XTZJZZJH.mjs +0 -134
- package/dist/sdk/validator-XTZJZZJH.mjs.map +0 -1
- /package/dist/sdk/{check-provider-registry-LVLC4EPF.mjs.map → check-provider-registry-6D5CAX44.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-TJAJVSMY.mjs.map → check-provider-registry-RRYBG6AU.mjs.map} +0 -0
- /package/dist/sdk/{config-KQH254CA.mjs.map → check-provider-registry-YFC5KSJY.mjs.map} +0 -0
- /package/dist/sdk/{chunk-RJLJUTSU.mjs.map → chunk-FZEQ744M.mjs.map} +0 -0
- /package/dist/sdk/{chunk-CISJ6DJW.mjs.map → chunk-GIAN7HCT.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-IVCTD4BZ.mjs.map → config-MTEIGCOQ.mjs.map} +0 -0
- /package/dist/sdk/{routing-LU5PAREW.mjs.map → failure-condition-evaluator-P6QUFLIN.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-4U32CSH6.mjs.map → failure-condition-evaluator-XV2ZMFFY.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-DFT5G32K.mjs.map → github-frontend-A2R7D4N6.mjs.map} +0 -0
- /package/dist/sdk/{host-H7IX4GBK.mjs.map → host-CLPM2WVQ.mjs.map} +0 -0
- /package/dist/sdk/{host-NZXGBBJI.mjs.map → host-MR7L57QI.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-NX75VKGA.mjs.map → routing-KLVK2MJZ.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-6S2DNP26.mjs.map → routing-V3MYZAOH.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-KKN7XJYT.mjs.map → schedule-tool-HYM55K3H.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-6ROJR7N3.mjs.map → schedule-tool-NYLNJUEW.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-AGZ5JY2I.mjs.map → schedule-tool-PRXFAV4K.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-FDNGOBBG.mjs.map → schedule-tool-handler-EFQIYD3W.mjs.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/file-exclusion.ts","../../src/git-repository-analyzer.ts"],"sourcesContent":["import ignore from 'ignore';\nimport * as fs from 'fs';\nimport * as path from 'path';\n\n/**\n * Default exclusion patterns for common build artifacts and dependencies.\n * These can be overridden by providing custom patterns to the constructor.\n */\nconst DEFAULT_EXCLUSION_PATTERNS = [\n 'dist/',\n 'build/',\n '.next/',\n 'out/',\n 'node_modules/',\n 'coverage/',\n '.turbo/',\n 'bundled/',\n];\n\n/**\n * Shared utility for filtering files based on .gitignore patterns\n *\n * Design Decision: Synchronous I/O in Constructor\n * ------------------------------------------------\n * This class intentionally uses synchronous file I/O in the constructor for the following reasons:\n *\n * 1. **Initialization Context**: The constructor is called once during application startup,\n * not in request handling or performance-critical paths.\n *\n * 2. **Small File Sizes**: .gitignore files are typically <10KB. Even in large monorepos,\n * they rarely exceed 100KB. Reading such small files synchronously has negligible impact.\n *\n * 3. **Immediate Availability**: The exclusion patterns must be ready immediately for use.\n * Asynchronous initialization would require either:\n * - Async factory method (adds API complexity)\n * - Lazy loading (race conditions, repeated checks)\n * - Promise-based initialization (complicates usage across codebase)\n *\n * 4. **Simplicity**: Synchronous loading keeps the API simple and prevents async contagion\n * throughout the codebase. Methods like shouldExcludeFile() remain synchronous.\n *\n * 5. **No DoS Risk**: The file reading happens exactly once per instance during construction.\n * Attackers cannot trigger repeated synchronous reads.\n *\n * 6. **Consistency**: This follows the same pattern as other configuration loaders in Node.js\n * ecosystem (e.g., require(), cosmiconfig's sync mode).\n *\n * Alternative Considered: Async factory pattern would add complexity without meaningful benefit\n * given the usage patterns and file sizes involved.\n */\nexport class FileExclusionHelper {\n private gitignore: ReturnType<typeof ignore> | null = null;\n private workingDirectory: string;\n\n /**\n * @param workingDirectory - Directory to search for .gitignore\n * @param additionalPatterns - Additional patterns to include (optional, defaults to common build artifacts)\n */\n constructor(\n workingDirectory: string = process.cwd(),\n additionalPatterns: string[] | null = DEFAULT_EXCLUSION_PATTERNS\n ) {\n // Validate and normalize workingDirectory to prevent path traversal\n const normalizedPath = path.resolve(workingDirectory);\n\n // Ensure path doesn't contain suspicious patterns after normalization\n // Check for null bytes which could be used for injection\n if (normalizedPath.includes('\\0')) {\n throw new Error('Invalid workingDirectory: contains null bytes');\n }\n\n this.workingDirectory = normalizedPath;\n\n // Load gitignore synchronously during construction\n // This is acceptable because:\n // 1. Constructor is called once during initialization\n // 2. .gitignore files are typically small (<10KB)\n // 3. Synchronous loading ensures patterns are ready immediately\n // 4. Avoids async constructor complexity\n this.loadGitignore(additionalPatterns);\n }\n\n /**\n * Load .gitignore patterns from the working directory (called once in constructor)\n * @param additionalPatterns - Additional patterns to add to gitignore rules\n */\n private loadGitignore(additionalPatterns: string[] | null): void {\n // Resolve both paths to absolute, normalized forms\n const gitignorePath = path.resolve(this.workingDirectory, '.gitignore');\n const resolvedWorkingDir = path.resolve(this.workingDirectory);\n\n try {\n // Robust path validation using path.relative()\n // This handles symlinks and edge cases better than string comparison\n const relativePath = path.relative(resolvedWorkingDir, gitignorePath);\n\n // Security check: ensure .gitignore is within working directory\n // Reject if:\n // - Starts with '..' (parent directory)\n // - Is an absolute path (should be relative after path.relative())\n if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {\n throw new Error('Invalid gitignore path: path traversal detected');\n }\n\n // Additionally verify it's exactly '.gitignore' (no subdirectories)\n if (relativePath !== '.gitignore') {\n throw new Error('Invalid gitignore path: must be .gitignore in working directory');\n }\n\n this.gitignore = ignore();\n\n // Add additional patterns first (lower priority)\n if (additionalPatterns && additionalPatterns.length > 0) {\n this.gitignore.add(additionalPatterns);\n }\n\n // Load and add .gitignore patterns (higher priority)\n if (fs.existsSync(gitignorePath)) {\n const rawContent = fs.readFileSync(gitignorePath, 'utf8');\n\n // Comprehensive sanitization to prevent injection attacks\n const gitignoreContent = rawContent\n .replace(/[\\r\\n]+/g, '\\n') // Normalize line endings first\n .replace(/[\\x00-\\x09\\x0B-\\x1F\\x7F]/g, '') // Remove control chars except \\n (0x0A)\n .split('\\n')\n .filter(line => line.length < 1000) // Reject extremely long lines that could cause DoS\n .join('\\n')\n .trim();\n\n this.gitignore.add(gitignoreContent);\n if (process.env.VISOR_DEBUG === 'true') {\n console.error('✅ Loaded .gitignore patterns for file filtering');\n }\n } else if (additionalPatterns && additionalPatterns.length > 0) {\n // Always emit a user-visible warning so callers can assert this behavior in tests\n console.error('No .gitignore found, using default exclusion patterns');\n console.warn('No .gitignore found, using default exclusion patterns');\n }\n } catch (error) {\n // Always emit a warning with the error for visibility and tests\n console.warn('Failed to load .gitignore:', error instanceof Error ? error.message : error);\n }\n }\n\n /**\n * Check if a file should be excluded based on .gitignore patterns\n */\n shouldExcludeFile(filename: string): boolean {\n // Check against .gitignore patterns if loaded\n if (this.gitignore) {\n return this.gitignore.ignores(filename);\n }\n\n return false;\n }\n}\n","import { simpleGit, SimpleGit, type DefaultLogFields, type ListLogLine } from 'simple-git';\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport { PRInfo, PRDiff } from './pr-analyzer';\nimport { FileExclusionHelper } from './utils/file-exclusion';\n\nexport interface GitFileChange {\n filename: string;\n status: 'added' | 'removed' | 'modified' | 'renamed';\n additions: number;\n deletions: number;\n changes: number;\n content?: string;\n patch?: string;\n truncated?: boolean;\n}\n\n// Maximum patch size in bytes (50KB) - helps prevent token limit issues\nconst MAX_PATCH_SIZE = 50 * 1024;\n\nexport interface GitRepositoryInfo {\n title: string;\n body: string;\n author: string;\n base: string;\n head: string;\n files: GitFileChange[];\n totalAdditions: number;\n totalDeletions: number;\n isGitRepository: boolean;\n workingDirectory: string;\n}\n\nexport class GitRepositoryAnalyzer {\n private git: SimpleGit;\n private cwd: string;\n private fileExclusionHelper: FileExclusionHelper;\n\n constructor(workingDirectory: string = process.cwd()) {\n this.cwd = workingDirectory;\n this.git = simpleGit(workingDirectory);\n this.fileExclusionHelper = new FileExclusionHelper(workingDirectory);\n }\n\n /**\n * Analyze the current git repository state and return data compatible with PRInfo interface\n */\n async analyzeRepository(\n includeContext: boolean = true,\n enableBranchDiff: boolean = false\n ): Promise<GitRepositoryInfo> {\n // Check if we're in a git repository\n const isRepo = await this.isGitRepository();\n if (!isRepo) {\n return this.createEmptyRepositoryInfo('Not a git repository');\n }\n\n try {\n // Get current branch and status\n const [status, currentBranch, baseBranch] = await Promise.all([\n this.git.status(),\n this.getCurrentBranch(),\n this.getBaseBranch(),\n ]);\n\n // Determine if we're on a feature branch\n const isFeatureBranch =\n currentBranch !== baseBranch && currentBranch !== 'main' && currentBranch !== 'master';\n\n // Get uncommitted changes first\n let uncommittedFiles = await this.getUncommittedChanges(includeContext);\n\n // If branch diff is explicitly enabled, use branch diff (ignoring uncommitted changes)\n // Otherwise, if on a feature branch with no uncommitted changes AND branch diff is enabled, get diff vs base branch\n if (isFeatureBranch && includeContext && enableBranchDiff) {\n if (uncommittedFiles.length > 0) {\n console.error(`📊 Feature branch detected: ${currentBranch}`);\n console.error(\n `⚠️ Ignoring ${uncommittedFiles.length} uncommitted file(s) due to --analyze-branch-diff flag`\n );\n } else {\n console.error(`📊 Feature branch detected: ${currentBranch}`);\n }\n console.error(\n `📂 Analyzing diff vs ${baseBranch} (${uncommittedFiles.length > 0 ? 'forced by --analyze-branch-diff' : 'auto-enabled for code-review schemas'})`\n );\n uncommittedFiles = await this.getBranchDiff(baseBranch, includeContext);\n } else if (uncommittedFiles.length > 0) {\n console.error(`📝 Analyzing uncommitted changes (${uncommittedFiles.length} files)`);\n }\n\n // Get recent commit info (handle repos with no commits)\n let lastCommit: (ListLogLine & DefaultLogFields) | null = null;\n try {\n const recentCommits = await this.git.log({ maxCount: 1 });\n lastCommit = recentCommits.latest;\n } catch {\n // Repository has no commits yet - this is OK\n console.error('📝 Repository has no commits yet, analyzing uncommitted changes');\n }\n\n // Get author from git config if no commits exist\n let author = lastCommit?.author_name;\n if (!author) {\n try {\n // Read ONLY repository-local config to avoid leaking global user identity into tests\n const [userName, userEmail] = await Promise.all([\n this.git.raw(['config', '--local', 'user.name']).catch(() => null),\n this.git.raw(['config', '--local', 'user.email']).catch(() => null),\n ]);\n author = userName?.trim() || userEmail?.trim() || 'unknown';\n } catch {\n author = 'unknown';\n }\n }\n\n // Create repository info\n const repositoryInfo: GitRepositoryInfo = {\n title: this.generateTitle(status, currentBranch),\n body: this.generateDescription(status, lastCommit),\n author,\n base: baseBranch,\n head: currentBranch,\n files: uncommittedFiles,\n totalAdditions: uncommittedFiles.reduce((sum, file) => sum + file.additions, 0),\n totalDeletions: uncommittedFiles.reduce((sum, file) => sum + file.deletions, 0),\n isGitRepository: true,\n workingDirectory: this.cwd,\n };\n\n return repositoryInfo;\n } catch (error) {\n // Don't log the full error object to avoid confusing stack traces\n const errorMessage = error instanceof Error ? error.message : 'Unknown error';\n console.error('Error analyzing git repository:', errorMessage);\n return this.createEmptyRepositoryInfo('Error analyzing git repository');\n }\n }\n\n /**\n * Convert GitRepositoryInfo to PRInfo format for compatibility with existing PRReviewer\n */\n toPRInfo(repositoryInfo: GitRepositoryInfo, includeContext: boolean = true): PRInfo {\n const files = repositoryInfo.files.map(\n (file): PRDiff => ({\n filename: file.filename,\n additions: file.additions,\n deletions: file.deletions,\n changes: file.changes,\n patch: includeContext ? file.patch : undefined,\n status: file.status,\n })\n );\n\n // Generate fullDiff from patches if includeContext is true\n let fullDiff: string | undefined;\n if (includeContext) {\n fullDiff = files\n .filter(file => file.patch)\n .map(file => `--- ${file.filename}\\n${file.patch}`)\n .join('\\n\\n');\n }\n\n return {\n number: 0, // Local analysis doesn't have PR number\n title: repositoryInfo.title,\n body: repositoryInfo.body,\n author: repositoryInfo.author,\n base: repositoryInfo.base,\n head: repositoryInfo.head,\n files,\n totalAdditions: repositoryInfo.totalAdditions,\n totalDeletions: repositoryInfo.totalDeletions,\n fullDiff,\n };\n }\n\n private async isGitRepository(): Promise<boolean> {\n try {\n await this.git.checkIsRepo();\n return true;\n } catch {\n return false;\n }\n }\n\n private async getCurrentBranch(): Promise<string> {\n try {\n const branchSummary = await this.git.branch();\n return branchSummary.current || 'unknown';\n } catch {\n return 'unknown';\n }\n }\n\n private async getBaseBranch(): Promise<string> {\n try {\n // Try to get the default branch from remote\n const branches = await this.git.branch(['-r']);\n const mainBranches = ['origin/main', 'origin/master', 'origin/develop'];\n\n for (const mainBranch of mainBranches) {\n if (branches.all.includes(mainBranch)) {\n return mainBranch.replace('origin/', '');\n }\n }\n\n // Fallback to main/master\n return 'main';\n } catch {\n return 'main';\n }\n }\n\n /**\n * Truncate a patch if it exceeds MAX_PATCH_SIZE\n */\n private truncatePatch(patch: string, filename: string): { patch: string; truncated: boolean } {\n const patchSize = Buffer.byteLength(patch, 'utf8');\n\n if (patchSize <= MAX_PATCH_SIZE) {\n return { patch, truncated: false };\n }\n\n // Truncate to MAX_PATCH_SIZE and add a notice\n const truncated = patch.substring(0, MAX_PATCH_SIZE);\n const truncatedPatch = `${truncated}\\n\\n... [TRUNCATED: Diff too large (${(patchSize / 1024).toFixed(1)}KB), showing first ${(MAX_PATCH_SIZE / 1024).toFixed(0)}KB] ...`;\n\n console.error(\n `⚠️ Truncated diff for ${filename} (${(patchSize / 1024).toFixed(1)}KB → ${(MAX_PATCH_SIZE / 1024).toFixed(0)}KB)`\n );\n\n return { patch: truncatedPatch, truncated: true };\n }\n\n private async getRemoteInfo(): Promise<{ name: string; url: string } | null> {\n try {\n const remotes = await this.git.getRemotes(true);\n const origin = remotes.find(r => r.name === 'origin');\n return origin\n ? { name: origin.name, url: origin.refs.fetch || origin.refs.push || '' }\n : null;\n } catch {\n return null;\n }\n }\n\n private async getUncommittedChanges(includeContext: boolean = true): Promise<GitFileChange[]> {\n try {\n const status = await this.git.status();\n const changes: GitFileChange[] = [];\n\n // Process different types of changes\n const fileChanges = [\n ...status.created.map(f => ({ file: f, status: 'added' as const })),\n ...status.deleted.map(f => ({ file: f, status: 'removed' as const })),\n ...status.modified.map(f => ({ file: f, status: 'modified' as const })),\n ...status.renamed.map(f => ({\n file: typeof f === 'string' ? f : f.to || f.from,\n status: 'renamed' as const,\n })),\n ];\n\n for (const { file, status } of fileChanges) {\n // Skip files that should be excluded from analysis\n // FileExclusionHelper uses .gitignore patterns, which is sufficient\n if (this.fileExclusionHelper.shouldExcludeFile(file)) {\n console.error(`⏭️ Skipping excluded file: ${file}`);\n continue;\n }\n\n const filePath = path.join(this.cwd, file);\n const fileChange = await this.analyzeFileChange(file, status, filePath, includeContext);\n changes.push(fileChange);\n }\n\n return changes;\n } catch (error) {\n console.error('Error getting uncommitted changes:', error);\n return [];\n }\n }\n\n /**\n * Get diff between current branch and base branch (for feature branch analysis)\n */\n private async getBranchDiff(\n baseBranch: string,\n includeContext: boolean = true\n ): Promise<GitFileChange[]> {\n try {\n // Get the list of changed files between base and current branch\n const diffSummary = await this.git.diffSummary([baseBranch]);\n const changes: GitFileChange[] = [];\n\n if (!diffSummary || !diffSummary.files) {\n return [];\n }\n\n for (const file of diffSummary.files) {\n // Skip files that should be excluded from analysis\n // FileExclusionHelper uses .gitignore patterns, which is sufficient\n if (this.fileExclusionHelper.shouldExcludeFile(file.file)) {\n console.error(`⏭️ Skipping excluded file: ${file.file}`);\n continue;\n }\n\n // Handle different file types (binary files don't have insertions/deletions)\n const isBinary = 'binary' in file && file.binary;\n const insertions = 'insertions' in file ? file.insertions : 0;\n const deletions = 'deletions' in file ? file.deletions : 0;\n const fileChanges = 'changes' in file ? file.changes : 0;\n\n // Determine status based on insertions/deletions\n let status: 'added' | 'removed' | 'modified' | 'renamed';\n if (isBinary) {\n status = 'modified';\n } else if (insertions > 0 && deletions === 0) {\n status = 'added';\n } else if (insertions === 0 && deletions > 0) {\n status = 'removed';\n } else {\n status = 'modified';\n }\n\n // Get the actual diff patch if needed\n let patch: string | undefined;\n let truncated = false;\n if (includeContext && !isBinary) {\n try {\n const rawPatch = await this.git.diff([baseBranch, '--', file.file]);\n if (rawPatch) {\n const result = this.truncatePatch(rawPatch, file.file);\n patch = result.patch;\n truncated = result.truncated;\n }\n } catch {\n // Ignore diff errors for specific files\n }\n }\n\n const fileChange: GitFileChange = {\n filename: file.file,\n additions: insertions,\n deletions: deletions,\n changes: fileChanges,\n status,\n patch,\n truncated,\n };\n\n changes.push(fileChange);\n }\n\n return changes;\n } catch (error) {\n console.error('Error getting branch diff:', error);\n return [];\n }\n }\n\n private async analyzeFileChange(\n filename: string,\n status: 'added' | 'removed' | 'modified' | 'renamed',\n filePath: string,\n includeContext: boolean = true\n ): Promise<GitFileChange> {\n let additions = 0;\n let deletions = 0;\n let patch: string | undefined;\n let content: string | undefined;\n let truncated = false;\n\n try {\n // Get diff for the file if it exists and is not binary\n if (includeContext && status !== 'added' && fs.existsSync(filePath)) {\n const diff = await this.git.diff(['--', filename]).catch(() => '');\n if (diff) {\n const result = this.truncatePatch(diff, filename);\n patch = result.patch;\n truncated = result.truncated;\n // Count additions and deletions from diff\n const lines = diff.split('\\n');\n additions = lines.filter(line => line.startsWith('+')).length;\n deletions = lines.filter(line => line.startsWith('-')).length;\n }\n } else if (status !== 'added' && fs.existsSync(filePath)) {\n // If not including context, still count changes for statistics\n const diff = await this.git.diff(['--', filename]).catch(() => '');\n if (diff) {\n const lines = diff.split('\\n');\n additions = lines.filter(line => line.startsWith('+')).length;\n deletions = lines.filter(line => line.startsWith('-')).length;\n }\n }\n\n // For added files\n if (status === 'added' && fs.existsSync(filePath)) {\n try {\n const stats = fs.statSync(filePath);\n if (stats.isFile() && stats.size < 1024 * 1024) {\n // Skip files larger than 1MB\n if (includeContext) {\n content = fs.readFileSync(filePath, 'utf8');\n const result = this.truncatePatch(content, filename);\n patch = result.patch; // For new files, the entire content is the \"patch\"\n truncated = result.truncated;\n }\n // Always count additions for statistics\n const fileContent = includeContext ? content : fs.readFileSync(filePath, 'utf8');\n additions = fileContent!.split('\\n').length;\n }\n } catch {\n // Skip binary or unreadable files\n }\n }\n\n // For removed files, we can't easily count the lines without the previous version\n if (status === 'removed') {\n deletions = 1; // Placeholder - in real git we'd need the previous version\n }\n } catch (error) {\n console.error(`Error analyzing file change for ${filename}:`, error);\n }\n\n return {\n filename,\n status,\n additions,\n deletions,\n changes: additions + deletions,\n content,\n patch,\n truncated,\n };\n }\n\n private generateTitle(status: import('simple-git').StatusResult, branch: string): string {\n if (status.files.length === 0) {\n return `Local Analysis: ${branch} (No changes)`;\n }\n\n const changeTypes = [];\n if (status.created.length > 0) changeTypes.push(`${status.created.length} added`);\n if (status.modified.length > 0) changeTypes.push(`${status.modified.length} modified`);\n if (status.deleted.length > 0) changeTypes.push(`${status.deleted.length} deleted`);\n if (status.renamed.length > 0) changeTypes.push(`${status.renamed.length} renamed`);\n\n return `Local Analysis: ${branch} (${changeTypes.join(', ')})`;\n }\n\n private generateDescription(\n status: import('simple-git').StatusResult,\n lastCommit: import('simple-git').DefaultLogFields | null\n ): string {\n let description = `Analysis of local git repository working directory.\\n\\n`;\n\n if (lastCommit) {\n description += `**Last Commit:** ${lastCommit.message}\\n`;\n description += `**Author:** ${lastCommit.author_name} <${lastCommit.author_email}>\\n`;\n description += `**Date:** ${lastCommit.date}\\n\\n`;\n }\n\n if (status.files.length === 0) {\n description += `**Status:** Working directory is clean - no uncommitted changes found.\\n`;\n } else {\n description += `**Changes Summary:**\\n`;\n description += `- Files to be committed: ${status.staged.length}\\n`;\n description += `- Modified files: ${status.modified.length}\\n`;\n description += `- Untracked files: ${status.not_added.length}\\n`;\n\n if (status.conflicted.length > 0) {\n description += `- Conflicted files: ${status.conflicted.length}\\n`;\n }\n }\n\n return description;\n }\n\n private createEmptyRepositoryInfo(reason: string): GitRepositoryInfo {\n return {\n title: `Local Analysis: ${reason}`,\n body: `Unable to analyze repository: ${reason}`,\n author: 'system',\n base: 'main',\n head: 'HEAD',\n files: [],\n totalAdditions: 0,\n totalDeletions: 0,\n isGitRepository: false,\n workingDirectory: this.cwd,\n };\n }\n}\n"],"mappings":";;;;;AAAA,OAAO,YAAY;AACnB,YAAY,QAAQ;AACpB,YAAY,UAAU;AAFtB,IAQM,4BA0CO;AAlDb;AAAA;AAAA;AAQA,IAAM,6BAA6B;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAiCO,IAAM,sBAAN,MAA0B;AAAA,MACvB,YAA8C;AAAA,MAC9C;AAAA;AAAA;AAAA;AAAA;AAAA,MAMR,YACE,mBAA2B,QAAQ,IAAI,GACvC,qBAAsC,4BACtC;AAEA,cAAM,iBAAsB,aAAQ,gBAAgB;AAIpD,YAAI,eAAe,SAAS,IAAI,GAAG;AACjC,gBAAM,IAAI,MAAM,+CAA+C;AAAA,QACjE;AAEA,aAAK,mBAAmB;AAQxB,aAAK,cAAc,kBAAkB;AAAA,MACvC;AAAA;AAAA;AAAA;AAAA;AAAA,MAMQ,cAAc,oBAA2C;AAE/D,cAAM,gBAAqB,aAAQ,KAAK,kBAAkB,YAAY;AACtE,cAAM,qBAA0B,aAAQ,KAAK,gBAAgB;AAE7D,YAAI;AAGF,gBAAM,eAAoB,cAAS,oBAAoB,aAAa;AAMpE,cAAI,aAAa,WAAW,IAAI,KAAU,gBAAW,YAAY,GAAG;AAClE,kBAAM,IAAI,MAAM,iDAAiD;AAAA,UACnE;AAGA,cAAI,iBAAiB,cAAc;AACjC,kBAAM,IAAI,MAAM,iEAAiE;AAAA,UACnF;AAEA,eAAK,YAAY,OAAO;AAGxB,cAAI,sBAAsB,mBAAmB,SAAS,GAAG;AACvD,iBAAK,UAAU,IAAI,kBAAkB;AAAA,UACvC;AAGA,cAAO,cAAW,aAAa,GAAG;AAChC,kBAAM,aAAgB,gBAAa,eAAe,MAAM;AAGxD,kBAAM,mBAAmB,WACtB,QAAQ,YAAY,IAAI,EACxB,QAAQ,6BAA6B,EAAE,EACvC,MAAM,IAAI,EACV,OAAO,UAAQ,KAAK,SAAS,GAAI,EACjC,KAAK,IAAI,EACT,KAAK;AAER,iBAAK,UAAU,IAAI,gBAAgB;AACnC,gBAAI,QAAQ,IAAI,gBAAgB,QAAQ;AACtC,sBAAQ,MAAM,sDAAiD;AAAA,YACjE;AAAA,UACF,WAAW,sBAAsB,mBAAmB,SAAS,GAAG;AAE9D,oBAAQ,MAAM,uDAAuD;AACrE,oBAAQ,KAAK,uDAAuD;AAAA,UACtE;AAAA,QACF,SAAS,OAAO;AAEd,kBAAQ,KAAK,8BAA8B,iBAAiB,QAAQ,MAAM,UAAU,KAAK;AAAA,QAC3F;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,kBAAkB,UAA2B;AAE3C,YAAI,KAAK,WAAW;AAClB,iBAAO,KAAK,UAAU,QAAQ,QAAQ;AAAA,QACxC;AAEA,eAAO;AAAA,MACT;AAAA,IACF;AAAA;AAAA;;;AC3JA,SAAS,iBAAqE;AAC9E,YAAYA,WAAU;AACtB,YAAYC,SAAQ;AAFpB,IAkBM,gBAeO;AAjCb;AAAA;AAIA;AAcA,IAAM,iBAAiB,KAAK;AAerB,IAAM,wBAAN,MAA4B;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,MAER,YAAY,mBAA2B,QAAQ,IAAI,GAAG;AACpD,aAAK,MAAM;AACX,aAAK,MAAM,UAAU,gBAAgB;AACrC,aAAK,sBAAsB,IAAI,oBAAoB,gBAAgB;AAAA,MACrE;AAAA;AAAA;AAAA;AAAA,MAKA,MAAM,kBACJ,iBAA0B,MAC1B,mBAA4B,OACA;AAE5B,cAAM,SAAS,MAAM,KAAK,gBAAgB;AAC1C,YAAI,CAAC,QAAQ;AACX,iBAAO,KAAK,0BAA0B,sBAAsB;AAAA,QAC9D;AAEA,YAAI;AAEF,gBAAM,CAAC,QAAQ,eAAe,UAAU,IAAI,MAAM,QAAQ,IAAI;AAAA,YAC5D,KAAK,IAAI,OAAO;AAAA,YAChB,KAAK,iBAAiB;AAAA,YACtB,KAAK,cAAc;AAAA,UACrB,CAAC;AAGD,gBAAM,kBACJ,kBAAkB,cAAc,kBAAkB,UAAU,kBAAkB;AAGhF,cAAI,mBAAmB,MAAM,KAAK,sBAAsB,cAAc;AAItE,cAAI,mBAAmB,kBAAkB,kBAAkB;AACzD,gBAAI,iBAAiB,SAAS,GAAG;AAC/B,sBAAQ,MAAM,sCAA+B,aAAa,EAAE;AAC5D,sBAAQ;AAAA,gBACN,0BAAgB,iBAAiB,MAAM;AAAA,cACzC;AAAA,YACF,OAAO;AACL,sBAAQ,MAAM,sCAA+B,aAAa,EAAE;AAAA,YAC9D;AACA,oBAAQ;AAAA,cACN,+BAAwB,UAAU,KAAK,iBAAiB,SAAS,IAAI,oCAAoC,sCAAsC;AAAA,YACjJ;AACA,+BAAmB,MAAM,KAAK,cAAc,YAAY,cAAc;AAAA,UACxE,WAAW,iBAAiB,SAAS,GAAG;AACtC,oBAAQ,MAAM,4CAAqC,iBAAiB,MAAM,SAAS;AAAA,UACrF;AAGA,cAAI,aAAsD;AAC1D,cAAI;AACF,kBAAM,gBAAgB,MAAM,KAAK,IAAI,IAAI,EAAE,UAAU,EAAE,CAAC;AACxD,yBAAa,cAAc;AAAA,UAC7B,QAAQ;AAEN,oBAAQ,MAAM,wEAAiE;AAAA,UACjF;AAGA,cAAI,SAAS,YAAY;AACzB,cAAI,CAAC,QAAQ;AACX,gBAAI;AAEF,oBAAM,CAAC,UAAU,SAAS,IAAI,MAAM,QAAQ,IAAI;AAAA,gBAC9C,KAAK,IAAI,IAAI,CAAC,UAAU,WAAW,WAAW,CAAC,EAAE,MAAM,MAAM,IAAI;AAAA,gBACjE,KAAK,IAAI,IAAI,CAAC,UAAU,WAAW,YAAY,CAAC,EAAE,MAAM,MAAM,IAAI;AAAA,cACpE,CAAC;AACD,uBAAS,UAAU,KAAK,KAAK,WAAW,KAAK,KAAK;AAAA,YACpD,QAAQ;AACN,uBAAS;AAAA,YACX;AAAA,UACF;AAGA,gBAAM,iBAAoC;AAAA,YACxC,OAAO,KAAK,cAAc,QAAQ,aAAa;AAAA,YAC/C,MAAM,KAAK,oBAAoB,QAAQ,UAAU;AAAA,YACjD;AAAA,YACA,MAAM;AAAA,YACN,MAAM;AAAA,YACN,OAAO;AAAA,YACP,gBAAgB,iBAAiB,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,WAAW,CAAC;AAAA,YAC9E,gBAAgB,iBAAiB,OAAO,CAAC,KAAK,SAAS,MAAM,KAAK,WAAW,CAAC;AAAA,YAC9E,iBAAiB;AAAA,YACjB,kBAAkB,KAAK;AAAA,UACzB;AAEA,iBAAO;AAAA,QACT,SAAS,OAAO;AAEd,gBAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU;AAC9D,kBAAQ,MAAM,mCAAmC,YAAY;AAC7D,iBAAO,KAAK,0BAA0B,gCAAgC;AAAA,QACxE;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,SAAS,gBAAmC,iBAA0B,MAAc;AAClF,cAAM,QAAQ,eAAe,MAAM;AAAA,UACjC,CAAC,UAAkB;AAAA,YACjB,UAAU,KAAK;AAAA,YACf,WAAW,KAAK;AAAA,YAChB,WAAW,KAAK;AAAA,YAChB,SAAS,KAAK;AAAA,YACd,OAAO,iBAAiB,KAAK,QAAQ;AAAA,YACrC,QAAQ,KAAK;AAAA,UACf;AAAA,QACF;AAGA,YAAI;AACJ,YAAI,gBAAgB;AAClB,qBAAW,MACR,OAAO,UAAQ,KAAK,KAAK,EACzB,IAAI,UAAQ,OAAO,KAAK,QAAQ;AAAA,EAAK,KAAK,KAAK,EAAE,EACjD,KAAK,MAAM;AAAA,QAChB;AAEA,eAAO;AAAA,UACL,QAAQ;AAAA;AAAA,UACR,OAAO,eAAe;AAAA,UACtB,MAAM,eAAe;AAAA,UACrB,QAAQ,eAAe;AAAA,UACvB,MAAM,eAAe;AAAA,UACrB,MAAM,eAAe;AAAA,UACrB;AAAA,UACA,gBAAgB,eAAe;AAAA,UAC/B,gBAAgB,eAAe;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,MAEA,MAAc,kBAAoC;AAChD,YAAI;AACF,gBAAM,KAAK,IAAI,YAAY;AAC3B,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAc,mBAAoC;AAChD,YAAI;AACF,gBAAM,gBAAgB,MAAM,KAAK,IAAI,OAAO;AAC5C,iBAAO,cAAc,WAAW;AAAA,QAClC,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAc,gBAAiC;AAC7C,YAAI;AAEF,gBAAM,WAAW,MAAM,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;AAC7C,gBAAM,eAAe,CAAC,eAAe,iBAAiB,gBAAgB;AAEtE,qBAAW,cAAc,cAAc;AACrC,gBAAI,SAAS,IAAI,SAAS,UAAU,GAAG;AACrC,qBAAO,WAAW,QAAQ,WAAW,EAAE;AAAA,YACzC;AAAA,UACF;AAGA,iBAAO;AAAA,QACT,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKQ,cAAc,OAAe,UAAyD;AAC5F,cAAM,YAAY,OAAO,WAAW,OAAO,MAAM;AAEjD,YAAI,aAAa,gBAAgB;AAC/B,iBAAO,EAAE,OAAO,WAAW,MAAM;AAAA,QACnC;AAGA,cAAM,YAAY,MAAM,UAAU,GAAG,cAAc;AACnD,cAAM,iBAAiB,GAAG,SAAS;AAAA;AAAA,mCAAwC,YAAY,MAAM,QAAQ,CAAC,CAAC,uBAAuB,iBAAiB,MAAM,QAAQ,CAAC,CAAC;AAE/J,gBAAQ;AAAA,UACN,oCAA0B,QAAQ,MAAM,YAAY,MAAM,QAAQ,CAAC,CAAC,cAAS,iBAAiB,MAAM,QAAQ,CAAC,CAAC;AAAA,QAChH;AAEA,eAAO,EAAE,OAAO,gBAAgB,WAAW,KAAK;AAAA,MAClD;AAAA,MAEA,MAAc,gBAA+D;AAC3E,YAAI;AACF,gBAAM,UAAU,MAAM,KAAK,IAAI,WAAW,IAAI;AAC9C,gBAAM,SAAS,QAAQ,KAAK,OAAK,EAAE,SAAS,QAAQ;AACpD,iBAAO,SACH,EAAE,MAAM,OAAO,MAAM,KAAK,OAAO,KAAK,SAAS,OAAO,KAAK,QAAQ,GAAG,IACtE;AAAA,QACN,QAAQ;AACN,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,MAEA,MAAc,sBAAsB,iBAA0B,MAAgC;AAC5F,YAAI;AACF,gBAAM,SAAS,MAAM,KAAK,IAAI,OAAO;AACrC,gBAAM,UAA2B,CAAC;AAGlC,gBAAM,cAAc;AAAA,YAClB,GAAG,OAAO,QAAQ,IAAI,QAAM,EAAE,MAAM,GAAG,QAAQ,QAAiB,EAAE;AAAA,YAClE,GAAG,OAAO,QAAQ,IAAI,QAAM,EAAE,MAAM,GAAG,QAAQ,UAAmB,EAAE;AAAA,YACpE,GAAG,OAAO,SAAS,IAAI,QAAM,EAAE,MAAM,GAAG,QAAQ,WAAoB,EAAE;AAAA,YACtE,GAAG,OAAO,QAAQ,IAAI,QAAM;AAAA,cAC1B,MAAM,OAAO,MAAM,WAAW,IAAI,EAAE,MAAM,EAAE;AAAA,cAC5C,QAAQ;AAAA,YACV,EAAE;AAAA,UACJ;AAEA,qBAAW,EAAE,MAAM,QAAAC,QAAO,KAAK,aAAa;AAG1C,gBAAI,KAAK,oBAAoB,kBAAkB,IAAI,GAAG;AACpD,sBAAQ,MAAM,yCAA+B,IAAI,EAAE;AACnD;AAAA,YACF;AAEA,kBAAM,WAAgB,WAAK,KAAK,KAAK,IAAI;AACzC,kBAAM,aAAa,MAAM,KAAK,kBAAkB,MAAMA,SAAQ,UAAU,cAAc;AACtF,oBAAQ,KAAK,UAAU;AAAA,UACzB;AAEA,iBAAO;AAAA,QACT,SAAS,OAAO;AACd,kBAAQ,MAAM,sCAAsC,KAAK;AACzD,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AAAA;AAAA;AAAA;AAAA,MAKA,MAAc,cACZ,YACA,iBAA0B,MACA;AAC1B,YAAI;AAEF,gBAAM,cAAc,MAAM,KAAK,IAAI,YAAY,CAAC,UAAU,CAAC;AAC3D,gBAAM,UAA2B,CAAC;AAElC,cAAI,CAAC,eAAe,CAAC,YAAY,OAAO;AACtC,mBAAO,CAAC;AAAA,UACV;AAEA,qBAAW,QAAQ,YAAY,OAAO;AAGpC,gBAAI,KAAK,oBAAoB,kBAAkB,KAAK,IAAI,GAAG;AACzD,sBAAQ,MAAM,yCAA+B,KAAK,IAAI,EAAE;AACxD;AAAA,YACF;AAGA,kBAAM,WAAW,YAAY,QAAQ,KAAK;AAC1C,kBAAM,aAAa,gBAAgB,OAAO,KAAK,aAAa;AAC5D,kBAAM,YAAY,eAAe,OAAO,KAAK,YAAY;AACzD,kBAAM,cAAc,aAAa,OAAO,KAAK,UAAU;AAGvD,gBAAI;AACJ,gBAAI,UAAU;AACZ,uBAAS;AAAA,YACX,WAAW,aAAa,KAAK,cAAc,GAAG;AAC5C,uBAAS;AAAA,YACX,WAAW,eAAe,KAAK,YAAY,GAAG;AAC5C,uBAAS;AAAA,YACX,OAAO;AACL,uBAAS;AAAA,YACX;AAGA,gBAAI;AACJ,gBAAI,YAAY;AAChB,gBAAI,kBAAkB,CAAC,UAAU;AAC/B,kBAAI;AACF,sBAAM,WAAW,MAAM,KAAK,IAAI,KAAK,CAAC,YAAY,MAAM,KAAK,IAAI,CAAC;AAClE,oBAAI,UAAU;AACZ,wBAAM,SAAS,KAAK,cAAc,UAAU,KAAK,IAAI;AACrD,0BAAQ,OAAO;AACf,8BAAY,OAAO;AAAA,gBACrB;AAAA,cACF,QAAQ;AAAA,cAER;AAAA,YACF;AAEA,kBAAM,aAA4B;AAAA,cAChC,UAAU,KAAK;AAAA,cACf,WAAW;AAAA,cACX;AAAA,cACA,SAAS;AAAA,cACT;AAAA,cACA;AAAA,cACA;AAAA,YACF;AAEA,oBAAQ,KAAK,UAAU;AAAA,UACzB;AAEA,iBAAO;AAAA,QACT,SAAS,OAAO;AACd,kBAAQ,MAAM,8BAA8B,KAAK;AACjD,iBAAO,CAAC;AAAA,QACV;AAAA,MACF;AAAA,MAEA,MAAc,kBACZ,UACA,QACA,UACA,iBAA0B,MACF;AACxB,YAAI,YAAY;AAChB,YAAI,YAAY;AAChB,YAAI;AACJ,YAAI;AACJ,YAAI,YAAY;AAEhB,YAAI;AAEF,cAAI,kBAAkB,WAAW,WAAc,eAAW,QAAQ,GAAG;AACnE,kBAAM,OAAO,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,QAAQ,CAAC,EAAE,MAAM,MAAM,EAAE;AACjE,gBAAI,MAAM;AACR,oBAAM,SAAS,KAAK,cAAc,MAAM,QAAQ;AAChD,sBAAQ,OAAO;AACf,0BAAY,OAAO;AAEnB,oBAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,0BAAY,MAAM,OAAO,UAAQ,KAAK,WAAW,GAAG,CAAC,EAAE;AACvD,0BAAY,MAAM,OAAO,UAAQ,KAAK,WAAW,GAAG,CAAC,EAAE;AAAA,YACzD;AAAA,UACF,WAAW,WAAW,WAAc,eAAW,QAAQ,GAAG;AAExD,kBAAM,OAAO,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,QAAQ,CAAC,EAAE,MAAM,MAAM,EAAE;AACjE,gBAAI,MAAM;AACR,oBAAM,QAAQ,KAAK,MAAM,IAAI;AAC7B,0BAAY,MAAM,OAAO,UAAQ,KAAK,WAAW,GAAG,CAAC,EAAE;AACvD,0BAAY,MAAM,OAAO,UAAQ,KAAK,WAAW,GAAG,CAAC,EAAE;AAAA,YACzD;AAAA,UACF;AAGA,cAAI,WAAW,WAAc,eAAW,QAAQ,GAAG;AACjD,gBAAI;AACF,oBAAM,QAAW,aAAS,QAAQ;AAClC,kBAAI,MAAM,OAAO,KAAK,MAAM,OAAO,OAAO,MAAM;AAE9C,oBAAI,gBAAgB;AAClB,4BAAa,iBAAa,UAAU,MAAM;AAC1C,wBAAM,SAAS,KAAK,cAAc,SAAS,QAAQ;AACnD,0BAAQ,OAAO;AACf,8BAAY,OAAO;AAAA,gBACrB;AAEA,sBAAM,cAAc,iBAAiB,UAAa,iBAAa,UAAU,MAAM;AAC/E,4BAAY,YAAa,MAAM,IAAI,EAAE;AAAA,cACvC;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAGA,cAAI,WAAW,WAAW;AACxB,wBAAY;AAAA,UACd;AAAA,QACF,SAAS,OAAO;AACd,kBAAQ,MAAM,mCAAmC,QAAQ,KAAK,KAAK;AAAA,QACrE;AAEA,eAAO;AAAA,UACL;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA,SAAS,YAAY;AAAA,UACrB;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,MAEQ,cAAc,QAA2C,QAAwB;AACvF,YAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,iBAAO,mBAAmB,MAAM;AAAA,QAClC;AAEA,cAAM,cAAc,CAAC;AACrB,YAAI,OAAO,QAAQ,SAAS,EAAG,aAAY,KAAK,GAAG,OAAO,QAAQ,MAAM,QAAQ;AAChF,YAAI,OAAO,SAAS,SAAS,EAAG,aAAY,KAAK,GAAG,OAAO,SAAS,MAAM,WAAW;AACrF,YAAI,OAAO,QAAQ,SAAS,EAAG,aAAY,KAAK,GAAG,OAAO,QAAQ,MAAM,UAAU;AAClF,YAAI,OAAO,QAAQ,SAAS,EAAG,aAAY,KAAK,GAAG,OAAO,QAAQ,MAAM,UAAU;AAElF,eAAO,mBAAmB,MAAM,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,MAC7D;AAAA,MAEQ,oBACN,QACA,YACQ;AACR,YAAI,cAAc;AAAA;AAAA;AAElB,YAAI,YAAY;AACd,yBAAe,oBAAoB,WAAW,OAAO;AAAA;AACrD,yBAAe,eAAe,WAAW,WAAW,KAAK,WAAW,YAAY;AAAA;AAChF,yBAAe,aAAa,WAAW,IAAI;AAAA;AAAA;AAAA,QAC7C;AAEA,YAAI,OAAO,MAAM,WAAW,GAAG;AAC7B,yBAAe;AAAA;AAAA,QACjB,OAAO;AACL,yBAAe;AAAA;AACf,yBAAe,4BAA4B,OAAO,OAAO,MAAM;AAAA;AAC/D,yBAAe,qBAAqB,OAAO,SAAS,MAAM;AAAA;AAC1D,yBAAe,sBAAsB,OAAO,UAAU,MAAM;AAAA;AAE5D,cAAI,OAAO,WAAW,SAAS,GAAG;AAChC,2BAAe,uBAAuB,OAAO,WAAW,MAAM;AAAA;AAAA,UAChE;AAAA,QACF;AAEA,eAAO;AAAA,MACT;AAAA,MAEQ,0BAA0B,QAAmC;AACnE,eAAO;AAAA,UACL,OAAO,mBAAmB,MAAM;AAAA,UAChC,MAAM,iCAAiC,MAAM;AAAA,UAC7C,QAAQ;AAAA,UACR,MAAM;AAAA,UACN,MAAM;AAAA,UACN,OAAO,CAAC;AAAA,UACR,gBAAgB;AAAA,UAChB,gBAAgB;AAAA,UAChB,iBAAiB;AAAA,UACjB,kBAAkB,KAAK;AAAA,QACzB;AAAA,MACF;AAAA,IACF;AAAA;AAAA;","names":["path","fs","status"]}
|
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
init_logger,
|
|
3
|
-
logger
|
|
4
|
-
} from "./chunk-SZXICFQ3.mjs";
|
|
5
|
-
import "./chunk-UCMJJ3IM.mjs";
|
|
6
|
-
import {
|
|
7
|
-
__esm,
|
|
8
|
-
__require
|
|
9
|
-
} from "./chunk-J7LXIPZS.mjs";
|
|
10
|
-
|
|
11
|
-
// src/enterprise/scheduler/knex-store.ts
|
|
12
|
-
import * as fs from "fs";
|
|
13
|
-
import * as path from "path";
|
|
14
|
-
import { v4 as uuidv4 } from "uuid";
|
|
15
|
-
function toNum(val) {
|
|
16
|
-
if (val === null || val === void 0) return void 0;
|
|
17
|
-
return typeof val === "string" ? parseInt(val, 10) : val;
|
|
18
|
-
}
|
|
19
|
-
function safeJsonParse(value) {
|
|
20
|
-
if (!value) return void 0;
|
|
21
|
-
try {
|
|
22
|
-
return JSON.parse(value);
|
|
23
|
-
} catch {
|
|
24
|
-
return void 0;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
function fromDbRow(row) {
|
|
28
|
-
return {
|
|
29
|
-
id: row.id,
|
|
30
|
-
creatorId: row.creator_id,
|
|
31
|
-
creatorContext: row.creator_context ?? void 0,
|
|
32
|
-
creatorName: row.creator_name ?? void 0,
|
|
33
|
-
timezone: row.timezone,
|
|
34
|
-
schedule: row.schedule_expr,
|
|
35
|
-
runAt: toNum(row.run_at),
|
|
36
|
-
isRecurring: row.is_recurring === true || row.is_recurring === 1,
|
|
37
|
-
originalExpression: row.original_expression,
|
|
38
|
-
workflow: row.workflow ?? void 0,
|
|
39
|
-
workflowInputs: safeJsonParse(row.workflow_inputs),
|
|
40
|
-
outputContext: safeJsonParse(row.output_context),
|
|
41
|
-
status: row.status,
|
|
42
|
-
createdAt: toNum(row.created_at),
|
|
43
|
-
lastRunAt: toNum(row.last_run_at),
|
|
44
|
-
nextRunAt: toNum(row.next_run_at),
|
|
45
|
-
runCount: row.run_count,
|
|
46
|
-
failureCount: row.failure_count,
|
|
47
|
-
lastError: row.last_error ?? void 0,
|
|
48
|
-
previousResponse: row.previous_response ?? void 0
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
function toInsertRow(schedule) {
|
|
52
|
-
return {
|
|
53
|
-
id: schedule.id,
|
|
54
|
-
creator_id: schedule.creatorId,
|
|
55
|
-
creator_context: schedule.creatorContext ?? null,
|
|
56
|
-
creator_name: schedule.creatorName ?? null,
|
|
57
|
-
timezone: schedule.timezone,
|
|
58
|
-
schedule_expr: schedule.schedule,
|
|
59
|
-
run_at: schedule.runAt ?? null,
|
|
60
|
-
is_recurring: schedule.isRecurring,
|
|
61
|
-
original_expression: schedule.originalExpression,
|
|
62
|
-
workflow: schedule.workflow ?? null,
|
|
63
|
-
workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,
|
|
64
|
-
output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,
|
|
65
|
-
status: schedule.status,
|
|
66
|
-
created_at: schedule.createdAt,
|
|
67
|
-
last_run_at: schedule.lastRunAt ?? null,
|
|
68
|
-
next_run_at: schedule.nextRunAt ?? null,
|
|
69
|
-
run_count: schedule.runCount,
|
|
70
|
-
failure_count: schedule.failureCount,
|
|
71
|
-
last_error: schedule.lastError ?? null,
|
|
72
|
-
previous_response: schedule.previousResponse ?? null
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
var KnexStoreBackend;
|
|
76
|
-
var init_knex_store = __esm({
|
|
77
|
-
"src/enterprise/scheduler/knex-store.ts"() {
|
|
78
|
-
init_logger();
|
|
79
|
-
KnexStoreBackend = class {
|
|
80
|
-
knex = null;
|
|
81
|
-
driver;
|
|
82
|
-
connection;
|
|
83
|
-
constructor(driver, storageConfig, _haConfig) {
|
|
84
|
-
this.driver = driver;
|
|
85
|
-
this.connection = storageConfig.connection || {};
|
|
86
|
-
}
|
|
87
|
-
async initialize() {
|
|
88
|
-
const { createRequire } = __require("module");
|
|
89
|
-
const runtimeRequire = createRequire(__filename);
|
|
90
|
-
let knexFactory;
|
|
91
|
-
try {
|
|
92
|
-
knexFactory = runtimeRequire("knex");
|
|
93
|
-
} catch (err) {
|
|
94
|
-
const code = err?.code;
|
|
95
|
-
if (code === "MODULE_NOT_FOUND" || code === "ERR_MODULE_NOT_FOUND") {
|
|
96
|
-
throw new Error(
|
|
97
|
-
"knex is required for PostgreSQL/MySQL/MSSQL schedule storage. Install it with: npm install knex"
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
throw err;
|
|
101
|
-
}
|
|
102
|
-
const clientMap = {
|
|
103
|
-
postgresql: "pg",
|
|
104
|
-
mysql: "mysql2",
|
|
105
|
-
mssql: "tedious"
|
|
106
|
-
};
|
|
107
|
-
const client = clientMap[this.driver];
|
|
108
|
-
let connection;
|
|
109
|
-
if (this.connection.connection_string) {
|
|
110
|
-
connection = this.connection.connection_string;
|
|
111
|
-
} else if (this.driver === "mssql") {
|
|
112
|
-
connection = this.buildMssqlConnection();
|
|
113
|
-
} else {
|
|
114
|
-
connection = this.buildStandardConnection();
|
|
115
|
-
}
|
|
116
|
-
this.knex = knexFactory({
|
|
117
|
-
client,
|
|
118
|
-
connection,
|
|
119
|
-
pool: {
|
|
120
|
-
min: this.connection.pool?.min ?? 0,
|
|
121
|
-
max: this.connection.pool?.max ?? 10
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
await this.migrateSchema();
|
|
125
|
-
logger.info(`[KnexStore] Initialized (${this.driver})`);
|
|
126
|
-
}
|
|
127
|
-
buildStandardConnection() {
|
|
128
|
-
return {
|
|
129
|
-
host: this.connection.host || "localhost",
|
|
130
|
-
port: this.connection.port,
|
|
131
|
-
database: this.connection.database || "visor",
|
|
132
|
-
user: this.connection.user,
|
|
133
|
-
password: this.connection.password,
|
|
134
|
-
ssl: this.resolveSslConfig()
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
buildMssqlConnection() {
|
|
138
|
-
const ssl = this.connection.ssl;
|
|
139
|
-
const sslEnabled = ssl === true || typeof ssl === "object" && ssl.enabled !== false;
|
|
140
|
-
return {
|
|
141
|
-
server: this.connection.host || "localhost",
|
|
142
|
-
port: this.connection.port,
|
|
143
|
-
database: this.connection.database || "visor",
|
|
144
|
-
user: this.connection.user,
|
|
145
|
-
password: this.connection.password,
|
|
146
|
-
options: {
|
|
147
|
-
encrypt: sslEnabled,
|
|
148
|
-
trustServerCertificate: typeof ssl === "object" ? ssl.reject_unauthorized === false : !sslEnabled
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
resolveSslConfig() {
|
|
153
|
-
const ssl = this.connection.ssl;
|
|
154
|
-
if (ssl === false || ssl === void 0) return false;
|
|
155
|
-
if (ssl === true) return { rejectUnauthorized: true };
|
|
156
|
-
if (ssl.enabled === false) return false;
|
|
157
|
-
const result = {
|
|
158
|
-
rejectUnauthorized: ssl.reject_unauthorized !== false
|
|
159
|
-
};
|
|
160
|
-
if (ssl.ca) {
|
|
161
|
-
const caPath = this.validateSslPath(ssl.ca, "CA certificate");
|
|
162
|
-
result.ca = fs.readFileSync(caPath, "utf8");
|
|
163
|
-
}
|
|
164
|
-
if (ssl.cert) {
|
|
165
|
-
const certPath = this.validateSslPath(ssl.cert, "client certificate");
|
|
166
|
-
result.cert = fs.readFileSync(certPath, "utf8");
|
|
167
|
-
}
|
|
168
|
-
if (ssl.key) {
|
|
169
|
-
const keyPath = this.validateSslPath(ssl.key, "client key");
|
|
170
|
-
result.key = fs.readFileSync(keyPath, "utf8");
|
|
171
|
-
}
|
|
172
|
-
return result;
|
|
173
|
-
}
|
|
174
|
-
validateSslPath(filePath, label) {
|
|
175
|
-
const resolved = path.resolve(filePath);
|
|
176
|
-
if (resolved !== path.normalize(resolved)) {
|
|
177
|
-
throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);
|
|
178
|
-
}
|
|
179
|
-
if (!fs.existsSync(resolved)) {
|
|
180
|
-
throw new Error(`SSL ${label} not found: ${filePath}`);
|
|
181
|
-
}
|
|
182
|
-
return resolved;
|
|
183
|
-
}
|
|
184
|
-
async shutdown() {
|
|
185
|
-
if (this.knex) {
|
|
186
|
-
await this.knex.destroy();
|
|
187
|
-
this.knex = null;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
async migrateSchema() {
|
|
191
|
-
const knex = this.getKnex();
|
|
192
|
-
const exists = await knex.schema.hasTable("schedules");
|
|
193
|
-
if (!exists) {
|
|
194
|
-
await knex.schema.createTable("schedules", (table) => {
|
|
195
|
-
table.string("id", 36).primary();
|
|
196
|
-
table.string("creator_id", 255).notNullable().index();
|
|
197
|
-
table.string("creator_context", 255);
|
|
198
|
-
table.string("creator_name", 255);
|
|
199
|
-
table.string("timezone", 64).notNullable().defaultTo("UTC");
|
|
200
|
-
table.string("schedule_expr", 255);
|
|
201
|
-
table.bigInteger("run_at");
|
|
202
|
-
table.boolean("is_recurring").notNullable();
|
|
203
|
-
table.text("original_expression");
|
|
204
|
-
table.string("workflow", 255);
|
|
205
|
-
table.text("workflow_inputs");
|
|
206
|
-
table.text("output_context");
|
|
207
|
-
table.string("status", 20).notNullable().index();
|
|
208
|
-
table.bigInteger("created_at").notNullable();
|
|
209
|
-
table.bigInteger("last_run_at");
|
|
210
|
-
table.bigInteger("next_run_at");
|
|
211
|
-
table.integer("run_count").notNullable().defaultTo(0);
|
|
212
|
-
table.integer("failure_count").notNullable().defaultTo(0);
|
|
213
|
-
table.text("last_error");
|
|
214
|
-
table.text("previous_response");
|
|
215
|
-
table.index(["status", "next_run_at"]);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
const locksExist = await knex.schema.hasTable("scheduler_locks");
|
|
219
|
-
if (!locksExist) {
|
|
220
|
-
await knex.schema.createTable("scheduler_locks", (table) => {
|
|
221
|
-
table.string("lock_id", 255).primary();
|
|
222
|
-
table.string("node_id", 255).notNullable();
|
|
223
|
-
table.string("lock_token", 36).notNullable();
|
|
224
|
-
table.bigInteger("acquired_at").notNullable();
|
|
225
|
-
table.bigInteger("expires_at").notNullable();
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
getKnex() {
|
|
230
|
-
if (!this.knex) {
|
|
231
|
-
throw new Error("[KnexStore] Not initialized. Call initialize() first.");
|
|
232
|
-
}
|
|
233
|
-
return this.knex;
|
|
234
|
-
}
|
|
235
|
-
// --- CRUD ---
|
|
236
|
-
async create(schedule) {
|
|
237
|
-
const knex = this.getKnex();
|
|
238
|
-
const newSchedule = {
|
|
239
|
-
...schedule,
|
|
240
|
-
id: uuidv4(),
|
|
241
|
-
createdAt: Date.now(),
|
|
242
|
-
runCount: 0,
|
|
243
|
-
failureCount: 0,
|
|
244
|
-
status: "active"
|
|
245
|
-
};
|
|
246
|
-
await knex("schedules").insert(toInsertRow(newSchedule));
|
|
247
|
-
logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);
|
|
248
|
-
return newSchedule;
|
|
249
|
-
}
|
|
250
|
-
async importSchedule(schedule) {
|
|
251
|
-
const knex = this.getKnex();
|
|
252
|
-
const existing = await knex("schedules").where("id", schedule.id).first();
|
|
253
|
-
if (existing) return;
|
|
254
|
-
await knex("schedules").insert(toInsertRow(schedule));
|
|
255
|
-
}
|
|
256
|
-
async get(id) {
|
|
257
|
-
const knex = this.getKnex();
|
|
258
|
-
const row = await knex("schedules").where("id", id).first();
|
|
259
|
-
return row ? fromDbRow(row) : void 0;
|
|
260
|
-
}
|
|
261
|
-
async update(id, patch) {
|
|
262
|
-
const knex = this.getKnex();
|
|
263
|
-
const existing = await knex("schedules").where("id", id).first();
|
|
264
|
-
if (!existing) return void 0;
|
|
265
|
-
const current = fromDbRow(existing);
|
|
266
|
-
const updated = { ...current, ...patch, id: current.id };
|
|
267
|
-
const row = toInsertRow(updated);
|
|
268
|
-
delete row.id;
|
|
269
|
-
await knex("schedules").where("id", id).update(row);
|
|
270
|
-
return updated;
|
|
271
|
-
}
|
|
272
|
-
async delete(id) {
|
|
273
|
-
const knex = this.getKnex();
|
|
274
|
-
const deleted = await knex("schedules").where("id", id).del();
|
|
275
|
-
if (deleted > 0) {
|
|
276
|
-
logger.info(`[KnexStore] Deleted schedule ${id}`);
|
|
277
|
-
return true;
|
|
278
|
-
}
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
// --- Queries ---
|
|
282
|
-
async getByCreator(creatorId) {
|
|
283
|
-
const knex = this.getKnex();
|
|
284
|
-
const rows = await knex("schedules").where("creator_id", creatorId);
|
|
285
|
-
return rows.map((r) => fromDbRow(r));
|
|
286
|
-
}
|
|
287
|
-
async getActiveSchedules() {
|
|
288
|
-
const knex = this.getKnex();
|
|
289
|
-
const rows = await knex("schedules").where("status", "active");
|
|
290
|
-
return rows.map((r) => fromDbRow(r));
|
|
291
|
-
}
|
|
292
|
-
async getDueSchedules(now) {
|
|
293
|
-
const ts = now ?? Date.now();
|
|
294
|
-
const knex = this.getKnex();
|
|
295
|
-
const bFalse = this.driver === "mssql" ? 0 : false;
|
|
296
|
-
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
297
|
-
const rows = await knex("schedules").where("status", "active").andWhere(function() {
|
|
298
|
-
this.where(function() {
|
|
299
|
-
this.where("is_recurring", bFalse).whereNotNull("run_at").where("run_at", "<=", ts);
|
|
300
|
-
}).orWhere(function() {
|
|
301
|
-
this.where("is_recurring", bTrue).whereNotNull("next_run_at").where("next_run_at", "<=", ts);
|
|
302
|
-
});
|
|
303
|
-
});
|
|
304
|
-
return rows.map((r) => fromDbRow(r));
|
|
305
|
-
}
|
|
306
|
-
async findByWorkflow(creatorId, workflowName) {
|
|
307
|
-
const knex = this.getKnex();
|
|
308
|
-
const escaped = workflowName.toLowerCase().replace(/[%_\\]/g, "\\$&");
|
|
309
|
-
const pattern = `%${escaped}%`;
|
|
310
|
-
const rows = await knex("schedules").where("creator_id", creatorId).where("status", "active").whereRaw("LOWER(workflow) LIKE ? ESCAPE '\\'", [pattern]);
|
|
311
|
-
return rows.map((r) => fromDbRow(r));
|
|
312
|
-
}
|
|
313
|
-
async getAll() {
|
|
314
|
-
const knex = this.getKnex();
|
|
315
|
-
const rows = await knex("schedules");
|
|
316
|
-
return rows.map((r) => fromDbRow(r));
|
|
317
|
-
}
|
|
318
|
-
async getStats() {
|
|
319
|
-
const knex = this.getKnex();
|
|
320
|
-
const boolTrue = this.driver === "mssql" ? "1" : "true";
|
|
321
|
-
const boolFalse = this.driver === "mssql" ? "0" : "false";
|
|
322
|
-
const result = await knex("schedules").select(
|
|
323
|
-
knex.raw("COUNT(*) as total"),
|
|
324
|
-
knex.raw("SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active"),
|
|
325
|
-
knex.raw("SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused"),
|
|
326
|
-
knex.raw("SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed"),
|
|
327
|
-
knex.raw("SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed"),
|
|
328
|
-
knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),
|
|
329
|
-
knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)
|
|
330
|
-
).first();
|
|
331
|
-
return {
|
|
332
|
-
total: Number(result.total) || 0,
|
|
333
|
-
active: Number(result.active) || 0,
|
|
334
|
-
paused: Number(result.paused) || 0,
|
|
335
|
-
completed: Number(result.completed) || 0,
|
|
336
|
-
failed: Number(result.failed) || 0,
|
|
337
|
-
recurring: Number(result.recurring) || 0,
|
|
338
|
-
oneTime: Number(result.one_time) || 0
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
async validateLimits(creatorId, isRecurring, limits) {
|
|
342
|
-
const knex = this.getKnex();
|
|
343
|
-
if (limits.maxGlobal) {
|
|
344
|
-
const result = await knex("schedules").count("* as cnt").first();
|
|
345
|
-
if (Number(result?.cnt) >= limits.maxGlobal) {
|
|
346
|
-
throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
if (limits.maxPerUser) {
|
|
350
|
-
const result = await knex("schedules").where("creator_id", creatorId).count("* as cnt").first();
|
|
351
|
-
if (Number(result?.cnt) >= limits.maxPerUser) {
|
|
352
|
-
throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
if (isRecurring && limits.maxRecurringPerUser) {
|
|
356
|
-
const bTrue = this.driver === "mssql" ? 1 : true;
|
|
357
|
-
const result = await knex("schedules").where("creator_id", creatorId).where("is_recurring", bTrue).count("* as cnt").first();
|
|
358
|
-
if (Number(result?.cnt) >= limits.maxRecurringPerUser) {
|
|
359
|
-
throw new Error(
|
|
360
|
-
`You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`
|
|
361
|
-
);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
// --- HA Distributed Locking (via scheduler_locks table) ---
|
|
366
|
-
async tryAcquireLock(lockId, nodeId, ttlSeconds) {
|
|
367
|
-
const knex = this.getKnex();
|
|
368
|
-
const now = Date.now();
|
|
369
|
-
const expiresAt = now + ttlSeconds * 1e3;
|
|
370
|
-
const token = uuidv4();
|
|
371
|
-
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("expires_at", "<", now).update({
|
|
372
|
-
node_id: nodeId,
|
|
373
|
-
lock_token: token,
|
|
374
|
-
acquired_at: now,
|
|
375
|
-
expires_at: expiresAt
|
|
376
|
-
});
|
|
377
|
-
if (updated > 0) return token;
|
|
378
|
-
try {
|
|
379
|
-
await knex("scheduler_locks").insert({
|
|
380
|
-
lock_id: lockId,
|
|
381
|
-
node_id: nodeId,
|
|
382
|
-
lock_token: token,
|
|
383
|
-
acquired_at: now,
|
|
384
|
-
expires_at: expiresAt
|
|
385
|
-
});
|
|
386
|
-
return token;
|
|
387
|
-
} catch {
|
|
388
|
-
return null;
|
|
389
|
-
}
|
|
390
|
-
}
|
|
391
|
-
async releaseLock(lockId, lockToken) {
|
|
392
|
-
const knex = this.getKnex();
|
|
393
|
-
await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).del();
|
|
394
|
-
}
|
|
395
|
-
async renewLock(lockId, lockToken, ttlSeconds) {
|
|
396
|
-
const knex = this.getKnex();
|
|
397
|
-
const now = Date.now();
|
|
398
|
-
const expiresAt = now + ttlSeconds * 1e3;
|
|
399
|
-
const updated = await knex("scheduler_locks").where("lock_id", lockId).where("lock_token", lockToken).update({ acquired_at: now, expires_at: expiresAt });
|
|
400
|
-
return updated > 0;
|
|
401
|
-
}
|
|
402
|
-
async flush() {
|
|
403
|
-
}
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
init_knex_store();
|
|
408
|
-
export {
|
|
409
|
-
KnexStoreBackend
|
|
410
|
-
};
|
|
411
|
-
//# sourceMappingURL=knex-store-HPXJILBL.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/enterprise/scheduler/knex-store.ts"],"sourcesContent":["/**\n * Copyright (c) ProbeLabs. All rights reserved.\n * Licensed under the Elastic License 2.0; you may not use this file except\n * in compliance with the Elastic License 2.0.\n */\n\n/**\n * Knex-backed schedule store for PostgreSQL, MySQL, and MSSQL (Enterprise)\n *\n * Uses Knex query builder for database-agnostic SQL. Same schema as SQLite backend\n * but with real distributed locking via row-level claims (claimed_by/claimed_at/lock_token).\n */\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { v4 as uuidv4 } from 'uuid';\nimport { logger } from '../../logger';\nimport type { Schedule, ScheduleLimits } from '../../scheduler/schedule-store';\nimport type {\n ScheduleStoreBackend,\n ScheduleStoreStats,\n StorageConfig,\n HAConfig,\n ServerConnectionConfig,\n} from '../../scheduler/store/types';\n\n// Knex types — loaded dynamically to avoid ncc bundling\ntype Knex = import('knex').Knex;\n\n/**\n * Database row shape (snake_case)\n */\ninterface ScheduleRow {\n id: string;\n creator_id: string;\n creator_context: string | null;\n creator_name: string | null;\n timezone: string;\n schedule_expr: string;\n run_at: number | string | null;\n is_recurring: boolean | number;\n original_expression: string;\n workflow: string | null;\n workflow_inputs: string | null;\n output_context: string | null;\n status: string;\n created_at: number | string;\n last_run_at: number | string | null;\n next_run_at: number | string | null;\n run_count: number;\n failure_count: number;\n last_error: string | null;\n previous_response: string | null;\n}\n\nfunction toNum(val: number | string | null | undefined): number | undefined {\n if (val === null || val === undefined) return undefined;\n return typeof val === 'string' ? parseInt(val, 10) : val;\n}\n\nfunction safeJsonParse<T = unknown>(value: string | null): T | undefined {\n if (!value) return undefined;\n try {\n return JSON.parse(value) as T;\n } catch {\n return undefined;\n }\n}\n\nfunction fromDbRow(row: ScheduleRow): Schedule {\n return {\n id: row.id,\n creatorId: row.creator_id,\n creatorContext: row.creator_context ?? undefined,\n creatorName: row.creator_name ?? undefined,\n timezone: row.timezone,\n schedule: row.schedule_expr,\n runAt: toNum(row.run_at),\n isRecurring: row.is_recurring === true || row.is_recurring === 1,\n originalExpression: row.original_expression,\n workflow: row.workflow ?? undefined,\n workflowInputs: safeJsonParse(row.workflow_inputs),\n outputContext: safeJsonParse(row.output_context),\n status: row.status as Schedule['status'],\n createdAt: toNum(row.created_at)!,\n lastRunAt: toNum(row.last_run_at),\n nextRunAt: toNum(row.next_run_at),\n runCount: row.run_count,\n failureCount: row.failure_count,\n lastError: row.last_error ?? undefined,\n previousResponse: row.previous_response ?? undefined,\n };\n}\n\nfunction toInsertRow(schedule: Schedule): Record<string, unknown> {\n return {\n id: schedule.id,\n creator_id: schedule.creatorId,\n creator_context: schedule.creatorContext ?? null,\n creator_name: schedule.creatorName ?? null,\n timezone: schedule.timezone,\n schedule_expr: schedule.schedule,\n run_at: schedule.runAt ?? null,\n is_recurring: schedule.isRecurring,\n original_expression: schedule.originalExpression,\n workflow: schedule.workflow ?? null,\n workflow_inputs: schedule.workflowInputs ? JSON.stringify(schedule.workflowInputs) : null,\n output_context: schedule.outputContext ? JSON.stringify(schedule.outputContext) : null,\n status: schedule.status,\n created_at: schedule.createdAt,\n last_run_at: schedule.lastRunAt ?? null,\n next_run_at: schedule.nextRunAt ?? null,\n run_count: schedule.runCount,\n failure_count: schedule.failureCount,\n last_error: schedule.lastError ?? null,\n previous_response: schedule.previousResponse ?? null,\n };\n}\n\n/**\n * Enterprise Knex-backed store for PostgreSQL, MySQL, and MSSQL\n */\nexport class KnexStoreBackend implements ScheduleStoreBackend {\n private knex: Knex | null = null;\n private driver: 'postgresql' | 'mysql' | 'mssql';\n private connection: ServerConnectionConfig;\n\n constructor(\n driver: 'postgresql' | 'mysql' | 'mssql',\n storageConfig: StorageConfig,\n _haConfig?: HAConfig\n ) {\n this.driver = driver;\n this.connection = (storageConfig.connection || {}) as ServerConnectionConfig;\n }\n\n async initialize(): Promise<void> {\n // Load knex dynamically\n const { createRequire } = require('module') as typeof import('module');\n const runtimeRequire = createRequire(__filename);\n let knexFactory: typeof import('knex').default;\n try {\n knexFactory = runtimeRequire('knex');\n } catch (err: unknown) {\n const code = (err as { code?: string })?.code;\n if (code === 'MODULE_NOT_FOUND' || code === 'ERR_MODULE_NOT_FOUND') {\n throw new Error(\n 'knex is required for PostgreSQL/MySQL/MSSQL schedule storage. ' +\n 'Install it with: npm install knex'\n );\n }\n throw err;\n }\n\n const clientMap: Record<string, string> = {\n postgresql: 'pg',\n mysql: 'mysql2',\n mssql: 'tedious',\n };\n const client = clientMap[this.driver];\n\n // Build connection config\n let connection: string | Record<string, unknown>;\n if (this.connection.connection_string) {\n connection = this.connection.connection_string;\n } else if (this.driver === 'mssql') {\n connection = this.buildMssqlConnection();\n } else {\n connection = this.buildStandardConnection();\n }\n\n this.knex = knexFactory({\n client,\n connection,\n pool: {\n min: this.connection.pool?.min ?? 0,\n max: this.connection.pool?.max ?? 10,\n },\n });\n\n // Run schema migration\n await this.migrateSchema();\n\n logger.info(`[KnexStore] Initialized (${this.driver})`);\n }\n\n private buildStandardConnection(): Record<string, unknown> {\n return {\n host: this.connection.host || 'localhost',\n port: this.connection.port,\n database: this.connection.database || 'visor',\n user: this.connection.user,\n password: this.connection.password,\n ssl: this.resolveSslConfig(),\n };\n }\n\n private buildMssqlConnection(): Record<string, unknown> {\n const ssl = this.connection.ssl;\n const sslEnabled = ssl === true || (typeof ssl === 'object' && ssl.enabled !== false);\n\n return {\n server: this.connection.host || 'localhost',\n port: this.connection.port,\n database: this.connection.database || 'visor',\n user: this.connection.user,\n password: this.connection.password,\n options: {\n encrypt: sslEnabled,\n trustServerCertificate:\n typeof ssl === 'object' ? ssl.reject_unauthorized === false : !sslEnabled,\n },\n };\n }\n\n private resolveSslConfig(): boolean | Record<string, unknown> {\n const ssl = this.connection.ssl;\n if (ssl === false || ssl === undefined) return false;\n if (ssl === true) return { rejectUnauthorized: true };\n\n // Object config\n if (ssl.enabled === false) return false;\n\n const result: Record<string, unknown> = {\n rejectUnauthorized: ssl.reject_unauthorized !== false,\n };\n\n if (ssl.ca) {\n const caPath = this.validateSslPath(ssl.ca, 'CA certificate');\n result.ca = fs.readFileSync(caPath, 'utf8');\n }\n if (ssl.cert) {\n const certPath = this.validateSslPath(ssl.cert, 'client certificate');\n result.cert = fs.readFileSync(certPath, 'utf8');\n }\n if (ssl.key) {\n const keyPath = this.validateSslPath(ssl.key, 'client key');\n result.key = fs.readFileSync(keyPath, 'utf8');\n }\n\n return result;\n }\n\n private validateSslPath(filePath: string, label: string): string {\n const resolved = path.resolve(filePath);\n if (resolved !== path.normalize(resolved)) {\n throw new Error(`SSL ${label} path contains invalid sequences: ${filePath}`);\n }\n if (!fs.existsSync(resolved)) {\n throw new Error(`SSL ${label} not found: ${filePath}`);\n }\n return resolved;\n }\n\n async shutdown(): Promise<void> {\n if (this.knex) {\n await this.knex.destroy();\n this.knex = null;\n }\n }\n\n private async migrateSchema(): Promise<void> {\n const knex = this.getKnex();\n\n const exists = await knex.schema.hasTable('schedules');\n if (!exists) {\n await knex.schema.createTable('schedules', table => {\n table.string('id', 36).primary();\n table.string('creator_id', 255).notNullable().index();\n table.string('creator_context', 255);\n table.string('creator_name', 255);\n table.string('timezone', 64).notNullable().defaultTo('UTC');\n table.string('schedule_expr', 255);\n table.bigInteger('run_at');\n table.boolean('is_recurring').notNullable();\n table.text('original_expression');\n table.string('workflow', 255);\n table.text('workflow_inputs');\n table.text('output_context');\n table.string('status', 20).notNullable().index();\n table.bigInteger('created_at').notNullable();\n table.bigInteger('last_run_at');\n table.bigInteger('next_run_at');\n table.integer('run_count').notNullable().defaultTo(0);\n table.integer('failure_count').notNullable().defaultTo(0);\n table.text('last_error');\n table.text('previous_response');\n\n table.index(['status', 'next_run_at']);\n });\n }\n\n // Create scheduler_locks table for distributed locking\n const locksExist = await knex.schema.hasTable('scheduler_locks');\n if (!locksExist) {\n await knex.schema.createTable('scheduler_locks', table => {\n table.string('lock_id', 255).primary();\n table.string('node_id', 255).notNullable();\n table.string('lock_token', 36).notNullable();\n table.bigInteger('acquired_at').notNullable();\n table.bigInteger('expires_at').notNullable();\n });\n }\n }\n\n private getKnex(): Knex {\n if (!this.knex) {\n throw new Error('[KnexStore] Not initialized. Call initialize() first.');\n }\n return this.knex;\n }\n\n // --- CRUD ---\n\n async create(\n schedule: Omit<Schedule, 'id' | 'createdAt' | 'runCount' | 'failureCount' | 'status'>\n ): Promise<Schedule> {\n const knex = this.getKnex();\n\n const newSchedule: Schedule = {\n ...schedule,\n id: uuidv4(),\n createdAt: Date.now(),\n runCount: 0,\n failureCount: 0,\n status: 'active',\n };\n\n await knex('schedules').insert(toInsertRow(newSchedule));\n\n logger.info(`[KnexStore] Created schedule ${newSchedule.id} for user ${newSchedule.creatorId}`);\n return newSchedule;\n }\n\n async importSchedule(schedule: Schedule): Promise<void> {\n const knex = this.getKnex();\n const existing = await knex('schedules').where('id', schedule.id).first();\n if (existing) return; // Already imported (idempotent)\n await knex('schedules').insert(toInsertRow(schedule));\n }\n\n async get(id: string): Promise<Schedule | undefined> {\n const knex = this.getKnex();\n const row = await knex('schedules').where('id', id).first();\n return row ? fromDbRow(row as ScheduleRow) : undefined;\n }\n\n async update(id: string, patch: Partial<Schedule>): Promise<Schedule | undefined> {\n const knex = this.getKnex();\n\n const existing = await knex('schedules').where('id', id).first();\n if (!existing) return undefined;\n\n const current = fromDbRow(existing as ScheduleRow);\n const updated: Schedule = { ...current, ...patch, id: current.id };\n const row = toInsertRow(updated);\n // Remove id from update (PK cannot change)\n delete (row as Record<string, unknown>).id;\n\n await knex('schedules').where('id', id).update(row);\n return updated;\n }\n\n async delete(id: string): Promise<boolean> {\n const knex = this.getKnex();\n const deleted = await knex('schedules').where('id', id).del();\n if (deleted > 0) {\n logger.info(`[KnexStore] Deleted schedule ${id}`);\n return true;\n }\n return false;\n }\n\n // --- Queries ---\n\n async getByCreator(creatorId: string): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules').where('creator_id', creatorId);\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getActiveSchedules(): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules').where('status', 'active');\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getDueSchedules(now?: number): Promise<Schedule[]> {\n const ts = now ?? Date.now();\n const knex = this.getKnex();\n // MSSQL uses 1/0 for booleans\n const bFalse = this.driver === 'mssql' ? 0 : false;\n const bTrue = this.driver === 'mssql' ? 1 : true;\n const rows = await knex('schedules')\n .where('status', 'active')\n .andWhere(function () {\n this.where(function () {\n this.where('is_recurring', bFalse as unknown as boolean)\n .whereNotNull('run_at')\n .where('run_at', '<=', ts);\n }).orWhere(function () {\n this.where('is_recurring', bTrue as unknown as boolean)\n .whereNotNull('next_run_at')\n .where('next_run_at', '<=', ts);\n });\n });\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async findByWorkflow(creatorId: string, workflowName: string): Promise<Schedule[]> {\n const knex = this.getKnex();\n const escaped = workflowName.toLowerCase().replace(/[%_\\\\]/g, '\\\\$&');\n const pattern = `%${escaped}%`;\n const rows = await knex('schedules')\n .where('creator_id', creatorId)\n .where('status', 'active')\n .whereRaw(\"LOWER(workflow) LIKE ? ESCAPE '\\\\'\", [pattern]);\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getAll(): Promise<Schedule[]> {\n const knex = this.getKnex();\n const rows = await knex('schedules');\n return rows.map((r: ScheduleRow) => fromDbRow(r));\n }\n\n async getStats(): Promise<ScheduleStoreStats> {\n const knex = this.getKnex();\n // MSSQL uses 1/0 for booleans; PostgreSQL/MySQL accept both true/1\n const boolTrue = this.driver === 'mssql' ? '1' : 'true';\n const boolFalse = this.driver === 'mssql' ? '0' : 'false';\n const result = await knex('schedules')\n .select(\n knex.raw('COUNT(*) as total'),\n knex.raw(\"SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active\"),\n knex.raw(\"SUM(CASE WHEN status = 'paused' THEN 1 ELSE 0 END) as paused\"),\n knex.raw(\"SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed\"),\n knex.raw(\"SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed\"),\n knex.raw(`SUM(CASE WHEN is_recurring = ${boolTrue} THEN 1 ELSE 0 END) as recurring`),\n knex.raw(`SUM(CASE WHEN is_recurring = ${boolFalse} THEN 1 ELSE 0 END) as one_time`)\n )\n .first();\n\n return {\n total: Number(result.total) || 0,\n active: Number(result.active) || 0,\n paused: Number(result.paused) || 0,\n completed: Number(result.completed) || 0,\n failed: Number(result.failed) || 0,\n recurring: Number(result.recurring) || 0,\n oneTime: Number(result.one_time) || 0,\n };\n }\n\n async validateLimits(\n creatorId: string,\n isRecurring: boolean,\n limits: ScheduleLimits\n ): Promise<void> {\n const knex = this.getKnex();\n\n if (limits.maxGlobal) {\n const result = await knex('schedules').count('* as cnt').first();\n if (Number(result?.cnt) >= limits.maxGlobal) {\n throw new Error(`Global schedule limit reached (${limits.maxGlobal})`);\n }\n }\n\n if (limits.maxPerUser) {\n const result = await knex('schedules')\n .where('creator_id', creatorId)\n .count('* as cnt')\n .first();\n if (Number(result?.cnt) >= limits.maxPerUser) {\n throw new Error(`You have reached the maximum number of schedules (${limits.maxPerUser})`);\n }\n }\n\n if (isRecurring && limits.maxRecurringPerUser) {\n const bTrue = this.driver === 'mssql' ? 1 : true;\n const result = await knex('schedules')\n .where('creator_id', creatorId)\n .where('is_recurring', bTrue as unknown as boolean)\n .count('* as cnt')\n .first();\n if (Number(result?.cnt) >= limits.maxRecurringPerUser) {\n throw new Error(\n `You have reached the maximum number of recurring schedules (${limits.maxRecurringPerUser})`\n );\n }\n }\n }\n\n // --- HA Distributed Locking (via scheduler_locks table) ---\n\n async tryAcquireLock(lockId: string, nodeId: string, ttlSeconds: number): Promise<string | null> {\n const knex = this.getKnex();\n const now = Date.now();\n const expiresAt = now + ttlSeconds * 1000;\n const token = uuidv4();\n\n // Step 1: Try to claim an existing expired lock\n const updated = await knex('scheduler_locks')\n .where('lock_id', lockId)\n .where('expires_at', '<', now)\n .update({\n node_id: nodeId,\n lock_token: token,\n acquired_at: now,\n expires_at: expiresAt,\n });\n\n if (updated > 0) return token;\n\n // Step 2: Try to INSERT a new lock row\n try {\n await knex('scheduler_locks').insert({\n lock_id: lockId,\n node_id: nodeId,\n lock_token: token,\n acquired_at: now,\n expires_at: expiresAt,\n });\n return token;\n } catch {\n // Unique constraint violation — another node holds the lock\n return null;\n }\n }\n\n async releaseLock(lockId: string, lockToken: string): Promise<void> {\n const knex = this.getKnex();\n await knex('scheduler_locks').where('lock_id', lockId).where('lock_token', lockToken).del();\n }\n\n async renewLock(lockId: string, lockToken: string, ttlSeconds: number): Promise<boolean> {\n const knex = this.getKnex();\n const now = Date.now();\n const expiresAt = now + ttlSeconds * 1000;\n\n const updated = await knex('scheduler_locks')\n .where('lock_id', lockId)\n .where('lock_token', lockToken)\n .update({ acquired_at: now, expires_at: expiresAt });\n\n return updated > 0;\n }\n\n async flush(): Promise<void> {\n // No-op for server-based backends\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
init_logger,
|
|
3
|
-
logger_exports
|
|
4
|
-
} from "./chunk-SZXICFQ3.mjs";
|
|
5
|
-
import "./chunk-UCMJJ3IM.mjs";
|
|
6
|
-
import {
|
|
7
|
-
__esm,
|
|
8
|
-
__toCommonJS
|
|
9
|
-
} from "./chunk-J7LXIPZS.mjs";
|
|
10
|
-
|
|
11
|
-
// src/policy/default-engine.ts
|
|
12
|
-
var DefaultPolicyEngine;
|
|
13
|
-
var init_default_engine = __esm({
|
|
14
|
-
"src/policy/default-engine.ts"() {
|
|
15
|
-
"use strict";
|
|
16
|
-
DefaultPolicyEngine = class {
|
|
17
|
-
async initialize(_config) {
|
|
18
|
-
}
|
|
19
|
-
async evaluateCheckExecution(_checkId, _checkConfig) {
|
|
20
|
-
return { allowed: true };
|
|
21
|
-
}
|
|
22
|
-
async evaluateToolInvocation(_serverName, _methodName, _transport) {
|
|
23
|
-
return { allowed: true };
|
|
24
|
-
}
|
|
25
|
-
async evaluateCapabilities(_checkId, _capabilities) {
|
|
26
|
-
return { allowed: true };
|
|
27
|
-
}
|
|
28
|
-
async shutdown() {
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
// src/enterprise/loader.ts
|
|
35
|
-
async function loadEnterprisePolicyEngine(config) {
|
|
36
|
-
try {
|
|
37
|
-
const { LicenseValidator } = await import("./validator-XTZJZZJH.mjs");
|
|
38
|
-
const validator = new LicenseValidator();
|
|
39
|
-
const license = await validator.loadAndValidate();
|
|
40
|
-
if (!license || !validator.hasFeature("policy")) {
|
|
41
|
-
return new DefaultPolicyEngine();
|
|
42
|
-
}
|
|
43
|
-
if (validator.isInGracePeriod()) {
|
|
44
|
-
console.warn(
|
|
45
|
-
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
const { OpaPolicyEngine } = await import("./opa-policy-engine-S2S2ULEI.mjs");
|
|
49
|
-
const engine = new OpaPolicyEngine(config);
|
|
50
|
-
await engine.initialize(config);
|
|
51
|
-
return engine;
|
|
52
|
-
} catch (err) {
|
|
53
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
54
|
-
try {
|
|
55
|
-
const { logger } = (init_logger(), __toCommonJS(logger_exports));
|
|
56
|
-
logger.warn(`[PolicyEngine] Enterprise policy init failed, falling back to default: ${msg}`);
|
|
57
|
-
} catch {
|
|
58
|
-
}
|
|
59
|
-
return new DefaultPolicyEngine();
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
async function loadEnterpriseStoreBackend(driver, storageConfig, haConfig) {
|
|
63
|
-
const { LicenseValidator } = await import("./validator-XTZJZZJH.mjs");
|
|
64
|
-
const validator = new LicenseValidator();
|
|
65
|
-
const license = await validator.loadAndValidate();
|
|
66
|
-
if (!license || !validator.hasFeature("scheduler-sql")) {
|
|
67
|
-
throw new Error(
|
|
68
|
-
`The ${driver} schedule storage driver requires a Visor Enterprise license with the 'scheduler-sql' feature. Please upgrade or use driver: 'sqlite' (default).`
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
if (validator.isInGracePeriod()) {
|
|
72
|
-
console.warn(
|
|
73
|
-
"[visor:enterprise] License has expired but is within the 72-hour grace period. Please renew your license."
|
|
74
|
-
);
|
|
75
|
-
}
|
|
76
|
-
const { KnexStoreBackend } = await import("./knex-store-HPXJILBL.mjs");
|
|
77
|
-
return new KnexStoreBackend(driver, storageConfig, haConfig);
|
|
78
|
-
}
|
|
79
|
-
var init_loader = __esm({
|
|
80
|
-
"src/enterprise/loader.ts"() {
|
|
81
|
-
init_default_engine();
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
init_loader();
|
|
85
|
-
export {
|
|
86
|
-
loadEnterprisePolicyEngine,
|
|
87
|
-
loadEnterpriseStoreBackend
|
|
88
|
-
};
|
|
89
|
-
//# sourceMappingURL=loader-YSRMVXC3.mjs.map
|