mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 09:20:44 +00:00
* Add proxy capture core and CLI * Expand transport capture coverage * Add QA Lab capture backend * Refine QA Lab capture UI * Fix proxy capture review feedback * Fix proxy run cleanup and TTS capture * Fix proxy capture transport follow-ups * Fix debug proxy CONNECT target parsing * Harden QA Lab asset path containment
164 lines
5.2 KiB
TypeScript
164 lines
5.2 KiB
TypeScript
import { spawn } from "node:child_process";
|
|
import { randomUUID } from "node:crypto";
|
|
import process from "node:process";
|
|
import { ensureDebugProxyCa } from "../proxy-capture/ca.js";
|
|
import { buildDebugProxyCoverageReport } from "../proxy-capture/coverage.js";
|
|
import { resolveDebugProxySettings, applyDebugProxyEnv } from "../proxy-capture/env.js";
|
|
import { startDebugProxyServer } from "../proxy-capture/proxy-server.js";
|
|
import {
|
|
finalizeDebugProxyCapture,
|
|
initializeDebugProxyCapture,
|
|
} from "../proxy-capture/runtime.js";
|
|
import {
|
|
closeDebugProxyCaptureStore,
|
|
getDebugProxyCaptureStore,
|
|
} from "../proxy-capture/store.sqlite.js";
|
|
import type { CaptureQueryPreset } from "../proxy-capture/types.js";
|
|
|
|
export async function runDebugProxyStartCommand(opts: { host?: string; port?: number }) {
|
|
const settings = resolveDebugProxySettings();
|
|
const store = getDebugProxyCaptureStore(settings.dbPath, settings.blobDir);
|
|
store.upsertSession({
|
|
id: settings.sessionId,
|
|
startedAt: Date.now(),
|
|
mode: "proxy-start",
|
|
sourceScope: "openclaw",
|
|
sourceProcess: "openclaw",
|
|
proxyUrl: settings.proxyUrl,
|
|
dbPath: settings.dbPath,
|
|
blobDir: settings.blobDir,
|
|
});
|
|
initializeDebugProxyCapture("proxy-start", settings);
|
|
const ca = await ensureDebugProxyCa(settings.certDir);
|
|
const server = await startDebugProxyServer({
|
|
host: opts.host,
|
|
port: opts.port,
|
|
settings,
|
|
});
|
|
process.stdout.write(`Debug proxy: ${server.proxyUrl}\n`);
|
|
process.stdout.write(`CA cert: ${ca.certPath}\n`);
|
|
process.stdout.write(`Capture DB: ${settings.dbPath}\n`);
|
|
process.stdout.write("Press Ctrl+C to stop.\n");
|
|
const shutdown = async () => {
|
|
process.off("SIGINT", onSignal);
|
|
process.off("SIGTERM", onSignal);
|
|
await server.stop();
|
|
if (settings.enabled) {
|
|
finalizeDebugProxyCapture(settings);
|
|
} else {
|
|
store.endSession(settings.sessionId);
|
|
closeDebugProxyCaptureStore();
|
|
}
|
|
process.exit(0);
|
|
};
|
|
const onSignal = () => {
|
|
void shutdown();
|
|
};
|
|
process.on("SIGINT", onSignal);
|
|
process.on("SIGTERM", onSignal);
|
|
await new Promise(() => undefined);
|
|
}
|
|
|
|
export async function runDebugProxyRunCommand(opts: {
|
|
host?: string;
|
|
port?: number;
|
|
commandArgs: string[];
|
|
}) {
|
|
if (opts.commandArgs.length === 0) {
|
|
throw new Error("proxy run requires a command after --");
|
|
}
|
|
const sessionId = randomUUID();
|
|
const baseSettings = resolveDebugProxySettings();
|
|
const settings = {
|
|
...baseSettings,
|
|
sessionId,
|
|
};
|
|
getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).upsertSession({
|
|
id: sessionId,
|
|
startedAt: Date.now(),
|
|
mode: "proxy-run",
|
|
sourceScope: "openclaw",
|
|
sourceProcess: "openclaw",
|
|
proxyUrl: undefined,
|
|
dbPath: settings.dbPath,
|
|
blobDir: settings.blobDir,
|
|
});
|
|
const server = await startDebugProxyServer({
|
|
host: opts.host,
|
|
port: opts.port,
|
|
settings,
|
|
});
|
|
const [command, ...args] = opts.commandArgs;
|
|
const childEnv = applyDebugProxyEnv(process.env, {
|
|
proxyUrl: server.proxyUrl,
|
|
sessionId,
|
|
dbPath: settings.dbPath,
|
|
blobDir: settings.blobDir,
|
|
certDir: settings.certDir,
|
|
});
|
|
try {
|
|
await new Promise<void>((resolve, reject) => {
|
|
const child = spawn(command, args, {
|
|
stdio: "inherit",
|
|
env: childEnv,
|
|
cwd: process.cwd(),
|
|
});
|
|
child.once("error", reject);
|
|
child.once("exit", (code, signal) => {
|
|
process.exitCode = signal ? 1 : (code ?? 1);
|
|
resolve();
|
|
});
|
|
});
|
|
} finally {
|
|
await server.stop();
|
|
getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).endSession(sessionId);
|
|
}
|
|
}
|
|
|
|
export async function runDebugProxySessionsCommand(opts: { limit?: number }) {
|
|
const settings = resolveDebugProxySettings();
|
|
const sessions = getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).listSessions(
|
|
opts.limit ?? 20,
|
|
);
|
|
process.stdout.write(`${JSON.stringify(sessions, null, 2)}\n`);
|
|
closeDebugProxyCaptureStore();
|
|
}
|
|
|
|
export async function runDebugProxyQueryCommand(opts: {
|
|
preset: CaptureQueryPreset;
|
|
sessionId?: string;
|
|
}) {
|
|
const settings = resolveDebugProxySettings();
|
|
const rows = getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).queryPreset(
|
|
opts.preset,
|
|
opts.sessionId,
|
|
);
|
|
process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`);
|
|
closeDebugProxyCaptureStore();
|
|
}
|
|
|
|
export async function runDebugProxyCoverageCommand() {
|
|
process.stdout.write(`${JSON.stringify(buildDebugProxyCoverageReport(), null, 2)}\n`);
|
|
closeDebugProxyCaptureStore();
|
|
}
|
|
|
|
export async function runDebugProxyPurgeCommand() {
|
|
const settings = resolveDebugProxySettings();
|
|
const result = getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).purgeAll();
|
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
closeDebugProxyCaptureStore();
|
|
}
|
|
|
|
export async function readDebugProxyBlobCommand(opts: { blobId: string }) {
|
|
const settings = resolveDebugProxySettings();
|
|
const content = getDebugProxyCaptureStore(settings.dbPath, settings.blobDir).readBlob(
|
|
opts.blobId,
|
|
);
|
|
if (content == null) {
|
|
closeDebugProxyCaptureStore();
|
|
throw new Error(`Unknown blob: ${opts.blobId}`);
|
|
}
|
|
process.stdout.write(content);
|
|
closeDebugProxyCaptureStore();
|
|
}
|