diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ebae892 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,221 @@ +name: CI/CD Pipeline + +on: + push: + branches: [ main, master, develop, 'feature/**' ] + pull_request: + branches: [ main, master, develop ] + +env: + NODE_VERSION: '20' + +jobs: + # ===================================================== + # Job 1: 代码质量检查 (Lint & Type Check) + # ===================================================== + lint-and-typecheck: + name: Code Quality Check + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run ESLint + run: pnpm run lint + + - name: Type check + run: npx tsc --noEmit + + # ===================================================== + # Job 2: 单元测试 (Unit Tests) + # ===================================================== + unit-tests: + name: Unit Tests + runs-on: windows-latest # Windows runner for Electron app + timeout-minutes: 15 + needs: lint-and-typecheck + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run unit tests + run: pnpm run test:unit + + - name: Generate coverage report + run: pnpm run test:coverage + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + files: ./coverage/lcov.info + flags: unittests + name: codecov-umbrella + fail_ci_if_error: false + + # ===================================================== + # Job 3: 构建测试 (Build Test) + # ===================================================== + build-test: + name: Build Test + runs-on: windows-latest + timeout-minutes: 20 + needs: unit-tests + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Package application (dry run) + run: pnpm run package + + # ===================================================== + # Job 4: 发布检查 (Release Check) + # ===================================================== + release-check: + name: Release Check + runs-on: windows-latest + timeout-minutes: 30 + needs: [unit-tests, build-test] + if: github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js ${{ env.NODE_VERSION }} + uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Get pnpm store directory + shell: bash + run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: actions/cache@v4 + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build and package + run: | + pnpm run package + pnpm run make + + # ===================================================== + # Job 5: 测试结果通知 (Notifications) + # ===================================================== + test-notify: + name: Test Results Summary + runs-on: ubuntu-latest + needs: [lint-and-typecheck, unit-tests, build-test] + if: always() + + steps: + - name: Test Summary + run: | + echo "=========================================" + echo " Test Results Summary" + echo "=========================================" + echo "Lint & Type Check: ${{ needs.lint-and-typecheck.result }}" + echo "Unit Tests: ${{ needs.unit-tests.result }}" + echo "Build Test: ${{ needs.build-test.result }}" + echo "=========================================" + + - name: Job Status + if: needs.lint-and-typecheck.result == 'success' && needs.unit-tests.result == 'success' && needs.build-test.result == 'success' + run: echo "✅ All jobs passed!" + + - name: Job Status (Failed) + if: needs.lint-and-typecheck.result != 'success' || needs.unit-tests.result != 'success' || needs.build-test.result != 'success' + run: | + echo "❌ Some jobs failed!" + exit 1 diff --git a/coverage/base.css b/coverage/base.css new file mode 100644 index 0000000..f418035 --- /dev/null +++ b/coverage/base.css @@ -0,0 +1,224 @@ +body, html { + margin:0; padding: 0; + height: 100%; +} +body { + font-family: Helvetica Neue, Helvetica, Arial; + font-size: 14px; + color:#333; +} +.small { font-size: 12px; } +*, *:after, *:before { + -webkit-box-sizing:border-box; + -moz-box-sizing:border-box; + box-sizing:border-box; + } +h1 { font-size: 20px; margin: 0;} +h2 { font-size: 14px; } +pre { + font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; + margin: 0; + padding: 0; + -moz-tab-size: 2; + -o-tab-size: 2; + tab-size: 2; +} +a { color:#0074D9; text-decoration:none; } +a:hover { text-decoration:underline; } +.strong { font-weight: bold; } +.space-top1 { padding: 10px 0 0 0; } +.pad2y { padding: 20px 0; } +.pad1y { padding: 10px 0; } +.pad2x { padding: 0 20px; } +.pad2 { padding: 20px; } +.pad1 { padding: 10px; } +.space-left2 { padding-left:55px; } +.space-right2 { padding-right:20px; } +.center { text-align:center; } +.clearfix { display:block; } +.clearfix:after { + content:''; + display:block; + height:0; + clear:both; + visibility:hidden; + } +.fl { float: left; } +@media only screen and (max-width:640px) { + .col3 { width:100%; max-width:100%; } + .hide-mobile { display:none!important; } +} + +.quiet { + color: #7f7f7f; + color: rgba(0,0,0,0.5); +} +.quiet a { opacity: 0.7; } + +.fraction { + font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; + font-size: 10px; + color: #555; + background: #E8E8E8; + padding: 4px 5px; + border-radius: 3px; + vertical-align: middle; +} + +div.path a:link, div.path a:visited { color: #333; } +table.coverage { + border-collapse: collapse; + margin: 10px 0 0 0; + padding: 0; +} + +table.coverage td { + margin: 0; + padding: 0; + vertical-align: top; +} +table.coverage td.line-count { + text-align: right; + padding: 0 5px 0 20px; +} +table.coverage td.line-coverage { + text-align: right; + padding-right: 10px; + min-width:20px; +} + +table.coverage td span.cline-any { + display: inline-block; + padding: 0 5px; + width: 100%; +} +.missing-if-branch { + display: inline-block; + margin-right: 5px; + border-radius: 3px; + position: relative; + padding: 0 4px; + background: #333; + color: yellow; +} + +.skip-if-branch { + display: none; + margin-right: 10px; + position: relative; + padding: 0 4px; + background: #ccc; + color: white; +} +.missing-if-branch .typ, .skip-if-branch .typ { + color: inherit !important; +} +.coverage-summary { + border-collapse: collapse; + width: 100%; +} +.coverage-summary tr { border-bottom: 1px solid #bbb; } +.keyline-all { border: 1px solid #ddd; } +.coverage-summary td, .coverage-summary th { padding: 10px; } +.coverage-summary tbody { border: 1px solid #bbb; } +.coverage-summary td { border-right: 1px solid #bbb; } +.coverage-summary td:last-child { border-right: none; } +.coverage-summary th { + text-align: left; + font-weight: normal; + white-space: nowrap; +} +.coverage-summary th.file { border-right: none !important; } +.coverage-summary th.pct { } +.coverage-summary th.pic, +.coverage-summary th.abs, +.coverage-summary td.pct, +.coverage-summary td.abs { text-align: right; } +.coverage-summary td.file { white-space: nowrap; } +.coverage-summary td.pic { min-width: 120px !important; } +.coverage-summary tfoot td { } + +.coverage-summary .sorter { + height: 10px; + width: 7px; + display: inline-block; + margin-left: 0.5em; + background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; +} +.coverage-summary .sorted .sorter { + background-position: 0 -20px; +} +.coverage-summary .sorted-desc .sorter { + background-position: 0 -10px; +} +.status-line { height: 10px; } +/* yellow */ +.cbranch-no { background: yellow !important; color: #111; } +/* dark red */ +.red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } +.low .chart { border:1px solid #C21F39 } +.highlighted, +.highlighted .cstat-no, .highlighted .fstat-no, .highlighted .cbranch-no{ + background: #C21F39 !important; +} +/* medium red */ +.cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } +/* light red */ +.low, .cline-no { background:#FCE1E5 } +/* light green */ +.high, .cline-yes { background:rgb(230,245,208) } +/* medium green */ +.cstat-yes { background:rgb(161,215,106) } +/* dark green */ +.status-line.high, .high .cover-fill { background:rgb(77,146,33) } +.high .chart { border:1px solid rgb(77,146,33) } +/* dark yellow (gold) */ +.status-line.medium, .medium .cover-fill { background: #f9cd0b; } +.medium .chart { border:1px solid #f9cd0b; } +/* light yellow */ +.medium { background: #fff4c2; } + +.cstat-skip { background: #ddd; color: #111; } +.fstat-skip { background: #ddd; color: #111 !important; } +.cbranch-skip { background: #ddd !important; color: #111; } + +span.cline-neutral { background: #eaeaea; } + +.coverage-summary td.empty { + opacity: .5; + padding-top: 4px; + padding-bottom: 4px; + line-height: 1; + color: #888; +} + +.cover-fill, .cover-empty { + display:inline-block; + height: 12px; +} +.chart { + line-height: 0; +} +.cover-empty { + background: white; +} +.cover-full { + border-right: none !important; +} +pre.prettyprint { + border: none !important; + padding: 0 !important; + margin: 0 !important; +} +.com { color: #999 !important; } +.ignore-none { color: #999; font-weight: normal; } + +.wrapper { + min-height: 100%; + height: auto !important; + height: 100%; + margin: 0 auto -48px; +} +.footer, .push { + height: 48px; +} diff --git a/coverage/block-navigation.js b/coverage/block-navigation.js new file mode 100644 index 0000000..530d1ed --- /dev/null +++ b/coverage/block-navigation.js @@ -0,0 +1,87 @@ +/* eslint-disable */ +var jumpToCode = (function init() { + // Classes of code we would like to highlight in the file view + var missingCoverageClasses = ['.cbranch-no', '.cstat-no', '.fstat-no']; + + // Elements to highlight in the file listing view + var fileListingElements = ['td.pct.low']; + + // We don't want to select elements that are direct descendants of another match + var notSelector = ':not(' + missingCoverageClasses.join('):not(') + ') > '; // becomes `:not(a):not(b) > ` + + // Selector that finds elements on the page to which we can jump + var selector = + fileListingElements.join(', ') + + ', ' + + notSelector + + missingCoverageClasses.join(', ' + notSelector); // becomes `:not(a):not(b) > a, :not(a):not(b) > b` + + // The NodeList of matching elements + var missingCoverageElements = document.querySelectorAll(selector); + + var currentIndex; + + function toggleClass(index) { + missingCoverageElements + .item(currentIndex) + .classList.remove('highlighted'); + missingCoverageElements.item(index).classList.add('highlighted'); + } + + function makeCurrent(index) { + toggleClass(index); + currentIndex = index; + missingCoverageElements.item(index).scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'center' + }); + } + + function goToPrevious() { + var nextIndex = 0; + if (typeof currentIndex !== 'number' || currentIndex === 0) { + nextIndex = missingCoverageElements.length - 1; + } else if (missingCoverageElements.length > 1) { + nextIndex = currentIndex - 1; + } + + makeCurrent(nextIndex); + } + + function goToNext() { + var nextIndex = 0; + + if ( + typeof currentIndex === 'number' && + currentIndex < missingCoverageElements.length - 1 + ) { + nextIndex = currentIndex + 1; + } + + makeCurrent(nextIndex); + } + + return function jump(event) { + if ( + document.getElementById('fileSearch') === document.activeElement && + document.activeElement != null + ) { + // if we're currently focused on the search input, we don't want to navigate + return; + } + + switch (event.which) { + case 78: // n + case 74: // j + goToNext(); + break; + case 66: // b + case 75: // k + case 80: // p + goToPrevious(); + break; + } + }; +})(); +window.addEventListener('keydown', jumpToCode); diff --git a/coverage/coverage-final.json b/coverage/coverage-final.json new file mode 100644 index 0000000..88275ed --- /dev/null +++ b/coverage/coverage-final.json @@ -0,0 +1,13 @@ +{"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\data\\repositories\\BackupSnapshotRepo.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\data\\repositories\\BackupSnapshotRepo.ts","statementMap":{"0":{"start":{"line":15,"column":31},"end":{"line":15,"column":54}},"1":{"start":{"line":18,"column":17},"end":{"line":22,"column":null}},"2":{"start":{"line":23,"column":19},"end":{"line":28,"column":null}},"3":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"4":{"start":{"line":34,"column":17},"end":{"line":36,"column":12}},"5":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"6":{"start":{"line":41,"column":16},"end":{"line":43,"column":14}},"7":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"8":{"start":{"line":48,"column":4},"end":{"line":48,"column":null}},"9":{"start":{"line":52,"column":4},"end":{"line":59,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":15,"column":2},"end":{"line":15,"column":14}},"loc":{"start":{"line":15,"column":54},"end":{"line":15,"column":54}},"line":15},"1":{"name":"(anonymous_1)","decl":{"start":{"line":17,"column":2},"end":{"line":17,"column":9}},"loc":{"start":{"line":17,"column":63},"end":{"line":30,"column":null}},"line":17},"2":{"name":"(anonymous_2)","decl":{"start":{"line":33,"column":2},"end":{"line":33,"column":30}},"loc":{"start":{"line":33,"column":30},"end":{"line":37,"column":null}},"line":33},"3":{"name":"(anonymous_3)","decl":{"start":{"line":40,"column":2},"end":{"line":40,"column":11}},"loc":{"start":{"line":40,"column":46},"end":{"line":44,"column":null}},"line":40},"4":{"name":"(anonymous_4)","decl":{"start":{"line":47,"column":2},"end":{"line":47,"column":9}},"loc":{"start":{"line":47,"column":27},"end":{"line":48,"column":null}},"line":47},"5":{"name":"(anonymous_5)","decl":{"start":{"line":51,"column":2},"end":{"line":51,"column":10}},"loc":{"start":{"line":51,"column":46},"end":{"line":59,"column":null}},"line":51}},"branchMap":{"0":{"loc":{"start":{"line":44,"column":11},"end":{"line":44,"column":null}},"type":"cond-expr","locations":[{"start":{"line":44,"column":17},"end":{"line":44,"column":34}},{"start":{"line":44,"column":37},"end":{"line":44,"column":null}}],"line":44}},"s":{"0":6,"1":1,"2":1,"3":1,"4":2,"5":2,"6":2,"7":2,"8":1,"9":3},"f":{"0":6,"1":1,"2":2,"3":2,"4":1,"5":3},"b":{"0":[1,1]},"meta":{"lastBranch":1,"lastFunction":6,"lastStatement":10,"seen":{"f:15:2:15:14":0,"s:15:31:15:54":0,"f:17:2:17:9":1,"s:18:17:22:Infinity":1,"s:23:19:28:Infinity":2,"s:30:4:30:Infinity":3,"f:33:2:33:30":2,"s:34:17:36:12":4,"s:37:4:37:Infinity":5,"f:40:2:40:11":3,"s:41:16:43:14":6,"s:44:4:44:Infinity":7,"b:44:17:44:34:44:37:44:Infinity":0,"f:47:2:47:9":4,"s:48:4:48:Infinity":8,"f:51:2:51:10":5,"s:52:4:59:Infinity":9}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\data\\repositories\\OperationRecordRepo.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\data\\repositories\\OperationRecordRepo.ts","statementMap":{"0":{"start":{"line":16,"column":31},"end":{"line":16,"column":54}},"1":{"start":{"line":19,"column":17},"end":{"line":23,"column":null}},"2":{"start":{"line":24,"column":19},"end":{"line":30,"column":null}},"3":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"4":{"start":{"line":36,"column":17},"end":{"line":38,"column":12}},"5":{"start":{"line":39,"column":4},"end":{"line":39,"column":null}},"6":{"start":{"line":43,"column":16},"end":{"line":45,"column":14}},"7":{"start":{"line":46,"column":4},"end":{"line":46,"column":null}},"8":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"9":{"start":{"line":54,"column":4},"end":{"line":62,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":16,"column":2},"end":{"line":16,"column":14}},"loc":{"start":{"line":16,"column":54},"end":{"line":16,"column":54}},"line":16},"1":{"name":"(anonymous_1)","decl":{"start":{"line":18,"column":2},"end":{"line":18,"column":9}},"loc":{"start":{"line":18,"column":63},"end":{"line":32,"column":null}},"line":18},"2":{"name":"(anonymous_2)","decl":{"start":{"line":35,"column":2},"end":{"line":35,"column":31}},"loc":{"start":{"line":35,"column":31},"end":{"line":39,"column":null}},"line":35},"3":{"name":"(anonymous_3)","decl":{"start":{"line":42,"column":2},"end":{"line":42,"column":11}},"loc":{"start":{"line":42,"column":47},"end":{"line":46,"column":null}},"line":42},"4":{"name":"(anonymous_4)","decl":{"start":{"line":49,"column":2},"end":{"line":49,"column":20}},"loc":{"start":{"line":49,"column":20},"end":{"line":50,"column":null}},"line":49},"5":{"name":"(anonymous_5)","decl":{"start":{"line":53,"column":2},"end":{"line":53,"column":10}},"loc":{"start":{"line":53,"column":47},"end":{"line":62,"column":null}},"line":53}},"branchMap":{"0":{"loc":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"type":"binary-expr","locations":[{"start":{"line":29,"column":6},"end":{"line":29,"column":25}},{"start":{"line":29,"column":25},"end":{"line":29,"column":null}}],"line":29},"1":{"loc":{"start":{"line":30,"column":6},"end":{"line":30,"column":null}},"type":"binary-expr","locations":[{"start":{"line":30,"column":6},"end":{"line":30,"column":25}},{"start":{"line":30,"column":25},"end":{"line":30,"column":null}}],"line":30},"2":{"loc":{"start":{"line":46,"column":11},"end":{"line":46,"column":null}},"type":"cond-expr","locations":[{"start":{"line":46,"column":17},"end":{"line":46,"column":34}},{"start":{"line":46,"column":37},"end":{"line":46,"column":null}}],"line":46}},"s":{"0":6,"1":2,"2":2,"3":2,"4":1,"5":1,"6":2,"7":2,"8":1,"9":3},"f":{"0":6,"1":2,"2":1,"3":2,"4":1,"5":3},"b":{"0":[2,1],"1":[2,1],"2":[1,1]},"meta":{"lastBranch":3,"lastFunction":6,"lastStatement":10,"seen":{"f:16:2:16:14":0,"s:16:31:16:54":0,"f:18:2:18:9":1,"s:19:17:23:Infinity":1,"s:24:19:30:Infinity":2,"b:29:6:29:25:29:25:29:Infinity":0,"b:30:6:30:25:30:25:30:Infinity":1,"s:32:4:32:Infinity":3,"f:35:2:35:31":2,"s:36:17:38:12":4,"s:39:4:39:Infinity":5,"f:42:2:42:11":3,"s:43:16:45:14":6,"s:46:4:46:Infinity":7,"b:46:17:46:34:46:37:46:Infinity":2,"f:49:2:49:20":4,"s:50:4:50:Infinity":8,"f:53:2:53:10":5,"s:54:4:62:Infinity":9}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\ipc\\registry.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\ipc\\registry.ts","statementMap":{"0":{"start":{"line":9,"column":2},"end":{"line":14,"column":null}},"1":{"start":{"line":12,"column":6},"end":{"line":12,"column":37}},"2":{"start":{"line":16,"column":2},"end":{"line":33,"column":null}},"3":{"start":{"line":19,"column":19},"end":{"line":29,"column":null}},"4":{"start":{"line":30,"column":21},"end":{"line":30,"column":55}},"5":{"start":{"line":31,"column":6},"end":{"line":31,"column":null}},"6":{"start":{"line":35,"column":2},"end":{"line":45,"column":null}},"7":{"start":{"line":38,"column":6},"end":{"line":41,"column":null}},"8":{"start":{"line":39,"column":8},"end":{"line":39,"column":null}},"9":{"start":{"line":41,"column":8},"end":{"line":41,"column":null}},"10":{"start":{"line":43,"column":6},"end":{"line":43,"column":null}}},"fnMap":{"0":{"name":"registerRegistryHandlers","decl":{"start":{"line":8,"column":16},"end":{"line":8,"column":41}},"loc":{"start":{"line":8,"column":80},"end":{"line":45,"column":null}},"line":8},"1":{"name":"(anonymous_1)","decl":{"start":{"line":11,"column":4},"end":{"line":11,"column":17}},"loc":{"start":{"line":12,"column":6},"end":{"line":12,"column":37}},"line":12},"2":{"name":"(anonymous_2)","decl":{"start":{"line":18,"column":16},"end":{"line":18,"column":23}},"loc":{"start":{"line":18,"column":69},"end":{"line":32,"column":null}},"line":18},"3":{"name":"(anonymous_3)","decl":{"start":{"line":37,"column":16},"end":{"line":37,"column":23}},"loc":{"start":{"line":37,"column":70},"end":{"line":44,"column":null}},"line":37}},"branchMap":{"0":{"loc":{"start":{"line":28,"column":14},"end":{"line":28,"column":null}},"type":"binary-expr","locations":[{"start":{"line":28,"column":14},"end":{"line":28,"column":29}},{"start":{"line":28,"column":29},"end":{"line":28,"column":null}}],"line":28},"1":{"loc":{"start":{"line":38,"column":6},"end":{"line":41,"column":null}},"type":"if","locations":[{"start":{"line":38,"column":6},"end":{"line":41,"column":null}},{"start":{"line":40,"column":13},"end":{"line":41,"column":null}}],"line":38}},"s":{"0":7,"1":1,"2":7,"3":1,"4":1,"5":1,"6":7,"7":2,"8":1,"9":1,"10":2},"f":{"0":7,"1":1,"2":1,"3":2},"b":{"0":[1,0],"1":[1,1]},"meta":{"lastBranch":2,"lastFunction":4,"lastStatement":11,"seen":{"f:8:16:8:41":0,"s:9:2:14:Infinity":0,"f:11:4:11:17":1,"s:12:6:12:37":1,"s:16:2:33:Infinity":2,"f:18:16:18:23":2,"s:19:19:29:Infinity":3,"b:28:14:28:29:28:29:28:Infinity":0,"s:30:21:30:55":4,"s:31:6:31:Infinity":5,"s:35:2:45:Infinity":6,"f:37:16:37:23":3,"b:38:6:41:Infinity:40:13:41:Infinity":1,"s:38:6:41:Infinity":7,"s:39:8:39:Infinity":8,"s:41:8:41:Infinity":9,"s:43:6:43:Infinity":10}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\BackupService.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\BackupService.ts","statementMap":{"0":{"start":{"line":13,"column":14},"end":{"line":13,"column":35}},"1":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"2":{"start":{"line":14,"column":18},"end":{"line":14,"column":null}},"3":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"4":{"start":{"line":20,"column":21},"end":{"line":20,"column":null}},"5":{"start":{"line":21,"column":21},"end":{"line":21,"column":null}},"6":{"start":{"line":22,"column":21},"end":{"line":22,"column":null}},"7":{"start":{"line":26,"column":38},"end":{"line":26,"column":40}},"8":{"start":{"line":27,"column":4},"end":{"line":29,"column":null}},"9":{"start":{"line":28,"column":20},"end":{"line":28,"column":62}},"10":{"start":{"line":29,"column":6},"end":{"line":29,"column":null}},"11":{"start":{"line":32,"column":21},"end":{"line":32,"column":45}},"12":{"start":{"line":33,"column":10},"end":{"line":33,"column":72}},"13":{"start":{"line":35,"column":21},"end":{"line":41,"column":6}},"14":{"start":{"line":43,"column":4},"end":{"line":43,"column":null}},"15":{"start":{"line":44,"column":4},"end":{"line":44,"column":null}},"16":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"17":{"start":{"line":49,"column":21},"end":{"line":49,"column":51}},"18":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"19":{"start":{"line":50,"column":19},"end":{"line":50,"column":null}},"20":{"start":{"line":52,"column":10},"end":{"line":54,"column":20}},"21":{"start":{"line":55,"column":4},"end":{"line":56,"column":null}},"22":{"start":{"line":56,"column":6},"end":{"line":56,"column":null}},"23":{"start":{"line":60,"column":4},"end":{"line":63,"column":null}},"24":{"start":{"line":65,"column":44},"end":{"line":65,"column":78}},"25":{"start":{"line":66,"column":4},"end":{"line":66,"column":null}},"26":{"start":{"line":66,"column":32},"end":{"line":66,"column":null}},"27":{"start":{"line":68,"column":45},"end":{"line":68,"column":47}},"28":{"start":{"line":69,"column":4},"end":{"line":70,"column":null}},"29":{"start":{"line":70,"column":6},"end":{"line":70,"column":null}},"30":{"start":{"line":73,"column":38},"end":{"line":73,"column":40}},"31":{"start":{"line":74,"column":39},"end":{"line":74,"column":41}},"32":{"start":{"line":76,"column":4},"end":{"line":81,"column":null}},"33":{"start":{"line":77,"column":22},"end":{"line":77,"column":119}},"34":{"start":{"line":77,"column":50},"end":{"line":77,"column":118}},"35":{"start":{"line":78,"column":6},"end":{"line":81,"column":null}},"36":{"start":{"line":79,"column":8},"end":{"line":79,"column":null}},"37":{"start":{"line":80,"column":8},"end":{"line":81,"column":null}},"38":{"start":{"line":80,"column":34},"end":{"line":80,"column":null}},"39":{"start":{"line":81,"column":13},"end":{"line":81,"column":null}},"40":{"start":{"line":85,"column":4},"end":{"line":85,"column":null}},"41":{"start":{"line":85,"column":25},"end":{"line":85,"column":null}},"42":{"start":{"line":86,"column":4},"end":{"line":86,"column":null}},"43":{"start":{"line":86,"column":26},"end":{"line":86,"column":null}},"44":{"start":{"line":88,"column":4},"end":{"line":88,"column":null}},"45":{"start":{"line":89,"column":4},"end":{"line":89,"column":null}},"46":{"start":{"line":93,"column":4},"end":{"line":93,"column":null}},"47":{"start":{"line":97,"column":4},"end":{"line":97,"column":null}},"48":{"start":{"line":101,"column":21},"end":{"line":101,"column":51}},"49":{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},"50":{"start":{"line":102,"column":19},"end":{"line":102,"column":null}},"51":{"start":{"line":104,"column":41},"end":{"line":104,"column":75}},"52":{"start":{"line":105,"column":42},"end":{"line":105,"column":44}},"53":{"start":{"line":106,"column":4},"end":{"line":107,"column":null}},"54":{"start":{"line":107,"column":6},"end":{"line":107,"column":null}},"55":{"start":{"line":110,"column":36},"end":{"line":110,"column":38}},"56":{"start":{"line":111,"column":4},"end":{"line":114,"column":null}},"57":{"start":{"line":112,"column":22},"end":{"line":112,"column":116}},"58":{"start":{"line":112,"column":47},"end":{"line":112,"column":115}},"59":{"start":{"line":113,"column":6},"end":{"line":114,"column":null}},"60":{"start":{"line":114,"column":8},"end":{"line":114,"column":null}},"61":{"start":{"line":117,"column":4},"end":{"line":117,"column":null}},"62":{"start":{"line":121,"column":21},"end":{"line":121,"column":51}},"63":{"start":{"line":122,"column":4},"end":{"line":122,"column":null}},"64":{"start":{"line":122,"column":19},"end":{"line":122,"column":null}},"65":{"start":{"line":124,"column":35},"end":{"line":128,"column":6}},"66":{"start":{"line":130,"column":4},"end":{"line":130,"column":null}},"67":{"start":{"line":130,"column":31},"end":{"line":130,"column":null}},"68":{"start":{"line":131,"column":4},"end":{"line":131,"column":null}},"69":{"start":{"line":132,"column":4},"end":{"line":132,"column":null}},"70":{"start":{"line":136,"column":36},"end":{"line":143,"column":6}},"71":{"start":{"line":145,"column":4},"end":{"line":145,"column":null}},"72":{"start":{"line":145,"column":39},"end":{"line":145,"column":null}},"73":{"start":{"line":147,"column":21},"end":{"line":147,"column":null}},"74":{"start":{"line":148,"column":21},"end":{"line":148,"column":57}},"75":{"start":{"line":149,"column":10},"end":{"line":149,"column":72}},"76":{"start":{"line":151,"column":21},"end":{"line":157,"column":6}},"77":{"start":{"line":159,"column":4},"end":{"line":159,"column":null}},"78":{"start":{"line":160,"column":4},"end":{"line":160,"column":null}}},"fnMap":{"0":{"name":"normalizeKey","decl":{"start":{"line":12,"column":9},"end":{"line":12,"column":22}},"loc":{"start":{"line":12,"column":43},"end":{"line":15,"column":null}},"line":12},"1":{"name":"(anonymous_1)","decl":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"loc":{"start":{"line":23,"column":4},"end":{"line":22,"column":null}},"line":23},"2":{"name":"(anonymous_2)","decl":{"start":{"line":25,"column":8},"end":{"line":25,"column":21}},"loc":{"start":{"line":25,"column":86},"end":{"line":45,"column":null}},"line":25},"3":{"name":"(anonymous_3)","decl":{"start":{"line":48,"column":8},"end":{"line":48,"column":22}},"loc":{"start":{"line":48,"column":57},"end":{"line":89,"column":null}},"line":48},"4":{"name":"(anonymous_4)","decl":{"start":{"line":77,"column":38},"end":{"line":77,"column":44}},"loc":{"start":{"line":77,"column":50},"end":{"line":77,"column":118}},"line":77},"5":{"name":"(anonymous_5)","decl":{"start":{"line":92,"column":8},"end":{"line":92,"column":21}},"loc":{"start":{"line":92,"column":48},"end":{"line":93,"column":null}},"line":92},"6":{"name":"(anonymous_6)","decl":{"start":{"line":96,"column":2},"end":{"line":96,"column":36}},"loc":{"start":{"line":96,"column":36},"end":{"line":97,"column":null}},"line":96},"7":{"name":"(anonymous_7)","decl":{"start":{"line":100,"column":8},"end":{"line":100,"column":27}},"loc":{"start":{"line":100,"column":75},"end":{"line":117,"column":null}},"line":100},"8":{"name":"(anonymous_8)","decl":{"start":{"line":112,"column":35},"end":{"line":112,"column":41}},"loc":{"start":{"line":112,"column":47},"end":{"line":112,"column":115}},"line":112},"9":{"name":"(anonymous_9)","decl":{"start":{"line":120,"column":8},"end":{"line":120,"column":21}},"loc":{"start":{"line":120,"column":76},"end":{"line":132,"column":null}},"line":120},"10":{"name":"(anonymous_10)","decl":{"start":{"line":135,"column":8},"end":{"line":135,"column":21}},"loc":{"start":{"line":135,"column":66},"end":{"line":160,"column":null}},"line":135}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},{"start":{},"end":{}}],"line":14},"1":{"loc":{"start":{"line":25,"column":35},"end":{"line":25,"column":86}},"type":"default-arg","locations":[{"start":{"line":25,"column":42},"end":{"line":25,"column":86}}],"line":25},"2":{"loc":{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":4},"end":{"line":50,"column":null}},{"start":{},"end":{}}],"line":50},"3":{"loc":{"start":{"line":55,"column":4},"end":{"line":56,"column":null}},"type":"if","locations":[{"start":{"line":55,"column":4},"end":{"line":56,"column":null}},{"start":{},"end":{}}],"line":55},"4":{"loc":{"start":{"line":66,"column":4},"end":{"line":66,"column":null}},"type":"if","locations":[{"start":{"line":66,"column":4},"end":{"line":66,"column":null}},{"start":{},"end":{}}],"line":66},"5":{"loc":{"start":{"line":78,"column":6},"end":{"line":81,"column":null}},"type":"if","locations":[{"start":{"line":78,"column":6},"end":{"line":81,"column":null}},{"start":{},"end":{}}],"line":78},"6":{"loc":{"start":{"line":78,"column":10},"end":{"line":78,"column":65}},"type":"binary-expr","locations":[{"start":{"line":78,"column":10},"end":{"line":78,"column":21}},{"start":{"line":78,"column":21},"end":{"line":78,"column":65}}],"line":78},"7":{"loc":{"start":{"line":80,"column":8},"end":{"line":81,"column":null}},"type":"if","locations":[{"start":{"line":80,"column":8},"end":{"line":81,"column":null}},{"start":{"line":81,"column":13},"end":{"line":81,"column":null}}],"line":80},"8":{"loc":{"start":{"line":85,"column":4},"end":{"line":85,"column":null}},"type":"if","locations":[{"start":{"line":85,"column":4},"end":{"line":85,"column":null}},{"start":{},"end":{}}],"line":85},"9":{"loc":{"start":{"line":86,"column":4},"end":{"line":86,"column":null}},"type":"if","locations":[{"start":{"line":86,"column":4},"end":{"line":86,"column":null}},{"start":{},"end":{}}],"line":86},"10":{"loc":{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},"type":"if","locations":[{"start":{"line":102,"column":4},"end":{"line":102,"column":null}},{"start":{},"end":{}}],"line":102},"11":{"loc":{"start":{"line":113,"column":6},"end":{"line":114,"column":null}},"type":"if","locations":[{"start":{"line":113,"column":6},"end":{"line":114,"column":null}},{"start":{},"end":{}}],"line":113},"12":{"loc":{"start":{"line":113,"column":10},"end":{"line":113,"column":65}},"type":"binary-expr","locations":[{"start":{"line":113,"column":10},"end":{"line":113,"column":21}},{"start":{"line":113,"column":21},"end":{"line":113,"column":65}}],"line":113},"13":{"loc":{"start":{"line":122,"column":4},"end":{"line":122,"column":null}},"type":"if","locations":[{"start":{"line":122,"column":4},"end":{"line":122,"column":null}},{"start":{},"end":{}}],"line":122},"14":{"loc":{"start":{"line":130,"column":4},"end":{"line":130,"column":null}},"type":"if","locations":[{"start":{"line":130,"column":4},"end":{"line":130,"column":null}},{"start":{},"end":{}}],"line":130},"15":{"loc":{"start":{"line":130,"column":8},"end":{"line":130,"column":31}},"type":"binary-expr","locations":[{"start":{"line":130,"column":8},"end":{"line":130,"column":20}},{"start":{"line":130,"column":20},"end":{"line":130,"column":31}}],"line":130},"16":{"loc":{"start":{"line":145,"column":4},"end":{"line":145,"column":null}},"type":"if","locations":[{"start":{"line":145,"column":4},"end":{"line":145,"column":null}},{"start":{},"end":{}}],"line":145},"17":{"loc":{"start":{"line":145,"column":8},"end":{"line":145,"column":39}},"type":"binary-expr","locations":[{"start":{"line":145,"column":8},"end":{"line":145,"column":20}},{"start":{"line":145,"column":20},"end":{"line":145,"column":39}}],"line":145}},"s":{"0":0,"1":0,"2":0,"3":0,"4":4,"5":4,"6":4,"7":2,"8":2,"9":12,"10":12,"11":2,"12":2,"13":2,"14":2,"15":2,"16":2,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":1,"47":1,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":0,"57":0,"58":0,"59":0,"60":0,"61":0,"62":0,"63":0,"64":0,"65":0,"66":0,"67":0,"68":0,"69":0,"70":0,"71":0,"72":0,"73":0,"74":0,"75":0,"76":0,"77":0,"78":0},"f":{"0":0,"1":4,"2":2,"3":0,"4":0,"5":1,"6":1,"7":0,"8":0,"9":0,"10":0},"b":{"0":[0,0],"1":[2],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,0],"12":[0,0],"13":[0,0],"14":[0,0],"15":[0,0],"16":[0,0],"17":[0,0]},"meta":{"lastBranch":18,"lastFunction":11,"lastStatement":79,"seen":{"f:12:9:12:22":0,"s:13:14:13:35":0,"b:14:2:14:Infinity:undefined:undefined:undefined:undefined":0,"s:14:2:14:Infinity":1,"s:14:18:14:Infinity":2,"s:15:2:15:Infinity":3,"f:19:2:19:Infinity":1,"s:20:21:20:Infinity":4,"s:21:21:21:Infinity":5,"s:22:21:22:Infinity":6,"f:25:8:25:21":2,"b:25:42:25:86":1,"s:26:38:26:40":7,"s:27:4:29:Infinity":8,"s:28:20:28:62":9,"s:29:6:29:Infinity":10,"s:32:21:32:45":11,"s:33:10:33:72":12,"s:35:21:41:6":13,"s:43:4:43:Infinity":14,"s:44:4:44:Infinity":15,"s:45:4:45:Infinity":16,"f:48:8:48:22":3,"s:49:21:49:51":17,"b:50:4:50:Infinity:undefined:undefined:undefined:undefined":2,"s:50:4:50:Infinity":18,"s:50:19:50:Infinity":19,"s:52:10:54:20":20,"b:55:4:56:Infinity:undefined:undefined:undefined:undefined":3,"s:55:4:56:Infinity":21,"s:56:6:56:Infinity":22,"s:60:4:63:Infinity":23,"s:65:44:65:78":24,"b:66:4:66:Infinity:undefined:undefined:undefined:undefined":4,"s:66:4:66:Infinity":25,"s:66:32:66:Infinity":26,"s:68:45:68:47":27,"s:69:4:70:Infinity":28,"s:70:6:70:Infinity":29,"s:73:38:73:40":30,"s:74:39:74:41":31,"s:76:4:81:Infinity":32,"s:77:22:77:119":33,"f:77:38:77:44":4,"s:77:50:77:118":34,"b:78:6:81:Infinity:undefined:undefined:undefined:undefined":5,"s:78:6:81:Infinity":35,"b:78:10:78:21:78:21:78:65":6,"s:79:8:79:Infinity":36,"b:80:8:81:Infinity:81:13:81:Infinity":7,"s:80:8:81:Infinity":37,"s:80:34:80:Infinity":38,"s:81:13:81:Infinity":39,"b:85:4:85:Infinity:undefined:undefined:undefined:undefined":8,"s:85:4:85:Infinity":40,"s:85:25:85:Infinity":41,"b:86:4:86:Infinity:undefined:undefined:undefined:undefined":9,"s:86:4:86:Infinity":42,"s:86:26:86:Infinity":43,"s:88:4:88:Infinity":44,"s:89:4:89:Infinity":45,"f:92:8:92:21":5,"s:93:4:93:Infinity":46,"f:96:2:96:36":6,"s:97:4:97:Infinity":47,"f:100:8:100:27":7,"s:101:21:101:51":48,"b:102:4:102:Infinity:undefined:undefined:undefined:undefined":10,"s:102:4:102:Infinity":49,"s:102:19:102:Infinity":50,"s:104:41:104:75":51,"s:105:42:105:44":52,"s:106:4:107:Infinity":53,"s:107:6:107:Infinity":54,"s:110:36:110:38":55,"s:111:4:114:Infinity":56,"s:112:22:112:116":57,"f:112:35:112:41":8,"s:112:47:112:115":58,"b:113:6:114:Infinity:undefined:undefined:undefined:undefined":11,"s:113:6:114:Infinity":59,"b:113:10:113:21:113:21:113:65":12,"s:114:8:114:Infinity":60,"s:117:4:117:Infinity":61,"f:120:8:120:21":9,"s:121:21:121:51":62,"b:122:4:122:Infinity:undefined:undefined:undefined:undefined":13,"s:122:4:122:Infinity":63,"s:122:19:122:Infinity":64,"s:124:35:128:6":65,"b:130:4:130:Infinity:undefined:undefined:undefined:undefined":14,"s:130:4:130:Infinity":66,"b:130:8:130:20:130:20:130:31":15,"s:130:31:130:Infinity":67,"s:131:4:131:Infinity":68,"s:132:4:132:Infinity":69,"f:135:8:135:21":10,"s:136:36:143:6":70,"b:145:4:145:Infinity:undefined:undefined:undefined:undefined":16,"s:145:4:145:Infinity":71,"b:145:8:145:20:145:20:145:39":17,"s:145:39:145:Infinity":72,"s:147:21:147:Infinity":73,"s:148:21:148:57":74,"s:149:10:149:72":75,"s:151:21:157:6":76,"s:159:4:159:Infinity":77,"s:160:4:160:Infinity":78}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\MenuManagerService.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\MenuManagerService.ts","statementMap":{"0":{"start":{"line":9,"column":21},"end":{"line":9,"column":null}},"1":{"start":{"line":10,"column":21},"end":{"line":10,"column":null}},"2":{"start":{"line":14,"column":4},"end":{"line":14,"column":null}},"3":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"4":{"start":{"line":18,"column":24},"end":{"line":18,"column":null}},"5":{"start":{"line":19,"column":19},"end":{"line":19,"column":77}},"6":{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},"7":{"start":{"line":20,"column":31},"end":{"line":20,"column":null}},"8":{"start":{"line":21,"column":4},"end":{"line":21,"column":null}},"9":{"start":{"line":22,"column":4},"end":{"line":28,"column":null}},"10":{"start":{"line":29,"column":4},"end":{"line":29,"column":null}},"11":{"start":{"line":30,"column":4},"end":{"line":30,"column":null}},"12":{"start":{"line":34,"column":4},"end":{"line":34,"column":null}},"13":{"start":{"line":34,"column":25},"end":{"line":34,"column":null}},"14":{"start":{"line":35,"column":19},"end":{"line":35,"column":78}},"15":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"16":{"start":{"line":36,"column":31},"end":{"line":36,"column":null}},"17":{"start":{"line":37,"column":4},"end":{"line":37,"column":null}},"18":{"start":{"line":38,"column":4},"end":{"line":44,"column":null}},"19":{"start":{"line":45,"column":4},"end":{"line":45,"column":null}},"20":{"start":{"line":46,"column":4},"end":{"line":46,"column":null}},"21":{"start":{"line":50,"column":4},"end":{"line":53,"column":null}},"22":{"start":{"line":51,"column":6},"end":{"line":51,"column":null}},"23":{"start":{"line":53,"column":6},"end":{"line":53,"column":null}},"24":{"start":{"line":58,"column":20},"end":{"line":58,"column":53}},"25":{"start":{"line":58,"column":40},"end":{"line":58,"column":53}},"26":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"27":{"start":{"line":59,"column":25},"end":{"line":59,"column":null}},"28":{"start":{"line":61,"column":4},"end":{"line":61,"column":null}},"29":{"start":{"line":62,"column":4},"end":{"line":69,"column":null}},"30":{"start":{"line":63,"column":6},"end":{"line":64,"column":null}},"31":{"start":{"line":64,"column":8},"end":{"line":64,"column":null}},"32":{"start":{"line":66,"column":6},"end":{"line":66,"column":null}},"33":{"start":{"line":68,"column":6},"end":{"line":68,"column":null}},"34":{"start":{"line":69,"column":6},"end":{"line":69,"column":null}},"35":{"start":{"line":74,"column":20},"end":{"line":74,"column":52}},"36":{"start":{"line":74,"column":40},"end":{"line":74,"column":52}},"37":{"start":{"line":75,"column":4},"end":{"line":75,"column":null}},"38":{"start":{"line":75,"column":25},"end":{"line":75,"column":null}},"39":{"start":{"line":77,"column":4},"end":{"line":77,"column":null}},"40":{"start":{"line":78,"column":4},"end":{"line":85,"column":null}},"41":{"start":{"line":79,"column":6},"end":{"line":80,"column":null}},"42":{"start":{"line":80,"column":8},"end":{"line":80,"column":null}},"43":{"start":{"line":82,"column":6},"end":{"line":82,"column":null}},"44":{"start":{"line":84,"column":6},"end":{"line":84,"column":null}},"45":{"start":{"line":85,"column":6},"end":{"line":85,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"loc":{"start":{"line":11,"column":4},"end":{"line":10,"column":null}},"line":11},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":8},"end":{"line":13,"column":21}},"loc":{"start":{"line":13,"column":65},"end":{"line":14,"column":null}},"line":13},"2":{"name":"(anonymous_2)","decl":{"start":{"line":17,"column":8},"end":{"line":17,"column":19}},"loc":{"start":{"line":17,"column":78},"end":{"line":30,"column":null}},"line":17},"3":{"name":"(anonymous_3)","decl":{"start":{"line":33,"column":8},"end":{"line":33,"column":20}},"loc":{"start":{"line":33,"column":79},"end":{"line":46,"column":null}},"line":33},"4":{"name":"(anonymous_4)","decl":{"start":{"line":49,"column":8},"end":{"line":49,"column":19}},"loc":{"start":{"line":49,"column":78},"end":{"line":53,"column":null}},"line":49},"5":{"name":"(anonymous_5)","decl":{"start":{"line":57,"column":8},"end":{"line":57,"column":20}},"loc":{"start":{"line":57,"column":59},"end":{"line":69,"column":null}},"line":57},"6":{"name":"(anonymous_6)","decl":{"start":{"line":58,"column":26},"end":{"line":58,"column":34}},"loc":{"start":{"line":58,"column":40},"end":{"line":58,"column":53}},"line":58},"7":{"name":"(anonymous_7)","decl":{"start":{"line":73,"column":8},"end":{"line":73,"column":21}},"loc":{"start":{"line":73,"column":60},"end":{"line":85,"column":null}},"line":73},"8":{"name":"(anonymous_8)","decl":{"start":{"line":74,"column":26},"end":{"line":74,"column":34}},"loc":{"start":{"line":74,"column":40},"end":{"line":74,"column":52}},"line":74}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":18,"column":null}},{"start":{},"end":{}}],"line":18},"1":{"loc":{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},"type":"if","locations":[{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},{"start":{},"end":{}}],"line":20},"2":{"loc":{"start":{"line":34,"column":4},"end":{"line":34,"column":null}},"type":"if","locations":[{"start":{"line":34,"column":4},"end":{"line":34,"column":null}},{"start":{},"end":{}}],"line":34},"3":{"loc":{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},"type":"if","locations":[{"start":{"line":36,"column":4},"end":{"line":36,"column":null}},{"start":{},"end":{}}],"line":36},"4":{"loc":{"start":{"line":50,"column":4},"end":{"line":53,"column":null}},"type":"if","locations":[{"start":{"line":50,"column":4},"end":{"line":53,"column":null}},{"start":{"line":52,"column":11},"end":{"line":53,"column":null}}],"line":50},"5":{"loc":{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},"type":"if","locations":[{"start":{"line":59,"column":4},"end":{"line":59,"column":null}},{"start":{},"end":{}}],"line":59},"6":{"loc":{"start":{"line":75,"column":4},"end":{"line":75,"column":null}},"type":"if","locations":[{"start":{"line":75,"column":4},"end":{"line":75,"column":null}},{"start":{},"end":{}}],"line":75}},"s":{"0":13,"1":13,"2":1,"3":6,"4":1,"5":5,"6":4,"7":4,"8":4,"9":4,"10":4,"11":4,"12":6,"13":1,"14":5,"15":4,"16":4,"17":4,"18":4,"19":4,"20":4,"21":2,"22":1,"23":1,"24":3,"25":4,"26":3,"27":1,"28":2,"29":2,"30":2,"31":3,"32":1,"33":1,"34":1,"35":3,"36":4,"37":3,"38":1,"39":2,"40":2,"41":2,"42":3,"43":1,"44":1,"45":1},"f":{"0":13,"1":1,"2":6,"3":6,"4":2,"5":3,"6":4,"7":3,"8":4},"b":{"0":[1,5],"1":[0,4],"2":[1,5],"3":[0,4],"4":[1,1],"5":[1,2],"6":[1,2]},"meta":{"lastBranch":7,"lastFunction":9,"lastStatement":46,"seen":{"f:8:2:8:Infinity":0,"s:9:21:9:Infinity":0,"s:10:21:10:Infinity":1,"f:13:8:13:21":1,"s:14:4:14:Infinity":2,"f:17:8:17:19":2,"b:18:4:18:Infinity:undefined:undefined:undefined:undefined":0,"s:18:4:18:Infinity":3,"s:18:24:18:Infinity":4,"s:19:19:19:77":5,"b:20:4:20:Infinity:undefined:undefined:undefined:undefined":1,"s:20:4:20:Infinity":6,"s:20:31:20:Infinity":7,"s:21:4:21:Infinity":8,"s:22:4:28:Infinity":9,"s:29:4:29:Infinity":10,"s:30:4:30:Infinity":11,"f:33:8:33:20":3,"b:34:4:34:Infinity:undefined:undefined:undefined:undefined":2,"s:34:4:34:Infinity":12,"s:34:25:34:Infinity":13,"s:35:19:35:78":14,"b:36:4:36:Infinity:undefined:undefined:undefined:undefined":3,"s:36:4:36:Infinity":15,"s:36:31:36:Infinity":16,"s:37:4:37:Infinity":17,"s:38:4:44:Infinity":18,"s:45:4:45:Infinity":19,"s:46:4:46:Infinity":20,"f:49:8:49:19":4,"b:50:4:53:Infinity:52:11:53:Infinity":4,"s:50:4:53:Infinity":21,"s:51:6:51:Infinity":22,"s:53:6:53:Infinity":23,"f:57:8:57:20":5,"s:58:20:58:53":24,"f:58:26:58:34":6,"s:58:40:58:53":25,"b:59:4:59:Infinity:undefined:undefined:undefined:undefined":5,"s:59:4:59:Infinity":26,"s:59:25:59:Infinity":27,"s:61:4:61:Infinity":28,"s:62:4:69:Infinity":29,"s:63:6:64:Infinity":30,"s:64:8:64:Infinity":31,"s:66:6:66:Infinity":32,"s:68:6:68:Infinity":33,"s:69:6:69:Infinity":34,"f:73:8:73:21":7,"s:74:20:74:52":35,"f:74:26:74:34":8,"s:74:40:74:52":36,"b:75:4:75:Infinity:undefined:undefined:undefined:undefined":6,"s:75:4:75:Infinity":37,"s:75:25:75:Infinity":38,"s:77:4:77:Infinity":39,"s:78:4:85:Infinity":40,"s:79:6:80:Infinity":41,"s:80:8:80:Infinity":42,"s:82:6:82:Infinity":43,"s:84:6:84:Infinity":44,"s:85:6:85:Infinity":45}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\PowerShellBridge.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\PowerShellBridge.ts","statementMap":{"0":{"start":{"line":10,"column":6},"end":{"line":10,"column":41}},"1":{"start":{"line":12,"column":19},"end":{"line":12,"column":null}},"2":{"start":{"line":13,"column":15},"end":{"line":13,"column":null}},"3":{"start":{"line":20,"column":4},"end":{"line":20,"column":null}},"4":{"start":{"line":21,"column":31},"end":{"line":24,"column":null}},"5":{"start":{"line":27,"column":4},"end":{"line":28,"column":null}},"6":{"start":{"line":28,"column":6},"end":{"line":28,"column":null}},"7":{"start":{"line":31,"column":20},"end":{"line":31,"column":33}},"8":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"9":{"start":{"line":32,"column":18},"end":{"line":32,"column":null}},"10":{"start":{"line":34,"column":4},"end":{"line":38,"column":null}},"11":{"start":{"line":35,"column":6},"end":{"line":35,"column":null}},"12":{"start":{"line":37,"column":6},"end":{"line":37,"column":null}},"13":{"start":{"line":38,"column":6},"end":{"line":38,"column":null}},"14":{"start":{"line":47,"column":4},"end":{"line":48,"column":null}},"15":{"start":{"line":48,"column":6},"end":{"line":48,"column":null}},"16":{"start":{"line":51,"column":16},"end":{"line":51,"column":35}},"17":{"start":{"line":52,"column":22},"end":{"line":52,"column":64}},"18":{"start":{"line":53,"column":23},"end":{"line":53,"column":67}},"19":{"start":{"line":56,"column":25},"end":{"line":56,"column":55}},"20":{"start":{"line":57,"column":27},"end":{"line":71,"column":8}},"21":{"start":{"line":73,"column":4},"end":{"line":73,"column":null}},"22":{"start":{"line":76,"column":25},"end":{"line":76,"column":null}},"23":{"start":{"line":78,"column":4},"end":{"line":78,"column":null}},"24":{"start":{"line":79,"column":4},"end":{"line":86,"column":null}},"25":{"start":{"line":80,"column":6},"end":{"line":84,"column":null}},"26":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"27":{"start":{"line":86,"column":12},"end":{"line":86,"column":45}},"28":{"start":{"line":89,"column":4},"end":{"line":90,"column":null}},"29":{"start":{"line":90,"column":6},"end":{"line":90,"column":null}},"30":{"start":{"line":94,"column":4},"end":{"line":98,"column":null}},"31":{"start":{"line":95,"column":6},"end":{"line":95,"column":null}},"32":{"start":{"line":96,"column":6},"end":{"line":96,"column":null}},"33":{"start":{"line":98,"column":6},"end":{"line":98,"column":null}},"34":{"start":{"line":102,"column":4},"end":{"line":105,"column":null}},"35":{"start":{"line":103,"column":6},"end":{"line":103,"column":null}},"36":{"start":{"line":105,"column":6},"end":{"line":105,"column":null}},"37":{"start":{"line":108,"column":4},"end":{"line":109,"column":null}},"38":{"start":{"line":109,"column":6},"end":{"line":109,"column":null}},"39":{"start":{"line":112,"column":4},"end":{"line":112,"column":null}},"40":{"start":{"line":121,"column":4},"end":{"line":152,"column":null}},"41":{"start":{"line":161,"column":19},"end":{"line":161,"column":null}},"42":{"start":{"line":162,"column":4},"end":{"line":185,"column":null}},"43":{"start":{"line":163,"column":6},"end":{"line":174,"column":null}},"44":{"start":{"line":176,"column":6},"end":{"line":185,"column":null}},"45":{"start":{"line":199,"column":4},"end":{"line":342,"column":null}},"46":{"start":{"line":351,"column":22},"end":{"line":351,"column":55}},"47":{"start":{"line":352,"column":26},"end":{"line":352,"column":65}},"48":{"start":{"line":353,"column":20},"end":{"line":353,"column":60}},"49":{"start":{"line":354,"column":22},"end":{"line":354,"column":48}},"50":{"start":{"line":355,"column":25},"end":{"line":355,"column":null}},"51":{"start":{"line":357,"column":29},"end":{"line":357,"column":null}},"52":{"start":{"line":358,"column":25},"end":{"line":358,"column":null}},"53":{"start":{"line":359,"column":4},"end":{"line":371,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":19,"column":8},"end":{"line":19,"column":19}},"loc":{"start":{"line":19,"column":47},"end":{"line":38,"column":null}},"line":19},"1":{"name":"(anonymous_1)","decl":{"start":{"line":46,"column":8},"end":{"line":46,"column":27}},"loc":{"start":{"line":46,"column":55},"end":{"line":112,"column":null}},"line":46},"2":{"name":"(anonymous_2)","decl":{"start":{"line":119,"column":2},"end":{"line":119,"column":22}},"loc":{"start":{"line":119,"column":51},"end":{"line":152,"column":null}},"line":119},"3":{"name":"(anonymous_3)","decl":{"start":{"line":160,"column":2},"end":{"line":160,"column":24}},"loc":{"start":{"line":160,"column":74},"end":{"line":185,"column":null}},"line":160},"4":{"name":"(anonymous_4)","decl":{"start":{"line":198,"column":2},"end":{"line":198,"column":30}},"loc":{"start":{"line":198,"column":62},"end":{"line":342,"column":null}},"line":198},"5":{"name":"(anonymous_5)","decl":{"start":{"line":350,"column":2},"end":{"line":350,"column":28}},"loc":{"start":{"line":350,"column":78},"end":{"line":371,"column":null}},"line":350}},"branchMap":{"0":{"loc":{"start":{"line":13,"column":15},"end":{"line":13,"column":null}},"type":"cond-expr","locations":[{"start":{"line":13,"column":43},"end":{"line":13,"column":56}},{"start":{"line":13,"column":56},"end":{"line":13,"column":null}}],"line":13},"1":{"loc":{"start":{"line":27,"column":4},"end":{"line":28,"column":null}},"type":"if","locations":[{"start":{"line":27,"column":4},"end":{"line":28,"column":null}},{"start":{},"end":{}}],"line":27},"2":{"loc":{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},"type":"if","locations":[{"start":{"line":32,"column":4},"end":{"line":32,"column":null}},{"start":{},"end":{}}],"line":32},"3":{"loc":{"start":{"line":47,"column":4},"end":{"line":48,"column":null}},"type":"if","locations":[{"start":{"line":47,"column":4},"end":{"line":48,"column":null}},{"start":{},"end":{}}],"line":47},"4":{"loc":{"start":{"line":89,"column":4},"end":{"line":90,"column":null}},"type":"if","locations":[{"start":{"line":89,"column":4},"end":{"line":90,"column":null}},{"start":{},"end":{}}],"line":89},"5":{"loc":{"start":{"line":108,"column":4},"end":{"line":109,"column":null}},"type":"if","locations":[{"start":{"line":108,"column":4},"end":{"line":109,"column":null}},{"start":{},"end":{}}],"line":108},"6":{"loc":{"start":{"line":108,"column":8},"end":{"line":108,"column":69}},"type":"binary-expr","locations":[{"start":{"line":108,"column":8},"end":{"line":108,"column":18}},{"start":{"line":108,"column":18},"end":{"line":108,"column":48}},{"start":{"line":108,"column":48},"end":{"line":108,"column":69}}],"line":108},"7":{"loc":{"start":{"line":162,"column":4},"end":{"line":185,"column":null}},"type":"if","locations":[{"start":{"line":162,"column":4},"end":{"line":185,"column":null}},{"start":{"line":175,"column":11},"end":{"line":185,"column":null}}],"line":162},"8":{"loc":{"start":{"line":357,"column":29},"end":{"line":357,"column":null}},"type":"cond-expr","locations":[{"start":{"line":357,"column":38},"end":{"line":357,"column":56}},{"start":{"line":357,"column":56},"end":{"line":357,"column":null}}],"line":357},"9":{"loc":{"start":{"line":358,"column":25},"end":{"line":358,"column":null}},"type":"cond-expr","locations":[{"start":{"line":358,"column":34},"end":{"line":358,"column":46}},{"start":{"line":358,"column":46},"end":{"line":358,"column":null}}],"line":358}},"s":{"0":1,"1":1,"2":1,"3":0,"4":0,"5":0,"6":0,"7":0,"8":0,"9":0,"10":0,"11":0,"12":0,"13":0,"14":0,"15":0,"16":0,"17":0,"18":0,"19":0,"20":0,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":0,"35":0,"36":0,"37":0,"38":0,"39":0,"40":1,"41":2,"42":2,"43":1,"44":1,"45":0,"46":0,"47":0,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0},"f":{"0":0,"1":0,"2":1,"3":2,"4":0,"5":0},"b":{"0":[0,1],"1":[0,0],"2":[0,0],"3":[0,0],"4":[0,0],"5":[0,0],"6":[0,0,0],"7":[1,1],"8":[0,0],"9":[0,0]},"meta":{"lastBranch":10,"lastFunction":6,"lastStatement":54,"seen":{"s:10:6:10:41":0,"s:12:19:12:Infinity":1,"s:13:15:13:Infinity":2,"b:13:43:13:56:13:56:13:Infinity":0,"f:19:8:19:19":0,"s:20:4:20:Infinity":3,"s:21:31:24:Infinity":4,"b:27:4:28:Infinity:undefined:undefined:undefined:undefined":1,"s:27:4:28:Infinity":5,"s:28:6:28:Infinity":6,"s:31:20:31:33":7,"b:32:4:32:Infinity:undefined:undefined:undefined:undefined":2,"s:32:4:32:Infinity":8,"s:32:18:32:Infinity":9,"s:34:4:38:Infinity":10,"s:35:6:35:Infinity":11,"s:37:6:37:Infinity":12,"s:38:6:38:Infinity":13,"f:46:8:46:27":1,"b:47:4:48:Infinity:undefined:undefined:undefined:undefined":3,"s:47:4:48:Infinity":14,"s:48:6:48:Infinity":15,"s:51:16:51:35":16,"s:52:22:52:64":17,"s:53:23:53:67":18,"s:56:25:56:55":19,"s:57:27:71:8":20,"s:73:4:73:Infinity":21,"s:76:25:76:Infinity":22,"s:78:4:78:Infinity":23,"s:79:4:86:Infinity":24,"s:80:6:84:Infinity":25,"s:86:6:86:Infinity":26,"s:86:12:86:45":27,"b:89:4:90:Infinity:undefined:undefined:undefined:undefined":4,"s:89:4:90:Infinity":28,"s:90:6:90:Infinity":29,"s:94:4:98:Infinity":30,"s:95:6:95:Infinity":31,"s:96:6:96:Infinity":32,"s:98:6:98:Infinity":33,"s:102:4:105:Infinity":34,"s:103:6:103:Infinity":35,"s:105:6:105:Infinity":36,"b:108:4:109:Infinity:undefined:undefined:undefined:undefined":5,"s:108:4:109:Infinity":37,"b:108:8:108:18:108:18:108:48:108:48:108:69":6,"s:109:6:109:Infinity":38,"s:112:4:112:Infinity":39,"f:119:2:119:22":2,"s:121:4:152:Infinity":40,"f:160:2:160:24":3,"s:161:19:161:Infinity":41,"b:162:4:185:Infinity:175:11:185:Infinity":7,"s:162:4:185:Infinity":42,"s:163:6:174:Infinity":43,"s:176:6:185:Infinity":44,"f:198:2:198:30":4,"s:199:4:342:Infinity":45,"f:350:2:350:28":5,"s:351:22:351:55":46,"s:352:26:352:65":47,"s:353:20:353:60":48,"s:354:22:354:48":49,"s:355:25:355:Infinity":50,"s:357:29:357:Infinity":51,"b:357:38:357:56:357:56:357:Infinity":8,"s:358:25:358:Infinity":52,"b:358:34:358:46:358:46:358:Infinity":9,"s:359:4:371:Infinity":53}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\RegistryService.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\services\\RegistryService.ts","statementMap":{"0":{"start":{"line":7,"column":56},"end":{"line":14,"column":null}},"1":{"start":{"line":17,"column":55},"end":{"line":24,"column":null}},"2":{"start":{"line":27,"column":20},"end":{"line":27,"column":null}},"3":{"start":{"line":43,"column":25},"end":{"line":43,"column":51}},"4":{"start":{"line":44,"column":26},"end":{"line":44,"column":null}},"5":{"start":{"line":45,"column":19},"end":{"line":45,"column":null}},"6":{"start":{"line":48,"column":4},"end":{"line":48,"column":null}},"7":{"start":{"line":55,"column":21},"end":{"line":55,"column":null}},"8":{"start":{"line":56,"column":24},"end":{"line":56,"column":null}},"9":{"start":{"line":57,"column":4},"end":{"line":86,"column":null}},"10":{"start":{"line":59,"column":21},"end":{"line":59,"column":58}},"11":{"start":{"line":60,"column":18},"end":{"line":60,"column":64}},"12":{"start":{"line":61,"column":20},"end":{"line":61,"column":64}},"13":{"start":{"line":64,"column":42},"end":{"line":64,"column":44}},"14":{"start":{"line":65,"column":6},"end":{"line":70,"column":null}},"15":{"start":{"line":66,"column":30},"end":{"line":66,"column":78}},"16":{"start":{"line":67,"column":27},"end":{"line":67,"column":80}},"17":{"start":{"line":68,"column":8},"end":{"line":68,"column":null}},"18":{"start":{"line":70,"column":8},"end":{"line":70,"column":null}},"19":{"start":{"line":73,"column":6},"end":{"line":83,"column":null}},"20":{"start":{"line":73,"column":53},"end":{"line":83,"column":9}},"21":{"start":{"line":85,"column":6},"end":{"line":85,"column":null}},"22":{"start":{"line":86,"column":6},"end":{"line":86,"column":null}},"23":{"start":{"line":96,"column":4},"end":{"line":112,"column":null}},"24":{"start":{"line":97,"column":6},"end":{"line":104,"column":null}},"25":{"start":{"line":98,"column":23},"end":{"line":98,"column":78}},"26":{"start":{"line":99,"column":8},"end":{"line":99,"column":null}},"27":{"start":{"line":100,"column":8},"end":{"line":100,"column":null}},"28":{"start":{"line":102,"column":23},"end":{"line":102,"column":74}},"29":{"start":{"line":103,"column":8},"end":{"line":103,"column":null}},"30":{"start":{"line":104,"column":8},"end":{"line":104,"column":null}},"31":{"start":{"line":107,"column":6},"end":{"line":108,"column":null}},"32":{"start":{"line":108,"column":8},"end":{"line":108,"column":null}},"33":{"start":{"line":110,"column":6},"end":{"line":112,"column":null}},"34":{"start":{"line":120,"column":4},"end":{"line":120,"column":null}},"35":{"start":{"line":121,"column":4},"end":{"line":122,"column":null}},"36":{"start":{"line":122,"column":6},"end":{"line":122,"column":null}},"37":{"start":{"line":124,"column":4},"end":{"line":124,"column":null}},"38":{"start":{"line":131,"column":4},"end":{"line":131,"column":null}},"39":{"start":{"line":131,"column":29},"end":{"line":131,"column":null}},"40":{"start":{"line":132,"column":4},"end":{"line":132,"column":null}},"41":{"start":{"line":133,"column":4},"end":{"line":139,"column":null}},"42":{"start":{"line":134,"column":6},"end":{"line":135,"column":null}},"43":{"start":{"line":135,"column":8},"end":{"line":135,"column":null}},"44":{"start":{"line":138,"column":6},"end":{"line":138,"column":null}},"45":{"start":{"line":139,"column":6},"end":{"line":139,"column":null}},"46":{"start":{"line":147,"column":4},"end":{"line":147,"column":null}},"47":{"start":{"line":148,"column":4},"end":{"line":148,"column":null}},"48":{"start":{"line":155,"column":4},"end":{"line":155,"column":null}},"49":{"start":{"line":159,"column":4},"end":{"line":164,"column":null}},"50":{"start":{"line":160,"column":21},"end":{"line":160,"column":76}},"51":{"start":{"line":161,"column":6},"end":{"line":161,"column":null}},"52":{"start":{"line":163,"column":21},"end":{"line":163,"column":72}},"53":{"start":{"line":164,"column":6},"end":{"line":164,"column":null}},"54":{"start":{"line":170,"column":4},"end":{"line":170,"column":null}},"55":{"start":{"line":174,"column":4},"end":{"line":174,"column":null}},"56":{"start":{"line":178,"column":4},"end":{"line":178,"column":null}},"57":{"start":{"line":178,"column":33},"end":{"line":178,"column":null}},"58":{"start":{"line":179,"column":4},"end":{"line":179,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":47,"column":2},"end":{"line":47,"column":14}},"loc":{"start":{"line":47,"column":36},"end":{"line":48,"column":null}},"line":47},"1":{"name":"(anonymous_1)","decl":{"start":{"line":54,"column":8},"end":{"line":54,"column":21}},"loc":{"start":{"line":54,"column":65},"end":{"line":86,"column":null}},"line":54},"2":{"name":"(anonymous_2)","decl":{"start":{"line":73,"column":41},"end":{"line":73,"column":46}},"loc":{"start":{"line":73,"column":53},"end":{"line":83,"column":9}},"line":73},"3":{"name":"(anonymous_3)","decl":{"start":{"line":95,"column":8},"end":{"line":95,"column":23}},"loc":{"start":{"line":95,"column":100},"end":{"line":112,"column":null}},"line":95},"4":{"name":"(anonymous_4)","decl":{"start":{"line":119,"column":2},"end":{"line":119,"column":22}},"loc":{"start":{"line":119,"column":87},"end":{"line":124,"column":null}},"line":119},"5":{"name":"(anonymous_5)","decl":{"start":{"line":130,"column":8},"end":{"line":130,"column":34}},"loc":{"start":{"line":130,"column":34},"end":{"line":139,"column":null}},"line":130},"6":{"name":"(anonymous_6)","decl":{"start":{"line":146,"column":2},"end":{"line":146,"column":28}},"loc":{"start":{"line":146,"column":28},"end":{"line":148,"column":null}},"line":146},"7":{"name":"(anonymous_7)","decl":{"start":{"line":154,"column":2},"end":{"line":154,"column":22}},"loc":{"start":{"line":154,"column":48},"end":{"line":155,"column":null}},"line":154},"8":{"name":"(anonymous_8)","decl":{"start":{"line":158,"column":16},"end":{"line":158,"column":39}},"loc":{"start":{"line":158,"column":93},"end":{"line":164,"column":null}},"line":158},"9":{"name":"(anonymous_9)","decl":{"start":{"line":169,"column":2},"end":{"line":169,"column":10}},"loc":{"start":{"line":169,"column":54},"end":{"line":170,"column":null}},"line":169},"10":{"name":"(anonymous_10)","decl":{"start":{"line":173,"column":0},"end":{"line":173,"column":8}},"loc":{"start":{"line":173,"column":48},"end":{"line":174,"column":null}},"line":173},"11":{"name":"(anonymous_11)","decl":{"start":{"line":177,"column":2},"end":{"line":177,"column":10}},"loc":{"start":{"line":177,"column":57},"end":{"line":179,"column":null}},"line":177}},"branchMap":{"0":{"loc":{"start":{"line":61,"column":20},"end":{"line":61,"column":64}},"type":"cond-expr","locations":[{"start":{"line":61,"column":41},"end":{"line":61,"column":48}},{"start":{"line":61,"column":48},"end":{"line":61,"column":64}}],"line":61},"1":{"loc":{"start":{"line":61,"column":48},"end":{"line":61,"column":64}},"type":"cond-expr","locations":[{"start":{"line":61,"column":54},"end":{"line":61,"column":59}},{"start":{"line":61,"column":62},"end":{"line":61,"column":64}}],"line":61},"2":{"loc":{"start":{"line":68,"column":23},"end":{"line":68,"column":95}},"type":"cond-expr","locations":[{"start":{"line":68,"column":51},"end":{"line":68,"column":65}},{"start":{"line":68,"column":65},"end":{"line":68,"column":95}}],"line":68},"3":{"loc":{"start":{"line":68,"column":65},"end":{"line":68,"column":95}},"type":"cond-expr","locations":[{"start":{"line":68,"column":78},"end":{"line":68,"column":90}},{"start":{"line":68,"column":93},"end":{"line":68,"column":95}}],"line":68},"4":{"loc":{"start":{"line":79,"column":16},"end":{"line":79,"column":58}},"type":"binary-expr","locations":[{"start":{"line":79,"column":16},"end":{"line":79,"column":28}},{"start":{"line":79,"column":28},"end":{"line":79,"column":58}}],"line":79},"5":{"loc":{"start":{"line":97,"column":6},"end":{"line":104,"column":null}},"type":"if","locations":[{"start":{"line":97,"column":6},"end":{"line":104,"column":null}},{"start":{"line":101,"column":13},"end":{"line":104,"column":null}}],"line":97},"6":{"loc":{"start":{"line":107,"column":6},"end":{"line":108,"column":null}},"type":"if","locations":[{"start":{"line":107,"column":6},"end":{"line":108,"column":null}},{"start":{},"end":{}}],"line":107},"7":{"loc":{"start":{"line":131,"column":4},"end":{"line":131,"column":null}},"type":"if","locations":[{"start":{"line":131,"column":4},"end":{"line":131,"column":null}},{"start":{},"end":{}}],"line":131},"8":{"loc":{"start":{"line":159,"column":4},"end":{"line":164,"column":null}},"type":"if","locations":[{"start":{"line":159,"column":4},"end":{"line":164,"column":null}},{"start":{"line":162,"column":11},"end":{"line":164,"column":null}}],"line":159},"9":{"loc":{"start":{"line":170,"column":11},"end":{"line":170,"column":89}},"type":"binary-expr","locations":[{"start":{"line":170,"column":11},"end":{"line":170,"column":42}},{"start":{"line":170,"column":46},"end":{"line":170,"column":89}}],"line":170},"10":{"loc":{"start":{"line":174,"column":11},"end":{"line":174,"column":null}},"type":"binary-expr","locations":[{"start":{"line":174,"column":11},"end":{"line":174,"column":25}},{"start":{"line":174,"column":25},"end":{"line":174,"column":null}}],"line":174},"11":{"loc":{"start":{"line":178,"column":4},"end":{"line":178,"column":null}},"type":"if","locations":[{"start":{"line":178,"column":4},"end":{"line":178,"column":null}},{"start":{},"end":{}}],"line":178}},"s":{"0":1,"1":1,"2":1,"3":4,"4":4,"5":4,"6":4,"7":2,"8":2,"9":2,"10":2,"11":2,"12":2,"13":2,"14":2,"15":2,"16":2,"17":2,"18":0,"19":2,"20":1,"21":0,"22":0,"23":0,"24":0,"25":0,"26":0,"27":0,"28":0,"29":0,"30":0,"31":0,"32":0,"33":0,"34":2,"35":2,"36":3,"37":2,"38":0,"39":0,"40":0,"41":0,"42":0,"43":0,"44":0,"45":0,"46":1,"47":1,"48":0,"49":0,"50":0,"51":0,"52":0,"53":0,"54":0,"55":0,"56":1,"57":0,"58":1},"f":{"0":4,"1":2,"2":1,"3":0,"4":2,"5":0,"6":1,"7":0,"8":0,"9":0,"10":0,"11":1},"b":{"0":[2,0],"1":[0,0],"2":[2,0],"3":[0,0],"4":[1,0],"5":[0,0],"6":[0,0],"7":[0,0],"8":[0,0],"9":[0,0],"10":[0,0],"11":[0,1]},"meta":{"lastBranch":12,"lastFunction":12,"lastStatement":59,"seen":{"s:7:56:14:Infinity":0,"s:17:55:24:Infinity":1,"s:27:20:27:Infinity":2,"s:43:25:43:51":3,"s:44:26:44:Infinity":4,"s:45:19:45:Infinity":5,"f:47:2:47:14":0,"s:48:4:48:Infinity":6,"f:54:8:54:21":1,"s:55:21:55:Infinity":7,"s:56:24:56:Infinity":8,"s:57:4:86:Infinity":9,"s:59:21:59:58":10,"s:60:18:60:64":11,"s:61:20:61:64":12,"b:61:41:61:48:61:48:61:64":0,"b:61:54:61:59:61:62:61:64":1,"s:64:42:64:44":13,"s:65:6:70:Infinity":14,"s:66:30:66:78":15,"s:67:27:67:80":16,"s:68:8:68:Infinity":17,"b:68:51:68:65:68:65:68:95":2,"b:68:78:68:90:68:93:68:95":3,"s:70:8:70:Infinity":18,"s:73:6:83:Infinity":19,"f:73:41:73:46":2,"s:73:53:83:9":20,"b:79:16:79:28:79:28:79:58":4,"s:85:6:85:Infinity":21,"s:86:6:86:Infinity":22,"f:95:8:95:23":3,"s:96:4:112:Infinity":23,"b:97:6:104:Infinity:101:13:104:Infinity":5,"s:97:6:104:Infinity":24,"s:98:23:98:78":25,"s:99:8:99:Infinity":26,"s:100:8:100:Infinity":27,"s:102:23:102:74":28,"s:103:8:103:Infinity":29,"s:104:8:104:Infinity":30,"b:107:6:108:Infinity:undefined:undefined:undefined:undefined":6,"s:107:6:108:Infinity":31,"s:108:8:108:Infinity":32,"s:110:6:112:Infinity":33,"f:119:2:119:22":4,"s:120:4:120:Infinity":34,"s:121:4:122:Infinity":35,"s:122:6:122:Infinity":36,"s:124:4:124:Infinity":37,"f:130:8:130:34":5,"b:131:4:131:Infinity:undefined:undefined:undefined:undefined":7,"s:131:4:131:Infinity":38,"s:131:29:131:Infinity":39,"s:132:4:132:Infinity":40,"s:133:4:139:Infinity":41,"s:134:6:135:Infinity":42,"s:135:8:135:Infinity":43,"s:138:6:138:Infinity":44,"s:139:6:139:Infinity":45,"f:146:2:146:28":6,"s:147:4:147:Infinity":46,"s:148:4:148:Infinity":47,"f:154:2:154:22":7,"s:155:4:155:Infinity":48,"f:158:16:158:39":8,"b:159:4:164:Infinity:162:11:164:Infinity":8,"s:159:4:164:Infinity":49,"s:160:21:160:76":50,"s:161:6:161:Infinity":51,"s:163:21:163:72":52,"s:164:6:164:Infinity":53,"f:169:2:169:10":9,"s:170:4:170:Infinity":54,"b:170:11:170:42:170:46:170:89":9,"f:173:0:173:8":10,"s:174:4:174:Infinity":55,"b:174:11:174:25:174:25:174:Infinity":10,"f:177:2:177:10":11,"b:178:4:178:Infinity:undefined:undefined:undefined:undefined":11,"s:178:4:178:Infinity":56,"s:178:33:178:Infinity":57,"s:179:4:179:Infinity":58}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\AdminHelper.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\AdminHelper.ts","statementMap":{"0":{"start":{"line":6,"column":34},"end":{"line":6,"column":null}},"1":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"2":{"start":{"line":14,"column":36},"end":{"line":14,"column":null}},"3":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"4":{"start":{"line":15,"column":28},"end":{"line":15,"column":null}},"5":{"start":{"line":16,"column":2},"end":{"line":27,"column":null}},"6":{"start":{"line":17,"column":10},"end":{"line":24,"column":23}},"7":{"start":{"line":25,"column":4},"end":{"line":25,"column":null}},"8":{"start":{"line":27,"column":4},"end":{"line":27,"column":null}},"9":{"start":{"line":29,"column":2},"end":{"line":29,"column":null}},"10":{"start":{"line":37,"column":18},"end":{"line":37,"column":36}},"11":{"start":{"line":38,"column":2},"end":{"line":38,"column":null}},"12":{"start":{"line":40,"column":17},"end":{"line":40,"column":null}},"13":{"start":{"line":41,"column":2},"end":{"line":49,"column":null}},"14":{"start":{"line":45,"column":6},"end":{"line":46,"column":null}},"15":{"start":{"line":46,"column":8},"end":{"line":46,"column":null}},"16":{"start":{"line":52,"column":2},"end":{"line":52,"column":null}},"17":{"start":{"line":52,"column":19},"end":{"line":52,"column":29}}},"fnMap":{"0":{"name":"isAdmin","decl":{"start":{"line":13,"column":16},"end":{"line":13,"column":35}},"loc":{"start":{"line":13,"column":35},"end":{"line":29,"column":null}},"line":13},"1":{"name":"restartAsAdmin","decl":{"start":{"line":36,"column":16},"end":{"line":36,"column":39}},"loc":{"start":{"line":36,"column":39},"end":{"line":52,"column":null}},"line":36},"2":{"name":"(anonymous_2)","decl":{"start":{"line":43,"column":57},"end":{"line":43,"column":null}},"loc":{"start":{"line":44,"column":13},"end":{"line":49,"column":null}},"line":44},"3":{"name":"(anonymous_3)","decl":{"start":{"line":52,"column":2},"end":{"line":52,"column":19}},"loc":{"start":{"line":52,"column":19},"end":{"line":52,"column":29}},"line":52}},"branchMap":{"0":{"loc":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},"type":"if","locations":[{"start":{"line":14,"column":2},"end":{"line":14,"column":null}},{"start":{},"end":{}}],"line":14},"1":{"loc":{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},"type":"if","locations":[{"start":{"line":15,"column":2},"end":{"line":15,"column":null}},{"start":{},"end":{}}],"line":15},"2":{"loc":{"start":{"line":45,"column":6},"end":{"line":46,"column":null}},"type":"if","locations":[{"start":{"line":45,"column":6},"end":{"line":46,"column":null}},{"start":{},"end":{}}],"line":45}},"s":{"0":9,"1":8,"2":1,"3":7,"4":3,"5":4,"6":4,"7":4,"8":1,"9":4,"10":3,"11":3,"12":3,"13":3,"14":2,"15":1,"16":3,"17":1},"f":{"0":8,"1":3,"2":2,"3":1},"b":{"0":[1,7],"1":[3,4],"2":[1,1]},"meta":{"lastBranch":3,"lastFunction":4,"lastStatement":18,"seen":{"s:6:34:6:Infinity":0,"f:13:16:13:35":0,"b:14:2:14:Infinity:undefined:undefined:undefined:undefined":0,"s:14:2:14:Infinity":1,"s:14:36:14:Infinity":2,"b:15:2:15:Infinity:undefined:undefined:undefined:undefined":1,"s:15:2:15:Infinity":3,"s:15:28:15:Infinity":4,"s:16:2:27:Infinity":5,"s:17:10:24:23":6,"s:25:4:25:Infinity":7,"s:27:4:27:Infinity":8,"s:29:2:29:Infinity":9,"f:36:16:36:39":1,"s:37:18:37:36":10,"s:38:2:38:Infinity":11,"s:40:17:40:Infinity":12,"s:41:2:49:Infinity":13,"f:43:57:43:Infinity":2,"b:45:6:46:Infinity:undefined:undefined:undefined:undefined":2,"s:45:6:46:Infinity":14,"s:46:8:46:Infinity":15,"s:52:2:52:Infinity":16,"f:52:2:52:19":3,"s:52:19:52:29":17}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\ipcWrapper.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\ipcWrapper.ts","statementMap":{"0":{"start":{"line":13,"column":2},"end":{"line":20,"column":null}},"1":{"start":{"line":14,"column":4},"end":{"line":20,"column":null}},"2":{"start":{"line":15,"column":19},"end":{"line":15,"column":36}},"3":{"start":{"line":16,"column":6},"end":{"line":16,"column":null}},"4":{"start":{"line":18,"column":18},"end":{"line":18,"column":60}},"5":{"start":{"line":19,"column":6},"end":{"line":19,"column":null}},"6":{"start":{"line":20,"column":6},"end":{"line":20,"column":null}}},"fnMap":{"0":{"name":"wrapHandler","decl":{"start":{"line":9,"column":16},"end":{"line":9,"column":null}},"loc":{"start":{"line":11,"column":45},"end":{"line":20,"column":null}},"line":11},"1":{"name":"(anonymous_1)","decl":{"start":{"line":13,"column":9},"end":{"line":13,"column":16}},"loc":{"start":{"line":13,"column":58},"end":{"line":20,"column":null}},"line":13}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":18},"end":{"line":18,"column":60}},"type":"cond-expr","locations":[{"start":{"line":18,"column":39},"end":{"line":18,"column":51}},{"start":{"line":18,"column":51},"end":{"line":18,"column":60}}],"line":18}},"s":{"0":8,"1":8,"2":8,"3":1,"4":7,"5":7,"6":7},"f":{"0":8,"1":8},"b":{"0":[2,5]},"meta":{"lastBranch":1,"lastFunction":2,"lastStatement":7,"seen":{"f:9:16:9:Infinity":0,"s:13:2:20:Infinity":0,"f:13:9:13:16":1,"s:14:4:20:Infinity":1,"s:15:19:15:36":2,"s:16:6:16:Infinity":3,"s:18:18:18:60":4,"b:18:39:18:51:18:51:18:60":0,"s:19:6:19:Infinity":5,"s:20:6:20:Infinity":6}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\logger.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\main\\utils\\logger.ts","statementMap":{"0":{"start":{"line":6,"column":2},"end":{"line":7,"column":null}},"1":{"start":{"line":7,"column":4},"end":{"line":7,"column":58}},"2":{"start":{"line":8,"column":2},"end":{"line":8,"column":null}},"3":{"start":{"line":9,"column":2},"end":{"line":9,"column":null}},"4":{"start":{"line":10,"column":2},"end":{"line":10,"column":null}},"5":{"start":{"line":14,"column":2},"end":{"line":14,"column":null}}},"fnMap":{"0":{"name":"initLogger","decl":{"start":{"line":5,"column":16},"end":{"line":5,"column":35}},"loc":{"start":{"line":5,"column":35},"end":{"line":10,"column":null}},"line":5},"1":{"name":"(anonymous_1)","decl":{"start":{"line":6,"column":22},"end":{"line":6,"column":null}},"loc":{"start":{"line":7,"column":4},"end":{"line":7,"column":58}},"line":7},"2":{"name":"getLogDir","decl":{"start":{"line":13,"column":16},"end":{"line":13,"column":36}},"loc":{"start":{"line":13,"column":36},"end":{"line":14,"column":null}},"line":13}},"branchMap":{},"s":{"0":0,"1":0,"2":0,"3":0,"4":0,"5":0},"f":{"0":0,"1":0,"2":0},"b":{},"meta":{"lastBranch":0,"lastFunction":3,"lastStatement":6,"seen":{"f:5:16:5:35":0,"s:6:2:7:Infinity":0,"f:6:22:6:Infinity":1,"s:7:4:7:58":1,"s:8:2:8:Infinity":2,"s:9:2:9:Infinity":3,"s:10:2:10:Infinity":4,"f:13:16:13:36":2,"s:14:2:14:Infinity":5}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\shared\\enums.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\shared\\enums.ts","statementMap":{"0":{"start":{"line":1,"column":7},"end":{"line":8,"column":null}},"1":{"start":{"line":2,"column":2},"end":{"line":2,"column":null}},"2":{"start":{"line":3,"column":2},"end":{"line":3,"column":null}},"3":{"start":{"line":4,"column":2},"end":{"line":4,"column":null}},"4":{"start":{"line":5,"column":2},"end":{"line":5,"column":null}},"5":{"start":{"line":6,"column":2},"end":{"line":6,"column":null}},"6":{"start":{"line":7,"column":2},"end":{"line":7,"column":null}},"7":{"start":{"line":10,"column":7},"end":{"line":14,"column":null}},"8":{"start":{"line":11,"column":2},"end":{"line":11,"column":null}},"9":{"start":{"line":12,"column":2},"end":{"line":12,"column":null}},"10":{"start":{"line":13,"column":2},"end":{"line":13,"column":null}},"11":{"start":{"line":16,"column":7},"end":{"line":24,"column":null}},"12":{"start":{"line":17,"column":2},"end":{"line":17,"column":null}},"13":{"start":{"line":18,"column":2},"end":{"line":18,"column":null}},"14":{"start":{"line":19,"column":2},"end":{"line":19,"column":null}},"15":{"start":{"line":20,"column":2},"end":{"line":20,"column":null}},"16":{"start":{"line":21,"column":2},"end":{"line":21,"column":null}},"17":{"start":{"line":22,"column":2},"end":{"line":22,"column":null}},"18":{"start":{"line":23,"column":2},"end":{"line":23,"column":null}},"19":{"start":{"line":26,"column":7},"end":{"line":29,"column":null}},"20":{"start":{"line":27,"column":2},"end":{"line":27,"column":null}},"21":{"start":{"line":28,"column":2},"end":{"line":28,"column":null}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":1,"column":7},"end":{"line":1,"column":12}},"loc":{"start":{"line":1,"column":7},"end":{"line":8,"column":null}},"line":1},"1":{"name":"(anonymous_1)","decl":{"start":{"line":10,"column":7},"end":{"line":10,"column":12}},"loc":{"start":{"line":10,"column":7},"end":{"line":14,"column":null}},"line":10},"2":{"name":"(anonymous_2)","decl":{"start":{"line":16,"column":7},"end":{"line":16,"column":12}},"loc":{"start":{"line":16,"column":7},"end":{"line":24,"column":null}},"line":16},"3":{"name":"(anonymous_3)","decl":{"start":{"line":26,"column":7},"end":{"line":26,"column":12}},"loc":{"start":{"line":26,"column":7},"end":{"line":29,"column":null}},"line":26}},"branchMap":{},"s":{"0":6,"1":6,"2":6,"3":6,"4":6,"5":6,"6":6,"7":6,"8":6,"9":6,"10":6,"11":6,"12":6,"13":6,"14":6,"15":6,"16":6,"17":6,"18":6,"19":6,"20":6,"21":6},"f":{"0":6,"1":6,"2":6,"3":6},"b":{},"meta":{"lastBranch":0,"lastFunction":4,"lastStatement":22,"seen":{"s:1:7:8:Infinity":0,"f:1:7:1:12":0,"s:2:2:2:Infinity":1,"s:3:2:3:Infinity":2,"s:4:2:4:Infinity":3,"s:5:2:5:Infinity":4,"s:6:2:6:Infinity":5,"s:7:2:7:Infinity":6,"s:10:7:14:Infinity":7,"f:10:7:10:12":1,"s:11:2:11:Infinity":8,"s:12:2:12:Infinity":9,"s:13:2:13:Infinity":10,"s:16:7:24:Infinity":11,"f:16:7:16:12":2,"s:17:2:17:Infinity":12,"s:18:2:18:Infinity":13,"s:19:2:19:Infinity":14,"s:20:2:20:Infinity":15,"s:21:2:21:Infinity":16,"s:22:2:22:Infinity":17,"s:23:2:23:Infinity":18,"s:26:7:29:Infinity":19,"f:26:7:26:12":3,"s:27:2:27:Infinity":20,"s:28:2:28:Infinity":21}}} +,"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\shared\\ipc-channels.ts": {"path":"E:\\workspaces\\windows-apps\\ContextMaster\\.claude\\worktrees\\elastic-swirles\\src\\shared\\ipc-channels.ts","statementMap":{"0":{"start":{"line":2,"column":19},"end":{"line":33,"column":null}}},"fnMap":{},"branchMap":{},"s":{"0":1},"f":{},"b":{},"meta":{"lastBranch":0,"lastFunction":0,"lastStatement":1,"seen":{"s:2:19:33:Infinity":0}}} +} diff --git a/coverage/favicon.png b/coverage/favicon.png new file mode 100644 index 0000000..c1525b8 Binary files /dev/null and b/coverage/favicon.png differ diff --git a/coverage/index.html b/coverage/index.html new file mode 100644 index 0000000..faf90e3 --- /dev/null +++ b/coverage/index.html @@ -0,0 +1,176 @@ + + + + +
++ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| main/data/repositories | +
+
+ |
+ 100% | +20/20 | +100% | +8/8 | +100% | +12/12 | +100% | +20/20 | +
| main/ipc | +
+
+ |
+ 100% | +11/11 | +75% | +3/4 | +100% | +4/4 | +100% | +11/11 | +
| main/services | +
+
+ |
+ 40.75% | +97/238 | +21.27% | +20/94 | +55.26% | +21/38 | +41.31% | +88/213 | +
| main/utils | +
+
+ |
+ 80.64% | +25/31 | +100% | +8/8 | +66.66% | +6/9 | +78.57% | +22/28 | +
| shared | +
+
+ |
+ 100% | +23/23 | +100% | +0/0 | +100% | +4/4 | +100% | +23/23 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 | + + + + + + + + + + + + + +6x + + +1x + + + + +1x + + + + + + +1x + + + +2x + + +2x + + + +2x + + +2x + + + +1x + + + +3x + + + + + + + + + + | import type Database from 'better-sqlite3';
+import { BackupSnapshot } from '../../../shared/types';
+import { BackupType } from '../../../shared/enums';
+
+interface DbRow {
+ id: number;
+ name: string;
+ creation_time: string;
+ type: string;
+ menu_items_json: string;
+ sha256_checksum: string;
+}
+
+export class BackupSnapshotRepo {
+ constructor(private readonly db: Database.Database) {}
+
+ insert(snapshot: Omit<BackupSnapshot, 'id'>): BackupSnapshot {
+ const stmt = this.db.prepare(`
+ INSERT INTO backup_snapshots
+ (name, creation_time, type, menu_items_json, sha256_checksum)
+ VALUES (?, ?, ?, ?, ?)
+ `);
+ const result = stmt.run(
+ snapshot.name,
+ snapshot.creationTime,
+ snapshot.type,
+ snapshot.menuItemsJson,
+ snapshot.sha256Checksum
+ );
+ return { ...snapshot, id: result.lastInsertRowid as number };
+ }
+
+ findAll(): BackupSnapshot[] {
+ const rows = this.db
+ .prepare('SELECT * FROM backup_snapshots ORDER BY creation_time DESC')
+ .all() as DbRow[];
+ return rows.map(this.toModel);
+ }
+
+ findById(id: number): BackupSnapshot | null {
+ const row = this.db
+ .prepare('SELECT * FROM backup_snapshots WHERE id = ?')
+ .get(id) as DbRow | undefined;
+ return row ? this.toModel(row) : null;
+ }
+
+ delete(id: number): void {
+ this.db.prepare('DELETE FROM backup_snapshots WHERE id = ?').run(id);
+ }
+
+ private toModel(row: DbRow): BackupSnapshot {
+ return {
+ id: row.id,
+ name: row.name,
+ creationTime: row.creation_time,
+ type: row.type as BackupType,
+ menuItemsJson: row.menu_items_json,
+ sha256Checksum: row.sha256_checksum,
+ };
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 | + + + + + + + + + + + + + + +6x + + +2x + + + + +2x + + + + + + + +2x + + + +1x + + +1x + + + +2x + + +2x + + + +1x + + + +3x + + + + + + + + + + + | import type Database from 'better-sqlite3';
+import { OperationRecord } from '../../../shared/types';
+import { OperationType } from '../../../shared/enums';
+
+interface DbRow {
+ id: number;
+ timestamp: string;
+ operation_type: string;
+ target_name: string;
+ registry_path: string;
+ old_value: string | null;
+ new_value: string | null;
+}
+
+export class OperationRecordRepo {
+ constructor(private readonly db: Database.Database) {}
+
+ insert(record: Omit<OperationRecord, 'id'>): OperationRecord {
+ const stmt = this.db.prepare(`
+ INSERT INTO operation_records
+ (timestamp, operation_type, target_name, registry_path, old_value, new_value)
+ VALUES (?, ?, ?, ?, ?, ?)
+ `);
+ const result = stmt.run(
+ record.timestamp,
+ record.operationType,
+ record.targetEntryName,
+ record.registryPath,
+ record.oldValue ?? null,
+ record.newValue ?? null
+ );
+ return { ...record, id: result.lastInsertRowid as number };
+ }
+
+ findAll(): OperationRecord[] {
+ const rows = this.db
+ .prepare('SELECT * FROM operation_records ORDER BY timestamp DESC')
+ .all() as DbRow[];
+ return rows.map(this.toModel);
+ }
+
+ findById(id: number): OperationRecord | null {
+ const row = this.db
+ .prepare('SELECT * FROM operation_records WHERE id = ?')
+ .get(id) as DbRow | undefined;
+ return row ? this.toModel(row) : null;
+ }
+
+ deleteAll(): void {
+ this.db.prepare('DELETE FROM operation_records').run();
+ }
+
+ private toModel(row: DbRow): OperationRecord {
+ return {
+ id: row.id,
+ timestamp: row.timestamp,
+ operationType: row.operation_type as OperationType,
+ targetEntryName: row.target_name,
+ registryPath: row.registry_path,
+ oldValue: row.old_value,
+ newValue: row.new_value,
+ };
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| BackupSnapshotRepo.ts | +
+
+ |
+ 100% | +10/10 | +100% | +2/2 | +100% | +6/6 | +100% | +10/10 | +
| OperationRecordRepo.ts | +
+
+ |
+ 100% | +10/10 | +100% | +6/6 | +100% | +6/6 | +100% | +10/10 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| registry.ts | +
+
+ |
+ 100% | +11/11 | +75% | +3/4 | +100% | +4/4 | +100% | +11/11 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 | + + + + + + + +7x + + +1x + + + +7x + + +1x + + + + + + + + + + +1x +1x + + + +7x + + +2x +1x + +1x + +2x + + + + | import { ipcMain } from 'electron';
+import { IPC } from '../../shared/ipc-channels';
+import { MenuScene, MenuItemType } from '../../shared/enums';
+import { ToggleItemParams, BatchToggleParams } from '../../shared/types';
+import { MenuManagerService } from '../services/MenuManagerService';
+import { wrapHandler } from '../utils/ipcWrapper';
+
+export function registerRegistryHandlers(menuManager: MenuManagerService): void {
+ ipcMain.handle(
+ IPC.REGISTRY_GET_ITEMS,
+ wrapHandler((_event: unknown, scene: MenuScene) =>
+ menuManager.getMenuItems(scene)
+ )
+ );
+
+ ipcMain.handle(
+ IPC.REGISTRY_TOGGLE,
+ wrapHandler(async (_event: unknown, params: ToggleItemParams) => {
+ const item = {
+ id: -1,
+ name: params.name,
+ command: '',
+ iconPath: null,
+ isEnabled: params.isEnabled,
+ source: '',
+ menuScene: params.menuScene,
+ registryKey: params.registryKey,
+ type: params.type ?? MenuItemType.System,
+ };
+ const result = await menuManager.toggleItem(item);
+ return { newState: !params.isEnabled, newRegistryKey: result.newRegistryKey };
+ })
+ );
+
+ ipcMain.handle(
+ IPC.REGISTRY_BATCH,
+ wrapHandler(async (_event: unknown, params: BatchToggleParams) => {
+ if (params.enable) {
+ await menuManager.batchEnable(params.items);
+ } else {
+ await menuManager.batchDisable(params.items);
+ }
+ return true;
+ })
+ );
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 | + + + + + + + + + + + + + + + + + + +4x +4x +4x + + + +2x +2x +12x +12x + + +2x +2x + +2x + + + + + + + +2x +2x +2x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + + + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import { createHash } from 'crypto';
+import { promises as fs } from 'fs';
+import { dialog, BrowserWindow } from 'electron';
+import { BackupType, MenuScene, OperationType } from '../../shared/enums';
+import { BackupSnapshot, MenuItemEntry, RestoreDiffItem } from '../../shared/types';
+import { BackupSnapshotRepo } from '../data/repositories/BackupSnapshotRepo';
+import { MenuManagerService } from './MenuManagerService';
+import { OperationHistoryService } from './OperationHistoryService';
+import log from '../utils/logger';
+
+/** 去掉 ShellExt registryKey 末段的 '-' 前缀,用于跨状态匹配 */
+function normalizeKey(key: string): string {
+ const sep = key.lastIndexOf('\\');
+ if (sep === -1) return key.replace(/^-+/, '');
+ return key.substring(0, sep + 1) + key.substring(sep + 1).replace(/^-+/, '');
+}
+
+export class BackupService {
+ constructor(
+ private readonly repo: BackupSnapshotRepo,
+ private readonly menuManager: MenuManagerService,
+ private readonly history: OperationHistoryService
+ ) {}
+
+ async createBackup(name: string, type = BackupType.Manual): Promise<BackupSnapshot> {
+ const allItems: MenuItemEntry[] = [];
+ for (const scene of Object.values(MenuScene)) {
+ const items = await this.menuManager.getMenuItems(scene);
+ allItems.push(...items);
+ }
+
+ const jsonData = JSON.stringify(allItems);
+ const checksum = createHash('sha256').update(jsonData).digest('hex');
+
+ const snapshot = this.repo.insert({
+ name,
+ creationTime: new Date().toISOString(),
+ type,
+ menuItemsJson: jsonData,
+ sha256Checksum: checksum,
+ });
+
+ this.history.recordOperation(OperationType.Backup, name, '', '', checksum);
+ log.info(`Backup created: ${name} (${allItems.length} items)`);
+ return snapshot;
+ }
+
+ async restoreBackup(snapshotId: number): Promise<void> {
+ const snapshot = this.repo.findById(snapshotId);
+ if (!snapshot) throw new Error('找不到备份快照');
+
+ const expectedChecksum = createHash('sha256')
+ .update(snapshot.menuItemsJson)
+ .digest('hex');
+ if (expectedChecksum !== snapshot.sha256Checksum) {
+ throw new Error('备份校验失败,文件可能已被篡改');
+ }
+
+ // 还原前先自动创建备份
+ await this.createBackup(
+ `AutoBackup_BeforeRestore_${new Date().toISOString().replace(/[:.]/g, '-')}`,
+ BackupType.Auto
+ );
+
+ const itemsToRestore: MenuItemEntry[] = JSON.parse(snapshot.menuItemsJson);
+ if (!itemsToRestore.length) throw new Error('备份文件中没有有效的菜单项数据');
+
+ const allCurrentItems: MenuItemEntry[] = [];
+ for (const scene of Object.values(MenuScene)) {
+ allCurrentItems.push(...(await this.menuManager.getMenuItems(scene)));
+ }
+
+ const toEnable: MenuItemEntry[] = [];
+ const toDisable: MenuItemEntry[] = [];
+
+ for (const backupItem of itemsToRestore) {
+ const current = allCurrentItems.find((i) => normalizeKey(i.registryKey) === normalizeKey(backupItem.registryKey));
+ if (current && current.isEnabled !== backupItem.isEnabled) {
+ current.isEnabled = backupItem.isEnabled;
+ if (backupItem.isEnabled) toEnable.push(current);
+ else toDisable.push(current);
+ }
+ }
+
+ if (toEnable.length) await this.menuManager.batchEnable(toEnable);
+ if (toDisable.length) await this.menuManager.batchDisable(toDisable);
+
+ this.history.recordOperation(OperationType.Restore, snapshot.name, '', '', snapshotId.toString());
+ log.info(`Restore completed from backup: ${snapshot.name}`);
+ }
+
+ async deleteBackup(id: number): Promise<void> {
+ this.repo.delete(id);
+ }
+
+ getAllBackups(): BackupSnapshot[] {
+ return this.repo.findAll();
+ }
+
+ async previewRestoreDiff(snapshotId: number): Promise<RestoreDiffItem[]> {
+ const snapshot = this.repo.findById(snapshotId);
+ if (!snapshot) throw new Error('找不到备份快照');
+
+ const backupItems: MenuItemEntry[] = JSON.parse(snapshot.menuItemsJson);
+ const currentItems: MenuItemEntry[] = [];
+ for (const scene of Object.values(MenuScene)) {
+ currentItems.push(...(await this.menuManager.getMenuItems(scene)));
+ }
+
+ const diff: RestoreDiffItem[] = [];
+ for (const backupItem of backupItems) {
+ const current = currentItems.find((i) => normalizeKey(i.registryKey) === normalizeKey(backupItem.registryKey));
+ if (current && current.isEnabled !== backupItem.isEnabled) {
+ diff.push({ current, backup: backupItem });
+ }
+ }
+ return diff;
+ }
+
+ async exportBackup(snapshotId: number, win: BrowserWindow): Promise<void> {
+ const snapshot = this.repo.findById(snapshotId);
+ if (!snapshot) throw new Error('找不到备份快照');
+
+ const { filePath, canceled } = await dialog.showSaveDialog(win, {
+ title: '导出备份文件',
+ defaultPath: `${snapshot.name.replace(/[\\/:*?"<>|]/g, '_')}.cmbackup`,
+ filters: [{ name: 'ContextMaster Backup', extensions: ['cmbackup'] }],
+ });
+
+ if (canceled || !filePath) return;
+ await fs.writeFile(filePath, snapshot.menuItemsJson, 'utf-8');
+ log.info(`Backup exported to: ${filePath}`);
+ }
+
+ async importBackup(win: BrowserWindow): Promise<BackupSnapshot> {
+ const { filePaths, canceled } = await dialog.showOpenDialog(win, {
+ title: '导入备份文件',
+ filters: [
+ { name: 'ContextMaster Backup', extensions: ['cmbackup'] },
+ { name: 'JSON Files', extensions: ['json'] },
+ ],
+ properties: ['openFile'],
+ });
+
+ if (canceled || !filePaths.length) throw new Error('未选择文件');
+
+ const filePath = filePaths[0];
+ const jsonData = await fs.readFile(filePath, 'utf-8');
+ const checksum = createHash('sha256').update(jsonData).digest('hex');
+
+ const snapshot = this.repo.insert({
+ name: `导入 · ${require('path').basename(filePath, '.cmbackup')}`,
+ creationTime: new Date().toISOString(),
+ type: BackupType.Manual,
+ menuItemsJson: jsonData,
+ sha256Checksum: checksum,
+ });
+
+ log.info(`Backup imported from: ${filePath}`);
+ return snapshot;
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 | + + + + + + + +13x +13x + + + +1x + + + +6x +5x +4x +4x +4x + + + + + + +4x +4x + + + +6x +5x +4x +4x +4x + + + + + + +4x +4x + + + +2x +1x + +1x + + + + +4x +3x + +2x +2x +2x +3x + +1x + +1x +1x + + + + +4x +3x + +2x +2x +2x +3x + +1x + +1x +1x + + + + | import { MenuScene, OperationType } from '../../shared/enums';
+import { MenuItemEntry } from '../../shared/types';
+import { RegistryService } from './RegistryService';
+import { OperationHistoryService } from './OperationHistoryService';
+import log from '../utils/logger';
+
+export class MenuManagerService {
+ constructor(
+ private readonly registry: RegistryService,
+ private readonly history: OperationHistoryService
+ ) {}
+
+ async getMenuItems(scene: MenuScene): Promise<MenuItemEntry[]> {
+ return this.registry.getMenuItems(scene);
+ }
+
+ async enableItem(item: MenuItemEntry): Promise<{ newRegistryKey?: string }> {
+ if (item.isEnabled) return {};
+ const result = await this.registry.setItemEnabled(item.registryKey, true);
+ Iif (result.newRegistryKey) item.registryKey = result.newRegistryKey;
+ item.isEnabled = true;
+ this.history.recordOperation(
+ OperationType.Enable,
+ item.name,
+ item.registryKey,
+ 'false',
+ 'true'
+ );
+ log.info(`Enabled: ${item.name}`);
+ return result;
+ }
+
+ async disableItem(item: MenuItemEntry): Promise<{ newRegistryKey?: string }> {
+ if (!item.isEnabled) return {};
+ const result = await this.registry.setItemEnabled(item.registryKey, false);
+ Iif (result.newRegistryKey) item.registryKey = result.newRegistryKey;
+ item.isEnabled = false;
+ this.history.recordOperation(
+ OperationType.Disable,
+ item.name,
+ item.registryKey,
+ 'true',
+ 'false'
+ );
+ log.info(`Disabled: ${item.name}`);
+ return result;
+ }
+
+ async toggleItem(item: MenuItemEntry): Promise<{ newRegistryKey?: string }> {
+ if (item.isEnabled) {
+ return this.disableItem(item);
+ } else {
+ return this.enableItem(item);
+ }
+ }
+
+ async batchEnable(items: MenuItemEntry[]): Promise<void> {
+ const targets = items.filter((i) => !i.isEnabled);
+ if (!targets.length) return;
+
+ this.registry.createRollbackPoint(targets);
+ try {
+ for (const item of targets) {
+ await this.enableItem(item);
+ }
+ this.registry.commitTransaction();
+ } catch (e) {
+ await this.registry.rollback();
+ throw new Error(`批量启用失败,已回滚: ${(e as Error).message}`);
+ }
+ }
+
+ async batchDisable(items: MenuItemEntry[]): Promise<void> {
+ const targets = items.filter((i) => i.isEnabled);
+ if (!targets.length) return;
+
+ this.registry.createRollbackPoint(targets);
+ try {
+ for (const item of targets) {
+ await this.disableItem(item);
+ }
+ this.registry.commitTransaction();
+ } catch (e) {
+ await this.registry.rollback();
+ throw new Error(`批量禁用失败,已回滚: ${(e as Error).message}`);
+ }
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 +183 +184 +185 +186 +187 +188 +189 +190 +191 +192 +193 +194 +195 +196 +197 +198 +199 +200 +201 +202 +203 +204 +205 +206 +207 +208 +209 +210 +211 +212 +213 +214 +215 +216 +217 +218 +219 +220 +221 +222 +223 +224 +225 +226 +227 +228 +229 +230 +231 +232 +233 +234 +235 +236 +237 +238 +239 +240 +241 +242 +243 +244 +245 +246 +247 +248 +249 +250 +251 +252 +253 +254 +255 +256 +257 +258 +259 +260 +261 +262 +263 +264 +265 +266 +267 +268 +269 +270 +271 +272 +273 +274 +275 +276 +277 +278 +279 +280 +281 +282 +283 +284 +285 +286 +287 +288 +289 +290 +291 +292 +293 +294 +295 +296 +297 +298 +299 +300 +301 +302 +303 +304 +305 +306 +307 +308 +309 +310 +311 +312 +313 +314 +315 +316 +317 +318 +319 +320 +321 +322 +323 +324 +325 +326 +327 +328 +329 +330 +331 +332 +333 +334 +335 +336 +337 +338 +339 +340 +341 +342 +343 +344 +345 +346 +347 +348 +349 +350 +351 +352 +353 +354 +355 +356 +357 +358 +359 +360 +361 +362 +363 +364 +365 +366 +367 +368 +369 +370 +371 +372 +373 +374 | + + + + + + + + +1x + +1x +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2x +2x +1x + + + + + + + + + + + + +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + | import { execFile } from 'child_process';
+import { promisify } from 'util';
+import * as os from 'os';
+import * as fs from 'fs';
+import * as path from 'path';
+import * as crypto from 'crypto';
+import { isAdmin } from '../utils/AdminHelper';
+import log from '../utils/logger';
+
+const execFileAsync = promisify(execFile);
+
+const PWSH7_PATH = 'C:\\Program Files\\PowerShell\\7\\pwsh.exe';
+const PS_EXE = fs.existsSync(PWSH7_PATH) ? PWSH7_PATH : 'powershell.exe';
+
+export class PowerShellBridge {
+ /**
+ * 执行 PowerShell 脚本并将 stdout 解析为 JSON
+ */
+ async execute<T>(script: string): Promise<T> {
+ log.debug('[PS] execute:', script.substring(0, 200));
+ const { stdout, stderr } = await execFileAsync(
+ PS_EXE,
+ ['-NonInteractive', '-NoProfile', '-OutputFormat', 'Text', '-Command', script],
+ { maxBuffer: 10 * 1024 * 1024, timeout: 30000 }
+ );
+
+ if (stderr) {
+ log.warn('[PS] stderr:', stderr);
+ }
+
+ const trimmed = stdout.trim();
+ if (!trimmed) return [] as unknown as T;
+
+ try {
+ return JSON.parse(trimmed) as T;
+ } catch (e) {
+ log.error('[PS] JSON parse error. stdout:', trimmed.substring(0, 500));
+ throw new Error(`PowerShell 输出 JSON 解析失败: ${String(e)}`);
+ }
+ }
+
+ /**
+ * 以提权方式执行脚本(非管理员时弹出 UAC 对话框)
+ * 管理员身份下直接 fallback 到 execute()
+ */
+ async executeElevated<T>(script: string): Promise<T> {
+ if (isAdmin()) {
+ return this.execute<T>(script);
+ }
+
+ const uid = crypto.randomUUID();
+ const opScript = path.join(os.tmpdir(), `cm_op_${uid}.ps1`);
+ const resultFile = path.join(os.tmpdir(), `cm_res_${uid}.json`);
+
+ // 包装原始脚本:捕获原始 JSON 输出(脚本本身已输出 JSON),写入 resultFile
+ const resultFilePs = resultFile.replace(/'/g, "''");
+ const wrappedScript2 = `
+$ErrorActionPreference = 'Stop'
+try {
+ $__out = & {
+${script}
+ } | Out-String
+ $__out = $__out.Trim()
+ if (-not $__out) { $__out = 'null' }
+ Set-Content -LiteralPath '${resultFilePs}' -Value $__out -Encoding UTF8
+} catch {
+ $__err = (@{ __error = $_.Exception.Message } | ConvertTo-Json -Compress)
+ Set-Content -LiteralPath '${resultFilePs}' -Value $__err -Encoding UTF8
+ exit 1
+}
+`.trim();
+
+ fs.writeFileSync(opScript, wrappedScript2, 'utf8');
+
+ // 通过非提权 PS 启动提权子进程(弹 UAC)
+ const launchScript = `Start-Process '${PS_EXE.replace(/'/g, "''")}' -Verb RunAs -Wait -WindowStyle Hidden -ArgumentList @('-NonInteractive','-NoProfile','-ExecutionPolicy','Bypass','-File','${opScript.replace(/'/g, "''")}')`;
+
+ log.debug('[PS] executeElevated: launching UAC process');
+ try {
+ await execFileAsync(
+ PS_EXE,
+ ['-NonInteractive', '-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', launchScript],
+ { maxBuffer: 1 * 1024 * 1024, timeout: 120000 }
+ );
+ } finally {
+ try { fs.unlinkSync(opScript); } catch { /* ignore */ }
+ }
+
+ if (!fs.existsSync(resultFile)) {
+ throw new Error('操作已取消(UAC 提权被拒绝)');
+ }
+
+ let resultJson: string;
+ try {
+ resultJson = fs.readFileSync(resultFile, 'utf8').trim();
+ fs.unlinkSync(resultFile);
+ } catch {
+ throw new Error('读取操作结果失败');
+ }
+
+ let parsed: unknown;
+ try {
+ parsed = JSON.parse(resultJson);
+ } catch {
+ throw new Error(`结果 JSON 解析失败: ${resultJson.substring(0, 200)}`);
+ }
+
+ if (parsed && typeof parsed === 'object' && '__error' in parsed) {
+ throw new Error(String((parsed as Record<string, unknown>).__error));
+ }
+
+ return parsed as T;
+ }
+
+ /**
+ * 构建扫描指定注册表路径下所有子键的脚本
+ * 返回 JSON 数组,每项含菜单条目信息
+ */
+ buildGetItemsScript(hkcrSubPath: string): string {
+ // PS 单对象会返回哈希表而非数组,用 @(...) 强制数组
+ return `
+$ErrorActionPreference = 'SilentlyContinue'
+New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ErrorAction SilentlyContinue | Out-Null
+$basePath = 'HKCR:\\${hkcrSubPath}'
+if (-not (Test-Path -LiteralPath $basePath)) { Write-Output '[]'; exit }
+$subKeys = Get-ChildItem -LiteralPath $basePath | Where-Object { $_.PSIsContainer }
+$result = @($subKeys | ForEach-Object {
+ $key = $_
+ $keyName = $key.PSChildName
+ $name = $key.GetValue('')
+ if (-not $name) { $name = $keyName }
+ $iconPath = $key.GetValue('Icon')
+ $isEnabled = ($key.GetValue('LegacyDisable') -eq $null)
+ $commandSubKey = Join-Path $key.PSPath 'command'
+ $command = ''
+ if (Test-Path -LiteralPath $commandSubKey) {
+ $command = (Get-Item -LiteralPath $commandSubKey).GetValue('')
+ if (-not $command) { $command = '' }
+ }
+ $regKey = '${hkcrSubPath}\\' + $keyName
+ [PSCustomObject]@{
+ name = [string]$name
+ command = [string]$command
+ iconPath = if ($iconPath) { [string]$iconPath } else { $null }
+ isEnabled = [bool]$isEnabled
+ source = ''
+ registryKey = [string]$regKey
+ subKeyName = [string]$keyName
+ }
+})
+$result | ConvertTo-Json -Compress -Depth 3
+`.trim();
+ }
+
+ /**
+ * 构建启用/禁用单个菜单项的脚本
+ * enable=true → Remove-ItemProperty LegacyDisable
+ * enable=false → Set-ItemProperty LegacyDisable -Value ''
+ */
+ buildSetEnabledScript(hkcrRelativeKey: string, enable: boolean): string {
+ const psPath = `HKCR:\\${hkcrRelativeKey}`;
+ if (enable) {
+ return `
+$ErrorActionPreference = 'Stop'
+New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ErrorAction SilentlyContinue | Out-Null
+$keyPath = '${psPath}'
+if (Test-Path -LiteralPath $keyPath) {
+ $prop = Get-ItemProperty -LiteralPath $keyPath -Name 'LegacyDisable' -ErrorAction SilentlyContinue
+ if ($prop -ne $null) {
+ Remove-ItemProperty -LiteralPath $keyPath -Name 'LegacyDisable' -Force
+ }
+}
+Write-Output '{"ok":true}'
+`.trim();
+ } else {
+ return `
+$ErrorActionPreference = 'Stop'
+New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ErrorAction SilentlyContinue | Out-Null
+$keyPath = '${psPath}'
+if (-not (Test-Path -LiteralPath $keyPath)) {
+ throw "注册表项不存在: ${hkcrRelativeKey}"
+}
+Set-ItemProperty -LiteralPath $keyPath -Name 'LegacyDisable' -Value '' -Type String -Force
+Write-Output '{"ok":true}'
+`.trim();
+ }
+ }
+
+ /**
+ * 构建枚举 shellex\ContextMenuHandlers 下所有 Shell 扩展的脚本
+ * 使用四级级联策略解析本地化名称:
+ * 1. LocalizedString/FriendlyTypeName → SHLoadIndirectString(解析 @DLL,-ID 格式)
+ * 2. InprocServer32 DLL 字符串表 → 通用字符串质量筛选(LoadLibraryEx + LoadString)
+ * 3. CLSID 默认值
+ * 4. 处理程序键名(最终兜底)
+ * CmHelper.dll 编译后缓存至 %LOCALAPPDATA%\ContextMaster\,避免重复编译开销
+ */
+ buildGetShellExtItemsScript(shellexSubPath: string): string {
+ return `
+$ErrorActionPreference = 'SilentlyContinue'
+New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ErrorAction SilentlyContinue | Out-Null
+$cmDir = Join-Path $env:LOCALAPPDATA 'ContextMaster'
+$cmDll = Join-Path $cmDir 'CmHelper.dll'
+$helperLoaded = $false
+if (Test-Path $cmDll) {
+ try { Add-Type -Path $cmDll -ErrorAction Stop; $helperLoaded = $true } catch {}
+}
+if (-not $helperLoaded) {
+ $src = @'
+using System;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Collections.Generic;
+public class CmHelper {
+ const uint LOAD_AS_DATA = 2u;
+ [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
+ static extern int SHLoadIndirectString(string s, StringBuilder buf, int cap, IntPtr r);
+ [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
+ static extern IntPtr LoadLibraryEx(string p, IntPtr h, uint f);
+ [DllImport("kernel32.dll")]
+ static extern bool FreeLibrary(IntPtr h);
+ [DllImport("user32.dll", CharSet = CharSet.Unicode)]
+ static extern int LoadString(IntPtr h, uint id, StringBuilder buf, int cap);
+ public static string ResolveIndirect(string s) {
+ if (string.IsNullOrEmpty(s) || !s.StartsWith("@")) return null;
+ var sb = new StringBuilder(512);
+ return SHLoadIndirectString(s, sb, 512, IntPtr.Zero) == 0 ? sb.ToString() : null;
+ }
+ public static string[] ReadDllStrings(string dll, uint from, uint to) {
+ var list = new List<string>();
+ var hMod = LoadLibraryEx(dll, IntPtr.Zero, LOAD_AS_DATA);
+ if (hMod == IntPtr.Zero) return list.ToArray();
+ try {
+ for (uint i = from; i <= to; i++) {
+ var sb = new StringBuilder(512);
+ if (LoadString(hMod, i, sb, 512) > 0) list.Add(sb.ToString());
+ }
+ } finally { FreeLibrary(hMod); }
+ return list.ToArray();
+ }
+}
+'@
+ if (-not (Test-Path $cmDir)) { New-Item -Path $cmDir -ItemType Directory -Force | Out-Null }
+ if (Test-Path $cmDll) { Remove-Item -Path $cmDll -Force -ErrorAction SilentlyContinue }
+ try {
+ Add-Type -TypeDefinition $src -OutputAssembly $cmDll -ErrorAction Stop
+ $helperLoaded = $true
+ } catch {
+ try { Add-Type -TypeDefinition $src -ErrorAction Stop; $helperLoaded = $true } catch {}
+ }
+}
+function Resolve-ExtName($clsid, $fallback) {
+ if ($clsid -match '^\\{[0-9A-Fa-f-]+\\}$') {
+ $clsidPath = 'HKCR:\\CLSID\\' + $clsid
+ if (Test-Path -LiteralPath $clsidPath) {
+ $clsidKey = Get-Item -LiteralPath $clsidPath
+ foreach ($valName in @('LocalizedString', 'FriendlyTypeName')) {
+ $raw = $clsidKey.GetValue($valName)
+ if ($raw) {
+ if ($raw.StartsWith('@')) {
+ try {
+ $resolved = [CmHelper]::ResolveIndirect($raw)
+ if ($resolved -and $resolved.Length -ge 2) { return $resolved }
+ } catch {}
+ } elseif ($raw.Length -ge 2) {
+ return $raw
+ }
+ }
+ }
+ $inprocPath = Join-Path $clsidPath 'InprocServer32'
+ if (Test-Path -LiteralPath $inprocPath) {
+ $dllPath = (Get-Item -LiteralPath $inprocPath).GetValue('')
+ # Fix 1: 展开 %SystemRoot% 等环境变量,否则 Test-Path 永远返回 $false
+ if ($dllPath) {
+ $dllPath = [System.Environment]::ExpandEnvironmentVariables($dllPath)
+ }
+ if ($dllPath -and (Test-Path -LiteralPath $dllPath)) {
+ # Level 2: 通用字符串质量筛选(过滤后取第一条,无硬编码词表,无长度限制)
+ try {
+ $candidates = [CmHelper]::ReadDllStrings($dllPath, 1, 1000) |
+ Where-Object {
+ $_.Length -ge 2 -and
+ $_ -notmatch '[\\\\/:*?<>|]' -and
+ $_ -notmatch '^\\{' -and
+ $_ -notmatch '^https?://' -and
+ $_ -notmatch '%[0-9A-Za-z]' -and
+ $_ -notmatch '\\{[0-9]+\\}' -and
+ $_ -notmatch '[\\r\\n\\t]' -and
+ $_ -notmatch '^[0-9]' -and
+ $_ -notmatch '[\\u3002\\uff01\\uff1f]' -and
+ $_ -notmatch '[.!?]$'
+ }
+ $pool = $candidates | Where-Object { $_ -match '[^\\x00-\\x7F]' }
+ if (-not $pool) { $pool = $candidates }
+ $best = $pool | Select-Object -First 1
+ if ($best) { return $best }
+ } catch {}
+ # Level 2.5: DLL VersionInfo(适用于英文/日文等非中文软件)
+ try {
+ $ver = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($dllPath)
+ $desc = $null
+ if ($ver.FileDescription -and $ver.FileDescription.Length -ge 2) {
+ $desc = $ver.FileDescription
+ } elseif ($ver.ProductName -and $ver.ProductName.Length -ge 2) {
+ $desc = $ver.ProductName
+ }
+ if ($desc -and $desc.Length -le 80 -and $desc -notmatch '^\\{' -and $desc -notmatch '[\\\\/:*?<>|]') {
+ return $desc
+ }
+ } catch {}
+ }
+ }
+ $def = $clsidKey.GetValue('')
+ if ($def) { return [string]$def }
+ }
+ }
+ return $fallback
+}
+$shellexPath = 'HKCR:\\${shellexSubPath}'
+if (-not (Test-Path -LiteralPath $shellexPath)) { Write-Output '[]'; exit }
+$handlers = Get-ChildItem -LiteralPath $shellexPath | Where-Object { $_.PSIsContainer }
+$result = @($handlers | ForEach-Object {
+ $handlerKeyName = $_.PSChildName
+ $clsid = $_.GetValue('')
+ if (-not $clsid) { $clsid = $handlerKeyName }
+ $cleanName = $handlerKeyName -replace '^-+', ''
+ $displayName = Resolve-ExtName $clsid $cleanName
+ $isEnabled = -not $handlerKeyName.StartsWith('-')
+ $regKey = '${shellexSubPath}\\' + $cleanName
+ [PSCustomObject]@{
+ name = [string]$displayName
+ command = [string]$clsid
+ iconPath = $null
+ isEnabled = [bool]$isEnabled
+ source = [string]$handlerKeyName
+ registryKey = [string]$regKey
+ subKeyName = [string]$handlerKeyName
+ itemType = 'ShellExt'
+ }
+})
+$result | ConvertTo-Json -Compress -Depth 3
+`.trim();
+ }
+
+ /**
+ * 构建启用/禁用 Shell 扩展的脚本(通过重命名键名添加/去除 '-' 前缀)
+ * enable=true → 将 '-Name' 重命名为 'Name'
+ * enable=false → 将 'Name' 重命名为 '-Name'
+ */
+ buildShellExtToggleScript(hkcrRelativeKey: string, enable: boolean): string {
+ const lastSlash = hkcrRelativeKey.lastIndexOf('\\');
+ const parentRelPath = hkcrRelativeKey.substring(0, lastSlash);
+ const keyName = hkcrRelativeKey.substring(lastSlash + 1);
+ const cleanName = keyName.replace(/^-+/, '');
+ const psParentPath = `HKCR:\\${parentRelPath}`;
+ // enable: 找 -cleanName 改为 cleanName;disable: 找 cleanName 改为 -cleanName
+ const psCurrentKeyName = enable ? `-${cleanName}` : cleanName;
+ const psNewKeyName = enable ? cleanName : `-${cleanName}`;
+ return `
+$ErrorActionPreference = 'Stop'
+New-PSDrive -Name HKCR -PSProvider Registry -Root HKEY_CLASSES_ROOT -ErrorAction SilentlyContinue | Out-Null
+$parentPath = '${psParentPath}'
+$currentKey = '${psCurrentKeyName}'
+$newKey = '${psNewKeyName}'
+$fullPath = Join-Path $parentPath $currentKey
+if (-not (Test-Path -LiteralPath $fullPath)) {
+ throw "ShellExt key not found: $fullPath"
+}
+Rename-Item -LiteralPath $fullPath -NewName $newKey -Force
+Write-Output '{"ok":true}'
+`.trim();
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 +55 +56 +57 +58 +59 +60 +61 +62 +63 +64 +65 +66 +67 +68 +69 +70 +71 +72 +73 +74 +75 +76 +77 +78 +79 +80 +81 +82 +83 +84 +85 +86 +87 +88 +89 +90 +91 +92 +93 +94 +95 +96 +97 +98 +99 +100 +101 +102 +103 +104 +105 +106 +107 +108 +109 +110 +111 +112 +113 +114 +115 +116 +117 +118 +119 +120 +121 +122 +123 +124 +125 +126 +127 +128 +129 +130 +131 +132 +133 +134 +135 +136 +137 +138 +139 +140 +141 +142 +143 +144 +145 +146 +147 +148 +149 +150 +151 +152 +153 +154 +155 +156 +157 +158 +159 +160 +161 +162 +163 +164 +165 +166 +167 +168 +169 +170 +171 +172 +173 +174 +175 +176 +177 +178 +179 +180 +181 +182 | + + + + + +1x + + + + + + + + + +1x + + + + + + + + + +1x + + + + + + + + + + + + + + + +4x +4x +4x + + +4x + + + + + + +2x +2x +2x + +2x +2x +2x + + +2x +2x +2x +2x +2x + + + + +2x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +2x +2x +3x + +2x + + + + + + + + + + + + + + + + + + + + + + +1x +1x + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +1x +1x + + + | import { MenuScene, MenuItemType } from '../../shared/enums';
+import { MenuItemEntry } from '../../shared/types';
+import { PowerShellBridge } from './PowerShellBridge';
+import log from '../utils/logger';
+
+// 与 C# RegistryService._sceneRegistryPaths 完全一致
+const SCENE_REGISTRY_PATHS: Record<MenuScene, string> = {
+ [MenuScene.Desktop]: 'DesktopBackground\\Shell',
+ [MenuScene.File]: '*\\shell',
+ [MenuScene.Folder]: 'Directory\\shell',
+ [MenuScene.Drive]: 'Drive\\shell',
+ [MenuScene.DirectoryBackground]:'Directory\\Background\\shell',
+ [MenuScene.RecycleBin]: 'CLSID\\{645FF040-5081-101B-9F08-00AA002F954E}\\shell',
+};
+
+// Shell 扩展(COM)注册路径:shellex\ContextMenuHandlers
+const SCENE_SHELLEX_PATHS: Record<MenuScene, string> = {
+ [MenuScene.Desktop]: 'DesktopBackground\\shellex\\ContextMenuHandlers',
+ [MenuScene.File]: '*\\shellex\\ContextMenuHandlers',
+ [MenuScene.Folder]: 'Directory\\shellex\\ContextMenuHandlers',
+ [MenuScene.Drive]: 'Drive\\shellex\\ContextMenuHandlers',
+ [MenuScene.DirectoryBackground]:'Directory\\Background\\shellex\\ContextMenuHandlers',
+ [MenuScene.RecycleBin]: 'CLSID\\{645FF040-5081-101B-9F08-00AA002F954E}\\shellex\\ContextMenuHandlers',
+};
+
+// 完整 HKCR 前缀(用于显示)
+const HKCR_PREFIX = 'HKEY_CLASSES_ROOT';
+
+interface PsMenuItemRaw {
+ name: string;
+ command: string;
+ iconPath: string | null;
+ isEnabled: boolean;
+ source: string;
+ registryKey: string;
+ subKeyName: string;
+ itemType?: string; // 'ShellExt' for shell extensions
+}
+
+export class RegistryService {
+ private readonly ps: PowerShellBridge;
+ /** 事务回滚数据:registryKey → 原始 isEnabled */
+ private rollbackData = new Map<string, boolean>();
+ private inTransaction = false;
+ private nextId = 1;
+
+ constructor(ps: PowerShellBridge) {
+ this.ps = ps;
+ }
+
+ /**
+ * 获取指定场景下的所有菜单条目(Classic Shell + Shell 扩展)
+ */
+ async getMenuItems(scene: MenuScene): Promise<MenuItemEntry[]> {
+ const basePath = SCENE_REGISTRY_PATHS[scene];
+ const shellexPath = SCENE_SHELLEX_PATHS[scene];
+ try {
+ // 读取 Classic Shell 命令
+ const script = this.ps.buildGetItemsScript(basePath);
+ const raw = await this.ps.execute<PsMenuItemRaw[]>(script);
+ const items = Array.isArray(raw) ? raw : (raw ? [raw] : []);
+
+ // 读取 Shell 扩展(COM ContextMenuHandlers),失败不阻断主流程
+ let shellexItems: PsMenuItemRaw[] = [];
+ try {
+ const shellexScript = this.ps.buildGetShellExtItemsScript(shellexPath);
+ const shellexRaw = await this.ps.execute<PsMenuItemRaw[]>(shellexScript);
+ shellexItems = Array.isArray(shellexRaw) ? shellexRaw : (shellexRaw ? [shellexRaw] : []);
+ } catch (e) {
+ log.warn(`getMenuItems shellex(${scene}) failed (non-fatal):`, e);
+ }
+
+ return [...items, ...shellexItems].map((r) => ({
+ id: this.nextId++,
+ name: r.name,
+ command: r.command,
+ iconPath: r.iconPath,
+ isEnabled: r.isEnabled,
+ source: r.source || this.inferSource(r.subKeyName),
+ menuScene: scene,
+ registryKey: r.registryKey,
+ type: this.determineType(r.itemType),
+ }));
+ } catch (e) {
+ log.error(`getMenuItems(${scene}) failed:`, e);
+ throw new Error(`读取注册表场景 ${scene} 失败: ${(e as Error).message}`);
+ }
+ }
+
+ /**
+ * 启用或禁用单个菜单条目
+ * ShellExt 通过重命名键(±前缀)实现;Classic Shell 通过 LegacyDisable 值实现
+ * ShellExt 通过重命名键(±前缀)实现,registryKey 已归一化,身份不变
+ */
+ async setItemEnabled(registryKey: string, enabled: boolean): Promise<{ newRegistryKey?: string }> {
+ try {
+ if (this.isShellExtKey(registryKey)) {
+ const script = this.ps.buildShellExtToggleScript(registryKey, enabled);
+ await this.ps.executeElevated<{ ok: boolean }>(script);
+ return {};
+ } else {
+ const script = this.ps.buildSetEnabledScript(registryKey, enabled);
+ await this.ps.executeElevated<{ ok: boolean }>(script);
+ return {};
+ }
+ } catch (e) {
+ if (this.inTransaction) {
+ await this.rollback();
+ }
+ throw new Error(
+ `修改菜单项状态失败 [${registryKey}]: ${(e as Error).message}`
+ );
+ }
+ }
+
+ /**
+ * 创建回滚点(事务开始前调用)
+ */
+ createRollbackPoint(items: Array<{ registryKey: string; isEnabled: boolean }>): void {
+ this.rollbackData.clear();
+ for (const item of items) {
+ this.rollbackData.set(item.registryKey, item.isEnabled);
+ }
+ this.inTransaction = true;
+ }
+
+ /**
+ * 回滚到之前保存的状态
+ */
+ async rollback(): Promise<void> {
+ if (!this.inTransaction) return;
+ log.warn('Rolling back registry changes...');
+ try {
+ for (const [key, wasEnabled] of this.rollbackData) {
+ await this.setItemEnabledInternal(key, wasEnabled);
+ }
+ } finally {
+ this.inTransaction = false;
+ this.rollbackData.clear();
+ }
+ }
+
+ /**
+ * 提交事务(清空回滚数据)
+ */
+ commitTransaction(): void {
+ this.inTransaction = false;
+ this.rollbackData.clear();
+ }
+
+ /**
+ * 获取场景对应的完整注册表路径(用于 UI 显示)
+ */
+ getFullRegistryPath(scene: MenuScene): string {
+ return `${HKCR_PREFIX}\\${SCENE_REGISTRY_PATHS[scene]}`;
+ }
+
+ private async setItemEnabledInternal(registryKey: string, enabled: boolean): Promise<void> {
+ if (this.isShellExtKey(registryKey)) {
+ const script = this.ps.buildShellExtToggleScript(registryKey, enabled);
+ await this.ps.executeElevated<{ ok: boolean }>(script);
+ } else {
+ const script = this.ps.buildSetEnabledScript(registryKey, enabled);
+ await this.ps.executeElevated<{ ok: boolean }>(script);
+ }
+ }
+
+ /** Shell 扩展的 registryKey 包含 'shellex' 和 'ContextMenuHandlers' 路径段 */
+ private isShellExtKey(registryKey: string): boolean {
+ return registryKey.includes('shellex') && registryKey.includes('ContextMenuHandlers');
+ }
+
+private inferSource(subKeyName: string): string {
+ return subKeyName || '';
+ }
+
+ private determineType(itemType?: string): MenuItemType {
+ Iif (itemType === 'ShellExt') return MenuItemType.ShellExt;
+ return MenuItemType.System;
+ }
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| BackupService.ts | +
+
+ |
+ 18.98% | +15/79 | +2.85% | +1/35 | +36.36% | +4/11 | +22.38% | +15/67 | +
| MenuManagerService.ts | +
+
+ |
+ 100% | +46/46 | +85.71% | +12/14 | +100% | +9/9 | +100% | +38/38 | +
| PowerShellBridge.ts | +
+
+ |
+ 14.81% | +8/54 | +14.28% | +3/21 | +33.33% | +2/6 | +15.38% | +8/52 | +
| RegistryService.ts | +
+
+ |
+ 47.45% | +28/59 | +16.66% | +4/24 | +50% | +6/12 | +48.21% | +27/56 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 +25 +26 +27 +28 +29 +30 +31 +32 +33 +34 +35 +36 +37 +38 +39 +40 +41 +42 +43 +44 +45 +46 +47 +48 +49 +50 +51 +52 +53 +54 | + + + + +9x + + + + + + + +8x +7x +4x +4x + + + + + + + +4x + +1x + +4x + + + + + + + +3x +3x + +3x +3x + + + +2x +1x + + + + + +3x + + | import { execFileSync, execFile } from 'child_process';
+import { app } from 'electron';
+import log from './logger';
+
+// 进程级缓存,避免每次写操作都 spawn 子进程检测
+let _adminCache: boolean | null = null;
+
+/**
+ * 检查当前进程是否以管理员身份运行(进程令牌已提权)
+ * 使用 Windows Security Principal API,比 net session 更可靠:
+ * net session 在域环境或特定组策略下可能误报 true
+ */
+export function isAdmin(): boolean {
+ if (process.platform !== 'win32') return true;
+ if (_adminCache !== null) return _adminCache;
+ try {
+ const out = execFileSync(
+ 'powershell.exe',
+ [
+ '-NonInteractive', '-NoProfile', '-Command',
+ '([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)',
+ ],
+ { stdio: 'pipe' }
+ ).toString().trim();
+ _adminCache = out === 'True';
+ } catch {
+ _adminCache = false;
+ }
+ return _adminCache;
+}
+
+/**
+ * 以管理员身份重启应用(UAC 提权)
+ * 使用 PowerShell Start-Process -Verb RunAs
+ */
+export function restartAsAdmin(): void {
+ const exePath = app.getPath('exe');
+ log.info(`Restarting as admin: ${exePath}`);
+
+ const script = `Start-Process -FilePath "${exePath}" -Verb RunAs`;
+ execFile(
+ 'powershell.exe',
+ ['-NonInteractive', '-NoProfile', '-Command', script],
+ (err) => {
+ if (err) {
+ log.error('Failed to restart as admin:', err);
+ }
+ }
+ );
+
+ // 延迟退出,确保 Start-Process 已发出
+ setTimeout(() => app.quit(), 500);
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| File | ++ | Statements | ++ | Branches | ++ | Functions | ++ | Lines | ++ |
|---|---|---|---|---|---|---|---|---|---|
| AdminHelper.ts | +
+
+ |
+ 100% | +18/18 | +100% | +6/6 | +100% | +4/4 | +100% | +15/15 | +
| ipcWrapper.ts | +
+
+ |
+ 100% | +7/7 | +100% | +2/2 | +100% | +2/2 | +100% | +7/7 | +
| logger.ts | +
+
+ |
+ 0% | +0/6 | +100% | +0/0 | +0% | +0/3 | +0% | +0/6 | +
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 +21 +22 +23 +24 | + + + + + + + + + + + +8x +8x +8x +1x + +7x +7x +7x + + + + | import { IpcResult } from '../../shared/types';
+import log from 'electron-log';
+
+/**
+ * 统一 IPC handler 包装:捕获异常,返回 IpcResult<T>
+ * renderer 层无需 try-catch
+ */
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export function wrapHandler<T>(
+ fn: (...args: any[]) => T | Promise<T>
+): (...args: any[]) => Promise<IpcResult<T>> {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ return async (...args: any[]): Promise<IpcResult<T>> => {
+ try {
+ const data = await fn(...args);
+ return { success: true, data };
+ } catch (e: unknown) {
+ const msg = e instanceof Error ? e.message : String(e);
+ log.error('[IPC Error]', msg, e);
+ return { success: false, error: msg };
+ }
+ };
+}
+ |
+ Press n or j to go to the next uncovered block, b, p or k for the previous block. +
+ +| 1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 | + + + + + + + + + + + + + + + + + | import log from 'electron-log';
+import path from 'path';
+import { app } from 'electron';
+
+export function initLogger(): void {
+ log.transports.file.resolvePathFn = () =>
+ path.join(app.getPath('userData'), 'logs', 'main.log');
+ log.transports.file.level = 'info';
+ log.transports.file.maxDays = 7;
+ log.transports.console.level = 'debug';
+}
+
+export function getLogDir(): string {
+ return path.join(app.getPath('userData'), 'logs');
+}
+
+export default log;
+ |