sela-core 0.1.0-alpha.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/README.md +103 -0
- package/bin/sela.js +3 -0
- package/dist/cli/ErrorHandler.d.ts +10 -0
- package/dist/cli/ErrorHandler.d.ts.map +1 -0
- package/dist/cli/ErrorHandler.js +70 -0
- package/dist/cli/commands/bulk.d.ts +3 -0
- package/dist/cli/commands/bulk.d.ts.map +1 -0
- package/dist/cli/commands/bulk.js +158 -0
- package/dist/cli/commands/find.d.ts +3 -0
- package/dist/cli/commands/find.d.ts.map +1 -0
- package/dist/cli/commands/find.js +51 -0
- package/dist/cli/commands/init.d.ts +3 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +166 -0
- package/dist/cli/commands/list.d.ts +3 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +56 -0
- package/dist/cli/commands/merge.d.ts +13 -0
- package/dist/cli/commands/merge.d.ts.map +1 -0
- package/dist/cli/commands/merge.js +119 -0
- package/dist/cli/commands/refactor.d.ts +3 -0
- package/dist/cli/commands/refactor.d.ts.map +1 -0
- package/dist/cli/commands/refactor.js +30 -0
- package/dist/cli/commands/showReport.d.ts +3 -0
- package/dist/cli/commands/showReport.d.ts.map +1 -0
- package/dist/cli/commands/showReport.js +82 -0
- package/dist/cli/commands/status.d.ts +3 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +51 -0
- package/dist/cli/commands/sync.d.ts +3 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +131 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +94 -0
- package/dist/cli/ui/DnaTable.d.ts +3 -0
- package/dist/cli/ui/DnaTable.d.ts.map +1 -0
- package/dist/cli/ui/DnaTable.js +77 -0
- package/dist/cli/ui/LocatorPicker.d.ts +8 -0
- package/dist/cli/ui/LocatorPicker.d.ts.map +1 -0
- package/dist/cli/ui/LocatorPicker.js +33 -0
- package/dist/cli/ui/ProgressReporter.d.ts +11 -0
- package/dist/cli/ui/ProgressReporter.d.ts.map +1 -0
- package/dist/cli/ui/ProgressReporter.js +33 -0
- package/dist/cli/ui/RefactorWizard.d.ts +26 -0
- package/dist/cli/ui/RefactorWizard.d.ts.map +1 -0
- package/dist/cli/ui/RefactorWizard.js +295 -0
- package/dist/config/ConfigLoader.d.ts +16 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +185 -0
- package/dist/config/DryRunGuard.d.ts +14 -0
- package/dist/config/DryRunGuard.d.ts.map +1 -0
- package/dist/config/DryRunGuard.js +80 -0
- package/dist/config/PathScopeGuard.d.ts +42 -0
- package/dist/config/PathScopeGuard.d.ts.map +1 -0
- package/dist/config/PathScopeGuard.js +192 -0
- package/dist/config/SelaConfig.d.ts +178 -0
- package/dist/config/SelaConfig.d.ts.map +1 -0
- package/dist/config/SelaConfig.js +141 -0
- package/dist/config/defineConfig.d.ts +3 -0
- package/dist/config/defineConfig.d.ts.map +1 -0
- package/dist/config/defineConfig.js +9 -0
- package/dist/engine/FixwrightEngine.d.ts +24 -0
- package/dist/engine/FixwrightEngine.d.ts.map +1 -0
- package/dist/engine/FixwrightEngine.js +403 -0
- package/dist/engine/HealingRegistry.d.ts +40 -0
- package/dist/engine/HealingRegistry.d.ts.map +1 -0
- package/dist/engine/HealingRegistry.js +99 -0
- package/dist/engine/SelaEngine.d.ts +57 -0
- package/dist/engine/SelaEngine.d.ts.map +1 -0
- package/dist/engine/SelaEngine.js +1146 -0
- package/dist/engine/singleton.d.ts +3 -0
- package/dist/engine/singleton.d.ts.map +1 -0
- package/dist/engine/singleton.js +5 -0
- package/dist/errors/SelaError.d.ts +146 -0
- package/dist/errors/SelaError.d.ts.map +1 -0
- package/dist/errors/SelaError.js +168 -0
- package/dist/fixtures/expectProxy.d.ts +12 -0
- package/dist/fixtures/expectProxy.d.ts.map +1 -0
- package/dist/fixtures/expectProxy.js +246 -0
- package/dist/fixtures/index.d.ts +30 -0
- package/dist/fixtures/index.d.ts.map +1 -0
- package/dist/fixtures/index.js +765 -0
- package/dist/fixtures/moduleExpect.d.ts +2 -0
- package/dist/fixtures/moduleExpect.d.ts.map +1 -0
- package/dist/fixtures/moduleExpect.js +54 -0
- package/dist/fixtures/proxyTag.d.ts +12 -0
- package/dist/fixtures/proxyTag.d.ts.map +1 -0
- package/dist/fixtures/proxyTag.js +45 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/reporter/SelaReporter.d.ts +165 -0
- package/dist/reporter/SelaReporter.d.ts.map +1 -0
- package/dist/reporter/SelaReporter.js +517 -0
- package/dist/services/ASTSourceUpdater.d.ts +143 -0
- package/dist/services/ASTSourceUpdater.d.ts.map +1 -0
- package/dist/services/ASTSourceUpdater.js +3514 -0
- package/dist/services/AnchorResolver.d.ts +157 -0
- package/dist/services/AnchorResolver.d.ts.map +1 -0
- package/dist/services/AnchorResolver.js +289 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts +26 -0
- package/dist/services/ArgumentTypeAnalyzer.d.ts.map +1 -0
- package/dist/services/ArgumentTypeAnalyzer.js +92 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts +15 -0
- package/dist/services/BlastRadiusAnalyzer.d.ts.map +1 -0
- package/dist/services/BlastRadiusAnalyzer.js +107 -0
- package/dist/services/ChainValidator.d.ts +76 -0
- package/dist/services/ChainValidator.d.ts.map +1 -0
- package/dist/services/ChainValidator.js +570 -0
- package/dist/services/CrossFileHealer.d.ts +6 -0
- package/dist/services/CrossFileHealer.d.ts.map +1 -0
- package/dist/services/CrossFileHealer.js +135 -0
- package/dist/services/DecisionEngine.d.ts +51 -0
- package/dist/services/DecisionEngine.d.ts.map +1 -0
- package/dist/services/DecisionEngine.js +260 -0
- package/dist/services/DefinitionTracer.d.ts +41 -0
- package/dist/services/DefinitionTracer.d.ts.map +1 -0
- package/dist/services/DefinitionTracer.js +366 -0
- package/dist/services/DnaEditorService.d.ts +31 -0
- package/dist/services/DnaEditorService.d.ts.map +1 -0
- package/dist/services/DnaEditorService.js +217 -0
- package/dist/services/DnaIndexService.d.ts +24 -0
- package/dist/services/DnaIndexService.d.ts.map +1 -0
- package/dist/services/DnaIndexService.js +131 -0
- package/dist/services/HealBudget.d.ts +32 -0
- package/dist/services/HealBudget.d.ts.map +1 -0
- package/dist/services/HealBudget.js +85 -0
- package/dist/services/HealReportService.d.ts +161 -0
- package/dist/services/HealReportService.d.ts.map +1 -0
- package/dist/services/HealReportService.js +1039 -0
- package/dist/services/HealingAdvisory.d.ts +30 -0
- package/dist/services/HealingAdvisory.d.ts.map +1 -0
- package/dist/services/HealingAdvisory.js +51 -0
- package/dist/services/HealingCacheService.d.ts +135 -0
- package/dist/services/HealingCacheService.d.ts.map +1 -0
- package/dist/services/HealingCacheService.js +404 -0
- package/dist/services/HealthReportService.d.ts +10 -0
- package/dist/services/HealthReportService.d.ts.map +1 -0
- package/dist/services/HealthReportService.js +84 -0
- package/dist/services/InitializerUpdater.d.ts +16 -0
- package/dist/services/InitializerUpdater.d.ts.map +1 -0
- package/dist/services/InitializerUpdater.js +73 -0
- package/dist/services/IntentAuditor.d.ts +56 -0
- package/dist/services/IntentAuditor.d.ts.map +1 -0
- package/dist/services/IntentAuditor.js +326 -0
- package/dist/services/InteractiveReview.d.ts +15 -0
- package/dist/services/InteractiveReview.d.ts.map +1 -0
- package/dist/services/InteractiveReview.js +128 -0
- package/dist/services/LLMService.d.ts +121 -0
- package/dist/services/LLMService.d.ts.map +1 -0
- package/dist/services/LLMService.js +475 -0
- package/dist/services/MutationApplier.d.ts +69 -0
- package/dist/services/MutationApplier.d.ts.map +1 -0
- package/dist/services/MutationApplier.js +360 -0
- package/dist/services/PRAutomationService.d.ts +50 -0
- package/dist/services/PRAutomationService.d.ts.map +1 -0
- package/dist/services/PRAutomationService.js +760 -0
- package/dist/services/PendingPromptLedger.d.ts +51 -0
- package/dist/services/PendingPromptLedger.d.ts.map +1 -0
- package/dist/services/PendingPromptLedger.js +205 -0
- package/dist/services/ReportGenerator.d.ts +156 -0
- package/dist/services/ReportGenerator.d.ts.map +1 -0
- package/dist/services/ReportGenerator.js +278 -0
- package/dist/services/ReportMergeService.d.ts +95 -0
- package/dist/services/ReportMergeService.d.ts.map +1 -0
- package/dist/services/ReportMergeService.js +0 -0
- package/dist/services/SafetyGuard.d.ts +112 -0
- package/dist/services/SafetyGuard.d.ts.map +1 -0
- package/dist/services/SafetyGuard.js +87 -0
- package/dist/services/Sanitizer.d.ts +28 -0
- package/dist/services/Sanitizer.d.ts.map +1 -0
- package/dist/services/Sanitizer.js +142 -0
- package/dist/services/SanitizingAnthropic.d.ts +19 -0
- package/dist/services/SanitizingAnthropic.d.ts.map +1 -0
- package/dist/services/SanitizingAnthropic.js +84 -0
- package/dist/services/SelectorSanitizer.d.ts +52 -0
- package/dist/services/SelectorSanitizer.d.ts.map +1 -0
- package/dist/services/SelectorSanitizer.js +318 -0
- package/dist/services/SnapshotService.d.ts +11 -0
- package/dist/services/SnapshotService.d.ts.map +1 -0
- package/dist/services/SnapshotService.js +352 -0
- package/dist/services/SourceLinkService.d.ts +26 -0
- package/dist/services/SourceLinkService.d.ts.map +1 -0
- package/dist/services/SourceLinkService.js +165 -0
- package/dist/services/SourceUpdater.d.ts +45 -0
- package/dist/services/SourceUpdater.d.ts.map +1 -0
- package/dist/services/SourceUpdater.js +1091 -0
- package/dist/services/TemplateDiffService.d.ts +25 -0
- package/dist/services/TemplateDiffService.d.ts.map +1 -0
- package/dist/services/TemplateDiffService.js +334 -0
- package/dist/services/TraceBackEngine.d.ts +67 -0
- package/dist/services/TraceBackEngine.d.ts.map +1 -0
- package/dist/services/TraceBackEngine.js +672 -0
- package/dist/services/WorkspaceSnapshotService.d.ts +71 -0
- package/dist/services/WorkspaceSnapshotService.d.ts.map +1 -0
- package/dist/services/WorkspaceSnapshotService.js +200 -0
- package/dist/services/safety/PipelineContext.d.ts +63 -0
- package/dist/services/safety/PipelineContext.d.ts.map +1 -0
- package/dist/services/safety/PipelineContext.js +34 -0
- package/dist/services/safety/roleClassifier.d.ts +4 -0
- package/dist/services/safety/roleClassifier.d.ts.map +1 -0
- package/dist/services/safety/roleClassifier.js +233 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts +3 -0
- package/dist/services/safety/rules/ConfidenceTierRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ConfidenceTierRule.js +32 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts +10 -0
- package/dist/services/safety/rules/DangerousSinkRule.d.ts.map +1 -0
- package/dist/services/safety/rules/DangerousSinkRule.js +53 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts +3 -0
- package/dist/services/safety/rules/FunctionalRoleRule.d.ts.map +1 -0
- package/dist/services/safety/rules/FunctionalRoleRule.js +64 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts +3 -0
- package/dist/services/safety/rules/HardConfidenceRule.d.ts.map +1 -0
- package/dist/services/safety/rules/HardConfidenceRule.js +22 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts +3 -0
- package/dist/services/safety/rules/IntentAuditorRule.d.ts.map +1 -0
- package/dist/services/safety/rules/IntentAuditorRule.js +97 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts +3 -0
- package/dist/services/safety/rules/SemanticOppositeRule.d.ts.map +1 -0
- package/dist/services/safety/rules/SemanticOppositeRule.js +30 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts +3 -0
- package/dist/services/safety/rules/StructuralOnlyRule.d.ts.map +1 -0
- package/dist/services/safety/rules/StructuralOnlyRule.js +38 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts +3 -0
- package/dist/services/safety/rules/VisibilityRule.d.ts.map +1 -0
- package/dist/services/safety/rules/VisibilityRule.js +53 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts +3 -0
- package/dist/services/safety/rules/ZeroTrustRule.d.ts.map +1 -0
- package/dist/services/safety/rules/ZeroTrustRule.js +37 -0
- package/dist/services/safety/rules/index.d.ts +3 -0
- package/dist/services/safety/rules/index.d.ts.map +1 -0
- package/dist/services/safety/rules/index.js +34 -0
- package/dist/services/types.d.ts +59 -0
- package/dist/services/types.d.ts.map +1 -0
- package/dist/services/types.js +2 -0
- package/dist/storage/SnapshotManager.d.ts +5 -0
- package/dist/storage/SnapshotManager.d.ts.map +1 -0
- package/dist/storage/SnapshotManager.js +32 -0
- package/dist/types/index.d.ts +95 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +7 -0
- package/dist/utils/DOMUtils.d.ts +49 -0
- package/dist/utils/DOMUtils.d.ts.map +1 -0
- package/dist/utils/DOMUtils.js +466 -0
- package/dist/utils/FileLock.d.ts +44 -0
- package/dist/utils/FileLock.d.ts.map +1 -0
- package/dist/utils/FileLock.js +189 -0
- package/dist/utils/IsolatedDiff.d.ts +45 -0
- package/dist/utils/IsolatedDiff.d.ts.map +1 -0
- package/dist/utils/IsolatedDiff.js +382 -0
- package/dist/utils/StackUtils.d.ts +11 -0
- package/dist/utils/StackUtils.d.ts.map +1 -0
- package/dist/utils/StackUtils.js +120 -0
- package/dist/utils/SummaryTable.d.ts +17 -0
- package/dist/utils/SummaryTable.d.ts.map +1 -0
- package/dist/utils/SummaryTable.js +37 -0
- package/dist/utils/atomicWrite.d.ts +9 -0
- package/dist/utils/atomicWrite.d.ts.map +1 -0
- package/dist/utils/atomicWrite.js +83 -0
- package/dist/utils/logger.d.ts +59 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +123 -0
- package/dist/vendor/enquirer.d.ts +33 -0
- package/dist/vendor/enquirer.d.ts.map +1 -0
- package/dist/vendor/enquirer.js +11 -0
- package/package.json +71 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/config/DryRunGuard.ts
|
|
3
|
+
//
|
|
4
|
+
// Single source of truth for the "dry run" runtime gate.
|
|
5
|
+
//
|
|
6
|
+
// The flag is sourced from the `SELA_DRY_RUN` environment variable using the
|
|
7
|
+
// strict parser in SelaConfig.parseEnvBoolean (no silent coercion - bad
|
|
8
|
+
// values throw). The variable is re-read on every call so tests and
|
|
9
|
+
// long-running processes can toggle it without restarting the engine.
|
|
10
|
+
//
|
|
11
|
+
// Two writer paths route through this guard:
|
|
12
|
+
//
|
|
13
|
+
// 1. SourceUpdater._coreUpdate - every `fs.writeFileSync` to a spec/source
|
|
14
|
+
// file. When dry-run is active the write is skipped and a console
|
|
15
|
+
// breadcrumb is emitted with the absolute path that *would have* been
|
|
16
|
+
// written.
|
|
17
|
+
//
|
|
18
|
+
// 2. ASTSourceUpdater._persistChanges - wraps ts-morph `SourceFile.saveSync`.
|
|
19
|
+
// When dry-run is active the in-memory AST mutation stays, but the
|
|
20
|
+
// `saveSync` call (which forwards to fs.writeFileSync inside ts-morph)
|
|
21
|
+
// is skipped so the spec on disk is identical pre/post heal.
|
|
22
|
+
//
|
|
23
|
+
// Git auto-commit operations consult `DryRunGuard.active()` directly so
|
|
24
|
+
// `git add` / `git commit` never fire during a dry run.
|
|
25
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
28
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
29
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(o, k2, desc);
|
|
32
|
+
}) : (function(o, m, k, k2) {
|
|
33
|
+
if (k2 === undefined) k2 = k;
|
|
34
|
+
o[k2] = m[k];
|
|
35
|
+
}));
|
|
36
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
37
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
38
|
+
}) : function(o, v) {
|
|
39
|
+
o["default"] = v;
|
|
40
|
+
});
|
|
41
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
42
|
+
var ownKeys = function(o) {
|
|
43
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
44
|
+
var ar = [];
|
|
45
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
46
|
+
return ar;
|
|
47
|
+
};
|
|
48
|
+
return ownKeys(o);
|
|
49
|
+
};
|
|
50
|
+
return function (mod) {
|
|
51
|
+
if (mod && mod.__esModule) return mod;
|
|
52
|
+
var result = {};
|
|
53
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
54
|
+
__setModuleDefault(result, mod);
|
|
55
|
+
return result;
|
|
56
|
+
};
|
|
57
|
+
})();
|
|
58
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
59
|
+
exports.DryRunGuard = void 0;
|
|
60
|
+
const path = __importStar(require("path"));
|
|
61
|
+
const SelaConfig_1 = require("./SelaConfig");
|
|
62
|
+
const logger_1 = require("../utils/logger");
|
|
63
|
+
exports.DryRunGuard = {
|
|
64
|
+
/**
|
|
65
|
+
* Returns true when `SELA_DRY_RUN` is set to a truthy value in the current
|
|
66
|
+
* process environment. Throws via `parseEnvBoolean` when the value is not
|
|
67
|
+
* one of the accepted tokens - fail-fast keeps misconfigured CI loud.
|
|
68
|
+
*/
|
|
69
|
+
active(env = process.env) {
|
|
70
|
+
return (0, SelaConfig_1.parseEnvBoolean)("SELA_DRY_RUN", env.SELA_DRY_RUN);
|
|
71
|
+
},
|
|
72
|
+
/**
|
|
73
|
+
* Centralised log line for skipped writes so the diff between a dry run
|
|
74
|
+
* and a real run is auditable in test output and CI logs.
|
|
75
|
+
*/
|
|
76
|
+
logSkippedWrite(label, target) {
|
|
77
|
+
const display = target ? path.basename(target) : "<no-path>";
|
|
78
|
+
logger_1.logger.debug(`Dry Run - skip ${label} → ${display}`);
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export interface PathScopeDecision {
|
|
2
|
+
/** True when the write is permitted. */
|
|
3
|
+
allowed: boolean;
|
|
4
|
+
/** Absolute, normalized (symlink-resolved when possible) path evaluated. */
|
|
5
|
+
resolved: string;
|
|
6
|
+
/** cwd-relative POSIX form actually matched against the globs. */
|
|
7
|
+
relative: string;
|
|
8
|
+
/** The allowedPaths entry that authorized the write, or null. */
|
|
9
|
+
matchedPattern: string | null;
|
|
10
|
+
/** Human-readable explanation (surfaced in the security warning / tests). */
|
|
11
|
+
reason: string;
|
|
12
|
+
}
|
|
13
|
+
export interface PathScopeOptions {
|
|
14
|
+
/** Override the allowlist. Defaults to `ConfigLoader.getInstance().allowedPaths`. */
|
|
15
|
+
allowedPaths?: string[];
|
|
16
|
+
/** Override the working directory. Defaults to `process.cwd()`. */
|
|
17
|
+
cwd?: string;
|
|
18
|
+
/** Override platform for case-sensitivity. Defaults to `process.platform`. */
|
|
19
|
+
platform?: NodeJS.Platform;
|
|
20
|
+
/** Resolve symlinks before the escape check (escape hardening). Default: true. */
|
|
21
|
+
followSymlinks?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare const PathScopeGuard: {
|
|
24
|
+
/**
|
|
25
|
+
* Pure decision function — no logging, no config I/O. Given a raw path and
|
|
26
|
+
* an explicit allowlist, returns whether the write is permitted.
|
|
27
|
+
*
|
|
28
|
+
* `allowedPaths` undefined or `[]` => allow all (backward-compatible).
|
|
29
|
+
*/
|
|
30
|
+
evaluate(rawPath: string, opts?: PathScopeOptions): PathScopeDecision;
|
|
31
|
+
/**
|
|
32
|
+
* Production gate used by the write heads. Reads `allowedPaths` from the
|
|
33
|
+
* resolved config (unless injected), evaluates, and logs a security warning
|
|
34
|
+
* on block. Returns true when the write may proceed.
|
|
35
|
+
*
|
|
36
|
+
* If config resolution itself fails, fail OPEN (allow) rather than break
|
|
37
|
+
* every heal — path scoping is a guardrail, not a kill switch.
|
|
38
|
+
*/
|
|
39
|
+
enforce(rawPath: string, opts?: PathScopeOptions): boolean;
|
|
40
|
+
logBlocked(decision: PathScopeDecision, allowedPaths: string[]): void;
|
|
41
|
+
};
|
|
42
|
+
//# sourceMappingURL=PathScopeGuard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PathScopeGuard.d.ts","sourceRoot":"","sources":["../../src/config/PathScopeGuard.ts"],"names":[],"mappings":"AA4BA,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,QAAQ,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,mEAAmE;IACnE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,kFAAkF;IAClF,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AA2CD,eAAO,MAAM,cAAc;IACzB;;;;;OAKG;sBACe,MAAM,SAAQ,gBAAgB,GAAQ,iBAAiB;IA6DzE;;;;;;;OAOG;qBACc,MAAM,SAAQ,gBAAgB,GAAQ,OAAO;yBAOzC,iBAAiB,gBAAgB,MAAM,EAAE,GAAG,IAAI;CAOtE,CAAC"}
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/config/PathScopeGuard.ts
|
|
3
|
+
//
|
|
4
|
+
// Single source of truth for the "safe path scoping" runtime gate (T1).
|
|
5
|
+
//
|
|
6
|
+
// Every disk-mutating write head in the heal pipeline (SourceUpdater,
|
|
7
|
+
// ASTSourceUpdater, InitializerUpdater, MutationApplier) routes through
|
|
8
|
+
// `PathScopeGuard.enforce(filePath)` immediately before its existing
|
|
9
|
+
// `DryRunGuard.active()` check. A heal whose target file falls outside the
|
|
10
|
+
// configured `allowedPaths` is blocked, the file is left untouched, and a
|
|
11
|
+
// security warning is logged.
|
|
12
|
+
//
|
|
13
|
+
// The allowlist comes from `ConfigLoader.getInstance().allowedPaths`, so the
|
|
14
|
+
// write heads need zero extra plumbing (mirrors how DryRunGuard reads the
|
|
15
|
+
// environment directly). Tests inject `allowedPaths` / `cwd` / `platform`
|
|
16
|
+
// for deterministic, filesystem-independent assertions.
|
|
17
|
+
//
|
|
18
|
+
// CI vs Local: both sides are normalized to a cwd-relative POSIX path before
|
|
19
|
+
// glob matching, so a CI absolute checkout path
|
|
20
|
+
// (`/home/runner/work/repo/tests/a.spec.ts`) and a local relative path
|
|
21
|
+
// (`tests/a.spec.ts`) converge to the same `tests/a.spec.ts` matched against
|
|
22
|
+
// the same `tests/**` pattern.
|
|
23
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
26
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
27
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
28
|
+
}
|
|
29
|
+
Object.defineProperty(o, k2, desc);
|
|
30
|
+
}) : (function(o, m, k, k2) {
|
|
31
|
+
if (k2 === undefined) k2 = k;
|
|
32
|
+
o[k2] = m[k];
|
|
33
|
+
}));
|
|
34
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
35
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
36
|
+
}) : function(o, v) {
|
|
37
|
+
o["default"] = v;
|
|
38
|
+
});
|
|
39
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
40
|
+
var ownKeys = function(o) {
|
|
41
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
42
|
+
var ar = [];
|
|
43
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
44
|
+
return ar;
|
|
45
|
+
};
|
|
46
|
+
return ownKeys(o);
|
|
47
|
+
};
|
|
48
|
+
return function (mod) {
|
|
49
|
+
if (mod && mod.__esModule) return mod;
|
|
50
|
+
var result = {};
|
|
51
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
52
|
+
__setModuleDefault(result, mod);
|
|
53
|
+
return result;
|
|
54
|
+
};
|
|
55
|
+
})();
|
|
56
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
+
exports.PathScopeGuard = void 0;
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const minimatch_1 = require("minimatch");
|
|
61
|
+
const ConfigLoader_1 = require("./ConfigLoader");
|
|
62
|
+
const logger_1 = require("../utils/logger");
|
|
63
|
+
/**
|
|
64
|
+
* Resolve symlinks best-effort. The target file may not exist yet (a fresh
|
|
65
|
+
* write), so fall back to realpath-ing the nearest existing ancestor and
|
|
66
|
+
* re-appending the basename. Never throws.
|
|
67
|
+
*/
|
|
68
|
+
function realpathSafe(abs) {
|
|
69
|
+
try {
|
|
70
|
+
return fs.realpathSync.native(abs);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
try {
|
|
74
|
+
const dir = path.dirname(abs);
|
|
75
|
+
const base = path.basename(abs);
|
|
76
|
+
return path.join(fs.realpathSync.native(dir), base);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return abs;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Normalize an allowedPaths entry: backslashes → POSIX slashes, strip a
|
|
85
|
+
* leading `./`, and expand a bare folder (`tests` / `tests/`) into a
|
|
86
|
+
* recursive `tests/**` glob so folder entries match their contents.
|
|
87
|
+
*/
|
|
88
|
+
function normalizePattern(pattern) {
|
|
89
|
+
let s = pattern.replace(/\\/g, "/").trim().replace(/^\.\//, "");
|
|
90
|
+
const hasMagic = /[*?[\]{}!]/.test(s);
|
|
91
|
+
if (!hasMagic) {
|
|
92
|
+
const last = s.split("/").pop() ?? "";
|
|
93
|
+
const looksLikeDir = s.endsWith("/") || !last.includes(".");
|
|
94
|
+
if (looksLikeDir) {
|
|
95
|
+
s = s.replace(/\/+$/, "") + "/**";
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return s;
|
|
99
|
+
}
|
|
100
|
+
function isAbsolutePattern(norm) {
|
|
101
|
+
return path.posix.isAbsolute(norm) || /^[A-Za-z]:/.test(norm);
|
|
102
|
+
}
|
|
103
|
+
exports.PathScopeGuard = {
|
|
104
|
+
/**
|
|
105
|
+
* Pure decision function — no logging, no config I/O. Given a raw path and
|
|
106
|
+
* an explicit allowlist, returns whether the write is permitted.
|
|
107
|
+
*
|
|
108
|
+
* `allowedPaths` undefined or `[]` => allow all (backward-compatible).
|
|
109
|
+
*/
|
|
110
|
+
evaluate(rawPath, opts = {}) {
|
|
111
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
112
|
+
const allow = opts.allowedPaths;
|
|
113
|
+
const absRaw = path.isAbsolute(rawPath)
|
|
114
|
+
? rawPath
|
|
115
|
+
: path.resolve(cwd, rawPath);
|
|
116
|
+
let abs = path.normalize(absRaw);
|
|
117
|
+
if (opts.followSymlinks !== false) {
|
|
118
|
+
abs = realpathSafe(abs);
|
|
119
|
+
}
|
|
120
|
+
const relative = path.relative(cwd, abs).split(path.sep).join("/");
|
|
121
|
+
const absPosix = abs.split(path.sep).join("/");
|
|
122
|
+
// No allowlist configured → allow all (unset and [] are equivalent).
|
|
123
|
+
if (!allow || allow.length === 0) {
|
|
124
|
+
return {
|
|
125
|
+
allowed: true,
|
|
126
|
+
resolved: abs,
|
|
127
|
+
relative,
|
|
128
|
+
matchedPattern: null,
|
|
129
|
+
reason: "no allowedPaths configured (allow-all)",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
const platform = opts.platform ?? process.platform;
|
|
133
|
+
const mmOpts = { dot: true, nocase: platform === "win32" };
|
|
134
|
+
// Out-of-tree resolution (`..` traversal / symlink escape): only an
|
|
135
|
+
// explicit ABSOLUTE allowedPaths entry may authorize it. Relative
|
|
136
|
+
// patterns can never reach outside the project root.
|
|
137
|
+
const escaped = relative === "" || relative.startsWith("..");
|
|
138
|
+
for (const pattern of allow) {
|
|
139
|
+
const norm = normalizePattern(pattern);
|
|
140
|
+
const patIsAbs = isAbsolutePattern(norm);
|
|
141
|
+
if (escaped && !patIsAbs)
|
|
142
|
+
continue;
|
|
143
|
+
const target = patIsAbs ? absPosix : relative;
|
|
144
|
+
if ((0, minimatch_1.minimatch)(target, norm, mmOpts)) {
|
|
145
|
+
return {
|
|
146
|
+
allowed: true,
|
|
147
|
+
resolved: abs,
|
|
148
|
+
relative,
|
|
149
|
+
matchedPattern: pattern,
|
|
150
|
+
reason: `matched allowedPaths entry "${pattern}"`,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return {
|
|
155
|
+
allowed: false,
|
|
156
|
+
resolved: abs,
|
|
157
|
+
relative,
|
|
158
|
+
matchedPattern: null,
|
|
159
|
+
reason: escaped
|
|
160
|
+
? `path resolves outside the project root ("${relative}") and no absolute allowedPaths entry matched`
|
|
161
|
+
: `path "${relative}" is outside allowedPaths`,
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
/**
|
|
165
|
+
* Production gate used by the write heads. Reads `allowedPaths` from the
|
|
166
|
+
* resolved config (unless injected), evaluates, and logs a security warning
|
|
167
|
+
* on block. Returns true when the write may proceed.
|
|
168
|
+
*
|
|
169
|
+
* If config resolution itself fails, fail OPEN (allow) rather than break
|
|
170
|
+
* every heal — path scoping is a guardrail, not a kill switch.
|
|
171
|
+
*/
|
|
172
|
+
enforce(rawPath, opts = {}) {
|
|
173
|
+
const allowedPaths = opts.allowedPaths ?? readAllowedPaths();
|
|
174
|
+
const decision = this.evaluate(rawPath, { ...opts, allowedPaths });
|
|
175
|
+
if (!decision.allowed)
|
|
176
|
+
this.logBlocked(decision, allowedPaths);
|
|
177
|
+
return decision.allowed;
|
|
178
|
+
},
|
|
179
|
+
logBlocked(decision, allowedPaths) {
|
|
180
|
+
logger_1.logger.warn(`SECURITY: blocked mutation outside allowedPaths — ${decision.resolved}\n` +
|
|
181
|
+
` ${decision.reason}\n` +
|
|
182
|
+
` allowedPaths: ${JSON.stringify(allowedPaths)}`);
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
function readAllowedPaths() {
|
|
186
|
+
try {
|
|
187
|
+
return ConfigLoader_1.ConfigLoader.getInstance().allowedPaths;
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
export type PRStrategy = "directCommit" | "pr" | "draftPR" | "readOnly";
|
|
2
|
+
export type BugAction = "failCI" | "createIssue" | "labelOnly";
|
|
3
|
+
export interface SelaPRAutomationConfig {
|
|
4
|
+
/** Master switch. Default: false (fully backward-compatible). */
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
/** Git-flow strategy. Default: 'readOnly' (safest). */
|
|
7
|
+
strategy?: PRStrategy;
|
|
8
|
+
/** Branches on which directCommit is FORBIDDEN - auto-downgraded. */
|
|
9
|
+
protectedBranches?: string[];
|
|
10
|
+
/** Trust-aware downgrade thresholds (0–100). 0 = never downgrade by that metric. */
|
|
11
|
+
reviewThresholds?: {
|
|
12
|
+
/** Min AI confidence required for directCommit. Below → downgrade. */
|
|
13
|
+
minConfidenceForDirectCommit?: number;
|
|
14
|
+
/** Min auditor adjusted confidence required for directCommit. Below → downgrade. */
|
|
15
|
+
minAuditorScoreForDirectCommit?: number;
|
|
16
|
+
/** Strategy to downgrade TO. Default: 'draftPR'. */
|
|
17
|
+
downgradeTo?: Exclude<PRStrategy, "directCommit">;
|
|
18
|
+
};
|
|
19
|
+
/** Behavior when SafetyGuard blocks a heal (PROTECTED events). Default: 'failCI'. */
|
|
20
|
+
onBugDetected?: BugAction;
|
|
21
|
+
/** New PR branch name. Vars: ${id}, ${timestamp}. Default: 'sela/heal-${id}'. */
|
|
22
|
+
branchNameTemplate?: string;
|
|
23
|
+
/** PR/Issue title template. Vars: ${testName}, ${fileName}, ${count}. */
|
|
24
|
+
titleTemplate?: string;
|
|
25
|
+
/** PR body template (Markdown). null = engine uses built-in rich template. */
|
|
26
|
+
bodyTemplate?: string;
|
|
27
|
+
/** Labels attached to PR/Issue. */
|
|
28
|
+
labels?: string[];
|
|
29
|
+
/** Path/URL of report artifact. Default: 'sela-report.html'. */
|
|
30
|
+
reportArtifactPath?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface SanitizationConfig {
|
|
33
|
+
/** Master switch. Default: true (secure by default). */
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Mask the `value=` of `<input>` elements whose type is password/tel/email.
|
|
37
|
+
* This is structural (type-driven), not text-based, so it never touches a
|
|
38
|
+
* button label or heading. Default: true.
|
|
39
|
+
*/
|
|
40
|
+
redactInputValues?: boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Extra regex sources (as strings) merged with the built-in pattern table.
|
|
43
|
+
* Each is compiled with the global flag; invalid patterns are skipped with
|
|
44
|
+
* a warning rather than throwing.
|
|
45
|
+
*/
|
|
46
|
+
customPatterns?: string[];
|
|
47
|
+
}
|
|
48
|
+
export interface SelaConfig {
|
|
49
|
+
/** Shorthand preset that expands to concrete threshold values. */
|
|
50
|
+
healingPolicy?: "conservative" | "balanced" | "aggressive";
|
|
51
|
+
/** Fine-grained threshold overrides. Explicit values always win over preset expansion. */
|
|
52
|
+
thresholds?: {
|
|
53
|
+
/** Minimum AI confidence to attempt a heal. Default: 85. Maps to CONFIDENCE_HARD_STOP. */
|
|
54
|
+
minHealerConfidence?: number;
|
|
55
|
+
/** How strictly the Intent Auditor treats SUSPICIOUS verdicts. Default: 'balanced'. */
|
|
56
|
+
auditorStrictness?: "strict" | "balanced" | "loose";
|
|
57
|
+
};
|
|
58
|
+
agenticDiscovery?: {
|
|
59
|
+
enabled?: boolean;
|
|
60
|
+
maxSteps?: number;
|
|
61
|
+
forbiddenSelectors?: string[];
|
|
62
|
+
};
|
|
63
|
+
/** How healed selectors are persisted. Default: 'inPlace'. */
|
|
64
|
+
updateStrategy?: "inPlace" | "commentOnly" | "branch";
|
|
65
|
+
/**
|
|
66
|
+
* @deprecated Use `prAutomation.strategy = 'directCommit'` instead.
|
|
67
|
+
* When `prAutomation.enabled` is true this flag is forced to false and
|
|
68
|
+
* git operations are batched at end-of-run inside SelaEngine.commitUpdates().
|
|
69
|
+
* Legacy mapping: `autoCommit: true` with no `prAutomation` block →
|
|
70
|
+
* `prAutomation = { enabled: true, strategy: 'directCommit' }`.
|
|
71
|
+
*/
|
|
72
|
+
autoCommit?: boolean;
|
|
73
|
+
/** Git-flow automation (PR/draft PR/direct commit/read-only). See SelaPRAutomationConfig. */
|
|
74
|
+
prAutomation?: SelaPRAutomationConfig;
|
|
75
|
+
/** Directory for DNA snapshots. Default: 'sela-snapshots'. */
|
|
76
|
+
dnaStoragePath?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Allowlist of globs/folders Sela may MUTATE. A heal whose target file
|
|
79
|
+
* falls outside this scope is blocked, the file is skipped, and a security
|
|
80
|
+
* warning is logged. Matched against the cwd-relative posix path so the
|
|
81
|
+
* same patterns work for local relative paths and CI absolute checkouts.
|
|
82
|
+
* Unset or `[]` => allow all (backward-compatible). Default: [].
|
|
83
|
+
*/
|
|
84
|
+
allowedPaths?: string[];
|
|
85
|
+
/**
|
|
86
|
+
* Maximum number of LLM-backed heals permitted in a single run. Once
|
|
87
|
+
* reached, Sela stops triggering the LLM, finalizes already-applied fixes,
|
|
88
|
+
* and warns the user. Cache replays do NOT count. Unset => unlimited.
|
|
89
|
+
* Must be a positive integer when provided (0/non-integer throws).
|
|
90
|
+
*/
|
|
91
|
+
maxHealsPerRun?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Pre-flight PII/secrets masking of all outbound LLM payloads. Enabled by
|
|
94
|
+
* default (this is a security control). Masking is in-memory only and
|
|
95
|
+
* targets high-precision patterns + sensitive input values, leaving the
|
|
96
|
+
* structural anchors the healer relies on intact. See SanitizationConfig.
|
|
97
|
+
*/
|
|
98
|
+
sanitization?: SanitizationConfig;
|
|
99
|
+
/**
|
|
100
|
+
* Per-branch config overrides. Keys are exact branch names.
|
|
101
|
+
* Merge order: expandPreset(base) → base.thresholds → expandPreset(override) → override.thresholds
|
|
102
|
+
*/
|
|
103
|
+
branchOverrides?: Record<string, Partial<SelaConfig>>;
|
|
104
|
+
}
|
|
105
|
+
export interface ResolvedThresholds {
|
|
106
|
+
/** Minimum AI confidence (0–100). Heals below this are always BLOCKED. */
|
|
107
|
+
confidenceHardStop: number;
|
|
108
|
+
/** Confidence below which a heal is tagged REQUIRES_REVIEW instead of SAFE. */
|
|
109
|
+
confidenceReviewThreshold: number;
|
|
110
|
+
/** Minimum auditor confidence to treat a verdict as CONSISTENT. */
|
|
111
|
+
auditorConfidenceMin: number;
|
|
112
|
+
/** When true, SUSPICIOUS in assertion mode yields REQUIRES_REVIEW instead of BLOCKED. */
|
|
113
|
+
allowSuspicious: boolean;
|
|
114
|
+
/** The resolved strictness name - used for policy-aware log messages. */
|
|
115
|
+
auditorStrictness: "strict" | "balanced" | "loose";
|
|
116
|
+
}
|
|
117
|
+
export interface ResolvedPRAutomation {
|
|
118
|
+
enabled: boolean;
|
|
119
|
+
strategy: PRStrategy;
|
|
120
|
+
protectedBranches: string[];
|
|
121
|
+
reviewThresholds: {
|
|
122
|
+
minConfidenceForDirectCommit: number;
|
|
123
|
+
minAuditorScoreForDirectCommit: number;
|
|
124
|
+
downgradeTo: Exclude<PRStrategy, "directCommit">;
|
|
125
|
+
};
|
|
126
|
+
onBugDetected: BugAction;
|
|
127
|
+
branchNameTemplate: string;
|
|
128
|
+
titleTemplate: string;
|
|
129
|
+
bodyTemplate: string | null;
|
|
130
|
+
labels: string[];
|
|
131
|
+
reportArtifactPath: string;
|
|
132
|
+
}
|
|
133
|
+
export interface ResolvedSanitization {
|
|
134
|
+
enabled: boolean;
|
|
135
|
+
redactInputValues: boolean;
|
|
136
|
+
customPatterns: string[];
|
|
137
|
+
}
|
|
138
|
+
export interface ResolvedConfig {
|
|
139
|
+
thresholds: ResolvedThresholds;
|
|
140
|
+
updateStrategy: "inPlace" | "commentOnly" | "branch";
|
|
141
|
+
autoCommit: boolean;
|
|
142
|
+
dnaStoragePath: string;
|
|
143
|
+
prAutomation: ResolvedPRAutomation;
|
|
144
|
+
agenticDiscovery: {
|
|
145
|
+
enabled: boolean;
|
|
146
|
+
maxSteps: number;
|
|
147
|
+
forbiddenSelectors: string[];
|
|
148
|
+
};
|
|
149
|
+
/** Allowlist of globs/folders Sela may mutate. `[]` = allow all. */
|
|
150
|
+
allowedPaths: string[];
|
|
151
|
+
/** Max LLM-backed heals per run. `null` = unlimited. */
|
|
152
|
+
maxHealsPerRun: number | null;
|
|
153
|
+
/** Resolved PII/secrets masking config (always defaulted). */
|
|
154
|
+
sanitization: ResolvedSanitization;
|
|
155
|
+
/**
|
|
156
|
+
* When true, every disk-mutating side-effect in the heal pipeline is
|
|
157
|
+
* suppressed: `fs.writeFileSync`, ts-morph `saveSync`, and git auto-commits.
|
|
158
|
+
* The heal still runs end-to-end (LLM call, SafetyGuard, candidate diff),
|
|
159
|
+
* but no source files are modified and no commits are produced.
|
|
160
|
+
* Sourced from `SELA_DRY_RUN` env var (`1`, `true`, `yes`, `on`).
|
|
161
|
+
*/
|
|
162
|
+
dryRun: boolean;
|
|
163
|
+
/**
|
|
164
|
+
* When true, the healing cache layer is bypassed - every heal request
|
|
165
|
+
* goes straight to the LLM. Sourced from `SELA_NO_CACHE` env var.
|
|
166
|
+
*/
|
|
167
|
+
noCache: boolean;
|
|
168
|
+
}
|
|
169
|
+
export declare function resolveThresholds(config: SelaConfig): ResolvedThresholds;
|
|
170
|
+
export declare function parseEnvBoolean(varName: string, rawValue: string | undefined): boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Validates maxHealsPerRun. Fail-fast (mirrors parseEnvBoolean philosophy):
|
|
173
|
+
* a misconfigured limit should crash loudly, not silently run unbounded.
|
|
174
|
+
* undefined => null (unlimited). 0 / negative / non-integer => throw.
|
|
175
|
+
*/
|
|
176
|
+
export declare function resolveMaxHealsPerRun(value: number | undefined): number | null;
|
|
177
|
+
export declare function resolveConfig(config: SelaConfig, env?: NodeJS.ProcessEnv): ResolvedConfig;
|
|
178
|
+
//# sourceMappingURL=SelaConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SelaConfig.d.ts","sourceRoot":"","sources":["../../src/config/SelaConfig.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/config/SelaConfig.ts
|
|
3
|
+
// Single source of truth for all Sela configuration types and preset expansion logic.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.resolveThresholds = resolveThresholds;
|
|
6
|
+
exports.parseEnvBoolean = parseEnvBoolean;
|
|
7
|
+
exports.resolveMaxHealsPerRun = resolveMaxHealsPerRun;
|
|
8
|
+
exports.resolveConfig = resolveConfig;
|
|
9
|
+
const PRESET_MAP = {
|
|
10
|
+
conservative: {
|
|
11
|
+
minHealerConfidence: 92,
|
|
12
|
+
auditorStrictness: "strict",
|
|
13
|
+
},
|
|
14
|
+
balanced: {
|
|
15
|
+
minHealerConfidence: 85,
|
|
16
|
+
auditorStrictness: "balanced",
|
|
17
|
+
},
|
|
18
|
+
aggressive: {
|
|
19
|
+
minHealerConfidence: 75,
|
|
20
|
+
auditorStrictness: "loose",
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
const STRICTNESS_CONFIDENCE = {
|
|
24
|
+
strict: 90,
|
|
25
|
+
balanced: 75,
|
|
26
|
+
loose: 60,
|
|
27
|
+
};
|
|
28
|
+
function expandPreset(policy) {
|
|
29
|
+
return policy ? { ...PRESET_MAP[policy] } : {};
|
|
30
|
+
}
|
|
31
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
32
|
+
// RESOLUTION HELPERS
|
|
33
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
34
|
+
function resolveThresholds(config) {
|
|
35
|
+
const fromPreset = expandPreset(config.healingPolicy);
|
|
36
|
+
// Explicit thresholds always win over preset expansion
|
|
37
|
+
const merged = { ...fromPreset, ...config.thresholds };
|
|
38
|
+
const minHealerConfidence = merged.minHealerConfidence ?? 85;
|
|
39
|
+
const auditorStrictness = merged.auditorStrictness ?? "balanced";
|
|
40
|
+
return {
|
|
41
|
+
confidenceHardStop: minHealerConfidence,
|
|
42
|
+
confidenceReviewThreshold: 95,
|
|
43
|
+
auditorConfidenceMin: STRICTNESS_CONFIDENCE[auditorStrictness],
|
|
44
|
+
// allowSuspicious is derived - never independently settable to avoid footguns
|
|
45
|
+
allowSuspicious: auditorStrictness === "loose",
|
|
46
|
+
auditorStrictness,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function resolvePRAutomation(config) {
|
|
50
|
+
const pr = config.prAutomation ?? {};
|
|
51
|
+
// Legacy bridge: autoCommit:true with no explicit prAutomation block
|
|
52
|
+
// → enable prAutomation in directCommit mode. Explicit prAutomation always wins.
|
|
53
|
+
const legacyEnabled = config.autoCommit === true && config.prAutomation === undefined;
|
|
54
|
+
const enabled = pr.enabled ?? legacyEnabled;
|
|
55
|
+
const strategy = pr.strategy ?? (legacyEnabled ? "directCommit" : "readOnly");
|
|
56
|
+
return {
|
|
57
|
+
enabled,
|
|
58
|
+
strategy,
|
|
59
|
+
protectedBranches: pr.protectedBranches ?? ["main", "master", "release"],
|
|
60
|
+
reviewThresholds: {
|
|
61
|
+
minConfidenceForDirectCommit: pr.reviewThresholds?.minConfidenceForDirectCommit ?? 0,
|
|
62
|
+
minAuditorScoreForDirectCommit: pr.reviewThresholds?.minAuditorScoreForDirectCommit ?? 0,
|
|
63
|
+
downgradeTo: pr.reviewThresholds?.downgradeTo ?? "draftPR",
|
|
64
|
+
},
|
|
65
|
+
onBugDetected: pr.onBugDetected ?? "failCI",
|
|
66
|
+
branchNameTemplate: pr.branchNameTemplate ?? "sela/heal-${id}",
|
|
67
|
+
titleTemplate: pr.titleTemplate ?? "fix(sela): heal selector in ${fileName}",
|
|
68
|
+
bodyTemplate: pr.bodyTemplate ?? null,
|
|
69
|
+
labels: pr.labels ?? [],
|
|
70
|
+
reportArtifactPath: pr.reportArtifactPath ?? "sela-report.html",
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
74
|
+
// ENV FLAG PARSING - strict, fail-fast
|
|
75
|
+
//
|
|
76
|
+
// Accepted truthy values (case-insensitive): "1", "true", "yes", "on".
|
|
77
|
+
// Accepted falsy values (case-insensitive): "0", "false", "no", "off", "".
|
|
78
|
+
// Anything else → throws so misconfigured CI environments fail loudly
|
|
79
|
+
// instead of silently running the wrong mode.
|
|
80
|
+
// ═══════════════════════════════════════════════════════════════════
|
|
81
|
+
const ENV_TRUTHY = new Set(["1", "true", "yes", "on"]);
|
|
82
|
+
const ENV_FALSY = new Set(["0", "false", "no", "off", ""]);
|
|
83
|
+
function parseEnvBoolean(varName, rawValue) {
|
|
84
|
+
if (rawValue === undefined)
|
|
85
|
+
return false;
|
|
86
|
+
const normalized = rawValue.trim().toLowerCase();
|
|
87
|
+
if (ENV_TRUTHY.has(normalized))
|
|
88
|
+
return true;
|
|
89
|
+
if (ENV_FALSY.has(normalized))
|
|
90
|
+
return false;
|
|
91
|
+
throw new Error(`[Sela] Invalid value for env var "${varName}": "${rawValue}". ` +
|
|
92
|
+
`Expected one of: 1, 0, true, false, yes, no, on, off.`);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Validates maxHealsPerRun. Fail-fast (mirrors parseEnvBoolean philosophy):
|
|
96
|
+
* a misconfigured limit should crash loudly, not silently run unbounded.
|
|
97
|
+
* undefined => null (unlimited). 0 / negative / non-integer => throw.
|
|
98
|
+
*/
|
|
99
|
+
function resolveMaxHealsPerRun(value) {
|
|
100
|
+
if (value === undefined)
|
|
101
|
+
return null;
|
|
102
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
103
|
+
throw new Error(`[Sela] Invalid maxHealsPerRun: ${JSON.stringify(value)}. ` +
|
|
104
|
+
`Expected a positive integer (or omit it for unlimited).`);
|
|
105
|
+
}
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
function resolveConfig(config, env = process.env) {
|
|
109
|
+
const prAutomation = resolvePRAutomation(config);
|
|
110
|
+
// When prAutomation owns the git pipeline, suppress per-heal git side-effects
|
|
111
|
+
// in SourceUpdater (autoCommit + updateStrategy:'branch') to avoid double-commits.
|
|
112
|
+
const suppressLegacyGit = prAutomation.enabled;
|
|
113
|
+
const requestedUpdateStrategy = config.updateStrategy ?? "inPlace";
|
|
114
|
+
const updateStrategy = suppressLegacyGit && requestedUpdateStrategy === "branch"
|
|
115
|
+
? "inPlace"
|
|
116
|
+
: requestedUpdateStrategy;
|
|
117
|
+
const autoCommit = suppressLegacyGit ? false : (config.autoCommit ?? false);
|
|
118
|
+
const dryRun = parseEnvBoolean("SELA_DRY_RUN", env.SELA_DRY_RUN);
|
|
119
|
+
const noCache = parseEnvBoolean("SELA_NO_CACHE", env.SELA_NO_CACHE);
|
|
120
|
+
return {
|
|
121
|
+
thresholds: resolveThresholds(config),
|
|
122
|
+
updateStrategy,
|
|
123
|
+
autoCommit,
|
|
124
|
+
dnaStoragePath: config.dnaStoragePath ?? "sela-snapshots",
|
|
125
|
+
prAutomation,
|
|
126
|
+
agenticDiscovery: {
|
|
127
|
+
enabled: config.agenticDiscovery?.enabled ?? false,
|
|
128
|
+
maxSteps: config.agenticDiscovery?.maxSteps ?? 10,
|
|
129
|
+
forbiddenSelectors: config.agenticDiscovery?.forbiddenSelectors ?? [],
|
|
130
|
+
},
|
|
131
|
+
allowedPaths: config.allowedPaths ?? [],
|
|
132
|
+
maxHealsPerRun: resolveMaxHealsPerRun(config.maxHealsPerRun),
|
|
133
|
+
sanitization: {
|
|
134
|
+
enabled: config.sanitization?.enabled ?? true,
|
|
135
|
+
redactInputValues: config.sanitization?.redactInputValues ?? true,
|
|
136
|
+
customPatterns: config.sanitization?.customPatterns ?? [],
|
|
137
|
+
},
|
|
138
|
+
dryRun,
|
|
139
|
+
noCache,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"defineConfig.d.ts","sourceRoot":"","sources":["../../src/config/defineConfig.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU,CAE3D"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/config/defineConfig.ts
|
|
3
|
+
// Identity helper that provides TypeScript inference for sela.config.ts files
|
|
4
|
+
// without requiring users to manually annotate their export.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.defineConfig = defineConfig;
|
|
7
|
+
function defineConfig(config) {
|
|
8
|
+
return config;
|
|
9
|
+
}
|