171 lines
4.8 KiB
JavaScript
171 lines
4.8 KiB
JavaScript
import { spawnSync } from "node:child_process";
|
|
import { existsSync, mkdirSync, rmSync, statSync } from "node:fs";
|
|
import path from "node:path";
|
|
import { pathToFileURL } from "node:url";
|
|
import { repoRoot } from "./manifest-lib.mjs";
|
|
|
|
export const pluginInspectorRef = "8899fc796c64655bdef3583286215ca0aeeef388";
|
|
export const pluginInspectorPackage = "@openclaw/plugin-inspector@0.3.10";
|
|
|
|
export async function loadPluginInspector() {
|
|
const publicApi = await import(pathToFileURL(resolvePluginInspectorSourcePath()).href);
|
|
if (typeof publicApi.inspectPlugin === "function" && typeof publicApi.inspectSourceText === "function") {
|
|
return publicApi;
|
|
}
|
|
|
|
const advancedApiPath = path.join(resolvePluginInspectorRoot(), "src", "advanced.js");
|
|
return import(pathToFileURL(advancedApiPath).href);
|
|
}
|
|
|
|
export async function loadPluginInspectorPublicApi() {
|
|
return import(pathToFileURL(resolvePluginInspectorPublicApiPath()).href);
|
|
}
|
|
|
|
export function resolvePluginInspectorCliInvocation(options = {}) {
|
|
if (process.env.CRABPOT_PLUGIN_INSPECTOR_BIN) {
|
|
return {
|
|
command: process.env.CRABPOT_PLUGIN_INSPECTOR_BIN,
|
|
args: [],
|
|
};
|
|
}
|
|
|
|
const useSource = options.preferSource || process.env.CRABPOT_PLUGIN_INSPECTOR_CLI === "source";
|
|
if (!useSource) {
|
|
return {
|
|
command: npmCommand(),
|
|
args: ["exec", "--yes", "--package", pluginInspectorPackage, "--", "plugin-inspector"],
|
|
shell: process.platform === "win32",
|
|
};
|
|
}
|
|
|
|
return {
|
|
command: process.execPath,
|
|
args: [resolvePluginInspectorCliPath()],
|
|
};
|
|
}
|
|
|
|
export function resolvePluginInspectorCliPath() {
|
|
return path.join(resolvePluginInspectorRoot(), "src", "cli.js");
|
|
}
|
|
|
|
function resolvePluginInspectorSourcePath() {
|
|
return path.join(resolvePluginInspectorRoot(), "src", "advanced.js");
|
|
}
|
|
|
|
function resolvePluginInspectorPublicApiPath() {
|
|
return path.join(resolvePluginInspectorRoot(), "src", "index.js");
|
|
}
|
|
|
|
function resolvePluginInspectorRoot() {
|
|
if (process.env.CRABPOT_PLUGIN_INSPECTOR_DIR) {
|
|
return path.resolve(repoRoot, process.env.CRABPOT_PLUGIN_INSPECTOR_DIR);
|
|
}
|
|
|
|
const siblingRoot = path.resolve(repoRoot, "../plugin-inspector");
|
|
if (existsSync(path.join(siblingRoot, "src", "index.js"))) {
|
|
return siblingRoot;
|
|
}
|
|
|
|
return ensurePinnedInspectorCheckout();
|
|
}
|
|
|
|
function ensurePinnedInspectorCheckout() {
|
|
const checkoutParent = path.join(repoRoot, ".crabpot", "plugin-inspector");
|
|
const checkoutDir = path.join(checkoutParent, pluginInspectorRef);
|
|
const sourcePath = path.join(checkoutDir, "src", "index.js");
|
|
|
|
if (existsSync(sourcePath) && readGitHead(checkoutDir) === pluginInspectorRef) {
|
|
return checkoutDir;
|
|
}
|
|
|
|
mkdirSync(checkoutParent, { recursive: true });
|
|
withCheckoutLock(checkoutParent, () => {
|
|
if (existsSync(sourcePath) && readGitHead(checkoutDir) === pluginInspectorRef) {
|
|
return;
|
|
}
|
|
|
|
rmSync(checkoutDir, { force: true, recursive: true });
|
|
run("git", ["init", checkoutDir]);
|
|
run("git", ["-C", checkoutDir, "fetch", "--depth=1", "https://github.com/openclaw/plugin-inspector.git", pluginInspectorRef]);
|
|
run("git", ["-C", checkoutDir, "checkout", "--detach", "FETCH_HEAD"]);
|
|
});
|
|
|
|
if (readGitHead(checkoutDir) !== pluginInspectorRef) {
|
|
throw new Error(`plugin-inspector checkout did not resolve to ${pluginInspectorRef}`);
|
|
}
|
|
return checkoutDir;
|
|
}
|
|
|
|
function withCheckoutLock(checkoutParent, callback) {
|
|
const lockDir = path.join(checkoutParent, ".checkout.lock");
|
|
const startedAt = Date.now();
|
|
|
|
while (true) {
|
|
try {
|
|
mkdirSync(lockDir);
|
|
break;
|
|
} catch (error) {
|
|
if (error?.code !== "EEXIST") {
|
|
throw error;
|
|
}
|
|
if (isStaleLock(lockDir) || Date.now() - startedAt > 120_000) {
|
|
rmSync(lockDir, { force: true, recursive: true });
|
|
continue;
|
|
}
|
|
sleep(100);
|
|
}
|
|
}
|
|
|
|
try {
|
|
callback();
|
|
} finally {
|
|
rmSync(lockDir, { force: true, recursive: true });
|
|
}
|
|
}
|
|
|
|
function isStaleLock(lockDir) {
|
|
try {
|
|
return Date.now() - statSync(lockDir).mtimeMs > 300_000;
|
|
} catch {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function sleep(ms) {
|
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
}
|
|
|
|
function npmCommand() {
|
|
return "npm";
|
|
}
|
|
|
|
function readGitHead(checkoutDir) {
|
|
const result = spawnSync("git", ["-C", checkoutDir, "rev-parse", "HEAD"], {
|
|
encoding: "utf8",
|
|
});
|
|
if (result.status !== 0) {
|
|
return null;
|
|
}
|
|
return result.stdout.trim();
|
|
}
|
|
|
|
function run(command, commandArgs) {
|
|
const result = spawnSync(command, commandArgs, {
|
|
cwd: repoRoot,
|
|
encoding: "utf8",
|
|
stdio: "pipe",
|
|
});
|
|
if (result.error) {
|
|
throw result.error;
|
|
}
|
|
if (result.status !== 0) {
|
|
if (result.stdout) {
|
|
process.stderr.write(result.stdout);
|
|
}
|
|
if (result.stderr) {
|
|
process.stderr.write(result.stderr);
|
|
}
|
|
throw new Error(`${command} ${commandArgs.join(" ")} failed with exit code ${result.status}`);
|
|
}
|
|
}
|