mirror of
https://github.com/openclaw/openclaw.git
synced 2026-06-06 05:51:15 +08:00
docs: document profiling scripts
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
// Generates npm-shrinkwrap.json files that mirror pnpm lock policy for
|
||||
// published packages while stripping dev-only dependency state.
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
@@ -384,6 +386,9 @@ function packageJsonForShrinkwrap(packageJson, shrinkwrapOverrides) {
|
||||
return normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the npm command invocation used by shrinkwrap generation.
|
||||
*/
|
||||
export function createNpmShrinkwrapCommand(args, options = {}) {
|
||||
return resolveNpmRunner({
|
||||
comSpec: options.comSpec,
|
||||
@@ -395,6 +400,9 @@ export function createNpmShrinkwrapCommand(args, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a positive integer env override for shrinkwrap subprocess limits.
|
||||
*/
|
||||
export function readPositiveIntEnv(name, fallback, env = process.env) {
|
||||
const text = String(env[name] ?? fallback).trim();
|
||||
if (!/^\d+$/u.test(text)) {
|
||||
@@ -407,6 +415,9 @@ export function readPositiveIntEnv(name, fallback, env = process.env) {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds execFileSync options with bounded timeout and output buffer limits.
|
||||
*/
|
||||
export function createNpmShrinkwrapExecOptions(invocation, cwd, env = process.env) {
|
||||
return {
|
||||
cwd,
|
||||
@@ -1299,6 +1310,8 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
|
||||
}
|
||||
|
||||
export {
|
||||
// Test-facing helpers cover lockfile normalization, override merging, and
|
||||
// changed-package detection without invoking npm.
|
||||
collectCurrentShrinkwrapOverrides,
|
||||
collectOverrideViolations,
|
||||
collectPnpmLockViolations,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Measures gateway RPC round-trip time by launching an isolated local gateway
|
||||
// and writing qa-lab-compatible summary artifacts.
|
||||
import { spawn } from "node:child_process";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { existsSync } from "node:fs";
|
||||
@@ -10,7 +12,9 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
|
||||
const DEFAULT_METHODS = ["health", "config.get"];
|
||||
const DEFAULT_ITERATIONS = 10;
|
||||
/** Maximum time to wait for a spawned gateway to become reachable. */
|
||||
export const READY_TIMEOUT_MS = 120_000;
|
||||
/** Per-probe timeout used while polling gateway readiness endpoints. */
|
||||
export const READY_PROBE_TIMEOUT_MS = 1_000;
|
||||
const PARENT_TERMINATION_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"];
|
||||
const IS_DIRECT_RUN =
|
||||
@@ -100,6 +104,9 @@ function formatErrorMessage(error) {
|
||||
return String(error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls readiness endpoints while also failing fast if the child exits.
|
||||
*/
|
||||
export async function waitForGatewayReady({
|
||||
child,
|
||||
fetchImpl = fetch,
|
||||
@@ -165,6 +172,9 @@ function resolveOpenClawLaunchArgs(repoRoot, sourceEntryExists = existsSync) {
|
||||
return [path.join(repoRoot, "openclaw.mjs")];
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals the gateway process group on POSIX so spawned children are cleaned up.
|
||||
*/
|
||||
export function signalGatewayProcess(child, signal, killProcess = defaultKillProcess) {
|
||||
if (process.platform !== "win32" && typeof child.pid === "number") {
|
||||
try {
|
||||
@@ -187,6 +197,9 @@ export function signalGatewayProcess(child, signal, killProcess = defaultKillPro
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks process-group liveness without treating an already-exited child as an error.
|
||||
*/
|
||||
export function isGatewayProcessAlive(child, killProcess = defaultKillProcess) {
|
||||
if (process.platform !== "win32" && typeof child.pid === "number") {
|
||||
try {
|
||||
@@ -210,6 +223,9 @@ function signalGatewayProcessForParentExit(child, signal, killProcess) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs parent-process cleanup handlers for a spawned gateway.
|
||||
*/
|
||||
export function installGatewayParentCleanup(
|
||||
child,
|
||||
{ killProcess = defaultKillProcess, processLike = process } = {},
|
||||
@@ -255,6 +271,9 @@ async function waitForGatewayExit(child, timeoutMs, killProcess = defaultKillPro
|
||||
return !isGatewayProcessAlive(child, killProcess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the gateway with SIGTERM first and SIGKILL after the grace window.
|
||||
*/
|
||||
export async function stopGateway(child, options = {}) {
|
||||
if (!isGatewayProcessAlive(child, options.killProcess)) {
|
||||
return;
|
||||
@@ -275,6 +294,9 @@ async function closeFileHandles(handles) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an isolated loopback gateway with temp HOME/state directories.
|
||||
*/
|
||||
export async function startGateway({
|
||||
configPath,
|
||||
env = process.env,
|
||||
@@ -354,6 +376,9 @@ export async function startGateway({
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the temporary root used by the RPC RTT probe.
|
||||
*/
|
||||
export async function cleanupTempRoot(tempRoot, { rmImpl = fs.rm } = {}) {
|
||||
try {
|
||||
await rmImpl(tempRoot, { force: true, recursive: true });
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Prepares declaration and entry-shim artifacts that prove plugin package
|
||||
// boundary imports resolve through public package surfaces.
|
||||
import { spawn } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path, { resolve } from "node:path";
|
||||
@@ -248,6 +250,9 @@ function isRelevantTypeInput(filePath) {
|
||||
return TYPE_INPUT_EXTENSIONS.has(path.extname(filePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the artifact preparation mode from CLI arguments.
|
||||
*/
|
||||
export function parseMode(argv = process.argv.slice(2)) {
|
||||
const modeArg = argv.find((arg) => arg.startsWith("--mode="));
|
||||
const mode = modeArg?.slice("--mode=".length) ?? "all";
|
||||
@@ -257,6 +262,9 @@ export function parseMode(argv = process.argv.slice(2)) {
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the root shim timeout override for long package-boundary builds.
|
||||
*/
|
||||
export function resolveBoundaryRootShimsTimeoutMs(env = process.env) {
|
||||
const raw = env.OPENCLAW_PLUGIN_SDK_BOUNDARY_ROOT_SHIMS_TIMEOUT_MS?.trim();
|
||||
if (!raw) {
|
||||
@@ -309,6 +317,9 @@ function collectOldestMtime(paths, params = {}) {
|
||||
return Number.isFinite(oldestMtimeMs) ? oldestMtimeMs : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares input and output mtimes to skip fresh generated artifacts.
|
||||
*/
|
||||
export function isArtifactSetFresh(params) {
|
||||
const newestInputMtimeMs = collectNewestMtime(params.inputPaths, {
|
||||
rootDir: params.rootDir,
|
||||
@@ -335,6 +346,9 @@ function writeStampFile(relativePath) {
|
||||
fs.writeFileSync(filePath, `${new Date().toISOString()}\n`, "utf8");
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefixes streamed child output line-by-line without breaking partial chunks.
|
||||
*/
|
||||
export function createPrefixedOutputWriter(label, target) {
|
||||
let buffered = "";
|
||||
const prefix = `[${label}] `;
|
||||
@@ -412,6 +426,9 @@ function installNodeStepParentSignalForwarders() {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs one artifact step with timeout, abort propagation, and prefixed output.
|
||||
*/
|
||||
export function runNodeStep(label, args, timeoutMs, params = {}) {
|
||||
const abortController = params.abortController;
|
||||
const spawnImpl = params.spawnImpl ?? spawn;
|
||||
@@ -516,6 +533,9 @@ export function runNodeStep(label, args, timeoutMs, params = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs independent artifact steps together and aborts siblings on first failure.
|
||||
*/
|
||||
export async function runNodeStepsInParallel(steps) {
|
||||
const abortController = new AbortController();
|
||||
const results = await Promise.allSettled(
|
||||
@@ -529,6 +549,9 @@ export async function runNodeStepsInParallel(steps) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses serial or parallel artifact execution based on local heavy-check policy.
|
||||
*/
|
||||
export async function runNodeSteps(steps, env = process.env) {
|
||||
if (!isLocalCheckEnabled(env)) {
|
||||
await runNodeStepsInParallel(steps);
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Configures this checkout's Git hooks path during package prepare when git
|
||||
// and the hooks directory are available.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { dirname, join } from "node:path";
|
||||
@@ -21,6 +23,9 @@ function runGit(spawn, gitBin, args, cwd, stdio) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the repo-local hooks path and returns a structured reason if skipped.
|
||||
*/
|
||||
export function configurePrepareGitHooks(params = {}) {
|
||||
const cwd = params.cwd ?? DEFAULT_PACKAGE_ROOT;
|
||||
const exists = params.existsSync ?? existsSync;
|
||||
@@ -32,13 +37,11 @@ export function configurePrepareGitHooks(params = {}) {
|
||||
return { configured: false, reason: "missing-hooks-dir" };
|
||||
}
|
||||
|
||||
const worktree = runGit(
|
||||
spawn,
|
||||
gitBin,
|
||||
["rev-parse", "--is-inside-work-tree"],
|
||||
cwd,
|
||||
["ignore", "pipe", "ignore"],
|
||||
);
|
||||
const worktree = runGit(spawn, gitBin, ["rev-parse", "--is-inside-work-tree"], cwd, [
|
||||
"ignore",
|
||||
"pipe",
|
||||
"ignore",
|
||||
]);
|
||||
const missingGitReason = getMissingGitReason(worktree.error);
|
||||
if (missingGitReason) {
|
||||
return { configured: false, reason: missingGitReason };
|
||||
@@ -47,13 +50,11 @@ export function configurePrepareGitHooks(params = {}) {
|
||||
return { configured: false, reason: "not-worktree" };
|
||||
}
|
||||
|
||||
const configured = runGit(
|
||||
spawn,
|
||||
gitBin,
|
||||
["config", "core.hooksPath", "git-hooks"],
|
||||
cwd,
|
||||
["ignore", "ignore", "pipe"],
|
||||
);
|
||||
const configured = runGit(spawn, gitBin, ["config", "core.hooksPath", "git-hooks"], cwd, [
|
||||
"ignore",
|
||||
"ignore",
|
||||
"pipe",
|
||||
]);
|
||||
const configMissingGitReason = getMissingGitReason(configured.error);
|
||||
if (configMissingGitReason) {
|
||||
return { configured: false, reason: configMissingGitReason };
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
// Installs a process-wide warning filter for dependency warnings that are known
|
||||
// noise in current toolchains.
|
||||
const warningFilterKey = Symbol.for("openclaw.warning-filter");
|
||||
|
||||
/**
|
||||
* Suppresses punycode deprecation warnings while preserving all other warnings.
|
||||
*/
|
||||
export function installProcessWarningFilter() {
|
||||
if (globalThis[warningFilterKey]?.installed) {
|
||||
return;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Profiles peak RSS for built bundled plugin entrypoints and emits a JSON
|
||||
// report suitable for extension memory budget review.
|
||||
import { spawn } from "node:child_process";
|
||||
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||
import os from "node:os";
|
||||
@@ -53,6 +55,9 @@ function parsePositiveInt(raw, flagName) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses extension memory profiler options after pnpm's optional separator.
|
||||
*/
|
||||
export function parseArgs(argv) {
|
||||
const args = stripLeadingPackageManagerSeparator(argv);
|
||||
const options = {
|
||||
@@ -171,6 +176,9 @@ function summarizeStderr(stderr, lines = 8, maxChars = STDERR_PREVIEW_MAX_CHARS)
|
||||
)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs one import scenario in a child process and captures bounded output plus RSS.
|
||||
*/
|
||||
export async function runCase({
|
||||
repoRoot,
|
||||
env,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// Profiles selected tsgo graphs and writes diagnostics/trace artifacts for
|
||||
// TypeScript graph size and performance investigations.
|
||||
import { spawnSync } from "node:child_process";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
Reference in New Issue
Block a user