@rulemetric/cli 0.7.2 → 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 -309
- 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 -781
- 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 -46
- 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 -15
- 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 -14
- 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 -11
- 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 -17
- package/dist/lib/manual-tasks-meta.js +1 -8
- package/dist/lib/manual-tasks.js +1 -79
- 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 +8 -7
- 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-7DULPSFU.js +0 -299
- package/dist/chunk-7DULPSFU.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-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-FZKLLNDS.js +0 -228
- package/dist/chunk-FZKLLNDS.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-OK3I555A.js +0 -243
- package/dist/chunk-OK3I555A.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-PMI56ED5.js +0 -167
- package/dist/chunk-PMI56ED5.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-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-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-YOBA3NAM.js +0 -55
- package/dist/chunk-YOBA3NAM.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,688 +1,48 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
BaseCommand
|
|
7
|
-
} from "../../chunk-2HK7RGZB.js";
|
|
8
|
-
import "../../chunk-R56PH7YE.js";
|
|
9
|
-
import {
|
|
10
|
-
emitSetupEvent
|
|
11
|
-
} from "../../chunk-GQNJO2P2.js";
|
|
12
|
-
import {
|
|
13
|
-
bootstrapActiveOrg
|
|
14
|
-
} from "../../chunk-37LN5O7M.js";
|
|
15
|
-
import "../../chunk-EKJ2CABV.js";
|
|
16
|
-
import "../../chunk-ZUJBM6HE.js";
|
|
17
|
-
import "../../chunk-W2YERO7E.js";
|
|
18
|
-
import "../../chunk-NSBPE2FW.js";
|
|
19
|
-
|
|
20
|
-
// src/commands/service/install.ts
|
|
21
|
-
import { Flags } from "@oclif/core";
|
|
22
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
23
|
-
import { execSync, execFileSync, spawnSync } from "node:child_process";
|
|
24
|
-
import { homedir, userInfo } from "node:os";
|
|
25
|
-
import { dirname, join } from "node:path";
|
|
26
|
-
var ALLOWED_ENV_VARS = /* @__PURE__ */ new Set([
|
|
27
|
-
"SUPABASE_URL",
|
|
28
|
-
"SUPABASE_ANON_KEY",
|
|
29
|
-
"SUPABASE_SERVICE_ROLE_KEY",
|
|
30
|
-
// Enables local HS256 JWT verification in the API's authMiddleware —
|
|
31
|
-
// without it every authed request pays a /auth/v1/user network round-trip
|
|
32
|
-
// (measured p50 ~244ms) to Supabase.
|
|
33
|
-
"SUPABASE_JWT_SECRET",
|
|
34
|
-
"DATABASE_URL",
|
|
35
|
-
"RULEMETRIC_API_URL",
|
|
36
|
-
"RULEMETRIC_API_KEY",
|
|
37
|
-
"RULEMETRIC_ACCESS_TOKEN",
|
|
38
|
-
// Recipient for the weekly cron_data_integrity_audit notification.
|
|
39
|
-
// Without one of these, the audit cron runs but nobody is notified —
|
|
40
|
-
// see cron-data-integrity-audit.ts (defaults: NOTIFY_USER_ID then USER_ID).
|
|
41
|
-
"RULEMETRIC_AUDIT_NOTIFY_USER_ID",
|
|
42
|
-
"RULEMETRIC_USER_ID",
|
|
43
|
-
// cron_refresh_skills needs an authenticated GitHub API budget (5000
|
|
44
|
-
// req/hr) — without it the handler skips and the skills registry goes
|
|
45
|
-
// stale, which makes cron_suggest_instructions mark every suggestion
|
|
46
|
-
// stale on each nightly run.
|
|
47
|
-
"GITHUB_TOKEN",
|
|
48
|
-
// Pool-tuning overrides so the plist generators (buildPlist / buildWorkerPlist)
|
|
49
|
-
// actually honor a user-set value instead of always falling back to the pinned
|
|
50
|
-
// default — without these in the allow-list the `envVars.PG_* ?? '<default>'`
|
|
51
|
-
// escape hatch was silently dead. Pool-exhaustion RCA 2026-06-21.
|
|
52
|
-
"PG_POOL_MAX",
|
|
53
|
-
"PG_IDLE_TIMEOUT",
|
|
54
|
-
"PG_STATEMENT_TIMEOUT_MS",
|
|
55
|
-
// Heavy-read bulkhead size + client-side query deadline — prod sets both
|
|
56
|
-
// (fly.toml); without them here a local launchd install can't match prod
|
|
57
|
-
// pool math. See packages/db/src/client.ts (getHeavyDb / raceWithDeadline).
|
|
58
|
-
"PG_HEAVY_POOL_MAX",
|
|
59
|
-
"PG_QUERY_DEADLINE_MS",
|
|
60
|
-
// Resend transactional email. The worker (`evals agent`) sends changelog
|
|
61
|
-
// announcements via `@rulemetric/email` → `sendEmail()`, which picks the
|
|
62
|
-
// provider from `process.env.RESEND_API_KEY`. Without these in the allow-list
|
|
63
|
-
// the installer stripped them out of the worker plist, the CLI never loads
|
|
64
|
-
// `.env.local`, so the worker fell back to the `noop` provider and SILENTLY
|
|
65
|
-
// dropped every announcement email. RESEND_FROM_EMAIL must be the verified
|
|
66
|
-
// sender (noreply@rulemetric.com) — otherwise the provider defaults to
|
|
67
|
-
// Resend's shared sandbox `onboarding@resend.dev`, which Gmail spam-filters.
|
|
68
|
-
"RESEND_API_KEY",
|
|
69
|
-
"RESEND_FROM_EMAIL",
|
|
70
|
-
// Sentry error capture for the worker process. The worker (`evals agent`)
|
|
71
|
-
// calls initSentry(), which now keys off DSN presence alone. Without this in
|
|
72
|
-
// the allow-list the installer stripped SENTRY_DSN from the worker plist, so
|
|
73
|
-
// worker crashes went unreported even when the DSN was set in .env.local /
|
|
74
|
-
// ~/.config/rulemetric/env. The DSN is ingest-only (not a secret).
|
|
75
|
-
"SENTRY_DSN"
|
|
76
|
-
]);
|
|
77
|
-
var PLIST_LABEL = "com.rulemetric.api";
|
|
78
|
-
var PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
79
|
-
var WORKER_PLIST_LABEL = "com.rulemetric.worker";
|
|
80
|
-
var WORKER_PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${WORKER_PLIST_LABEL}.plist`);
|
|
81
|
-
var GATEWAY_PLIST_LABEL = "com.rulemetric.gateway";
|
|
82
|
-
var GATEWAY_PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${GATEWAY_PLIST_LABEL}.plist`);
|
|
83
|
-
var PROXY_PLIST_LABEL = "com.rulemetric.proxy";
|
|
84
|
-
var PROXY_PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${PROXY_PLIST_LABEL}.plist`);
|
|
85
|
-
var WORKER_RESTART_PLIST_LABEL = "com.rulemetric.worker.restart";
|
|
86
|
-
var WORKER_RESTART_PLIST_PATH = join(homedir(), "Library", "LaunchAgents", `${WORKER_RESTART_PLIST_LABEL}.plist`);
|
|
87
|
-
var CONFIG_DIR = join(homedir(), ".config", "rulemetric");
|
|
88
|
-
var LOG_DIR = join(CONFIG_DIR, "logs");
|
|
89
|
-
var GATEWAY_PORT = 8787;
|
|
90
|
-
var PROXY_PORT = 8788;
|
|
91
|
-
var WORKER_SYSTEMD_DIR = join(homedir(), ".config", "systemd", "user");
|
|
92
|
-
var WORKER_SYSTEMD_UNIT = "rulemetric-worker.service";
|
|
93
|
-
var WORKER_SYSTEMD_PATH = join(WORKER_SYSTEMD_DIR, WORKER_SYSTEMD_UNIT);
|
|
94
|
-
var ServiceInstall = class _ServiceInstall extends BaseCommand {
|
|
95
|
-
static description = "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).";
|
|
96
|
-
static examples = [
|
|
97
|
-
"<%= config.bin %> service install",
|
|
98
|
-
"<%= config.bin %> service install --port 3000",
|
|
99
|
-
"<%= config.bin %> service install --skip-build",
|
|
100
|
-
"<%= config.bin %> service install --no-proxy",
|
|
101
|
-
"<%= config.bin %> service install --worker-only"
|
|
102
|
-
];
|
|
103
|
-
static flags = {
|
|
104
|
-
port: Flags.integer({
|
|
105
|
-
char: "p",
|
|
106
|
-
default: 3e3,
|
|
107
|
-
description: "API server port"
|
|
108
|
-
}),
|
|
109
|
-
force: Flags.boolean({
|
|
110
|
-
char: "f",
|
|
111
|
-
default: false,
|
|
112
|
-
description: "Overwrite existing service configuration and reclaim conflicting ports"
|
|
113
|
-
}),
|
|
114
|
-
"skip-build": Flags.boolean({
|
|
115
|
-
default: false,
|
|
116
|
-
description: "Skip the pre-install workspace build (API/web/cli dist must already exist)"
|
|
117
|
-
}),
|
|
118
|
-
"no-proxy": Flags.boolean({
|
|
119
|
-
default: false,
|
|
120
|
-
description: "Skip gateway + mitmproxy plists (just install API + worker)"
|
|
121
|
-
}),
|
|
122
|
-
"worker-only": Flags.boolean({
|
|
123
|
-
default: false,
|
|
124
|
-
description: "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)."
|
|
125
|
-
})
|
|
126
|
-
};
|
|
127
|
-
async run() {
|
|
128
|
-
const { flags } = await this.parse(_ServiceInstall);
|
|
129
|
-
const port = flags.port;
|
|
130
|
-
const skipBuild = flags["skip-build"];
|
|
131
|
-
const noProxy = flags["no-proxy"];
|
|
132
|
-
if (flags["worker-only"]) {
|
|
133
|
-
await this.runWorkerOnly(flags.force);
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
if (process.platform !== "darwin") {
|
|
137
|
-
this.error(
|
|
138
|
-
"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"
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
if (existsSync(PLIST_PATH) && !flags.force) {
|
|
142
|
-
this.log(`Service already installed at ${PLIST_PATH}`);
|
|
143
|
-
this.log("Use --force to overwrite, or run: rulemetric service status");
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
try {
|
|
147
|
-
const res = await fetch(`http://localhost:${port}/health`);
|
|
148
|
-
if (res.ok && !flags.force) {
|
|
149
|
-
this.warn(`Port ${port} is already in use (API may be running via start.sh or in another terminal).`);
|
|
150
|
-
this.warn("Stop it first with scripts/stop.sh, or use --force to take over the port.");
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
} catch {
|
|
154
|
-
}
|
|
155
|
-
const projectRoot = this.findProjectRoot();
|
|
156
|
-
if (!projectRoot) {
|
|
157
|
-
this.error(
|
|
158
|
-
"Could not find RuleMetric project root. Run this command from within the momento-mori repo, or set RULEMETRIC_PROJECT_ROOT."
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
const envLocal = join(projectRoot, ".env.local");
|
|
162
|
-
if (!existsSync(envLocal)) {
|
|
163
|
-
this.error(`.env.local not found at ${envLocal}. The API needs Supabase credentials to start.`);
|
|
164
|
-
}
|
|
165
|
-
const envVars = this.guardAlwaysOnDatabaseUrl(this.loadEnvVars(projectRoot));
|
|
166
|
-
const nodePath = this.findBinary("node");
|
|
167
|
-
const pnpmPath = this.findBinary("pnpm");
|
|
168
|
-
if (!nodePath || !pnpmPath) {
|
|
169
|
-
this.error("Could not find node or pnpm. Ensure they are in your PATH.");
|
|
170
|
-
}
|
|
171
|
-
let mitmdumpPath = null;
|
|
172
|
-
if (!noProxy) {
|
|
173
|
-
mitmdumpPath = this.findBinary("mitmdump");
|
|
174
|
-
if (!mitmdumpPath) {
|
|
175
|
-
this.error(
|
|
176
|
-
"mitmdump not found in PATH. Install with: pipx install mitmproxy (or brew install mitmproxy), then re-run.\nOr skip the capture proxy with --no-proxy."
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
if (!skipBuild) {
|
|
181
|
-
this.log("[..] Building workspace artifacts (api, web, cli) via turbo...");
|
|
182
|
-
const buildFilters = [
|
|
183
|
-
"--filter",
|
|
184
|
-
"@rulemetric/api...",
|
|
185
|
-
"--filter",
|
|
186
|
-
"@rulemetric/web...",
|
|
187
|
-
"--filter",
|
|
188
|
-
"@rulemetric/cli"
|
|
189
|
-
];
|
|
190
|
-
const build = spawnSync(pnpmPath, ["exec", "turbo", "run", "build", ...buildFilters], {
|
|
191
|
-
cwd: projectRoot,
|
|
192
|
-
stdio: "inherit"
|
|
193
|
-
});
|
|
194
|
-
if (build.status !== 0) {
|
|
195
|
-
this.error(
|
|
196
|
-
`Workspace build failed (exit ${build.status}). Fix the build first, or use --skip-build if dist/ already exists.`
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
this.log("[OK] Build complete");
|
|
200
|
-
} else {
|
|
201
|
-
this.log("[..] Skipping pre-install build (--skip-build)");
|
|
202
|
-
}
|
|
203
|
-
const apiEntry = join(projectRoot, "apps", "api", "dist", "index.js");
|
|
204
|
-
if (!existsSync(apiEntry)) {
|
|
205
|
-
this.error(`API entry not found at ${apiEntry}. Re-run without --skip-build, or: pnpm --filter @rulemetric/api... build`);
|
|
206
|
-
}
|
|
207
|
-
const webIndex = join(projectRoot, "apps", "web", "dist", "index.html");
|
|
208
|
-
if (!existsSync(webIndex)) {
|
|
209
|
-
this.warn(`Web bundle not found at ${webIndex}. Dashboard at :${port} will 404 until you build apps/web.`);
|
|
210
|
-
}
|
|
211
|
-
if (!noProxy) {
|
|
212
|
-
const gatewayEntry = join(projectRoot, "apps", "cli", "dist", "lib", "gateway-entry.js");
|
|
213
|
-
if (!existsSync(gatewayEntry)) {
|
|
214
|
-
this.error(`Gateway entry not found at ${gatewayEntry}. Re-run without --skip-build, or: pnpm --filter @rulemetric/cli build`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
if (flags.force) {
|
|
218
|
-
this.killStaleProcess(port, null, "API on :" + port);
|
|
219
|
-
}
|
|
220
|
-
if (!noProxy) {
|
|
221
|
-
this.killStaleProcess(GATEWAY_PORT, join(CONFIG_DIR, "gateway.pid"), "gateway");
|
|
222
|
-
this.killStaleProcess(PROXY_PORT, join(CONFIG_DIR, "proxy.pid"), "mitmproxy");
|
|
223
|
-
}
|
|
224
|
-
mkdirSync(join(homedir(), "Library", "LaunchAgents"), { recursive: true });
|
|
225
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
226
|
-
const uid = execSync("id -u", { encoding: "utf-8" }).trim();
|
|
227
|
-
const apiPlist = this.buildPlist({ port, projectRoot, nodePath, pnpmPath, envVars });
|
|
228
|
-
this.bootoutAndWrite(uid, PLIST_PATH, apiPlist, "API");
|
|
229
|
-
this.bootstrapPlist(uid, PLIST_PATH, "API");
|
|
230
|
-
this.log(`[..] Waiting for API on :${port}...`);
|
|
231
|
-
const apiStarted = await this.waitForApi(port, 15);
|
|
232
|
-
if (apiStarted) {
|
|
233
|
-
this.log(`[OK] RuleMetric API running on :${port}`);
|
|
234
|
-
if (existsSync(webIndex)) {
|
|
235
|
-
this.log(`[OK] Dashboard available at http://localhost:${port}`);
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
this.warn("API did not respond within 15s. Check logs:");
|
|
239
|
-
this.log(` tail -f ${LOG_DIR}/api-stdout.log`);
|
|
240
|
-
this.log(` tail -f ${LOG_DIR}/api-stderr.log`);
|
|
241
|
-
}
|
|
242
|
-
this.log("");
|
|
243
|
-
this.log("Installing worker service...");
|
|
244
|
-
this.killStaleAgents();
|
|
245
|
-
const workerPlist = this.buildWorkerPlist({
|
|
246
|
-
cliBin: join(projectRoot, "apps", "cli", "bin", "run.js"),
|
|
247
|
-
cliDist: join(projectRoot, "apps", "cli", "dist"),
|
|
248
|
-
workingDir: projectRoot,
|
|
249
|
-
nodePath,
|
|
250
|
-
pnpmPath,
|
|
251
|
-
envVars
|
|
252
|
-
});
|
|
253
|
-
this.bootoutAndWrite(uid, WORKER_PLIST_PATH, workerPlist, "Worker");
|
|
254
|
-
this.bootstrapPlist(uid, WORKER_PLIST_PATH, "Worker");
|
|
255
|
-
if (!noProxy) {
|
|
256
|
-
this.log("");
|
|
257
|
-
this.log("Installing gateway + capture proxy services...");
|
|
258
|
-
const gatewayPlist = this.buildGatewayPlist({ projectRoot, nodePath });
|
|
259
|
-
this.bootoutAndWrite(uid, GATEWAY_PLIST_PATH, gatewayPlist, "Gateway");
|
|
260
|
-
this.bootstrapPlist(uid, GATEWAY_PLIST_PATH, "Gateway");
|
|
261
|
-
const proxyPlist = this.buildProxyPlist({
|
|
262
|
-
projectRoot,
|
|
263
|
-
mitmdumpPath,
|
|
264
|
-
envVars
|
|
265
|
-
});
|
|
266
|
-
this.bootoutAndWrite(uid, PROXY_PLIST_PATH, proxyPlist, "Proxy");
|
|
267
|
-
this.bootstrapPlist(uid, PROXY_PLIST_PATH, "Proxy");
|
|
268
|
-
}
|
|
269
|
-
if (existsSync(WORKER_RESTART_PLIST_PATH)) {
|
|
270
|
-
try {
|
|
271
|
-
execFileSync("launchctl", ["bootout", `gui/${uid}`, WORKER_RESTART_PLIST_PATH], { encoding: "utf-8" });
|
|
272
|
-
} catch {
|
|
273
|
-
}
|
|
274
|
-
try {
|
|
275
|
-
execFileSync("rm", ["-f", WORKER_RESTART_PLIST_PATH]);
|
|
276
|
-
this.log("[OK] Removed stale daily worker-restart agent (no longer needed on agent path)");
|
|
277
|
-
} catch {
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
this.log("");
|
|
281
|
-
if (noProxy) {
|
|
282
|
-
this.log("Installed: API + worker. Gateway/proxy skipped (--no-proxy).");
|
|
283
|
-
} else {
|
|
284
|
-
this.log("Installed: API + worker + gateway + capture proxy.");
|
|
285
|
-
}
|
|
286
|
-
this.log("All services start at login, restart on crash, and pick up rebuilds via WatchPaths.");
|
|
287
|
-
this.log("");
|
|
288
|
-
this.log("NOTE: Env vars are baked in at install time. If you change");
|
|
289
|
-
this.log(".env.local or credentials, re-run: rulemetric service install --force");
|
|
290
|
-
this.log("");
|
|
291
|
-
this.log("Manage with:");
|
|
292
|
-
this.log(" rulemetric service status \u2014 check if running");
|
|
293
|
-
this.log(" rulemetric service uninstall \u2014 remove all services");
|
|
294
|
-
await bootstrapActiveOrg();
|
|
295
|
-
const tmux = await detectTmux();
|
|
296
|
-
this.log("");
|
|
297
|
-
if (tmux.available) {
|
|
298
|
-
this.log(`[OK] tmux found${tmux.version ? ` (v${tmux.version})` : ""} \u2014 live send enabled`);
|
|
299
|
-
} else {
|
|
300
|
-
this.log("[!!] tmux not found \u2014 live send will fall back to opening Terminal. Install with: brew install tmux");
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Worker-only install: a persistent job worker for end users on the
|
|
305
|
-
* globally-installed CLI, with NO repo checkout, NO build, and NO
|
|
306
|
-
* API/gateway/proxy. macOS → launchd; Linux → systemd --user. Auth is
|
|
307
|
-
* sourced from ~/.config/rulemetric/env (written by `rulemetric auth
|
|
308
|
-
* login`); the thin-agent worker is HTTP-only (long-polls /api/work/next),
|
|
309
|
-
* so it needs only the API URL + a key/token, never DB credentials.
|
|
310
|
-
*/
|
|
311
|
-
async runWorkerOnly(force) {
|
|
312
|
-
const platform = process.platform;
|
|
313
|
-
if (platform !== "darwin" && platform !== "linux") {
|
|
314
|
-
this.error(`--worker-only supports macOS (launchd) and Linux (systemd) only; detected '${platform}'.`);
|
|
315
|
-
}
|
|
316
|
-
const nodePath = this.findBinary("node");
|
|
317
|
-
if (!nodePath) {
|
|
318
|
-
this.error("Could not find `node` in PATH.");
|
|
319
|
-
}
|
|
320
|
-
const cliBin = join(this.config.root, "bin", "run.js");
|
|
321
|
-
if (!existsSync(cliBin)) {
|
|
322
|
-
this.error(
|
|
323
|
-
`Could not locate the installed CLI entry at ${cliBin}. Reinstall the CLI (e.g. npm i -g @rulemetric/cli) and re-run.`
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
const cliDist = join(this.config.root, "dist");
|
|
327
|
-
const envVars = this.loadGlobalWorkerEnv();
|
|
328
|
-
if (!envVars.RULEMETRIC_API_KEY && !envVars.RULEMETRIC_ACCESS_TOKEN) {
|
|
329
|
-
this.error(
|
|
330
|
-
"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`."
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
if (!envVars.RULEMETRIC_API_URL) {
|
|
334
|
-
envVars.RULEMETRIC_API_URL = "https://rulemetric.com";
|
|
335
|
-
}
|
|
336
|
-
mkdirSync(LOG_DIR, { recursive: true });
|
|
337
|
-
this.killStaleAgents();
|
|
338
|
-
if (platform === "darwin") {
|
|
339
|
-
if (existsSync(WORKER_PLIST_PATH) && !force) {
|
|
340
|
-
this.log(`Worker already installed at ${WORKER_PLIST_PATH}.`);
|
|
341
|
-
this.log("Use --force to overwrite, or run: rulemetric service status");
|
|
342
|
-
return;
|
|
343
|
-
}
|
|
344
|
-
mkdirSync(join(homedir(), "Library", "LaunchAgents"), { recursive: true });
|
|
345
|
-
const uid = execSync("id -u", { encoding: "utf-8" }).trim();
|
|
346
|
-
const plist = this.buildWorkerPlist({
|
|
347
|
-
cliBin,
|
|
348
|
-
cliDist,
|
|
349
|
-
workingDir: homedir(),
|
|
350
|
-
nodePath,
|
|
351
|
-
nodeEnv: "production",
|
|
352
|
-
envVars
|
|
353
|
-
});
|
|
354
|
-
this.bootoutAndWrite(uid, WORKER_PLIST_PATH, plist, "Worker");
|
|
355
|
-
this.bootstrapPlist(uid, WORKER_PLIST_PATH, "Worker");
|
|
356
|
-
this.log("");
|
|
357
|
-
this.log("[OK] Worker installed (launchd). Starts at login, restarts on crash.");
|
|
358
|
-
this.log(` Logs: tail -f ${join(LOG_DIR, "worker-stdout.log")}`);
|
|
359
|
-
} else {
|
|
360
|
-
if (existsSync(WORKER_SYSTEMD_PATH) && !force) {
|
|
361
|
-
this.log(`Worker already installed at ${WORKER_SYSTEMD_PATH}.`);
|
|
362
|
-
this.log(`Use --force to overwrite, or: systemctl --user status ${WORKER_SYSTEMD_UNIT}`);
|
|
363
|
-
return;
|
|
364
|
-
}
|
|
365
|
-
mkdirSync(WORKER_SYSTEMD_DIR, { recursive: true });
|
|
366
|
-
const unit = this.buildWorkerSystemdUnit({ cliBin, nodePath, envVars });
|
|
367
|
-
writeFileSync(WORKER_SYSTEMD_PATH, unit, { mode: 384 });
|
|
368
|
-
this.log(`[OK] Wrote ${WORKER_SYSTEMD_PATH}`);
|
|
369
|
-
try {
|
|
370
|
-
execFileSync("systemctl", ["--user", "daemon-reload"]);
|
|
371
|
-
execFileSync("systemctl", ["--user", "enable", "--now", WORKER_SYSTEMD_UNIT]);
|
|
372
|
-
this.log("[OK] Worker enabled + started (systemctl --user).");
|
|
373
|
-
try {
|
|
374
|
-
execFileSync("loginctl", ["enable-linger", userInfo().username]);
|
|
375
|
-
this.log("[OK] Enabled linger \u2014 worker survives logout/reboot.");
|
|
376
|
-
} catch {
|
|
377
|
-
this.log("[!!] Could not enable linger (worker will stop on logout).");
|
|
378
|
-
this.log(' Run: sudo loginctl enable-linger "$USER"');
|
|
379
|
-
}
|
|
380
|
-
this.log(` Logs: journalctl --user -u ${WORKER_SYSTEMD_UNIT} -f`);
|
|
381
|
-
} catch (err) {
|
|
382
|
-
this.warn(
|
|
383
|
-
`Wrote the unit but \`systemctl --user\` failed: ${err instanceof Error ? err.message : String(err)}`
|
|
384
|
-
);
|
|
385
|
-
this.log(
|
|
386
|
-
`Enable it manually: systemctl --user daemon-reload && systemctl --user enable --now ${WORKER_SYSTEMD_UNIT}`
|
|
387
|
-
);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
this.log("");
|
|
391
|
-
this.log("Manage with:");
|
|
392
|
-
this.log(" rulemetric service status \u2014 check if running");
|
|
393
|
-
this.log(" rulemetric service uninstall \u2014 remove the worker");
|
|
394
|
-
await bootstrapActiveOrg();
|
|
395
|
-
void emitSetupEvent("worker_started", { mode: "worker-only" }, this.config.version);
|
|
396
|
-
}
|
|
397
|
-
/**
|
|
398
|
-
* Load worker env from ~/.config/rulemetric/env only (no repo .env.local).
|
|
399
|
-
* Filtered by ALLOWED_ENV_VARS, same as the full install's loader.
|
|
400
|
-
*/
|
|
401
|
-
loadGlobalWorkerEnv() {
|
|
402
|
-
const vars = {};
|
|
403
|
-
const globalEnv = join(CONFIG_DIR, "env");
|
|
404
|
-
if (existsSync(globalEnv)) {
|
|
405
|
-
for (const line of readFileSync(globalEnv, "utf-8").split("\n")) {
|
|
406
|
-
const trimmed = line.trim();
|
|
407
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
408
|
-
const eqIdx = trimmed.indexOf("=");
|
|
409
|
-
if (eqIdx > 0) {
|
|
410
|
-
vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
return Object.fromEntries(Object.entries(vars).filter(([k]) => ALLOWED_ENV_VARS.has(k)));
|
|
415
|
-
}
|
|
416
|
-
/** systemd --user unit for the Linux worker-only path. */
|
|
417
|
-
buildWorkerSystemdUnit(opts) {
|
|
418
|
-
const { cliBin, nodePath, envVars } = opts;
|
|
419
|
-
const env = {
|
|
420
|
-
...envVars,
|
|
421
|
-
NODE_ENV: "production",
|
|
422
|
-
// Mirror the launchd worker's pooler bound (RCA 2026-06-21).
|
|
423
|
-
PG_POOL_MAX: envVars.PG_POOL_MAX ?? "3",
|
|
424
|
-
PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? "10"
|
|
425
|
-
};
|
|
426
|
-
const envLines = Object.entries(env).map(([k, v]) => `Environment="${k}=${v.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`).join("\n");
|
|
427
|
-
return `[Unit]
|
|
1
|
+
import{a as N}from"../../chunk-6XEC6474.js";import"../../chunk-N7ODX6PO.js";import{a as W}from"../../chunk-LQRYQE6Q.js";import"../../chunk-IZTIYR5Q.js";import{c as M}from"../../chunk-CRAKDQ4H.js";import{b as D}from"../../chunk-WPJ2BND4.js";import"../../chunk-3AV3GTMP.js";import"../../chunk-TIQ7DQ3M.js";import"../../chunk-M6AJDVCL.js";import{a as $}from"../../chunk-ZLLER3HP.js";import{Flags as v}from"@oclif/core";import{existsSync as g,mkdirSync as T,readFileSync as R,writeFileSync as j}from"node:fs";import{execSync as B,execFileSync as h,spawnSync as x}from"node:child_process";import{homedir as y,userInfo as Z}from"node:os";import{dirname as E,join as o}from"node:path";var K=new Set(["SUPABASE_URL","SUPABASE_ANON_KEY","SUPABASE_SERVICE_ROLE_KEY","SUPABASE_JWT_SECRET","DATABASE_URL","RULEMETRIC_API_URL","RULEMETRIC_API_KEY","RULEMETRIC_ACCESS_TOKEN","RULEMETRIC_AUDIT_NOTIFY_USER_ID","RULEMETRIC_USER_ID","GITHUB_TOKEN","PG_POOL_MAX","PG_IDLE_TIMEOUT","PG_STATEMENT_TIMEOUT_MS","PG_HEAVY_POOL_MAX","PG_QUERY_DEADLINE_MS","RESEND_API_KEY","RESEND_FROM_EMAIL","SENTRY_DSN"]),H="com.rulemetric.api",I=o(y(),"Library","LaunchAgents",`${H}.plist`),F="com.rulemetric.worker",b=o(y(),"Library","LaunchAgents",`${F}.plist`),X="com.rulemetric.gateway",G=o(y(),"Library","LaunchAgents",`${X}.plist`),J="com.rulemetric.proxy",Y=o(y(),"Library","LaunchAgents",`${J}.plist`),tt="com.rulemetric.worker.restart",U=o(y(),"Library","LaunchAgents",`${tt}.plist`),w=o(y(),".config","rulemetric"),p=o(w,"logs"),et=8787,V=8788,q=o(y(),".config","systemd","user"),_="rulemetric-worker.service",S=o(q,_),C=class L extends W{static{$(this,"ServiceInstall")}static description="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).";static examples=["<%= config.bin %> service install","<%= config.bin %> service install --port 3000","<%= config.bin %> service install --skip-build","<%= config.bin %> service install --no-proxy","<%= config.bin %> service install --worker-only"];static flags={port:v.integer({char:"p",default:3e3,description:"API server port"}),force:v.boolean({char:"f",default:!1,description:"Overwrite existing service configuration and reclaim conflicting ports"}),"skip-build":v.boolean({default:!1,description:"Skip the pre-install workspace build (API/web/cli dist must already exist)"}),"no-proxy":v.boolean({default:!1,description:"Skip gateway + mitmproxy plists (just install API + worker)"}),"worker-only":v.boolean({default:!1,description:"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)."})};async run(){let{flags:r}=await this.parse(L),t=r.port,e=r["skip-build"],s=r["no-proxy"];if(r["worker-only"]){await this.runWorkerOnly(r.force);return}if(process.platform!=="darwin"&&this.error("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"),g(I)&&!r.force){this.log(`Service already installed at ${I}`),this.log("Use --force to overwrite, or run: rulemetric service status");return}try{if((await fetch(`http://localhost:${t}/health`)).ok&&!r.force){this.warn(`Port ${t} is already in use (API may be running via start.sh or in another terminal).`),this.warn("Stop it first with scripts/stop.sh, or use --force to take over the port.");return}}catch{}let i=this.findProjectRoot();i||this.error("Could not find RuleMetric project root. Run this command from within the momento-mori repo, or set RULEMETRIC_PROJECT_ROOT.");let n=o(i,".env.local");g(n)||this.error(`.env.local not found at ${n}. The API needs Supabase credentials to start.`);let a=this.guardAlwaysOnDatabaseUrl(this.loadEnvVars(i)),c=this.findBinary("node"),d=this.findBinary("pnpm");(!c||!d)&&this.error("Could not find node or pnpm. Ensure they are in your PATH.");let u=null;if(s||(u=this.findBinary("mitmdump"),u||this.error(`mitmdump not found in PATH. Install with: pipx install mitmproxy (or brew install mitmproxy), then re-run.
|
|
2
|
+
Or skip the capture proxy with --no-proxy.`)),e)this.log("[..] Skipping pre-install build (--skip-build)");else{this.log("[..] Building workspace artifacts (api, web, cli) via turbo...");let A=x(d,["exec","turbo","run","build",...["--filter","@rulemetric/api...","--filter","@rulemetric/web...","--filter","@rulemetric/cli"]],{cwd:i,stdio:"inherit"});A.status!==0&&this.error(`Workspace build failed (exit ${A.status}). Fix the build first, or use --skip-build if dist/ already exists.`),this.log("[OK] Build complete")}let f=o(i,"apps","api","dist","index.js");g(f)||this.error(`API entry not found at ${f}. Re-run without --skip-build, or: pnpm --filter @rulemetric/api... build`);let k=o(i,"apps","web","dist","index.html");if(g(k)||this.warn(`Web bundle not found at ${k}. Dashboard at :${t} will 404 until you build apps/web.`),!s){let P=o(i,"apps","cli","dist","lib","gateway-entry.js");g(P)||this.error(`Gateway entry not found at ${P}. Re-run without --skip-build, or: pnpm --filter @rulemetric/cli build`)}r.force&&this.killStaleProcess(t,null,"API on :"+t),s||(this.killStaleProcess(et,o(w,"gateway.pid"),"gateway"),this.killStaleProcess(V,o(w,"proxy.pid"),"mitmproxy")),T(o(y(),"Library","LaunchAgents"),{recursive:!0}),T(p,{recursive:!0});let m=B("id -u",{encoding:"utf-8"}).trim(),z=this.buildPlist({port:t,projectRoot:i,nodePath:c,pnpmPath:d,envVars:a});this.bootoutAndWrite(m,I,z,"API"),this.bootstrapPlist(m,I,"API"),this.log(`[..] Waiting for API on :${t}...`),await this.waitForApi(t,15)?(this.log(`[OK] RuleMetric API running on :${t}`),g(k)&&this.log(`[OK] Dashboard available at http://localhost:${t}`)):(this.warn("API did not respond within 15s. Check logs:"),this.log(` tail -f ${p}/api-stdout.log`),this.log(` tail -f ${p}/api-stderr.log`)),this.log(""),this.log("Installing worker service..."),this.killStaleAgents();let Q=this.buildWorkerPlist({cliBin:o(i,"apps","cli","bin","run.js"),cliDist:o(i,"apps","cli","dist"),workingDir:i,nodePath:c,pnpmPath:d,envVars:a});if(this.bootoutAndWrite(m,b,Q,"Worker"),this.bootstrapPlist(m,b,"Worker"),!s){this.log(""),this.log("Installing gateway + capture proxy services...");let P=this.buildGatewayPlist({projectRoot:i,nodePath:c});this.bootoutAndWrite(m,G,P,"Gateway"),this.bootstrapPlist(m,G,"Gateway");let A=this.buildProxyPlist({projectRoot:i,mitmdumpPath:u,envVars:a});this.bootoutAndWrite(m,Y,A,"Proxy"),this.bootstrapPlist(m,Y,"Proxy")}if(g(U)){try{h("launchctl",["bootout",`gui/${m}`,U],{encoding:"utf-8"})}catch{}try{h("rm",["-f",U]),this.log("[OK] Removed stale daily worker-restart agent (no longer needed on agent path)")}catch{}}this.log(""),s?this.log("Installed: API + worker. Gateway/proxy skipped (--no-proxy)."):this.log("Installed: API + worker + gateway + capture proxy."),this.log("All services start at login, restart on crash, and pick up rebuilds via WatchPaths."),this.log(""),this.log("NOTE: Env vars are baked in at install time. If you change"),this.log(".env.local or credentials, re-run: rulemetric service install --force"),this.log(""),this.log("Manage with:"),this.log(" rulemetric service status \u2014 check if running"),this.log(" rulemetric service uninstall \u2014 remove all services"),await D();let O=await N();this.log(""),O.available?this.log(`[OK] tmux found${O.version?` (v${O.version})`:""} \u2014 live send enabled`):this.log("[!!] tmux not found \u2014 live send will fall back to opening Terminal. Install with: brew install tmux")}async runWorkerOnly(r){let t=process.platform;t!=="darwin"&&t!=="linux"&&this.error(`--worker-only supports macOS (launchd) and Linux (systemd) only; detected '${t}'.`);let e=this.findBinary("node");e||this.error("Could not find `node` in PATH.");let s=o(this.config.root,"bin","run.js");g(s)||this.error(`Could not locate the installed CLI entry at ${s}. Reinstall the CLI (e.g. npm i -g @rulemetric/cli) and re-run.`);let i=o(this.config.root,"dist"),n=this.loadGlobalWorkerEnv();if(!n.RULEMETRIC_API_KEY&&!n.RULEMETRIC_ACCESS_TOKEN&&this.error("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.RULEMETRIC_API_URL||(n.RULEMETRIC_API_URL="https://rulemetric.com"),T(p,{recursive:!0}),this.killStaleAgents(),t==="darwin"){if(g(b)&&!r){this.log(`Worker already installed at ${b}.`),this.log("Use --force to overwrite, or run: rulemetric service status");return}T(o(y(),"Library","LaunchAgents"),{recursive:!0});let a=B("id -u",{encoding:"utf-8"}).trim(),c=this.buildWorkerPlist({cliBin:s,cliDist:i,workingDir:y(),nodePath:e,nodeEnv:"production",envVars:n});this.bootoutAndWrite(a,b,c,"Worker"),this.bootstrapPlist(a,b,"Worker"),this.log(""),this.log("[OK] Worker installed (launchd). Starts at login, restarts on crash."),this.log(` Logs: tail -f ${o(p,"worker-stdout.log")}`)}else{if(g(S)&&!r){this.log(`Worker already installed at ${S}.`),this.log(`Use --force to overwrite, or: systemctl --user status ${_}`);return}T(q,{recursive:!0});let a=this.buildWorkerSystemdUnit({cliBin:s,nodePath:e,envVars:n});j(S,a,{mode:384}),this.log(`[OK] Wrote ${S}`);try{h("systemctl",["--user","daemon-reload"]),h("systemctl",["--user","enable","--now",_]),this.log("[OK] Worker enabled + started (systemctl --user).");try{h("loginctl",["enable-linger",Z().username]),this.log("[OK] Enabled linger \u2014 worker survives logout/reboot.")}catch{this.log("[!!] Could not enable linger (worker will stop on logout)."),this.log(' Run: sudo loginctl enable-linger "$USER"')}this.log(` Logs: journalctl --user -u ${_} -f`)}catch(c){this.warn(`Wrote the unit but \`systemctl --user\` failed: ${c instanceof Error?c.message:String(c)}`),this.log(`Enable it manually: systemctl --user daemon-reload && systemctl --user enable --now ${_}`)}}this.log(""),this.log("Manage with:"),this.log(" rulemetric service status \u2014 check if running"),this.log(" rulemetric service uninstall \u2014 remove the worker"),await D(),M("worker_started",{mode:"worker-only"},this.config.version)}loadGlobalWorkerEnv(){let r={},t=o(w,"env");if(g(t))for(let e of R(t,"utf-8").split(`
|
|
3
|
+
`)){let s=e.trim();if(!s||s.startsWith("#"))continue;let i=s.indexOf("=");i>0&&(r[s.slice(0,i)]=s.slice(i+1))}return Object.fromEntries(Object.entries(r).filter(([e])=>K.has(e)))}buildWorkerSystemdUnit(r){let{cliBin:t,nodePath:e,envVars:s}=r,i={...s,NODE_ENV:"production",PATH:[...new Set([E(e),o(y(),".local","bin"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"])].join(":"),PG_POOL_MAX:s.PG_POOL_MAX??"3",PG_IDLE_TIMEOUT:s.PG_IDLE_TIMEOUT??"10"},n=Object.entries(i).map(([a,c])=>`Environment="${a}=${c.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`).join(`
|
|
4
|
+
`);return`[Unit]
|
|
428
5
|
Description=RuleMetric job worker (evals agent)
|
|
429
6
|
After=network-online.target
|
|
430
7
|
Wants=network-online.target
|
|
431
8
|
|
|
432
9
|
[Service]
|
|
433
10
|
Type=simple
|
|
434
|
-
ExecStart=${
|
|
11
|
+
ExecStart=${e} ${t} evals agent
|
|
435
12
|
Restart=on-failure
|
|
436
13
|
RestartSec=15
|
|
437
|
-
${
|
|
14
|
+
${n}
|
|
438
15
|
|
|
439
16
|
[Install]
|
|
440
17
|
WantedBy=default.target
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
const parent = join(dir, "..");
|
|
453
|
-
if (parent === dir) break;
|
|
454
|
-
dir = parent;
|
|
455
|
-
}
|
|
456
|
-
return null;
|
|
457
|
-
}
|
|
458
|
-
findBinary(name) {
|
|
459
|
-
try {
|
|
460
|
-
return execFileSync("which", [name], { encoding: "utf-8", stdio: ["pipe", "pipe", "ignore"] }).trim() || null;
|
|
461
|
-
} catch {
|
|
462
|
-
return null;
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
loadEnvVars(projectRoot) {
|
|
466
|
-
const vars = {};
|
|
467
|
-
const envLocal = join(projectRoot, ".env.local");
|
|
468
|
-
if (existsSync(envLocal)) {
|
|
469
|
-
for (const line of readFileSync(envLocal, "utf-8").split("\n")) {
|
|
470
|
-
const trimmed = line.trim();
|
|
471
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
472
|
-
const eqIdx = trimmed.indexOf("=");
|
|
473
|
-
if (eqIdx > 0) {
|
|
474
|
-
vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
const globalEnv = join(CONFIG_DIR, "env");
|
|
479
|
-
if (existsSync(globalEnv)) {
|
|
480
|
-
for (const line of readFileSync(globalEnv, "utf-8").split("\n")) {
|
|
481
|
-
const trimmed = line.trim();
|
|
482
|
-
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
483
|
-
const eqIdx = trimmed.indexOf("=");
|
|
484
|
-
if (eqIdx > 0) {
|
|
485
|
-
vars[trimmed.slice(0, eqIdx)] = trimmed.slice(eqIdx + 1);
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
return Object.fromEntries(
|
|
490
|
-
Object.entries(vars).filter(([k]) => ALLOWED_ENV_VARS.has(k))
|
|
491
|
-
);
|
|
492
|
-
}
|
|
493
|
-
// Guard: the always-on API + worker MUST use the TRANSACTION-mode pooler
|
|
494
|
-
// (port 6543). A SESSION-mode URL (:5432, 15-slot cap) baked into these
|
|
495
|
-
// services was the trigger of the recurring pool saturation — a pool-cascade
|
|
496
|
-
// debugging override leaked into the plists through this very installer. Auto-
|
|
497
|
-
// correct the obvious session→transaction port and warn loudly; warn (but do
|
|
498
|
-
// not block) on a direct, non-pooler URL. Pool-exhaustion RCA 2026-06-21.
|
|
499
|
-
guardAlwaysOnDatabaseUrl(envVars) {
|
|
500
|
-
const url = envVars.DATABASE_URL;
|
|
501
|
-
if (!url) return envVars;
|
|
502
|
-
if (url.includes("pooler.supabase.com:5432")) {
|
|
503
|
-
this.warn(
|
|
504
|
-
"DATABASE_URL points at the SESSION-mode pooler (:5432, 15-slot cap). The always-on API/worker must use TRANSACTION mode (:6543) \u2014 auto-correcting for the installed services. Fix .env.local to silence this warning."
|
|
505
|
-
);
|
|
506
|
-
return { ...envVars, DATABASE_URL: url.replace("pooler.supabase.com:5432", "pooler.supabase.com:6543") };
|
|
507
|
-
}
|
|
508
|
-
if (/@db\.[a-z0-9]+\.supabase\.co[:/]/.test(url)) {
|
|
509
|
-
this.warn(
|
|
510
|
-
"DATABASE_URL is a DIRECT database connection (db.<ref>.supabase.co), not the pooler. Always-on services should use the transaction pooler (aws-*.pooler.supabase.com:6543) to avoid exhausting Postgres connections."
|
|
511
|
-
);
|
|
512
|
-
}
|
|
513
|
-
return envVars;
|
|
514
|
-
}
|
|
515
|
-
// Reap orphaned, unsupervised `evals agent` worker processes before
|
|
516
|
-
// (re)bootstrapping the launchd worker. The worker holds no listening port,
|
|
517
|
-
// so killStaleProcess (port/pidfile based) cannot find detached agents — a
|
|
518
|
-
// fleet of 4 ran 3+ days and helped saturate the pool (RCA 2026-06-21). pgrep
|
|
519
|
-
// the command line and SIGTERM every match except ourselves; the agent's own
|
|
520
|
-
// single-instance lock then keeps a fresh fleet from forming.
|
|
521
|
-
killStaleAgents() {
|
|
522
|
-
try {
|
|
523
|
-
const out = execFileSync("pgrep", ["-f", "bin/run.js evals agent"], {
|
|
524
|
-
encoding: "utf-8",
|
|
525
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
526
|
-
});
|
|
527
|
-
const pids = out.trim().split("\n").filter(Boolean).map((s) => parseInt(s, 10)).filter((p) => !isNaN(p) && p !== process.pid);
|
|
528
|
-
for (const pid of pids) {
|
|
529
|
-
try {
|
|
530
|
-
process.kill(pid, "SIGTERM");
|
|
531
|
-
this.log(`[OK] Reaped orphan evals agent (PID ${pid})`);
|
|
532
|
-
} catch {
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
if (pids.length) spawnSync("sleep", ["1"], { stdio: "ignore" });
|
|
536
|
-
} catch {
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
// Best-effort cleanup of a process that would conflict with an
|
|
540
|
-
// about-to-bootstrap launchd plist. Tries two routes:
|
|
541
|
-
// (a) the PID file written by the legacy spawn path
|
|
542
|
-
// (gateway-lifecycle / proxy start), if pidFile is non-null
|
|
543
|
-
// (b) anything actively listening on the expected port
|
|
544
|
-
// Either way: SIGTERM + small grace + SIGKILL fallback. Stale PID file
|
|
545
|
-
// gets unlinked.
|
|
546
|
-
killStaleProcess(port, pidFile, name) {
|
|
547
|
-
let killedAny = false;
|
|
548
|
-
if (pidFile && existsSync(pidFile)) {
|
|
549
|
-
try {
|
|
550
|
-
const pidStr = readFileSync(pidFile, "utf-8").trim();
|
|
551
|
-
const pid = parseInt(pidStr, 10);
|
|
552
|
-
if (!isNaN(pid)) {
|
|
553
|
-
try {
|
|
554
|
-
process.kill(pid, "SIGTERM");
|
|
555
|
-
killedAny = true;
|
|
556
|
-
this.log(`[OK] Stopped existing ${name} (PID ${pid} from ${pidFile})`);
|
|
557
|
-
} catch {
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
} catch {
|
|
561
|
-
}
|
|
562
|
-
try {
|
|
563
|
-
execFileSync("rm", ["-f", pidFile]);
|
|
564
|
-
} catch {
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
try {
|
|
568
|
-
const result = execFileSync("lsof", ["-ti", `:${port}`, "-sTCP:LISTEN"], {
|
|
569
|
-
encoding: "utf-8",
|
|
570
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
571
|
-
});
|
|
572
|
-
const pids = result.trim().split("\n").filter(Boolean);
|
|
573
|
-
for (const pidStr of pids) {
|
|
574
|
-
const pid = parseInt(pidStr, 10);
|
|
575
|
-
if (!isNaN(pid)) {
|
|
576
|
-
try {
|
|
577
|
-
process.kill(pid, "SIGTERM");
|
|
578
|
-
killedAny = true;
|
|
579
|
-
this.log(`[OK] Stopped orphan on :${port} (PID ${pid})`);
|
|
580
|
-
} catch {
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
} catch {
|
|
585
|
-
}
|
|
586
|
-
if (killedAny) {
|
|
587
|
-
spawnSync("sleep", ["1"], { stdio: "ignore" });
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
bootoutAndWrite(uid, plistPath, content, name) {
|
|
591
|
-
const benignBootoutPatterns = [
|
|
592
|
-
"Could not find specified service",
|
|
593
|
-
"No such process",
|
|
594
|
-
"3: No such process",
|
|
595
|
-
"5: Input/output error"
|
|
596
|
-
];
|
|
597
|
-
try {
|
|
598
|
-
execFileSync("launchctl", ["bootout", `gui/${uid}`, plistPath], {
|
|
599
|
-
encoding: "utf-8",
|
|
600
|
-
// Capture stderr instead of letting it print to the user's terminal.
|
|
601
|
-
// The plist was either already-loaded (we unload it cleanly) or not
|
|
602
|
-
// loaded (we want to swallow the error) — the user shouldn't see
|
|
603
|
-
// launchctl's raw output either way.
|
|
604
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
605
|
-
});
|
|
606
|
-
this.log(`[OK] Unloaded existing ${name} service`);
|
|
607
|
-
} catch (err) {
|
|
608
|
-
const msg = err.message || "";
|
|
609
|
-
const stderr = (err.stderr ?? "").toString();
|
|
610
|
-
const combined = `${msg}
|
|
611
|
-
${stderr}`;
|
|
612
|
-
if (!benignBootoutPatterns.some((p) => combined.includes(p))) {
|
|
613
|
-
this.warn(`Could not unload existing ${name} service: ${msg.trim()}`);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
writeFileSync(plistPath, content, { mode: 384 });
|
|
617
|
-
this.log(`[OK] ${name} plist written to ${plistPath}`);
|
|
618
|
-
}
|
|
619
|
-
bootstrapPlist(uid, plistPath, name) {
|
|
620
|
-
try {
|
|
621
|
-
execFileSync("launchctl", ["bootstrap", `gui/${uid}`, plistPath]);
|
|
622
|
-
this.log(`[OK] ${name} service loaded into launchd`);
|
|
623
|
-
} catch (bootstrapErr) {
|
|
624
|
-
try {
|
|
625
|
-
execFileSync("launchctl", ["load", plistPath]);
|
|
626
|
-
this.log(`[OK] ${name} service loaded into launchd (legacy)`);
|
|
627
|
-
} catch (loadErr) {
|
|
628
|
-
this.warn(
|
|
629
|
-
`Could not load ${name} service.
|
|
630
|
-
bootstrap: ${bootstrapErr.message.trim()}
|
|
631
|
-
load: ${loadErr.message.trim()}`
|
|
632
|
-
);
|
|
633
|
-
}
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
buildPlist(opts) {
|
|
637
|
-
const { port, projectRoot, nodePath, pnpmPath, envVars } = opts;
|
|
638
|
-
const pathDirs = [.../* @__PURE__ */ new Set([dirname(nodePath), dirname(pnpmPath), "/usr/local/bin", "/usr/bin", "/bin"])];
|
|
639
|
-
const envEntries = Object.entries({
|
|
640
|
-
...envVars,
|
|
641
|
-
PORT: String(port),
|
|
642
|
-
NODE_ENV: "production",
|
|
643
|
-
PATH: pathDirs.join(":"),
|
|
644
|
-
// Pool resilience for the always-on API (NOT the worker, which runs
|
|
645
|
-
// legitimately long analytics queries). A wedged Supavisor socket with
|
|
646
|
-
// the default max=3 pool and no query timeout hangs every authenticated
|
|
647
|
-
// request — the 2026-06-09 login-hang. statement_timeout lets Postgres
|
|
648
|
-
// cancel a stuck query so the connection returns to the pool (verified
|
|
649
|
-
// honored through the transaction pooler); the larger pool adds headroom.
|
|
650
|
-
// Both env-overridable. See packages/db/src/client.ts.
|
|
651
|
-
PG_POOL_MAX: envVars.PG_POOL_MAX ?? "10",
|
|
652
|
-
PG_STATEMENT_TIMEOUT_MS: envVars.PG_STATEMENT_TIMEOUT_MS ?? "10000",
|
|
653
|
-
// Keep the pool warm so the API stops reconnecting (and re-resolving the
|
|
654
|
-
// rotating pooler ELB) on nearly every request — the churn that exposed
|
|
655
|
-
// it to intermittent CONNECT_TIMEOUT/ENOTFOUND. Worker/tests keep the
|
|
656
|
-
// conservative 5s default. See packages/db/src/client.ts (2026-06-09).
|
|
657
|
-
PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? "30",
|
|
658
|
-
// Silence per-span stdout dumps on the local API — it has no OTLP
|
|
659
|
-
// endpoint and a fixed (now rotated) log file; the ConsoleSpanExporter
|
|
660
|
-
// grew api-stdout to 370MB / ~400k spans. Fly leaves this unset so its
|
|
661
|
-
// console spans stay grep-able in the managed log stream.
|
|
662
|
-
OTEL_SPAN_EXPORT: envVars.OTEL_SPAN_EXPORT ?? "none"
|
|
663
|
-
}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
664
|
-
<string>${escapeXml(v)}</string>`).join("\n");
|
|
665
|
-
const apiEntry = join(projectRoot, "apps", "api", "dist", "index.js");
|
|
666
|
-
const apiDist = join(projectRoot, "apps", "api", "dist");
|
|
667
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
18
|
+
`}findProjectRoot(){if(process.env.RULEMETRIC_PROJECT_ROOT)return process.env.RULEMETRIC_PROJECT_ROOT;let r=process.cwd();for(let t=0;t<10;t++){if(g(o(r,"apps","api","package.json")))return r;let e=o(r,"..");if(e===r)break;r=e}return null}findBinary(r){try{return h("which",[r],{encoding:"utf-8",stdio:["pipe","pipe","ignore"]}).trim()||null}catch{return null}}loadEnvVars(r){let t={},e=o(r,".env.local");if(g(e))for(let i of R(e,"utf-8").split(`
|
|
19
|
+
`)){let n=i.trim();if(!n||n.startsWith("#"))continue;let a=n.indexOf("=");a>0&&(t[n.slice(0,a)]=n.slice(a+1))}let s=o(w,"env");if(g(s))for(let i of R(s,"utf-8").split(`
|
|
20
|
+
`)){let n=i.trim();if(!n||n.startsWith("#"))continue;let a=n.indexOf("=");a>0&&(t[n.slice(0,a)]=n.slice(a+1))}return Object.fromEntries(Object.entries(t).filter(([i])=>K.has(i)))}guardAlwaysOnDatabaseUrl(r){let t=r.DATABASE_URL;return t?t.includes("pooler.supabase.com:5432")?(this.warn("DATABASE_URL points at the SESSION-mode pooler (:5432, 15-slot cap). The always-on API/worker must use TRANSACTION mode (:6543) \u2014 auto-correcting for the installed services. Fix .env.local to silence this warning."),{...r,DATABASE_URL:t.replace("pooler.supabase.com:5432","pooler.supabase.com:6543")}):(/@db\.[a-z0-9]+\.supabase\.co[:/]/.test(t)&&this.warn("DATABASE_URL is a DIRECT database connection (db.<ref>.supabase.co), not the pooler. Always-on services should use the transaction pooler (aws-*.pooler.supabase.com:6543) to avoid exhausting Postgres connections."),r):r}killStaleAgents(){try{let t=h("pgrep",["-f","bin/run.js evals agent"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim().split(`
|
|
21
|
+
`).filter(Boolean).map(e=>parseInt(e,10)).filter(e=>!isNaN(e)&&e!==process.pid);for(let e of t)try{process.kill(e,"SIGTERM"),this.log(`[OK] Reaped orphan evals agent (PID ${e})`)}catch{}t.length&&x("sleep",["1"],{stdio:"ignore"})}catch{}}killStaleProcess(r,t,e){let s=!1;if(t&&g(t)){try{let i=R(t,"utf-8").trim(),n=parseInt(i,10);if(!isNaN(n))try{process.kill(n,"SIGTERM"),s=!0,this.log(`[OK] Stopped existing ${e} (PID ${n} from ${t})`)}catch{}}catch{}try{h("rm",["-f",t])}catch{}}try{let n=h("lsof",["-ti",`:${r}`,"-sTCP:LISTEN"],{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim().split(`
|
|
22
|
+
`).filter(Boolean);for(let a of n){let c=parseInt(a,10);if(!isNaN(c))try{process.kill(c,"SIGTERM"),s=!0,this.log(`[OK] Stopped orphan on :${r} (PID ${c})`)}catch{}}}catch{}s&&x("sleep",["1"],{stdio:"ignore"})}bootoutAndWrite(r,t,e,s){let i=["Could not find specified service","No such process","3: No such process","5: Input/output error"];try{h("launchctl",["bootout",`gui/${r}`,t],{encoding:"utf-8",stdio:["ignore","pipe","pipe"]}),this.log(`[OK] Unloaded existing ${s} service`)}catch(n){let a=n.message||"",c=(n.stderr??"").toString(),d=`${a}
|
|
23
|
+
${c}`;i.some(u=>d.includes(u))||this.warn(`Could not unload existing ${s} service: ${a.trim()}`)}j(t,e,{mode:384}),this.log(`[OK] ${s} plist written to ${t}`)}bootstrapPlist(r,t,e){try{h("launchctl",["bootstrap",`gui/${r}`,t]),this.log(`[OK] ${e} service loaded into launchd`)}catch(s){try{h("launchctl",["load",t]),this.log(`[OK] ${e} service loaded into launchd (legacy)`)}catch(i){this.warn(`Could not load ${e} service.
|
|
24
|
+
bootstrap: ${s.message.trim()}
|
|
25
|
+
load: ${i.message.trim()}`)}}}buildPlist(r){let{port:t,projectRoot:e,nodePath:s,pnpmPath:i,envVars:n}=r,a=[...new Set([E(s),E(i),"/usr/local/bin","/usr/bin","/bin"])],c=Object.entries({...n,PORT:String(t),NODE_ENV:"production",PATH:a.join(":"),PG_POOL_MAX:n.PG_POOL_MAX??"10",PG_STATEMENT_TIMEOUT_MS:n.PG_STATEMENT_TIMEOUT_MS??"10000",PG_IDLE_TIMEOUT:n.PG_IDLE_TIMEOUT??"30",OTEL_SPAN_EXPORT:n.OTEL_SPAN_EXPORT??"none"}).map(([f,k])=>` <key>${l(f)}</key>
|
|
26
|
+
<string>${l(k)}</string>`).join(`
|
|
27
|
+
`),d=o(e,"apps","api","dist","index.js"),u=o(e,"apps","api","dist");return`<?xml version="1.0" encoding="UTF-8"?>
|
|
668
28
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
669
29
|
<plist version="1.0">
|
|
670
30
|
<dict>
|
|
671
31
|
<key>Label</key>
|
|
672
|
-
<string>${
|
|
32
|
+
<string>${H}</string>
|
|
673
33
|
|
|
674
34
|
<key>ProgramArguments</key>
|
|
675
35
|
<array>
|
|
676
|
-
<string>${
|
|
677
|
-
<string>${
|
|
36
|
+
<string>${l(s)}</string>
|
|
37
|
+
<string>${l(d)}</string>
|
|
678
38
|
</array>
|
|
679
39
|
|
|
680
40
|
<key>WorkingDirectory</key>
|
|
681
|
-
<string>${
|
|
41
|
+
<string>${l(e)}</string>
|
|
682
42
|
|
|
683
43
|
<key>EnvironmentVariables</key>
|
|
684
44
|
<dict>
|
|
685
|
-
${
|
|
45
|
+
${c}
|
|
686
46
|
</dict>
|
|
687
47
|
|
|
688
48
|
<key>RunAtLoad</key>
|
|
@@ -699,65 +59,42 @@ ${envEntries}
|
|
|
699
59
|
|
|
700
60
|
<key>WatchPaths</key>
|
|
701
61
|
<array>
|
|
702
|
-
<string>${
|
|
62
|
+
<string>${l(u)}</string>
|
|
703
63
|
</array>
|
|
704
64
|
|
|
705
65
|
<key>StandardOutPath</key>
|
|
706
|
-
<string>${
|
|
66
|
+
<string>${l(o(p,"api-stdout.log"))}</string>
|
|
707
67
|
|
|
708
68
|
<key>StandardErrorPath</key>
|
|
709
|
-
<string>${
|
|
69
|
+
<string>${l(o(p,"api-stderr.log"))}</string>
|
|
710
70
|
|
|
711
71
|
<key>ProcessType</key>
|
|
712
72
|
<string>Background</string>
|
|
713
73
|
</dict>
|
|
714
74
|
</plist>
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
const { cliBin, cliDist, workingDir, nodePath, pnpmPath, envVars } = opts;
|
|
719
|
-
const nodeEnv = opts.nodeEnv ?? "development";
|
|
720
|
-
const pathDirs = [
|
|
721
|
-
...new Set(
|
|
722
|
-
[dirname(nodePath), pnpmPath ? dirname(pnpmPath) : null, "/usr/local/bin", "/usr/bin", "/bin"].filter(
|
|
723
|
-
(d) => Boolean(d)
|
|
724
|
-
)
|
|
725
|
-
)
|
|
726
|
-
];
|
|
727
|
-
const envEntries = Object.entries({
|
|
728
|
-
...envVars,
|
|
729
|
-
NODE_ENV: nodeEnv,
|
|
730
|
-
PATH: pathDirs.join(":"),
|
|
731
|
-
// Bound the worker's pooler footprint. The worker is mostly idle (HTTP
|
|
732
|
-
// long-poll); an UNPINNED pool plus the previously-missing single-instance
|
|
733
|
-
// guard let an orphaned fleet of agents each hold a pool and saturate
|
|
734
|
-
// Supavisor's 200-client cap. A small pool + short idle timeout releases
|
|
735
|
-
// sessions promptly. Pool-exhaustion RCA 2026-06-21.
|
|
736
|
-
PG_POOL_MAX: envVars.PG_POOL_MAX ?? "3",
|
|
737
|
-
PG_IDLE_TIMEOUT: envVars.PG_IDLE_TIMEOUT ?? "10"
|
|
738
|
-
}).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
739
|
-
<string>${escapeXml(v)}</string>`).join("\n");
|
|
740
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
75
|
+
`}buildWorkerPlist(r){let{cliBin:t,cliDist:e,workingDir:s,nodePath:i,pnpmPath:n,envVars:a}=r,c=r.nodeEnv??"development",d=[...new Set([E(i),n?E(n):null,o(y(),".local","bin"),"/opt/homebrew/bin","/usr/local/bin","/usr/bin","/bin"].filter(f=>!!f))],u=Object.entries({...a,NODE_ENV:c,PATH:d.join(":"),PG_POOL_MAX:a.PG_POOL_MAX??"3",PG_IDLE_TIMEOUT:a.PG_IDLE_TIMEOUT??"10"}).map(([f,k])=>` <key>${l(f)}</key>
|
|
76
|
+
<string>${l(k)}</string>`).join(`
|
|
77
|
+
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
741
78
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
742
79
|
<plist version="1.0">
|
|
743
80
|
<dict>
|
|
744
81
|
<key>Label</key>
|
|
745
|
-
<string>${
|
|
82
|
+
<string>${F}</string>
|
|
746
83
|
|
|
747
84
|
<key>ProgramArguments</key>
|
|
748
85
|
<array>
|
|
749
|
-
<string>${
|
|
750
|
-
<string>${
|
|
86
|
+
<string>${l(i)}</string>
|
|
87
|
+
<string>${l(t)}</string>
|
|
751
88
|
<string>evals</string>
|
|
752
89
|
<string>agent</string>
|
|
753
90
|
</array>
|
|
754
91
|
|
|
755
92
|
<key>WorkingDirectory</key>
|
|
756
|
-
<string>${
|
|
93
|
+
<string>${l(s)}</string>
|
|
757
94
|
|
|
758
95
|
<key>EnvironmentVariables</key>
|
|
759
96
|
<dict>
|
|
760
|
-
${
|
|
97
|
+
${u}
|
|
761
98
|
</dict>
|
|
762
99
|
|
|
763
100
|
<key>RunAtLoad</key>
|
|
@@ -774,58 +111,40 @@ ${envEntries}
|
|
|
774
111
|
|
|
775
112
|
<key>WatchPaths</key>
|
|
776
113
|
<array>
|
|
777
|
-
<string>${
|
|
114
|
+
<string>${l(e)}</string>
|
|
778
115
|
</array>
|
|
779
116
|
|
|
780
117
|
<key>StandardOutPath</key>
|
|
781
|
-
<string>${
|
|
118
|
+
<string>${l(o(p,"worker-stdout.log"))}</string>
|
|
782
119
|
|
|
783
120
|
<key>StandardErrorPath</key>
|
|
784
|
-
<string>${
|
|
121
|
+
<string>${l(o(p,"worker-stderr.log"))}</string>
|
|
785
122
|
|
|
786
123
|
<key>ProcessType</key>
|
|
787
124
|
<string>Background</string>
|
|
788
125
|
</dict>
|
|
789
126
|
</plist>
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
// .claude/settings.json's HTTPS_PROXY pointer never resolves to a dead
|
|
794
|
-
// socket (which would break Claude Code entirely — see CLAUDE.md).
|
|
795
|
-
// NOT watched on rebuild: bouncing the gateway mid-request would drop
|
|
796
|
-
// in-flight streaming responses. Gateway code changes rarely; on
|
|
797
|
-
// upgrade, the user can `launchctl kickstart gui/<uid>/com.rulemetric.gateway`.
|
|
798
|
-
buildGatewayPlist(opts) {
|
|
799
|
-
const { projectRoot, nodePath } = opts;
|
|
800
|
-
const gatewayEntry = join(projectRoot, "apps", "cli", "dist", "lib", "gateway-entry.js");
|
|
801
|
-
const cliBin = join(projectRoot, "apps", "cli", "bin", "run.js");
|
|
802
|
-
const pathDirs = [.../* @__PURE__ */ new Set([dirname(nodePath), "/usr/local/bin", "/usr/bin", "/bin"])];
|
|
803
|
-
const env = {
|
|
804
|
-
RULEMETRIC_CLI_VERSION: this.config.version,
|
|
805
|
-
RULEMETRIC_CLI_ENTRY: cliBin,
|
|
806
|
-
PATH: pathDirs.join(":")
|
|
807
|
-
};
|
|
808
|
-
const envEntries = Object.entries(env).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
809
|
-
<string>${escapeXml(v)}</string>`).join("\n");
|
|
810
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
127
|
+
`}buildGatewayPlist(r){let{projectRoot:t,nodePath:e}=r,s=o(t,"apps","cli","dist","lib","gateway-entry.js"),i=o(t,"apps","cli","bin","run.js"),n=[...new Set([E(e),"/usr/local/bin","/usr/bin","/bin"])],a={RULEMETRIC_CLI_VERSION:this.config.version,RULEMETRIC_CLI_ENTRY:i,PATH:n.join(":")},c=Object.entries(a).map(([d,u])=>` <key>${l(d)}</key>
|
|
128
|
+
<string>${l(u)}</string>`).join(`
|
|
129
|
+
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
811
130
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
812
131
|
<plist version="1.0">
|
|
813
132
|
<dict>
|
|
814
133
|
<key>Label</key>
|
|
815
|
-
<string>${
|
|
134
|
+
<string>${X}</string>
|
|
816
135
|
|
|
817
136
|
<key>ProgramArguments</key>
|
|
818
137
|
<array>
|
|
819
|
-
<string>${
|
|
820
|
-
<string>${
|
|
138
|
+
<string>${l(e)}</string>
|
|
139
|
+
<string>${l(s)}</string>
|
|
821
140
|
</array>
|
|
822
141
|
|
|
823
142
|
<key>WorkingDirectory</key>
|
|
824
|
-
<string>${
|
|
143
|
+
<string>${l(t)}</string>
|
|
825
144
|
|
|
826
145
|
<key>EnvironmentVariables</key>
|
|
827
146
|
<dict>
|
|
828
|
-
${
|
|
147
|
+
${c}
|
|
829
148
|
</dict>
|
|
830
149
|
|
|
831
150
|
<key>RunAtLoad</key>
|
|
@@ -841,58 +160,41 @@ ${envEntries}
|
|
|
841
160
|
<integer>10</integer>
|
|
842
161
|
|
|
843
162
|
<key>StandardOutPath</key>
|
|
844
|
-
<string>${
|
|
163
|
+
<string>${l(o(p,"gateway-stdout.log"))}</string>
|
|
845
164
|
|
|
846
165
|
<key>StandardErrorPath</key>
|
|
847
|
-
<string>${
|
|
166
|
+
<string>${l(o(p,"gateway-stderr.log"))}</string>
|
|
848
167
|
|
|
849
168
|
<key>ProcessType</key>
|
|
850
169
|
<string>Background</string>
|
|
851
170
|
</dict>
|
|
852
171
|
</plist>
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
// `rulemetric proxy start` (see commands/proxy/start.ts:308) so the
|
|
857
|
-
// launchd path and the CLI path are interchangeable. We don't WatchPath
|
|
858
|
-
// the Python addon — mitmdump has its own addon reload that handles
|
|
859
|
-
// edits without a full process restart.
|
|
860
|
-
buildProxyPlist(opts) {
|
|
861
|
-
const { projectRoot, mitmdumpPath, envVars } = opts;
|
|
862
|
-
const addonPath = join(projectRoot, "packages", "proxy", "addon", "rulemetric_addon.py");
|
|
863
|
-
const pathDirs = [.../* @__PURE__ */ new Set([dirname(mitmdumpPath), "/usr/local/bin", "/usr/bin", "/bin"])];
|
|
864
|
-
const env = {
|
|
865
|
-
...envVars,
|
|
866
|
-
RULEMETRIC_PROJECT_PATH: projectRoot,
|
|
867
|
-
RULEMETRIC_CLI_VERSION: this.config.version,
|
|
868
|
-
PATH: pathDirs.join(":")
|
|
869
|
-
};
|
|
870
|
-
const envEntries = Object.entries(env).map(([k, v]) => ` <key>${escapeXml(k)}</key>
|
|
871
|
-
<string>${escapeXml(v)}</string>`).join("\n");
|
|
872
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
172
|
+
`}buildProxyPlist(r){let{projectRoot:t,mitmdumpPath:e,envVars:s}=r,i=o(t,"packages","proxy","addon","rulemetric_addon.py"),n=[...new Set([E(e),"/usr/local/bin","/usr/bin","/bin"])],a={...s,RULEMETRIC_PROJECT_PATH:t,RULEMETRIC_CLI_VERSION:this.config.version,PATH:n.join(":")},c=Object.entries(a).map(([d,u])=>` <key>${l(d)}</key>
|
|
173
|
+
<string>${l(u)}</string>`).join(`
|
|
174
|
+
`);return`<?xml version="1.0" encoding="UTF-8"?>
|
|
873
175
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
874
176
|
<plist version="1.0">
|
|
875
177
|
<dict>
|
|
876
178
|
<key>Label</key>
|
|
877
|
-
<string>${
|
|
179
|
+
<string>${J}</string>
|
|
878
180
|
|
|
879
181
|
<key>ProgramArguments</key>
|
|
880
182
|
<array>
|
|
881
|
-
<string>${
|
|
183
|
+
<string>${l(e)}</string>
|
|
882
184
|
<string>--listen-port</string>
|
|
883
|
-
<string>${
|
|
185
|
+
<string>${V}</string>
|
|
884
186
|
<string>-s</string>
|
|
885
|
-
<string>${
|
|
187
|
+
<string>${l(i)}</string>
|
|
886
188
|
<string>--set</string>
|
|
887
189
|
<string>console_eventlog_verbosity=info</string>
|
|
888
190
|
</array>
|
|
889
191
|
|
|
890
192
|
<key>WorkingDirectory</key>
|
|
891
|
-
<string>${
|
|
193
|
+
<string>${l(t)}</string>
|
|
892
194
|
|
|
893
195
|
<key>EnvironmentVariables</key>
|
|
894
196
|
<dict>
|
|
895
|
-
${
|
|
197
|
+
${c}
|
|
896
198
|
</dict>
|
|
897
199
|
|
|
898
200
|
<key>RunAtLoad</key>
|
|
@@ -908,39 +210,13 @@ ${envEntries}
|
|
|
908
210
|
<integer>15</integer>
|
|
909
211
|
|
|
910
212
|
<key>StandardOutPath</key>
|
|
911
|
-
<string>${
|
|
213
|
+
<string>${l(o(p,"proxy-stdout.log"))}</string>
|
|
912
214
|
|
|
913
215
|
<key>StandardErrorPath</key>
|
|
914
|
-
<string>${
|
|
216
|
+
<string>${l(o(p,"proxy-stderr.log"))}</string>
|
|
915
217
|
|
|
916
218
|
<key>ProcessType</key>
|
|
917
219
|
<string>Background</string>
|
|
918
220
|
</dict>
|
|
919
221
|
</plist>
|
|
920
|
-
|
|
921
|
-
}
|
|
922
|
-
async waitForApi(port, timeoutSecs) {
|
|
923
|
-
let lastError = null;
|
|
924
|
-
for (let i = 0; i < timeoutSecs; i++) {
|
|
925
|
-
try {
|
|
926
|
-
const res = await fetch(`http://localhost:${port}/health`);
|
|
927
|
-
if (res.ok) return true;
|
|
928
|
-
lastError = `health check returned ${res.status}`;
|
|
929
|
-
} catch (err) {
|
|
930
|
-
lastError = err.message;
|
|
931
|
-
}
|
|
932
|
-
await new Promise((r) => setTimeout(r, 1e3));
|
|
933
|
-
}
|
|
934
|
-
if (lastError) {
|
|
935
|
-
this.warn(`Last health check error: ${lastError}`);
|
|
936
|
-
}
|
|
937
|
-
return false;
|
|
938
|
-
}
|
|
939
|
-
};
|
|
940
|
-
function escapeXml(s) {
|
|
941
|
-
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
942
|
-
}
|
|
943
|
-
export {
|
|
944
|
-
ServiceInstall as default
|
|
945
|
-
};
|
|
946
|
-
//# sourceMappingURL=install.js.map
|
|
222
|
+
`}async waitForApi(r,t){let e=null;for(let s=0;s<t;s++){try{let i=await fetch(`http://localhost:${r}/health`);if(i.ok)return!0;e=`health check returned ${i.status}`}catch(i){e=i.message}await new Promise(i=>setTimeout(i,1e3))}return e&&this.warn(`Last health check error: ${e}`),!1}};function l(L){return L.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""")}$(l,"escapeXml");export{C as default};
|