@rulemetric/cli 0.6.0 → 0.6.2
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/chunk-3AEVHOTS.js +33 -0
- package/dist/chunk-3AEVHOTS.js.map +7 -0
- package/dist/{chunk-WLFX4PYR.js → chunk-6TPJGYFF.js} +4 -4
- package/dist/{chunk-4LHEN2FS.js → chunk-CVMRMSV4.js} +14 -14
- package/dist/{chunk-H3C6EGR2.js → chunk-MUOUV5LY.js} +4 -4
- package/dist/{chunk-OVGP4OGK.js → chunk-OTPC2LXW.js} +6 -4
- package/dist/chunk-OTPC2LXW.js.map +7 -0
- package/dist/{chunk-JCYCAQ6U.js → chunk-TXHL3AUA.js} +86 -93
- package/dist/chunk-TXHL3AUA.js.map +7 -0
- package/dist/{chunk-7K4ILWS7.js → chunk-UPXIO7CG.js} +4 -4
- package/dist/{chunk-IATQXFJA.js → chunk-VBGUNFKY.js} +4 -4
- package/dist/{chunk-BCIUSUEV.js → chunk-VIYUIQHW.js} +4 -4
- package/dist/{chunk-HWGRTIXN.js → chunk-XN23W5HL.js} +9 -6
- package/dist/chunk-XN23W5HL.js.map +7 -0
- package/dist/commands/auth/login.js +4 -0
- package/dist/commands/auth/login.js.map +2 -2
- package/dist/commands/auth/signup.js +81 -0
- package/dist/commands/auth/signup.js.map +7 -0
- package/dist/commands/evals/agent.js +10 -10
- package/dist/commands/gateway/ensure.js +1 -1
- package/dist/commands/hooks/install.js +10 -4
- package/dist/commands/hooks/install.js.map +2 -2
- package/dist/commands/hooks/uninstall.js +2 -2
- package/dist/commands/proxy/env.js +1 -1
- package/dist/commands/proxy/start.js +10 -8
- package/dist/commands/proxy/start.js.map +2 -2
- package/dist/commands/proxy/status.js +1 -1
- package/dist/commands/proxy/stop.js +4 -2
- package/dist/commands/proxy/stop.js.map +2 -2
- package/dist/commands/setup.js +3 -3
- package/dist/dashboard/Dashboard.js +11 -11
- package/dist/dashboard/launcher/LauncherOverlay.js +3 -3
- package/dist/lib/agent-loop.js +10 -10
- package/dist/lib/ensure-api-key.js +10 -0
- package/dist/lib/ensure-api-key.js.map +7 -0
- package/dist/lib/gateway-lifecycle.js +1 -1
- package/dist/lib/handlers/cron-suggest-instructions.js +2 -2
- package/dist/lib/handlers/process-announcement.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 +1 -1
- package/dist/lib/manual-tasks.js +2 -2
- package/dist/lib/setup-steps.js +3 -3
- package/oclif.manifest.json +53 -2
- package/package.json +9 -6
- package/dist/chunk-HWGRTIXN.js.map +0 -7
- package/dist/chunk-JCYCAQ6U.js.map +0 -7
- package/dist/chunk-OVGP4OGK.js.map +0 -7
- /package/dist/{chunk-WLFX4PYR.js.map → chunk-6TPJGYFF.js.map} +0 -0
- /package/dist/{chunk-4LHEN2FS.js.map → chunk-CVMRMSV4.js.map} +0 -0
- /package/dist/{chunk-H3C6EGR2.js.map → chunk-MUOUV5LY.js.map} +0 -0
- /package/dist/{chunk-7K4ILWS7.js.map → chunk-UPXIO7CG.js.map} +0 -0
- /package/dist/{chunk-IATQXFJA.js.map → chunk-VBGUNFKY.js.map} +0 -0
- /package/dist/{chunk-BCIUSUEV.js.map → chunk-VIYUIQHW.js.map} +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {
|
|
2
|
+
apiPost
|
|
3
|
+
} from "./chunk-ZRLYHBPN.js";
|
|
4
|
+
import {
|
|
5
|
+
__resetEnvFileAuthCache,
|
|
6
|
+
loadEnvFileAuth,
|
|
7
|
+
saveApiKey
|
|
8
|
+
} from "./chunk-W2YERO7E.js";
|
|
9
|
+
|
|
10
|
+
// src/lib/ensure-api-key.ts
|
|
11
|
+
import { hostname } from "node:os";
|
|
12
|
+
async function ensureDeviceApiKey(log, warn) {
|
|
13
|
+
__resetEnvFileAuthCache();
|
|
14
|
+
const existing = process.env.RULEMETRIC_API_KEY || loadEnvFileAuth().apiKey;
|
|
15
|
+
if (existing) return;
|
|
16
|
+
const name = `${hostname()} (auto)`;
|
|
17
|
+
try {
|
|
18
|
+
const data = await apiPost("/api/auth/api-keys", { name });
|
|
19
|
+
saveApiKey(data.key, process.env.RULEMETRIC_API_URL);
|
|
20
|
+
__resetEnvFileAuthCache();
|
|
21
|
+
log(`Created API key "${name}" for hooks + proxy auth (saved to your config).`);
|
|
22
|
+
} catch (err) {
|
|
23
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
24
|
+
warn(
|
|
25
|
+
`Could not auto-create an API key (${msg}). Hooks/proxy still work via your session token; run \`rulemetric auth create-key --name "my-laptop"\` if capture auth fails.`
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
ensureDeviceApiKey
|
|
32
|
+
};
|
|
33
|
+
//# sourceMappingURL=chunk-3AEVHOTS.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/ensure-api-key.ts"],
|
|
4
|
+
"sourcesContent": ["import { hostname } from 'node:os';\nimport { apiPost } from './api-client.js';\nimport { saveApiKey, loadEnvFileAuth, __resetEnvFileAuthCache } from './auth.js';\n\ninterface CreateKeyResponse {\n key: string;\n name: string;\n}\n\n/**\n * Best-effort: mint a long-lived API key for hooks/proxy auth right after\n * login/signup, so `auth create-key` isn't a separate manual step a new user\n * has to discover. No-ops when a key already exists (env var or on-disk env\n * file) so repeated logins never pile up duplicate keys. NEVER throws \u2014 a key\n * failure must not fail the auth flow; the user can always run\n * `rulemetric auth create-key` by hand.\n */\nexport async function ensureDeviceApiKey(\n log: (msg: string) => void,\n warn: (msg: string) => void,\n): Promise<void> {\n // Re-read the env file we just wrote (login/signup saved the token there).\n __resetEnvFileAuthCache();\n const existing = process.env.RULEMETRIC_API_KEY || loadEnvFileAuth().apiKey;\n if (existing) return;\n\n const name = `${hostname()} (auto)`;\n try {\n const data = await apiPost<CreateKeyResponse>('/api/auth/api-keys', { name });\n saveApiKey(data.key, process.env.RULEMETRIC_API_URL);\n __resetEnvFileAuthCache();\n log(`Created API key \"${name}\" for hooks + proxy auth (saved to your config).`);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n warn(\n `Could not auto-create an API key (${msg}). Hooks/proxy still work via your ` +\n 'session token; run `rulemetric auth create-key --name \"my-laptop\"` if capture auth fails.',\n );\n }\n}\n"],
|
|
5
|
+
"mappings": ";;;;;;;;;;AAAA,SAAS,gBAAgB;AAiBzB,eAAsB,mBACpB,KACA,MACe;AAEf,0BAAwB;AACxB,QAAM,WAAW,QAAQ,IAAI,sBAAsB,gBAAgB,EAAE;AACrE,MAAI,SAAU;AAEd,QAAM,OAAO,GAAG,SAAS,CAAC;AAC1B,MAAI;AACF,UAAM,OAAO,MAAM,QAA2B,sBAAsB,EAAE,KAAK,CAAC;AAC5E,eAAW,KAAK,KAAK,QAAQ,IAAI,kBAAkB;AACnD,4BAAwB;AACxB,QAAI,oBAAoB,IAAI,kDAAkD;AAAA,EAChF,SAAS,KAAK;AACZ,UAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D;AAAA,MACE,qCAAqC,GAAG;AAAA,IAE1C;AAAA,EACF;AACF;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
removeWorktree
|
|
3
3
|
} from "./chunk-Y4BJXXYV.js";
|
|
4
|
+
import {
|
|
5
|
+
PermanentJobError
|
|
6
|
+
} from "./chunk-DGHWRQXL.js";
|
|
4
7
|
import {
|
|
5
8
|
agentRuns,
|
|
6
9
|
getDb,
|
|
7
10
|
sessions
|
|
8
11
|
} from "./chunk-QNSVCSHG.js";
|
|
9
|
-
import {
|
|
10
|
-
PermanentJobError
|
|
11
|
-
} from "./chunk-DGHWRQXL.js";
|
|
12
12
|
|
|
13
13
|
// src/lib/handlers/process-run-cleanup.ts
|
|
14
14
|
import { and, eq, isNotNull } from "drizzle-orm";
|
|
@@ -96,4 +96,4 @@ async function processRunCleanup(payload, deps = {}) {
|
|
|
96
96
|
export {
|
|
97
97
|
processRunCleanup
|
|
98
98
|
};
|
|
99
|
-
//# sourceMappingURL=chunk-
|
|
99
|
+
//# sourceMappingURL=chunk-6TPJGYFF.js.map
|
|
@@ -1,39 +1,39 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startTerminalTargetHeartbeat
|
|
3
3
|
} from "./chunk-JEJ3J5J6.js";
|
|
4
|
+
import {
|
|
5
|
+
processEval
|
|
6
|
+
} from "./chunk-RCMXTLQF.js";
|
|
7
|
+
import {
|
|
8
|
+
processInsights
|
|
9
|
+
} from "./chunk-N5A2IQAK.js";
|
|
4
10
|
import {
|
|
5
11
|
processLaunch
|
|
6
12
|
} from "./chunk-OTOWLUOJ.js";
|
|
7
13
|
import {
|
|
8
14
|
processRunCleanup
|
|
9
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6TPJGYFF.js";
|
|
10
16
|
import {
|
|
11
17
|
processSendMessage
|
|
12
18
|
} from "./chunk-W53GKIZQ.js";
|
|
13
19
|
import {
|
|
14
20
|
processSessionGoal
|
|
15
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-MUOUV5LY.js";
|
|
16
22
|
import {
|
|
17
23
|
cronRefreshSkills
|
|
18
24
|
} from "./chunk-RQ2TMLKG.js";
|
|
19
25
|
import {
|
|
20
26
|
cronSuggestInstructions
|
|
21
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-VBGUNFKY.js";
|
|
22
28
|
import {
|
|
23
29
|
processAnnouncement
|
|
24
|
-
} from "./chunk-
|
|
25
|
-
import {
|
|
26
|
-
processConversation
|
|
27
|
-
} from "./chunk-3POYC5NA.js";
|
|
28
|
-
import {
|
|
29
|
-
processEval
|
|
30
|
-
} from "./chunk-RCMXTLQF.js";
|
|
31
|
-
import {
|
|
32
|
-
processInsights
|
|
33
|
-
} from "./chunk-N5A2IQAK.js";
|
|
30
|
+
} from "./chunk-VIYUIQHW.js";
|
|
34
31
|
import {
|
|
35
32
|
isPermanentJobError
|
|
36
33
|
} from "./chunk-DGHWRQXL.js";
|
|
34
|
+
import {
|
|
35
|
+
processConversation
|
|
36
|
+
} from "./chunk-3POYC5NA.js";
|
|
37
37
|
import {
|
|
38
38
|
ApiError,
|
|
39
39
|
apiGet,
|
|
@@ -267,4 +267,4 @@ export {
|
|
|
267
267
|
pollForWork,
|
|
268
268
|
runAgentLoop
|
|
269
269
|
};
|
|
270
|
-
//# sourceMappingURL=chunk-
|
|
270
|
+
//# sourceMappingURL=chunk-CVMRMSV4.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
runLlmPrompt
|
|
3
|
+
} from "./chunk-FZKLLNDS.js";
|
|
1
4
|
import {
|
|
2
5
|
getDb,
|
|
3
6
|
sessionEvents,
|
|
4
7
|
sessions
|
|
5
8
|
} from "./chunk-QNSVCSHG.js";
|
|
6
|
-
import {
|
|
7
|
-
runLlmPrompt
|
|
8
|
-
} from "./chunk-FZKLLNDS.js";
|
|
9
9
|
|
|
10
10
|
// src/lib/handlers/process-session-goal.ts
|
|
11
11
|
import { eq, asc, sql } from "drizzle-orm";
|
|
@@ -52,4 +52,4 @@ async function processSessionGoal(payload, deps = {}) {
|
|
|
52
52
|
export {
|
|
53
53
|
processSessionGoal
|
|
54
54
|
};
|
|
55
|
-
//# sourceMappingURL=chunk-
|
|
55
|
+
//# sourceMappingURL=chunk-MUOUV5LY.js.map
|
|
@@ -3,7 +3,9 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { createConnection } from "node:net";
|
|
4
4
|
import { existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|
|
6
|
-
import { join, resolve } from "node:path";
|
|
6
|
+
import { dirname, join, resolve } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
var MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
7
9
|
var CONFIG_DIR = join(homedir(), ".config", "rulemetric");
|
|
8
10
|
var GATEWAY_PID_FILE = join(CONFIG_DIR, "gateway.pid");
|
|
9
11
|
var GATEWAY_LOG_FILE = join(CONFIG_DIR, "gateway.log");
|
|
@@ -50,8 +52,8 @@ function getGatewayPid() {
|
|
|
50
52
|
}
|
|
51
53
|
function findGatewayEntry() {
|
|
52
54
|
const candidates = [
|
|
53
|
-
resolve(
|
|
54
|
-
resolve(
|
|
55
|
+
resolve(MODULE_DIR, "./gateway-entry.js"),
|
|
56
|
+
resolve(MODULE_DIR, "../lib/gateway-entry.js")
|
|
55
57
|
];
|
|
56
58
|
for (const p of candidates) {
|
|
57
59
|
if (existsSync(p)) return p;
|
|
@@ -117,4 +119,4 @@ export {
|
|
|
117
119
|
spawnGateway,
|
|
118
120
|
stopGateway
|
|
119
121
|
};
|
|
120
|
-
//# sourceMappingURL=chunk-
|
|
122
|
+
//# sourceMappingURL=chunk-OTPC2LXW.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/gateway-lifecycle.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Gateway process lifecycle helpers \u2014 spawn, stop, check status.\n * Used by hooks install/uninstall, session-start hook, and proxy status.\n */\nimport { spawn } from 'node:child_process';\nimport { createConnection } from 'node:net';\nimport { existsSync, mkdirSync, openSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { dirname, join, resolve } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\n// `import.meta.dirname` only exists on Node >= 20.11 \u2014 on Node 18.x/\u226420.10 it is\n// `undefined`, and `resolve(undefined, \u2026)` throws, surfacing as a spurious\n// \"Could not find gateway-entry.js\" on fresh npm installs (the launchd gateway\n// masks it in dev). Derive the dir portably instead. See onboarding field report\n// 2026-07-07 F1.\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nconst CONFIG_DIR = join(homedir(), '.config', 'rulemetric');\nexport const GATEWAY_PID_FILE = join(CONFIG_DIR, 'gateway.pid');\nexport const GATEWAY_LOG_FILE = join(CONFIG_DIR, 'gateway.log');\nexport const GATEWAY_PORT = 8787;\nexport const MITMPROXY_PORT = 8788;\n\n/**\n * TCP liveness probe: does something actually ACCEPT a connection on the port?\n * Distinct from isGatewayRunning() (which only checks the PID is alive) \u2014 a\n * process can be \"running\" per its pidfile while :8787 accepts nothing. This is\n * the check that must gate writing HTTPS_PROXY: never pin Claude Code's proxy\n * at a port with nothing behind it (the fresh-laptop ConnectionRefused footgun).\n */\nexport function isGatewayListening(port: number = GATEWAY_PORT, timeoutMs = 1500): Promise<boolean> {\n return new Promise((resolveProbe) => {\n const socket = createConnection({ host: '127.0.0.1', port });\n const finish = (result: boolean) => {\n socket.destroy();\n resolveProbe(result);\n };\n socket.setTimeout(timeoutMs);\n socket.once('connect', () => finish(true));\n socket.once('timeout', () => finish(false));\n socket.once('error', () => finish(false));\n });\n}\n\n/**\n * Poll isGatewayListening() until it's true or the window elapses. spawnGateway()\n * returns as soon as it writes the PID file \u2014 the detached child hasn't bound\n * :8787 yet (Node bootstrap + module load). A one-shot probe microseconds later\n * fails-closed on every fresh install, so the proxy/deep-capture layer silently\n * never turns on. This tolerates a normal cold-start bind delay.\n */\nexport async function waitForGatewayListening(\n port: number = GATEWAY_PORT,\n { timeoutMs = 3000, intervalMs = 150 }: { timeoutMs?: number; intervalMs?: number } = {},\n): Promise<boolean> {\n const deadline = Date.now() + timeoutMs;\n for (;;) {\n if (await isGatewayListening(port, Math.min(1500, timeoutMs))) return true;\n if (Date.now() >= deadline) return false;\n await new Promise((resolve) => setTimeout(resolve, intervalMs));\n }\n}\n\nexport function isGatewayRunning(): boolean {\n if (!existsSync(GATEWAY_PID_FILE)) return false;\n\n const content = readFileSync(GATEWAY_PID_FILE, 'utf-8').trim();\n const pid = parseInt(content, 10);\n if (isNaN(pid)) return false;\n\n try {\n process.kill(pid, 0);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getGatewayPid(): number | null {\n if (!existsSync(GATEWAY_PID_FILE)) return null;\n const content = readFileSync(GATEWAY_PID_FILE, 'utf-8').trim();\n const pid = parseInt(content, 10);\n return isNaN(pid) ? null : pid;\n}\n\nfunction findGatewayEntry(): string {\n // Look for the compiled gateway-entry.js relative to this file's location\n const candidates = [\n resolve(MODULE_DIR, './gateway-entry.js'),\n resolve(MODULE_DIR, '../lib/gateway-entry.js'),\n ];\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n throw new Error(\n 'Could not find gateway-entry.js. Is the CLI built?\\n' +\n 'Searched:\\n' + candidates.map(c => ` ${c}`).join('\\n'),\n );\n}\n\nexport function spawnGateway(cliVersion?: string): number {\n if (isGatewayRunning()) {\n const pid = getGatewayPid();\n if (pid) return pid;\n }\n\n mkdirSync(CONFIG_DIR, { recursive: true });\n\n const entryPath = findGatewayEntry();\n const logFd = openSync(GATEWAY_LOG_FILE, 'a');\n\n const child = spawn(process.execPath, [entryPath], {\n detached: true,\n stdio: ['ignore', logFd, logFd],\n // The gateway inherits the spawning CLI's env. RULEMETRIC_CLI_VERSION\n // identifies the CLI version that started this gateway \u2014 used to\n // detect drift when a newer CLI starts mitmproxy with a different\n // version. RULEMETRIC_CLI_ENTRY is the path to the oclif entry point\n // (process.argv[1]) so the gateway can re-invoke the CLI for\n // `proxy restart` without needing the binary in PATH. Falls back to\n // \"unknown\" if the caller didn't pass them.\n env: {\n ...process.env,\n RULEMETRIC_CLI_VERSION: cliVersion ?? 'unknown',\n RULEMETRIC_CLI_ENTRY: process.argv[1] ?? '',\n },\n });\n\n child.unref();\n\n if (!child.pid) {\n throw new Error('Failed to spawn gateway process');\n }\n\n writeFileSync(GATEWAY_PID_FILE, String(child.pid));\n return child.pid;\n}\n\nexport function stopGateway(): boolean {\n const pid = getGatewayPid();\n if (pid === null) return false;\n\n try {\n process.kill(pid, 'SIGTERM');\n } catch {\n // Process already gone\n }\n\n try { unlinkSync(GATEWAY_PID_FILE); } catch { /* already gone */ }\n return true;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["resolve"]
|
|
7
|
+
}
|
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
detectInstalledTools,
|
|
3
3
|
hasRulemetricClaudeHooks,
|
|
4
4
|
mergeClaudeCodeHooks
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-XN23W5HL.js";
|
|
6
6
|
import {
|
|
7
|
-
GATEWAY_PORT
|
|
8
|
-
|
|
9
|
-
} from "./chunk-OVGP4OGK.js";
|
|
7
|
+
GATEWAY_PORT
|
|
8
|
+
} from "./chunk-OTPC2LXW.js";
|
|
10
9
|
import {
|
|
11
10
|
apiGet
|
|
12
11
|
} from "./chunk-ZRLYHBPN.js";
|
|
@@ -21,7 +20,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
21
20
|
import { homedir } from "node:os";
|
|
22
21
|
import { join } from "node:path";
|
|
23
22
|
var CORE_STEP_IDS = ["install", "auth", "hooks", "worker"];
|
|
24
|
-
var OPTIONAL_STEP_IDS = [
|
|
23
|
+
var OPTIONAL_STEP_IDS = [];
|
|
25
24
|
var ALL_STEP_IDS = [...CORE_STEP_IDS, ...OPTIONAL_STEP_IDS];
|
|
26
25
|
function defaultContext() {
|
|
27
26
|
return {
|
|
@@ -199,12 +198,17 @@ var STEPS = [
|
|
|
199
198
|
},
|
|
200
199
|
{
|
|
201
200
|
id: "hooks",
|
|
202
|
-
title: "Install hooks",
|
|
203
|
-
// Default onboarding
|
|
204
|
-
// proxy
|
|
205
|
-
//
|
|
206
|
-
//
|
|
207
|
-
|
|
201
|
+
title: "Install hooks + capture proxy",
|
|
202
|
+
// Default onboarding installs the FULL capture path: session hooks PLUS the
|
|
203
|
+
// MITM proxy (rendered-system-prompt / instruction linking + cross-tool
|
|
204
|
+
// capture). Every invasive side effect is disclosed up front in plan()
|
|
205
|
+
// below — nothing is silent. `hooks install` fails SAFE: if the CA sudo
|
|
206
|
+
// trust is declined it degrades to hooks-only (no machine-wide proxy pin),
|
|
207
|
+
// and HTTPS_PROXY is only pinned once the gateway actually answers on
|
|
208
|
+
// :8787 — so the default never bricks Claude Code. detect() greens on the
|
|
209
|
+
// capture hooks being present, so `coreComplete` is reachable even when the
|
|
210
|
+
// user declines the proxy.
|
|
211
|
+
command: "rulemetric hooks install",
|
|
208
212
|
async detect(ctx) {
|
|
209
213
|
const settingsPath = join(ctx.projectPath, ".claude", "settings.json");
|
|
210
214
|
if (!existsSync(settingsPath)) return false;
|
|
@@ -218,95 +222,38 @@ var STEPS = [
|
|
|
218
222
|
async plan(ctx) {
|
|
219
223
|
const settingsPath = join(ctx.projectPath, ".claude", "settings.json");
|
|
220
224
|
const { before, after } = projectHooksSettings(ctx.projectPath);
|
|
221
|
-
return {
|
|
222
|
-
id: "hooks",
|
|
223
|
-
title: "Install hooks",
|
|
224
|
-
command: "rulemetric hooks install --no-proxy",
|
|
225
|
-
artifacts: [
|
|
226
|
-
{ path: settingsPath, kind: "file", before, after },
|
|
227
|
-
// Disclose what the hooks DO, not just that they're registered — these
|
|
228
|
-
// are the privacy-relevant behaviors a user is consenting to.
|
|
229
|
-
{
|
|
230
|
-
path: "what these hooks capture",
|
|
231
|
-
kind: "service",
|
|
232
|
-
before: null,
|
|
233
|
-
after: "session-end reimports the FULL transcript to the API; post-tool-use records each tool call and snapshots the working tree to a LOCAL shadow git ref (paper-trail, never pushed); user-prompt posts prompt text. All scoped to sessions in this project."
|
|
234
|
-
}
|
|
235
|
-
]
|
|
236
|
-
};
|
|
237
|
-
},
|
|
238
|
-
async apply() {
|
|
239
|
-
runCommand("rulemetric hooks install --no-proxy");
|
|
240
|
-
}
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
id: "worker",
|
|
244
|
-
title: "Run the worker",
|
|
245
|
-
command: "rulemetric service install --worker-only",
|
|
246
|
-
async detect() {
|
|
247
|
-
return workerActive();
|
|
248
|
-
},
|
|
249
|
-
async plan() {
|
|
250
|
-
const isMac = process.platform === "darwin";
|
|
251
|
-
const servicePath = isMac ? WORKER_PLIST_PATH : WORKER_SYSTEMD_PATH;
|
|
252
|
-
const before = existsSync(servicePath) ? `(service already installed at ${servicePath})` : null;
|
|
253
|
-
const after = isMac ? [
|
|
254
|
-
`Label: com.rulemetric.worker`,
|
|
255
|
-
`ProgramArguments: <node> <cli>/bin/run.js evals agent`,
|
|
256
|
-
`RunAtLoad: true, KeepAlive: true`
|
|
257
|
-
].join("\n") : [
|
|
258
|
-
`[Service]`,
|
|
259
|
-
`ExecStart=<node> <cli>/bin/run.js evals agent`,
|
|
260
|
-
`Restart=on-failure`
|
|
261
|
-
].join("\n");
|
|
262
|
-
return {
|
|
263
|
-
id: "worker",
|
|
264
|
-
title: "Run the worker",
|
|
265
|
-
command: "rulemetric service install --worker-only",
|
|
266
|
-
artifacts: [
|
|
267
|
-
{ path: servicePath, kind: "service", before, after }
|
|
268
|
-
]
|
|
269
|
-
};
|
|
270
|
-
},
|
|
271
|
-
async apply() {
|
|
272
|
-
runCommand("rulemetric service install --worker-only");
|
|
273
|
-
}
|
|
274
|
-
},
|
|
275
|
-
{
|
|
276
|
-
id: "deep-capture",
|
|
277
|
-
title: "Enable deep capture (proxy) \u2014 optional",
|
|
278
|
-
optional: true,
|
|
279
|
-
// The full (proxy-enabled) install. Distinct from the core `hooks` step so
|
|
280
|
-
// the invasive side effects below are an explicit, disclosed opt-in.
|
|
281
|
-
command: "rulemetric hooks install",
|
|
282
|
-
async detect(ctx) {
|
|
283
|
-
const settingsPath = join(ctx.projectPath, ".claude", "settings.json");
|
|
284
|
-
if (!existsSync(settingsPath)) return false;
|
|
285
|
-
try {
|
|
286
|
-
const settings = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
287
|
-
const env = settings.env ?? {};
|
|
288
|
-
if (typeof env.HTTPS_PROXY !== "string") return false;
|
|
289
|
-
} catch {
|
|
290
|
-
return false;
|
|
291
|
-
}
|
|
292
|
-
return isGatewayListening(GATEWAY_PORT);
|
|
293
|
-
},
|
|
294
|
-
async plan(ctx) {
|
|
295
|
-
const settingsPath = join(ctx.projectPath, ".claude", "settings.json");
|
|
296
|
-
const { before, after } = projectProxyEnvSettings(ctx.projectPath);
|
|
297
225
|
const artifacts = [
|
|
298
226
|
{ path: settingsPath, kind: "file", before, after },
|
|
227
|
+
// Disclose what the hooks DO, not just that they're registered — these
|
|
228
|
+
// are the privacy-relevant behaviors a user is consenting to.
|
|
229
|
+
{
|
|
230
|
+
path: "what these hooks capture",
|
|
231
|
+
kind: "service",
|
|
232
|
+
before: null,
|
|
233
|
+
after: "session-end reimports the FULL transcript to the API; post-tool-use records each tool call and snapshots the working tree to a LOCAL shadow git ref (paper-trail, never pushed); user-prompt posts prompt text. All scoped to sessions in this project."
|
|
234
|
+
},
|
|
235
|
+
// FULL DISCLOSURE of the proxy layer — every invasive side effect of
|
|
236
|
+
// `hooks install` §2 is enumerated here so the default path hides
|
|
237
|
+
// nothing. Each degrades gracefully (see the step comment): a declined
|
|
238
|
+
// CA-trust sudo skips the machine-wide layer and stays hooks-only.
|
|
239
|
+
{
|
|
240
|
+
// The proxy env block `hooks install` §2 adds to the SAME
|
|
241
|
+
// .claude/settings.json (HTTPS_PROXY / NO_PROXY / NODE_EXTRA_CA_CERTS).
|
|
242
|
+
path: `${settingsPath} (proxy env block)`,
|
|
243
|
+
kind: "file",
|
|
244
|
+
...projectProxyEnvSettings(ctx.projectPath)
|
|
245
|
+
},
|
|
299
246
|
{
|
|
300
|
-
path: "
|
|
247
|
+
path: "system keychain \u2014 mitmproxy root CA",
|
|
301
248
|
kind: "service",
|
|
302
249
|
before: existsSync(CERT_PATH) ? "(CA generated; trust state unchanged)" : "(no CA yet)",
|
|
303
|
-
after: "Installs + trusts the mitmproxy CA (requires sudo). This lets the proxy DECRYPT Claude Code HTTPS request/response bodies \u2014 the capability that powers rendered-system-prompt / instruction linking."
|
|
250
|
+
after: "Installs + trusts the mitmproxy CA (requires sudo). This lets the proxy DECRYPT Claude Code HTTPS request/response bodies \u2014 the capability that powers rendered-system-prompt / instruction linking. Decline the sudo prompt and setup stays hooks-only (still fully onboarded)."
|
|
304
251
|
},
|
|
305
252
|
{
|
|
306
253
|
path: "launchctl setenv HTTPS_PROXY (machine-wide, all GUI apps)",
|
|
307
254
|
kind: "command",
|
|
308
255
|
before: null,
|
|
309
|
-
after: `http://localhost:${GATEWAY_PORT} \u2014 routes every GUI app's HTTPS through the local gateway`
|
|
256
|
+
after: `http://localhost:${GATEWAY_PORT} \u2014 routes every GUI app's HTTPS through the local gateway (only when the CA is trusted AND :${GATEWAY_PORT} is live)`
|
|
310
257
|
},
|
|
311
258
|
{
|
|
312
259
|
path: "VS Code + Cursor user settings (http.proxy)",
|
|
@@ -329,9 +276,22 @@ var STEPS = [
|
|
|
329
276
|
after: `proxy \u2192 http://localhost:${GATEWAY_PORT}`
|
|
330
277
|
});
|
|
331
278
|
}
|
|
279
|
+
for (const [tool, home, file] of [
|
|
280
|
+
["Cursor", join(homedir(), ".cursor"), join(homedir(), ".cursor", "hooks.json")],
|
|
281
|
+
["Antigravity/Gemini", join(homedir(), ".gemini"), join(homedir(), ".gemini", "config", "hooks.json")]
|
|
282
|
+
]) {
|
|
283
|
+
if (existsSync(home)) {
|
|
284
|
+
artifacts.push({
|
|
285
|
+
path: `${file} (${tool} user hooks)`,
|
|
286
|
+
kind: "file",
|
|
287
|
+
before: existsSync(file) ? "(existing config)" : null,
|
|
288
|
+
after: "adds RuleMetric session-capture hooks for this tool"
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
332
292
|
return {
|
|
333
|
-
id: "
|
|
334
|
-
title: "
|
|
293
|
+
id: "hooks",
|
|
294
|
+
title: "Install hooks + capture proxy",
|
|
335
295
|
command: "rulemetric hooks install",
|
|
336
296
|
artifacts
|
|
337
297
|
};
|
|
@@ -339,6 +299,39 @@ var STEPS = [
|
|
|
339
299
|
async apply() {
|
|
340
300
|
runCommand("rulemetric hooks install");
|
|
341
301
|
}
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
id: "worker",
|
|
305
|
+
title: "Run the worker",
|
|
306
|
+
command: "rulemetric service install --worker-only",
|
|
307
|
+
async detect() {
|
|
308
|
+
return workerActive();
|
|
309
|
+
},
|
|
310
|
+
async plan() {
|
|
311
|
+
const isMac = process.platform === "darwin";
|
|
312
|
+
const servicePath = isMac ? WORKER_PLIST_PATH : WORKER_SYSTEMD_PATH;
|
|
313
|
+
const before = existsSync(servicePath) ? `(service already installed at ${servicePath})` : null;
|
|
314
|
+
const after = isMac ? [
|
|
315
|
+
`Label: com.rulemetric.worker`,
|
|
316
|
+
`ProgramArguments: <node> <cli>/bin/run.js evals agent`,
|
|
317
|
+
`RunAtLoad: true, KeepAlive: true`
|
|
318
|
+
].join("\n") : [
|
|
319
|
+
`[Service]`,
|
|
320
|
+
`ExecStart=<node> <cli>/bin/run.js evals agent`,
|
|
321
|
+
`Restart=on-failure`
|
|
322
|
+
].join("\n");
|
|
323
|
+
return {
|
|
324
|
+
id: "worker",
|
|
325
|
+
title: "Run the worker",
|
|
326
|
+
command: "rulemetric service install --worker-only",
|
|
327
|
+
artifacts: [
|
|
328
|
+
{ path: servicePath, kind: "service", before, after }
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
},
|
|
332
|
+
async apply() {
|
|
333
|
+
runCommand("rulemetric service install --worker-only");
|
|
334
|
+
}
|
|
342
335
|
}
|
|
343
336
|
];
|
|
344
337
|
var STEP_BY_ID = new Map(STEPS.map((s) => [s.id, s]));
|
|
@@ -399,4 +392,4 @@ export {
|
|
|
399
392
|
isStepId,
|
|
400
393
|
computeStatus
|
|
401
394
|
};
|
|
402
|
-
//# sourceMappingURL=chunk-
|
|
395
|
+
//# sourceMappingURL=chunk-TXHL3AUA.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/lib/setup-steps.ts"],
|
|
4
|
+
"sourcesContent": ["import { execSync } from 'node:child_process';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { homedir } from 'node:os';\nimport { join } from 'node:path';\nimport { isAuthenticated, getDefaultConfigDir } from './auth.js';\nimport { apiGet } from './api-client.js';\nimport { GATEWAY_PORT, isGatewayListening } from './gateway-lifecycle.js';\nimport { detectInstalledTools, mergeClaudeCodeHooks, hasRulemetricClaudeHooks } from './hooks-config.js';\n\n/**\n * Data model for the four core onboarding steps, shared (by step-ID list +\n * install-monotonicity) with the web reducer `deriveSetupProgress()`.\n *\n * The two surfaces are separate implementations by necessity, but they no\n * longer read *different sources* for the same fact:\n * - Server-observable signals (cli-worker heartbeat / captured session) are\n * unified through the shared `GET /api/setup/status` endpoint \u2014 the CLI's\n * `workerActive()` and the web's `use-setup-progress.ts` both bottom out on\n * that one server-side authority, so they cannot drift on it.\n * - Local signals (auth = `~/.config/rulemetric` creds via `isAuthenticated()`,\n * hooks = cwd `.claude/settings.json`) stay machine-local: the browser\n * cannot read the user's filesystem, so these legitimately differ from the\n * web's account-scoped proxies for the same step (see the design doc's\n * \"Shared step model, two implementations\").\n * Each step exposes:\n * - detect(): is it done? (reads a local/remote signal, never mutates)\n * - plan(): what would apply() write? (read-only, returns diffs/artifacts)\n * - apply(): perform the step (thin \u2014 shells out to existing commands)\n */\n\nexport const CORE_STEP_IDS = ['install', 'auth', 'hooks', 'worker'] as const;\n// Optional steps live in the walk (offered, recommended) but do NOT gate\n// `coreComplete` \u2014 declining them still leaves the user fully onboarded. The\n// proxy/CA capture now ships as the DEFAULT of the core `hooks` step (see that\n// step's plan(), which fully discloses the invasive MITM/keychain side effects\n// up front); it is not a separate opt-in step. The mechanism is retained for\n// any future optional step.\nexport const OPTIONAL_STEP_IDS = [] as const;\nexport const ALL_STEP_IDS = [...CORE_STEP_IDS, ...OPTIONAL_STEP_IDS] as const;\nexport type StepId = (typeof ALL_STEP_IDS)[number];\n\nexport type ArtifactKind = 'file' | 'command' | 'service';\n\nexport interface Artifact {\n path: string;\n kind: ArtifactKind;\n before: string | null;\n after: string | null;\n /** True when `after` contains a masked secret (never the raw value). */\n masked?: boolean;\n}\n\nexport interface StepPlan {\n id: StepId;\n title: string;\n command?: string;\n artifacts: Artifact[];\n}\n\nexport interface SetupContext {\n projectPath: string;\n configDir: string;\n}\n\nexport interface SetupStep {\n id: StepId;\n title: string;\n command?: string;\n /** Optional steps are offered in the walk but never gate `coreComplete`. */\n optional?: boolean;\n detect(ctx: SetupContext): Promise<boolean>;\n plan(ctx: SetupContext): Promise<StepPlan>;\n apply(ctx: SetupContext): Promise<void>;\n}\n\nexport interface StepStatus {\n id: StepId;\n done: boolean;\n}\n\nexport interface SetupStatus {\n steps: StepStatus[];\n coreComplete: boolean;\n currentStepId: StepId | null;\n}\n\n/** Build the default context from the current process (cwd + config dir). */\nexport function defaultContext(): SetupContext {\n return {\n projectPath: process.cwd(),\n configDir: getDefaultConfigDir(),\n };\n}\n\nconst CERT_PATH = join(homedir(), '.mitmproxy', 'mitmproxy-ca-cert.pem');\n\n/**\n * Mask a secret: keep the first 4 and last 4 characters, replace the middle\n * with asterisks. Strings too short to reveal safely are fully masked.\n */\nexport function maskToken(s: string): string {\n if (!s) return '';\n if (s.length <= 8) return '*'.repeat(s.length);\n const head = s.slice(0, 4);\n const tail = s.slice(-4);\n return `${head}${'*'.repeat(Math.max(4, s.length - 8))}${tail}`;\n}\n\n/**\n * Simple line-oriented +/- diff for terminal display. Not a real LCS \u2014 good\n * enough to show a human/agent exactly which lines an artifact would change.\n */\nexport function renderDiff(before: string | null, after: string | null): string {\n const beforeLines = before === null ? [] : before.replace(/\\n$/, '').split('\\n');\n const afterLines = after === null ? [] : after.replace(/\\n$/, '').split('\\n');\n const out: string[] = [];\n const max = Math.max(beforeLines.length, afterLines.length);\n for (let i = 0; i < max; i++) {\n const b = beforeLines[i];\n const a = afterLines[i];\n if (b === a) {\n out.push(` ${b}`);\n } else {\n if (b !== undefined) out.push(`-${b}`);\n if (a !== undefined) out.push(`+${a}`);\n }\n }\n return out.join('\\n');\n}\n\n/** Read a single KEY=value from the env file, or null if absent. */\nfunction readEnvValue(configDir: string, key: string): string | null {\n const envPath = join(configDir, 'env');\n if (!existsSync(envPath)) return null;\n try {\n const content = readFileSync(envPath, 'utf-8');\n const match = content.match(new RegExp(`^${key}=(.+)$`, 'm'));\n return match ? match[1] : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Is a CLI worker heartbeat active for the caller? (best-effort, network)\n *\n * Reads the shared `GET /api/setup/status` endpoint's `workerActive` field\n * rather than re-deriving it from `/api/workers/status`. That endpoint computes\n * the cli-worker heartbeat signal server-side using the SAME window +\n * staleness thresholds + `workerType === 'cli-worker'` filter the web strip\n * uses, so the CLI and web can't drift on this fact by construction (they used\n * to each filter/threshold client-side, which is exactly how they could\n * disagree). Fast-fails after 2.5s so `setup --json` / `setup plan` degrade to\n * \"not running\" instead of blocking on a stalled connection \u2014 detection is a\n * UX signal, not a correctness gate; offline \u2192 false.\n */\nasync function workerActive(): Promise<boolean> {\n if (!isAuthenticated()) return false;\n try {\n const status = await Promise.race([\n apiGet<{\n cliEverConnected?: boolean;\n anyCapturedSession?: boolean;\n workerActive?: boolean;\n }>('/api/setup/status'),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error('setup/status timeout')), 2500),\n ),\n ]);\n return status?.workerActive === true;\n } catch {\n return false;\n }\n}\n\n/** Read the project's `.claude/settings.json` for a read-only preview (no throw). */\nfunction readProjectSettings(projectPath: string): { settings: Record<string, unknown>; before: string | null } {\n const settingsPath = join(projectPath, '.claude', 'settings.json');\n let settings: Record<string, unknown> = {};\n let before: string | null = null;\n if (existsSync(settingsPath)) {\n try {\n before = readFileSync(settingsPath, 'utf-8').replace(/\\n$/, '');\n settings = JSON.parse(before) as Record<string, unknown>;\n } catch {\n settings = {};\n }\n }\n return { settings, before };\n}\n\n/**\n * Compute the in-memory `.claude/settings.json` after the RuleMetric session\n * HOOKS are merged, WITHOUT touching disk. This is the \u00A71 (hooks) portion of\n * the diff; the proxy env block that `hooks install` \u00A72 also writes is disclosed\n * separately via projectProxyEnvSettings so the two changes read distinctly.\n */\nfunction projectHooksSettings(projectPath: string): { before: string | null; after: string } {\n const { settings, before } = readProjectSettings(projectPath);\n mergeClaudeCodeHooks(settings);\n return { before, after: JSON.stringify(settings, null, 2) };\n}\n\n/**\n * The proxy-env addition to `.claude/settings.json` that `hooks install` \u00A72\n * makes on the default path \u2014 for the disclosure diff only. Read-only.\n */\nfunction projectProxyEnvSettings(projectPath: string): { before: string | null; after: string } {\n const { settings, before } = readProjectSettings(projectPath);\n const env = { ...((settings.env as Record<string, string>) ?? {}) };\n env.HTTPS_PROXY = `http://localhost:${GATEWAY_PORT}`;\n env.NO_PROXY = 'localhost,127.0.0.1,*.supabase.co,*.supabase.in';\n env.NODE_EXTRA_CA_CERTS = CERT_PATH;\n return { before, after: JSON.stringify({ ...settings, env }, null, 2) };\n}\n\nconst WORKER_PLIST_PATH = join(homedir(), 'Library', 'LaunchAgents', 'com.rulemetric.worker.plist');\nconst WORKER_SYSTEMD_PATH = join(homedir(), '.config', 'systemd', 'user', 'rulemetric-worker.service');\n\n/** Run a command with inherited stdio (interactive-friendly). Thin apply(). */\nfunction runCommand(cmd: string): void {\n execSync(cmd, { stdio: 'inherit' });\n}\n\nexport const STEPS: SetupStep[] = [\n {\n id: 'install',\n title: 'Install the CLI',\n command: 'npm install -g @rulemetric/cli',\n // Monotonic: the CLI must exist to produce any downstream signal, so it\n // greens off auth or worker presence (nothing local phones home before).\n async detect(): Promise<boolean> {\n return isAuthenticated() || (await workerActive());\n },\n async plan(): Promise<StepPlan> {\n return {\n id: 'install',\n title: 'Install the CLI',\n command: 'npm install -g @rulemetric/cli',\n artifacts: [\n {\n path: 'npm install -g @rulemetric/cli',\n kind: 'command',\n before: null,\n after: 'rulemetric --version # verifies the binary is on PATH',\n },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('npm install -g @rulemetric/cli');\n },\n },\n {\n id: 'auth',\n title: 'Authenticate',\n command: 'rulemetric auth login',\n async detect(): Promise<boolean> {\n return isAuthenticated();\n },\n async plan(ctx): Promise<StepPlan> {\n const envPath = join(ctx.configDir, 'env');\n // Guard the read like the sibling helpers (readEnvValue /\n // projectHooksSettings) do: a TOCTOU race or permission error between\n // existsSync and readFileSync must NOT throw out of plan(). Fall back to\n // a secret-free marker rather than crashing `setup plan`/`--show`.\n let before: string | null = null;\n if (existsSync(envPath)) {\n try {\n before = maskEnvFile(readFileSync(envPath, 'utf-8'));\n } catch {\n before = '(unreadable)';\n }\n }\n const rawToken = readEnvValue(ctx.configDir, 'RULEMETRIC_ACCESS_TOKEN');\n const tokenDisplay = rawToken ? maskToken(rawToken) : maskToken('<token-from-login>');\n // Reconstruct EXACTLY what `auth login`'s saveEnvFile writes so the diff\n // doesn't show phantom changes: line order [TOKEN, KEY?, URL?], the key\n // preserved (masked) when present, and the URL sourced from\n // process.env.RULEMETRIC_API_URL \u2014 the same value login passes \u2014 and OMITTED\n // when unset (login writes no URL line then). All masking preserved; the raw\n // token/key are never emitted.\n const afterLines = [`RULEMETRIC_ACCESS_TOKEN=${tokenDisplay}`];\n const rawApiKey = readEnvValue(ctx.configDir, 'RULEMETRIC_API_KEY');\n if (rawApiKey) {\n afterLines.push(`RULEMETRIC_API_KEY=${maskToken(rawApiKey)}`);\n }\n const loginUrl = process.env.RULEMETRIC_API_URL;\n if (loginUrl) {\n afterLines.push(`RULEMETRIC_API_URL=${loginUrl}`);\n }\n const after = afterLines.join('\\n');\n return {\n id: 'auth',\n title: 'Authenticate',\n command: 'rulemetric auth login',\n artifacts: [\n {\n path: envPath,\n kind: 'file',\n before,\n after,\n masked: true,\n },\n // `auth login` (saveToken) also writes auth.json \u2014 disclose it so the\n // plan matches every file apply() touches.\n {\n path: join(ctx.configDir, 'auth.json'),\n kind: 'file',\n before: existsSync(join(ctx.configDir, 'auth.json')) ? '(existing session tokens)' : null,\n after: '{ accessToken, refreshToken, expiresAt, email } \u2014 OAuth session tokens (secret)',\n masked: true,\n },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric auth login');\n },\n },\n {\n id: 'hooks',\n title: 'Install hooks + capture proxy',\n // Default onboarding installs the FULL capture path: session hooks PLUS the\n // MITM proxy (rendered-system-prompt / instruction linking + cross-tool\n // capture). Every invasive side effect is disclosed up front in plan()\n // below \u2014 nothing is silent. `hooks install` fails SAFE: if the CA sudo\n // trust is declined it degrades to hooks-only (no machine-wide proxy pin),\n // and HTTPS_PROXY is only pinned once the gateway actually answers on\n // :8787 \u2014 so the default never bricks Claude Code. detect() greens on the\n // capture hooks being present, so `coreComplete` is reachable even when the\n // user declines the proxy.\n command: 'rulemetric hooks install',\n async detect(ctx): Promise<boolean> {\n const settingsPath = join(ctx.projectPath, '.claude', 'settings.json');\n if (!existsSync(settingsPath)) return false;\n try {\n const settings = JSON.parse(readFileSync(settingsPath, 'utf-8')) as Record<string, unknown>;\n // Ready when capture hooks are actually wired up \u2014 NOT when a proxy env\n // var was written (that greened even against a dead proxy port). The\n // proxy is a best-effort upgrade layered on top; hooks presence is the\n // honest \"capture is working\" signal.\n return hasRulemetricClaudeHooks(settings);\n } catch {\n return false;\n }\n },\n async plan(ctx): Promise<StepPlan> {\n const settingsPath = join(ctx.projectPath, '.claude', 'settings.json');\n const { before, after } = projectHooksSettings(ctx.projectPath);\n const artifacts: Artifact[] = [\n { path: settingsPath, kind: 'file', before, after },\n // Disclose what the hooks DO, not just that they're registered \u2014 these\n // are the privacy-relevant behaviors a user is consenting to.\n {\n path: 'what these hooks capture',\n kind: 'service',\n before: null,\n after:\n 'session-end reimports the FULL transcript to the API; post-tool-use records each ' +\n 'tool call and snapshots the working tree to a LOCAL shadow git ref (paper-trail, ' +\n 'never pushed); user-prompt posts prompt text. All scoped to sessions in this project.',\n },\n // FULL DISCLOSURE of the proxy layer \u2014 every invasive side effect of\n // `hooks install` \u00A72 is enumerated here so the default path hides\n // nothing. Each degrades gracefully (see the step comment): a declined\n // CA-trust sudo skips the machine-wide layer and stays hooks-only.\n {\n // The proxy env block `hooks install` \u00A72 adds to the SAME\n // .claude/settings.json (HTTPS_PROXY / NO_PROXY / NODE_EXTRA_CA_CERTS).\n path: `${settingsPath} (proxy env block)`,\n kind: 'file',\n ...projectProxyEnvSettings(ctx.projectPath),\n },\n {\n path: 'system keychain \u2014 mitmproxy root CA',\n kind: 'service',\n before: existsSync(CERT_PATH) ? '(CA generated; trust state unchanged)' : '(no CA yet)',\n after:\n 'Installs + trusts the mitmproxy CA (requires sudo). This lets the ' +\n 'proxy DECRYPT Claude Code HTTPS request/response bodies \u2014 the ' +\n 'capability that powers rendered-system-prompt / instruction linking. ' +\n 'Decline the sudo prompt and setup stays hooks-only (still fully onboarded).',\n },\n {\n path: 'launchctl setenv HTTPS_PROXY (machine-wide, all GUI apps)',\n kind: 'command',\n before: null,\n after: `http://localhost:${GATEWAY_PORT} \u2014 routes every GUI app's HTTPS through the local gateway (only when the CA is trusted AND :${GATEWAY_PORT} is live)`,\n },\n {\n path: 'VS Code + Cursor user settings (http.proxy)',\n kind: 'file',\n before: null,\n after: `http.proxy \u2192 http://localhost:${GATEWAY_PORT} (+ http.proxyStrictSSL=false)`,\n },\n {\n path: `gateway process on :${GATEWAY_PORT}`,\n kind: 'service',\n before: null,\n after: 'started (routes Claude Code \u2192 mitmproxy \u2192 Anthropic; falls back to direct if mitmproxy is down)',\n },\n ];\n // Workspace-level editor configs, only if those dirs exist here.\n for (const tool of detectInstalledTools(ctx.projectPath).filter((t) => t === 'cursor' || t === 'copilot_agent')) {\n artifacts.push({\n path: `${ctx.projectPath} (${tool} workspace proxy config)`,\n kind: 'file',\n before: null,\n after: `proxy \u2192 http://localhost:${GATEWAY_PORT}`,\n });\n }\n // Cross-tool USER-level hooks \u2014 disclosed here, and (post-F2) written ONLY\n // when the tool is already present so we never manufacture config for a\n // tool the user doesn't have.\n for (const [tool, home, file] of [\n ['Cursor', join(homedir(), '.cursor'), join(homedir(), '.cursor', 'hooks.json')],\n ['Antigravity/Gemini', join(homedir(), '.gemini'), join(homedir(), '.gemini', 'config', 'hooks.json')],\n ] as const) {\n if (existsSync(home)) {\n artifacts.push({\n path: `${file} (${tool} user hooks)`,\n kind: 'file',\n before: existsSync(file) ? '(existing config)' : null,\n after: 'adds RuleMetric session-capture hooks for this tool',\n });\n }\n }\n return {\n id: 'hooks',\n title: 'Install hooks + capture proxy',\n command: 'rulemetric hooks install',\n artifacts,\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric hooks install');\n },\n },\n {\n id: 'worker',\n title: 'Run the worker',\n command: 'rulemetric service install --worker-only',\n async detect(): Promise<boolean> {\n return workerActive();\n },\n async plan(): Promise<StepPlan> {\n const isMac = process.platform === 'darwin';\n const servicePath = isMac ? WORKER_PLIST_PATH : WORKER_SYSTEMD_PATH;\n // SECURITY: never read the installed plist/systemd unit into `before` \u2014\n // those files embed RULEMETRIC_ACCESS_TOKEN / RULEMETRIC_API_KEY verbatim\n // (service/install.ts writes them from ALLOWED_ENV_VARS), so echoing the\n // file contents would leak the raw secret through `plan`, `--show`, and\n // `--json`. Use a synthetic, secret-free marker for the current state.\n const before = existsSync(servicePath)\n ? `(service already installed at ${servicePath})`\n : null;\n // Concise representation (path + ExecStart node/cli invocation) rather\n // than reconstructing the full plist \u2014 keep read-only + legible.\n const after = isMac\n ? [\n `Label: com.rulemetric.worker`,\n `ProgramArguments: <node> <cli>/bin/run.js evals agent`,\n `RunAtLoad: true, KeepAlive: true`,\n ].join('\\n')\n : [\n `[Service]`,\n `ExecStart=<node> <cli>/bin/run.js evals agent`,\n `Restart=on-failure`,\n ].join('\\n');\n return {\n id: 'worker',\n title: 'Run the worker',\n command: 'rulemetric service install --worker-only',\n artifacts: [\n { path: servicePath, kind: 'service', before, after },\n ],\n };\n },\n async apply(): Promise<void> {\n runCommand('rulemetric service install --worker-only');\n },\n },\n];\n\nconst STEP_BY_ID = new Map<StepId, SetupStep>(STEPS.map((s) => [s.id, s]));\n\nexport function getStep(id: StepId): SetupStep {\n const step = STEP_BY_ID.get(id);\n if (!step) throw new Error(`Unknown setup step: ${id}`);\n return step;\n}\n\nexport function isStepId(value: string): value is StepId {\n return (ALL_STEP_IDS as readonly string[]).includes(value);\n}\n\n/**\n * Keys whose values are safe to show verbatim in the env-file diff. Everything\n * NOT in this set is masked. Deny-by-default is deliberate: `~/.config/rulemetric/env`\n * can legitimately hold DATABASE_URL, SUPABASE_SERVICE_ROLE_KEY, SUPABASE_JWT_SECRET,\n * GITHUB_TOKEN, RESEND_API_KEY, etc. (see ALLOWED_ENV_VARS in service/install.ts),\n * so an allowlist-to-hide would leak any secret we forgot to enumerate. Revealing\n * only known-safe keys means a future secret added to the file is masked automatically.\n */\nconst SAFE_ENV_KEYS = new Set([\n 'RULEMETRIC_API_URL',\n 'RULEMETRIC_USER_ID',\n 'RULEMETRIC_CLIENT_NAME',\n 'RESEND_FROM_EMAIL',\n 'SENTRY_DSN', // a DSN is not a secret\n 'PG_POOL_MAX',\n 'PG_IDLE_TIMEOUT',\n 'PG_STATEMENT_TIMEOUT_MS',\n 'NODE_ENV',\n]);\n\n/**\n * Mask secret-bearing lines in a raw env file for display. Deny-by-default:\n * every `KEY=value` line has its value masked unless KEY is in SAFE_ENV_KEYS.\n * Non-assignment lines (comments, blanks) pass through untouched.\n */\nfunction maskEnvFile(content: string): string {\n return content\n .replace(/\\n$/, '')\n .split('\\n')\n .map((line) => {\n const match = line.match(/^([A-Z0-9_]+)=(.+)$/);\n if (!match) return line; // comment / blank / non-assignment\n const [, key, value] = match;\n return SAFE_ENV_KEYS.has(key) ? line : `${key}=${maskToken(value)}`;\n })\n .join('\\n');\n}\n\n/**\n * Run every step's detect() and derive the overall status. `currentStepId` is\n * the first incomplete step in core order (mirrors the web reducer).\n */\nexport async function computeStatus(ctx: SetupContext = defaultContext()): Promise<SetupStatus> {\n const steps: StepStatus[] = [];\n for (const step of STEPS) {\n // Sequential: detect() may hit the network; order is stable + cheap.\n // eslint-disable-next-line no-await-in-loop\n const done = await step.detect(ctx);\n steps.push({ id: step.id, done });\n }\n // Monotonicity (mirrors the web reducer's `cliDetected`): the CLI binary must\n // exist to produce ANY downstream signal, so `install` greens whenever auth,\n // hooks, or worker is detected \u2014 even if `install.detect()` alone (which only\n // sees auth/worker, not hooks) came back false. Without this, a user with\n // hooks installed but no live worker sees \"Install the CLI: waiting\" while\n // \"Install hooks: done\" \u2014 a contradiction.\n const install = steps.find((s) => s.id === 'install');\n if (install && !install.done) {\n install.done = steps.some((s) => s.id !== 'install' && s.done);\n }\n\n // coreComplete + currentStepId are derived from CORE steps only. The proxy now\n // ships inside the `hooks` step (whose detect() greens on hooks-presence, so a\n // declined CA still completes it); any future OPTIONAL_STEP_IDS never block\n // \"done\" or steal the cursor. Mirrors the web reducer's optional-step handling.\n const coreIds = new Set<string>(CORE_STEP_IDS);\n const coreSteps = steps.filter((s) => coreIds.has(s.id));\n const currentStepId = coreSteps.find((s) => !s.done)?.id ?? null;\n const coreComplete = coreSteps.every((s) => s.done);\n return { steps, coreComplete, currentStepId };\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ActiveJobsList
|
|
3
|
+
} from "./chunk-QD6X4L4L.js";
|
|
1
4
|
import {
|
|
2
5
|
NewLaunchForm
|
|
3
6
|
} from "./chunk-DTFXCHHI.js";
|
|
@@ -9,9 +12,6 @@ import {
|
|
|
9
12
|
createLaunchJob,
|
|
10
13
|
listLaunchers
|
|
11
14
|
} from "./chunk-W4MZXKZ2.js";
|
|
12
|
-
import {
|
|
13
|
-
ActiveJobsList
|
|
14
|
-
} from "./chunk-QD6X4L4L.js";
|
|
15
15
|
import {
|
|
16
16
|
ORANGE
|
|
17
17
|
} from "./chunk-QJQS6TUN.js";
|
|
@@ -158,4 +158,4 @@ function LauncherOverlay({
|
|
|
158
158
|
export {
|
|
159
159
|
LauncherOverlay
|
|
160
160
|
};
|
|
161
|
-
//# sourceMappingURL=chunk-
|
|
161
|
+
//# sourceMappingURL=chunk-UPXIO7CG.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
buildRecommendationPrompt
|
|
3
|
-
} from "./chunk-E3BIT53W.js";
|
|
4
1
|
import {
|
|
5
2
|
detectLlmBackend,
|
|
6
3
|
runLlmPrompt
|
|
7
4
|
} from "./chunk-FZKLLNDS.js";
|
|
5
|
+
import {
|
|
6
|
+
buildRecommendationPrompt
|
|
7
|
+
} from "./chunk-E3BIT53W.js";
|
|
8
8
|
import {
|
|
9
9
|
detectLanguages
|
|
10
10
|
} from "./chunk-OQSQC7VB.js";
|
|
@@ -164,4 +164,4 @@ var cronSuggestInstructions = async (_payload, helpers) => {
|
|
|
164
164
|
export {
|
|
165
165
|
cronSuggestInstructions
|
|
166
166
|
};
|
|
167
|
-
//# sourceMappingURL=chunk-
|
|
167
|
+
//# sourceMappingURL=chunk-VBGUNFKY.js.map
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PermanentJobError
|
|
3
|
+
} from "./chunk-DGHWRQXL.js";
|
|
1
4
|
import {
|
|
2
5
|
sendEmail
|
|
3
6
|
} from "./chunk-QSN77T7C.js";
|
|
@@ -8,9 +11,6 @@ import {
|
|
|
8
11
|
notificationPreferences,
|
|
9
12
|
profiles
|
|
10
13
|
} from "./chunk-QNSVCSHG.js";
|
|
11
|
-
import {
|
|
12
|
-
PermanentJobError
|
|
13
|
-
} from "./chunk-DGHWRQXL.js";
|
|
14
14
|
|
|
15
15
|
// src/lib/handlers/process-announcement.ts
|
|
16
16
|
import { and, asc, eq, gt, isNotNull } from "drizzle-orm";
|
|
@@ -154,4 +154,4 @@ async function sendOne(args) {
|
|
|
154
154
|
export {
|
|
155
155
|
processAnnouncement
|
|
156
156
|
};
|
|
157
|
-
//# sourceMappingURL=chunk-
|
|
157
|
+
//# sourceMappingURL=chunk-VIYUIQHW.js.map
|
|
@@ -179,8 +179,10 @@ function writeCursorHooksToDir(cursorDir, binDir) {
|
|
|
179
179
|
function writeCursorHooks(projectPath, binDir) {
|
|
180
180
|
return writeCursorHooksToDir(join(projectPath, ".cursor"), binDir);
|
|
181
181
|
}
|
|
182
|
-
function writeCursorUserHooks(binDir) {
|
|
183
|
-
|
|
182
|
+
function writeCursorUserHooks(binDir, home = homedir()) {
|
|
183
|
+
const cursorHome = join(home, ".cursor");
|
|
184
|
+
if (!existsSync(cursorHome)) return null;
|
|
185
|
+
return writeCursorHooksToDir(cursorHome, binDir);
|
|
184
186
|
}
|
|
185
187
|
function writeCopilotHooks(projectPath, binDir) {
|
|
186
188
|
const githubDir = join(projectPath, ".github");
|
|
@@ -468,9 +470,10 @@ function writeAntigravityHooksToDir(dir) {
|
|
|
468
470
|
function writeAntigravityHooks(projectPath) {
|
|
469
471
|
return writeAntigravityHooksToDir(join(projectPath, ".agents"));
|
|
470
472
|
}
|
|
471
|
-
function writeAntigravityUserHooks() {
|
|
472
|
-
const
|
|
473
|
-
|
|
473
|
+
function writeAntigravityUserHooks(home = homedir()) {
|
|
474
|
+
const geminiHome = join(home, ".gemini");
|
|
475
|
+
if (!existsSync(geminiHome)) return null;
|
|
476
|
+
return writeAntigravityHooksToDir(join(geminiHome, "config"));
|
|
474
477
|
}
|
|
475
478
|
function removeAntigravityHooksFromFile(configPath) {
|
|
476
479
|
if (!existsSync(configPath)) return false;
|
|
@@ -532,4 +535,4 @@ export {
|
|
|
532
535
|
removeAntigravityHooks,
|
|
533
536
|
removeAntigravityUserHooks
|
|
534
537
|
};
|
|
535
|
-
//# sourceMappingURL=chunk-
|
|
538
|
+
//# sourceMappingURL=chunk-XN23W5HL.js.map
|