pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
407 lines (402 loc) • 13.7 kB
JavaScript
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
DEFAULT_MAX_OUTPUT_BYTES,
writeFileAtomic
} from "./chunk-2EECBNC5.js";
import {
incrementDegradationCount
} from "./chunk-N3YQJI6O.js";
import {
normalizeMapKey
} from "./chunk-Q5U6FMDI.js";
// dist/clients/mcp/ipc.js
import * as crypto from "node:crypto";
import * as fs from "node:fs";
import * as net from "node:net";
import * as os from "node:os";
import * as path from "node:path";
var WARM_DIAGNOSTICS_SCHEMA_VERSION = 1;
var WARM_CODE_ACTION_LOOKUP_LIMIT = 6;
function ipcPathForCwd(cwd, platform = process.platform) {
const hash = workspaceHash(cwd, platform);
if (platform === "win32") {
return `\\\\.\\pipe\\pi-lens-mcp-${hash}`;
}
return path.join(os.tmpdir(), `pi-lens-mcp-${hash}.sock`);
}
function workspaceHash(cwd, platform = process.platform) {
const resolved = path.resolve(cwd);
const root = platform === "win32" || platform === "darwin" ? resolved.toLowerCase() : resolved;
return crypto.createHash("sha256").update(root).digest("hex").slice(0, 16);
}
function diagnosticsIpcPathForCwd(cwd, pid, platform = process.platform) {
const base = ipcPathForCwd(cwd, platform);
if (platform === "win32")
return `${base}-diagnostics-${pid}`;
return base.replace(/\.sock$/, `-diagnostics-${pid}.sock`);
}
function isNoListenerError(error) {
const code = error?.code;
return code === "ENOENT" || code === "ECONNREFUSED";
}
function contentHash(content) {
return crypto.createHash("sha256").update(content).digest("hex");
}
function requestOverWarmIpc(endpoint, timeoutMs, buildRequest, validate) {
return new Promise((resolve3) => {
let settled = false;
const finish = (value) => {
if (settled)
return;
settled = true;
clearTimeout(timer);
socket.destroy();
resolve3(value);
};
const deadlineAt = Date.now() + timeoutMs;
const socket = net.createConnection(endpoint);
socket.setEncoding("utf8");
const timer = setTimeout(() => finish({ available: false, reason: "timeout" }), timeoutMs);
timer.unref();
socket.on("connect", () => {
socket.write(`${JSON.stringify(buildRequest(deadlineAt))}
`);
});
socket.on("data", createWarmIpcLineReader((line) => {
try {
const message = JSON.parse(line);
const result = message.result;
if (message.error || !result) {
finish({ available: false, reason: "ipc-error" });
return;
}
const reason = validate(result, deadlineAt);
if (reason === void 0) {
finish({ available: true, response: result });
} else {
finish({ available: false, reason });
}
} catch {
finish({ available: false, reason: "schema-mismatch" });
}
}, {
label: "warm-diagnostics-reply",
onOverflow: () => finish({ available: false, reason: "ipc-error" })
}));
socket.on("error", (error) => finish({
available: false,
reason: isNoListenerError(error) ? "no-listener" : "ipc-error"
}));
socket.on("close", () => finish({ available: false, reason: "ipc-error" }));
});
}
function requestWarmDiagnostics(cwd, incumbentPid, file, content, timeoutMs) {
const expectedHash = contentHash(content);
return requestOverWarmIpc(diagnosticsIpcPathForCwd(cwd, incumbentPid), timeoutMs, (deadlineAt) => ({
route: "diagnostics",
version: WARM_DIAGNOSTICS_SCHEMA_VERSION,
file,
cwd,
content,
contentHash: expectedHash,
deadlineAt
}), (result, deadlineAt) => {
if (result.route !== "diagnostics" || result.version !== WARM_DIAGNOSTICS_SCHEMA_VERSION) {
return "schema-mismatch";
}
if (!result.fresh || result.inconclusive || result.contentHash !== expectedHash || result.servedAt > deadlineAt) {
return "stale-answer";
}
return void 0;
});
}
function requestWarmCodeActions(cwd, incumbentPid, file, expectedContentHash, ranges, timeoutMs) {
return requestOverWarmIpc(diagnosticsIpcPathForCwd(cwd, incumbentPid), timeoutMs, (deadlineAt) => ({
route: "code-actions",
version: WARM_DIAGNOSTICS_SCHEMA_VERSION,
file,
cwd,
contentHash: expectedContentHash,
ranges,
deadlineAt
}), (result, deadlineAt) => {
if (result.route !== "code-actions" || result.version !== WARM_DIAGNOSTICS_SCHEMA_VERSION || !Array.isArray(result.actions) || result.actions.length !== ranges.length || result.actions.some((actions) => !Array.isArray(actions))) {
return "schema-mismatch";
}
if (result.contentHash !== expectedContentHash || result.servedAt > deadlineAt) {
return "stale-answer";
}
return void 0;
});
}
var WARM_TURN_END_SCHEMA_VERSION = 2;
async function requestWarmTurnEnd(cwd, timeoutMs = 55e3) {
const startedAt = Date.now();
const first = await requestOverWarmIpc(ipcPathForCwd(cwd), timeoutMs, () => ({
route: "turn-end",
version: WARM_TURN_END_SCHEMA_VERSION,
cwd
}), (result) => result.route === "turn-end" && result.version === WARM_TURN_END_SCHEMA_VERSION ? void 0 : "schema-mismatch");
if (!first.available || !first.response.deliveryId)
return first;
const remainingMs = timeoutMs - (Date.now() - startedAt);
if (remainingMs <= 0)
return { available: false, reason: "timeout" };
const ack = await requestOverWarmIpc(ipcPathForCwd(cwd), remainingMs, () => ({
route: "turn-end-ack",
version: WARM_TURN_END_SCHEMA_VERSION,
cwd,
deliveryId: first.response.deliveryId
}), (result) => result.route === "turn-end-ack" && result.acknowledged === true ? void 0 : "ipc-error");
return ack.available ? { available: true, response: first.response } : { available: false, reason: ack.reason };
}
function turnEndStatusPathForCwd(cwd, platform = process.platform) {
return path.join(os.tmpdir(), `pi-lens-turn-end-${workspaceHash(cwd, platform)}.json`);
}
function readTurnEndStatus(cwd, platform = process.platform) {
try {
const raw = fs.readFileSync(turnEndStatusPathForCwd(cwd, platform), "utf8");
const parsed = JSON.parse(raw);
if (!parsed || typeof parsed !== "object")
return void 0;
return {
ran: typeof parsed.ran === "number" ? parsed.ran : 0,
skipped: typeof parsed.skipped === "number" ? parsed.skipped : 0,
failed: typeof parsed.failed === "number" ? parsed.failed : 0,
lastSkipReason: parsed.lastSkipReason,
lastRunAt: parsed.lastRunAt,
lastSkipAt: parsed.lastSkipAt,
lastFailureOperation: parsed.lastFailureOperation,
lastFailureReason: parsed.lastFailureReason,
lastFailureAt: parsed.lastFailureAt
};
} catch {
return void 0;
}
}
function recordTurnEndOutcome(cwd, outcome, platform = process.platform) {
try {
const now = (/* @__PURE__ */ new Date()).toISOString();
const previous = readTurnEndStatus(cwd, platform) ?? {
ran: 0,
skipped: 0,
failed: 0
};
let next;
if ("failed" in outcome) {
next = {
...previous,
failed: previous.failed + 1,
lastFailureOperation: outcome.operation,
lastFailureReason: outcome.reason,
lastFailureAt: now
};
} else {
next = outcome.ran ? { ...previous, ran: previous.ran + 1, lastRunAt: now } : {
...previous,
skipped: previous.skipped + 1,
lastSkipReason: outcome.reason,
lastSkipAt: now
};
}
writeFileAtomic(turnEndStatusPathForCwd(cwd, platform), `${JSON.stringify(next)}
`);
} catch {
}
}
function requestWarmAnalyze(cwd, file, timeoutMs = 3e4) {
return new Promise((resolve3) => {
let settled = false;
const finish = (value) => {
if (settled)
return;
settled = true;
clearTimeout(timer);
resolve3(value);
};
const socket = net.createConnection(ipcPathForCwd(cwd));
socket.setEncoding("utf8");
const timer = setTimeout(() => {
socket.destroy();
finish(void 0);
}, timeoutMs);
timer.unref();
socket.on("connect", () => {
const request = { file, cwd };
socket.write(`${JSON.stringify(request)}
`);
});
socket.on("data", createWarmIpcLineReader((line) => {
try {
const message = JSON.parse(line);
finish(message.error ? void 0 : message.result);
} catch {
finish(void 0);
}
socket.end();
}, {
label: "warm-analyze-reply",
// `finish` here only clears the timer and resolves — unlike
// `requestOverWarmIpc`'s, which destroys the socket itself — so the
// peer that misframed would otherwise keep an open connection and
// keep writing into a reader that ignores it.
onOverflow: () => {
socket.destroy();
finish(void 0);
}
}));
socket.on("error", () => finish(void 0));
socket.on("close", () => finish(void 0));
});
}
var MAX_FRAMED_LINE_BYTES = DEFAULT_MAX_OUTPUT_BYTES;
function createWarmIpcLineReader(onLine, options) {
let buffer = "";
let bufferedBytes = 0;
let dispatched = false;
let resyncing = false;
const overflow = (frameBytes, awaitingNewline) => {
buffer = "";
bufferedBytes = 0;
resyncing = awaitingNewline;
if (options.continuous !== true)
dispatched = true;
incrementDegradationCount({
kind: "ipc-frame-overflow",
subject: options.label,
reason: `discarded ${awaitingNewline ? "an unterminated" : "a complete"} line at ${frameBytes} bytes (limit ${MAX_FRAMED_LINE_BYTES})`,
metadata: {
limitBytes: MAX_FRAMED_LINE_BYTES,
overflowBytes: frameBytes,
terminated: !awaitingNewline
}
});
options.onOverflow?.();
};
return (chunk) => {
if (dispatched)
return;
let from = 0;
if (resyncing) {
const end = chunk.indexOf("\n");
if (end === -1)
return;
resyncing = false;
from = end + 1;
}
let newline = chunk.indexOf("\n", from);
while (newline !== -1) {
const segment = chunk.slice(from, newline);
from = newline + 1;
const frameBytes = bufferedBytes + Buffer.byteLength(segment);
if (frameBytes > MAX_FRAMED_LINE_BYTES) {
overflow(frameBytes, false);
if (dispatched)
return;
newline = chunk.indexOf("\n", from);
continue;
}
const line = buffer + segment;
buffer = "";
bufferedBytes = 0;
if (options.continuous !== true) {
dispatched = true;
onLine(line);
return;
}
onLine(line);
newline = chunk.indexOf("\n", from);
}
if (from < chunk.length) {
const rest = chunk.slice(from);
buffer += rest;
bufferedBytes += Buffer.byteLength(rest);
}
if (bufferedBytes <= MAX_FRAMED_LINE_BYTES)
return;
overflow(bufferedBytes, true);
};
}
function createWarmIpcRequestQueue() {
let chain = Promise.resolve();
return {
enqueue(work) {
const next = chain.then(work);
chain = next.catch(() => void 0);
return next;
}
};
}
// dist/clients/dispatch/runner-context.js
import * as fs2 from "node:fs";
import * as path2 from "node:path";
function resolveAgainstAncestors(cwd, filePath) {
if (path2.isAbsolute(filePath)) {
return path2.resolve(filePath);
}
const normalizedRelative = filePath.replace(/\\/g, "/");
const cwdBaseName = path2.basename(path2.resolve(cwd)).replace(/\\/g, "/");
if (cwdBaseName && (normalizedRelative === cwdBaseName || normalizedRelative.startsWith(`${cwdBaseName}/`))) {
const trimmedRelative = normalizedRelative === cwdBaseName ? "" : normalizedRelative.slice(cwdBaseName.length + 1);
const trimmedCandidate = trimmedRelative ? path2.resolve(cwd, trimmedRelative) : path2.resolve(cwd);
if (fs2.existsSync(trimmedCandidate)) {
return trimmedCandidate;
}
}
const directCandidate = path2.resolve(cwd, filePath);
if (fs2.existsSync(directCandidate)) {
return directCandidate;
}
let current = path2.resolve(cwd);
const { root } = path2.parse(current);
while (current !== root) {
const parent = path2.dirname(current);
if (parent === current)
break;
const candidate = path2.resolve(parent, filePath);
if (fs2.existsSync(candidate)) {
return candidate;
}
current = parent;
}
return directCandidate;
}
function resolveRunnerPath(cwd, filePath) {
return normalizeMapKey(resolveAgainstAncestors(cwd, filePath));
}
function toRunnerDisplayPath(cwd, filePath) {
const cwdKey = normalizeMapKey(path2.resolve(cwd));
const fileKey = resolveRunnerPath(cwd, filePath);
const relative2 = path2.relative(cwdKey, fileKey).replace(/\\/g, "/");
if (relative2 && relative2 !== "." && !relative2.startsWith("../")) {
return relative2;
}
return fileKey;
}
// dist/clients/freshness.js
var MTIME_DRIFT_TOLERANCE_MS = 50;
function freshnessFromMtime(input) {
const tolerance = input.toleranceMs ?? MTIME_DRIFT_TOLERANCE_MS;
if (input.mtimeMs === void 0) {
return { verdict: "indeterminate", reason: "no-mtime-evidence" };
}
return input.mtimeMs > input.referenceMs + tolerance ? { verdict: "stale", reason: "modified-after-reference" } : { verdict: "fresh" };
}
export {
resolveRunnerPath,
toRunnerDisplayPath,
freshnessFromMtime,
WARM_CODE_ACTION_LOOKUP_LIMIT,
ipcPathForCwd,
diagnosticsIpcPathForCwd,
contentHash,
requestWarmDiagnostics,
requestWarmCodeActions,
WARM_TURN_END_SCHEMA_VERSION,
requestWarmTurnEnd,
readTurnEndStatus,
recordTurnEndOutcome,
requestWarmAnalyze,
createWarmIpcLineReader,
createWarmIpcRequestQueue
};