playwright-intelligence-engine 0.0.0-stage → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +191 -0
- package/README.md +229 -3
- package/bin/pw-intel.js +37 -0
- package/dist/authoring/author.d.ts +83 -0
- package/dist/authoring/author.d.ts.map +1 -0
- package/dist/authoring/generation-contract.d.ts +90 -0
- package/dist/authoring/generation-contract.d.ts.map +1 -0
- package/dist/authoring/hybrid-author.d.ts +6 -0
- package/dist/authoring/hybrid-author.d.ts.map +1 -0
- package/dist/authoring/index.d.ts +7 -0
- package/dist/authoring/index.d.ts.map +1 -0
- package/dist/authoring/llm-author.d.ts +28 -0
- package/dist/authoring/llm-author.d.ts.map +1 -0
- package/dist/authoring/naming/index.d.ts +4 -0
- package/dist/authoring/naming/index.d.ts.map +1 -0
- package/dist/authoring/naming/name-plan.d.ts +45 -0
- package/dist/authoring/naming/name-plan.d.ts.map +1 -0
- package/dist/authoring/naming/name-planner.d.ts +25 -0
- package/dist/authoring/naming/name-planner.d.ts.map +1 -0
- package/dist/authoring/naming/naming-linter.d.ts +20 -0
- package/dist/authoring/naming/naming-linter.d.ts.map +1 -0
- package/dist/authoring/template-author.d.ts +6 -0
- package/dist/authoring/template-author.d.ts.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/collectors/collector-interface.d.ts +7 -0
- package/dist/collectors/collector-interface.d.ts.map +1 -0
- package/dist/collectors/environment-collector.d.ts +12 -0
- package/dist/collectors/environment-collector.d.ts.map +1 -0
- package/dist/collectors/error-collector.d.ts +18 -0
- package/dist/collectors/error-collector.d.ts.map +1 -0
- package/dist/collectors/git-diff-collector.d.ts +20 -0
- package/dist/collectors/git-diff-collector.d.ts.map +1 -0
- package/dist/collectors/live-browser-collector.d.ts +45 -0
- package/dist/collectors/live-browser-collector.d.ts.map +1 -0
- package/dist/collectors/network-collector.d.ts +28 -0
- package/dist/collectors/network-collector.d.ts.map +1 -0
- package/dist/collectors/static-ast-collector.d.ts +32 -0
- package/dist/collectors/static-ast-collector.d.ts.map +1 -0
- package/dist/collectors/trace-collector.d.ts +12 -0
- package/dist/collectors/trace-collector.d.ts.map +1 -0
- package/dist/config/project-config.d.ts +33 -0
- package/dist/config/project-config.d.ts.map +1 -0
- package/dist/core/errors/engine-errors.d.ts +34 -0
- package/dist/core/errors/engine-errors.d.ts.map +1 -0
- package/dist/core/types/diagnostic-context.d.ts +151 -0
- package/dist/core/types/diagnostic-context.d.ts.map +1 -0
- package/dist/core/types/repair-evidence.d.ts +56 -0
- package/dist/core/types/repair-evidence.d.ts.map +1 -0
- package/dist/core/version.d.ts +13 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/diagnostics/diagnostic-contract.d.ts +52 -0
- package/dist/diagnostics/diagnostic-contract.d.ts.map +1 -0
- package/dist/diagnostics/diagnostic-report.d.ts +11 -0
- package/dist/diagnostics/diagnostic-report.d.ts.map +1 -0
- package/dist/diagnostics/evidence-correlator.d.ts +22 -0
- package/dist/diagnostics/evidence-correlator.d.ts.map +1 -0
- package/dist/diagnostics/external-failure-evidence.d.ts +18 -0
- package/dist/diagnostics/external-failure-evidence.d.ts.map +1 -0
- package/dist/diagnostics/failure-category.d.ts +4 -0
- package/dist/diagnostics/failure-category.d.ts.map +1 -0
- package/dist/diagnostics/failure-classifier.d.ts +58 -0
- package/dist/diagnostics/failure-classifier.d.ts.map +1 -0
- package/dist/evaluation/evaluation-scorecard.d.ts +72 -0
- package/dist/evaluation/evaluation-scorecard.d.ts.map +1 -0
- package/dist/evaluation/repository-inspector.d.ts +21 -0
- package/dist/evaluation/repository-inspector.d.ts.map +1 -0
- package/dist/evaluation/repository-runner.d.ts +24 -0
- package/dist/evaluation/repository-runner.d.ts.map +1 -0
- package/dist/evidence/evidence-model.d.ts +88 -0
- package/dist/evidence/evidence-model.d.ts.map +1 -0
- package/dist/evidence/evidence-normalizer.d.ts +19 -0
- package/dist/evidence/evidence-normalizer.d.ts.map +1 -0
- package/dist/evidence/evidence-redactor.d.ts +37 -0
- package/dist/evidence/evidence-redactor.d.ts.map +1 -0
- package/dist/framework/config-adapter.d.ts +28 -0
- package/dist/framework/config-adapter.d.ts.map +1 -0
- package/dist/gate/index.d.ts +21 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/oracle.d.ts +26 -0
- package/dist/gate/oracle.d.ts.map +1 -0
- package/dist/gate/quality.d.ts +27 -0
- package/dist/gate/quality.d.ts.map +1 -0
- package/dist/gate/safety.d.ts +28 -0
- package/dist/gate/safety.d.ts.map +1 -0
- package/dist/generator/architecture-generator.d.ts +55 -0
- package/dist/generator/architecture-generator.d.ts.map +1 -0
- package/dist/generator/assertion-quality-gate.d.ts +18 -0
- package/dist/generator/assertion-quality-gate.d.ts.map +1 -0
- package/dist/generator/candidate-writer.d.ts +34 -0
- package/dist/generator/candidate-writer.d.ts.map +1 -0
- package/dist/generator/test-generator.d.ts +26 -0
- package/dist/generator/test-generator.d.ts.map +1 -0
- package/dist/governance/audit-log.d.ts +23 -0
- package/dist/governance/audit-log.d.ts.map +1 -0
- package/dist/governance/governance-policy.d.ts +24 -0
- package/dist/governance/governance-policy.d.ts.map +1 -0
- package/dist/governance/team-dashboard.d.ts +51 -0
- package/dist/governance/team-dashboard.d.ts.map +1 -0
- package/dist/governance/workflow-audit.d.ts +13 -0
- package/dist/governance/workflow-audit.d.ts.map +1 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20318 -0
- package/dist/integrations/ci-summary.d.ts +14 -0
- package/dist/integrations/ci-summary.d.ts.map +1 -0
- package/dist/integrations/playwright-report-adapter.d.ts +22 -0
- package/dist/integrations/playwright-report-adapter.d.ts.map +1 -0
- package/dist/integrations/quality-gate.d.ts +35 -0
- package/dist/integrations/quality-gate.d.ts.map +1 -0
- package/dist/integrations/sarif-report.d.ts +48 -0
- package/dist/integrations/sarif-report.d.ts.map +1 -0
- package/dist/intelligence/autonomous-repair-workflow.d.ts +31 -0
- package/dist/intelligence/autonomous-repair-workflow.d.ts.map +1 -0
- package/dist/intelligence/business-intent.d.ts +58 -0
- package/dist/intelligence/business-intent.d.ts.map +1 -0
- package/dist/intelligence/evidence-bundle.d.ts +364 -0
- package/dist/intelligence/evidence-bundle.d.ts.map +1 -0
- package/dist/intelligence/failure-clustering.d.ts +38 -0
- package/dist/intelligence/failure-clustering.d.ts.map +1 -0
- package/dist/intelligence/generation-agent.d.ts +120 -0
- package/dist/intelligence/generation-agent.d.ts.map +1 -0
- package/dist/intelligence/generation-verifier.d.ts +151 -0
- package/dist/intelligence/generation-verifier.d.ts.map +1 -0
- package/dist/intelligence/llm-proposal.d.ts +377 -0
- package/dist/intelligence/llm-proposal.d.ts.map +1 -0
- package/dist/intelligence/locator-disambiguation-engine.d.ts +38 -0
- package/dist/intelligence/locator-disambiguation-engine.d.ts.map +1 -0
- package/dist/intelligence/model-contract.d.ts +86 -0
- package/dist/intelligence/model-contract.d.ts.map +1 -0
- package/dist/intelligence/model-provider.d.ts +75 -0
- package/dist/intelligence/model-provider.d.ts.map +1 -0
- package/dist/intelligence/project-intelligence.d.ts +22 -0
- package/dist/intelligence/project-intelligence.d.ts.map +1 -0
- package/dist/intelligence/repair-agent.d.ts +46 -0
- package/dist/intelligence/repair-agent.d.ts.map +1 -0
- package/dist/intelligence/repository-profile.d.ts +131 -0
- package/dist/intelligence/repository-profile.d.ts.map +1 -0
- package/dist/intelligence/suite-intelligence.d.ts +59 -0
- package/dist/intelligence/suite-intelligence.d.ts.map +1 -0
- package/dist/intelligence/test-intelligence.d.ts +44 -0
- package/dist/intelligence/test-intelligence.d.ts.map +1 -0
- package/dist/intelligence/trace-proposal-workflow.d.ts +30 -0
- package/dist/intelligence/trace-proposal-workflow.d.ts.map +1 -0
- package/dist/intelligence/verified-llm-repair-workflow.d.ts +21 -0
- package/dist/intelligence/verified-llm-repair-workflow.d.ts.map +1 -0
- package/dist/intelligence/workflow-planner.d.ts +278 -0
- package/dist/intelligence/workflow-planner.d.ts.map +1 -0
- package/dist/mcp/server.d.ts +32 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/memory/stability-ledger.d.ts +46 -0
- package/dist/memory/stability-ledger.d.ts.map +1 -0
- package/dist/perception/app-model.d.ts +55 -0
- package/dist/perception/app-model.d.ts.map +1 -0
- package/dist/perception/crawler.d.ts +18 -0
- package/dist/perception/crawler.d.ts.map +1 -0
- package/dist/perception/index.d.ts +3 -0
- package/dist/perception/index.d.ts.map +1 -0
- package/dist/repair/approval-service.d.ts +139 -0
- package/dist/repair/approval-service.d.ts.map +1 -0
- package/dist/repair/assertion-hasher.d.ts +62 -0
- package/dist/repair/assertion-hasher.d.ts.map +1 -0
- package/dist/repair/ast-patcher.d.ts +36 -0
- package/dist/repair/ast-patcher.d.ts.map +1 -0
- package/dist/repair/browser-operation-executor.d.ts +27 -0
- package/dist/repair/browser-operation-executor.d.ts.map +1 -0
- package/dist/repair/diff-allowlist.d.ts +74 -0
- package/dist/repair/diff-allowlist.d.ts.map +1 -0
- package/dist/repair/intent-validator.d.ts +157 -0
- package/dist/repair/intent-validator.d.ts.map +1 -0
- package/dist/repair/repair-operations.d.ts +40 -0
- package/dist/repair/repair-operations.d.ts.map +1 -0
- package/dist/repair/repair-planner.d.ts +98 -0
- package/dist/repair/repair-planner.d.ts.map +1 -0
- package/dist/repair/repair-workflow.d.ts +42 -0
- package/dist/repair/repair-workflow.d.ts.map +1 -0
- package/dist/repair/safe-operation-compiler.d.ts +15 -0
- package/dist/repair/safe-operation-compiler.d.ts.map +1 -0
- package/dist/repair/source-mapper.d.ts +19 -0
- package/dist/repair/source-mapper.d.ts.map +1 -0
- package/dist/repair/statement-patcher.d.ts +75 -0
- package/dist/repair/statement-patcher.d.ts.map +1 -0
- package/dist/repair/verification-gates.d.ts +25 -0
- package/dist/repair/verification-gates.d.ts.map +1 -0
- package/dist/repair/workflow-plan.d.ts +35 -0
- package/dist/repair/workflow-plan.d.ts.map +1 -0
- package/dist/repair/workspace-promoter.d.ts +43 -0
- package/dist/repair/workspace-promoter.d.ts.map +1 -0
- package/dist/reproduce/diagnostic-rerunner.d.ts +40 -0
- package/dist/reproduce/diagnostic-rerunner.d.ts.map +1 -0
- package/dist/reproduce/direct-browser-investigator.d.ts +41 -0
- package/dist/reproduce/direct-browser-investigator.d.ts.map +1 -0
- package/dist/reproduce/interactive-workflow-runner.d.ts +43 -0
- package/dist/reproduce/interactive-workflow-runner.d.ts.map +1 -0
- package/dist/sandbox/dependency-strategy.d.ts +45 -0
- package/dist/sandbox/dependency-strategy.d.ts.map +1 -0
- package/dist/sandbox/execution-runner.d.ts +81 -0
- package/dist/sandbox/execution-runner.d.ts.map +1 -0
- package/dist/sandbox/worktree-manager.d.ts +51 -0
- package/dist/sandbox/worktree-manager.d.ts.map +1 -0
- package/dist/selector/abstention-policy.d.ts +34 -0
- package/dist/selector/abstention-policy.d.ts.map +1 -0
- package/dist/selector/candidate-extractor.d.ts +71 -0
- package/dist/selector/candidate-extractor.d.ts.map +1 -0
- package/dist/selector/live-locator-verifier.d.ts +28 -0
- package/dist/selector/live-locator-verifier.d.ts.map +1 -0
- package/dist/selector/repair-candidate-ranker.d.ts +10 -0
- package/dist/selector/repair-candidate-ranker.d.ts.map +1 -0
- package/dist/selector/selector-scorer.d.ts +43 -0
- package/dist/selector/selector-scorer.d.ts.map +1 -0
- package/dist/trace/adapters/adapter-registry.d.ts +18 -0
- package/dist/trace/adapters/adapter-registry.d.ts.map +1 -0
- package/dist/trace/adapters/trace-adapter.d.ts +21 -0
- package/dist/trace/adapters/trace-adapter.d.ts.map +1 -0
- package/dist/trace/adapters/unknown-adapter.d.ts +10 -0
- package/dist/trace/adapters/unknown-adapter.d.ts.map +1 -0
- package/dist/trace/adapters/v10-adapter.d.ts +10 -0
- package/dist/trace/adapters/v10-adapter.d.ts.map +1 -0
- package/dist/trace/adapters/v4-adapter.d.ts +11 -0
- package/dist/trace/adapters/v4-adapter.d.ts.map +1 -0
- package/dist/trace/adapters/v8-adapter.d.ts +10 -0
- package/dist/trace/adapters/v8-adapter.d.ts.map +1 -0
- package/dist/trace/adapters/v9-adapter.d.ts +10 -0
- package/dist/trace/adapters/v9-adapter.d.ts.map +1 -0
- package/dist/trace/archive-reader.d.ts +44 -0
- package/dist/trace/archive-reader.d.ts.map +1 -0
- package/dist/trace/snapshot-resolver.d.ts +55 -0
- package/dist/trace/snapshot-resolver.d.ts.map +1 -0
- package/dist/trace/trace-parser.d.ts +50 -0
- package/dist/trace/trace-parser.d.ts.map +1 -0
- package/dist/trace/version-detector.d.ts +20 -0
- package/dist/trace/version-detector.d.ts.map +1 -0
- package/package.json +76 -6
- package/skills/README.md +20 -0
- package/skills/pie-failure-investigation/SKILL.md +16 -0
- package/skills/pie-quality-engineer/SKILL.md +66 -0
- package/skills/pie-release-readiness/SKILL.md +22 -0
- package/skills/pie-repair-review/SKILL.md +21 -0
- package/skills/pie-trace-to-repair/SKILL.md +35 -0
- package/src/authoring/author.ts +81 -0
- package/src/authoring/generation-contract.ts +283 -0
- package/src/authoring/hybrid-author.ts +115 -0
- package/src/authoring/index.ts +7 -0
- package/src/authoring/llm-author.ts +282 -0
- package/src/authoring/naming/index.ts +4 -0
- package/src/authoring/naming/name-plan.ts +28 -0
- package/src/authoring/naming/name-planner.ts +339 -0
- package/src/authoring/naming/naming-linter.ts +292 -0
- package/src/authoring/template-author.ts +39 -0
- package/src/cli/index.ts +1172 -0
- package/src/collectors/collector-interface.ts +8 -0
- package/src/collectors/environment-collector.ts +103 -0
- package/src/collectors/error-collector.ts +177 -0
- package/src/collectors/git-diff-collector.ts +190 -0
- package/src/collectors/live-browser-collector.ts +412 -0
- package/src/collectors/network-collector.ts +90 -0
- package/src/collectors/static-ast-collector.ts +226 -0
- package/src/collectors/trace-collector.ts +66 -0
- package/src/config/project-config.ts +114 -0
- package/src/core/errors/engine-errors.ts +90 -0
- package/src/core/types/diagnostic-context.ts +94 -0
- package/src/core/types/repair-evidence.ts +45 -0
- package/src/core/version.ts +25 -0
- package/src/diagnostics/diagnostic-contract.ts +79 -0
- package/src/diagnostics/diagnostic-report.ts +204 -0
- package/src/diagnostics/evidence-correlator.ts +117 -0
- package/src/diagnostics/external-failure-evidence.ts +72 -0
- package/src/diagnostics/failure-category.ts +40 -0
- package/src/diagnostics/failure-classifier.ts +701 -0
- package/src/evaluation/evaluation-scorecard.ts +198 -0
- package/src/evaluation/repository-inspector.ts +143 -0
- package/src/evaluation/repository-runner.ts +197 -0
- package/src/evidence/evidence-model.ts +76 -0
- package/src/evidence/evidence-normalizer.ts +322 -0
- package/src/evidence/evidence-redactor.ts +218 -0
- package/src/framework/config-adapter.ts +131 -0
- package/src/gate/index.ts +43 -0
- package/src/gate/oracle.ts +69 -0
- package/src/gate/quality.ts +101 -0
- package/src/gate/safety.ts +94 -0
- package/src/generator/architecture-generator.ts +582 -0
- package/src/generator/assertion-quality-gate.ts +88 -0
- package/src/generator/candidate-writer.ts +288 -0
- package/src/generator/test-generator.ts +52 -0
- package/src/governance/audit-log.ts +115 -0
- package/src/governance/governance-policy.ts +72 -0
- package/src/governance/team-dashboard.ts +189 -0
- package/src/governance/workflow-audit.ts +72 -0
- package/src/index.ts +107 -0
- package/src/integrations/ci-summary.ts +37 -0
- package/src/integrations/playwright-report-adapter.ts +173 -0
- package/src/integrations/quality-gate.ts +95 -0
- package/src/integrations/sarif-report.ts +110 -0
- package/src/intelligence/autonomous-repair-workflow.ts +139 -0
- package/src/intelligence/business-intent.ts +163 -0
- package/src/intelligence/evidence-bundle.ts +300 -0
- package/src/intelligence/failure-clustering.ts +134 -0
- package/src/intelligence/generation-agent.ts +561 -0
- package/src/intelligence/generation-verifier.ts +1115 -0
- package/src/intelligence/llm-proposal.ts +519 -0
- package/src/intelligence/locator-disambiguation-engine.ts +331 -0
- package/src/intelligence/model-contract.ts +205 -0
- package/src/intelligence/model-provider.ts +348 -0
- package/src/intelligence/project-intelligence.ts +140 -0
- package/src/intelligence/repair-agent.ts +297 -0
- package/src/intelligence/repository-profile.ts +796 -0
- package/src/intelligence/suite-intelligence.ts +305 -0
- package/src/intelligence/test-intelligence.ts +136 -0
- package/src/intelligence/trace-proposal-workflow.ts +318 -0
- package/src/intelligence/verified-llm-repair-workflow.ts +48 -0
- package/src/intelligence/workflow-planner.ts +966 -0
- package/src/mcp/server.ts +970 -0
- package/src/memory/stability-ledger.ts +243 -0
- package/src/perception/app-model.ts +32 -0
- package/src/perception/crawler.ts +263 -0
- package/src/perception/index.ts +3 -0
- package/src/repair/approval-service.ts +541 -0
- package/src/repair/assertion-hasher.ts +386 -0
- package/src/repair/ast-patcher.ts +223 -0
- package/src/repair/browser-operation-executor.ts +263 -0
- package/src/repair/diff-allowlist.ts +612 -0
- package/src/repair/intent-validator.ts +761 -0
- package/src/repair/repair-operations.ts +65 -0
- package/src/repair/repair-planner.ts +467 -0
- package/src/repair/repair-workflow.ts +241 -0
- package/src/repair/safe-operation-compiler.ts +68 -0
- package/src/repair/source-mapper.ts +156 -0
- package/src/repair/statement-patcher.ts +544 -0
- package/src/repair/verification-gates.ts +46 -0
- package/src/repair/workflow-plan.ts +59 -0
- package/src/repair/workspace-promoter.ts +313 -0
- package/src/reproduce/diagnostic-rerunner.ts +287 -0
- package/src/reproduce/direct-browser-investigator.ts +208 -0
- package/src/reproduce/interactive-workflow-runner.ts +828 -0
- package/src/sandbox/dependency-strategy.ts +215 -0
- package/src/sandbox/execution-runner.ts +408 -0
- package/src/sandbox/worktree-manager.ts +287 -0
- package/src/selector/abstention-policy.ts +175 -0
- package/src/selector/candidate-extractor.ts +343 -0
- package/src/selector/live-locator-verifier.ts +139 -0
- package/src/selector/repair-candidate-ranker.ts +92 -0
- package/src/selector/selector-scorer.ts +344 -0
- package/src/trace/adapters/adapter-registry.ts +49 -0
- package/src/trace/adapters/trace-adapter.ts +28 -0
- package/src/trace/adapters/unknown-adapter.ts +50 -0
- package/src/trace/adapters/v10-adapter.ts +39 -0
- package/src/trace/adapters/v4-adapter.ts +31 -0
- package/src/trace/adapters/v8-adapter.ts +42 -0
- package/src/trace/adapters/v9-adapter.ts +39 -0
- package/src/trace/archive-reader.ts +246 -0
- package/src/trace/snapshot-resolver.ts +308 -0
- package/src/trace/trace-parser.ts +467 -0
- package/src/trace/version-detector.ts +81 -0
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { VerifiedExecutionProof } from './intent-validator.js';
|
|
5
|
+
|
|
6
|
+
export interface ApprovalTokenPayload {
|
|
7
|
+
tokenId: string;
|
|
8
|
+
proposalId: string;
|
|
9
|
+
targetFilePath: string; // canonical realpath
|
|
10
|
+
targetClassName: string;
|
|
11
|
+
targetMemberName: string;
|
|
12
|
+
originalSourceHash: string;
|
|
13
|
+
replacementSelector: string;
|
|
14
|
+
replacementAstHash: string;
|
|
15
|
+
fullDiffHash: string;
|
|
16
|
+
patchedSourceHash: string;
|
|
17
|
+
testFilePath: string; // canonical realpath
|
|
18
|
+
testFileHash: string;
|
|
19
|
+
executionVerified: boolean;
|
|
20
|
+
issuedAt: number;
|
|
21
|
+
expiresAt: number;
|
|
22
|
+
used: boolean;
|
|
23
|
+
nonce: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface StagedProposal {
|
|
27
|
+
proposalId: string;
|
|
28
|
+
targetFilePath: string;
|
|
29
|
+
targetClassName: string;
|
|
30
|
+
targetMemberName: string;
|
|
31
|
+
originalSourceHash: string;
|
|
32
|
+
replacementSelector: string;
|
|
33
|
+
replacementAstHash: string;
|
|
34
|
+
fullDiff: string;
|
|
35
|
+
fullDiffHash: string;
|
|
36
|
+
patchedSource: string;
|
|
37
|
+
patchedSourceHash: string;
|
|
38
|
+
testFilePath: string;
|
|
39
|
+
testFileHash: string;
|
|
40
|
+
worktreeId: string;
|
|
41
|
+
worktreeExecutionVerified: boolean;
|
|
42
|
+
executionProof?: VerifiedExecutionProof | undefined;
|
|
43
|
+
createdAt: number;
|
|
44
|
+
expiresAt: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface TokenValidationResult {
|
|
48
|
+
isValid: boolean;
|
|
49
|
+
reason?: string;
|
|
50
|
+
payload?: ApprovalTokenPayload;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Centralized cryptographic approval service for all source modifications.
|
|
55
|
+
* Guarantees that no repair can be applied without an explicit, unexpired,
|
|
56
|
+
* one-time approval token bound cryptographically to:
|
|
57
|
+
* - Proposal ID & Nonce
|
|
58
|
+
* - Canonical target file path & Original source SHA-256
|
|
59
|
+
* - Target Class Name & Member Name
|
|
60
|
+
* - Replacement Selector & AST Hash
|
|
61
|
+
* - Full Diff SHA-256 & Patched Source SHA-256
|
|
62
|
+
* - Test File Canonical Path & Test File SHA-256
|
|
63
|
+
* - Isolated Worktree Execution Verification Proof
|
|
64
|
+
*/
|
|
65
|
+
export class ApprovalService {
|
|
66
|
+
private static instance: ApprovalService | undefined;
|
|
67
|
+
private proposals = new Map<string, StagedProposal>();
|
|
68
|
+
private tokens = new Map<string, ApprovalTokenPayload>();
|
|
69
|
+
private readonly defaultTtlMs = 10 * 60 * 1000; // 10 minutes
|
|
70
|
+
private storageDir: string;
|
|
71
|
+
private proposalsDir: string;
|
|
72
|
+
private tokensDir: string;
|
|
73
|
+
|
|
74
|
+
public constructor(customStorageDir?: string) {
|
|
75
|
+
this.storageDir = customStorageDir || process.env.PIE_STORAGE_DIR || path.resolve(process.cwd(), '.pw-intelligence');
|
|
76
|
+
this.proposalsDir = path.join(this.storageDir, 'proposals');
|
|
77
|
+
this.tokensDir = path.join(this.storageDir, 'tokens');
|
|
78
|
+
try {
|
|
79
|
+
fs.mkdirSync(this.proposalsDir, { recursive: true });
|
|
80
|
+
fs.mkdirSync(this.tokensDir, { recursive: true });
|
|
81
|
+
} catch {
|
|
82
|
+
// Safe fallback if filesystem is read-only
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public static getInstance(customStorageDir?: string): ApprovalService {
|
|
87
|
+
if (!ApprovalService.instance) {
|
|
88
|
+
ApprovalService.instance = new ApprovalService(customStorageDir);
|
|
89
|
+
}
|
|
90
|
+
return ApprovalService.instance;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Computes SHA-256 hash of a string.
|
|
95
|
+
*/
|
|
96
|
+
public static hashContent(content: string): string {
|
|
97
|
+
return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Stages a verified repair proposal in memory.
|
|
102
|
+
* Note: Staging a proposal does NOT generate an approval token.
|
|
103
|
+
* An approval token is generated only via an explicit, trusted authorization call.
|
|
104
|
+
*/
|
|
105
|
+
public stageProposal(params: {
|
|
106
|
+
proposalId?: string;
|
|
107
|
+
targetFilePath: string;
|
|
108
|
+
targetClassName: string;
|
|
109
|
+
targetMemberName: string;
|
|
110
|
+
originalSourceContent: string;
|
|
111
|
+
replacementSelector: string;
|
|
112
|
+
fullDiff: string;
|
|
113
|
+
patchedSource: string;
|
|
114
|
+
testFilePath: string;
|
|
115
|
+
testFileContent: string;
|
|
116
|
+
worktreeId: string;
|
|
117
|
+
worktreeExecutionVerified?: boolean;
|
|
118
|
+
proof?: VerifiedExecutionProof;
|
|
119
|
+
ttlMs?: number;
|
|
120
|
+
}): StagedProposal {
|
|
121
|
+
const canonicalTarget = fs.existsSync(params.targetFilePath)
|
|
122
|
+
? fs.realpathSync(params.targetFilePath)
|
|
123
|
+
: path.resolve(params.targetFilePath);
|
|
124
|
+
|
|
125
|
+
const canonicalTest = fs.existsSync(params.testFilePath)
|
|
126
|
+
? fs.realpathSync(params.testFilePath)
|
|
127
|
+
: path.resolve(params.testFilePath);
|
|
128
|
+
|
|
129
|
+
const proposalId = params.proposalId ?? `prop-${Date.now()}-${crypto.randomBytes(8).toString('hex')}`;
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
const ttl = params.ttlMs ?? this.defaultTtlMs;
|
|
132
|
+
|
|
133
|
+
// Derived execution verification: caller-supplied boolean is never trusted
|
|
134
|
+
// unless verified proof from an actual worktree execution is attached.
|
|
135
|
+
let isVerified = false;
|
|
136
|
+
if (params.proof) {
|
|
137
|
+
isVerified = Boolean(
|
|
138
|
+
params.proof.passed &&
|
|
139
|
+
params.proof.exitCode === 0 &&
|
|
140
|
+
(params.proof.executionOutcome === 'PASS' || params.proof.executionOutcome === undefined) &&
|
|
141
|
+
(params.proof.assertionIntegrityValid !== false)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const staged: StagedProposal = {
|
|
146
|
+
proposalId,
|
|
147
|
+
targetFilePath: canonicalTarget,
|
|
148
|
+
targetClassName: params.targetClassName,
|
|
149
|
+
targetMemberName: params.targetMemberName,
|
|
150
|
+
originalSourceHash: ApprovalService.hashContent(params.originalSourceContent),
|
|
151
|
+
replacementSelector: params.replacementSelector,
|
|
152
|
+
replacementAstHash: ApprovalService.hashContent(params.replacementSelector.trim()),
|
|
153
|
+
fullDiff: params.fullDiff,
|
|
154
|
+
fullDiffHash: ApprovalService.hashContent(params.fullDiff),
|
|
155
|
+
patchedSource: params.patchedSource,
|
|
156
|
+
patchedSourceHash: ApprovalService.hashContent(params.patchedSource),
|
|
157
|
+
testFilePath: canonicalTest,
|
|
158
|
+
testFileHash: ApprovalService.hashContent(params.testFileContent),
|
|
159
|
+
worktreeId: params.worktreeId,
|
|
160
|
+
worktreeExecutionVerified: isVerified,
|
|
161
|
+
executionProof: params.proof,
|
|
162
|
+
createdAt: now,
|
|
163
|
+
expiresAt: now + ttl,
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
this.proposals.set(proposalId, staged);
|
|
167
|
+
try {
|
|
168
|
+
fs.mkdirSync(this.proposalsDir, { recursive: true });
|
|
169
|
+
fs.writeFileSync(
|
|
170
|
+
path.join(this.proposalsDir, `${proposalId}.json`),
|
|
171
|
+
JSON.stringify(staged, null, 2),
|
|
172
|
+
'utf-8'
|
|
173
|
+
);
|
|
174
|
+
} catch {
|
|
175
|
+
// Safe fallback if disk is read-only
|
|
176
|
+
}
|
|
177
|
+
return staged;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
public getTokenPayload(tokenId: string): ApprovalTokenPayload | undefined {
|
|
181
|
+
this.cleanupExpired();
|
|
182
|
+
let payload = this.tokens.get(tokenId);
|
|
183
|
+
if (!payload) {
|
|
184
|
+
const diskPath = path.join(this.tokensDir, `${tokenId}.json`);
|
|
185
|
+
if (fs.existsSync(diskPath)) {
|
|
186
|
+
try {
|
|
187
|
+
const raw = fs.readFileSync(diskPath, 'utf-8');
|
|
188
|
+
const parsed = JSON.parse(raw) as ApprovalTokenPayload;
|
|
189
|
+
if (Date.now() <= parsed.expiresAt && !parsed.used) {
|
|
190
|
+
payload = parsed;
|
|
191
|
+
this.tokens.set(tokenId, parsed);
|
|
192
|
+
} else {
|
|
193
|
+
try { fs.unlinkSync(diskPath); } catch {}
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
// Ignore corrupt file
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return payload;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
public getProposal(proposalId: string): StagedProposal | undefined {
|
|
204
|
+
this.cleanupExpired();
|
|
205
|
+
let proposal = this.proposals.get(proposalId);
|
|
206
|
+
if (!proposal) {
|
|
207
|
+
const diskPath = path.join(this.proposalsDir, `${proposalId}.json`);
|
|
208
|
+
if (fs.existsSync(diskPath)) {
|
|
209
|
+
try {
|
|
210
|
+
const raw = fs.readFileSync(diskPath, 'utf-8');
|
|
211
|
+
const parsed = JSON.parse(raw) as StagedProposal;
|
|
212
|
+
if (Date.now() <= parsed.expiresAt) {
|
|
213
|
+
proposal = parsed;
|
|
214
|
+
this.proposals.set(proposalId, parsed);
|
|
215
|
+
} else {
|
|
216
|
+
try { fs.unlinkSync(diskPath); } catch {}
|
|
217
|
+
}
|
|
218
|
+
} catch {
|
|
219
|
+
// Ignore corrupt file
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return proposal;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Authorizes a staged proposal and issues an unforgeable single-use approval token.
|
|
228
|
+
* Must be called explicitly by a human CLI action or a verified authorization hook.
|
|
229
|
+
*/
|
|
230
|
+
public authorizeProposal(params: {
|
|
231
|
+
proposalId: string;
|
|
232
|
+
ttlMs?: number;
|
|
233
|
+
}): ApprovalTokenPayload {
|
|
234
|
+
this.cleanupExpired();
|
|
235
|
+
const proposal = this.getProposal(params.proposalId);
|
|
236
|
+
if (!proposal) {
|
|
237
|
+
throw new Error(`Cannot authorize proposal: '${params.proposalId}' not found or expired.`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!proposal.worktreeExecutionVerified || !proposal.executionProof || !proposal.executionProof.passed) {
|
|
241
|
+
throw new Error(
|
|
242
|
+
`Security Violation: Cannot authorize proposal '${params.proposalId}'. Worktree test execution was not verified.`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const tokenId = crypto.randomBytes(32).toString('hex');
|
|
247
|
+
const nonce = crypto.randomBytes(16).toString('hex');
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
const ttl = params.ttlMs ?? this.defaultTtlMs;
|
|
250
|
+
|
|
251
|
+
const payload: ApprovalTokenPayload = {
|
|
252
|
+
tokenId,
|
|
253
|
+
proposalId: proposal.proposalId,
|
|
254
|
+
targetFilePath: proposal.targetFilePath,
|
|
255
|
+
targetClassName: proposal.targetClassName,
|
|
256
|
+
targetMemberName: proposal.targetMemberName,
|
|
257
|
+
originalSourceHash: proposal.originalSourceHash,
|
|
258
|
+
replacementSelector: proposal.replacementSelector,
|
|
259
|
+
replacementAstHash: proposal.replacementAstHash,
|
|
260
|
+
fullDiffHash: proposal.fullDiffHash,
|
|
261
|
+
patchedSourceHash: proposal.patchedSourceHash,
|
|
262
|
+
testFilePath: proposal.testFilePath,
|
|
263
|
+
testFileHash: proposal.testFileHash,
|
|
264
|
+
executionVerified: proposal.worktreeExecutionVerified,
|
|
265
|
+
issuedAt: now,
|
|
266
|
+
expiresAt: now + ttl,
|
|
267
|
+
used: false,
|
|
268
|
+
nonce,
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
this.tokens.set(tokenId, payload);
|
|
272
|
+
try {
|
|
273
|
+
fs.mkdirSync(this.tokensDir, { recursive: true });
|
|
274
|
+
fs.writeFileSync(
|
|
275
|
+
path.join(this.tokensDir, `${tokenId}.json`),
|
|
276
|
+
JSON.stringify(payload, null, 2),
|
|
277
|
+
'utf-8'
|
|
278
|
+
);
|
|
279
|
+
} catch {
|
|
280
|
+
// Safe fallback
|
|
281
|
+
}
|
|
282
|
+
return payload;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Legacy generator helper retained for compatibility, now strictly requires explicit execution verification.
|
|
287
|
+
*/
|
|
288
|
+
public generateToken(params: {
|
|
289
|
+
proposalId: string;
|
|
290
|
+
patchHash: string;
|
|
291
|
+
targetFilePath: string;
|
|
292
|
+
originalSourceHash: string;
|
|
293
|
+
targetClassName?: string;
|
|
294
|
+
targetMemberName?: string;
|
|
295
|
+
replacementSelector?: string;
|
|
296
|
+
fullDiffHash?: string;
|
|
297
|
+
patchedSourceHash?: string;
|
|
298
|
+
testFilePath?: string;
|
|
299
|
+
testFileHash?: string;
|
|
300
|
+
executionVerified?: boolean;
|
|
301
|
+
ttlMs?: number;
|
|
302
|
+
}): ApprovalTokenPayload {
|
|
303
|
+
let canonicalTarget = params.targetFilePath;
|
|
304
|
+
try {
|
|
305
|
+
if (fs.existsSync(params.targetFilePath)) {
|
|
306
|
+
canonicalTarget = fs.realpathSync(params.targetFilePath);
|
|
307
|
+
}
|
|
308
|
+
} catch {
|
|
309
|
+
// fallback
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let canonicalTest = params.testFilePath ?? params.targetFilePath;
|
|
313
|
+
try {
|
|
314
|
+
if (fs.existsSync(canonicalTest)) {
|
|
315
|
+
canonicalTest = fs.realpathSync(canonicalTest);
|
|
316
|
+
}
|
|
317
|
+
} catch {
|
|
318
|
+
// fallback
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
const tokenId = crypto.randomBytes(32).toString('hex');
|
|
322
|
+
const nonce = crypto.randomBytes(16).toString('hex');
|
|
323
|
+
const now = Date.now();
|
|
324
|
+
const ttl = params.ttlMs ?? this.defaultTtlMs;
|
|
325
|
+
|
|
326
|
+
// Fail closed: executionVerified is strictly false unless explicitly supplied as true
|
|
327
|
+
const executionVerified = params.executionVerified === true;
|
|
328
|
+
|
|
329
|
+
const payload: ApprovalTokenPayload = {
|
|
330
|
+
tokenId,
|
|
331
|
+
proposalId: params.proposalId,
|
|
332
|
+
targetFilePath: canonicalTarget,
|
|
333
|
+
targetClassName: params.targetClassName ?? 'UnknownClass',
|
|
334
|
+
targetMemberName: params.targetMemberName ?? 'unknownMember',
|
|
335
|
+
originalSourceHash: params.originalSourceHash,
|
|
336
|
+
replacementSelector: params.replacementSelector ?? '',
|
|
337
|
+
replacementAstHash: params.patchHash,
|
|
338
|
+
fullDiffHash: params.fullDiffHash ?? params.patchHash,
|
|
339
|
+
patchedSourceHash: params.patchedSourceHash ?? params.patchHash,
|
|
340
|
+
testFilePath: canonicalTest,
|
|
341
|
+
testFileHash: params.testFileHash ?? '',
|
|
342
|
+
executionVerified,
|
|
343
|
+
issuedAt: now,
|
|
344
|
+
expiresAt: now + ttl,
|
|
345
|
+
used: false,
|
|
346
|
+
nonce,
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
this.tokens.set(tokenId, payload);
|
|
350
|
+
try {
|
|
351
|
+
fs.mkdirSync(this.tokensDir, { recursive: true });
|
|
352
|
+
fs.writeFileSync(
|
|
353
|
+
path.join(this.tokensDir, `${tokenId}.json`),
|
|
354
|
+
JSON.stringify(payload, null, 2),
|
|
355
|
+
'utf-8'
|
|
356
|
+
);
|
|
357
|
+
} catch {}
|
|
358
|
+
return payload;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Verifies an approval token against the current file state, target member, diff hash,
|
|
363
|
+
* and test file hash, then atomically consumes the token on success.
|
|
364
|
+
*/
|
|
365
|
+
public verifyAndConsumeToken(params: {
|
|
366
|
+
tokenId: string;
|
|
367
|
+
targetFilePath: string;
|
|
368
|
+
currentSourceContent: string;
|
|
369
|
+
targetClassName?: string;
|
|
370
|
+
targetMemberName?: string;
|
|
371
|
+
patchHash?: string;
|
|
372
|
+
fullDiffHash?: string;
|
|
373
|
+
currentTestFileContent?: string;
|
|
374
|
+
}): TokenValidationResult {
|
|
375
|
+
this.cleanupExpired();
|
|
376
|
+
|
|
377
|
+
const payload = this.tokens.get(params.tokenId);
|
|
378
|
+
if (!payload) {
|
|
379
|
+
return {
|
|
380
|
+
isValid: false,
|
|
381
|
+
reason: 'Approval token not found or already expired/consumed.',
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (payload.used) {
|
|
386
|
+
return {
|
|
387
|
+
isValid: false,
|
|
388
|
+
reason: 'Security Violation: Approval token has already been consumed (replay rejected).',
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (Date.now() > payload.expiresAt) {
|
|
393
|
+
this.tokens.delete(params.tokenId);
|
|
394
|
+
return {
|
|
395
|
+
isValid: false,
|
|
396
|
+
reason: 'Approval token has expired.',
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
let canonicalPath = params.targetFilePath;
|
|
401
|
+
try {
|
|
402
|
+
if (fs.existsSync(params.targetFilePath)) {
|
|
403
|
+
canonicalPath = fs.realpathSync(params.targetFilePath);
|
|
404
|
+
}
|
|
405
|
+
} catch {
|
|
406
|
+
// ignore
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (canonicalPath !== payload.targetFilePath) {
|
|
410
|
+
return {
|
|
411
|
+
isValid: false,
|
|
412
|
+
reason: `Target file mismatch: token is bound to '${payload.targetFilePath}', received '${canonicalPath}'.`,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Target class and member validation
|
|
417
|
+
if (params.targetClassName && params.targetClassName !== payload.targetClassName) {
|
|
418
|
+
return {
|
|
419
|
+
isValid: false,
|
|
420
|
+
reason: `Class mismatch: token is bound to class '${payload.targetClassName}', received '${params.targetClassName}'.`,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (params.targetMemberName && params.targetMemberName !== payload.targetMemberName) {
|
|
425
|
+
return {
|
|
426
|
+
isValid: false,
|
|
427
|
+
reason: `Member mismatch: token is bound to member '${payload.targetMemberName}', received '${params.targetMemberName}'.`,
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Verify source file content has not changed since proposal was generated
|
|
432
|
+
const currentHash = ApprovalService.hashContent(params.currentSourceContent);
|
|
433
|
+
if (currentHash !== payload.originalSourceHash) {
|
|
434
|
+
return {
|
|
435
|
+
isValid: false,
|
|
436
|
+
reason: 'Target file has been modified since the repair proposal was approved (source hash mismatch).',
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Verify diff hash if supplied
|
|
441
|
+
if (params.fullDiffHash && params.fullDiffHash !== payload.fullDiffHash) {
|
|
442
|
+
return {
|
|
443
|
+
isValid: false,
|
|
444
|
+
reason: 'Diff hash mismatch: token is bound to a different patch diff.',
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Verify patch/ast hash if supplied
|
|
449
|
+
if (params.patchHash && params.patchHash !== payload.replacementAstHash && params.patchHash !== payload.fullDiffHash) {
|
|
450
|
+
return {
|
|
451
|
+
isValid: false,
|
|
452
|
+
reason: 'Patch content mismatch: token is bound to a different patch hash.',
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Verify test file content has not changed
|
|
457
|
+
if (params.currentTestFileContent && payload.testFileHash) {
|
|
458
|
+
const currentTestHash = ApprovalService.hashContent(params.currentTestFileContent);
|
|
459
|
+
if (currentTestHash !== payload.testFileHash) {
|
|
460
|
+
return {
|
|
461
|
+
isValid: false,
|
|
462
|
+
reason: 'Test file has been modified since the repair proposal was approved (test hash mismatch).',
|
|
463
|
+
};
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// Verify execution was actually verified
|
|
468
|
+
if (!payload.executionVerified) {
|
|
469
|
+
return {
|
|
470
|
+
isValid: false,
|
|
471
|
+
reason: 'Security Violation: Cannot consume token for unverified repair execution.',
|
|
472
|
+
};
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Atomically mark as consumed in memory and delete on disk
|
|
476
|
+
payload.used = true;
|
|
477
|
+
this.tokens.delete(params.tokenId);
|
|
478
|
+
const diskPath = path.join(this.tokensDir, `${params.tokenId}.json`);
|
|
479
|
+
if (fs.existsSync(diskPath)) {
|
|
480
|
+
try {
|
|
481
|
+
fs.unlinkSync(diskPath);
|
|
482
|
+
} catch {}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
isValid: true,
|
|
487
|
+
payload,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
public revokeToken(tokenId: string): boolean {
|
|
492
|
+
const memDeleted = this.tokens.delete(tokenId);
|
|
493
|
+
const diskPath = path.join(this.tokensDir, `${tokenId}.json`);
|
|
494
|
+
if (fs.existsSync(diskPath)) {
|
|
495
|
+
try {
|
|
496
|
+
fs.unlinkSync(diskPath);
|
|
497
|
+
return true;
|
|
498
|
+
} catch {}
|
|
499
|
+
}
|
|
500
|
+
return memDeleted;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
public clearAllTokens(): void {
|
|
504
|
+
this.tokens.clear();
|
|
505
|
+
this.proposals.clear();
|
|
506
|
+
try {
|
|
507
|
+
if (fs.existsSync(this.proposalsDir)) {
|
|
508
|
+
for (const f of fs.readdirSync(this.proposalsDir)) {
|
|
509
|
+
try { fs.unlinkSync(path.join(this.proposalsDir, f)); } catch {}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
if (fs.existsSync(this.tokensDir)) {
|
|
513
|
+
for (const f of fs.readdirSync(this.tokensDir)) {
|
|
514
|
+
try { fs.unlinkSync(path.join(this.tokensDir, f)); } catch {}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
} catch {}
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
private cleanupExpired(): void {
|
|
521
|
+
const now = Date.now();
|
|
522
|
+
for (const [id, payload] of this.tokens.entries()) {
|
|
523
|
+
if (now > payload.expiresAt || payload.used) {
|
|
524
|
+
this.tokens.delete(id);
|
|
525
|
+
const diskPath = path.join(this.tokensDir, `${id}.json`);
|
|
526
|
+
if (fs.existsSync(diskPath)) {
|
|
527
|
+
try { fs.unlinkSync(diskPath); } catch {}
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
for (const [id, proposal] of this.proposals.entries()) {
|
|
532
|
+
if (now > proposal.expiresAt) {
|
|
533
|
+
this.proposals.delete(id);
|
|
534
|
+
const diskPath = path.join(this.proposalsDir, `${id}.json`);
|
|
535
|
+
if (fs.existsSync(diskPath)) {
|
|
536
|
+
try { fs.unlinkSync(diskPath); } catch {}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|