@rulemetric/cli 0.6.9 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/base-command.js +4 -3
- package/dist/{chunk-SZ7VDCD6.js → chunk-2HK7RGZB.js} +22 -3
- package/dist/chunk-2HK7RGZB.js.map +7 -0
- package/dist/{chunk-RUCSRN7F.js → chunk-2ORJWTNZ.js} +2 -2
- package/dist/{chunk-SLL3OBKN.js → chunk-37LN5O7M.js} +2 -2
- package/dist/chunk-3S7223UR.js +81 -0
- package/dist/chunk-3S7223UR.js.map +7 -0
- package/dist/{chunk-DAJNO4OI.js → chunk-4LYCDS2N.js} +28 -16
- package/dist/chunk-4LYCDS2N.js.map +7 -0
- package/dist/{chunk-UPXIO7CG.js → chunk-4XAWK6S2.js} +8 -8
- package/dist/{chunk-QD6X4L4L.js → chunk-5BAOGNUH.js} +4 -4
- package/dist/chunk-5EDMGK3W.js +39 -0
- package/dist/chunk-5EDMGK3W.js.map +7 -0
- package/dist/{chunk-5ADHO5QF.js → chunk-6NBS5XFS.js} +6 -6
- package/dist/{chunk-BBLTN35F.js → chunk-7DULPSFU.js} +49 -20
- package/dist/chunk-7DULPSFU.js.map +7 -0
- package/dist/chunk-7LVVKQMZ.js +98 -0
- package/dist/chunk-7LVVKQMZ.js.map +7 -0
- package/dist/{chunk-PXVQLD7S.js → chunk-7NNFYWKK.js} +48 -1
- package/dist/chunk-7NNFYWKK.js.map +7 -0
- package/dist/{chunk-DTFXCHHI.js → chunk-DNKSVWMD.js} +4 -4
- package/dist/{chunk-CNMWYYSV.js → chunk-DUJSRBLP.js} +2 -2
- package/dist/chunk-EARWG4EV.js +22 -0
- package/dist/chunk-EARWG4EV.js.map +7 -0
- package/dist/{chunk-6EMZI4Y3.js → chunk-EI5BHWXA.js} +437 -401
- package/dist/chunk-EI5BHWXA.js.map +7 -0
- package/dist/{chunk-HNZLEEG2.js → chunk-EJ3YAFDP.js} +4 -4
- package/dist/{chunk-HNZLEEG2.js.map → chunk-EJ3YAFDP.js.map} +2 -2
- package/dist/{chunk-YSEQWCN4.js → chunk-EYPAAGN5.js} +2 -2
- package/dist/{chunk-JEJ3J5J6.js → chunk-GIUBARWS.js} +2 -2
- package/dist/chunk-GQNJO2P2.js +102 -0
- package/dist/chunk-GQNJO2P2.js.map +7 -0
- package/dist/chunk-ILYDELUE.js +46 -0
- package/dist/chunk-ILYDELUE.js.map +7 -0
- package/dist/chunk-J6QFW4LD.js +55 -0
- package/dist/chunk-J6QFW4LD.js.map +7 -0
- package/dist/{chunk-OTOWLUOJ.js → chunk-JULOLTZS.js} +2 -2
- package/dist/{chunk-DR5WLIOU.js → chunk-KJKBDQ4D.js} +2 -2
- package/dist/{chunk-XN23W5HL.js → chunk-LXKPNATC.js} +93 -7
- package/dist/chunk-LXKPNATC.js.map +7 -0
- package/dist/{chunk-N5A2IQAK.js → chunk-OK3I555A.js} +2 -2
- package/dist/{chunk-XNNOXGP6.js → chunk-OKWCKCPE.js} +38 -19
- package/dist/chunk-OKWCKCPE.js.map +7 -0
- package/dist/{chunk-AHDRN6NE.js → chunk-OO7JDFS4.js} +5 -5
- package/dist/{chunk-N2X35ITW.js → chunk-OQBBVTDG.js} +11 -1
- package/dist/{chunk-N2X35ITW.js.map → chunk-OQBBVTDG.js.map} +2 -2
- package/dist/{chunk-3QH73QOQ.js → chunk-PED7L5GX.js} +2 -2
- package/dist/{chunk-VBGUNFKY.js → chunk-PMI56ED5.js} +5 -5
- package/dist/{chunk-RCMXTLQF.js → chunk-RMTT4KDY.js} +2 -2
- package/dist/{chunk-W4MZXKZ2.js → chunk-UKMLICWS.js} +2 -2
- package/dist/{chunk-3POYC5NA.js → chunk-XK42ZBDE.js} +3 -3
- package/dist/{chunk-ZSGKRCKD.js → chunk-XUV6DDBF.js} +2 -2
- package/dist/{chunk-LAKZUE35.js → chunk-Y3WD5CSW.js} +2 -2
- package/dist/{chunk-3QVMIJUL.js → chunk-YOBA3NAM.js} +5 -5
- package/dist/{chunk-ZRLYHBPN.js → chunk-ZUJBM6HE.js} +20 -2
- package/dist/chunk-ZUJBM6HE.js.map +7 -0
- package/dist/commands/antigravity/backfill.js +5 -4
- package/dist/commands/antigravity/backfill.js.map +1 -1
- package/dist/commands/auth/create-key.js +4 -3
- package/dist/commands/auth/create-key.js.map +1 -1
- package/dist/commands/auth/login.js +13 -9
- package/dist/commands/auth/login.js.map +2 -2
- package/dist/commands/auth/logout.js +4 -3
- package/dist/commands/auth/logout.js.map +1 -1
- package/dist/commands/auth/revoke-key.js +4 -3
- package/dist/commands/auth/revoke-key.js.map +1 -1
- package/dist/commands/auth/signup.js +13 -9
- package/dist/commands/auth/signup.js.map +2 -2
- package/dist/commands/auth/status.js +4 -3
- package/dist/commands/auth/status.js.map +1 -1
- package/dist/commands/catalog/index.js +4 -3
- package/dist/commands/catalog/index.js.map +1 -1
- package/dist/commands/catalog/recommend.js +4 -3
- package/dist/commands/catalog/recommend.js.map +1 -1
- package/dist/commands/convert.js +4 -3
- package/dist/commands/convert.js.map +1 -1
- package/dist/commands/converters.js +4 -3
- package/dist/commands/converters.js.map +1 -1
- package/dist/commands/crons/list.js +4 -3
- package/dist/commands/crons/list.js.map +1 -1
- package/dist/commands/crons/run.js +4 -3
- package/dist/commands/crons/run.js.map +1 -1
- package/dist/commands/dashboard.js +4 -3
- package/dist/commands/dashboard.js.map +1 -1
- package/dist/commands/diagnostics.js +117 -0
- package/dist/commands/diagnostics.js.map +7 -0
- package/dist/commands/eval-targets/create.js +4 -3
- package/dist/commands/eval-targets/create.js.map +1 -1
- package/dist/commands/eval-targets/list.js +4 -3
- package/dist/commands/eval-targets/list.js.map +1 -1
- package/dist/commands/evals/agent.js +41 -20
- package/dist/commands/evals/agent.js.map +2 -2
- package/dist/commands/evals/benchmark.js +5 -4
- package/dist/commands/evals/benchmark.js.map +1 -1
- package/dist/commands/evals/compare.js +4 -3
- package/dist/commands/evals/compare.js.map +1 -1
- package/dist/commands/evals/create.js +4 -3
- package/dist/commands/evals/create.js.map +1 -1
- package/dist/commands/evals/optimize-description.js +4 -3
- package/dist/commands/evals/optimize-description.js.map +1 -1
- package/dist/commands/evals/optimize.js +4 -3
- package/dist/commands/evals/optimize.js.map +1 -1
- package/dist/commands/evals/run.js +4 -3
- package/dist/commands/evals/run.js.map +1 -1
- package/dist/commands/gateway/ensure.js +75 -5
- package/dist/commands/gateway/ensure.js.map +2 -2
- package/dist/commands/hooks/install.js +76 -23
- package/dist/commands/hooks/install.js.map +2 -2
- package/dist/commands/hooks/run.js +4 -3
- package/dist/commands/hooks/run.js.map +1 -1
- package/dist/commands/hooks/uninstall.js +31 -40
- package/dist/commands/hooks/uninstall.js.map +2 -2
- package/dist/commands/import.js +4 -3
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/instructions/create.js +4 -3
- package/dist/commands/instructions/create.js.map +1 -1
- package/dist/commands/instructions/delete.js +4 -3
- package/dist/commands/instructions/delete.js.map +1 -1
- package/dist/commands/instructions/fork.js +4 -3
- package/dist/commands/instructions/fork.js.map +1 -1
- package/dist/commands/instructions/get.js +4 -3
- package/dist/commands/instructions/get.js.map +1 -1
- package/dist/commands/instructions/list.js +4 -3
- package/dist/commands/instructions/list.js.map +1 -1
- package/dist/commands/instructions/promote.js +4 -3
- package/dist/commands/instructions/promote.js.map +1 -1
- package/dist/commands/instructions/pull.js +4 -3
- package/dist/commands/instructions/pull.js.map +1 -1
- package/dist/commands/instructions/upstream.js +4 -3
- package/dist/commands/instructions/upstream.js.map +1 -1
- package/dist/commands/instructions/versions.js +4 -3
- package/dist/commands/instructions/versions.js.map +1 -1
- package/dist/commands/launcher.js +4 -3
- package/dist/commands/launcher.js.map +1 -1
- package/dist/commands/org/current.js +4 -3
- package/dist/commands/org/current.js.map +1 -1
- package/dist/commands/org/list.js +4 -3
- package/dist/commands/org/list.js.map +1 -1
- package/dist/commands/org/switch.js +4 -3
- package/dist/commands/org/switch.js.map +1 -1
- package/dist/commands/packs/add-instruction.js +4 -3
- package/dist/commands/packs/add-instruction.js.map +1 -1
- package/dist/commands/packs/apply.js +4 -3
- package/dist/commands/packs/apply.js.map +1 -1
- package/dist/commands/packs/create.js +4 -3
- package/dist/commands/packs/create.js.map +1 -1
- package/dist/commands/packs/fork.js +4 -3
- package/dist/commands/packs/fork.js.map +1 -1
- package/dist/commands/packs/get.js +4 -3
- package/dist/commands/packs/get.js.map +1 -1
- package/dist/commands/packs/list.js +4 -3
- package/dist/commands/packs/list.js.map +1 -1
- package/dist/commands/packs/remove-instruction.js +4 -3
- package/dist/commands/packs/remove-instruction.js.map +1 -1
- package/dist/commands/proxy/env.js +7 -4
- package/dist/commands/proxy/env.js.map +1 -1
- package/dist/commands/proxy/logs.js +4 -3
- package/dist/commands/proxy/logs.js.map +1 -1
- package/dist/commands/proxy/restart.js +4 -3
- package/dist/commands/proxy/restart.js.map +1 -1
- package/dist/commands/proxy/setup.js +20 -14
- package/dist/commands/proxy/setup.js.map +2 -2
- package/dist/commands/proxy/start.js +29 -15
- package/dist/commands/proxy/start.js.map +2 -2
- package/dist/commands/proxy/status.js +16 -13
- package/dist/commands/proxy/status.js.map +2 -2
- package/dist/commands/proxy/stop.js +21 -12
- package/dist/commands/proxy/stop.js.map +2 -2
- package/dist/commands/research/backfill.js +5 -4
- package/dist/commands/research/backfill.js.map +1 -1
- package/dist/commands/research/tail-transcript.js +5 -4
- package/dist/commands/research/tail-transcript.js.map +1 -1
- package/dist/commands/service/install.js +6 -6
- package/dist/commands/service/status.js +4 -3
- package/dist/commands/service/status.js.map +1 -1
- package/dist/commands/service/uninstall.js +4 -3
- package/dist/commands/service/uninstall.js.map +1 -1
- package/dist/commands/sessions/analyze.js +4 -3
- package/dist/commands/sessions/analyze.js.map +1 -1
- package/dist/commands/sessions/end.js +4 -3
- package/dist/commands/sessions/end.js.map +1 -1
- package/dist/commands/sessions/import.js +4 -3
- package/dist/commands/sessions/import.js.map +1 -1
- package/dist/commands/sessions/list.js +4 -3
- package/dist/commands/sessions/list.js.map +1 -1
- package/dist/commands/sessions/start.js +4 -3
- package/dist/commands/sessions/start.js.map +1 -1
- package/dist/commands/setup.js +10 -6
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skills/export-approved.js +4 -3
- package/dist/commands/skills/export-approved.js.map +1 -1
- package/dist/commands/skills/install.js +4 -3
- package/dist/commands/skills/install.js.map +1 -1
- package/dist/commands/skills/list.js +4 -3
- package/dist/commands/skills/list.js.map +1 -1
- package/dist/commands/skills/publish.js +4 -3
- package/dist/commands/skills/publish.js.map +1 -1
- package/dist/commands/skills/search.js +4 -3
- package/dist/commands/skills/search.js.map +1 -1
- package/dist/commands/skills/update.js +4 -3
- package/dist/commands/skills/update.js.map +1 -1
- package/dist/commands/suggestions/accept.js +4 -3
- package/dist/commands/suggestions/accept.js.map +1 -1
- package/dist/commands/suggestions/dismiss.js +4 -3
- package/dist/commands/suggestions/dismiss.js.map +1 -1
- package/dist/commands/suggestions/list.js +4 -3
- package/dist/commands/suggestions/list.js.map +1 -1
- package/dist/commands/suggestions/refresh.js +4 -3
- package/dist/commands/suggestions/refresh.js.map +1 -1
- package/dist/dashboard/Dashboard.js +16 -16
- package/dist/dashboard/data.js +2 -2
- package/dist/dashboard/launcher/ActiveJobsList.js +2 -2
- package/dist/dashboard/launcher/LauncherOverlay.js +7 -7
- package/dist/dashboard/launcher/NewLaunchForm.js +2 -2
- package/dist/dashboard/launcher/api.js +2 -2
- package/dist/dashboard/launcher/useLaunchJobs.js +3 -3
- package/dist/lib/active-org-refresh.js +2 -2
- package/dist/lib/agent-loop.js +19 -17
- package/dist/lib/antigravity-transcript.js +2 -2
- package/dist/lib/api-client.js +1 -1
- package/dist/lib/capture-hosts.js +8 -0
- package/dist/lib/capture-hosts.js.map +7 -0
- package/dist/lib/diagnostics.js +12 -0
- package/dist/lib/diagnostics.js.map +7 -0
- package/dist/lib/editor-proxy-pins.js +14 -0
- package/dist/lib/editor-proxy-pins.js.map +7 -0
- package/dist/lib/ensure-api-key.js +3 -3
- package/dist/lib/eval-benchmark.js +2 -2
- package/dist/lib/eval-conversation-handler.js +2 -2
- package/dist/lib/gateway-entry.js +39 -1
- package/dist/lib/gateway-entry.js.map +2 -2
- package/dist/lib/gateway-identity.js +16 -0
- package/dist/lib/gateway-identity.js.map +7 -0
- package/dist/lib/gateway-lifecycle.js +9 -3
- package/dist/lib/gateway.js +3 -1
- package/dist/lib/handlers/cron-suggest-instructions.js +3 -3
- package/dist/lib/handlers/process-announcement.js +3 -3
- package/dist/lib/handlers/process-changelog.js +2 -2
- package/dist/lib/handlers/process-conversation.js +3 -3
- package/dist/lib/handlers/process-eval.js +2 -2
- package/dist/lib/handlers/process-insights.js +2 -2
- package/dist/lib/handlers/process-launch.js +2 -2
- package/dist/lib/handlers/process-run-cleanup.js +2 -2
- package/dist/lib/handlers/process-session-goal.js +2 -2
- package/dist/lib/hooks-config.js +18 -1
- package/dist/lib/manual-tasks.js +5 -5
- package/dist/lib/pid-identity.js +14 -0
- package/dist/lib/pid-identity.js.map +7 -0
- package/dist/lib/proxy-respawn.js +12 -0
- package/dist/lib/proxy-respawn.js.map +7 -0
- package/dist/lib/research-client.js +2 -2
- package/dist/lib/setup-steps.js +7 -4
- package/dist/lib/statusline-shim.js +3 -1
- package/dist/lib/telemetry.js +4 -2
- package/dist/lib/terminal-target-heartbeat.js +2 -2
- package/oclif.manifest.json +34 -1
- package/package.json +4 -4
- package/dist/chunk-6EMZI4Y3.js.map +0 -7
- package/dist/chunk-BBLTN35F.js.map +0 -7
- package/dist/chunk-DAJNO4OI.js.map +0 -7
- package/dist/chunk-PXVQLD7S.js.map +0 -7
- package/dist/chunk-SZ7VDCD6.js.map +0 -7
- package/dist/chunk-TX2HKJLA.js +0 -55
- package/dist/chunk-TX2HKJLA.js.map +0 -7
- package/dist/chunk-XN23W5HL.js.map +0 -7
- package/dist/chunk-XNNOXGP6.js.map +0 -7
- package/dist/chunk-ZRLYHBPN.js.map +0 -7
- /package/dist/{chunk-RUCSRN7F.js.map → chunk-2ORJWTNZ.js.map} +0 -0
- /package/dist/{chunk-SLL3OBKN.js.map → chunk-37LN5O7M.js.map} +0 -0
- /package/dist/{chunk-UPXIO7CG.js.map → chunk-4XAWK6S2.js.map} +0 -0
- /package/dist/{chunk-QD6X4L4L.js.map → chunk-5BAOGNUH.js.map} +0 -0
- /package/dist/{chunk-5ADHO5QF.js.map → chunk-6NBS5XFS.js.map} +0 -0
- /package/dist/{chunk-DTFXCHHI.js.map → chunk-DNKSVWMD.js.map} +0 -0
- /package/dist/{chunk-CNMWYYSV.js.map → chunk-DUJSRBLP.js.map} +0 -0
- /package/dist/{chunk-YSEQWCN4.js.map → chunk-EYPAAGN5.js.map} +0 -0
- /package/dist/{chunk-JEJ3J5J6.js.map → chunk-GIUBARWS.js.map} +0 -0
- /package/dist/{chunk-OTOWLUOJ.js.map → chunk-JULOLTZS.js.map} +0 -0
- /package/dist/{chunk-DR5WLIOU.js.map → chunk-KJKBDQ4D.js.map} +0 -0
- /package/dist/{chunk-N5A2IQAK.js.map → chunk-OK3I555A.js.map} +0 -0
- /package/dist/{chunk-AHDRN6NE.js.map → chunk-OO7JDFS4.js.map} +0 -0
- /package/dist/{chunk-3QH73QOQ.js.map → chunk-PED7L5GX.js.map} +0 -0
- /package/dist/{chunk-VBGUNFKY.js.map → chunk-PMI56ED5.js.map} +0 -0
- /package/dist/{chunk-RCMXTLQF.js.map → chunk-RMTT4KDY.js.map} +0 -0
- /package/dist/{chunk-W4MZXKZ2.js.map → chunk-UKMLICWS.js.map} +0 -0
- /package/dist/{chunk-3POYC5NA.js.map → chunk-XK42ZBDE.js.map} +0 -0
- /package/dist/{chunk-ZSGKRCKD.js.map → chunk-XUV6DDBF.js.map} +0 -0
- /package/dist/{chunk-LAKZUE35.js.map → chunk-Y3WD5CSW.js.map} +0 -0
- /package/dist/{chunk-3QVMIJUL.js.map → chunk-YOBA3NAM.js.map} +0 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// src/lib/gateway-identity.ts
|
|
2
|
+
import { execFileSync } from "node:child_process";
|
|
3
|
+
import * as http from "node:http";
|
|
4
|
+
var GATEWAY_IDENTITY_PATH = "/__rulemetric_gateway__";
|
|
5
|
+
var GATEWAY_IDENTITY_SERVICE = "rulemetric-gateway";
|
|
6
|
+
var MAX_IDENTITY_BODY_BYTES = 4096;
|
|
7
|
+
function probeGatewayIdentity(port, timeoutMs = 1500) {
|
|
8
|
+
return new Promise((resolve) => {
|
|
9
|
+
let settled = false;
|
|
10
|
+
const finish = (result) => {
|
|
11
|
+
if (settled) return;
|
|
12
|
+
settled = true;
|
|
13
|
+
resolve(result);
|
|
14
|
+
};
|
|
15
|
+
const req = http.get(
|
|
16
|
+
{ host: "127.0.0.1", port, path: GATEWAY_IDENTITY_PATH, timeout: timeoutMs },
|
|
17
|
+
(res) => {
|
|
18
|
+
let body = "";
|
|
19
|
+
res.setEncoding("utf-8");
|
|
20
|
+
res.on("data", (chunk) => {
|
|
21
|
+
body += chunk;
|
|
22
|
+
if (body.length > MAX_IDENTITY_BODY_BYTES) {
|
|
23
|
+
req.destroy();
|
|
24
|
+
finish({ status: "foreign", detail: "oversized identity response" });
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
res.on("end", () => {
|
|
28
|
+
if (res.statusCode === 200) {
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(body);
|
|
31
|
+
if (parsed?.service === GATEWAY_IDENTITY_SERVICE) {
|
|
32
|
+
finish({
|
|
33
|
+
status: "ours-live",
|
|
34
|
+
version: typeof parsed.version === "string" ? parsed.version : null,
|
|
35
|
+
pid: typeof parsed.pid === "number" ? parsed.pid : null
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finish({ status: "foreign", detail: `HTTP ${res.statusCode ?? "?"} without identity marker` });
|
|
43
|
+
});
|
|
44
|
+
res.on("error", () => finish({ status: "foreign", detail: "response stream error" }));
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
req.on("timeout", () => {
|
|
48
|
+
req.destroy();
|
|
49
|
+
finish({ status: "foreign", detail: "accepted TCP but no identity response (timeout)" });
|
|
50
|
+
});
|
|
51
|
+
req.on("error", (err) => {
|
|
52
|
+
const code = err.code;
|
|
53
|
+
if (code === "ECONNREFUSED") {
|
|
54
|
+
finish({ status: "dead" });
|
|
55
|
+
} else {
|
|
56
|
+
finish({ status: "foreign", detail: `socket error ${code ?? err.message}` });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async function waitForGatewayIdentity(port, { timeoutMs = 3e3, intervalMs = 150 } = {}) {
|
|
62
|
+
const deadline = Date.now() + timeoutMs;
|
|
63
|
+
for (; ; ) {
|
|
64
|
+
const identity = await probeGatewayIdentity(port, Math.min(1500, timeoutMs));
|
|
65
|
+
if (identity.status !== "dead") return identity;
|
|
66
|
+
if (Date.now() >= deadline) return identity;
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function runLsofDefault(port) {
|
|
71
|
+
try {
|
|
72
|
+
return execFileSync("lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN"], {
|
|
73
|
+
encoding: "utf-8",
|
|
74
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
75
|
+
});
|
|
76
|
+
} catch {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function describePortOwner(port, opts = {}) {
|
|
81
|
+
const output = (opts.runLsof ?? runLsofDefault)(port);
|
|
82
|
+
if (!output) return null;
|
|
83
|
+
const rows = output.split("\n").filter((line) => line.trim().length > 0);
|
|
84
|
+
const dataRow = rows.find((line) => !/^COMMAND\s/.test(line));
|
|
85
|
+
if (!dataRow) return null;
|
|
86
|
+
const [command, pid] = dataRow.trim().split(/\s+/);
|
|
87
|
+
if (!command) return null;
|
|
88
|
+
return pid ? `${command} (PID ${pid})` : command;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export {
|
|
92
|
+
GATEWAY_IDENTITY_PATH,
|
|
93
|
+
GATEWAY_IDENTITY_SERVICE,
|
|
94
|
+
probeGatewayIdentity,
|
|
95
|
+
waitForGatewayIdentity,
|
|
96
|
+
describePortOwner
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=chunk-7LVVKQMZ.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/gateway-identity.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Gateway identity handshake \u2014 distinguishes OUR gateway from a foreign\n * process squatting on :8787.\n *\n * A bare TCP-accept probe cannot tell the difference, and pinning Claude\n * Code's HTTPS_PROXY at a port owned by some other app breaks ALL of its\n * HTTPS (the worst client-side failure we have). The gateway serves a JSON\n * marker at GATEWAY_IDENTITY_PATH; this module probes it and classifies the\n * port as ours-live / foreign / dead. HTTPS_PROXY may only be pinned on\n * ours-live.\n *\n * Kept free of oclif so gateway-entry (a standalone Node process) can import it.\n */\nimport { execFileSync } from 'node:child_process';\nimport * as http from 'node:http';\n\nexport const GATEWAY_IDENTITY_PATH = '/__rulemetric_gateway__';\nexport const GATEWAY_IDENTITY_SERVICE = 'rulemetric-gateway';\n// The probe reads at most this much of a response \u2014 a foreign server could\n// stream forever on an unknown path.\nconst MAX_IDENTITY_BODY_BYTES = 4096;\n\nexport type GatewayIdentity =\n /** The port answered with our JSON marker \u2014 safe to pin HTTPS_PROXY. */\n | { status: 'ours-live'; version: string | null; pid: number | null }\n /** Something accepts connections but is NOT our gateway \u2014 never pin. */\n | { status: 'foreign'; detail: string }\n /** Nothing is listening \u2014 safe to spawn a gateway, unsafe to pin. */\n | { status: 'dead' };\n\n/**\n * One-shot identity probe against 127.0.0.1:<port>. Connects directly\n * (node:http ignores HTTPS_PROXY env), requests the identity path, and\n * verifies the marker. Anything that accepts TCP but fails the handshake \u2014\n * wrong body, non-JSON, hang, mid-request reset \u2014 is classified foreign:\n * the conservative direction, since foreign means \"do not pin\".\n */\nexport function probeGatewayIdentity(port: number, timeoutMs = 1500): Promise<GatewayIdentity> {\n return new Promise((resolve) => {\n let settled = false;\n const finish = (result: GatewayIdentity) => {\n if (settled) return;\n settled = true;\n resolve(result);\n };\n\n const req = http.get(\n { host: '127.0.0.1', port, path: GATEWAY_IDENTITY_PATH, timeout: timeoutMs },\n (res) => {\n let body = '';\n res.setEncoding('utf-8');\n res.on('data', (chunk: string) => {\n body += chunk;\n if (body.length > MAX_IDENTITY_BODY_BYTES) {\n req.destroy();\n finish({ status: 'foreign', detail: 'oversized identity response' });\n }\n });\n res.on('end', () => {\n if (res.statusCode === 200) {\n try {\n const parsed = JSON.parse(body) as { service?: unknown; version?: unknown; pid?: unknown };\n if (parsed?.service === GATEWAY_IDENTITY_SERVICE) {\n finish({\n status: 'ours-live',\n version: typeof parsed.version === 'string' ? parsed.version : null,\n pid: typeof parsed.pid === 'number' ? parsed.pid : null,\n });\n return;\n }\n } catch {\n /* not our JSON \u2014 fall through to foreign */\n }\n }\n finish({ status: 'foreign', detail: `HTTP ${res.statusCode ?? '?'} without identity marker` });\n });\n res.on('error', () => finish({ status: 'foreign', detail: 'response stream error' }));\n },\n );\n\n req.on('timeout', () => {\n // The connection was accepted but nothing answered the handshake in\n // time \u2014 on loopback that is a listener that isn't our gateway.\n req.destroy();\n finish({ status: 'foreign', detail: 'accepted TCP but no identity response (timeout)' });\n });\n req.on('error', (err) => {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === 'ECONNREFUSED') {\n finish({ status: 'dead' });\n } else {\n // Accepted-then-reset (e.g. a TLS-only listener) and anything else\n // that isn't a clean refusal means SOMETHING owns the port.\n finish({ status: 'foreign', detail: `socket error ${code ?? err.message}` });\n }\n });\n });\n}\n\n/**\n * Poll probeGatewayIdentity() until it resolves to a terminal state or the\n * window elapses. 'dead' is the only retryable state (a just-spawned gateway\n * needs a moment to bind); 'ours-live' and 'foreign' can't change by waiting.\n */\nexport async function waitForGatewayIdentity(\n port: number,\n { timeoutMs = 3000, intervalMs = 150 }: { timeoutMs?: number; intervalMs?: number } = {},\n): Promise<GatewayIdentity> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n const identity = await probeGatewayIdentity(port, Math.min(1500, timeoutMs));\n if (identity.status !== 'dead') return identity;\n if (Date.now() >= deadline) return identity;\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport interface DescribePortOwnerOptions {\n /** Override the `lsof` exec. Return raw lsof output, or null on failure. Used in tests. */\n runLsof?: (port: number) => string | null;\n}\n\nfunction runLsofDefault(port: number): string | null {\n try {\n return execFileSync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN'], {\n encoding: 'utf-8',\n stdio: ['ignore', 'pipe', 'ignore'],\n });\n } catch {\n // lsof missing (some Linuxes) or no permission \u2014 the caller degrades to a\n // generic \"something else holds the port\" message.\n return null;\n }\n}\n\n/**\n * Best-effort \"what is holding this port\" description for user-facing\n * messages, e.g. `dev-server (PID 4242)`. Returns null when it can't tell.\n */\nexport function describePortOwner(port: number, opts: DescribePortOwnerOptions = {}): string | null {\n const output = (opts.runLsof ?? runLsofDefault)(port);\n if (!output) return null;\n // lsof output: header line, then `COMMAND PID USER ...` rows.\n const rows = output.split('\\n').filter((line) => line.trim().length > 0);\n const dataRow = rows.find((line) => !/^COMMAND\\s/.test(line));\n if (!dataRow) return null;\n const [command, pid] = dataRow.trim().split(/\\s+/);\n if (!command) return null;\n return pid ? `${command} (PID ${pid})` : command;\n}\n"],
|
|
5
|
+
"mappings": ";AAaA,SAAS,oBAAoB;AAC7B,YAAY,UAAU;AAEf,IAAM,wBAAwB;AAC9B,IAAM,2BAA2B;AAGxC,IAAM,0BAA0B;AAiBzB,SAAS,qBAAqB,MAAc,YAAY,MAAgC;AAC7F,SAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,QAAI,UAAU;AACd,UAAM,SAAS,CAAC,WAA4B;AAC1C,UAAI,QAAS;AACb,gBAAU;AACV,cAAQ,MAAM;AAAA,IAChB;AAEA,UAAM,MAAW;AAAA,MACf,EAAE,MAAM,aAAa,MAAM,MAAM,uBAAuB,SAAS,UAAU;AAAA,MAC3E,CAAC,QAAQ;AACP,YAAI,OAAO;AACX,YAAI,YAAY,OAAO;AACvB,YAAI,GAAG,QAAQ,CAAC,UAAkB;AAChC,kBAAQ;AACR,cAAI,KAAK,SAAS,yBAAyB;AACzC,gBAAI,QAAQ;AACZ,mBAAO,EAAE,QAAQ,WAAW,QAAQ,8BAA8B,CAAC;AAAA,UACrE;AAAA,QACF,CAAC;AACD,YAAI,GAAG,OAAO,MAAM;AAClB,cAAI,IAAI,eAAe,KAAK;AAC1B,gBAAI;AACF,oBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,kBAAI,QAAQ,YAAY,0BAA0B;AAChD,uBAAO;AAAA,kBACL,QAAQ;AAAA,kBACR,SAAS,OAAO,OAAO,YAAY,WAAW,OAAO,UAAU;AAAA,kBAC/D,KAAK,OAAO,OAAO,QAAQ,WAAW,OAAO,MAAM;AAAA,gBACrD,CAAC;AACD;AAAA,cACF;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AACA,iBAAO,EAAE,QAAQ,WAAW,QAAQ,QAAQ,IAAI,cAAc,GAAG,2BAA2B,CAAC;AAAA,QAC/F,CAAC;AACD,YAAI,GAAG,SAAS,MAAM,OAAO,EAAE,QAAQ,WAAW,QAAQ,wBAAwB,CAAC,CAAC;AAAA,MACtF;AAAA,IACF;AAEA,QAAI,GAAG,WAAW,MAAM;AAGtB,UAAI,QAAQ;AACZ,aAAO,EAAE,QAAQ,WAAW,QAAQ,kDAAkD,CAAC;AAAA,IACzF,CAAC;AACD,QAAI,GAAG,SAAS,CAAC,QAAQ;AACvB,YAAM,OAAQ,IAA8B;AAC5C,UAAI,SAAS,gBAAgB;AAC3B,eAAO,EAAE,QAAQ,OAAO,CAAC;AAAA,MAC3B,OAAO;AAGL,eAAO,EAAE,QAAQ,WAAW,QAAQ,gBAAgB,QAAQ,IAAI,OAAO,GAAG,CAAC;AAAA,MAC7E;AAAA,IACF,CAAC;AAAA,EACH,CAAC;AACH;AAOA,eAAsB,uBACpB,MACA,EAAE,YAAY,KAAM,aAAa,IAAI,IAAiD,CAAC,GAC7D;AAC1B,QAAM,WAAW,KAAK,IAAI,IAAI;AAC9B,aAAS;AACP,UAAM,WAAW,MAAM,qBAAqB,MAAM,KAAK,IAAI,MAAM,SAAS,CAAC;AAC3E,QAAI,SAAS,WAAW,OAAQ,QAAO;AACvC,QAAI,KAAK,IAAI,KAAK,SAAU,QAAO;AACnC,UAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,EAChE;AACF;AAOA,SAAS,eAAe,MAA6B;AACnD,MAAI;AACF,WAAO,aAAa,QAAQ,CAAC,OAAO,SAAS,IAAI,IAAI,cAAc,GAAG;AAAA,MACpE,UAAU;AAAA,MACV,OAAO,CAAC,UAAU,QAAQ,QAAQ;AAAA,IACpC,CAAC;AAAA,EACH,QAAQ;AAGN,WAAO;AAAA,EACT;AACF;AAMO,SAAS,kBAAkB,MAAc,OAAiC,CAAC,GAAkB;AAClG,QAAM,UAAU,KAAK,WAAW,gBAAgB,IAAI;AACpD,MAAI,CAAC,OAAQ,QAAO;AAEpB,QAAM,OAAO,OAAO,MAAM,IAAI,EAAE,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC;AACvE,QAAM,UAAU,KAAK,KAAK,CAAC,SAAS,CAAC,aAAa,KAAK,IAAI,CAAC;AAC5D,MAAI,CAAC,QAAS,QAAO;AACrB,QAAM,CAAC,SAAS,GAAG,IAAI,QAAQ,KAAK,EAAE,MAAM,KAAK;AACjD,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO,MAAM,GAAG,OAAO,SAAS,GAAG,MAAM;AAC3C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
MitmproxyRespawnMonitor
|
|
3
|
+
} from "./chunk-ILYDELUE.js";
|
|
4
|
+
import {
|
|
5
|
+
GATEWAY_IDENTITY_PATH,
|
|
6
|
+
GATEWAY_IDENTITY_SERVICE
|
|
7
|
+
} from "./chunk-7LVVKQMZ.js";
|
|
8
|
+
|
|
1
9
|
// src/lib/gateway.ts
|
|
2
10
|
import { spawn } from "node:child_process";
|
|
3
11
|
import * as fs from "node:fs";
|
|
@@ -11,9 +19,12 @@ var HEALTH_INTERVAL = 3e3;
|
|
|
11
19
|
var HEALTH_TIMEOUT = 500;
|
|
12
20
|
var RESTART_COOLDOWN_MS = 6e4;
|
|
13
21
|
var VERSION_FILE = path.join(os.homedir(), ".config", "rulemetric", "proxy-addon.version");
|
|
22
|
+
var PROXY_PID_FILE = path.join(os.homedir(), ".config", "rulemetric", "proxy.pid");
|
|
14
23
|
var isProxyAlive = false;
|
|
15
24
|
var lastSeenAddonVersion = null;
|
|
16
25
|
var lastRestartAt = 0;
|
|
26
|
+
var identityVersion = "unknown";
|
|
27
|
+
var respawnMonitor = null;
|
|
17
28
|
function log(msg) {
|
|
18
29
|
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
19
30
|
console.log(`[${ts}] ${msg}`);
|
|
@@ -46,14 +57,31 @@ function maybeRestartOnDrift(gatewayCliVersion, cliEntry) {
|
|
|
46
57
|
`addon version drift detected (gateway=${gatewayCliVersion}, addon=${addonVersion}). Restarting proxy to pick up latest code.`
|
|
47
58
|
);
|
|
48
59
|
lastRestartAt = Date.now();
|
|
60
|
+
spawnProxyRestart(cliEntry);
|
|
61
|
+
}
|
|
62
|
+
function spawnProxyRestart(cliEntry) {
|
|
63
|
+
if (!cliEntry) {
|
|
64
|
+
log("cannot restart proxy: no CLI entry recorded (RULEMETRIC_CLI_ENTRY unset)");
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
49
67
|
try {
|
|
50
68
|
const child = spawn(process.execPath, [cliEntry, "proxy", "restart"], {
|
|
51
69
|
detached: true,
|
|
52
70
|
stdio: "ignore"
|
|
53
71
|
});
|
|
54
72
|
child.unref();
|
|
73
|
+
return true;
|
|
55
74
|
} catch (err) {
|
|
56
75
|
log(`restart spawn failed: ${err.message}`);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function proxyCrashEvidence() {
|
|
80
|
+
try {
|
|
81
|
+
const content = fs.readFileSync(PROXY_PID_FILE, "utf-8").trim();
|
|
82
|
+
return /^\d+$/.test(content);
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
57
85
|
}
|
|
58
86
|
}
|
|
59
87
|
function checkProxyHealth(mitmproxyPort) {
|
|
@@ -64,12 +92,14 @@ function checkProxyHealth(mitmproxyPort) {
|
|
|
64
92
|
isProxyAlive = true;
|
|
65
93
|
log(`mitmproxy up on :${mitmproxyPort}, routing through proxy`);
|
|
66
94
|
}
|
|
95
|
+
respawnMonitor?.onHealthCheck(true);
|
|
67
96
|
});
|
|
68
97
|
sock.on("error", () => {
|
|
69
98
|
if (isProxyAlive) {
|
|
70
99
|
isProxyAlive = false;
|
|
71
100
|
log(`mitmproxy down on :${mitmproxyPort}, routing direct`);
|
|
72
101
|
}
|
|
102
|
+
respawnMonitor?.onHealthCheck(false);
|
|
73
103
|
});
|
|
74
104
|
sock.on("timeout", () => {
|
|
75
105
|
sock.destroy();
|
|
@@ -77,6 +107,7 @@ function checkProxyHealth(mitmproxyPort) {
|
|
|
77
107
|
isProxyAlive = false;
|
|
78
108
|
log(`mitmproxy timeout on :${mitmproxyPort}, routing direct`);
|
|
79
109
|
}
|
|
110
|
+
respawnMonitor?.onHealthCheck(false);
|
|
80
111
|
});
|
|
81
112
|
}
|
|
82
113
|
function handleConnect(req, clientSocket, head, mitmproxyPort) {
|
|
@@ -133,6 +164,16 @@ function connectDirect(req, clientSocket, head, targetHost, targetPort) {
|
|
|
133
164
|
});
|
|
134
165
|
}
|
|
135
166
|
function handleRequest(req, res, mitmproxyPort) {
|
|
167
|
+
if (req.url === GATEWAY_IDENTITY_PATH) {
|
|
168
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
169
|
+
res.end(JSON.stringify({
|
|
170
|
+
service: GATEWAY_IDENTITY_SERVICE,
|
|
171
|
+
version: identityVersion,
|
|
172
|
+
pid: process.pid,
|
|
173
|
+
mitmproxyAlive: isProxyAlive
|
|
174
|
+
}));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
136
177
|
const targetUrl = req.url ?? "";
|
|
137
178
|
let parsedUrl;
|
|
138
179
|
try {
|
|
@@ -163,6 +204,12 @@ function handleRequest(req, res, mitmproxyPort) {
|
|
|
163
204
|
function startGateway(port = GATEWAY_PORT, mitmproxyPort = MITMPROXY_PORT, options = {}) {
|
|
164
205
|
const cliVersion = options.cliVersion ?? process.env.RULEMETRIC_CLI_VERSION ?? "unknown";
|
|
165
206
|
const cliEntry = options.cliEntry ?? process.env.RULEMETRIC_CLI_ENTRY ?? null;
|
|
207
|
+
identityVersion = cliVersion;
|
|
208
|
+
respawnMonitor = new MitmproxyRespawnMonitor({
|
|
209
|
+
hasCrashEvidence: proxyCrashEvidence,
|
|
210
|
+
respawn: () => spawnProxyRestart(cliEntry),
|
|
211
|
+
log
|
|
212
|
+
});
|
|
166
213
|
const server = http.createServer((req, res) => {
|
|
167
214
|
handleRequest(req, res, mitmproxyPort);
|
|
168
215
|
});
|
|
@@ -193,4 +240,4 @@ export {
|
|
|
193
240
|
MITMPROXY_PORT,
|
|
194
241
|
startGateway
|
|
195
242
|
};
|
|
196
|
-
//# sourceMappingURL=chunk-
|
|
243
|
+
//# sourceMappingURL=chunk-7NNFYWKK.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/gateway.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Resilient HTTP proxy gateway that always accepts connections on GATEWAY_PORT.\n * Routes through mitmproxy when it's alive, direct to upstream when it's not.\n */\nimport { spawn } from 'node:child_process';\nimport * as fs from 'node:fs';\nimport * as http from 'node:http';\nimport * as net from 'node:net';\nimport * as os from 'node:os';\nimport * as path from 'node:path';\nimport { GATEWAY_IDENTITY_PATH, GATEWAY_IDENTITY_SERVICE } from './gateway-identity.js';\nimport { MitmproxyRespawnMonitor } from './proxy-respawn.js';\n\nexport const GATEWAY_PORT = 8787;\nexport const MITMPROXY_PORT = 8788;\nconst HEALTH_INTERVAL = 3000;\nconst HEALTH_TIMEOUT = 500;\n// Floor on auto-restarts so a misconfigured drift can't trigger a restart\n// loop. Plenty long for the user to fix any underlying problem manually.\nconst RESTART_COOLDOWN_MS = 60_000;\nconst VERSION_FILE = path.join(os.homedir(), '.config', 'rulemetric', 'proxy-addon.version');\n// Crash evidence for the mitmproxy auto-restart: `proxy stop` removes this\n// file, a crash leaves it behind.\nconst PROXY_PID_FILE = path.join(os.homedir(), '.config', 'rulemetric', 'proxy.pid');\n\nlet isProxyAlive = false;\nlet lastState: boolean | null = null;\n// Drift state \u2014 tracked across health-check ticks. lastSeenAddonVersion is\n// what the file said last time we read it; lastRestartAt prevents loops.\nlet lastSeenAddonVersion: string | null = null;\nlet lastRestartAt = 0;\n// Set by startGateway; served on the identity endpoint so callers can verify\n// the thing on :8787 is actually us (and which build).\nlet identityVersion = 'unknown';\n// Mid-session mitmproxy crash recovery \u2014 constructed in startGateway.\nlet respawnMonitor: MitmproxyRespawnMonitor | null = null;\n\nfunction log(msg: string): void {\n const ts = new Date().toISOString();\n console.log(`[${ts}] ${msg}`);\n}\n\nfunction readAddonVersion(): string | null {\n try {\n return fs.readFileSync(VERSION_FILE, 'utf-8').trim() || null;\n } catch {\n // File may not exist yet (proxy not started, or older addon that doesn't\n // write the file). Returning null disables drift checks for this tick.\n return null;\n }\n}\n\nfunction maybeRestartOnDrift(gatewayCliVersion: string, cliEntry: string | null): void {\n // No reference version means we can't compare \u2014 skip.\n if (gatewayCliVersion === 'unknown' || !cliEntry) return;\n const addonVersion = readAddonVersion();\n if (!addonVersion || addonVersion === 'unknown') return;\n // Same as last tick \u2014 nothing changed, no need to log or act.\n if (addonVersion === lastSeenAddonVersion) return;\n lastSeenAddonVersion = addonVersion;\n\n if (addonVersion === gatewayCliVersion) {\n // Drift cleared (e.g. proxy was just restarted). Quiet log, no action.\n log(`addon version matches gateway (${gatewayCliVersion})`);\n return;\n }\n\n const sinceLast = Date.now() - lastRestartAt;\n if (sinceLast < RESTART_COOLDOWN_MS) {\n log(\n `addon version drift (gateway=${gatewayCliVersion}, addon=${addonVersion}); ` +\n `restart cooldown active (${Math.round((RESTART_COOLDOWN_MS - sinceLast) / 1000)}s left)`,\n );\n return;\n }\n\n log(\n `addon version drift detected (gateway=${gatewayCliVersion}, addon=${addonVersion}). ` +\n `Restarting proxy to pick up latest code.`,\n );\n lastRestartAt = Date.now();\n spawnProxyRestart(cliEntry);\n}\n\n/**\n * Fire one detached `rulemetric proxy restart` via the CLI entry the spawning\n * process recorded. Shared by the version-drift restart and the mid-session\n * crash auto-restart. Output goes to /dev/null; the proxy.log captures the\n * restart events from mitmproxy itself.\n */\nfunction spawnProxyRestart(cliEntry: string | null): boolean {\n if (!cliEntry) {\n log('cannot restart proxy: no CLI entry recorded (RULEMETRIC_CLI_ENTRY unset)');\n return false;\n }\n try {\n const child = spawn(process.execPath, [cliEntry, 'proxy', 'restart'], {\n detached: true,\n stdio: 'ignore',\n });\n child.unref();\n return true;\n } catch (err) {\n log(`restart spawn failed: ${(err as Error).message}`);\n return false;\n }\n}\n\n/**\n * True when the local proxy.pid file says a local-mode mitmproxy SHOULD be\n * running (numeric PID). `proxy stop` deletes the file, so its absence means\n * a deliberate stop \u2014 never auto-restart over that. A `docker:` marker means\n * Docker owns the lifecycle.\n */\nfunction proxyCrashEvidence(): boolean {\n try {\n const content = fs.readFileSync(PROXY_PID_FILE, 'utf-8').trim();\n return /^\\d+$/.test(content);\n } catch {\n return false;\n }\n}\n\nfunction checkProxyHealth(mitmproxyPort: number): void {\n const sock = net.connect({ port: mitmproxyPort, timeout: HEALTH_TIMEOUT });\n sock.on('connect', () => {\n sock.destroy();\n if (!isProxyAlive) {\n isProxyAlive = true;\n log(`mitmproxy up on :${mitmproxyPort}, routing through proxy`);\n }\n respawnMonitor?.onHealthCheck(true);\n });\n sock.on('error', () => {\n if (isProxyAlive) {\n isProxyAlive = false;\n log(`mitmproxy down on :${mitmproxyPort}, routing direct`);\n }\n respawnMonitor?.onHealthCheck(false);\n });\n sock.on('timeout', () => {\n sock.destroy();\n if (isProxyAlive) {\n isProxyAlive = false;\n log(`mitmproxy timeout on :${mitmproxyPort}, routing direct`);\n }\n respawnMonitor?.onHealthCheck(false);\n });\n}\n\nfunction handleConnect(\n req: http.IncomingMessage,\n clientSocket: net.Socket,\n head: Buffer,\n mitmproxyPort: number,\n): void {\n const [targetHost, targetPortStr] = (req.url ?? '').split(':');\n const targetPort = parseInt(targetPortStr, 10) || 443;\n\n if (isProxyAlive) {\n // Forward CONNECT to mitmproxy\n const proxySocket = net.connect({ port: mitmproxyPort, host: '127.0.0.1' });\n\n proxySocket.on('connect', () => {\n // Send the CONNECT request to mitmproxy\n proxySocket.write(\n `CONNECT ${req.url} HTTP/1.1\\r\\nHost: ${req.url}\\r\\n\\r\\n`,\n );\n });\n\n // Wait for mitmproxy's 200 response before piping\n let responseBuffer = Buffer.alloc(0);\n const onData = (chunk: Buffer) => {\n responseBuffer = Buffer.concat([responseBuffer, chunk]);\n const responseStr = responseBuffer.toString();\n if (responseStr.includes('\\r\\n\\r\\n')) {\n proxySocket.removeListener('data', onData);\n // Forward the 200 to client\n clientSocket.write('HTTP/1.1 200 Connection Established\\r\\n\\r\\n');\n // Pipe remaining data and head\n if (head.length > 0) proxySocket.write(head);\n clientSocket.pipe(proxySocket);\n proxySocket.pipe(clientSocket);\n }\n };\n proxySocket.on('data', onData);\n\n proxySocket.on('error', (err) => {\n log(`proxy socket error for ${req.url}: ${err.message}, falling back to direct`);\n // Mitmproxy failed mid-connect, fall back to direct\n connectDirect(req, clientSocket, head, targetHost, targetPort);\n });\n\n clientSocket.on('error', () => {\n proxySocket.destroy();\n });\n } else {\n connectDirect(req, clientSocket, head, targetHost, targetPort);\n }\n}\n\nfunction connectDirect(\n req: http.IncomingMessage,\n clientSocket: net.Socket,\n head: Buffer,\n targetHost: string,\n targetPort: number,\n): void {\n const upstream = net.connect({ port: targetPort, host: targetHost });\n\n upstream.on('connect', () => {\n clientSocket.write('HTTP/1.1 200 Connection Established\\r\\n\\r\\n');\n if (head.length > 0) upstream.write(head);\n clientSocket.pipe(upstream);\n upstream.pipe(clientSocket);\n });\n\n upstream.on('error', (err) => {\n log(`upstream error for ${req.url}: ${err.message}`);\n clientSocket.end('HTTP/1.1 502 Bad Gateway\\r\\n\\r\\n');\n });\n\n clientSocket.on('error', () => {\n upstream.destroy();\n });\n}\n\nfunction handleRequest(\n req: http.IncomingMessage,\n res: http.ServerResponse,\n mitmproxyPort: number,\n): void {\n // Identity handshake (origin-form GET, not a proxy request): lets callers\n // distinguish OUR gateway from a foreign process squatting on the port \u2014\n // the gate for pinning HTTPS_PROXY and for idempotent double-spawns.\n if (req.url === GATEWAY_IDENTITY_PATH) {\n res.writeHead(200, { 'content-type': 'application/json' });\n res.end(JSON.stringify({\n service: GATEWAY_IDENTITY_SERVICE,\n version: identityVersion,\n pid: process.pid,\n mitmproxyAlive: isProxyAlive,\n }));\n return;\n }\n\n // Plain HTTP proxy (non-CONNECT)\n const targetUrl = req.url ?? '';\n let parsedUrl: URL;\n try {\n parsedUrl = new URL(targetUrl);\n } catch {\n res.writeHead(400);\n res.end('Bad Request');\n return;\n }\n\n const options: http.RequestOptions = {\n hostname: isProxyAlive ? '127.0.0.1' : parsedUrl.hostname,\n port: isProxyAlive ? mitmproxyPort : (parseInt(parsedUrl.port, 10) || 80),\n path: isProxyAlive ? targetUrl : parsedUrl.pathname + parsedUrl.search,\n method: req.method,\n headers: req.headers,\n };\n\n const proxyReq = http.request(options, (proxyRes) => {\n res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);\n proxyRes.pipe(res);\n });\n\n proxyReq.on('error', (err) => {\n log(`HTTP proxy error for ${targetUrl}: ${err.message}`);\n res.writeHead(502);\n res.end('Bad Gateway');\n });\n\n req.pipe(proxyReq);\n}\n\nexport function startGateway(\n port: number = GATEWAY_PORT,\n mitmproxyPort: number = MITMPROXY_PORT,\n options: { cliVersion?: string; cliEntry?: string } = {},\n): { server: http.Server; healthInterval: NodeJS.Timeout } {\n const cliVersion = options.cliVersion ?? process.env.RULEMETRIC_CLI_VERSION ?? 'unknown';\n const cliEntry = options.cliEntry ?? process.env.RULEMETRIC_CLI_ENTRY ?? null;\n identityVersion = cliVersion;\n // Mid-session crash recovery: when the health check sees mitmproxy stay\n // dead, attempt a bounded, crash-evidence-gated `proxy restart` (see\n // proxy-respawn.ts for the guard rails).\n respawnMonitor = new MitmproxyRespawnMonitor({\n hasCrashEvidence: proxyCrashEvidence,\n respawn: () => spawnProxyRestart(cliEntry),\n log,\n });\n const server = http.createServer((req, res) => {\n handleRequest(req, res, mitmproxyPort);\n });\n\n server.on('connect', (req, clientSocket, head) => {\n handleConnect(req, clientSocket as net.Socket, head as Buffer, mitmproxyPort);\n });\n\n // Don't crash on client socket errors\n server.on('clientError', (_err, socket) => {\n if (socket.writable) {\n socket.end('HTTP/1.1 400 Bad Request\\r\\n\\r\\n');\n }\n });\n\n const healthInterval = setInterval(() => {\n checkProxyHealth(mitmproxyPort);\n maybeRestartOnDrift(cliVersion, cliEntry);\n }, HEALTH_INTERVAL);\n\n // Do an immediate check\n checkProxyHealth(mitmproxyPort);\n maybeRestartOnDrift(cliVersion, cliEntry);\n\n server.listen(port, '127.0.0.1', () => {\n log(\n `gateway listening on 127.0.0.1:${port} (mitmproxy target: :${mitmproxyPort}, ` +\n `cli version: ${cliVersion})`,\n );\n });\n\n return { server, healthInterval };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
toolLabel
|
|
3
|
-
} from "./chunk-RH7NHVSK.js";
|
|
4
1
|
import {
|
|
5
2
|
DIM,
|
|
6
3
|
ORANGE,
|
|
7
4
|
RED
|
|
8
5
|
} from "./chunk-QJQS6TUN.js";
|
|
6
|
+
import {
|
|
7
|
+
toolLabel
|
|
8
|
+
} from "./chunk-RH7NHVSK.js";
|
|
9
9
|
|
|
10
10
|
// src/dashboard/launcher/NewLaunchForm.tsx
|
|
11
11
|
import { useState } from "react";
|
|
@@ -105,4 +105,4 @@ function NewLaunchForm({
|
|
|
105
105
|
export {
|
|
106
106
|
NewLaunchForm
|
|
107
107
|
};
|
|
108
|
-
//# sourceMappingURL=chunk-
|
|
108
|
+
//# sourceMappingURL=chunk-DNKSVWMD.js.map
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
announcements,
|
|
10
10
|
getDb
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-EI5BHWXA.js";
|
|
12
12
|
|
|
13
13
|
// src/lib/handlers/process-changelog.ts
|
|
14
14
|
import * as fs from "node:fs/promises";
|
|
@@ -119,4 +119,4 @@ async function processChangelog(deps = {}) {
|
|
|
119
119
|
export {
|
|
120
120
|
processChangelog
|
|
121
121
|
};
|
|
122
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-DUJSRBLP.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// src/lib/capture-hosts.ts
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
function buildAllowHostsArgs(addonDir, env = process.env) {
|
|
5
|
+
if (env.RULEMETRIC_PROXY_ALL_HOSTS === "1") return [];
|
|
6
|
+
let patterns;
|
|
7
|
+
try {
|
|
8
|
+
patterns = JSON.parse(readFileSync(join(addonDir, "allow_hosts.json"), "utf-8")).allow;
|
|
9
|
+
} catch {
|
|
10
|
+
return [];
|
|
11
|
+
}
|
|
12
|
+
if (!Array.isArray(patterns) || patterns.length === 0) return [];
|
|
13
|
+
const valid = patterns.filter((p) => typeof p === "string" && p.length > 0);
|
|
14
|
+
if (valid.length === 0) return [];
|
|
15
|
+
const extra = (env.RULEMETRIC_PROXY_EXTRA_HOSTS ?? "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
16
|
+
return [...valid, ...extra].flatMap((p) => ["--allow-hosts", p]);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export {
|
|
20
|
+
buildAllowHostsArgs
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=chunk-EARWG4EV.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/capture-hosts.ts"],
|
|
4
|
+
"sourcesContent": ["import { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\n/**\n * Build the `--allow-hosts` args that scope mitmproxy interception to the\n * hosts the addon can actually capture (LLM provider APIs). Everything else\n * is TUNNELED untouched \u2014 no TLS interception, no CA involvement \u2014 so\n * bundled-CA tools (pip/certifi, gcloud, node without NODE_EXTRA_CA_CERTS)\n * keep working while the machine-wide HTTPS_PROXY is pinned.\n *\n * The canonical pattern list lives in the addon package\n * (`addon/allow_hosts.json`) so the pytest sync-guard there can enforce it\n * covers every `detect_provider()` host \u2014 the CLI only transports it.\n *\n * Escape hatches:\n * RULEMETRIC_PROXY_ALL_HOSTS=1 \u2192 intercept everything (required for the\n * generic OpenAI-compatible catch-all on arbitrary self-hosted domains)\n * RULEMETRIC_PROXY_EXTRA_HOSTS=a,b \u2192 extra host:port regexes appended\n *\n * Failure mode: a missing/corrupt manifest returns [] (intercept everything,\n * the pre-scoping behavior). Tunneling-by-default on a broken read would\n * silently kill ALL capture \u2014 strictly worse than the old blast radius.\n */\nexport function buildAllowHostsArgs(\n addonDir: string,\n env: NodeJS.ProcessEnv = process.env,\n): string[] {\n if (env.RULEMETRIC_PROXY_ALL_HOSTS === '1') return [];\n\n let patterns: unknown;\n try {\n patterns = (\n JSON.parse(readFileSync(join(addonDir, 'allow_hosts.json'), 'utf-8')) as {\n allow?: unknown;\n }\n ).allow;\n } catch {\n return [];\n }\n if (!Array.isArray(patterns) || patterns.length === 0) return [];\n const valid = patterns.filter((p): p is string => typeof p === 'string' && p.length > 0);\n if (valid.length === 0) return [];\n\n const extra = (env.RULEMETRIC_PROXY_EXTRA_HOSTS ?? '')\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean);\n\n return [...valid, ...extra].flatMap((p) => ['--allow-hosts', p]);\n}\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,oBAAoB;AAC7B,SAAS,YAAY;AAsBd,SAAS,oBACd,UACA,MAAyB,QAAQ,KACvB;AACV,MAAI,IAAI,+BAA+B,IAAK,QAAO,CAAC;AAEpD,MAAI;AACJ,MAAI;AACF,eACE,KAAK,MAAM,aAAa,KAAK,UAAU,kBAAkB,GAAG,OAAO,CAAC,EAGpE;AAAA,EACJ,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,MAAI,CAAC,MAAM,QAAQ,QAAQ,KAAK,SAAS,WAAW,EAAG,QAAO,CAAC;AAC/D,QAAM,QAAQ,SAAS,OAAO,CAAC,MAAmB,OAAO,MAAM,YAAY,EAAE,SAAS,CAAC;AACvF,MAAI,MAAM,WAAW,EAAG,QAAO,CAAC;AAEhC,QAAM,SAAS,IAAI,gCAAgC,IAChD,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,OAAO,OAAO;AAEjB,SAAO,CAAC,GAAG,OAAO,GAAG,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;AACjE;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|