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
|
#!/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 { execFileSync } from "node:child_process";
|
||||||
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
@@ -384,6 +386,9 @@ function packageJsonForShrinkwrap(packageJson, shrinkwrapOverrides) {
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the npm command invocation used by shrinkwrap generation.
|
||||||
|
*/
|
||||||
export function createNpmShrinkwrapCommand(args, options = {}) {
|
export function createNpmShrinkwrapCommand(args, options = {}) {
|
||||||
return resolveNpmRunner({
|
return resolveNpmRunner({
|
||||||
comSpec: options.comSpec,
|
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) {
|
export function readPositiveIntEnv(name, fallback, env = process.env) {
|
||||||
const text = String(env[name] ?? fallback).trim();
|
const text = String(env[name] ?? fallback).trim();
|
||||||
if (!/^\d+$/u.test(text)) {
|
if (!/^\d+$/u.test(text)) {
|
||||||
@@ -407,6 +415,9 @@ export function readPositiveIntEnv(name, fallback, env = process.env) {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds execFileSync options with bounded timeout and output buffer limits.
|
||||||
|
*/
|
||||||
export function createNpmShrinkwrapExecOptions(invocation, cwd, env = process.env) {
|
export function createNpmShrinkwrapExecOptions(invocation, cwd, env = process.env) {
|
||||||
return {
|
return {
|
||||||
cwd,
|
cwd,
|
||||||
@@ -1299,6 +1310,8 @@ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.me
|
|||||||
}
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
// Test-facing helpers cover lockfile normalization, override merging, and
|
||||||
|
// changed-package detection without invoking npm.
|
||||||
collectCurrentShrinkwrapOverrides,
|
collectCurrentShrinkwrapOverrides,
|
||||||
collectOverrideViolations,
|
collectOverrideViolations,
|
||||||
collectPnpmLockViolations,
|
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 { spawn } from "node:child_process";
|
||||||
import { randomUUID } from "node:crypto";
|
import { randomUUID } from "node:crypto";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
@@ -10,7 +12,9 @@ import { fileURLToPath, pathToFileURL } from "node:url";
|
|||||||
|
|
||||||
const DEFAULT_METHODS = ["health", "config.get"];
|
const DEFAULT_METHODS = ["health", "config.get"];
|
||||||
const DEFAULT_ITERATIONS = 10;
|
const DEFAULT_ITERATIONS = 10;
|
||||||
|
/** Maximum time to wait for a spawned gateway to become reachable. */
|
||||||
export const READY_TIMEOUT_MS = 120_000;
|
export const READY_TIMEOUT_MS = 120_000;
|
||||||
|
/** Per-probe timeout used while polling gateway readiness endpoints. */
|
||||||
export const READY_PROBE_TIMEOUT_MS = 1_000;
|
export const READY_PROBE_TIMEOUT_MS = 1_000;
|
||||||
const PARENT_TERMINATION_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"];
|
const PARENT_TERMINATION_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM"];
|
||||||
const IS_DIRECT_RUN =
|
const IS_DIRECT_RUN =
|
||||||
@@ -100,6 +104,9 @@ function formatErrorMessage(error) {
|
|||||||
return String(error);
|
return String(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Polls readiness endpoints while also failing fast if the child exits.
|
||||||
|
*/
|
||||||
export async function waitForGatewayReady({
|
export async function waitForGatewayReady({
|
||||||
child,
|
child,
|
||||||
fetchImpl = fetch,
|
fetchImpl = fetch,
|
||||||
@@ -165,6 +172,9 @@ function resolveOpenClawLaunchArgs(repoRoot, sourceEntryExists = existsSync) {
|
|||||||
return [path.join(repoRoot, "openclaw.mjs")];
|
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) {
|
export function signalGatewayProcess(child, signal, killProcess = defaultKillProcess) {
|
||||||
if (process.platform !== "win32" && typeof child.pid === "number") {
|
if (process.platform !== "win32" && typeof child.pid === "number") {
|
||||||
try {
|
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) {
|
export function isGatewayProcessAlive(child, killProcess = defaultKillProcess) {
|
||||||
if (process.platform !== "win32" && typeof child.pid === "number") {
|
if (process.platform !== "win32" && typeof child.pid === "number") {
|
||||||
try {
|
try {
|
||||||
@@ -210,6 +223,9 @@ function signalGatewayProcessForParentExit(child, signal, killProcess) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Installs parent-process cleanup handlers for a spawned gateway.
|
||||||
|
*/
|
||||||
export function installGatewayParentCleanup(
|
export function installGatewayParentCleanup(
|
||||||
child,
|
child,
|
||||||
{ killProcess = defaultKillProcess, processLike = process } = {},
|
{ killProcess = defaultKillProcess, processLike = process } = {},
|
||||||
@@ -255,6 +271,9 @@ async function waitForGatewayExit(child, timeoutMs, killProcess = defaultKillPro
|
|||||||
return !isGatewayProcessAlive(child, killProcess);
|
return !isGatewayProcessAlive(child, killProcess);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the gateway with SIGTERM first and SIGKILL after the grace window.
|
||||||
|
*/
|
||||||
export async function stopGateway(child, options = {}) {
|
export async function stopGateway(child, options = {}) {
|
||||||
if (!isGatewayProcessAlive(child, options.killProcess)) {
|
if (!isGatewayProcessAlive(child, options.killProcess)) {
|
||||||
return;
|
return;
|
||||||
@@ -275,6 +294,9 @@ async function closeFileHandles(handles) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts an isolated loopback gateway with temp HOME/state directories.
|
||||||
|
*/
|
||||||
export async function startGateway({
|
export async function startGateway({
|
||||||
configPath,
|
configPath,
|
||||||
env = process.env,
|
env = process.env,
|
||||||
@@ -354,6 +376,9 @@ export async function startGateway({
|
|||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the temporary root used by the RPC RTT probe.
|
||||||
|
*/
|
||||||
export async function cleanupTempRoot(tempRoot, { rmImpl = fs.rm } = {}) {
|
export async function cleanupTempRoot(tempRoot, { rmImpl = fs.rm } = {}) {
|
||||||
try {
|
try {
|
||||||
await rmImpl(tempRoot, { force: true, recursive: true });
|
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 { spawn } from "node:child_process";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path, { resolve } from "node:path";
|
import path, { resolve } from "node:path";
|
||||||
@@ -248,6 +250,9 @@ function isRelevantTypeInput(filePath) {
|
|||||||
return TYPE_INPUT_EXTENSIONS.has(path.extname(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)) {
|
export function parseMode(argv = process.argv.slice(2)) {
|
||||||
const modeArg = argv.find((arg) => arg.startsWith("--mode="));
|
const modeArg = argv.find((arg) => arg.startsWith("--mode="));
|
||||||
const mode = modeArg?.slice("--mode=".length) ?? "all";
|
const mode = modeArg?.slice("--mode=".length) ?? "all";
|
||||||
@@ -257,6 +262,9 @@ export function parseMode(argv = process.argv.slice(2)) {
|
|||||||
return mode;
|
return mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the root shim timeout override for long package-boundary builds.
|
||||||
|
*/
|
||||||
export function resolveBoundaryRootShimsTimeoutMs(env = process.env) {
|
export function resolveBoundaryRootShimsTimeoutMs(env = process.env) {
|
||||||
const raw = env.OPENCLAW_PLUGIN_SDK_BOUNDARY_ROOT_SHIMS_TIMEOUT_MS?.trim();
|
const raw = env.OPENCLAW_PLUGIN_SDK_BOUNDARY_ROOT_SHIMS_TIMEOUT_MS?.trim();
|
||||||
if (!raw) {
|
if (!raw) {
|
||||||
@@ -309,6 +317,9 @@ function collectOldestMtime(paths, params = {}) {
|
|||||||
return Number.isFinite(oldestMtimeMs) ? oldestMtimeMs : null;
|
return Number.isFinite(oldestMtimeMs) ? oldestMtimeMs : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compares input and output mtimes to skip fresh generated artifacts.
|
||||||
|
*/
|
||||||
export function isArtifactSetFresh(params) {
|
export function isArtifactSetFresh(params) {
|
||||||
const newestInputMtimeMs = collectNewestMtime(params.inputPaths, {
|
const newestInputMtimeMs = collectNewestMtime(params.inputPaths, {
|
||||||
rootDir: params.rootDir,
|
rootDir: params.rootDir,
|
||||||
@@ -335,6 +346,9 @@ function writeStampFile(relativePath) {
|
|||||||
fs.writeFileSync(filePath, `${new Date().toISOString()}\n`, "utf8");
|
fs.writeFileSync(filePath, `${new Date().toISOString()}\n`, "utf8");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prefixes streamed child output line-by-line without breaking partial chunks.
|
||||||
|
*/
|
||||||
export function createPrefixedOutputWriter(label, target) {
|
export function createPrefixedOutputWriter(label, target) {
|
||||||
let buffered = "";
|
let buffered = "";
|
||||||
const prefix = `[${label}] `;
|
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 = {}) {
|
export function runNodeStep(label, args, timeoutMs, params = {}) {
|
||||||
const abortController = params.abortController;
|
const abortController = params.abortController;
|
||||||
const spawnImpl = params.spawnImpl ?? spawn;
|
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) {
|
export async function runNodeStepsInParallel(steps) {
|
||||||
const abortController = new AbortController();
|
const abortController = new AbortController();
|
||||||
const results = await Promise.allSettled(
|
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) {
|
export async function runNodeSteps(steps, env = process.env) {
|
||||||
if (!isLocalCheckEnabled(env)) {
|
if (!isLocalCheckEnabled(env)) {
|
||||||
await runNodeStepsInParallel(steps);
|
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 { spawnSync } from "node:child_process";
|
||||||
import { existsSync } from "node:fs";
|
import { existsSync } from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
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 = {}) {
|
export function configurePrepareGitHooks(params = {}) {
|
||||||
const cwd = params.cwd ?? DEFAULT_PACKAGE_ROOT;
|
const cwd = params.cwd ?? DEFAULT_PACKAGE_ROOT;
|
||||||
const exists = params.existsSync ?? existsSync;
|
const exists = params.existsSync ?? existsSync;
|
||||||
@@ -32,13 +37,11 @@ export function configurePrepareGitHooks(params = {}) {
|
|||||||
return { configured: false, reason: "missing-hooks-dir" };
|
return { configured: false, reason: "missing-hooks-dir" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const worktree = runGit(
|
const worktree = runGit(spawn, gitBin, ["rev-parse", "--is-inside-work-tree"], cwd, [
|
||||||
spawn,
|
"ignore",
|
||||||
gitBin,
|
"pipe",
|
||||||
["rev-parse", "--is-inside-work-tree"],
|
"ignore",
|
||||||
cwd,
|
]);
|
||||||
["ignore", "pipe", "ignore"],
|
|
||||||
);
|
|
||||||
const missingGitReason = getMissingGitReason(worktree.error);
|
const missingGitReason = getMissingGitReason(worktree.error);
|
||||||
if (missingGitReason) {
|
if (missingGitReason) {
|
||||||
return { configured: false, reason: missingGitReason };
|
return { configured: false, reason: missingGitReason };
|
||||||
@@ -47,13 +50,11 @@ export function configurePrepareGitHooks(params = {}) {
|
|||||||
return { configured: false, reason: "not-worktree" };
|
return { configured: false, reason: "not-worktree" };
|
||||||
}
|
}
|
||||||
|
|
||||||
const configured = runGit(
|
const configured = runGit(spawn, gitBin, ["config", "core.hooksPath", "git-hooks"], cwd, [
|
||||||
spawn,
|
"ignore",
|
||||||
gitBin,
|
"ignore",
|
||||||
["config", "core.hooksPath", "git-hooks"],
|
"pipe",
|
||||||
cwd,
|
]);
|
||||||
["ignore", "ignore", "pipe"],
|
|
||||||
);
|
|
||||||
const configMissingGitReason = getMissingGitReason(configured.error);
|
const configMissingGitReason = getMissingGitReason(configured.error);
|
||||||
if (configMissingGitReason) {
|
if (configMissingGitReason) {
|
||||||
return { configured: false, reason: 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");
|
const warningFilterKey = Symbol.for("openclaw.warning-filter");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Suppresses punycode deprecation warnings while preserving all other warnings.
|
||||||
|
*/
|
||||||
export function installProcessWarningFilter() {
|
export function installProcessWarningFilter() {
|
||||||
if (globalThis[warningFilterKey]?.installed) {
|
if (globalThis[warningFilterKey]?.installed) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/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 { spawn } from "node:child_process";
|
||||||
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdtempSync, readdirSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import os from "node:os";
|
import os from "node:os";
|
||||||
@@ -53,6 +55,9 @@ function parsePositiveInt(raw, flagName) {
|
|||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses extension memory profiler options after pnpm's optional separator.
|
||||||
|
*/
|
||||||
export function parseArgs(argv) {
|
export function parseArgs(argv) {
|
||||||
const args = stripLeadingPackageManagerSeparator(argv);
|
const args = stripLeadingPackageManagerSeparator(argv);
|
||||||
const options = {
|
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({
|
export async function runCase({
|
||||||
repoRoot,
|
repoRoot,
|
||||||
env,
|
env,
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/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 { spawnSync } from "node:child_process";
|
||||||
import fs from "node:fs";
|
import fs from "node:fs";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|||||||
Reference in New Issue
Block a user