pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
150 lines (147 loc) • 4.37 kB
JavaScript
import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
import {
assertInstallAllowed
} from "./chunk-NTUJEGDZ.js";
import {
classifyProbeFailure,
installRetryDelayMs
} from "./chunk-WONERJTP.js";
import {
safeSpawnAsync
} from "./chunk-VN2AXLEX.js";
import {
logExtension
} from "./chunk-O6TQT6RI.js";
// dist/clients/dispatch/runners/utils/lazy-installer.js
var LAZY_INSTALL_SPECS = {
rubocop: { command: "gem", args: ["install", "rubocop", "--no-document"] },
"rust-clippy": { command: "rustup", args: ["component", "add", "clippy"] },
rustfmt: { command: "rustup", args: ["component", "add", "rustfmt"] }
};
var LAZY_INSTALL_IGNORES_AMBIENT_SIGNAL = true;
var LAZY_INSTALL_TIMEOUT_MS = 18e4;
var attempts = /* @__PURE__ */ new Map();
function key(cwd, tool) {
return `${cwd}:${tool}`;
}
function getLazyInstallAttempt(tool, cwd) {
return attempts.get(key(cwd, tool))?.fact;
}
function resetLazyInstallAttempts() {
attempts.clear();
}
function suppressionFor(state) {
if (!state)
return null;
if (state.fact.outcome === "succeeded")
return "succeeded";
if (state.retryAtMs === 0)
return "held";
return Date.now() < state.retryAtMs ? "cooling" : null;
}
var inFlight = /* @__PURE__ */ new Map();
async function runLazyInstall(tool, cwd, spec) {
const k = key(cwd, tool);
const running = inFlight.get(k);
if (running)
return running;
const previous = attempts.get(k);
const suppressed = suppressionFor(previous);
if (suppressed !== null) {
logExtension({
subsystem: "install",
message: `lazy-install ${tool} suppressed (${suppressed})`,
metadata: {
tool,
cwd,
suppressed,
outcome: previous?.fact.outcome,
...previous?.fact.reason && { reason: previous.fact.reason },
...suppressed === "cooling" && {
retryAfterMs: Math.max(0, (previous?.retryAtMs ?? 0) - Date.now())
}
}
});
return false;
}
const started = performInstall(k, tool, cwd, spec);
inFlight.set(k, started);
try {
return await started;
} finally {
inFlight.delete(k);
}
}
async function performInstall(k, tool, cwd, spec) {
let result;
try {
result = await safeSpawnAsync(spec.command, spec.args, {
timeout: LAZY_INSTALL_TIMEOUT_MS,
cwd,
...LAZY_INSTALL_IGNORES_AMBIENT_SIGNAL && { ignoreAmbientSignal: true }
});
} catch (err) {
result = {
stdout: "",
stderr: err?.message ?? String(err),
status: null,
error: err,
failure: "spawn"
};
}
if (!result.error && result.status === 0) {
attempts.set(k, {
fact: { outcome: "succeeded" },
retryAtMs: 0,
transientAttempts: 0
});
return true;
}
const classified = classifyProbeFailure(result, {
command: spec.command,
unclassifiedFailureOutcome: "transient"
});
const durable = classified.outcome === "missing";
const reason = (result.error?.message ?? result.stderr ?? `exit ${result.status}`).trim().slice(0, 200);
const settledPrevious = attempts.get(k);
const transientAttempts = durable ? 0 : (settledPrevious?.transientAttempts ?? 0) + 1;
const cooldownMs = durable ? 0 : installRetryDelayMs(transientAttempts);
const retryAtMs = cooldownMs === 0 ? 0 : Date.now() + cooldownMs;
attempts.set(k, {
fact: { outcome: "failed", reason },
retryAtMs,
transientAttempts
});
logExtension({
subsystem: "install",
message: `lazy-install ${tool} failed: ${reason}`,
metadata: {
tool,
cwd,
command: spec.command,
cause: classified.cause,
durable,
transientAttempts,
held: retryAtMs === 0,
...retryAtMs > 0 && { retryAfterMs: retryAtMs - Date.now() },
evidence: classified.evidence
}
});
return false;
}
async function tryLazyInstall(tool, cwd) {
if (!assertInstallAllowed(`runner lazy install: ${tool}`))
return false;
return runLazyInstall(tool, cwd, LAZY_INSTALL_SPECS[tool]);
}
async function tryLazyInstallForFormatter(tool, cwd) {
if (!assertInstallAllowed(`formatter lazy install: ${tool}`))
return false;
return runLazyInstall(tool, cwd, LAZY_INSTALL_SPECS[tool]);
}
export {
getLazyInstallAttempt,
resetLazyInstallAttempts,
tryLazyInstall,
tryLazyInstallForFormatter
};