Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

pi/agent/ultra/dynamic-extensions/catalog/security-review-writer/d0e991293c4407629bb3b19989f7e51429c04cdbe44d170cfd26c5f19bf2fcc6/debug.ts

Raw
import { appendFile, mkdir, open, stat } from "node:fs/promises";
import { homedir } from "node:os";
import { basename, dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";

const LOG_LIMIT_BYTES = 1024 * 1024;
const MARKER_RESERVE_BYTES = 512;
const VALUE_LIMIT = 12_000;

function cacheRoot(): string {
	if (process.platform === "win32")
		return process.env.LOCALAPPDATA ?? join(homedir(), "AppData", "Local");
	if (process.platform === "darwin")
		return join(homedir(), "Library", "Caches");
	return process.env.XDG_CACHE_HOME ?? join(homedir(), ".cache");
}

function bounded(value: unknown): unknown {
	let encoded: string;
	try {
		encoded = JSON.stringify(value);
	} catch {
		encoded = String(value);
	}
	return encoded.length <= VALUE_LIMIT
		? value
		: { truncated: true, preview: encoded.slice(0, VALUE_LIMIT) };
}

export function dynamicExtensionRevision(moduleUrl: string): string {
	return basename(dirname(fileURLToPath(moduleUrl)));
}

export function dynamicExtensionLogPath(
	sessionId: string,
	extensionName: string,
): string {
	return join(
		cacheRoot(),
		"pi",
		"ultra",
		"dynamic-extensions",
		"logs",
		sessionId,
		`${extensionName}.jsonl`,
	);
}

async function hasTruncationMarker(path: string, size: number): Promise<boolean> {
	if (size === 0) return false;
	const handle = await open(path, "r");
	try {
		const length = Math.min(size, 1024);
		const bytes = Buffer.alloc(length);
		await handle.read(bytes, 0, length, size - length);
		return bytes.toString("utf8").includes('"event":"log_truncated"');
	} finally {
		await handle.close();
	}
}

export async function logDynamicExtensionEvent(args: {
	name: string;
	revision: string;
	ctx: ExtensionContext;
	event: string;
	durationMs?: number;
	context?: unknown;
}): Promise<void> {
	const sessionId = args.ctx.sessionManager.getSessionId();
	const path = dynamicExtensionLogPath(sessionId, args.name);
	await mkdir(dirname(path), { recursive: true });
	const size = await stat(path).then((value) => value.size, () => 0);
	if (size >= LOG_LIMIT_BYTES) return;
	const record = {
		timestamp: new Date().toISOString(),
		extension: { name: args.name, revision: args.revision },
		childSessionId: sessionId,
		event: args.event,
		...(args.durationMs === undefined ? {} : { durationMs: args.durationMs }),
		...(args.context === undefined ? {} : { context: bounded(args.context) }),
	};
	const line = `${JSON.stringify(record)}\n`;
	if (
		size < LOG_LIMIT_BYTES - MARKER_RESERVE_BYTES &&
		size + Buffer.byteLength(line) <= LOG_LIMIT_BYTES - MARKER_RESERVE_BYTES
	) {
		await appendFile(path, line, "utf8");
		return;
	}
	if (await hasTruncationMarker(path, size)) return;
	const marker = {
		timestamp: new Date().toISOString(),
		extension: { name: args.name, revision: args.revision },
		childSessionId: sessionId,
		event: "log_truncated",
		context: { limitBytes: LOG_LIMIT_BYTES },
	};
	await appendFile(path, `${JSON.stringify(marker)}\n`, "utf8");
}