fix(cli): fallback to user-local private key + record analytics
- cli.ts: key resolution now falls back to ~/.hermes/keys/pr-agent-key.pem when /opt/pr-agent-server/private-key.pem is EACCES/ENOENT (CLI as non-root user works out of the box) - cli.ts+index.ts: export logReviewEvent; CLI now records an analytics event (describe/improve/review) in the legacy pr-agent.*.log format, best-effort (never fails the CLI on a log write), honoring PR_AGENT_ANALYTICS_DIR - Verified: 16/16 tests, tsc clean, describe publishes, review publishes (comment 5757282509), webhook 403/ping/ignored paths correct
This commit is contained in:
+34
-2
@@ -5,6 +5,26 @@ import { loadConfig } from "./config";
|
||||
import { runReview } from "./review";
|
||||
import { runDescribe } from "./describe";
|
||||
import { runImprove } from "./improve";
|
||||
import { logReviewEvent } from "./index";
|
||||
import { join } from "node:path";
|
||||
|
||||
const ANALYTICS_DIR = process.env.PR_AGENT_ANALYTICS_DIR || "/var/lib/pr-agent-server/analytics";
|
||||
|
||||
function recordAnalytics(command: string, result: { status?: string; model?: string }): void {
|
||||
try {
|
||||
logReviewEvent(ANALYTICS_DIR, {
|
||||
message: result.status === "success" ? "Generated code suggestions" : `Failed to generate (${result.status ?? "error"})`,
|
||||
extra: {
|
||||
command,
|
||||
pr_url_short: "",
|
||||
model: result.model ?? "",
|
||||
error: result.status === "success" ? "" : (result.status ?? "error"),
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// analytics is best-effort; never fail the CLI on a log write
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = process.argv.slice(2);
|
||||
@@ -32,9 +52,18 @@ async function main() {
|
||||
const tool = getArg("--tool") || "review";
|
||||
|
||||
const cfg = loadConfig();
|
||||
const keyPath = getArg("--private-key") || process.env.PRIVATE_KEY_PATH || "/opt/pr-agent-server/private-key.pem";
|
||||
const keyPath =
|
||||
getArg("--private-key") ||
|
||||
process.env.PRIVATE_KEY_PATH ||
|
||||
"/opt/pr-agent-server/private-key.pem";
|
||||
const fs = await import("node:fs");
|
||||
const privateKey = fs.readFileSync(keyPath, "utf-8");
|
||||
const fallbackKey = join(process.env.HOME ?? "/home/code", ".hermes", "keys", "pr-agent-key.pem");
|
||||
let privateKey: string;
|
||||
try {
|
||||
privateKey = fs.readFileSync(keyPath, "utf-8");
|
||||
} catch {
|
||||
privateKey = fs.readFileSync(fallbackKey, "utf-8"); // EACCES/ENOENT on /opt → user-local copy
|
||||
}
|
||||
const publish = !has("--no-publish");
|
||||
|
||||
if (tool === "describe") {
|
||||
@@ -54,6 +83,7 @@ async function main() {
|
||||
));
|
||||
console.log("\n--- MARKDOWN ---\n");
|
||||
console.log(result.markdown);
|
||||
recordAnalytics("describe", result);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,6 +104,7 @@ async function main() {
|
||||
));
|
||||
console.log("\n--- MARKDOWN ---\n");
|
||||
console.log(result.markdown);
|
||||
recordAnalytics("improve", result);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -95,6 +126,7 @@ async function main() {
|
||||
));
|
||||
console.log("\n--- MARKDOWN ---\n");
|
||||
console.log(result.markdown);
|
||||
recordAnalytics("review", result);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
|
||||
+1
-1
@@ -384,7 +384,7 @@ export function readAnalyticsLogs(dir: string, maxFiles = 5): AnalyticsRecord[]
|
||||
|
||||
/** Append an analytics event in the legacy pr-agent JSONL shape so external
|
||||
* dashboards that parse pr-agent.*.log keep working. */
|
||||
function logReviewEvent(dir: string, event: Record<string, unknown>): void {
|
||||
export function logReviewEvent(dir: string, event: Record<string, unknown>): void {
|
||||
if (!dir) return;
|
||||
const fs = require("node:fs") as typeof import("node:fs");
|
||||
const path = require("node:path") as typeof import("node:path");
|
||||
|
||||
Reference in New Issue
Block a user