@rulemetric/cli 0.7.3 → 0.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-command.js +1 -14
- package/dist/chunk-2TC6WCUT.js +34 -0
- package/dist/chunk-2UC3ULIG.js +1 -0
- package/dist/chunk-3AV3GTMP.js +1 -0
- package/dist/chunk-4Z5ECCJE.js +1 -0
- package/dist/chunk-5RBLIVBF.js +1 -0
- package/dist/chunk-67QCYUJU.js +1 -0
- package/dist/chunk-6QKI756T.js +2 -0
- package/dist/chunk-6V4WM73L.js +1 -0
- package/dist/chunk-6XEC6474.js +1 -0
- package/dist/chunk-72XLQE3X.js +2 -0
- package/dist/chunk-7DFNBRLY.js +61 -0
- package/dist/chunk-7XQ4VPLC.js +2 -0
- package/dist/chunk-AHRHCERJ.js +1 -0
- package/dist/chunk-ARNZQBMX.js +1 -0
- package/dist/chunk-CADKBP3D.js +58 -0
- package/dist/chunk-CDI5CCHU.js +1 -0
- package/dist/chunk-CNQ5DTNC.js +1 -0
- package/dist/chunk-CRAKDQ4H.js +1 -0
- package/dist/chunk-DNF4HDI6.js +14 -0
- package/dist/chunk-DSO2U4Y6.js +56 -0
- package/dist/chunk-DT4GKEP5.js +5 -0
- package/dist/chunk-DVN7GFJG.js +1 -0
- package/dist/chunk-E4SFUPYR.js +15 -0
- package/dist/chunk-EUHNCVJQ.js +33 -0
- package/dist/chunk-F2VP4B4J.js +1 -0
- package/dist/chunk-FEHM5OKW.js +1 -0
- package/dist/chunk-FHVBTDBM.js +69 -0
- package/dist/chunk-GBMF3TP6.js +1 -0
- package/dist/chunk-GY5IJ4RS.js +1 -0
- package/dist/chunk-HOHD5HY2.js +5 -0
- package/dist/chunk-HP2BUPGS.js +24 -0
- package/dist/{chunk-OQBBVTDG.js → chunk-I2DZGJRR.js} +6 -71
- package/dist/chunk-I4YJ7NTX.js +1 -0
- package/dist/chunk-IY5NXR3N.js +1 -0
- package/dist/chunk-IZTIYR5Q.js +2 -0
- package/dist/chunk-KKDHD4XA.js +2 -0
- package/dist/chunk-LQRYQE6Q.js +4 -0
- package/dist/chunk-M6AJDVCL.js +5 -0
- package/dist/chunk-MGHFDHHM.js +1 -0
- package/dist/chunk-ML36XQW7.js +1 -0
- package/dist/chunk-MODXEGCU.js +1 -0
- package/dist/chunk-MVM6OGAF.js +4 -0
- package/dist/chunk-N7ODX6PO.js +1 -0
- package/dist/chunk-NBTC5FNV.js +1 -0
- package/dist/chunk-NJ5UED3H.js +1 -0
- package/dist/chunk-NVKX36YN.js +3 -0
- package/dist/chunk-NVOTOQBA.js +1 -0
- package/dist/chunk-O7ZMHCMG.js +31 -0
- package/dist/chunk-OMXUK4M6.js +1 -0
- package/dist/chunk-PZS2KOU7.js +1 -0
- package/dist/chunk-RSKQRVPJ.js +1 -0
- package/dist/chunk-RTEATXTP.js +1 -0
- package/dist/chunk-RTWRHIKY.js +7 -0
- package/dist/chunk-RVO4HV3M.js +1 -0
- package/dist/chunk-SBILN45P.js +1 -0
- package/dist/chunk-SZ62CP55.js +1 -0
- package/dist/chunk-T3W5N3BP.js +2 -0
- package/dist/chunk-TDD7IOV2.js +1 -0
- package/dist/chunk-TIQ7DQ3M.js +1 -0
- package/dist/chunk-TU2AR6XO.js +1 -0
- package/dist/chunk-UF6US7VU.js +1 -0
- package/dist/chunk-V5QFOF4F.js +9 -0
- package/dist/chunk-V5U3E4BZ.js +1 -0
- package/dist/chunk-VAFAH6QL.js +1 -0
- package/dist/chunk-VMWY7KHH.js +1 -0
- package/dist/chunk-VUGRXM7R.js +1 -0
- package/dist/{chunk-XZXS2W24.js → chunk-VWEXKDWG.js} +10 -56
- package/dist/chunk-W2MV25SB.js +1 -0
- package/dist/chunk-WJ3H73G6.js +1 -0
- package/dist/chunk-WJUQBMTF.js +9 -0
- package/dist/chunk-WLVQJH4E.js +1 -0
- package/dist/chunk-WPJ2BND4.js +1 -0
- package/dist/chunk-WSCK4WNK.js +1 -0
- package/dist/chunk-Y65CSJFF.js +3 -0
- package/dist/chunk-ZL4MZMUK.js +34 -0
- package/dist/chunk-ZLLER3HP.js +1 -0
- package/dist/chunk-ZXF5NE54.js +1 -0
- package/dist/commands/antigravity/backfill.js +2 -136
- package/dist/commands/auth/create-key.js +1 -52
- package/dist/commands/auth/login.js +1 -85
- package/dist/commands/auth/logout.js +1 -29
- package/dist/commands/auth/revoke-key.js +1 -33
- package/dist/commands/auth/signup.js +1 -89
- package/dist/commands/auth/status.js +1 -42
- package/dist/commands/catalog/index.js +2 -52
- package/dist/commands/catalog/recommend.js +3 -43
- package/dist/commands/convert.js +1 -75
- package/dist/commands/converters.js +1 -54
- package/dist/commands/crons/list.js +1 -41
- package/dist/commands/crons/run.js +2 -76
- package/dist/commands/dashboard.js +1 -46
- package/dist/commands/diagnostics.js +1 -117
- package/dist/commands/eval-targets/create.js +1 -53
- package/dist/commands/eval-targets/list.js +1 -54
- package/dist/commands/evals/agent.js +3 -310
- package/dist/commands/evals/benchmark.js +6 -73
- package/dist/commands/evals/compare.js +13 -96
- package/dist/commands/evals/create.js +1 -67
- package/dist/commands/evals/optimize-description.js +25 -239
- package/dist/commands/evals/optimize.js +5 -211
- package/dist/commands/evals/run.js +3 -183
- package/dist/commands/gateway/ensure.js +1 -104
- package/dist/commands/hooks/emit-instructions.js +1 -25
- package/dist/commands/hooks/install.js +1 -442
- package/dist/commands/hooks/run.js +3 -81
- package/dist/commands/hooks/uninstall.js +2 -156
- package/dist/commands/import.js +1 -66
- package/dist/commands/instructions/create.js +1 -74
- package/dist/commands/instructions/delete.js +1 -55
- package/dist/commands/instructions/fork.js +1 -34
- package/dist/commands/instructions/get.js +1 -68
- package/dist/commands/instructions/list.js +1 -65
- package/dist/commands/instructions/promote.js +1 -42
- package/dist/commands/instructions/pull.js +1 -39
- package/dist/commands/instructions/upstream.js +1 -38
- package/dist/commands/instructions/versions.js +1 -53
- package/dist/commands/launcher.js +1 -40
- package/dist/commands/org/current.js +1 -77
- package/dist/commands/org/list.js +1 -52
- package/dist/commands/org/switch.js +1 -71
- package/dist/commands/packs/add-instruction.js +1 -37
- package/dist/commands/packs/apply.js +1 -39
- package/dist/commands/packs/create.js +1 -50
- package/dist/commands/packs/fork.js +1 -34
- package/dist/commands/packs/get.js +3 -56
- package/dist/commands/packs/list.js +1 -66
- package/dist/commands/packs/remove-instruction.js +1 -35
- package/dist/commands/proxy/env.js +3 -131
- package/dist/commands/proxy/logs.js +3 -85
- package/dist/commands/proxy/restart.js +1 -35
- package/dist/commands/proxy/setup.js +15 -454
- package/dist/commands/proxy/start.js +10 -333
- package/dist/commands/proxy/status.js +1 -78
- package/dist/commands/proxy/stop.js +1 -94
- package/dist/commands/research/backfill.js +1 -59
- package/dist/commands/research/tail-transcript.js +1 -70
- package/dist/commands/service/install.js +57 -796
- package/dist/commands/service/status.js +1 -180
- package/dist/commands/service/uninstall.js +1 -119
- package/dist/commands/sessions/analyze.js +3 -48
- package/dist/commands/sessions/end.js +2 -85
- package/dist/commands/sessions/import.js +1 -46
- package/dist/commands/sessions/list.js +1 -48
- package/dist/commands/sessions/start.js +3 -90
- package/dist/commands/setup.js +3 -150
- package/dist/commands/skills/export-approved.js +1 -46
- package/dist/commands/skills/install.js +4 -107
- package/dist/commands/skills/list.js +4 -65
- package/dist/commands/skills/publish.js +2 -98
- package/dist/commands/skills/search.js +3 -72
- package/dist/commands/skills/update.js +3 -55
- package/dist/commands/suggestions/accept.js +8 -84
- package/dist/commands/suggestions/dismiss.js +1 -45
- package/dist/commands/suggestions/list.js +1 -61
- package/dist/commands/suggestions/refresh.js +1 -27
- package/dist/cursor-T7AMCMKS.js +13 -0
- package/dist/dashboard/Dashboard.js +1 -243
- package/dist/dashboard/data.js +1 -15
- package/dist/dashboard/launcher/ActiveJobsList.js +1 -10
- package/dist/dashboard/launcher/LauncherOverlay.js +1 -16
- package/dist/dashboard/launcher/NewLaunchForm.js +1 -10
- package/dist/dashboard/launcher/api.js +1 -16
- package/dist/dashboard/launcher/tool-labels.js +1 -10
- package/dist/dashboard/launcher/types.js +0 -1
- package/dist/dashboard/launcher/useLaunchJobs.js +1 -11
- package/dist/dashboard/panels/friction.js +1 -10
- package/dist/dashboard/panels/insights.js +1 -10
- package/dist/dashboard/panels/instructions.js +1 -10
- package/dist/dashboard/panels/outcomes.js +1 -10
- package/dist/dashboard/panels/overview.js +1 -10
- package/dist/dashboard/panels/shared.js +1 -19
- package/dist/dashboard/types.js +1 -24
- package/dist/dist-FAVALJ5C.js +1 -0
- package/dist/index.js +1 -8
- package/dist/lib/active-org-refresh.js +1 -15
- package/dist/lib/active-org.js +1 -16
- package/dist/lib/agent-loop.js +1 -47
- package/dist/lib/antigravity-transcript.js +1 -22
- package/dist/lib/api-client.js +1 -23
- package/dist/lib/auth.js +1 -30
- package/dist/lib/capture-hosts.js +1 -8
- package/dist/lib/detect-languages.js +1 -8
- package/dist/lib/detect-tmux.js +1 -9
- package/dist/lib/diagnostics.js +1 -12
- package/dist/lib/editor-proxy-pins.js +1 -14
- package/dist/lib/ensure-api-key.js +1 -11
- package/dist/lib/eval-analyzer.js +1 -9
- package/dist/lib/eval-benchmark.js +1 -12
- package/dist/lib/eval-conversation-handler.js +1 -13
- package/dist/lib/eval-executor.js +1 -8
- package/dist/lib/eval-grader.js +1 -9
- package/dist/lib/eval-meta-judge.js +1 -9
- package/dist/lib/eval-trigger-tester.js +1 -11
- package/dist/lib/eval-worktree.js +1 -8
- package/dist/lib/gateway-entry.js +1 -61
- package/dist/lib/gateway-identity.js +1 -16
- package/dist/lib/gateway-lifecycle.js +1 -32
- package/dist/lib/gateway.js +1 -14
- package/dist/lib/handlers/cron-refresh-pricing.js +1 -0
- package/dist/lib/handlers/cron-refresh-skills.js +1 -8
- package/dist/lib/handlers/cron-suggest-instructions.js +1 -16
- package/dist/lib/handlers/process-announcement.js +1 -11
- package/dist/lib/handlers/process-changelog.js +1 -11
- package/dist/lib/handlers/process-conversation.js +1 -14
- package/dist/lib/handlers/process-eval.js +1 -15
- package/dist/lib/handlers/process-insights.js +1 -15
- package/dist/lib/handlers/process-launch.js +1 -16
- package/dist/lib/handlers/process-run-cleanup.js +1 -13
- package/dist/lib/handlers/process-send-message.js +1 -10
- package/dist/lib/handlers/process-session-goal.js +1 -12
- package/dist/lib/hooks-config.js +1 -81
- package/dist/lib/insights-prompts.js +1 -12
- package/dist/lib/instruction-snapshot.js +1 -8
- package/dist/lib/job-helpers.js +0 -1
- package/dist/lib/llm-client.js +1 -20
- package/dist/lib/manual-tasks-meta.js +1 -8
- package/dist/lib/manual-tasks.js +1 -80
- package/dist/lib/output.js +1 -12
- package/dist/lib/permanent-job-error.js +1 -10
- package/dist/lib/pid-identity.js +1 -14
- package/dist/lib/proxy-respawn.js +1 -12
- package/dist/lib/research-client.js +1 -12
- package/dist/lib/setup-steps.js +1 -33
- package/dist/lib/skills-loader.js +1 -8
- package/dist/lib/statusline-shim.js +1 -20
- package/dist/lib/telemetry.js +1 -16
- package/dist/lib/terminal-target-heartbeat.js +1 -15
- package/dist/lib/tmux.js +1 -16
- package/dist/lib/transcript-rate-limit-watcher.js +1 -22
- package/dist/lib/types.js +1 -8
- package/dist/lib/version-check.js +1 -12
- package/dist/lib/which.js +1 -8
- package/dist/lib/worktree.js +1 -17
- package/dist/opencode-QRJ3BH23.js +1 -0
- package/oclif.manifest.json +135 -135
- package/package.json +9 -8
- package/dist/base-command.js.map +0 -7
- package/dist/chunk-2HK7RGZB.js +0 -82
- package/dist/chunk-2HK7RGZB.js.map +0 -7
- package/dist/chunk-2ORJWTNZ.js +0 -307
- package/dist/chunk-2ORJWTNZ.js.map +0 -7
- package/dist/chunk-37LN5O7M.js +0 -59
- package/dist/chunk-37LN5O7M.js.map +0 -7
- package/dist/chunk-3S7223UR.js +0 -81
- package/dist/chunk-3S7223UR.js.map +0 -7
- package/dist/chunk-3TIMQ3O6.js +0 -96
- package/dist/chunk-3TIMQ3O6.js.map +0 -7
- package/dist/chunk-42GFSAJP.js +0 -32
- package/dist/chunk-42GFSAJP.js.map +0 -7
- package/dist/chunk-4BHDHHKL.js +0 -48
- package/dist/chunk-4BHDHHKL.js.map +0 -7
- package/dist/chunk-4D537GXE.js +0 -99
- package/dist/chunk-4D537GXE.js.map +0 -7
- package/dist/chunk-4LYCDS2N.js +0 -139
- package/dist/chunk-4LYCDS2N.js.map +0 -7
- package/dist/chunk-4SCZK7AL.js +0 -129
- package/dist/chunk-4SCZK7AL.js.map +0 -7
- package/dist/chunk-4XAWK6S2.js +0 -161
- package/dist/chunk-4XAWK6S2.js.map +0 -7
- package/dist/chunk-52WIYRZH.js +0 -73
- package/dist/chunk-52WIYRZH.js.map +0 -7
- package/dist/chunk-5BAOGNUH.js +0 -79
- package/dist/chunk-5BAOGNUH.js.map +0 -7
- package/dist/chunk-5EDMGK3W.js +0 -39
- package/dist/chunk-5EDMGK3W.js.map +0 -7
- package/dist/chunk-6NBS5XFS.js +0 -99
- package/dist/chunk-6NBS5XFS.js.map +0 -7
- package/dist/chunk-7LVVKQMZ.js +0 -98
- package/dist/chunk-7LVVKQMZ.js.map +0 -7
- package/dist/chunk-7NNFYWKK.js +0 -243
- package/dist/chunk-7NNFYWKK.js.map +0 -7
- package/dist/chunk-7NYKWN7U.js +0 -12
- package/dist/chunk-7NYKWN7U.js.map +0 -7
- package/dist/chunk-BO76WKJR.js +0 -42
- package/dist/chunk-BO76WKJR.js.map +0 -7
- package/dist/chunk-BRD5RUGM.js +0 -53
- package/dist/chunk-BRD5RUGM.js.map +0 -7
- package/dist/chunk-BT6Z6545.js +0 -43
- package/dist/chunk-BT6Z6545.js.map +0 -7
- package/dist/chunk-BZDKEQKU.js +0 -39
- package/dist/chunk-BZDKEQKU.js.map +0 -7
- package/dist/chunk-DGHWRQXL.js +0 -17
- package/dist/chunk-DGHWRQXL.js.map +0 -7
- package/dist/chunk-DIQUV5FR.js +0 -257
- package/dist/chunk-DIQUV5FR.js.map +0 -7
- package/dist/chunk-DMX5YOTM.js +0 -167
- package/dist/chunk-DMX5YOTM.js.map +0 -7
- package/dist/chunk-DNKSVWMD.js +0 -108
- package/dist/chunk-DNKSVWMD.js.map +0 -7
- package/dist/chunk-DQ3KELXD.js +0 -74
- package/dist/chunk-DQ3KELXD.js.map +0 -7
- package/dist/chunk-DUJSRBLP.js +0 -122
- package/dist/chunk-DUJSRBLP.js.map +0 -7
- package/dist/chunk-E3BIT53W.js +0 -154
- package/dist/chunk-E3BIT53W.js.map +0 -7
- package/dist/chunk-EARWG4EV.js +0 -22
- package/dist/chunk-EARWG4EV.js.map +0 -7
- package/dist/chunk-EI5BHWXA.js +0 -1662
- package/dist/chunk-EI5BHWXA.js.map +0 -7
- package/dist/chunk-EJ3YAFDP.js +0 -37
- package/dist/chunk-EJ3YAFDP.js.map +0 -7
- package/dist/chunk-EKJ2CABV.js +0 -76
- package/dist/chunk-EKJ2CABV.js.map +0 -7
- package/dist/chunk-EKP32DLN.js +0 -16
- package/dist/chunk-EKP32DLN.js.map +0 -7
- package/dist/chunk-EYPAAGN5.js +0 -51
- package/dist/chunk-EYPAAGN5.js.map +0 -7
- package/dist/chunk-F5N6CLJJ.js +0 -1995
- package/dist/chunk-F5N6CLJJ.js.map +0 -7
- package/dist/chunk-GIUBARWS.js +0 -99
- package/dist/chunk-GIUBARWS.js.map +0 -7
- package/dist/chunk-GQNJO2P2.js +0 -102
- package/dist/chunk-GQNJO2P2.js.map +0 -7
- package/dist/chunk-IJGF2ETG.js +0 -68
- package/dist/chunk-IJGF2ETG.js.map +0 -7
- package/dist/chunk-ILYDELUE.js +0 -46
- package/dist/chunk-ILYDELUE.js.map +0 -7
- package/dist/chunk-J6QFW4LD.js +0 -55
- package/dist/chunk-J6QFW4LD.js.map +0 -7
- package/dist/chunk-J7N3DLH6.js +0 -117
- package/dist/chunk-J7N3DLH6.js.map +0 -7
- package/dist/chunk-JFDWKLQC.js +0 -23
- package/dist/chunk-JFDWKLQC.js.map +0 -7
- package/dist/chunk-JULOLTZS.js +0 -61
- package/dist/chunk-JULOLTZS.js.map +0 -7
- package/dist/chunk-KJKBDQ4D.js +0 -220
- package/dist/chunk-KJKBDQ4D.js.map +0 -7
- package/dist/chunk-KRBQLMOP.js +0 -41
- package/dist/chunk-KRBQLMOP.js.map +0 -7
- package/dist/chunk-LXKPNATC.js +0 -624
- package/dist/chunk-LXKPNATC.js.map +0 -7
- package/dist/chunk-NSBPE2FW.js +0 -17
- package/dist/chunk-NSBPE2FW.js.map +0 -7
- package/dist/chunk-OCKHW72U.js +0 -55
- package/dist/chunk-OCKHW72U.js.map +0 -7
- package/dist/chunk-OKWCKCPE.js +0 -414
- package/dist/chunk-OKWCKCPE.js.map +0 -7
- package/dist/chunk-OO7JDFS4.js +0 -157
- package/dist/chunk-OO7JDFS4.js.map +0 -7
- package/dist/chunk-OQBBVTDG.js.map +0 -7
- package/dist/chunk-OQSQC7VB.js +0 -77
- package/dist/chunk-OQSQC7VB.js.map +0 -7
- package/dist/chunk-PED7L5GX.js +0 -92
- package/dist/chunk-PED7L5GX.js.map +0 -7
- package/dist/chunk-QJQS6TUN.js +0 -44
- package/dist/chunk-QJQS6TUN.js.map +0 -7
- package/dist/chunk-QN34TGD3.js +0 -83
- package/dist/chunk-QN34TGD3.js.map +0 -7
- package/dist/chunk-QSN77T7C.js +0 -250
- package/dist/chunk-QSN77T7C.js.map +0 -7
- package/dist/chunk-R56PH7YE.js +0 -78
- package/dist/chunk-R56PH7YE.js.map +0 -7
- package/dist/chunk-RAXRZYOB.js +0 -299
- package/dist/chunk-RAXRZYOB.js.map +0 -7
- package/dist/chunk-RH7NHVSK.js +0 -16
- package/dist/chunk-RH7NHVSK.js.map +0 -7
- package/dist/chunk-RMTT4KDY.js +0 -191
- package/dist/chunk-RMTT4KDY.js.map +0 -7
- package/dist/chunk-RQ2TMLKG.js +0 -142
- package/dist/chunk-RQ2TMLKG.js.map +0 -7
- package/dist/chunk-SO3T35U7.js +0 -80
- package/dist/chunk-SO3T35U7.js.map +0 -7
- package/dist/chunk-UKMLICWS.js +0 -51
- package/dist/chunk-UKMLICWS.js.map +0 -7
- package/dist/chunk-VA5FFOS7.js +0 -243
- package/dist/chunk-VA5FFOS7.js.map +0 -7
- package/dist/chunk-W2YERO7E.js +0 -170
- package/dist/chunk-W2YERO7E.js.map +0 -7
- package/dist/chunk-W53GKIZQ.js +0 -50
- package/dist/chunk-W53GKIZQ.js.map +0 -7
- package/dist/chunk-WA4MWYRW.js +0 -254
- package/dist/chunk-WA4MWYRW.js.map +0 -7
- package/dist/chunk-XC6M4CSW.js +0 -44
- package/dist/chunk-XC6M4CSW.js.map +0 -7
- package/dist/chunk-XK42ZBDE.js +0 -23
- package/dist/chunk-XK42ZBDE.js.map +0 -7
- package/dist/chunk-XUV6DDBF.js +0 -124
- package/dist/chunk-XUV6DDBF.js.map +0 -7
- package/dist/chunk-XZXS2W24.js.map +0 -7
- package/dist/chunk-Y3WD5CSW.js +0 -49
- package/dist/chunk-Y3WD5CSW.js.map +0 -7
- package/dist/chunk-Y4BJXXYV.js +0 -148
- package/dist/chunk-Y4BJXXYV.js.map +0 -7
- package/dist/chunk-YNIH3KMU.js +0 -121
- package/dist/chunk-YNIH3KMU.js.map +0 -7
- package/dist/chunk-ZUJBM6HE.js +0 -125
- package/dist/chunk-ZUJBM6HE.js.map +0 -7
- package/dist/commands/antigravity/backfill.js.map +0 -7
- package/dist/commands/auth/create-key.js.map +0 -7
- package/dist/commands/auth/login.js.map +0 -7
- package/dist/commands/auth/logout.js.map +0 -7
- package/dist/commands/auth/revoke-key.js.map +0 -7
- package/dist/commands/auth/signup.js.map +0 -7
- package/dist/commands/auth/status.js.map +0 -7
- package/dist/commands/catalog/index.js.map +0 -7
- package/dist/commands/catalog/recommend.js.map +0 -7
- package/dist/commands/convert.js.map +0 -7
- package/dist/commands/converters.js.map +0 -7
- package/dist/commands/crons/list.js.map +0 -7
- package/dist/commands/crons/run.js.map +0 -7
- package/dist/commands/dashboard.js.map +0 -7
- package/dist/commands/diagnostics.js.map +0 -7
- package/dist/commands/eval-targets/create.js.map +0 -7
- package/dist/commands/eval-targets/list.js.map +0 -7
- package/dist/commands/evals/agent.js.map +0 -7
- package/dist/commands/evals/benchmark.js.map +0 -7
- package/dist/commands/evals/compare.js.map +0 -7
- package/dist/commands/evals/create.js.map +0 -7
- package/dist/commands/evals/optimize-description.js.map +0 -7
- package/dist/commands/evals/optimize.js.map +0 -7
- package/dist/commands/evals/run.js.map +0 -7
- package/dist/commands/gateway/ensure.js.map +0 -7
- package/dist/commands/hooks/emit-instructions.js.map +0 -7
- package/dist/commands/hooks/install.js.map +0 -7
- package/dist/commands/hooks/run.js.map +0 -7
- package/dist/commands/hooks/uninstall.js.map +0 -7
- package/dist/commands/import.js.map +0 -7
- package/dist/commands/instructions/create.js.map +0 -7
- package/dist/commands/instructions/delete.js.map +0 -7
- package/dist/commands/instructions/fork.js.map +0 -7
- package/dist/commands/instructions/get.js.map +0 -7
- package/dist/commands/instructions/list.js.map +0 -7
- package/dist/commands/instructions/promote.js.map +0 -7
- package/dist/commands/instructions/pull.js.map +0 -7
- package/dist/commands/instructions/upstream.js.map +0 -7
- package/dist/commands/instructions/versions.js.map +0 -7
- package/dist/commands/launcher.js.map +0 -7
- package/dist/commands/org/current.js.map +0 -7
- package/dist/commands/org/list.js.map +0 -7
- package/dist/commands/org/switch.js.map +0 -7
- package/dist/commands/packs/add-instruction.js.map +0 -7
- package/dist/commands/packs/apply.js.map +0 -7
- package/dist/commands/packs/create.js.map +0 -7
- package/dist/commands/packs/fork.js.map +0 -7
- package/dist/commands/packs/get.js.map +0 -7
- package/dist/commands/packs/list.js.map +0 -7
- package/dist/commands/packs/remove-instruction.js.map +0 -7
- package/dist/commands/proxy/env.js.map +0 -7
- package/dist/commands/proxy/logs.js.map +0 -7
- package/dist/commands/proxy/restart.js.map +0 -7
- package/dist/commands/proxy/setup.js.map +0 -7
- package/dist/commands/proxy/start.js.map +0 -7
- package/dist/commands/proxy/status.js.map +0 -7
- package/dist/commands/proxy/stop.js.map +0 -7
- package/dist/commands/research/backfill.js.map +0 -7
- package/dist/commands/research/tail-transcript.js.map +0 -7
- package/dist/commands/service/install.js.map +0 -7
- package/dist/commands/service/status.js.map +0 -7
- package/dist/commands/service/uninstall.js.map +0 -7
- package/dist/commands/sessions/analyze.js.map +0 -7
- package/dist/commands/sessions/end.js.map +0 -7
- package/dist/commands/sessions/import.js.map +0 -7
- package/dist/commands/sessions/list.js.map +0 -7
- package/dist/commands/sessions/start.js.map +0 -7
- package/dist/commands/setup.js.map +0 -7
- package/dist/commands/skills/export-approved.js.map +0 -7
- package/dist/commands/skills/install.js.map +0 -7
- package/dist/commands/skills/list.js.map +0 -7
- package/dist/commands/skills/publish.js.map +0 -7
- package/dist/commands/skills/search.js.map +0 -7
- package/dist/commands/skills/update.js.map +0 -7
- package/dist/commands/suggestions/accept.js.map +0 -7
- package/dist/commands/suggestions/dismiss.js.map +0 -7
- package/dist/commands/suggestions/list.js.map +0 -7
- package/dist/commands/suggestions/refresh.js.map +0 -7
- package/dist/cursor-BGQJ4CWI.js +0 -134
- package/dist/cursor-BGQJ4CWI.js.map +0 -7
- package/dist/dashboard/Dashboard.js.map +0 -7
- package/dist/dashboard/data.js.map +0 -7
- package/dist/dashboard/launcher/ActiveJobsList.js.map +0 -7
- package/dist/dashboard/launcher/LauncherOverlay.js.map +0 -7
- package/dist/dashboard/launcher/NewLaunchForm.js.map +0 -7
- package/dist/dashboard/launcher/api.js.map +0 -7
- package/dist/dashboard/launcher/tool-labels.js.map +0 -7
- package/dist/dashboard/launcher/types.js.map +0 -7
- package/dist/dashboard/launcher/useLaunchJobs.js.map +0 -7
- package/dist/dashboard/panels/friction.js.map +0 -7
- package/dist/dashboard/panels/insights.js.map +0 -7
- package/dist/dashboard/panels/instructions.js.map +0 -7
- package/dist/dashboard/panels/outcomes.js.map +0 -7
- package/dist/dashboard/panels/overview.js.map +0 -7
- package/dist/dashboard/panels/shared.js.map +0 -7
- package/dist/dashboard/types.js.map +0 -7
- package/dist/dist-UNYP5PJR.js +0 -56
- package/dist/dist-UNYP5PJR.js.map +0 -7
- package/dist/index.js.map +0 -7
- package/dist/lib/active-org-refresh.js.map +0 -7
- package/dist/lib/active-org.js.map +0 -7
- package/dist/lib/agent-loop.js.map +0 -7
- package/dist/lib/antigravity-transcript.js.map +0 -7
- package/dist/lib/api-client.js.map +0 -7
- package/dist/lib/auth.js.map +0 -7
- package/dist/lib/capture-hosts.js.map +0 -7
- package/dist/lib/detect-languages.js.map +0 -7
- package/dist/lib/detect-tmux.js.map +0 -7
- package/dist/lib/diagnostics.js.map +0 -7
- package/dist/lib/editor-proxy-pins.js.map +0 -7
- package/dist/lib/ensure-api-key.js.map +0 -7
- package/dist/lib/eval-analyzer.js.map +0 -7
- package/dist/lib/eval-benchmark.js.map +0 -7
- package/dist/lib/eval-conversation-handler.js.map +0 -7
- package/dist/lib/eval-executor.js.map +0 -7
- package/dist/lib/eval-grader.js.map +0 -7
- package/dist/lib/eval-meta-judge.js.map +0 -7
- package/dist/lib/eval-trigger-tester.js.map +0 -7
- package/dist/lib/eval-worktree.js.map +0 -7
- package/dist/lib/gateway-entry.js.map +0 -7
- package/dist/lib/gateway-identity.js.map +0 -7
- package/dist/lib/gateway-lifecycle.js.map +0 -7
- package/dist/lib/gateway.js.map +0 -7
- package/dist/lib/handlers/cron-refresh-skills.js.map +0 -7
- package/dist/lib/handlers/cron-suggest-instructions.js.map +0 -7
- package/dist/lib/handlers/process-announcement.js.map +0 -7
- package/dist/lib/handlers/process-changelog.js.map +0 -7
- package/dist/lib/handlers/process-conversation.js.map +0 -7
- package/dist/lib/handlers/process-eval.js.map +0 -7
- package/dist/lib/handlers/process-insights.js.map +0 -7
- package/dist/lib/handlers/process-launch.js.map +0 -7
- package/dist/lib/handlers/process-run-cleanup.js.map +0 -7
- package/dist/lib/handlers/process-send-message.js.map +0 -7
- package/dist/lib/handlers/process-session-goal.js.map +0 -7
- package/dist/lib/hooks-config.js.map +0 -7
- package/dist/lib/insights-prompts.js.map +0 -7
- package/dist/lib/instruction-snapshot.js.map +0 -7
- package/dist/lib/job-helpers.js.map +0 -7
- package/dist/lib/llm-client.js.map +0 -7
- package/dist/lib/manual-tasks-meta.js.map +0 -7
- package/dist/lib/manual-tasks.js.map +0 -7
- package/dist/lib/output.js.map +0 -7
- package/dist/lib/permanent-job-error.js.map +0 -7
- package/dist/lib/pid-identity.js.map +0 -7
- package/dist/lib/proxy-respawn.js.map +0 -7
- package/dist/lib/research-client.js.map +0 -7
- package/dist/lib/setup-steps.js.map +0 -7
- package/dist/lib/skills-loader.js.map +0 -7
- package/dist/lib/statusline-shim.js.map +0 -7
- package/dist/lib/telemetry.js.map +0 -7
- package/dist/lib/terminal-target-heartbeat.js.map +0 -7
- package/dist/lib/tmux.js.map +0 -7
- package/dist/lib/transcript-rate-limit-watcher.js.map +0 -7
- package/dist/lib/types.js.map +0 -7
- package/dist/lib/version-check.js.map +0 -7
- package/dist/lib/which.js.map +0 -7
- package/dist/lib/worktree.js.map +0 -7
- package/dist/opencode-IQ7X5YMJ.js +0 -190
- package/dist/opencode-IQ7X5YMJ.js.map +0 -7
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/packs/fork.ts"],
|
|
4
|
-
"sourcesContent": ["import { Args } from '@oclif/core';\nimport { BaseCommand } from '../../base-command.js';\nimport { apiPost } from '../../lib/api-client.js';\n\nexport default class PacksFork extends BaseCommand {\n static override description = 'Fork a pack to your personal scope';\n\n static override examples = [\n '<%= config.bin %> packs fork <pack-id>',\n ];\n\n static override args = {\n id: Args.string({ description: 'ID of the pack to fork', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(PacksFork);\n this.requireAuth();\n\n const result = await apiPost<{ id: string; name: string }>(`/api/packs/${args.id}/fork`, {});\n this.log(`Forked pack: ${result.name} (${result.id})`);\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;AAAA,SAAS,YAAY;AAIrB,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA,EACjD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,OAAgB,OAAO;AAAA,IACrB,IAAI,KAAK,OAAO,EAAE,aAAa,0BAA0B,UAAU,KAAK,CAAC;AAAA,EAC3E;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,UAAS;AAC3C,SAAK,YAAY;AAEjB,UAAM,SAAS,MAAM,QAAsC,cAAc,KAAK,EAAE,SAAS,CAAC,CAAC;AAC3F,SAAK,IAAI,gBAAgB,OAAO,IAAI,KAAK,OAAO,EAAE,GAAG;AAAA,EACvD;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/packs/get.ts"],
|
|
4
|
-
"sourcesContent": ["import { Args } from '@oclif/core';\nimport { printTable } from '@oclif/table';\nimport { BaseCommand } from '../../base-command.js';\nimport { apiGet } from '../../lib/api-client.js';\nimport { truncate, formatScope } from '../../lib/output.js';\n\ninterface PackDetail {\n id: string;\n name: string;\n description: string;\n scope: string;\n version: string;\n tags: string[];\n tier: string;\n instructionCount: number;\n instructions: Array<{\n id: string;\n name: string;\n scope: string;\n version: string;\n position: number;\n }>;\n}\n\nexport default class PacksGet extends BaseCommand {\n static override description = 'Get pack detail with instruction list';\n\n static override examples = [\n '<%= config.bin %> packs get <pack-id>',\n ];\n\n static override args = {\n id: Args.string({ description: 'Pack ID', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(PacksGet);\n this.requireAuth();\n\n const pack = await apiGet<PackDetail>(`/api/packs/${args.id}`);\n\n this.log(`\\nPack: ${pack.name}`);\n this.log(`Description: ${pack.description}`);\n this.log(`Scope: ${formatScope(pack.scope)} | Version: ${pack.version} | Tier: ${pack.tier}`);\n this.log(`Tags: ${(pack.tags ?? []).join(', ') || '(none)'}`);\n this.log(`Instructions: ${pack.instructionCount}\\n`);\n\n if (pack.instructions.length > 0) {\n printTable({\n data: pack.instructions.map((i) => ({\n '#': String(i.position),\n Name: truncate(i.name, 40),\n Scope: formatScope(i.scope),\n Version: i.version,\n })),\n columns: ['#', 'Name', 'Scope', 'Version'],\n });\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAuB3B,IAAqB,WAArB,MAAqB,kBAAiB,YAAY;AAAA,EAChD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,OAAgB,OAAO;AAAA,IACrB,IAAI,KAAK,OAAO,EAAE,aAAa,WAAW,UAAU,KAAK,CAAC;AAAA,EAC5D;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,SAAQ;AAC1C,SAAK,YAAY;AAEjB,UAAM,OAAO,MAAM,OAAmB,cAAc,KAAK,EAAE,EAAE;AAE7D,SAAK,IAAI;AAAA,QAAW,KAAK,IAAI,EAAE;AAC/B,SAAK,IAAI,gBAAgB,KAAK,WAAW,EAAE;AAC3C,SAAK,IAAI,UAAU,YAAY,KAAK,KAAK,CAAC,eAAe,KAAK,OAAO,YAAY,KAAK,IAAI,EAAE;AAC5F,SAAK,IAAI,UAAU,KAAK,QAAQ,CAAC,GAAG,KAAK,IAAI,KAAK,QAAQ,EAAE;AAC5D,SAAK,IAAI,iBAAiB,KAAK,gBAAgB;AAAA,CAAI;AAEnD,QAAI,KAAK,aAAa,SAAS,GAAG;AAChC,iBAAW;AAAA,QACT,MAAM,KAAK,aAAa,IAAI,CAAC,OAAO;AAAA,UAClC,KAAK,OAAO,EAAE,QAAQ;AAAA,UACtB,MAAM,SAAS,EAAE,MAAM,EAAE;AAAA,UACzB,OAAO,YAAY,EAAE,KAAK;AAAA,UAC1B,SAAS,EAAE;AAAA,QACb,EAAE;AAAA,QACF,SAAS,CAAC,KAAK,QAAQ,SAAS,SAAS;AAAA,MAC3C,CAAC;AAAA,IACH;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/packs/list.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { printTable } from '@oclif/table';\nimport { BaseCommand } from '../../base-command.js';\nimport { apiGet } from '../../lib/api-client.js';\nimport { formatDate, truncate, formatScope } from '../../lib/output.js';\n\ninterface PackSummary {\n id: string;\n name: string;\n scope: string;\n version: string;\n tags: string[];\n instructionCount: number;\n tier: string;\n updatedAt: string;\n}\n\nexport default class PacksList extends BaseCommand {\n static override description = 'List your instruction packs';\n\n static override examples = [\n '<%= config.bin %> packs list',\n '<%= config.bin %> packs list --json',\n ];\n\n static override enableJsonFlag = true;\n\n static override flags = {\n format: Flags.string({\n char: 'f',\n description: 'Output format',\n options: ['table', 'json'],\n default: 'table',\n }),\n };\n\n async run(): Promise<PackSummary[]> {\n const { flags } = await this.parse(PacksList);\n this.requireAuth();\n\n const results = await apiGet<PackSummary[]>('/api/packs');\n\n if (results.length === 0) {\n this.log('No packs found. Create one with `rulemetric packs create`.');\n return results;\n }\n\n if (flags.format === 'json') {\n this.log(JSON.stringify(results, null, 2));\n } else {\n printTable({\n data: results.map((p) => ({\n Name: truncate(p.name, 40),\n Scope: formatScope(p.scope),\n Tier: p.tier,\n Count: String(p.instructionCount),\n Tags: (p.tags ?? []).join(', '),\n Updated: formatDate(p.updatedAt),\n })),\n columns: ['Name', 'Scope', 'Tier', 'Count', 'Tags', 'Updated'],\n });\n }\n\n return results;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,kBAAkB;AAgB3B,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA,EACjD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAgB,iBAAiB;AAAA,EAEjC,OAAgB,QAAQ;AAAA,IACtB,QAAQ,MAAM,OAAO;AAAA,MACnB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,SAAS,CAAC,SAAS,MAAM;AAAA,MACzB,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAA8B;AAClC,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,UAAS;AAC5C,SAAK,YAAY;AAEjB,UAAM,UAAU,MAAM,OAAsB,YAAY;AAExD,QAAI,QAAQ,WAAW,GAAG;AACxB,WAAK,IAAI,4DAA4D;AACrE,aAAO;AAAA,IACT;AAEA,QAAI,MAAM,WAAW,QAAQ;AAC3B,WAAK,IAAI,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA,IAC3C,OAAO;AACL,iBAAW;AAAA,QACT,MAAM,QAAQ,IAAI,CAAC,OAAO;AAAA,UACxB,MAAM,SAAS,EAAE,MAAM,EAAE;AAAA,UACzB,OAAO,YAAY,EAAE,KAAK;AAAA,UAC1B,MAAM,EAAE;AAAA,UACR,OAAO,OAAO,EAAE,gBAAgB;AAAA,UAChC,OAAO,EAAE,QAAQ,CAAC,GAAG,KAAK,IAAI;AAAA,UAC9B,SAAS,WAAW,EAAE,SAAS;AAAA,QACjC,EAAE;AAAA,QACF,SAAS,CAAC,QAAQ,SAAS,QAAQ,SAAS,QAAQ,SAAS;AAAA,MAC/D,CAAC;AAAA,IACH;AAEA,WAAO;AAAA,EACT;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/packs/remove-instruction.ts"],
|
|
4
|
-
"sourcesContent": ["import { Args } from '@oclif/core';\nimport { BaseCommand } from '../../base-command.js';\nimport { apiDelete } from '../../lib/api-client.js';\n\nexport default class PacksRemoveInstruction extends BaseCommand {\n static override description = 'Remove an instruction from a pack';\n\n static override examples = [\n '<%= config.bin %> packs remove-instruction <pack-id> <instruction-id>',\n ];\n\n static override args = {\n packId: Args.string({ description: 'Pack ID', required: true }),\n instructionId: Args.string({ description: 'Instruction ID', required: true }),\n };\n\n async run(): Promise<void> {\n const { args } = await this.parse(PacksRemoveInstruction);\n this.requireAuth();\n\n await apiDelete(`/api/packs/${args.packId}/instructions/${args.instructionId}`);\n\n this.log('Instruction removed from pack.');\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;AAAA,SAAS,YAAY;AAIrB,IAAqB,yBAArB,MAAqB,gCAA+B,YAAY;AAAA,EAC9D,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,OAAgB,OAAO;AAAA,IACrB,QAAQ,KAAK,OAAO,EAAE,aAAa,WAAW,UAAU,KAAK,CAAC;AAAA,IAC9D,eAAe,KAAK,OAAO,EAAE,aAAa,kBAAkB,UAAU,KAAK,CAAC;AAAA,EAC9E;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,KAAK,IAAI,MAAM,KAAK,MAAM,uBAAsB;AACxD,SAAK,YAAY;AAEjB,UAAM,UAAU,cAAc,KAAK,MAAM,iBAAiB,KAAK,aAAa,EAAE;AAE9E,SAAK,IAAI,gCAAgC;AAAA,EAC3C;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/env.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { existsSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport { GATEWAY_PORT } from '../../lib/gateway-lifecycle.js';\n\nconst CERT_PATH = join(homedir(), '.mitmproxy', 'mitmproxy-ca-cert.pem');\n\nexport default class ProxyEnv extends BaseCommand {\n static override description =\n 'Print shell env vars to route any client through the RuleMetric proxy';\n\n static override examples = [\n '<%= config.bin %> proxy env',\n '<%= config.bin %> proxy env --shell fish',\n 'eval \"$(rulemetric proxy env)\"',\n 'rulemetric proxy env --python',\n ];\n\n static override flags = {\n shell: Flags.string({\n char: 's',\n default: 'posix',\n description: 'Shell format (posix, fish, powershell)',\n options: ['posix', 'fish', 'powershell'],\n }),\n python: Flags.boolean({\n default: false,\n description: 'Include Python-specific env vars (REQUESTS_CA_BUNDLE, SSL_CERT_FILE)',\n }),\n node: Flags.boolean({\n default: false,\n description: 'Include Node.js-specific env vars (NODE_EXTRA_CA_CERTS)',\n }),\n all: Flags.boolean({\n default: false,\n description: 'Include env vars for all runtimes',\n }),\n vscode: Flags.boolean({\n default: false,\n description: 'Output VS Code settings.json snippet for Copilot proxy',\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ProxyEnv);\n const shell = flags.shell;\n const includeAll = flags.all;\n const includePython = flags.python || includeAll;\n const includeNode = flags.node || includeAll;\n\n const certExists = existsSync(CERT_PATH);\n const proxyUrl = `http://localhost:${GATEWAY_PORT}`;\n\n if (!certExists) {\n this.warn(\n `CA certificate not found at ${CERT_PATH}\\n` +\n `Run 'rulemetric hooks install' or 'rulemetric proxy setup' to generate it.`,\n );\n this.exit(1);\n }\n\n // VS Code settings.json snippet mode\n if (flags.vscode) {\n const snippet = {\n 'http.proxy': proxyUrl,\n 'http.proxyStrictSSL': certExists,\n 'github.copilot.advanced': {\n 'debug.overrideProxyUrl': proxyUrl,\n },\n };\n this.log(JSON.stringify(snippet, null, 2));\n return;\n }\n\n const vars: Array<{ key: string; value: string; comment?: string }> = [\n { key: 'HTTPS_PROXY', value: proxyUrl, comment: 'Route HTTPS through RuleMetric gateway' },\n { key: 'HTTP_PROXY', value: proxyUrl, comment: 'Route HTTP through RuleMetric gateway' },\n ];\n\n // Always include generic SSL vars\n vars.push({\n key: 'SSL_CERT_FILE',\n value: CERT_PATH,\n comment: 'Trust mitmproxy CA (generic \u2014 works with most libraries)',\n });\n\n if (includeNode || (!includePython && !includeNode)) {\n vars.push({\n key: 'NODE_EXTRA_CA_CERTS',\n value: CERT_PATH,\n comment: 'Trust mitmproxy CA (Node.js)',\n });\n }\n\n if (includePython) {\n vars.push({\n key: 'REQUESTS_CA_BUNDLE',\n value: CERT_PATH,\n comment: 'Trust mitmproxy CA (Python requests / httpx)',\n });\n vars.push({\n key: 'CURL_CA_BUNDLE',\n value: CERT_PATH,\n comment: 'Trust mitmproxy CA (curl / libcurl)',\n });\n }\n\n // Output in the requested shell format\n const lines: string[] = [];\n\n for (const v of vars) {\n switch (shell) {\n case 'fish':\n if (v.comment) lines.push(`# ${v.comment}`);\n lines.push(`set -gx ${v.key} \"${v.value}\"`);\n break;\n case 'powershell':\n if (v.comment) lines.push(`# ${v.comment}`);\n lines.push(`$env:${v.key} = \"${v.value}\"`);\n break;\n default:\n // posix (bash/zsh)\n if (v.comment) lines.push(`# ${v.comment}`);\n lines.push(`export ${v.key}=\"${v.value}\"`);\n }\n }\n\n this.log(lines.join('\\n'));\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/logs.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { spawn } from 'node:child_process';\nimport { BaseCommand } from '../../base-command.js';\n\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst LOG_FILE = join(CONFIG_DIR, 'proxy.log');\nconst PID_FILE = join(CONFIG_DIR, 'proxy.pid');\nconst DOCKER_CONTAINER = 'rulemetric-proxy';\n\nexport default class ProxyLogs extends BaseCommand {\n static override description = 'View context capture proxy logs';\n\n static override examples = [\n '<%= config.bin %> proxy logs',\n '<%= config.bin %> proxy logs --follow',\n '<%= config.bin %> proxy logs --lines 50',\n ];\n\n static override flags = {\n follow: Flags.boolean({\n char: 'f',\n default: false,\n description: 'Follow log output (like tail -f)',\n }),\n lines: Flags.integer({\n char: 'n',\n default: 30,\n description: 'Number of lines to show',\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ProxyLogs);\n\n // Check if running in Docker mode\n const isDocker = existsSync(PID_FILE) && readFileSync(PID_FILE, 'utf-8').trim().startsWith('docker:');\n\n if (isDocker) {\n const args = ['logs'];\n if (flags.follow) args.push('-f');\n args.push('--tail', String(flags.lines), DOCKER_CONTAINER);\n\n const child = spawn('docker', args, { stdio: 'inherit' });\n\n process.on('SIGINT', () => {\n child.kill();\n process.exit(0);\n });\n\n await new Promise<void>((resolve) => {\n child.on('exit', () => resolve());\n });\n return;\n }\n\n // Local mode \u2014 read log file\n if (!existsSync(LOG_FILE)) {\n this.log(`No proxy log file found at ${LOG_FILE}`);\n this.log('Start the proxy first: rulemetric proxy start');\n return;\n }\n\n if (flags.follow) {\n const tail = spawn('tail', ['-f', '-n', String(flags.lines), LOG_FILE], {\n stdio: 'inherit',\n });\n\n process.on('SIGINT', () => {\n tail.kill();\n process.exit(0);\n });\n\n await new Promise<void>((resolve) => {\n tail.on('exit', () => resolve());\n });\n } else {\n const content = readFileSync(LOG_FILE, 'utf-8');\n const lines = content.split('\\n');\n const lastN = lines.slice(-flags.lines).join('\\n');\n this.log(lastN);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,aAAa;AAGtB,IAAM,aAAa,KAAK,QAAQ,GAAG,WAAW,YAAY;AAC1D,IAAM,WAAW,KAAK,YAAY,WAAW;AAC7C,IAAM,WAAW,KAAK,YAAY,WAAW;AAC7C,IAAM,mBAAmB;AAEzB,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA,EACjD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAgB,QAAQ;AAAA,IACtB,QAAQ,MAAM,QAAQ;AAAA,MACpB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,IACf,CAAC;AAAA,IACD,OAAO,MAAM,QAAQ;AAAA,MACnB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,UAAS;AAG5C,UAAM,WAAW,WAAW,QAAQ,KAAK,aAAa,UAAU,OAAO,EAAE,KAAK,EAAE,WAAW,SAAS;AAEpG,QAAI,UAAU;AACZ,YAAM,OAAO,CAAC,MAAM;AACpB,UAAI,MAAM,OAAQ,MAAK,KAAK,IAAI;AAChC,WAAK,KAAK,UAAU,OAAO,MAAM,KAAK,GAAG,gBAAgB;AAEzD,YAAM,QAAQ,MAAM,UAAU,MAAM,EAAE,OAAO,UAAU,CAAC;AAExD,cAAQ,GAAG,UAAU,MAAM;AACzB,cAAM,KAAK;AACX,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAED,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,cAAM,GAAG,QAAQ,MAAM,QAAQ,CAAC;AAAA,MAClC,CAAC;AACD;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAK,IAAI,8BAA8B,QAAQ,EAAE;AACjD,WAAK,IAAI,+CAA+C;AACxD;AAAA,IACF;AAEA,QAAI,MAAM,QAAQ;AAChB,YAAM,OAAO,MAAM,QAAQ,CAAC,MAAM,MAAM,OAAO,MAAM,KAAK,GAAG,QAAQ,GAAG;AAAA,QACtE,OAAO;AAAA,MACT,CAAC;AAED,cAAQ,GAAG,UAAU,MAAM;AACzB,aAAK,KAAK;AACV,gBAAQ,KAAK,CAAC;AAAA,MAChB,CAAC;AAED,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,aAAK,GAAG,QAAQ,MAAM,QAAQ,CAAC;AAAA,MACjC,CAAC;AAAA,IACH,OAAO;AACL,YAAM,UAAU,aAAa,UAAU,OAAO;AAC9C,YAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,YAAM,QAAQ,MAAM,MAAM,CAAC,MAAM,KAAK,EAAE,KAAK,IAAI;AACjD,WAAK,IAAI,KAAK;AAAA,IAChB;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/restart.ts"],
|
|
4
|
-
"sourcesContent": ["// `rulemetric proxy restart` \u2014 stop + start in one command. Used by the\n// gateway when it detects an addon version drift (the proxy is loading\n// stale code from a previous CLI version), and by humans who just want\n// the obvious one-liner.\n//\n// We invoke the existing stop and start commands via oclif's runCommand\n// rather than re-implementing their logic. This keeps the addon-path\n// resolution, auth-env loading, and PID file management in exactly one\n// place.\nimport { Flags } from '@oclif/core';\nimport { setTimeout as sleep } from 'node:timers/promises';\nimport { BaseCommand } from '../../base-command.js';\n\nexport default class ProxyRestart extends BaseCommand {\n static override description = 'Restart the context capture proxy (stop + start)';\n static override examples = ['<%= config.bin %> proxy restart'];\n static override flags = {\n port: Flags.integer({ char: 'p', description: 'Proxy listen port' }),\n docker: Flags.boolean({ char: 'd', default: false, description: 'Run in Docker mode' }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ProxyRestart);\n // Stop is idempotent \u2014 exits cleanly even if nothing is running.\n await this.config.runCommand('proxy:stop', []);\n // Brief gap so the OS releases the port before we re-bind. Without this,\n // the new mitmdump can race with the dying process and fail with\n // \"address already in use\".\n await sleep(500);\n const args: string[] = ['--skip-checks'];\n if (flags.docker) args.push('--docker');\n if (flags.port) args.push('--port', String(flags.port));\n await this.config.runCommand('proxy:start', args);\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;AASA,SAAS,aAAa;AACtB,SAAS,cAAc,aAAa;AAGpC,IAAqB,eAArB,MAAqB,sBAAqB,YAAY;AAAA,EACpD,OAAgB,cAAc;AAAA,EAC9B,OAAgB,WAAW,CAAC,iCAAiC;AAAA,EAC7D,OAAgB,QAAQ;AAAA,IACtB,MAAM,MAAM,QAAQ,EAAE,MAAM,KAAK,aAAa,oBAAoB,CAAC;AAAA,IACnE,QAAQ,MAAM,QAAQ,EAAE,MAAM,KAAK,SAAS,OAAO,aAAa,qBAAqB,CAAC;AAAA,EACxF;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,aAAY;AAE/C,UAAM,KAAK,OAAO,WAAW,cAAc,CAAC,CAAC;AAI7C,UAAM,MAAM,GAAG;AACf,UAAM,OAAiB,CAAC,eAAe;AACvC,QAAI,MAAM,OAAQ,MAAK,KAAK,UAAU;AACtC,QAAI,MAAM,KAAM,MAAK,KAAK,UAAU,OAAO,MAAM,IAAI,CAAC;AACtD,UAAM,KAAK,OAAO,WAAW,eAAe,IAAI;AAAA,EAClD;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/setup.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { execSync, spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync, appendFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport { isAuthenticated } from '../../lib/auth.js';\nimport { findBinary } from '../../lib/which.js';\n\nconst CERT_PATH = join(homedir(), '.mitmproxy', 'mitmproxy-ca-cert.pem');\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst DEFAULT_PORT = 8787;\n\ninterface CheckResult {\n name: string;\n ok: boolean;\n message: string;\n fix?: () => Promise<boolean>;\n}\n\nexport default class ProxySetup extends BaseCommand {\n static override description = 'Set up context capture proxy (installs mitmproxy, configures shell)';\n\n static override examples = [\n '<%= config.bin %> proxy setup',\n '<%= config.bin %> proxy setup --shell zsh',\n '<%= config.bin %> proxy setup --check',\n ];\n\n static override flags = {\n shell: Flags.string({\n description: 'Shell to configure (auto-detected if omitted)',\n options: ['zsh', 'bash', 'fish', 'powershell'],\n }),\n port: Flags.integer({\n char: 'p',\n default: DEFAULT_PORT,\n description: 'Proxy listen port',\n }),\n check: Flags.boolean({\n description: 'Only check prerequisites, do not install anything',\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ProxySetup);\n const checkOnly = flags.check;\n const port = flags.port;\n\n this.log('RuleMetric Proxy Setup');\n this.log('=====================\\n');\n\n const checks: CheckResult[] = [\n await this.checkAuth(),\n await this.checkMitmproxy(),\n await this.checkCACert(),\n await this.checkHooks(),\n this.checkShellEnv(port),\n ];\n\n // Display results\n let allPassed = true;\n for (const check of checks) {\n const icon = check.ok ? '[OK]' : '[!!]';\n this.log(`${icon} ${check.name}: ${check.message}`);\n if (!check.ok) allPassed = false;\n }\n\n if (allPassed) {\n this.log('\\nAll checks passed. Run `rulemetric proxy start` to begin capturing.');\n return;\n }\n\n if (checkOnly) {\n this.log('\\nSome checks failed. Run `rulemetric proxy setup` (without --check) to fix.');\n return;\n }\n\n // Fix failures\n this.log('\\nFixing issues...\\n');\n\n for (const check of checks) {\n if (check.ok || !check.fix) continue;\n this.log(`Fixing: ${check.name}...`);\n const fixed = await check.fix();\n if (fixed) {\n this.log(` Fixed.\\n`);\n } else {\n this.log(` Could not auto-fix. See instructions above.\\n`);\n }\n }\n\n // Re-check\n this.log('---\\nVerifying...\\n');\n const reChecks = [\n await this.checkAuth(),\n await this.checkMitmproxy(),\n await this.checkCACert(),\n await this.checkHooks(),\n this.checkShellEnv(port),\n ];\n\n let finalOk = true;\n for (const check of reChecks) {\n const icon = check.ok ? '[OK]' : '[!!]';\n this.log(`${icon} ${check.name}: ${check.message}`);\n if (!check.ok) finalOk = false;\n }\n\n if (finalOk) {\n this.log('\\nSetup complete! Next steps:');\n this.log(' 1. Restart your shell (or run `source ~/.zshrc`)');\n this.log(' 2. Run `rulemetric proxy start`');\n this.log(' 3. Start Claude Code \u2014 context will be captured automatically');\n } else {\n this.log('\\nSome issues remain. Fix manually and re-run `rulemetric proxy setup --check`.');\n }\n }\n\n private async checkAuth(): Promise<CheckResult> {\n // Check JWT auth (auth.json)\n if (isAuthenticated()) {\n return { name: 'Authentication', ok: true, message: 'Logged in (JWT)' };\n }\n\n // Check API key auth (env vars or env file)\n const apiKey = process.env.RULEMETRIC_API_KEY;\n if (apiKey) {\n return { name: 'Authentication', ok: true, message: `API key (RULEMETRIC_API_KEY=${apiKey.slice(0, 8)}...)` };\n }\n\n // Check env file\n const envFile = join(homedir(), '.config', 'rulemetric', 'env');\n if (existsSync(envFile)) {\n const content = readFileSync(envFile, 'utf-8');\n if (content.includes('RULEMETRIC_API_KEY') || content.includes('RULEMETRIC_ACCESS_TOKEN')) {\n return { name: 'Authentication', ok: true, message: 'Configured via ~/.config/rulemetric/env' };\n }\n }\n\n return {\n name: 'Authentication',\n ok: false,\n message: 'Not authenticated',\n fix: async () => {\n this.log(' Run `rulemetric auth login` to authenticate.');\n this.log(' Or set RULEMETRIC_API_KEY in ~/.config/rulemetric/env.');\n return false;\n },\n };\n }\n\n private async checkMitmproxy(): Promise<CheckResult> {\n const binary = this.findBinary('mitmdump');\n if (binary) {\n // Get version\n try {\n const output = execSync(`${binary} --version 2>&1`, { encoding: 'utf-8' });\n const version = output.match(/Mitmproxy:\\s*([\\d.]+)/)?.[1] ?? 'unknown';\n return { name: 'mitmproxy', ok: true, message: `Installed (v${version})` };\n } catch {\n return { name: 'mitmproxy', ok: true, message: `Found at ${binary}` };\n }\n }\n\n return {\n name: 'mitmproxy',\n ok: false,\n message: 'Not installed',\n fix: async () => this.installMitmproxy(),\n };\n }\n\n private async installMitmproxy(): Promise<boolean> {\n // Try pipx first (cleanest isolation)\n if (this.findBinary('pipx')) {\n this.log(' Installing via pipx...');\n const result = spawnSync('pipx', ['install', 'mitmproxy'], { stdio: 'inherit' });\n if (result.status === 0) return true;\n }\n\n // Try uv tool install\n if (this.findBinary('uv')) {\n this.log(' Installing via uv...');\n const result = spawnSync('uv', ['tool', 'install', 'mitmproxy'], { stdio: 'inherit' });\n if (result.status === 0) return true;\n }\n\n // Try pip with --user\n if (this.findBinary('pip3') || this.findBinary('pip')) {\n const pip = this.findBinary('pip3') || this.findBinary('pip')!;\n this.log(` Installing via ${pip} --user...`);\n const result = spawnSync(pip, ['install', '--user', 'mitmproxy'], { stdio: 'inherit' });\n if (result.status === 0) return true;\n }\n\n // Try brew\n if (this.findBinary('brew')) {\n this.log(' Installing via brew...');\n const result = spawnSync('brew', ['install', 'mitmproxy'], { stdio: 'inherit' });\n if (result.status === 0) return true;\n }\n\n this.log(' Could not auto-install mitmproxy. Install manually:');\n this.log(' pipx install mitmproxy # recommended');\n this.log(' brew install mitmproxy # macOS');\n this.log(' pip install --user mitmproxy');\n return false;\n }\n\n private async checkCACert(): Promise<CheckResult> {\n if (existsSync(CERT_PATH)) {\n return { name: 'CA Certificate', ok: true, message: `Found at ${CERT_PATH}` };\n }\n\n return {\n name: 'CA Certificate',\n ok: false,\n message: 'Not generated yet',\n fix: async () => this.generateCACert(),\n };\n }\n\n private async generateCACert(): Promise<boolean> {\n const mitmdump = this.findBinary('mitmdump');\n if (!mitmdump) {\n this.log(' Cannot generate cert \u2014 mitmproxy not installed.');\n return false;\n }\n\n // Run mitmdump briefly to generate certs\n this.log(' Generating CA certificate...');\n const result = spawnSync(mitmdump, ['--set', 'listen_port=0', '-q'], {\n timeout: 5000,\n stdio: 'ignore',\n });\n\n // mitmdump will timeout (expected) \u2014 certs should be generated\n if (existsSync(CERT_PATH)) {\n this.log(` Certificate generated at ${CERT_PATH}`);\n\n // On macOS, offer to trust the cert\n if (process.platform === 'darwin') {\n this.log(' Adding CA cert to macOS keychain (may require sudo)...');\n const trustResult = spawnSync('sudo', [\n 'security', 'add-trusted-cert', '-d',\n '-r', 'trustRoot',\n '-k', '/Library/Keychains/System.keychain',\n CERT_PATH,\n ], { stdio: 'inherit' });\n if (trustResult.status === 0) {\n this.log(' CA cert trusted system-wide.');\n } else {\n this.log(' Could not auto-trust cert. You may need NODE_EXTRA_CA_CERTS env var.');\n }\n }\n\n return true;\n }\n\n this.log(' Failed to generate cert. Try running: mitmdump --help');\n return false;\n }\n\n private async checkHooks(): Promise<CheckResult> {\n // Hooks may live in settings.local.json (current installs write\n // machine-local state there) or the shared settings.json (legacy\n // installs) \u2014 either file counts.\n let sawSettingsFile = false;\n for (const fileName of ['settings.local.json', 'settings.json']) {\n const settingsPath = join(process.cwd(), '.claude', fileName);\n if (!existsSync(settingsPath)) continue;\n sawSettingsFile = true;\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));\n const hasHooks = settings.hooks && Object.keys(settings.hooks).length > 0;\n if (hasHooks) {\n return { name: 'Session Hooks', ok: true, message: 'Installed in current project' };\n }\n } catch { /* fall through */ }\n }\n\n if (!sawSettingsFile) {\n return {\n name: 'Session Hooks',\n ok: false,\n message: 'Not installed in current project',\n fix: async () => {\n this.log(' Run `rulemetric hooks install` in your project directory.');\n return false; // User needs to run in the right directory\n },\n };\n }\n\n return {\n name: 'Session Hooks',\n ok: false,\n message: 'settings file exists but hooks not configured',\n fix: async () => {\n this.log(' Run `rulemetric hooks install` to configure hooks.');\n return false;\n },\n };\n }\n\n private checkShellEnv(port: number): CheckResult {\n const proxySet = process.env.HTTPS_PROXY?.includes(String(port));\n const certSet = !!process.env.NODE_EXTRA_CA_CERTS;\n\n if (proxySet && certSet) {\n return { name: 'Shell Environment', ok: true, message: 'HTTPS_PROXY and NODE_EXTRA_CA_CERTS set' };\n }\n\n const missing: string[] = [];\n if (!proxySet) missing.push('HTTPS_PROXY');\n if (!certSet) missing.push('NODE_EXTRA_CA_CERTS');\n\n return {\n name: 'Shell Environment',\n ok: false,\n message: `Missing: ${missing.join(', ')}`,\n fix: async () => this.writeShellEnv(port),\n };\n }\n\n private async writeShellEnv(port: number): Promise<boolean> {\n const shell = this.detectShell();\n const rcFile = this.getShellRcFile(shell);\n\n if (!rcFile) {\n this.log(` Could not detect shell RC file. Add these to your shell profile:`);\n this.log(` export HTTPS_PROXY=http://localhost:${port}`);\n this.log(` export NODE_EXTRA_CA_CERTS=${CERT_PATH}`);\n return false;\n }\n\n const oldMarker = '# RuleMetric proxy';\n const marker = '# RuleMetric proxy (auto-detect)';\n const envFile = join(CONFIG_DIR, 'proxy.env');\n const pidFile = join(CONFIG_DIR, 'proxy.pid');\n\n // The sentinel proves the block is already installed (idempotency). POSIX\n // shells share one function name; PowerShell uses its own.\n const sentinel = shell === 'powershell' ? '_RuleMetricProxyCheck' : '_rulemetric_proxy_check';\n\n // Dynamic proxy env: re-evaluates on every prompt so stopping the proxy\n // immediately unsets HTTPS_PROXY without needing a new shell.\n const block = shell === 'powershell'\n ? this.buildPowerShellBlock(marker, pidFile, port)\n : shell === 'fish'\n ? [\n '',\n marker,\n 'function _rulemetric_proxy_check',\n ` if test -f ${pidFile}; and kill -0 (cat ${pidFile}) 2>/dev/null`,\n ` source ${envFile} 2>/dev/null`,\n ' else',\n ' set -e HTTPS_PROXY 2>/dev/null',\n ' set -e NODE_EXTRA_CA_CERTS 2>/dev/null',\n ' end',\n 'end',\n '',\n ].join('\\n')\n : shell === 'zsh'\n ? [\n '',\n marker,\n '_rulemetric_proxy_check() {',\n ` if [ -f \"${pidFile}\" ] && kill -0 \"$(cat \"${pidFile}\" 2>/dev/null)\" 2>/dev/null; then`,\n ` [ -z \"\\${HTTPS_PROXY:-}\" ] && source \"${envFile}\" 2>/dev/null`,\n ' else',\n ` [ -n \"\\${HTTPS_PROXY:-}\" ] && unset HTTPS_PROXY NODE_EXTRA_CA_CERTS`,\n ' fi',\n '}',\n 'precmd_functions+=(_rulemetric_proxy_check)',\n '',\n ].join('\\n')\n : [\n '',\n marker,\n '_rulemetric_proxy_check() {',\n ` if [ -f \"${pidFile}\" ] && kill -0 \"$(cat \"${pidFile}\" 2>/dev/null)\" 2>/dev/null; then`,\n ` [ -z \"\\${HTTPS_PROXY:-}\" ] && source \"${envFile}\" 2>/dev/null`,\n ' else',\n ` [ -n \"\\${HTTPS_PROXY:-}\" ] && unset HTTPS_PROXY NODE_EXTRA_CA_CERTS`,\n ' fi',\n '}',\n 'PROMPT_COMMAND=\"_rulemetric_proxy_check${PROMPT_COMMAND:+;$PROMPT_COMMAND}\"',\n '',\n ].join('\\n');\n\n // The PowerShell `$PROFILE` directory frequently does not exist yet; create\n // it (and any other missing rc dir, e.g. fish) before appending.\n mkdirSync(dirname(rcFile), { recursive: true });\n\n if (existsSync(rcFile)) {\n let content = readFileSync(rcFile, 'utf-8');\n\n // Remove old static block if present\n if (content.includes(oldMarker) && !content.includes(marker)) {\n content = content.replace(\n /\\n# RuleMetric proxy\\nexport HTTPS_PROXY=[^\\n]*\\nexport NODE_EXTRA_CA_CERTS=[^\\n]*\\n/,\n '\\n',\n );\n writeFileSync(rcFile, content);\n this.log(` Removed old static env vars from ${rcFile}`);\n }\n\n // Remove old auto-detect block (the one without precmd that doesn't unset on stop)\n if (content.includes('# RuleMetric proxy (auto-detect') && !content.includes('_rulemetric_proxy_check')) {\n content = content.replace(\n /\\n# RuleMetric proxy \\(auto-detect[^\\n]*\\)\\nif \\[.*?\\nfi\\n/s,\n '\\n',\n );\n writeFileSync(rcFile, content);\n this.log(` Upgraded old auto-detect block in ${rcFile}`);\n }\n\n if (content.includes(sentinel)) {\n this.log(` Shell env already configured in ${rcFile}`);\n return true;\n }\n }\n\n this.log(` Appending proxy auto-detect hook to ${rcFile}`);\n this.log(` (HTTPS_PROXY is set/unset automatically when proxy starts/stops)`);\n appendFileSync(rcFile, block);\n return true;\n }\n\n private detectShell(): string {\n // On Windows there is no $SHELL and PowerShell is the de-facto shell.\n if (process.platform === 'win32') return 'powershell';\n const shell = process.env.SHELL || '';\n if (shell.includes('zsh')) return 'zsh';\n if (shell.includes('bash')) return 'bash';\n if (shell.includes('fish')) return 'fish';\n return 'zsh'; // default on macOS\n }\n\n private getShellRcFile(shell: string): string | null {\n const home = homedir();\n switch (shell) {\n case 'zsh': return join(home, '.zshrc');\n case 'bash': return join(home, '.bashrc');\n case 'fish': return join(home, '.config', 'fish', 'config.fish');\n case 'powershell': return this.getPowerShellProfilePath();\n default: return null;\n }\n }\n\n /**\n * Resolve the PowerShell profile that loads for every host (console, VS Code\n * integrated terminal, etc.). We ask PowerShell itself for the canonical path\n * \u2014 guessing `~/Documents/...` is wrong when Documents is OneDrive-redirected.\n * Falls back to the PowerShell 7 default only if no PS binary is reachable.\n */\n private getPowerShellProfilePath(): string {\n const psBin = findBinary('pwsh') || findBinary('powershell');\n if (psBin) {\n try {\n const out = execSync(`\"${psBin}\" -NoProfile -Command \"$PROFILE.CurrentUserAllHosts\"`, {\n encoding: 'utf-8',\n }).trim();\n if (out) return out;\n } catch {\n /* fall through to constructed default */\n }\n }\n return join(homedir(), 'Documents', 'PowerShell', 'profile.ps1');\n }\n\n /**\n * Self-contained PowerShell prompt hook. Unlike the POSIX blocks it does NOT\n * source `proxy.env` (that file is `export KEY=val` syntax PowerShell can't\n * read) \u2014 it embeds the values directly. Each prompt it sets HTTPS_PROXY while\n * the proxy PID is alive and removes the vars once it stops, so a dead proxy\n * never leaves a dangling HTTPS_PROXY that would break Claude Code.\n */\n private buildPowerShellBlock(marker: string, pidFile: string, port: number): string {\n const proxyUrl = `http://localhost:${port}`;\n const psPid = pidFile.replace(/'/g, \"''\");\n const psCert = CERT_PATH.replace(/'/g, \"''\");\n return [\n '',\n marker,\n 'function _RuleMetricProxyCheck {',\n ` $pidFile = '${psPid}'`,\n ' $alive = $false',\n ' if (Test-Path $pidFile) {',\n ' $procId = (Get-Content $pidFile -ErrorAction SilentlyContinue | Select-Object -First 1)',\n ' if ($procId -match \\'^\\\\d+$\\' -and (Get-Process -Id ([int]$procId) -ErrorAction SilentlyContinue)) {',\n ' $alive = $true',\n ' }',\n ' }',\n ' if ($alive) {',\n ` $env:HTTPS_PROXY = '${proxyUrl}'`,\n ` $env:HTTP_PROXY = '${proxyUrl}'`,\n ` $env:NODE_EXTRA_CA_CERTS = '${psCert}'`,\n ' } else {',\n ' Remove-Item Env:HTTPS_PROXY, Env:HTTP_PROXY, Env:NODE_EXTRA_CA_CERTS -ErrorAction SilentlyContinue',\n ' }',\n '}',\n '# Wrap the existing prompt once so the check runs every prompt.',\n 'if (-not (Test-Path Function:\\\\_RuleMetricOriginalPrompt)) {',\n ' $existing = Get-Command prompt -ErrorAction SilentlyContinue',\n ' if ($existing) { Set-Item Function:\\\\_RuleMetricOriginalPrompt $existing.ScriptBlock }',\n '}',\n 'function prompt {',\n ' _RuleMetricProxyCheck',\n ' if (Test-Path Function:\\\\_RuleMetricOriginalPrompt) { & _RuleMetricOriginalPrompt } else { \"PS $($PWD.Path)> \" }',\n '}',\n '',\n ].join('\\n');\n }\n\n private findBinary(name: string): string | null {\n // Portable PATH scan \u2014 `which` does not exist on Windows.\n return findBinary(name);\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/start.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { execSync, spawn, spawnSync } from 'node:child_process';\nimport { appendFileSync, existsSync, mkdirSync, openSync, readFileSync, writeFileSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { BaseCommand } from '../../base-command.js';\nimport { isAuthenticated } from '../../lib/auth.js';\nimport { isPidLiveAndMatching, MITMPROXY_COMMAND_PATTERN } from '../../lib/pid-identity.js';\nimport { findBinary } from '../../lib/which.js';\nimport { buildAllowHostsArgs } from '../../lib/capture-hosts.js';\n\n// import.meta.dirname is undefined on Node < 20.11 (breaks resolve()); derive\n// portably. See onboarding field report 2026-07-07 F1.\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nconst DEFAULT_PORT = 8788;\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst PID_FILE = join(CONFIG_DIR, 'proxy.pid');\nconst LOG_FILE = join(CONFIG_DIR, 'proxy.log');\nconst DOCKER_IMAGE = 'rulemetric/proxy';\nconst DOCKER_CONTAINER = 'rulemetric-proxy';\n\n// Resolve a file inside @rulemetric/proxy via Node's OWN resolver first \u2014 it\n// handles every install layout (npm-flat global, pnpm virtual store, monorepo\n// workspace) that the hardcoded relative candidates cannot. The path guesses\n// missed pnpm-global installs entirely (deps live at `.pnpm/<pkg>/node_modules/\n// @rulemetric/proxy`, a SIBLING of the CLI, not under its node_modules), so\n// `proxy start` was dead for every pnpm user while `hooks run` \u2014 which already\n// uses import resolution \u2014 worked fine. Candidates remain as a fallback for\n// unbundled dev layouts where the workspace dep isn't installed.\nfunction resolveProxyFile(subpath: string, fallbacks: string[]): string | null {\n try {\n const req = createRequire(import.meta.url);\n const p = req.resolve(`@rulemetric/proxy/${subpath}`);\n if (existsSync(p)) return p;\n } catch {\n /* fall through to path candidates */\n }\n for (const p of fallbacks) {\n if (existsSync(p)) return p;\n }\n return null;\n}\n\nfunction findAddonPath(): string {\n const candidates = [\n resolve(MODULE_DIR, '../../../../../packages/proxy/addon/rulemetric_addon.py'),\n resolve(MODULE_DIR, '../../../node_modules/@rulemetric/proxy/addon/rulemetric_addon.py'),\n resolve(MODULE_DIR, '../../../../../node_modules/@rulemetric/proxy/addon/rulemetric_addon.py'),\n ];\n const found = resolveProxyFile('addon/rulemetric_addon.py', candidates);\n if (found) return found;\n throw new Error(\n 'Could not find rulemetric_addon.py. Is @rulemetric/proxy installed?\\n' +\n 'Searched (after module resolution):\\n' + candidates.map(c => ` ${c}`).join('\\n'),\n );\n}\n\nfunction findDockerfilePath(): string {\n const candidates = [\n resolve(MODULE_DIR, '../../../../../packages/proxy/Dockerfile'),\n resolve(MODULE_DIR, '../../../node_modules/@rulemetric/proxy/Dockerfile'),\n resolve(MODULE_DIR, '../../../../../node_modules/@rulemetric/proxy/Dockerfile'),\n ];\n const found = resolveProxyFile('Dockerfile', candidates);\n if (found) return dirname(found);\n throw new Error('Could not find proxy Dockerfile. Is @rulemetric/proxy installed?');\n}\n\nfunction dockerImageExists(): boolean {\n const result = spawnSync('docker', ['image', 'inspect', DOCKER_IMAGE], { stdio: 'ignore' });\n return result.status === 0;\n}\n\nfunction dockerContainerRunning(): boolean {\n const result = spawnSync('docker', ['container', 'inspect', '-f', '{{.State.Running}}', DOCKER_CONTAINER], {\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n return result.status === 0 && result.stdout?.trim() === 'true';\n}\n\nexport default class ProxyStart extends BaseCommand {\n static override description = 'Start context capture proxy';\n\n static override examples = [\n '<%= config.bin %> proxy start',\n '<%= config.bin %> proxy start --docker',\n '<%= config.bin %> proxy start --port 9090',\n '<%= config.bin %> proxy start --web',\n ];\n\n static override flags = {\n port: Flags.integer({ char: 'p', default: DEFAULT_PORT, description: 'Proxy listen port' }),\n docker: Flags.boolean({ char: 'd', default: false, description: 'Run proxy in Docker (no local mitmproxy needed)' }),\n web: Flags.boolean({ default: false, description: 'Launch mitmweb debug UI instead of mitmdump (local mode only)' }),\n 'skip-checks': Flags.boolean({ default: false, description: 'Skip prerequisite checks' }),\n build: Flags.boolean({ default: false, description: 'Force rebuild Docker image' }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ProxyStart);\n const port = flags.port;\n\n if (flags.docker) {\n await this.startDocker(port, flags.build);\n } else {\n await this.startLocal(port, flags.web, flags['skip-checks']);\n }\n }\n\n private async startDocker(port: number, forceBuild: boolean): Promise<void> {\n // Check Docker is available\n try {\n execSync('docker info', { stdio: 'ignore' });\n } catch {\n this.error('Docker is not running. Start Docker Desktop and try again.');\n }\n\n // Check if already running\n if (dockerContainerRunning()) {\n this.error(`Proxy container already running. Stop it first: rulemetric proxy stop`);\n }\n\n // Find the compose file (monorepo root or project root)\n const composeFile = this.findComposeFile();\n\n // Ensure mount dirs exist\n const certDir = join(homedir(), '.mitmproxy');\n mkdirSync(certDir, { recursive: true });\n const tmpDir = process.env.TMPDIR || '/tmp';\n mkdirSync(join(tmpDir, 'rulemetric'), { recursive: true });\n\n // Load auth from env file if not in environment\n const envOverrides: Record<string, string> = {\n PROXY_PORT: String(port),\n RULEMETRIC_PROJECT_PATH: process.cwd(),\n };\n\n if (!process.env.RULEMETRIC_API_KEY && !process.env.RULEMETRIC_ACCESS_TOKEN) {\n const envFile = join(homedir(), '.config', 'rulemetric', 'env');\n if (existsSync(envFile)) {\n for (const line of readFileSync(envFile, 'utf-8').split('\\n')) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {\n const [key, ...rest] = trimmed.split('=');\n envOverrides[key] = rest.join('=');\n }\n }\n }\n }\n\n if (composeFile) {\n // Use docker compose\n const composeEnv = { ...process.env, ...envOverrides };\n const buildFlag = forceBuild ? '--build' : '';\n const args = ['compose', '-f', composeFile, 'up', '-d', 'proxy'];\n if (forceBuild) args.splice(4, 0, '--build');\n\n this.log(forceBuild ? 'Building and starting proxy via Docker Compose...' : 'Starting proxy via Docker Compose...');\n const result = spawnSync('docker', args, {\n env: composeEnv,\n stdio: 'inherit',\n });\n\n if (result.status !== 0) {\n this.error('Docker Compose failed. Try: docker compose -f docker-compose.yml up proxy');\n }\n } else {\n // Fallback: raw docker run\n if (forceBuild || !dockerImageExists()) {\n const proxyDir = findDockerfilePath();\n this.log('Building proxy Docker image...');\n const build = spawnSync('docker', ['build', '-t', DOCKER_IMAGE, proxyDir], { stdio: 'inherit' });\n if (build.status !== 0) {\n this.error('Docker build failed');\n }\n }\n\n spawnSync('docker', ['rm', '-f', DOCKER_CONTAINER], { stdio: 'ignore' });\n\n const envArgs: string[] = [];\n for (const [key, val] of Object.entries(envOverrides)) {\n envArgs.push('-e', `${key}=${val}`);\n }\n // Pass through from current env\n for (const key of ['RULEMETRIC_API_URL', 'RULEMETRIC_API_KEY', 'RULEMETRIC_ACCESS_TOKEN']) {\n if (process.env[key]) envArgs.push('-e', `${key}=${process.env[key]}`);\n }\n\n const dockerArgs = [\n 'run', '-d',\n '--name', DOCKER_CONTAINER,\n '-p', `${port}:8787`,\n '-v', `${certDir}:/root/.mitmproxy`,\n '-v', `${join(tmpDir, 'rulemetric')}:/tmp/rulemetric:ro`,\n ...envArgs,\n DOCKER_IMAGE,\n ];\n\n const result = spawnSync('docker', dockerArgs, { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });\n if (result.status !== 0) {\n this.error(`Failed to start container: ${result.stderr}`);\n }\n }\n\n // Write marker so proxy stop knows it's Docker\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(PID_FILE, `docker:${DOCKER_CONTAINER}`);\n\n // Wait for container to be ready\n await new Promise(resolve => setTimeout(resolve, 2000));\n\n const inspectResult = spawnSync('docker', ['container', 'inspect', '-f', '{{.Id}}', DOCKER_CONTAINER], {\n encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],\n });\n const containerId = inspectResult.stdout?.trim().slice(0, 12) || 'unknown';\n\n this.log(`Proxy running in Docker on localhost:${port} (container: ${containerId})`);\n\n this.log('');\n this.log('View logs: rulemetric proxy logs -f');\n }\n\n private findComposeFile(): string | null {\n const candidates = [\n join(process.cwd(), 'docker-compose.yml'),\n resolve(MODULE_DIR, '../../../../../docker-compose.yml'),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return null;\n }\n\n private async startLocal(port: number, useWeb: boolean, skipChecks: boolean): Promise<void> {\n // Check if already running\n if (existsSync(PID_FILE)) {\n const content = readFileSync(PID_FILE, 'utf-8').trim();\n if (content.startsWith('docker:')) {\n this.error('Proxy is running in Docker. Stop it first: rulemetric proxy stop');\n }\n // PID files survive reboots, and the OS may hand the recorded PID to an\n // unrelated process \u2014 bare `kill -0` would then block every restart with\n // \"already running\". Only a live PID whose command line is actually\n // mitmproxy counts; anything else is a stale file we overwrite below.\n const pid = Number.parseInt(content, 10);\n if (!Number.isNaN(pid) && isPidLiveAndMatching(pid, MITMPROXY_COMMAND_PATTERN)) {\n this.error(`Proxy already running (PID ${pid}). Stop it first: rulemetric proxy stop`);\n }\n }\n\n // Check mitmproxy is installed (portable PATH scan \u2014 Windows has no `which`)\n // and spawn the RESOLVED path, never the bare name: findBinary probes\n // well-known install dirs beyond $PATH, so under a GUI/minimal-PATH launch\n // the check can succeed while spawn('mitmdump') would ENOENT \u2014 the exact\n // detection-vs-spawn split that made the 0.6.6 mitmproxy fix a false fix.\n const binaryName = useWeb ? 'mitmweb' : 'mitmdump';\n const binary = findBinary(binaryName);\n if (!binary) {\n this.error(\n `${binaryName} not found. Use Docker mode instead:\\n` +\n ' rulemetric proxy start --docker\\n\\n' +\n 'Or install mitmproxy locally:\\n' +\n ' pipx install mitmproxy\\n' +\n ' brew install mitmproxy',\n );\n }\n\n // Prerequisite warnings (non-blocking)\n if (!skipChecks) {\n const warnings: string[] = [];\n\n if (!isAuthenticated() && !process.env.RULEMETRIC_API_KEY) {\n const envFile = join(homedir(), '.config', 'rulemetric', 'env');\n let hasEnvAuth = false;\n if (existsSync(envFile)) {\n const content = readFileSync(envFile, 'utf-8');\n hasEnvAuth = content.includes('RULEMETRIC_API_KEY') || content.includes('RULEMETRIC_ACCESS_TOKEN');\n }\n if (!hasEnvAuth) {\n warnings.push('Not authenticated \u2014 snapshots cannot be stored. Run `rulemetric auth login`.');\n }\n }\n\n const certPath = join(homedir(), '.mitmproxy', 'mitmproxy-ca-cert.pem');\n if (!existsSync(certPath)) {\n warnings.push('CA cert not found \u2014 HTTPS interception may fail. Run `rulemetric proxy setup`.');\n }\n\n // Hooks may live in settings.local.json (current installs write\n // machine-local state there) or the shared settings.json (legacy\n // installs) \u2014 either file counts as installed.\n const hasClaudeSettings = ['settings.local.json', 'settings.json'].some((fileName) =>\n existsSync(join(process.cwd(), '.claude', fileName)),\n );\n if (!hasClaudeSettings) {\n warnings.push('Session hooks not installed \u2014 snapshots won\\'t link to sessions. Run `rulemetric hooks install`.');\n }\n\n if (warnings.length > 0) {\n for (const w of warnings) {\n this.log(` Warning: ${w}`);\n }\n this.log('');\n }\n }\n\n // Find addon\n const addonPath = findAddonPath();\n\n // Build env \u2014 pass through auth credentials for the reporter\n const env: Record<string, string | undefined> = {\n ...process.env,\n RULEMETRIC_PROJECT_PATH: process.cwd(),\n // Bound to this CLI binary's version. The addon writes this to a\n // version file the gateway watches; on drift the gateway restarts\n // mitmproxy so an upgraded CLI doesn't sit next to a stale proxy.\n RULEMETRIC_CLI_VERSION: this.config.version,\n };\n\n // Load auth from env file if not already in environment\n if (!env.RULEMETRIC_API_KEY && !env.RULEMETRIC_ACCESS_TOKEN) {\n const envFile = join(homedir(), '.config', 'rulemetric', 'env');\n if (existsSync(envFile)) {\n for (const line of readFileSync(envFile, 'utf-8').split('\\n')) {\n const trimmed = line.trim();\n if (trimmed && !trimmed.startsWith('#') && trimmed.includes('=')) {\n const [key, ...rest] = trimmed.split('=');\n env[key] = rest.join('=');\n }\n }\n }\n }\n\n // Ensure config dir exists\n mkdirSync(CONFIG_DIR, { recursive: true });\n\n // Open log file for proxy output (fd for spawn stdio)\n appendFileSync(LOG_FILE, `\\n--- Proxy started at ${new Date().toISOString()} (port ${port}) ---\\n`);\n const logFd = openSync(LOG_FILE, 'a');\n\n // Scope interception to capturable LLM hosts \u2014 everything else tunnels\n // untouched (no TLS interception), so bundled-CA tools survive the\n // machine-wide HTTPS_PROXY. See capture-hosts.ts for the escape hatches.\n const allowHostsArgs = buildAllowHostsArgs(dirname(addonPath));\n if (allowHostsArgs.length > 0) {\n this.log(`[OK] Interception scoped to ${allowHostsArgs.length / 2} LLM host patterns (RULEMETRIC_PROXY_ALL_HOSTS=1 to intercept everything)`);\n } else {\n this.log('[!!] Interception UNSCOPED \u2014 all HTTPS hosts pass through mitmproxy');\n }\n\n // Spawn mitmproxy with output redirected to log file\n const args = [\n '--listen-port', String(port),\n '-s', addonPath,\n '--set', 'console_eventlog_verbosity=info',\n ...allowHostsArgs,\n ];\n const child = spawn(binary, args, {\n env,\n stdio: ['ignore', logFd, logFd],\n detached: true,\n });\n\n child.unref();\n\n if (!child.pid) {\n this.error('Failed to start proxy process');\n }\n\n // Brief wait to check if process crashes immediately\n await new Promise<void>((resolve) => {\n let exited = false;\n child.once('exit', (code) => {\n exited = true;\n this.error(`Proxy exited immediately with code ${code}. Check logs:\\n ${LOG_FILE}`);\n });\n setTimeout(() => {\n if (!exited) resolve();\n }, 500);\n });\n\n // Write PID file\n writeFileSync(PID_FILE, String(child.pid));\n\n this.log(`Proxy running on localhost:${port} (PID ${child.pid})`);\n this.log(`Log file: ${LOG_FILE}`);\n\n if (useWeb) {\n this.log('');\n this.log('Debug UI: http://localhost:8081');\n }\n\n this.log('');\n this.log('View logs: rulemetric proxy logs -f');\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": ["resolve"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/status.ts"],
|
|
4
|
-
"sourcesContent": ["import { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport { isGatewayRunning, getGatewayPid, GATEWAY_PORT, MITMPROXY_PORT } from '../../lib/gateway-lifecycle.js';\nimport { isPidLiveAndMatching, pidAlive, MITMPROXY_COMMAND_PATTERN } from '../../lib/pid-identity.js';\n\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst PID_FILE = join(CONFIG_DIR, 'proxy.pid');\n\nexport default class ProxyStatus extends BaseCommand {\n static override description = 'Check gateway and context capture proxy status';\n\n static override examples = ['<%= config.bin %> proxy status'];\n\n async run(): Promise<void> {\n await this.parse(ProxyStatus);\n\n // Gateway status\n if (isGatewayRunning()) {\n const pid = getGatewayPid();\n this.log(`Gateway: running (PID ${pid}) on :${GATEWAY_PORT}`);\n } else {\n this.log(`Gateway: not running`);\n }\n\n // Mitmproxy status\n if (!existsSync(PID_FILE)) {\n this.log(`Mitmproxy: not running \u2014 routing direct (no capture)`);\n return;\n }\n\n const content = readFileSync(PID_FILE, 'utf-8').trim();\n\n // Docker mode\n if (content.startsWith('docker:')) {\n const containerName = content.slice('docker:'.length);\n const result = spawnSync('docker', ['container', 'inspect', '-f', '{{.State.Status}}', containerName], {\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n const status = result.stdout?.trim();\n if (result.status === 0 && status === 'running') {\n this.log(`Mitmproxy: running in Docker (container: ${containerName}) on :${MITMPROXY_PORT} \u2014 capturing context`);\n } else {\n this.log(`Mitmproxy: container ${status || 'not running'} (stale)`);\n }\n return;\n }\n\n // Local mode\n const pid = Number.parseInt(content, 10);\n\n if (Number.isNaN(pid)) {\n this.log('Mitmproxy: not running (invalid PID file)');\n return;\n }\n\n // Identity-checked: a reused PID (alive but not mitmproxy) must read as\n // stale, or self-heal/restarts stay blocked on a phantom \"running\" proxy.\n if (isPidLiveAndMatching(pid, MITMPROXY_COMMAND_PATTERN)) {\n this.log(`Mitmproxy: running (PID ${pid}) on :${MITMPROXY_PORT} \u2014 capturing context`);\n } else if (pidAlive(pid)) {\n this.log(`Mitmproxy: not running (PID ${pid} was reused by another process \u2014 stale PID file)`);\n } else {\n this.log('Mitmproxy: not running (stale PID file)');\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,YAAY,oBAAoB;AACzC,SAAS,eAAe;AACxB,SAAS,YAAY;AAKrB,IAAM,aAAa,KAAK,QAAQ,GAAG,WAAW,YAAY;AAC1D,IAAM,WAAW,KAAK,YAAY,WAAW;AAE7C,IAAqB,cAArB,MAAqB,qBAAoB,YAAY;AAAA,EACnD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW,CAAC,gCAAgC;AAAA,EAE5D,MAAM,MAAqB;AACzB,UAAM,KAAK,MAAM,YAAW;AAG5B,QAAI,iBAAiB,GAAG;AACtB,YAAMA,OAAM,cAAc;AAC1B,WAAK,IAAI,2BAA2BA,IAAG,SAAS,YAAY,EAAE;AAAA,IAChE,OAAO;AACL,WAAK,IAAI,wBAAwB;AAAA,IACnC;AAGA,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAK,IAAI,2DAAsD;AAC/D;AAAA,IACF;AAEA,UAAM,UAAU,aAAa,UAAU,OAAO,EAAE,KAAK;AAGrD,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,YAAM,gBAAgB,QAAQ,MAAM,UAAU,MAAM;AACpD,YAAM,SAAS,UAAU,UAAU,CAAC,aAAa,WAAW,MAAM,qBAAqB,aAAa,GAAG;AAAA,QACrG,UAAU;AAAA,QACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,MACpC,CAAC;AACD,YAAM,SAAS,OAAO,QAAQ,KAAK;AACnC,UAAI,OAAO,WAAW,KAAK,WAAW,WAAW;AAC/C,aAAK,IAAI,4CAA4C,aAAa,SAAS,cAAc,2BAAsB;AAAA,MACjH,OAAO;AACL,aAAK,IAAI,wBAAwB,UAAU,aAAa,UAAU;AAAA,MACpE;AACA;AAAA,IACF;AAGA,UAAM,MAAM,OAAO,SAAS,SAAS,EAAE;AAEvC,QAAI,OAAO,MAAM,GAAG,GAAG;AACrB,WAAK,IAAI,2CAA2C;AACpD;AAAA,IACF;AAIA,QAAI,qBAAqB,KAAK,yBAAyB,GAAG;AACxD,WAAK,IAAI,2BAA2B,GAAG,SAAS,cAAc,2BAAsB;AAAA,IACtF,WAAW,SAAS,GAAG,GAAG;AACxB,WAAK,IAAI,+BAA+B,GAAG,uDAAkD;AAAA,IAC/F,OAAO;AACL,WAAK,IAAI,yCAAyC;AAAA,IACpD;AAAA,EACF;AACF;",
|
|
6
|
-
"names": ["pid"]
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/proxy/stop.ts"],
|
|
4
|
-
"sourcesContent": ["import { spawnSync } from 'node:child_process';\nimport { existsSync, readFileSync, unlinkSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { BaseCommand } from '../../base-command.js';\nimport { isPidLiveAndMatching, MITMPROXY_COMMAND_PATTERN } from '../../lib/pid-identity.js';\n\n// MODULE_DIR is undefined on Node < 20.11 (breaks resolve()); derive\n// portably. See onboarding field report 2026-07-07 F1.\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst PID_FILE = join(CONFIG_DIR, 'proxy.pid');\n\nexport default class ProxyStop extends BaseCommand {\n static override description = 'Stop the context capture proxy';\n\n static override examples = ['<%= config.bin %> proxy stop'];\n\n async run(): Promise<void> {\n await this.parse(ProxyStop);\n\n if (!existsSync(PID_FILE)) {\n this.log('No proxy process found (no PID file).');\n return;\n }\n\n const content = readFileSync(PID_FILE, 'utf-8').trim();\n\n // Docker mode: PID file contains \"docker:<container-name>\"\n if (content.startsWith('docker:')) {\n const containerName = content.slice('docker:'.length);\n // Try docker compose first, fall back to docker stop\n const composeFile = this.findComposeFile();\n if (composeFile) {\n spawnSync('docker', ['compose', '-f', composeFile, 'stop', 'proxy'], { stdio: 'inherit' });\n spawnSync('docker', ['compose', '-f', composeFile, 'rm', '-f', 'proxy'], { stdio: 'ignore' });\n } else {\n spawnSync('docker', ['stop', containerName], { stdio: 'ignore' });\n spawnSync('docker', ['rm', containerName], { stdio: 'ignore' });\n }\n this.log(`Proxy container stopped.`);\n try { unlinkSync(PID_FILE); } catch { /* already gone */ }\n return;\n }\n\n // Local mode: PID file contains a process ID\n const pid = Number.parseInt(content, 10);\n\n if (Number.isNaN(pid)) {\n unlinkSync(PID_FILE);\n this.log('Invalid PID file removed.');\n return;\n }\n\n // Never SIGTERM a reused PID: the file may point at an unrelated process\n // that inherited the number after a reboot/crash. Identity-check first;\n // a mismatch is just a stale file to remove.\n if (isPidLiveAndMatching(pid, MITMPROXY_COMMAND_PATTERN)) {\n try {\n process.kill(pid, 'SIGTERM');\n this.log(`Proxy stopped (PID ${pid}).`);\n } catch (err: unknown) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ESRCH') {\n this.log('Proxy process not running (stale PID file removed).');\n } else {\n this.error(`Failed to stop proxy: ${(err as Error).message}`);\n }\n }\n } else {\n this.log('Proxy process not running (stale PID file removed).');\n }\n\n try { unlinkSync(PID_FILE); } catch { /* already gone */ }\n\n this.log('');\n this.log('Gateway is still running \u2014 Claude Code will route direct until proxy restarts.');\n }\n\n private findComposeFile(): string | null {\n const candidates = [\n join(process.cwd(), 'docker-compose.yml'),\n resolve(MODULE_DIR, '../../../../../docker-compose.yml'),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n return null;\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;AAAA,SAAS,iBAAiB;AAC1B,SAAS,YAAY,cAAc,kBAAkB;AACrD,SAAS,eAAe;AACxB,SAAS,SAAS,MAAM,eAAe;AACvC,SAAS,qBAAqB;AAM9B,IAAM,aAAa,QAAQ,cAAc,YAAY,GAAG,CAAC;AAEzD,IAAM,aAAa,KAAK,QAAQ,GAAG,WAAW,YAAY;AAC1D,IAAM,WAAW,KAAK,YAAY,WAAW;AAE7C,IAAqB,YAArB,MAAqB,mBAAkB,YAAY;AAAA,EACjD,OAAgB,cAAc;AAAA,EAE9B,OAAgB,WAAW,CAAC,8BAA8B;AAAA,EAE1D,MAAM,MAAqB;AACzB,UAAM,KAAK,MAAM,UAAS;AAE1B,QAAI,CAAC,WAAW,QAAQ,GAAG;AACzB,WAAK,IAAI,uCAAuC;AAChD;AAAA,IACF;AAEA,UAAM,UAAU,aAAa,UAAU,OAAO,EAAE,KAAK;AAGrD,QAAI,QAAQ,WAAW,SAAS,GAAG;AACjC,YAAM,gBAAgB,QAAQ,MAAM,UAAU,MAAM;AAEpD,YAAM,cAAc,KAAK,gBAAgB;AACzC,UAAI,aAAa;AACf,kBAAU,UAAU,CAAC,WAAW,MAAM,aAAa,QAAQ,OAAO,GAAG,EAAE,OAAO,UAAU,CAAC;AACzF,kBAAU,UAAU,CAAC,WAAW,MAAM,aAAa,MAAM,MAAM,OAAO,GAAG,EAAE,OAAO,SAAS,CAAC;AAAA,MAC9F,OAAO;AACL,kBAAU,UAAU,CAAC,QAAQ,aAAa,GAAG,EAAE,OAAO,SAAS,CAAC;AAChE,kBAAU,UAAU,CAAC,MAAM,aAAa,GAAG,EAAE,OAAO,SAAS,CAAC;AAAA,MAChE;AACA,WAAK,IAAI,0BAA0B;AACnC,UAAI;AAAE,mBAAW,QAAQ;AAAA,MAAG,QAAQ;AAAA,MAAqB;AACzD;AAAA,IACF;AAGA,UAAM,MAAM,OAAO,SAAS,SAAS,EAAE;AAEvC,QAAI,OAAO,MAAM,GAAG,GAAG;AACrB,iBAAW,QAAQ;AACnB,WAAK,IAAI,2BAA2B;AACpC;AAAA,IACF;AAKA,QAAI,qBAAqB,KAAK,yBAAyB,GAAG;AACxD,UAAI;AACF,gBAAQ,KAAK,KAAK,SAAS;AAC3B,aAAK,IAAI,sBAAsB,GAAG,IAAI;AAAA,MACxC,SAAS,KAAc;AACrB,cAAM,OAAQ,IAA8B;AAC5C,YAAI,SAAS,SAAS;AACpB,eAAK,IAAI,qDAAqD;AAAA,QAChE,OAAO;AACL,eAAK,MAAM,yBAA0B,IAAc,OAAO,EAAE;AAAA,QAC9D;AAAA,MACF;AAAA,IACF,OAAO;AACL,WAAK,IAAI,qDAAqD;AAAA,IAChE;AAEA,QAAI;AAAE,iBAAW,QAAQ;AAAA,IAAG,QAAQ;AAAA,IAAqB;AAEzD,SAAK,IAAI,EAAE;AACX,SAAK,IAAI,qFAAgF;AAAA,EAC3F;AAAA,EAEQ,kBAAiC;AACvC,UAAM,aAAa;AAAA,MACjB,KAAK,QAAQ,IAAI,GAAG,oBAAoB;AAAA,MACxC,QAAQ,YAAY,mCAAmC;AAAA,IACzD;AACA,eAAW,KAAK,YAAY;AAC1B,UAAI,WAAW,CAAC,EAAG,QAAO;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/research/backfill.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport path from 'node:path';\nimport os from 'node:os';\nimport { BaseCommand } from '../../base-command.js';\nimport { scanAllTranscripts } from '../../lib/transcript-rate-limit-watcher.js';\nimport { postObservations } from '../../lib/research-client.js';\n\nexport default class ResearchBackfill extends BaseCommand {\n static override description =\n 'Scan every ~/.claude/projects transcript for past rate-limit (429) events and POST them as observations. Opt-in seed for the usage-research dataset \u2014 forward-only tail is the default capture path.';\n\n static override examples = [\n '<%= config.bin %> research backfill',\n '<%= config.bin %> research backfill --claude-dir /custom/.claude --dry-run',\n ];\n\n static override flags = {\n 'claude-dir': Flags.string({\n description: 'Override the Claude Code projects directory',\n default: path.join(os.homedir(), '.claude'),\n }),\n 'dry-run': Flags.boolean({\n description: 'Print observation count without POSTing',\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ResearchBackfill);\n this.requireAuth();\n\n this.log(`Scanning ${path.join(flags['claude-dir'], 'projects')} for rate-limit events...`);\n const observations = await scanAllTranscripts(flags['claude-dir']);\n this.log(`Found ${observations.length} rate-limit observation(s).`);\n\n if (observations.length === 0) return;\n\n if (flags['dry-run']) {\n this.log('Dry run \u2014 nothing posted.');\n return;\n }\n\n const result = await postObservations(observations);\n this.log(`Posted ${result.posted} observation(s); ${result.failed} failed.`);\n if (result.failed > 0) {\n for (const e of result.errors) this.warn(e);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;AAAA,SAAS,aAAa;AACtB,OAAO,UAAU;AACjB,OAAO,QAAQ;AAKf,IAAqB,mBAArB,MAAqB,0BAAyB,YAAY;AAAA,EACxD,OAAgB,cACd;AAAA,EAEF,OAAgB,WAAW;AAAA,IACzB;AAAA,IACA;AAAA,EACF;AAAA,EAEA,OAAgB,QAAQ;AAAA,IACtB,cAAc,MAAM,OAAO;AAAA,MACzB,aAAa;AAAA,MACb,SAAS,KAAK,KAAK,GAAG,QAAQ,GAAG,SAAS;AAAA,IAC5C,CAAC;AAAA,IACD,WAAW,MAAM,QAAQ;AAAA,MACvB,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,IAAI,MAAM,KAAK,MAAM,iBAAgB;AACnD,SAAK,YAAY;AAEjB,SAAK,IAAI,YAAY,KAAK,KAAK,MAAM,YAAY,GAAG,UAAU,CAAC,2BAA2B;AAC1F,UAAM,eAAe,MAAM,mBAAmB,MAAM,YAAY,CAAC;AACjE,SAAK,IAAI,SAAS,aAAa,MAAM,6BAA6B;AAElE,QAAI,aAAa,WAAW,EAAG;AAE/B,QAAI,MAAM,SAAS,GAAG;AACpB,WAAK,IAAI,gCAA2B;AACpC;AAAA,IACF;AAEA,UAAM,SAAS,MAAM,iBAAiB,YAAY;AAClD,SAAK,IAAI,UAAU,OAAO,MAAM,oBAAoB,OAAO,MAAM,UAAU;AAC3E,QAAI,OAAO,SAAS,GAAG;AACrB,iBAAW,KAAK,OAAO,OAAQ,MAAK,KAAK,CAAC;AAAA,IAC5C;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/research/tail-transcript.ts"],
|
|
4
|
-
"sourcesContent": ["import { Args, Flags } from '@oclif/core';\nimport path from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport {\n loadOffsets,\n saveOffsets,\n tailJsonl,\n} from '../../lib/transcript-rate-limit-watcher.js';\nimport { postObservations } from '../../lib/research-client.js';\n\nexport default class ResearchTailTranscript extends BaseCommand {\n static override description =\n 'Read new lines from a Claude Code transcript since the last cached offset, POST any 429 events, and persist the new offset. Invoked by the session-end hook \u2014 safe to re-run.';\n static override hidden = true;\n\n static override args = {\n transcript: Args.string({\n description: 'Absolute path to a Claude Code .jsonl transcript',\n required: true,\n }),\n };\n\n static override flags = {\n 'no-post': Flags.boolean({\n description: 'Parse and advance the offset without POSTing (debug)',\n default: false,\n }),\n };\n\n async run(): Promise<void> {\n const { args, flags } = await this.parse(ResearchTailTranscript);\n const filepath = path.resolve(args.transcript);\n\n // Auth \u2014 but never crash the hook. Fall through to no-op on missing auth.\n try {\n this.requireAuth();\n } catch {\n // Hooks shouldn't break Claude Code. Silently exit.\n return;\n }\n\n const offsets = await loadOffsets();\n const fromOffset = offsets[filepath] ?? 0;\n const { observations, newOffset } = await tailJsonl(filepath, fromOffset);\n\n if (observations.length > 0 && !flags['no-post']) {\n try {\n const result = await postObservations(observations);\n if (process.env.RULEMETRIC_DEBUG) {\n this.log(`tail-transcript: posted ${result.posted}, failed ${result.failed}`);\n }\n } catch (err) {\n // Hook-safe: never throw.\n if (process.env.RULEMETRIC_DEBUG) {\n this.warn(`tail-transcript POST error: ${String(err)}`);\n }\n }\n }\n\n if (newOffset !== fromOffset) {\n offsets[filepath] = newOffset;\n await saveOffsets(offsets);\n }\n }\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;AAAA,SAAS,MAAM,aAAa;AAC5B,OAAO,UAAU;AASjB,IAAqB,yBAArB,MAAqB,gCAA+B,YAAY;AAAA,EAC9D,OAAgB,cACd;AAAA,EACF,OAAgB,SAAS;AAAA,EAEzB,OAAgB,OAAO;AAAA,IACrB,YAAY,KAAK,OAAO;AAAA,MACtB,aAAa;AAAA,MACb,UAAU;AAAA,IACZ,CAAC;AAAA,EACH;AAAA,EAEA,OAAgB,QAAQ;AAAA,IACtB,WAAW,MAAM,QAAQ;AAAA,MACvB,aAAa;AAAA,MACb,SAAS;AAAA,IACX,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,MAAqB;AACzB,UAAM,EAAE,MAAM,MAAM,IAAI,MAAM,KAAK,MAAM,uBAAsB;AAC/D,UAAM,WAAW,KAAK,QAAQ,KAAK,UAAU;AAG7C,QAAI;AACF,WAAK,YAAY;AAAA,IACnB,QAAQ;AAEN;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,YAAY;AAClC,UAAM,aAAa,QAAQ,QAAQ,KAAK;AACxC,UAAM,EAAE,cAAc,UAAU,IAAI,MAAM,UAAU,UAAU,UAAU;AAExE,QAAI,aAAa,SAAS,KAAK,CAAC,MAAM,SAAS,GAAG;AAChD,UAAI;AACF,cAAM,SAAS,MAAM,iBAAiB,YAAY;AAClD,YAAI,QAAQ,IAAI,kBAAkB;AAChC,eAAK,IAAI,2BAA2B,OAAO,MAAM,YAAY,OAAO,MAAM,EAAE;AAAA,QAC9E;AAAA,MACF,SAAS,KAAK;AAEZ,YAAI,QAAQ,IAAI,kBAAkB;AAChC,eAAK,KAAK,+BAA+B,OAAO,GAAG,CAAC,EAAE;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,cAAc,YAAY;AAC5B,cAAQ,QAAQ,IAAI;AACpB,YAAM,YAAY,OAAO;AAAA,IAC3B;AAAA,EACF;AACF;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/service/install.ts"],
|
|
4
|
-
"sourcesContent": ["import { Flags } from '@oclif/core';\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { execSync, execFileSync, spawnSync } from 'node:child_process';\nimport { homedir, userInfo } from 'node:os';\nimport { dirname, join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport { bootstrapActiveOrg } from '../../lib/active-org-refresh.js';\nimport { emitSetupEvent } from '../../lib/telemetry.js';\nimport { detectTmux } from '../../lib/detect-tmux.js';\n\n// Only these env vars are written to the plist \u2014 never leak unrelated secrets\nconst ALLOWED_ENV_VARS = new Set([\n 'SUPABASE_URL',\n 'SUPABASE_ANON_KEY',\n 'SUPABASE_SERVICE_ROLE_KEY',\n // Enables local HS256 JWT verification in the API's authMiddleware \u2014\n // without it every authed request pays a /auth/v1/user network round-trip\n // (measured p50 ~244ms) to Supabase.\n 'SUPABASE_JWT_SECRET',\n 'DATABASE_URL',\n 'RULEMETRIC_API_URL',\n 'RULEMETRIC_API_KEY',\n 'RULEMETRIC_ACCESS_TOKEN',\n // Recipient for the weekly cron_data_integrity_audit notification.\n // Without one of these, the audit cron runs but nobody is notified \u2014\n // see cron-data-integrity-audit.ts (defaults: NOTIFY_USER_ID then USER_ID).\n 'RULEMETRIC_AUDIT_NOTIFY_USER_ID',\n 'RULEMETRIC_USER_ID',\n // cron_refresh_skills needs an authenticated GitHub API budget (5000\n // req/hr) \u2014 without it the handler skips and the skills registry goes\n // stale, which makes cron_suggest_instructions mark every suggestion\n // stale on each nightly run.\n 'GITHUB_TOKEN',\n // Pool-tuning overrides so the plist generators (buildPlist / buildWorkerPlist)\n // actually honor a user-set value instead of always falling back to the pinned\n // default \u2014 without these in the allow-list the `envVars.PG_* ?? '<default>'`\n // escape hatch was silently dead. Pool-exhaustion RCA 2026-06-21.\n 'PG_POOL_MAX',\n 'PG_IDLE_TIMEOUT',\n 'PG_STATEMENT_TIMEOUT_MS',\n // Heavy-read bulkhead size + client-side query deadline \u2014 prod sets both\n // (fly.toml); without them here a local launchd install can't match prod\n // pool math. See packages/db/src/client.ts (getHeavyDb / raceWithDeadline).\n 'PG_HEAVY_POOL_MAX',\n 'PG_QUERY_DEADLINE_MS',\n // Resend transactional email. The worker (`evals agent`) sends changelog\n // announcements via `@rulemetric/email` \u2192 `sendEmail()`, which picks the\n // provider from `process.env.RESEND_API_KEY`. Without these in the allow-list\n // the installer stripped them out of the worker plist, the CLI never loads\n // `.env.local`, so the worker fell back to the `noop` provider and SILENTLY\n // dropped every announcement email. RESEND_FROM_EMAIL must be the verified\n // sender (noreply@rulemetric.com) \u2014 otherwise the provider defaults to\n // Resend's shared sandbox `onboarding@resend.dev`, which Gmail spam-filters.\n 'RESEND_API_KEY',\n 'RESEND_FROM_EMAIL',\n // Sentry error capture for the worker process. The worker (`evals agent`)\n // calls initSentry(), which now keys off DSN presence alone. Without this in\n // the allow-list the installer stripped SENTRY_DSN from the worker plist, so\n // worker crashes went unreported even when the DSN was set in .env.local /\n // ~/.config/rulemetric/env. The DSN is ingest-only (not a secret).\n 'SENTRY_DSN',\n]);\n\nconst PLIST_LABEL = 'com.rulemetric.api';\nconst PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`);\nconst WORKER_PLIST_LABEL = 'com.rulemetric.worker';\nconst WORKER_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${WORKER_PLIST_LABEL}.plist`);\nconst GATEWAY_PLIST_LABEL = 'com.rulemetric.gateway';\nconst GATEWAY_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${GATEWAY_PLIST_LABEL}.plist`);\nconst PROXY_PLIST_LABEL = 'com.rulemetric.proxy';\nconst PROXY_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${PROXY_PLIST_LABEL}.plist`);\n// Daily kickstart agent \u2014 see Phase 4c cleanup block below. Kept as a constant\n// so we can tear it down on every install if a stale copy is lying around.\nconst WORKER_RESTART_PLIST_LABEL = 'com.rulemetric.worker.restart';\nconst WORKER_RESTART_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${WORKER_RESTART_PLIST_LABEL}.plist`);\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nconst LOG_DIR = join(CONFIG_DIR, 'logs');\nconst GATEWAY_PORT = 8787;\nconst PROXY_PORT = 8788;\n// Linux systemd --user unit for the worker-only install path.\nconst WORKER_SYSTEMD_DIR = join(homedir(), '.config', 'systemd', 'user');\nconst WORKER_SYSTEMD_UNIT = 'rulemetric-worker.service';\nconst WORKER_SYSTEMD_PATH = join(WORKER_SYSTEMD_DIR, WORKER_SYSTEMD_UNIT);\n\nexport default class ServiceInstall extends BaseCommand {\n static override description =\n 'Install RuleMetric services. Default (in a repo checkout): API + worker + gateway + capture proxy as launchd agents. With --worker-only: just the job worker, targeting the globally-installed CLI \u2014 no repo, no build (works from `npm i -g @rulemetric/cli`, macOS launchd or Linux systemd).';\n\n static override examples = [\n '<%= config.bin %> service install',\n '<%= config.bin %> service install --port 3000',\n '<%= config.bin %> service install --skip-build',\n '<%= config.bin %> service install --no-proxy',\n '<%= config.bin %> service install --worker-only',\n ];\n\n static override flags = {\n port: Flags.integer({\n char: 'p',\n default: 3000,\n description: 'API server port',\n }),\n force: Flags.boolean({\n char: 'f',\n default: false,\n description: 'Overwrite existing service configuration and reclaim conflicting ports',\n }),\n 'skip-build': Flags.boolean({\n default: false,\n description: 'Skip the pre-install workspace build (API/web/cli dist must already exist)',\n }),\n 'no-proxy': Flags.boolean({\n default: false,\n description: 'Skip gateway + mitmproxy plists (just install API + worker)',\n }),\n 'worker-only': Flags.boolean({\n default: false,\n description:\n 'Install ONLY the job worker as a persistent service, targeting the globally-installed CLI (no repo checkout, no build, no API/gateway/proxy). Sources auth from ~/.config/rulemetric/env. macOS (launchd) + Linux (systemd --user).',\n }),\n };\n\n async run(): Promise<void> {\n const { flags } = await this.parse(ServiceInstall);\n const port = flags.port;\n const skipBuild = flags['skip-build'];\n const noProxy = flags['no-proxy'];\n\n // Worker-only path: a no-repo, no-build installer for end users on the\n // globally-installed CLI. Diverges so completely from the full monorepo\n // install (no findProjectRoot, no .env.local, no turbo build, no\n // API/gateway/proxy) that it gets its own method rather than threading\n // `if (!workerOnly)` through 200 lines below. Supports macOS + Linux.\n if (flags['worker-only']) {\n await this.runWorkerOnly(flags.force);\n return;\n }\n\n // The full install (API + web + gateway + proxy) is macOS-only.\n if (process.platform !== 'darwin') {\n this.error(\n 'The full service install is macOS-only (launchd). For just the job worker on Linux (or a clean npm install), use: rulemetric service install --worker-only',\n );\n }\n\n // Existing-service guard. Presence of the API plist implies a prior\n // install \u2014 we only proceed automatically when --force is set so a\n // user doesn't accidentally overwrite a working config.\n if (existsSync(PLIST_PATH) && !flags.force) {\n this.log(`Service already installed at ${PLIST_PATH}`);\n this.log('Use --force to overwrite, or run: rulemetric service status');\n return;\n }\n\n // API port conflict. Without --force we bail; with --force we'll reclaim\n // the port later in killStaleProcess so launchd can actually bind it.\n try {\n const res = await fetch(`http://localhost:${port}/health`);\n if (res.ok && !flags.force) {\n this.warn(`Port ${port} is already in use (API may be running via start.sh or in another terminal).`);\n this.warn('Stop it first with scripts/stop.sh, or use --force to take over the port.');\n return;\n }\n } catch {\n // Port free \u2014 good\n }\n\n // Find project root\n const projectRoot = this.findProjectRoot();\n if (!projectRoot) {\n this.error(\n 'Could not find RuleMetric project root. Run this command from within the momento-mori repo, or set RULEMETRIC_PROJECT_ROOT.',\n );\n }\n\n // Verify .env.local\n const envLocal = join(projectRoot, '.env.local');\n if (!existsSync(envLocal)) {\n this.error(`.env.local not found at ${envLocal}. The API needs Supabase credentials to start.`);\n }\n\n // Load env vars (filtered by ALLOWED_ENV_VARS)\n const envVars = this.guardAlwaysOnDatabaseUrl(this.loadEnvVars(projectRoot));\n\n // Resolve binaries\n const nodePath = this.findBinary('node');\n const pnpmPath = this.findBinary('pnpm');\n if (!nodePath || !pnpmPath) {\n this.error('Could not find node or pnpm. Ensure they are in your PATH.');\n }\n\n // Resolve mitmdump (required unless --no-proxy). We resolve via `which`\n // so the plist gets an absolute path \u2014 launchd's PATH at GUI session\n // start can be narrow, and pyenv shims often don't appear in it.\n let mitmdumpPath: string | null = null;\n if (!noProxy) {\n mitmdumpPath = this.findBinary('mitmdump');\n if (!mitmdumpPath) {\n this.error(\n 'mitmdump not found in PATH. Install with: pipx install mitmproxy (or brew install mitmproxy), then re-run.\\n' +\n 'Or skip the capture proxy with --no-proxy.',\n );\n }\n }\n\n // \u2500\u2500 Pre-install build \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n // Mirrors the Dockerfile prod stage: API + web + CLI all need their\n // dist/ directories to exist before launchd starts the processes. We\n // run the whole set via turbo so the cache eats the cost on re-installs.\n if (!skipBuild) {\n this.log('[..] Building workspace artifacts (api, web, cli) via turbo...');\n const buildFilters = [\n '--filter', '@rulemetric/api...',\n '--filter', '@rulemetric/web...',\n '--filter', '@rulemetric/cli',\n ];\n const build = spawnSync(pnpmPath!, ['exec', 'turbo', 'run', 'build', ...buildFilters], {\n cwd: projectRoot,\n stdio: 'inherit',\n });\n if (build.status !== 0) {\n this.error(\n `Workspace build failed (exit ${build.status}). Fix the build first, or use --skip-build if dist/ already exists.`,\n );\n }\n this.log('[OK] Build complete');\n } else {\n this.log('[..] Skipping pre-install build (--skip-build)');\n }\n\n // Verify the build artifacts launchd is about to invoke. A missing\n // entry would crash-loop the plist; surfacing it here is friendlier.\n const apiEntry = join(projectRoot, 'apps', 'api', 'dist', 'index.js');\n if (!existsSync(apiEntry)) {\n this.error(`API entry not found at ${apiEntry}. Re-run without --skip-build, or: pnpm --filter @rulemetric/api... build`);\n }\n const webIndex = join(projectRoot, 'apps', 'web', 'dist', 'index.html');\n if (!existsSync(webIndex)) {\n this.warn(`Web bundle not found at ${webIndex}. Dashboard at :${port} will 404 until you build apps/web.`);\n }\n if (!noProxy) {\n const gatewayEntry = join(projectRoot, 'apps', 'cli', 'dist', 'lib', 'gateway-entry.js');\n if (!existsSync(gatewayEntry)) {\n this.error(`Gateway entry not found at ${gatewayEntry}. Re-run without --skip-build, or: pnpm --filter @rulemetric/cli build`);\n }\n }\n\n // \u2500\u2500 Clean up conflicting PID-file-managed or orphan processes \u2500\n // hooks install + proxy start use PID files; we replace those with\n // launchd-managed lifecycles so both ports need to be free before\n // bootstrap. With --force we also clear whatever is squatting on\n // the API port.\n if (flags.force) {\n this.killStaleProcess(port, null, 'API on :' + port);\n }\n if (!noProxy) {\n this.killStaleProcess(GATEWAY_PORT, join(CONFIG_DIR, 'gateway.pid'), 'gateway');\n this.killStaleProcess(PROXY_PORT, join(CONFIG_DIR, 'proxy.pid'), 'mitmproxy');\n }\n\n // Create dirs\n mkdirSync(join(homedir(), 'Library', 'LaunchAgents'), { recursive: true });\n mkdirSync(LOG_DIR, { recursive: true });\n\n const uid = execSync('id -u', { encoding: 'utf-8' }).trim();\n\n // \u2500\u2500 Install API service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const apiPlist = this.buildPlist({ port, projectRoot, nodePath: nodePath!, pnpmPath: pnpmPath!, envVars });\n this.bootoutAndWrite(uid, PLIST_PATH, apiPlist, 'API');\n this.bootstrapPlist(uid, PLIST_PATH, 'API');\n\n // Wait for API to come up\n this.log(`[..] Waiting for API on :${port}...`);\n const apiStarted = await this.waitForApi(port, 15);\n if (apiStarted) {\n this.log(`[OK] RuleMetric API running on :${port}`);\n if (existsSync(webIndex)) {\n this.log(`[OK] Dashboard available at http://localhost:${port}`);\n }\n } else {\n this.warn('API did not respond within 15s. Check logs:');\n this.log(` tail -f ${LOG_DIR}/api-stdout.log`);\n this.log(` tail -f ${LOG_DIR}/api-stderr.log`);\n }\n\n // \u2500\u2500 Install worker service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n this.log('');\n this.log('Installing worker service...');\n // Reap detached, unsupervised evals-agent orphans before bootstrapping the\n // worker \u2014 they hold no listening port, so killStaleProcess can't find them\n // (a fleet of 4 ran 3+ days and helped saturate the pool). RCA 2026-06-21.\n this.killStaleAgents();\n const workerPlist = this.buildWorkerPlist({\n cliBin: join(projectRoot, 'apps', 'cli', 'bin', 'run.js'),\n cliDist: join(projectRoot, 'apps', 'cli', 'dist'),\n workingDir: projectRoot,\n nodePath: nodePath!,\n pnpmPath: pnpmPath!,\n envVars,\n });\n this.bootoutAndWrite(uid, WORKER_PLIST_PATH, workerPlist, 'Worker');\n this.bootstrapPlist(uid, WORKER_PLIST_PATH, 'Worker');\n\n // \u2500\u2500 Install gateway + capture-proxy services \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n if (!noProxy) {\n this.log('');\n this.log('Installing gateway + capture proxy services...');\n\n const gatewayPlist = this.buildGatewayPlist({ projectRoot, nodePath: nodePath! });\n this.bootoutAndWrite(uid, GATEWAY_PLIST_PATH, gatewayPlist, 'Gateway');\n this.bootstrapPlist(uid, GATEWAY_PLIST_PATH, 'Gateway');\n\n const proxyPlist = this.buildProxyPlist({\n projectRoot,\n mitmdumpPath: mitmdumpPath!,\n envVars,\n });\n this.bootoutAndWrite(uid, PROXY_PLIST_PATH, proxyPlist, 'Proxy');\n this.bootstrapPlist(uid, PROXY_PLIST_PATH, 'Proxy');\n }\n\n // Phase 4c cleanup: the daily worker-restart agent (04:00 kickstart)\n // existed to dodge the graphile-LISTEN-wedge class observed at ~25h\n // worker uptime. The agent path has no LISTEN socket, so the wedge\n // class is structurally gone \u2014 the daily kickstart is dead weight.\n // Bootout any leftover plist from prior installs so reinstall is a\n // truly clean state.\n if (existsSync(WORKER_RESTART_PLIST_PATH)) {\n try {\n execFileSync('launchctl', ['bootout', `gui/${uid}`, WORKER_RESTART_PLIST_PATH], { encoding: 'utf-8' });\n } catch {\n // Expected if not loaded\n }\n try {\n execFileSync('rm', ['-f', WORKER_RESTART_PLIST_PATH]);\n this.log('[OK] Removed stale daily worker-restart agent (no longer needed on agent path)');\n } catch {\n // Best-effort\n }\n }\n\n this.log('');\n if (noProxy) {\n this.log('Installed: API + worker. Gateway/proxy skipped (--no-proxy).');\n } else {\n this.log('Installed: API + worker + gateway + capture proxy.');\n }\n this.log('All services start at login, restart on crash, and pick up rebuilds via WatchPaths.');\n this.log('');\n this.log('NOTE: Env vars are baked in at install time. If you change');\n this.log('.env.local or credentials, re-run: rulemetric service install --force');\n this.log('');\n this.log('Manage with:');\n this.log(' rulemetric service status \u2014 check if running');\n this.log(' rulemetric service uninstall \u2014 remove all services');\n\n // Ensure the active-org cache reflects the user's current active_org_id\n // before the worker spawns its first capture. Belt-and-suspenders with\n // BaseCommand's guarded refresh.\n await bootstrapActiveOrg();\n\n // Soft tmux capability check \u2014 see hooks/install.ts for the same line.\n const tmux = await detectTmux();\n this.log('');\n if (tmux.available) {\n this.log(`[OK] tmux found${tmux.version ? ` (v${tmux.version})` : ''} \u2014 live send enabled`);\n } else {\n this.log('[!!] tmux not found \u2014 live send will fall back to opening Terminal. Install with: brew install tmux');\n }\n }\n\n /**\n * Worker-only install: a persistent job worker for end users on the\n * globally-installed CLI, with NO repo checkout, NO build, and NO\n * API/gateway/proxy. macOS \u2192 launchd; Linux \u2192 systemd --user. Auth is\n * sourced from ~/.config/rulemetric/env (written by `rulemetric auth\n * login`); the thin-agent worker is HTTP-only (long-polls /api/work/next),\n * so it needs only the API URL + a key/token, never DB credentials.\n */\n private async runWorkerOnly(force: boolean): Promise<void> {\n const platform = process.platform;\n if (platform !== 'darwin' && platform !== 'linux') {\n this.error(`--worker-only supports macOS (launchd) and Linux (systemd) only; detected '${platform}'.`);\n }\n\n // Resolve node + the GLOBAL CLI entry (the installed package root, NOT a\n // repo). oclif's this.config.root is the package directory; bin/run.js is\n // shipped in the published tarball (\"files\": [\"bin\", ...]).\n const nodePath = this.findBinary('node');\n if (!nodePath) {\n this.error('Could not find `node` in PATH.');\n }\n const cliBin = join(this.config.root, 'bin', 'run.js');\n if (!existsSync(cliBin)) {\n this.error(\n `Could not locate the installed CLI entry at ${cliBin}. Reinstall the CLI (e.g. npm i -g @rulemetric/cli) and re-run.`,\n );\n }\n const cliDist = join(this.config.root, 'dist');\n\n const envVars = this.loadGlobalWorkerEnv();\n if (!envVars.RULEMETRIC_API_KEY && !envVars.RULEMETRIC_ACCESS_TOKEN) {\n this.error(\n 'Not authenticated \u2014 no RULEMETRIC_API_KEY/RULEMETRIC_ACCESS_TOKEN in ~/.config/rulemetric/env.\\nRun `rulemetric auth login` first, then re-run `rulemetric service install --worker-only`.',\n );\n }\n // Default to prod so a user who never set RULEMETRIC_API_URL still works.\n if (!envVars.RULEMETRIC_API_URL) {\n envVars.RULEMETRIC_API_URL = 'https://rulemetric.com';\n }\n\n mkdirSync(LOG_DIR, { recursive: true });\n // Reap any orphaned foreground `evals agent` the user may have started by\n // hand \u2014 otherwise two workers double-claim jobs.\n this.killStaleAgents();\n\n if (platform === 'darwin') {\n if (existsSync(WORKER_PLIST_PATH) && !force) {\n this.log(`Worker already installed at ${WORKER_PLIST_PATH}.`);\n this.log('Use --force to overwrite, or run: rulemetric service status');\n return;\n }\n mkdirSync(join(homedir(), 'Library', 'LaunchAgents'), { recursive: true });\n const uid = execSync('id -u', { encoding: 'utf-8' }).trim();\n const plist = this.buildWorkerPlist({\n cliBin,\n cliDist,\n workingDir: homedir(),\n nodePath: nodePath!,\n nodeEnv: 'production',\n envVars,\n });\n this.bootoutAndWrite(uid, WORKER_PLIST_PATH, plist, 'Worker');\n this.bootstrapPlist(uid, WORKER_PLIST_PATH, 'Worker');\n this.log('');\n this.log('[OK] Worker installed (launchd). Starts at login, restarts on crash.');\n this.log(` Logs: tail -f ${join(LOG_DIR, 'worker-stdout.log')}`);\n } else {\n // Linux: systemd --user unit.\n if (existsSync(WORKER_SYSTEMD_PATH) && !force) {\n this.log(`Worker already installed at ${WORKER_SYSTEMD_PATH}.`);\n this.log(`Use --force to overwrite, or: systemctl --user status ${WORKER_SYSTEMD_UNIT}`);\n return;\n }\n mkdirSync(WORKER_SYSTEMD_DIR, { recursive: true });\n const unit = this.buildWorkerSystemdUnit({ cliBin, nodePath: nodePath!, envVars });\n writeFileSync(WORKER_SYSTEMD_PATH, unit, { mode: 0o600 });\n this.log(`[OK] Wrote ${WORKER_SYSTEMD_PATH}`);\n try {\n execFileSync('systemctl', ['--user', 'daemon-reload']);\n execFileSync('systemctl', ['--user', 'enable', '--now', WORKER_SYSTEMD_UNIT]);\n this.log('[OK] Worker enabled + started (systemctl --user).');\n // Enable linger, don't just hint it: without it a systemd --user unit is\n // torn down on logout, so the worker silently stops the moment the user\n // logs out \u2014 yet install would still report success. Best-effort (polkit\n // often allows self-linger without sudo); fall back to the manual hint.\n try {\n execFileSync('loginctl', ['enable-linger', userInfo().username]);\n this.log('[OK] Enabled linger \u2014 worker survives logout/reboot.');\n } catch {\n this.log('[!!] Could not enable linger (worker will stop on logout).');\n this.log(' Run: sudo loginctl enable-linger \"$USER\"');\n }\n this.log(` Logs: journalctl --user -u ${WORKER_SYSTEMD_UNIT} -f`);\n } catch (err) {\n this.warn(\n `Wrote the unit but \\`systemctl --user\\` failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n this.log(\n `Enable it manually: systemctl --user daemon-reload && systemctl --user enable --now ${WORKER_SYSTEMD_UNIT}`,\n );\n }\n }\n\n this.log('');\n this.log('Manage with:');\n this.log(' rulemetric service status \u2014 check if running');\n this.log(' rulemetric service uninstall \u2014 remove the worker');\n\n // Keep the active-org cache warm for the worker's first capture.\n await bootstrapActiveOrg();\n\n // Onboarding telemetry (disclosed + opt-outable) \u2014 the worker step of a\n // remote install landing.\n void emitSetupEvent('worker_started', { mode: 'worker-only' }, this.config.version);\n }\n\n /**\n * Load worker env from ~/.config/rulemetric/env only (no repo .env.local).\n * Filtered by ALLOWED_ENV_VARS, same as the full install's loader.\n */\n private loadGlobalWorkerEnv(): Record<string, string> {\n const vars: Record<string, string> = {};\n const globalEnv = join(CONFIG_DIR, 'env');\n if (existsSync(globalEnv)) {\n for (const line of readFileSync(globalEnv, 'utf-8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx > 0) {\n vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);\n }\n }\n }\n return Object.fromEntries(Object.entries(vars).filter(([k]) => ALLOWED_ENV_VARS.has(k)));\n }\n\n /** systemd --user unit for the Linux worker-only path. */\n private buildWorkerSystemdUnit(opts: {\n cliBin: string;\n nodePath: string;\n envVars: Record<string, string>;\n }): string {\n const { cliBin, nodePath, envVars } = opts;\n const env: Record<string, string> = {\n ...envVars,\n NODE_ENV: 'production',\n // systemd --user units get no shell PATH; without one the worker cannot\n // find `claude` and every eval/insights LLM call returns null. Same dir\n // set as the launchd worker plist (node siblings, native installer,\n // brew, system).\n PATH: [\n ...new Set(\n [dirname(nodePath), join(homedir(), '.local', 'bin'), '/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', '/bin'],\n ),\n ].join(':'),\n // Mirror the launchd worker's pooler bound (RCA 2026-06-21).\n PG_POOL_MAX: envVars.PG_POOL_MAX ?? '3',\n PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? '10',\n };\n // systemd Environment= values: wrap in double quotes and escape embedded\n // quotes/backslashes. Tokens/URLs carry neither, but be defensive.\n const envLines = Object.entries(env)\n .map(([k, v]) => `Environment=\"${k}=${v.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"')}\"`)\n .join('\\n');\n return `[Unit]\nDescription=RuleMetric job worker (evals agent)\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nExecStart=${nodePath} ${cliBin} evals agent\nRestart=on-failure\nRestartSec=15\n${envLines}\n\n[Install]\nWantedBy=default.target\n`;\n }\n\n private findProjectRoot(): string | null {\n // Check env var override\n if (process.env.RULEMETRIC_PROJECT_ROOT) {\n return process.env.RULEMETRIC_PROJECT_ROOT;\n }\n // Walk up from cwd looking for the monorepo marker\n let dir = process.cwd();\n for (let i = 0; i < 10; i++) {\n if (existsSync(join(dir, 'apps', 'api', 'package.json'))) {\n return dir;\n }\n const parent = join(dir, '..');\n if (parent === dir) break;\n dir = parent;\n }\n return null;\n }\n\n private findBinary(name: string): string | null {\n try {\n return execFileSync('which', [name], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] }).trim() || null;\n } catch {\n return null;\n }\n }\n\n private loadEnvVars(projectRoot: string): Record<string, string> {\n const vars: Record<string, string> = {};\n\n // Load from .env.local (Supabase credentials)\n const envLocal = join(projectRoot, '.env.local');\n if (existsSync(envLocal)) {\n for (const line of readFileSync(envLocal, 'utf-8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx > 0) {\n vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);\n }\n }\n }\n\n // Load from ~/.config/rulemetric/env (API key)\n const globalEnv = join(CONFIG_DIR, 'env');\n if (existsSync(globalEnv)) {\n for (const line of readFileSync(globalEnv, 'utf-8').split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx > 0) {\n vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);\n }\n }\n }\n\n // Only keep allowed env vars \u2014 never leak unrelated secrets\n return Object.fromEntries(\n Object.entries(vars).filter(([k]) => ALLOWED_ENV_VARS.has(k)),\n );\n }\n\n // Guard: the always-on API + worker MUST use the TRANSACTION-mode pooler\n // (port 6543). A SESSION-mode URL (:5432, 15-slot cap) baked into these\n // services was the trigger of the recurring pool saturation \u2014 a pool-cascade\n // debugging override leaked into the plists through this very installer. Auto-\n // correct the obvious session\u2192transaction port and warn loudly; warn (but do\n // not block) on a direct, non-pooler URL. Pool-exhaustion RCA 2026-06-21.\n private guardAlwaysOnDatabaseUrl(envVars: Record<string, string>): Record<string, string> {\n const url = envVars.DATABASE_URL;\n if (!url) return envVars;\n if (url.includes('pooler.supabase.com:5432')) {\n this.warn(\n 'DATABASE_URL points at the SESSION-mode pooler (:5432, 15-slot cap). The ' +\n 'always-on API/worker must use TRANSACTION mode (:6543) \u2014 auto-correcting ' +\n 'for the installed services. Fix .env.local to silence this warning.',\n );\n return { ...envVars, DATABASE_URL: url.replace('pooler.supabase.com:5432', 'pooler.supabase.com:6543') };\n }\n if (/@db\\.[a-z0-9]+\\.supabase\\.co[:/]/.test(url)) {\n this.warn(\n 'DATABASE_URL is a DIRECT database connection (db.<ref>.supabase.co), not the ' +\n 'pooler. Always-on services should use the transaction pooler ' +\n '(aws-*.pooler.supabase.com:6543) to avoid exhausting Postgres connections.',\n );\n }\n return envVars;\n }\n\n // Reap orphaned, unsupervised `evals agent` worker processes before\n // (re)bootstrapping the launchd worker. The worker holds no listening port,\n // so killStaleProcess (port/pidfile based) cannot find detached agents \u2014 a\n // fleet of 4 ran 3+ days and helped saturate the pool (RCA 2026-06-21). pgrep\n // the command line and SIGTERM every match except ourselves; the agent's own\n // single-instance lock then keeps a fresh fleet from forming.\n private killStaleAgents(): void {\n try {\n const out = execFileSync('pgrep', ['-f', 'bin/run.js evals agent'], {\n encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],\n });\n const pids = out.trim().split('\\n').filter(Boolean)\n .map((s) => parseInt(s, 10))\n .filter((p) => !isNaN(p) && p !== process.pid);\n for (const pid of pids) {\n try {\n process.kill(pid, 'SIGTERM');\n this.log(`[OK] Reaped orphan evals agent (PID ${pid})`);\n } catch {\n // already gone\n }\n }\n if (pids.length) spawnSync('sleep', ['1'], { stdio: 'ignore' });\n } catch {\n // pgrep exits non-zero when nothing matches \u2014 fine\n }\n }\n\n // Best-effort cleanup of a process that would conflict with an\n // about-to-bootstrap launchd plist. Tries two routes:\n // (a) the PID file written by the legacy spawn path\n // (gateway-lifecycle / proxy start), if pidFile is non-null\n // (b) anything actively listening on the expected port\n // Either way: SIGTERM + small grace + SIGKILL fallback. Stale PID file\n // gets unlinked.\n private killStaleProcess(port: number, pidFile: string | null, name: string): void {\n let killedAny = false;\n\n if (pidFile && existsSync(pidFile)) {\n try {\n const pidStr = readFileSync(pidFile, 'utf-8').trim();\n const pid = parseInt(pidStr, 10);\n if (!isNaN(pid)) {\n try {\n process.kill(pid, 'SIGTERM');\n killedAny = true;\n this.log(`[OK] Stopped existing ${name} (PID ${pid} from ${pidFile})`);\n } catch {\n // Already dead \u2014 fine\n }\n }\n } catch {\n // Unreadable PID file\n }\n try { execFileSync('rm', ['-f', pidFile]); } catch { /* best-effort */ }\n }\n\n // Port listener (catches orphans not in the PID file, and the foreground\n // dev API when --force is set on the API port)\n try {\n const result = execFileSync('lsof', ['-ti', `:${port}`, '-sTCP:LISTEN'], {\n encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],\n });\n const pids = result.trim().split('\\n').filter(Boolean);\n for (const pidStr of pids) {\n const pid = parseInt(pidStr, 10);\n if (!isNaN(pid)) {\n try {\n process.kill(pid, 'SIGTERM');\n killedAny = true;\n this.log(`[OK] Stopped orphan on :${port} (PID ${pid})`);\n } catch { /* already gone */ }\n }\n }\n } catch {\n // lsof exits non-zero when nothing matches \u2014 that's fine\n }\n\n if (killedAny) {\n // Brief grace so the port releases before launchd binds it\n spawnSync('sleep', ['1'], { stdio: 'ignore' });\n }\n }\n\n private bootoutAndWrite(uid: string, plistPath: string, content: string, name: string): void {\n // First install of a service has no prior load \u2192 launchctl bootout exits\n // non-zero. The messaging is inconsistent across macOS versions (\"Could\n // not find specified service\" on some, \"Boot-out failed: 5: Input/output\n // error\" on others). We treat all of these as \"nothing to unload\" and\n // only warn on errors that don't look like that family.\n const benignBootoutPatterns = [\n 'Could not find specified service',\n 'No such process',\n '3: No such process',\n '5: Input/output error',\n ];\n try {\n execFileSync('launchctl', ['bootout', `gui/${uid}`, plistPath], {\n encoding: 'utf-8',\n // Capture stderr instead of letting it print to the user's terminal.\n // The plist was either already-loaded (we unload it cleanly) or not\n // loaded (we want to swallow the error) \u2014 the user shouldn't see\n // launchctl's raw output either way.\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n this.log(`[OK] Unloaded existing ${name} service`);\n } catch (err) {\n const msg = (err as Error).message || '';\n const stderr = ((err as { stderr?: Buffer | string }).stderr ?? '').toString();\n const combined = `${msg}\\n${stderr}`;\n if (!benignBootoutPatterns.some((p) => combined.includes(p))) {\n this.warn(`Could not unload existing ${name} service: ${msg.trim()}`);\n }\n }\n writeFileSync(plistPath, content, { mode: 0o600 });\n this.log(`[OK] ${name} plist written to ${plistPath}`);\n }\n\n private bootstrapPlist(uid: string, plistPath: string, name: string): void {\n try {\n execFileSync('launchctl', ['bootstrap', `gui/${uid}`, plistPath]);\n this.log(`[OK] ${name} service loaded into launchd`);\n } catch (bootstrapErr) {\n try {\n execFileSync('launchctl', ['load', plistPath]);\n this.log(`[OK] ${name} service loaded into launchd (legacy)`);\n } catch (loadErr) {\n this.warn(\n `Could not load ${name} service.\\n` +\n ` bootstrap: ${(bootstrapErr as Error).message.trim()}\\n` +\n ` load: ${(loadErr as Error).message.trim()}`,\n );\n }\n }\n }\n\n private buildPlist(opts: {\n port: number;\n projectRoot: string;\n nodePath: string;\n pnpmPath: string;\n envVars: Record<string, string>;\n }): string {\n const { port, projectRoot, nodePath, pnpmPath, envVars } = opts;\n\n // Derive PATH from resolved binary locations (works with nvm, volta, fnm, homebrew)\n const pathDirs = [...new Set([dirname(nodePath), dirname(pnpmPath), '/usr/local/bin', '/usr/bin', '/bin'])];\n\n // Build environment dict entries. NODE_ENV=production mirrors fly.toml\n // so the bundled web build is served exactly as it is on rulemetric.com,\n // and middleware paths that branch on NODE_ENV match prod behavior\n // locally (rate-limits, error verbosity, etc.).\n const envEntries = Object.entries({\n ...envVars,\n PORT: String(port),\n NODE_ENV: 'production',\n PATH: pathDirs.join(':'),\n // Pool resilience for the always-on API (NOT the worker, which runs\n // legitimately long analytics queries). A wedged Supavisor socket with\n // the default max=3 pool and no query timeout hangs every authenticated\n // request \u2014 the 2026-06-09 login-hang. statement_timeout lets Postgres\n // cancel a stuck query so the connection returns to the pool (verified\n // honored through the transaction pooler); the larger pool adds headroom.\n // Both env-overridable. See packages/db/src/client.ts.\n PG_POOL_MAX: envVars.PG_POOL_MAX ?? '10',\n PG_STATEMENT_TIMEOUT_MS: envVars.PG_STATEMENT_TIMEOUT_MS ?? '10000',\n // Keep the pool warm so the API stops reconnecting (and re-resolving the\n // rotating pooler ELB) on nearly every request \u2014 the churn that exposed\n // it to intermittent CONNECT_TIMEOUT/ENOTFOUND. Worker/tests keep the\n // conservative 5s default. See packages/db/src/client.ts (2026-06-09).\n PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? '30',\n // Silence per-span stdout dumps on the local API \u2014 it has no OTLP\n // endpoint and a fixed (now rotated) log file; the ConsoleSpanExporter\n // grew api-stdout to 370MB / ~400k spans. Fly leaves this unset so its\n // console spans stay grep-able in the managed log stream.\n OTEL_SPAN_EXPORT: envVars.OTEL_SPAN_EXPORT ?? 'none',\n })\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n // Run the compiled API directly \u2014 same shape as the prod Dockerfile\n // CMD. Avoids tsx startup cost on every launchd restart, and serves\n // apps/web/dist from the same process so :3000 is \"API + dashboard\",\n // matching rulemetric.com. WatchPaths on apps/api/dist auto-bounces\n // the service on rebuild, so the dev cycle is: edit src \u2192 pnpm build\n // \u2192 launchd kicks the new code automatically (no manual kickstart).\n const apiEntry = join(projectRoot, 'apps', 'api', 'dist', 'index.js');\n const apiDist = join(projectRoot, 'apps', 'api', 'dist');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(nodePath)}</string>\n <string>${escapeXml(apiEntry)}</string>\n </array>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(projectRoot)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n\n <key>ThrottleInterval</key>\n <integer>10</integer>\n\n <key>WatchPaths</key>\n <array>\n <string>${escapeXml(apiDist)}</string>\n </array>\n\n <key>StandardOutPath</key>\n <string>${escapeXml(join(LOG_DIR, 'api-stdout.log'))}</string>\n\n <key>StandardErrorPath</key>\n <string>${escapeXml(join(LOG_DIR, 'api-stderr.log'))}</string>\n\n <key>ProcessType</key>\n <string>Background</string>\n</dict>\n</plist>\n`;\n }\n\n private buildWorkerPlist(opts: {\n cliBin: string;\n cliDist: string;\n workingDir: string;\n nodePath: string;\n pnpmPath?: string;\n nodeEnv?: string;\n envVars: Record<string, string>;\n }): string {\n const { cliBin, cliDist, workingDir, nodePath, pnpmPath, envVars } = opts;\n // Repo install runs as the dev host \u2192 'development'. --worker-only is an\n // end-user worker against prod \u2192 'production'.\n const nodeEnv = opts.nodeEnv ?? 'development';\n\n // dirname(nodePath) covers npm-global siblings (claude installed with the\n // same nvm node); ~/.local/bin covers the claude native installer;\n // /opt/homebrew/bin covers brew. The worker shells out to `claude` for\n // eval/insights jobs \u2014 a PATH that can't find it makes the worker claim\n // jobs it can only return null for.\n const pathDirs = [\n ...new Set(\n [\n dirname(nodePath),\n pnpmPath ? dirname(pnpmPath) : null,\n join(homedir(), '.local', 'bin'),\n '/opt/homebrew/bin',\n '/usr/local/bin',\n '/usr/bin',\n '/bin',\n ].filter((d): d is string => Boolean(d)),\n ),\n ];\n\n const envEntries = Object.entries({\n ...envVars,\n NODE_ENV: nodeEnv,\n PATH: pathDirs.join(':'),\n // Bound the worker's pooler footprint. The worker is mostly idle (HTTP\n // long-poll); an UNPINNED pool plus the previously-missing single-instance\n // guard let an orphaned fleet of agents each hold a pool and saturate\n // Supavisor's 200-client cap. A small pool + short idle timeout releases\n // sessions promptly. Pool-exhaustion RCA 2026-06-21.\n PG_POOL_MAX: envVars.PG_POOL_MAX ?? '3',\n PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? '10',\n })\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n // Run the CLI bin directly via node. Earlier this used\n // `pnpm --filter @rulemetric/cli exec rulemetric` but pnpm-exec couldn't\n // resolve the workspace bin symlink reliably under launchd, crash-looping\n // with `Command \"rulemetric\" not found`. Direct node invocation is both\n // simpler (one process less) and avoids the bin-symlink lookup entirely.\n // cliBin/cliDist are passed in: the repo install points them at\n // <repo>/apps/cli/{bin/run.js,dist}; --worker-only points them at the\n // globally-installed package (<config.root>/{bin/run.js,dist}). WatchPaths\n // on cliDist bounces the worker when the code changes \u2014 a local `pnpm\n // build` for the repo path, an `npm update -g` for the global path.\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${WORKER_PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(nodePath)}</string>\n <string>${escapeXml(cliBin)}</string>\n <string>evals</string>\n <string>agent</string>\n </array>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(workingDir)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n\n <key>ThrottleInterval</key>\n <integer>15</integer>\n\n <key>WatchPaths</key>\n <array>\n <string>${escapeXml(cliDist)}</string>\n </array>\n\n <key>StandardOutPath</key>\n <string>${escapeXml(join(LOG_DIR, 'worker-stdout.log'))}</string>\n\n <key>StandardErrorPath</key>\n <string>${escapeXml(join(LOG_DIR, 'worker-stderr.log'))}</string>\n\n <key>ProcessType</key>\n <string>Background</string>\n</dict>\n</plist>\n`;\n }\n\n // CONNECT-proxy router that fronts mitmproxy on :8787. Always-on so\n // .claude/settings.json's HTTPS_PROXY pointer never resolves to a dead\n // socket (which would break Claude Code entirely \u2014 see CLAUDE.md).\n // NOT watched on rebuild: bouncing the gateway mid-request would drop\n // in-flight streaming responses. Gateway code changes rarely; on\n // upgrade, the user can `launchctl kickstart gui/<uid>/com.rulemetric.gateway`.\n private buildGatewayPlist(opts: {\n projectRoot: string;\n nodePath: string;\n }): string {\n const { projectRoot, nodePath } = opts;\n const gatewayEntry = join(projectRoot, 'apps', 'cli', 'dist', 'lib', 'gateway-entry.js');\n const cliBin = join(projectRoot, 'apps', 'cli', 'bin', 'run.js');\n\n const pathDirs = [...new Set([dirname(nodePath), '/usr/local/bin', '/usr/bin', '/bin'])];\n\n // Gateway only needs PATH + the CLI metadata used for drift detection\n // (gateway.ts:43 \u2014 when the addon's on-disk version file diverges from\n // the gateway's runtime version, it re-invokes the CLI to restart\n // mitmproxy). It does NOT need Supabase/auth env vars.\n const env = {\n RULEMETRIC_CLI_VERSION: this.config.version,\n RULEMETRIC_CLI_ENTRY: cliBin,\n PATH: pathDirs.join(':'),\n };\n\n const envEntries = Object.entries(env)\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${GATEWAY_PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(nodePath)}</string>\n <string>${escapeXml(gatewayEntry)}</string>\n </array>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(projectRoot)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n\n <key>ThrottleInterval</key>\n <integer>10</integer>\n\n <key>StandardOutPath</key>\n <string>${escapeXml(join(LOG_DIR, 'gateway-stdout.log'))}</string>\n\n <key>StandardErrorPath</key>\n <string>${escapeXml(join(LOG_DIR, 'gateway-stderr.log'))}</string>\n\n <key>ProcessType</key>\n <string>Background</string>\n</dict>\n</plist>\n`;\n }\n\n // mitmproxy with the RuleMetric capture addon. Same invocation as\n // `rulemetric proxy start` (see commands/proxy/start.ts:308) so the\n // launchd path and the CLI path are interchangeable. We don't WatchPath\n // the Python addon \u2014 mitmdump has its own addon reload that handles\n // edits without a full process restart.\n private buildProxyPlist(opts: {\n projectRoot: string;\n mitmdumpPath: string;\n envVars: Record<string, string>;\n }): string {\n const { projectRoot, mitmdumpPath, envVars } = opts;\n const addonPath = join(projectRoot, 'packages', 'proxy', 'addon', 'rulemetric_addon.py');\n\n const pathDirs = [...new Set([dirname(mitmdumpPath), '/usr/local/bin', '/usr/bin', '/bin'])];\n\n // Proxy needs:\n // - auth env vars (so its reporter can POST events to the API)\n // - RULEMETRIC_PROJECT_PATH (writes session-meta + addon-version files)\n // - RULEMETRIC_CLI_VERSION (gateway uses this to detect drift)\n const env = {\n ...envVars,\n RULEMETRIC_PROJECT_PATH: projectRoot,\n RULEMETRIC_CLI_VERSION: this.config.version,\n PATH: pathDirs.join(':'),\n };\n\n const envEntries = Object.entries(env)\n .map(([k, v]) => ` <key>${escapeXml(k)}</key>\\n <string>${escapeXml(v)}</string>`)\n .join('\\n');\n\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${PROXY_PLIST_LABEL}</string>\n\n <key>ProgramArguments</key>\n <array>\n <string>${escapeXml(mitmdumpPath)}</string>\n <string>--listen-port</string>\n <string>${PROXY_PORT}</string>\n <string>-s</string>\n <string>${escapeXml(addonPath)}</string>\n <string>--set</string>\n <string>console_eventlog_verbosity=info</string>\n </array>\n\n <key>WorkingDirectory</key>\n <string>${escapeXml(projectRoot)}</string>\n\n <key>EnvironmentVariables</key>\n <dict>\n${envEntries}\n </dict>\n\n <key>RunAtLoad</key>\n <true/>\n\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n\n <key>ThrottleInterval</key>\n <integer>15</integer>\n\n <key>StandardOutPath</key>\n <string>${escapeXml(join(LOG_DIR, 'proxy-stdout.log'))}</string>\n\n <key>StandardErrorPath</key>\n <string>${escapeXml(join(LOG_DIR, 'proxy-stderr.log'))}</string>\n\n <key>ProcessType</key>\n <string>Background</string>\n</dict>\n</plist>\n`;\n }\n\n private async waitForApi(port: number, timeoutSecs: number): Promise<boolean> {\n let lastError: string | null = null;\n for (let i = 0; i < timeoutSecs; i++) {\n try {\n const res = await fetch(`http://localhost:${port}/health`);\n if (res.ok) return true;\n lastError = `health check returned ${res.status}`;\n } catch (err) {\n lastError = (err as Error).message;\n }\n await new Promise(r => setTimeout(r, 1000));\n }\n if (lastError) {\n this.warn(`Last health check error: ${lastError}`);\n }\n return false;\n }\n}\n\nfunction escapeXml(s: string): string {\n return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/\"/g, '"');\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/service/status.ts"],
|
|
4
|
-
"sourcesContent": ["import { existsSync, readFileSync } from 'node:fs';\nimport { execFileSync, execSync } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\nimport { detectTmux } from '../../lib/detect-tmux.js';\n\nconst PLIST_LABEL = 'com.rulemetric.api';\nconst PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`);\nconst WORKER_PLIST_LABEL = 'com.rulemetric.worker';\nconst WORKER_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${WORKER_PLIST_LABEL}.plist`);\nconst GATEWAY_PLIST_LABEL = 'com.rulemetric.gateway';\nconst GATEWAY_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${GATEWAY_PLIST_LABEL}.plist`);\nconst PROXY_PLIST_LABEL = 'com.rulemetric.proxy';\nconst PROXY_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', `${PROXY_PLIST_LABEL}.plist`);\nconst LOG_DIR = join(homedir(), '.config', 'rulemetric', 'logs');\nconst GATEWAY_PORT = 8787;\nconst PROXY_PORT = 8788;\n\ninterface ServiceState {\n installed: boolean;\n launchdStatus: string;\n pid: string | null;\n}\n\nexport default class ServiceStatus extends BaseCommand {\n static override description = 'Check the status of all RuleMetric launchd services';\n\n async run(): Promise<void> {\n if (process.platform !== 'darwin') {\n this.error('Service status is currently macOS-only.');\n }\n\n // Nothing installed at all \u2014 short-circuit with install hint.\n if (!existsSync(PLIST_PATH)) {\n this.log('Service: not installed');\n this.log('Run: rulemetric service install');\n return;\n }\n\n // \u2500\u2500 API \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const apiState = this.readServiceState(PLIST_LABEL, PLIST_PATH);\n const apiPort = this.readApiPort(PLIST_PATH);\n const apiHealth = await this.probeApiHealth(apiPort);\n const dashboardServed = await this.probeDashboard(apiPort);\n\n // \u2500\u2500 Worker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const workerState = this.readServiceState(WORKER_PLIST_LABEL, WORKER_PLIST_PATH);\n const workerMode = this.readWorkerMode();\n\n // \u2500\u2500 Gateway \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const gatewayState = this.readServiceState(GATEWAY_PLIST_LABEL, GATEWAY_PLIST_PATH);\n const gatewayListening = this.isPortListening(GATEWAY_PORT);\n\n // \u2500\u2500 Proxy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const proxyState = this.readServiceState(PROXY_PLIST_LABEL, PROXY_PLIST_PATH);\n const proxyListening = this.isPortListening(PROXY_PORT);\n\n // \u2500\u2500 Render \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const apiDetail = `${apiHealth} (port ${apiPort})${dashboardServed ? ' \u2014 dashboard served' : ''}`;\n this.log(`API: ${this.fmtState(apiState)} \u2014 ${apiDetail}`);\n // Post-Phase 4c there's only one mode (agent). Loudly flag any stale\n // plist still pointing at the (now-deleted) `evals listen` command \u2014\n // it would crash-loop. Operator needs to re-run `service install`.\n const modeSuffix = workerMode === 'listen'\n ? ' [mode: listen \u2014 DEPRECATED, run `rulemetric service install --force` to upgrade]'\n : '';\n this.log(`Worker: ${this.fmtState(workerState)}${modeSuffix}`);\n this.log(`Gateway: ${this.fmtState(gatewayState)} \u2014 ${gatewayListening ? `listening :${GATEWAY_PORT}` : `NOT listening :${GATEWAY_PORT}`}`);\n this.log(`Proxy: ${this.fmtState(proxyState)} \u2014 ${proxyListening ? `listening :${PROXY_PORT}` : `NOT listening :${PROXY_PORT}`}`);\n\n const tmux = await detectTmux();\n this.log(`Tmux: ${tmux.available ? (tmux.version ? `v${tmux.version}` : 'available') : 'not found'}`);\n this.log('');\n this.log(`Plists: ${PLIST_PATH}`);\n this.log(` ${WORKER_PLIST_PATH}`);\n if (gatewayState.installed) this.log(` ${GATEWAY_PLIST_PATH}`);\n if (proxyState.installed) this.log(` ${PROXY_PLIST_PATH}`);\n this.log(`Logs: ${LOG_DIR}/{api,worker,gateway,proxy}-{stdout,stderr}.log`);\n\n const allHealthy =\n apiState.launchdStatus === 'running' &&\n apiHealth === 'healthy' &&\n workerState.launchdStatus === 'running' &&\n (!gatewayState.installed || (gatewayState.launchdStatus === 'running' && gatewayListening)) &&\n (!proxyState.installed || (proxyState.launchdStatus === 'running' && proxyListening));\n\n this.log('');\n if (allHealthy) {\n this.log('All good \u2014 sessions tracked, jobs processed, capture pipeline live.');\n } else {\n if (apiState.launchdStatus !== 'running') {\n this.log('API is not running. Try:');\n this.log(` launchctl kickstart gui/$(id -u)/${PLIST_LABEL}`);\n }\n if (workerState.installed && workerState.launchdStatus !== 'running') {\n this.log('Worker is not running. Try:');\n this.log(` launchctl kickstart gui/$(id -u)/${WORKER_PLIST_LABEL}`);\n }\n if (gatewayState.installed && (gatewayState.launchdStatus !== 'running' || !gatewayListening)) {\n this.log('Gateway is not running. Try:');\n this.log(` launchctl kickstart gui/$(id -u)/${GATEWAY_PLIST_LABEL}`);\n }\n if (proxyState.installed && (proxyState.launchdStatus !== 'running' || !proxyListening)) {\n this.log('Proxy is not running. Try:');\n this.log(` launchctl kickstart gui/$(id -u)/${PROXY_PLIST_LABEL}`);\n }\n if (!gatewayState.installed || !proxyState.installed) {\n this.log('Gateway/proxy not installed. Run: rulemetric service install --force');\n }\n }\n }\n\n private readServiceState(label: string, plistPath: string): ServiceState {\n const installed = existsSync(plistPath);\n if (!installed) {\n return { installed: false, launchdStatus: 'not installed', pid: null };\n }\n try {\n const output = execSync(`launchctl list ${label} 2>/dev/null`, { encoding: 'utf-8' });\n const pidMatch = output.match(/\"PID\"\\s*=\\s*(\\d+)/);\n const statusMatch = output.match(/\"LastExitStatus\"\\s*=\\s*(\\d+)/);\n if (pidMatch) {\n return { installed: true, launchdStatus: 'running', pid: pidMatch[1] };\n }\n if (statusMatch) {\n const code = statusMatch[1];\n return {\n installed: true,\n launchdStatus: code === '0' ? 'stopped (clean exit)' : `stopped (exit ${code})`,\n pid: null,\n };\n }\n return { installed: true, launchdStatus: 'loaded', pid: null };\n } catch {\n return { installed: true, launchdStatus: 'not loaded', pid: null };\n }\n }\n\n private fmtState(state: ServiceState): string {\n if (!state.installed) return 'not installed';\n return `${state.launchdStatus}${state.pid ? ` (PID ${state.pid})` : ''}`;\n }\n\n private readApiPort(plistPath: string): number {\n try {\n const plistContent = readFileSync(plistPath, 'utf-8');\n const portMatch = plistContent.match(/<key>PORT<\\/key>\\s*<string>(\\d+)<\\/string>/);\n if (portMatch) return Number.parseInt(portMatch[1]);\n } catch {\n // fall through to default\n }\n return 3000;\n }\n\n // Returns 'agent' | 'listen' | null. Used to flag the stale Phase 4c\n // mode where the plist still says `evals listen` and would crash-loop.\n private readWorkerMode(): 'agent' | 'listen' | null {\n if (!existsSync(WORKER_PLIST_PATH)) return null;\n try {\n const plistContent = readFileSync(WORKER_PLIST_PATH, 'utf-8');\n const match = plistContent.match(/<string>evals<\\/string>\\s*<string>(listen|agent)<\\/string>/);\n if (match) return match[1] as 'agent' | 'listen';\n } catch {\n // Best-effort\n }\n return null;\n }\n\n private async probeApiHealth(port: number): Promise<string> {\n try {\n const res = await fetch(`http://localhost:${port}/health`);\n if (res.ok) return 'healthy';\n return `unhealthy (${res.status})`;\n } catch {\n return 'unreachable';\n }\n }\n\n // Probe `/` on the API. Returns true if it serves HTML (= web bundle\n // is mounted). Differentiates \"API runs but dashboard 404s because\n // apps/web/dist is missing\" from \"API serves dashboard properly\".\n private async probeDashboard(port: number): Promise<boolean> {\n try {\n const res = await fetch(`http://localhost:${port}/`);\n if (!res.ok) return false;\n const contentType = res.headers.get('content-type') ?? '';\n return contentType.includes('text/html');\n } catch {\n return false;\n }\n }\n\n private isPortListening(port: number): boolean {\n try {\n const out = execFileSync('lsof', ['-ti', `:${port}`, '-sTCP:LISTEN'], {\n encoding: 'utf-8', stdio: ['ignore', 'pipe', 'ignore'],\n });\n return out.trim().length > 0;\n } catch {\n return false;\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../../src/commands/service/uninstall.ts"],
|
|
4
|
-
"sourcesContent": ["import { existsSync, unlinkSync } from 'node:fs';\nimport { execSync, execFileSync } from 'node:child_process';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { BaseCommand } from '../../base-command.js';\n\n// Every plist label that `service install` may have written. Keeping these in\n// one ordered list lets uninstall march through them with the same teardown\n// logic; if install ever adds a fifth service, this is the only file that\n// needs to learn about it.\nconst SERVICES: Array<{ label: string; description: string }> = [\n { label: 'com.rulemetric.api', description: 'API' },\n { label: 'com.rulemetric.worker', description: 'Worker' },\n { label: 'com.rulemetric.gateway', description: 'Gateway' },\n { label: 'com.rulemetric.proxy', description: 'Proxy' },\n // Legacy daily worker-restart agent \u2014 install removes any stale copy,\n // but uninstall still cleans it up so a user running uninstall on an\n // older box gets a fully clean state.\n { label: 'com.rulemetric.worker.restart', description: 'Worker restart agent' },\n];\n\nfunction plistPathFor(label: string): string {\n return join(homedir(), 'Library', 'LaunchAgents', `${label}.plist`);\n}\n\n// Linux systemd --user unit written by `service install --worker-only`. Kept in\n// sync with the constants in install.ts.\nconst WORKER_SYSTEMD_UNIT = 'rulemetric-worker.service';\nconst WORKER_SYSTEMD_PATH = join(homedir(), '.config', 'systemd', 'user', WORKER_SYSTEMD_UNIT);\n\nexport default class ServiceUninstall extends BaseCommand {\n static override description = 'Remove RuleMetric services (macOS launchd: API/worker/gateway/proxy; Linux: the --worker-only systemd unit)';\n\n async run(): Promise<void> {\n // Linux: only the worker-only systemd unit can exist here.\n if (process.platform === 'linux') {\n this.uninstallLinuxWorker();\n return;\n }\n if (process.platform !== 'darwin') {\n this.error('Service uninstall supports macOS (launchd) and Linux (systemd) only.');\n }\n\n const installed = SERVICES.filter((s) => existsSync(plistPathFor(s.label)));\n if (installed.length === 0) {\n this.log('No RuleMetric services are installed.');\n return;\n }\n\n const uid = execSync('id -u', { encoding: 'utf-8' }).trim();\n\n for (const svc of installed) {\n const path = plistPathFor(svc.label);\n this.stopService(uid, path, svc.label, svc.description);\n this.removePlist(path, svc.description);\n }\n\n this.log('');\n this.log('All RuleMetric services removed. They will no longer start automatically.');\n this.log('Use scripts/start.sh (dev) or `rulemetric service install` (always-on) to bring them back.');\n }\n\n private stopService(uid: string, plistPath: string, label: string, description: string): void {\n try {\n execFileSync('launchctl', ['bootout', `gui/${uid}`, plistPath], { encoding: 'utf-8' });\n this.log(`[OK] ${description} stopped`);\n return;\n } catch {\n // Fall through to legacy unload\n }\n try {\n execFileSync('launchctl', ['unload', plistPath], { encoding: 'utf-8' });\n this.log(`[OK] ${description} stopped (legacy)`);\n return;\n } catch {\n // Verify whether it's actually running. launchctl list returns the\n // current state; if there's no PID, the service wasn't running and\n // both bootout/unload no-op'd legitimately.\n try {\n const output = execSync(`launchctl list ${label} 2>&1`, { encoding: 'utf-8' });\n if (output.includes('PID')) {\n this.warn(`${description} appears to still be running. Try manually:`);\n this.warn(` launchctl bootout gui/${uid} \"${plistPath}\"`);\n } else {\n this.log(`[OK] ${description} was not running`);\n }\n } catch {\n this.log(`[OK] ${description} was not running`);\n }\n }\n }\n\n private removePlist(plistPath: string, description: string): void {\n try {\n unlinkSync(plistPath);\n this.log(`[OK] Removed ${plistPath}`);\n } catch (err) {\n this.warn(`Could not remove ${description} plist: ${(err as Error).message}. Try: rm \"${plistPath}\"`);\n }\n }\n\n private uninstallLinuxWorker(): void {\n if (!existsSync(WORKER_SYSTEMD_PATH)) {\n this.log('No RuleMetric worker is installed (no systemd unit found).');\n return;\n }\n try {\n execFileSync('systemctl', ['--user', 'disable', '--now', WORKER_SYSTEMD_UNIT], { encoding: 'utf-8' });\n this.log('[OK] Worker stopped + disabled (systemctl --user)');\n } catch (err) {\n this.warn(`Could not disable the worker unit: ${(err as Error).message}`);\n this.warn(` systemctl --user disable --now ${WORKER_SYSTEMD_UNIT}`);\n }\n try {\n unlinkSync(WORKER_SYSTEMD_PATH);\n this.log(`[OK] Removed ${WORKER_SYSTEMD_PATH}`);\n } catch (err) {\n this.warn(`Could not remove ${WORKER_SYSTEMD_PATH}: ${(err as Error).message}. Try: rm \"${WORKER_SYSTEMD_PATH}\"`);\n }\n try {\n execFileSync('systemctl', ['--user', 'daemon-reload'], { encoding: 'utf-8' });\n } catch {\n // best-effort\n }\n this.log('');\n this.log('RuleMetric worker removed. It will no longer start automatically.');\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|