chore(lint): clear 172 lint:all baseline violations in five batches (#118098)

* chore(lint): clean Android-Linux app assets batch

* chore(lint): clean setup-launcher-plugin batch

* chore(lint): clean changelog-updater batch

* chore(lint): clean QA-runtime-helper batch

* chore(lint): clean script-tests batch

* fix(mxc): await sandbox spawn before bridge selection

* fix(test): make fake plutil metacharacter escaping survive the template hop
This commit is contained in:
Peter Steinberger
2026-08-02 14:08:09 -07:00
committed by GitHub
parent 70876c9790
commit 7c70571683
73 changed files with 597 additions and 362 deletions
@@ -44,7 +44,7 @@ const nonEditorialTypes = new Set([
"test",
]);
const nonEditorialTitlePattern =
/(?:^|[\s:([{\-])(docs?|documentation|tests?|testing|qa|quality assurance|refactor(?:ing)?|ci|continuous integration|build|chore|style|lint|format)(?:$|[\s:)\]}\-])/i;
/(?:^|[\s:([{-])(docs?|documentation|tests?|testing|qa|quality assurance|refactor(?:ing)?|ci|continuous integration|build|chore|style|lint|format)(?:$|[\s:)\]}-])/i;
const editorialTitlePattern =
/^\s*(?:\[[^\]]+\]\s*)?(?:#\d+:\s*)?(?:add|allow|block|enable|expose|fail|fix|harden|honor|improve|keep|migrate|move|persist|polish|preserve|prevent|propagate|rate[- ]?limit|restore|revert|ship|support|treat|validate)\b|^\s*#\d+:/i;
const genericDirectCommitTerms = new Set([
@@ -111,7 +111,7 @@ function parseArgs(argv) {
mainRef: undefined,
noGithubSnapshot: false,
refreshGithubSnapshot: false,
seedRef: undefined,
seedRef: /** @type {string | undefined} */ (undefined),
shippedRefs: [],
writeLedger: false,
};
@@ -233,7 +233,7 @@ function gitIsAncestor(base, target) {
if (result.status === 1) {
return false;
}
fail(
return fail(
`could not validate release range ancestry for ${base}..${target}: ${
result.stderr?.trim() || result.signal || result.status
}`,
@@ -252,7 +252,9 @@ function gitCommit(ref, required = false) {
if (!required) {
return undefined;
}
fail(`could not resolve canonical main ref ${ref}: ${result.stderr?.trim() || result.status}`);
return fail(
`could not resolve canonical main ref ${ref}: ${result.stderr?.trim() || result.status}`,
);
}
function fetchGithubApi(args) {
@@ -942,8 +944,8 @@ function authorsMatch(left, right) {
}
function pathsOverlap(left, right) {
for (const path of left) {
if (right.has(path)) {
for (const filePath of left) {
if (right.has(filePath)) {
return true;
}
}
@@ -998,7 +1000,7 @@ function canonicalMainCommits(base, mainRef) {
if (!mainRef) {
return [];
}
const mainCommit = gitCommit(mainRef, true);
const mainCommit = /** @type {string} */ (gitCommit(mainRef, true));
const mainBase = git(["merge-base", base, mainCommit]);
const output = git([
"log",
@@ -1103,8 +1105,8 @@ function sourceCommits(base, target, mainRef) {
fail(`cyclic revert history at ${hash}`);
}
seen.add(hash);
const output = git(["show", "-s", "--format=%s%x1f%B", hash]);
const [subject, ...bodyParts] = output.split("\x1f");
const commitOutput = git(["show", "-s", "--format=%s%x1f%B", hash]);
const [subject, ...bodyParts] = commitOutput.split("\x1f");
const body = bodyParts.join("\x1f");
const message = `${subject}\n${body}`;
const revertedHash = standardRevertedHash(body);
@@ -1345,11 +1347,11 @@ function sourceCommits(base, target, mainRef) {
}
}
const revertedPullRequests = new Set();
for (const pullRequests of resolveAssociatedPullRequests(
for (const revertedPullRequestNumbers of resolveAssociatedPullRequests(
[...revertedCommitHashes],
targetTimestamp,
).values()) {
for (const number of pullRequests) {
for (const number of revertedPullRequestNumbers) {
revertedPullRequests.add(number);
}
}
@@ -2544,7 +2546,6 @@ function main() {
];
const resolvedHandles = resolveGitHubHandles(contributorHandles);
const relationships = contributionRelationships(source, nodes, resolvedHandles);
const unlinkedCommits = source.activeCommits.filter((commit) => commit.references.length === 0);
const resolvedCommitAuthors = resolveDirectCommitAuthors(relationships.directCommits);
relationships.directCommits = withDirectCommitAuthors(
relationships.directCommits,
@@ -61,7 +61,7 @@ const DEPENDENCY_INPUT_RE =
/^(?:\.npmrc$|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|patches\/)|(?:^|\/)package\.json$/u;
class UpdateInvariantError extends Error {
constructor(code, message, details = undefined) {
constructor(code, message, details) {
super(message);
this.name = "UpdateInvariantError";
this.code = code;
@@ -69,6 +69,11 @@ class UpdateInvariantError extends Error {
}
}
/** Re-throw the original runtime value while exposing the Error contract to type-aware lint. */
function throwPreservingValue(value) {
throw /** @type {Error} */ (value);
}
function git(checkout, args, options = {}) {
return execFileSync("git", ["-C", checkout, ...args], {
encoding: options.encoding ?? "utf8",
@@ -149,7 +154,7 @@ export function classifyActions(
) {
// CI skips generated protocol-only macOS jobs, but the live app embeds these Swift sources.
const generatedMacProtocolChanged = changedPaths.some((changedPath) =>
/^apps\/shared\/OpenClawKit\/Sources\/OpenClawProtocol\//u.test(changedPath),
changedPath.startsWith("apps/shared/OpenClawKit/Sources/OpenClawProtocol/"),
);
const runMacos =
changedPaths.length > 0 &&
@@ -246,7 +251,9 @@ function missingControlUiAssets(checkout) {
if (!hasAssetPayload) {
missing.push("assets/*");
}
return [...new Set(missing)].toSorted();
return [...new Set(missing)].toSorted((left, right) =>
left < right ? -1 : left > right ? 1 : 0,
);
}
export function inspectBuildState(checkout, expectedSha) {
@@ -867,7 +874,7 @@ function readManagedGatewayLaunchAgent(checkout) {
if (plistResult.status !== 0) {
throw new UpdateInvariantError(
"gateway_launchagent_failed",
`could not read the managed Gateway LaunchAgent: ${String(plistResult.stderr).trim()}`,
`could not read the managed Gateway LaunchAgent: ${plistResult.stderr.trim()}`,
);
}
const plist = JSON.parse(plistResult.stdout);
@@ -999,7 +1006,7 @@ function prepareLaunchAgentEntrypointReplacement(deployment, entrypoint, options
if (plistResult.status !== 0) {
throw new UpdateInvariantError(
"gateway_repoint_failed",
`could not read the managed Gateway LaunchAgent: ${String(plistResult.stderr).trim()}`,
`could not read the managed Gateway LaunchAgent: ${plistResult.stderr.trim()}`,
);
}
const programArguments = replaceLaunchAgentProgramArgument(
@@ -1131,10 +1138,9 @@ function verifyManagedGatewayRuntime(checkout, expectedSha) {
["print", `gui/${process.getuid()}/${deployment.label}`],
{ encoding: "utf8" },
);
const pidMatch =
launchctl.status === 0 ? String(launchctl.stdout).match(/\bpid = (\d+)\b/u) : null;
const pidMatch = launchctl.status === 0 ? launchctl.stdout.match(/\bpid = (\d+)\b/u) : null;
const pid = Number(pidMatch?.[1] ?? Number.NaN);
const loadedArguments = parseLaunchctlArguments(String(launchctl.stdout));
const loadedArguments = parseLaunchctlArguments(launchctl.stdout);
const loadedCommand = resolveManagedGatewayCommand(
loadedArguments,
process.env.HOME,
@@ -1167,7 +1173,7 @@ function verifyManagedGatewayRuntime(checkout, expectedSha) {
["-nP", `-iTCP:${deployment.port}`, "-sTCP:LISTEN", "-t"],
{ encoding: "utf8" },
);
const listenerPids = String(listeners.stdout).trim().split(/\s+/u).filter(Boolean).map(Number);
const listenerPids = listeners.stdout.trim().split(/\s+/u).filter(Boolean).map(Number);
// The Gateway overwrites process.title, so ps cannot prove argv. The owned
// LaunchAgent arguments plus its exact listener PID remain stable evidence.
if (listeners.status !== 0 || !listenerPids.includes(pid)) {
@@ -1481,7 +1487,7 @@ function proveMacLaunchdGatewayStopped(checkout) {
encoding: "utf8",
});
const listenerClosed =
listeners.status === 1 && !String(listeners.stdout).trim() && !String(listeners.stderr).trim();
listeners.status === 1 && !listeners.stdout.trim() && !listeners.stderr.trim();
const details = { listenerClosed, processExited, serviceBootedOut };
if (!serviceBootedOut) {
throw new UpdateInvariantError(
@@ -1597,62 +1603,68 @@ function runBuildWithPreservedMacApp(runCommand, checkout, sleep = defaultSleep)
`.openclaw-live-mac-${process.pid}-${randomUUID()}.app`,
);
renameSync(appBundle, preservedBundle);
let buildFailed = false;
let buildError;
try {
runCommand("pnpm", ["build"], checkout);
} finally {
// A running app or external file coordinator can temporarily relocate and
// restore the exact bundle while the JS build runs. Allow that move to settle, but
// require the original inode so an unrelated replacement still fails closed.
for (let attempt = 0; attempt < 20; attempt += 1) {
if (existsSync(preservedBundle) || existsSync(appBundle)) {
break;
}
sleep(100);
} catch (error) {
buildFailed = true;
buildError = error;
}
// Restore outside `finally` so restoration failures retain precedence over build failures.
// Accept an external restore only when the original inode returns; replacements still fail closed.
for (let attempt = 0; attempt < 20; attempt += 1) {
if (existsSync(preservedBundle) || existsSync(appBundle)) {
break;
}
const alreadyRestored = isOriginalMacBundle(appBundle, appStat);
if (!alreadyRestored && existsSync(appBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_conflict",
`build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`,
);
}
if (!alreadyRestored) {
mkdirSync(path.dirname(appBundle), { recursive: true });
try {
renameSync(preservedBundle, appBundle);
} catch (error) {
if (!isOriginalMacBundle(appBundle, appStat)) {
if (existsSync(appBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_conflict",
`build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`,
);
}
if (existsSync(preservedBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_failed",
`failed to restore Mac app bundle: ${String(error)}`,
);
}
sleep(100);
}
const alreadyRestored = isOriginalMacBundle(appBundle, appStat);
if (!alreadyRestored && existsSync(appBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_conflict",
`build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`,
);
}
if (!alreadyRestored) {
mkdirSync(path.dirname(appBundle), { recursive: true });
try {
renameSync(preservedBundle, appBundle);
} catch (error) {
if (!isOriginalMacBundle(appBundle, appStat)) {
if (existsSync(appBundle)) {
throw new UpdateInvariantError(
"missing_preserved_mac_bundle",
`preserved Mac app bundle disappeared: ${preservedBundle}`,
"mac_bundle_restore_conflict",
`build unexpectedly created ${appBundle}; preserved bundle remains at ${preservedBundle}`,
);
}
if (existsSync(preservedBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_failed",
`failed to restore Mac app bundle: ${String(error)}`,
);
}
throw new UpdateInvariantError(
"missing_preserved_mac_bundle",
`preserved Mac app bundle disappeared: ${preservedBundle}`,
);
}
}
if (!isOriginalMacBundle(appBundle, appStat)) {
throw new UpdateInvariantError(
"missing_preserved_mac_bundle",
`original Mac app bundle was not restored to ${appBundle}`,
);
}
if (existsSync(preservedBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_conflict",
`original Mac app bundle exists at both ${appBundle} and ${preservedBundle}`,
);
}
}
if (!isOriginalMacBundle(appBundle, appStat)) {
throw new UpdateInvariantError(
"missing_preserved_mac_bundle",
`original Mac app bundle was not restored to ${appBundle}`,
);
}
if (existsSync(preservedBundle)) {
throw new UpdateInvariantError(
"mac_bundle_restore_conflict",
`original Mac app bundle exists at both ${appBundle} and ${preservedBundle}`,
);
}
if (buildFailed) {
throwPreservingValue(buildError);
}
}
@@ -1666,7 +1678,6 @@ function restartGateway(
options = {},
) {
assertExactBuild(checkout, expectedSha);
const now = options.now ?? Date.now;
if (!deployment) {
runCommand("pnpm", ["openclaw", "gateway", "restart"], checkout);
return { processStartedAt: null, restartStartedAtMs: startedAtMs };
@@ -1749,7 +1760,7 @@ function bootstrapManagedGateway(runCommand, checkout, deployment, options = {})
}
environmentRestore.disarm();
if (restartError) {
throw restartError;
throwPreservingValue(restartError);
}
return { processStartedAt };
}
@@ -1811,7 +1822,7 @@ function readLaunchdEnvironmentVariable(name) {
}
// launchd normalizes `setenv NAME ""` to the same absent manager state as
// `unsetenv NAME`; both `getenv` and `print gui/$UID` omit the value.
const value = String(result.stdout).replace(/\r?\n$/u, "");
const value = result.stdout.replace(/\r?\n$/u, "");
return value || null;
}
@@ -1821,7 +1832,7 @@ function waitForManagedGatewayProcess(deployment, sleep = defaultSleep) {
Math.ceil(GATEWAY_PROCESS_START_TIMEOUT_MS / GATEWAY_PROCESS_START_RETRY_DELAY_MS) + 1;
for (let attempt = 0; attempt < attempts; attempt += 1) {
const result = spawnSync("/bin/launchctl", ["print", target], { encoding: "utf8" });
if (result.status === 0 && /\bpid\s*=\s*\d+\b/iu.test(String(result.stdout))) {
if (result.status === 0 && /\bpid\s*=\s*\d+\b/iu.test(result.stdout)) {
return;
}
if (attempt + 1 < attempts) {
@@ -1849,7 +1860,7 @@ function waitForManagedGatewayReadiness(
sleep = defaultSleep,
) {
for (let attempt = 1; attempt <= GATEWAY_READINESS_ATTEMPTS; attempt += 1) {
if (probeMilestones(deployment)?.readyzReady === true) {
if (probeMilestones(deployment)?.readyzReady) {
return;
}
if (attempt < GATEWAY_READINESS_ATTEMPTS) {
@@ -1904,7 +1915,7 @@ function probeGatewayMilestones(deployment) {
["-nP", `-iTCP:${deployment.port}`, "-sTCP:LISTEN", "-t"],
{ encoding: "utf8" },
);
const listenerReady = listeners.status === 0 && Boolean(String(listeners.stdout).trim());
const listenerReady = listeners.status === 0 && Boolean(listeners.stdout.trim());
if (!listenerReady) {
return { listenerReady: false, healthzReady: false, readyzReady: false };
}
@@ -2342,7 +2353,7 @@ function verifyAndAuditGateway({
}
const audit = auditGatewayLogs(checkout, sinceMs, deployment);
if (verificationError) {
throw verificationError;
throwPreservingValue(verificationError);
}
return { audit, timing: gatewayTiming };
}
@@ -2815,7 +2826,7 @@ export function maintainMain(options, dependencies = {}) {
if (actions.macAppRebuild) {
const pendingState = {
...queuedMacState,
attempts: Number(queuedMacState?.attempts ?? 0) + 1,
attempts: (queuedMacState?.attempts ?? 0) + 1,
lastAttemptAt: new Date().toISOString(),
};
writeMaintenanceState(statePath, pendingState);
@@ -55,7 +55,7 @@ function canonicalize(value) {
}
return Object.fromEntries(
Object.entries(value)
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
.toSorted(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0))
.map(([key, child]) => [key, canonicalize(child)]),
);
}
@@ -128,7 +128,7 @@ function trackedPackageManifests(workspace) {
return result.stdout
.split("\0")
.filter((entry) => entry === "package.json" || entry.endsWith("/package.json"))
.sort();
.toSorted();
}
function computeDependencyFingerprint({ workspace, frozenLockfile }) {
@@ -145,7 +145,7 @@ function computeDependencyFingerprint({ workspace, frozenLockfile }) {
try {
manifest = JSON.parse(source);
} catch (error) {
throw new Error(`invalid JSON in ${relativePath}: ${error.message}`);
throw new Error(`invalid JSON in ${relativePath}: ${error.message}`, { cause: error });
}
return { manifest, relativePath };
});
@@ -91,7 +91,7 @@ function parseManifest(manifestPath) {
try {
return JSON.parse(readFileSync(manifestPath, "utf8"));
} catch (error) {
throw new Error(`could not read ${manifestPath}: ${error.message}`);
throw new Error(`could not read ${manifestPath}: ${error.message}`, { cause: error });
}
}
@@ -201,9 +201,13 @@ function verifyImporters(workspace, manifestPath) {
if (!expectedResolution) {
continue;
}
const manifestPath = findInstalledManifest({ dependencyName, projectPath, workspace });
const resolvedManifestPath = findInstalledManifest({
dependencyName,
projectPath,
workspace,
});
const importerDisplay = relativeDisplayPath(workspace, projectPath);
if (!manifestPath) {
if (!resolvedManifestPath) {
if (field.optional) {
continue;
}
@@ -214,7 +218,7 @@ function verifyImporters(workspace, manifestPath) {
}
checked += 1;
const installedLocation = normalizeLocation(
relativeDisplayPath(workspace, path.dirname(manifestPath)),
relativeDisplayPath(workspace, path.dirname(resolvedManifestPath)),
);
const expectedLocation = normalizeLocation(
path.join(
@@ -232,20 +236,22 @@ function verifyImporters(workspace, manifestPath) {
// says this importer owns the lockfile snapshot in its local slot.
if (exactImporterSlot && !installedSnapshotKeys?.has(expectedResolution.snapshotKey)) {
const actualKeys = installedSnapshotKeys
? [...installedSnapshotKeys].toSorted().join(", ")
? [...installedSnapshotKeys]
.toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0))
.join(", ")
: "<missing metadata>";
mismatches.push(
`${importerDisplay}: ${dependencyName} expected pnpm snapshot ${expectedResolution.snapshotKey}, resolved ${actualKeys} from ${installedLocation}`,
);
}
const actual = parseManifest(manifestPath);
const actual = parseManifest(resolvedManifestPath);
if (
actual.name !== expectedResolution.packageName ||
actual.version !== expectedResolution.version
) {
const resolvedFrom = relativeDisplayPath(
workspace,
realpathSync(path.dirname(manifestPath)),
realpathSync(path.dirname(resolvedManifestPath)),
);
mismatches.push(
`${importerDisplay}: ${dependencyName} expected ${expectedResolution.packageName}@${expectedResolution.version}, resolved ${actual.name ?? "<missing>"}@${actual.version ?? "<missing>"} from ${resolvedFrom}`,
@@ -16,17 +16,21 @@ window.renderMath = async (job) => {
trust: false,
});
await document.fonts.ready;
await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => {
setTimeout(resolve, 0);
});
const initialBounds = container.getBoundingClientRect();
const width = Math.ceil(Math.max(initialBounds.width, container.scrollWidth));
document.body.style.width = `${width}px`;
await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => {
setTimeout(resolve, 0);
});
const finalBounds = container.getBoundingClientRect();
const height = Math.ceil(Math.max(finalBounds.height, container.scrollHeight));
window.ChatMathBridge.postMessage(
JSON.stringify({ id: job.id, widthCssPx: width, heightCssPx: height, success: true }),
);
} catch (_) {
} catch {
window.ChatMathBridge.postMessage(
JSON.stringify({ id: job.id, widthCssPx: 0, heightCssPx: 0, success: false }),
);
@@ -303,10 +303,9 @@ function resolveApkSignerFromSdk(sdkRoot: string | undefined): string | null {
const candidates = readdirSync(buildToolsDir)
.toSorted((left, right) => right.localeCompare(left))
.map((version) => join(buildToolsDir, version, "apksigner"))
.filter((candidate) => existsSync(candidate));
.map((version) => join(buildToolsDir, version, "apksigner"));
return candidates[0] ?? null;
return candidates.find((candidate) => existsSync(candidate)) ?? null;
}
function resolveApkSigner(): string {
+47 -37
View File
@@ -169,7 +169,7 @@ function chatMessageWidgets(message) {
suffix += 1;
}
emitted.add(key);
return key === widget.key ? widget : { ...widget, key };
return key === widget.key ? widget : Object.assign({}, widget, { key });
});
}
@@ -573,7 +573,7 @@ function scheduleWidgetSync() {
generation,
}),
)
.catch((error) => {
.catch(/** @param {unknown} error */ (error) => {
sendError = friendlyError(error, "Could not render the widget.");
renderStatus();
});
@@ -903,7 +903,7 @@ async function openNamedPopover(kind) {
setPopoverVisibility(kind);
if (kind === "agents") {
const selectedIndex = agents.findIndex((agent) => agent.id === activeIdentity.id);
menuIndex = selectedIndex >= 0 ? selectedIndex : 0;
menuIndex = Math.max(selectedIndex, 0);
renderAgentList();
elements.agentList.querySelectorAll(".agent-option")[menuIndex]?.focus();
} else {
@@ -967,10 +967,18 @@ function acceleratorFromEvent(event) {
return null;
}
const parts = [];
if (event.ctrlKey) parts.push("Ctrl");
if (event.altKey) parts.push("Alt");
if (event.shiftKey) parts.push("Shift");
if (event.metaKey) parts.push("Super");
if (event.ctrlKey) {
parts.push("Ctrl");
}
if (event.altKey) {
parts.push("Alt");
}
if (event.shiftKey) {
parts.push("Shift");
}
if (event.metaKey) {
parts.push("Super");
}
if (parts.length === 0) {
return null;
}
@@ -1005,34 +1013,36 @@ async function requestHide() {
document.body.classList.remove("shown");
window.clearTimeout(hideTimer);
hideTimer = window.setTimeout(
async () => {
try {
const hidden = await invoke("quickchat_hide", {
sessionId: rendererSessionId,
rendererEpoch,
generation: operationGeneration,
});
if (visibilitySequence !== operationGeneration) {
return;
() => {
void (async () => {
try {
const hidden = await invoke("quickchat_hide", {
sessionId: rendererSessionId,
rendererEpoch,
generation: operationGeneration,
});
if (visibilitySequence !== operationGeneration) {
return;
}
if (hidden !== true) {
document.body.classList.add("shown");
return;
}
resetAccepted();
clearReply();
} catch (error) {
if (visibilitySequence === operationGeneration) {
sendError = friendlyError(error);
renderStatus();
document.body.classList.add("shown");
elements.input.focus();
}
} finally {
if (visibilitySequence === operationGeneration) {
hiding = false;
}
}
if (hidden !== true) {
document.body.classList.add("shown");
return;
}
resetAccepted();
clearReply();
} catch (error) {
if (visibilitySequence === operationGeneration) {
sendError = friendlyError(error);
renderStatus();
document.body.classList.add("shown");
elements.input.focus();
}
} finally {
if (visibilitySequence === operationGeneration) {
hiding = false;
}
}
})();
},
reducedMotion.matches ? 45 : 120,
);
@@ -1045,13 +1055,13 @@ function reveal() {
rendererEpoch,
generation: operationGeneration,
})
.then((accepted) => {
if (accepted !== true && visibilitySequence === operationGeneration) {
.then((activated) => {
if (activated !== true && visibilitySequence === operationGeneration) {
document.body.classList.remove("shown");
return;
}
if (
accepted === true &&
activated === true &&
visibilitySequence === operationGeneration &&
activeReply?.widgets.length
) {
+30 -17
View File
@@ -35,9 +35,9 @@ export function forwardSignals(spawned, options = {}) {
if (exitTimer) {
return;
}
const setTimeoutFn = options.setTimeout ?? setTimeout;
const setTimeoutFn = options.setTimeout?.bind(undefined) ?? setTimeout;
exitTimer = setTimeoutFn(() => {
const exit = options.exit ?? process.exit;
const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code));
exit(signalExitCode(signal));
}, exitGraceMs);
exitTimer?.unref?.();
@@ -86,11 +86,37 @@ function bridgeChildProcess(child) {
export function exitOnChildProcessClose(child, options = {}) {
child.on("close", (exitCode, signal) => {
const exit = options.exit ?? process.exit;
const exit = options.exit?.bind(undefined) ?? ((code) => process.exit(code));
exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
});
}
function attachPtyProcess(spawned) {
bridgeStdio(spawned);
forwardSignals(spawned);
spawned.onExit(({ exitCode, signal }) => {
process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
});
}
function attachChildProcess(spawned) {
bridgeChildProcess(spawned);
forwardSignals(spawned);
exitOnChildProcessClose(spawned);
}
export async function launchSandbox(spawnSandboxFromConfig, config, options, bridges = {}) {
// Normalize sync and Promise-returning SDK implementations before selecting an I/O bridge.
const spawned = await spawnSandboxFromConfig(config, options ?? {});
if (typeof spawned.onData === "function") {
(bridges.pty ?? attachPtyProcess)(spawned);
return;
}
(bridges.child ?? attachChildProcess)(spawned);
}
const SIGNAL_NUMBERS = new Map([
["SIGHUP", 1],
["SIGINT", 2],
@@ -123,20 +149,7 @@ export async function main() {
try {
const { config, options } = decodePayload(process.argv.slice(2));
const { spawnSandboxFromConfig } = await import("@microsoft/mxc-sdk");
const spawned = await spawnSandboxFromConfig(config, options ?? {});
if (typeof spawned.onData === "function") {
bridgeStdio(spawned);
forwardSignals(spawned);
spawned.onExit(({ exitCode, signal }) => {
process.exit(typeof exitCode === "number" ? exitCode : signalExitCode(signal));
});
return;
}
bridgeChildProcess(spawned);
forwardSignals(spawned);
exitOnChildProcessClose(spawned);
await launchSandbox(spawnSandboxFromConfig, config, options);
} catch (error) {
process.stderr.write(`${formatErrorStack(error)}\n`);
process.exit(127);
@@ -28,6 +28,15 @@ const loadLauncher = () =>
setTimeout?: (callback: () => void, ms: number) => { unref?: () => void };
},
) => void;
launchSandbox: (
spawnSandboxFromConfig: (config: unknown, options: unknown) => unknown,
config: unknown,
options: unknown,
bridges?: {
child?: (spawned: unknown) => void;
pty?: (spawned: unknown) => void;
},
) => Promise<void>;
signalExitCode: (signal: number | string | undefined) => number;
};
@@ -64,6 +73,45 @@ describe("mxc-spawn-launcher", () => {
expect(signalExitCode(undefined)).toBe(1);
});
it("resolves a promised PTY before selecting its bridge", async () => {
const { launchSandbox } = loadLauncher();
const pty = { onData: vi.fn() };
const spawnResult = Promise.resolve(pty);
const ptyBridge = vi.fn();
const childBridge = vi.fn();
await launchSandbox(() => spawnResult, { process: {} }, undefined, {
pty: ptyBridge,
child: childBridge,
});
expect(ptyBridge).toHaveBeenCalledWith(pty);
expect(ptyBridge).not.toHaveBeenCalledWith(spawnResult);
expect(childBridge).not.toHaveBeenCalled();
});
it("resolves a promised child process before selecting its bridge", async () => {
const { launchSandbox } = loadLauncher();
const child = { on: vi.fn(), stdout: undefined, stderr: undefined };
const spawnResult = Promise.resolve(child);
const ptyBridge = vi.fn();
const childBridge = vi.fn();
await launchSandbox(
() => spawnResult,
{ process: {} },
{ usePty: false },
{
pty: ptyBridge,
child: childBridge,
},
);
expect(childBridge).toHaveBeenCalledWith(child);
expect(childBridge).not.toHaveBeenCalledWith(spawnResult);
expect(ptyBridge).not.toHaveBeenCalled();
});
it("forwards process termination signals to spawned sandbox children", () => {
const { forwardSignals } = loadLauncher();
const listeners = new Map<string, () => void>();
@@ -18,7 +18,7 @@ function readStdin() {
let input = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => {
input += chunk;
input += String(chunk);
});
process.stdin.on("error", reject);
process.stdin.on("end", () => resolve(input));
+2 -3
View File
@@ -51,7 +51,7 @@ const ensureSupportedRuntimeVersion = () => {
if (process.versions.bun) {
// Bun >=1.4 (Rust rewrite) ships node:sqlite; feature-probe instead of
// rejecting Bun outright so capable Bun builds can run OpenClaw.
let hasNodeSqlite = false;
let hasNodeSqlite;
try {
hasNodeSqlite = Boolean(process.getBuiltinModule?.("node:sqlite"));
} catch {
@@ -463,8 +463,7 @@ const hasLauncherContainerTarget = (argv) => {
return true;
}
const args = argv.slice(2);
for (let index = 0; index < args.length; index += 1) {
const arg = args[index];
for (const arg of args) {
if (!arg || arg === "--") {
return false;
}
@@ -93,7 +93,7 @@ function requireBuzzPrivateKey(
try {
return { value, publicKey: getPublicKey(decodeBuzzPrivateKey(value)) };
} catch {
throwPayloadError(
return throwPayloadError(
createFailure,
`Credential payload for kind "buzz" must include "${key}" as an nsec or 64-character hex private key.`,
);
+23 -11
View File
@@ -245,16 +245,20 @@ async function waitForGatewayReadiness(
async function startFakeClawRouter(): Promise<FakeClawRouter> {
const requests: CapturedRequest[] = [];
const server = createServer((req, res) => {
void handleClawRouterRequest(req, res, requests).catch((error) => {
void handleClawRouterRequest(req, res, requests).catch((error: unknown) => {
res.writeHead(500, { "content-type": "application/json" });
res.end(JSON.stringify({ error: { message: String(error) } }));
});
});
await new Promise<void>((resolve) => server.listen(0, "127.0.0.1", resolve));
await new Promise<void>((resolve) => {
server.listen(0, "127.0.0.1", resolve);
});
const address = server.address();
if (!address || typeof address === "string") {
server.closeAllConnections();
await new Promise<void>((resolve) => server.close(() => resolve()));
await new Promise<void>((resolve) => {
server.close(() => resolve());
});
throw new Error("fake ClawRouter did not bind a TCP port");
}
return {
@@ -262,7 +266,9 @@ async function startFakeClawRouter(): Promise<FakeClawRouter> {
requests,
close: async () => {
server.closeAllConnections();
await new Promise<void>((resolve) => server.close(() => resolve()));
await new Promise<void>((resolve) => {
server.close(() => resolve());
});
},
};
}
@@ -273,13 +279,19 @@ async function handleClawRouterRequest(
requests: CapturedRequest[],
): Promise<void> {
const method = req.method ?? "GET";
const path = new URL(req.url ?? "/", "http://127.0.0.1").pathname;
const requestPath = new URL(req.url ?? "/", "http://127.0.0.1").pathname;
const bodyText = await readRequestBody(req);
const body = bodyText ? (JSON.parse(bodyText) as Record<string, unknown>) : undefined;
const authorization = req.headers.authorization;
requests.push({ method, path, authorization, headers: { ...req.headers }, body });
requests.push({
method,
path: requestPath,
authorization,
headers: { ...req.headers },
body,
});
if (method === "GET" && path === "/v1/health") {
if (method === "GET" && requestPath === "/v1/health") {
writeJson(res, 200, {
ok: true,
environment: "fakeco",
@@ -296,7 +308,7 @@ async function handleClawRouterRequest(
return;
}
if (method === "GET" && path === "/v1/catalog") {
if (method === "GET" && requestPath === "/v1/catalog") {
writeJson(res, 200, {
providers: [
{
@@ -318,7 +330,7 @@ async function handleClawRouterRequest(
return;
}
if (method === "GET" && path === "/v1/usage") {
if (method === "GET" && requestPath === "/v1/usage") {
writeJson(res, 200, {
budget: { configured: false, ledger: "unmetered" },
usage: { summary: { requestCount: 0, totalTokens: 0, actualCostMicros: 0 } },
@@ -326,12 +338,12 @@ async function handleClawRouterRequest(
return;
}
if (method === "POST" && path === "/v1/responses") {
if (method === "POST" && requestPath === "/v1/responses") {
writeResponsesStream(res, resolveResponseText(body));
return;
}
writeJson(res, 404, { error: { message: `unexpected ${method} ${path}` } });
writeJson(res, 404, { error: { message: `unexpected ${method} ${requestPath}` } });
}
function resolveResponseText(body: Record<string, unknown> | undefined): string {
+8 -2
View File
@@ -166,7 +166,10 @@ async function runRealPicker(options: ProducerOptions, openclawHome: string) {
await sendAndWait("\r", /Configure DM access policies now\?/u);
await sendAndWait("\r", /Configuration updated\./u);
while (!exit) {
for (;;) {
if (exit) {
break;
}
if (remainingMs() === 0) {
throw new Error(`picker timed out after ${options.timeoutMs}ms`);
}
@@ -182,7 +185,10 @@ async function runRealPicker(options: ProducerOptions, openclawHome: string) {
if (!exit) {
child.kill("SIGTERM");
const cleanupDeadline = Date.now() + 5_000;
while (!exit && Date.now() < cleanupDeadline) {
while (Date.now() < cleanupDeadline) {
if (exit) {
break;
}
await delay(25);
}
}
@@ -406,7 +406,7 @@ async function selectProviders(params: {
const candidates = explicit
? params.suite.providers
: (params.suite.defaultProviders ?? params.suite.providers);
let providers = candidates.filter((provider) => (explicit ? explicit.has(provider) : true));
const providers = candidates.filter((provider) => (explicit ? explicit.has(provider) : true));
if (!params.requireAuth) {
return providers;
}
@@ -60,7 +60,7 @@ class FakeCommandChild extends EventEmitter {
}
}
afterEach(tempDirs.cleanup);
afterEach(() => tempDirs.cleanup());
describe("plugin lifecycle matrix probe", () => {
it("accepts inspect JSON for an enabled loaded plugin", async () => {
@@ -21,7 +21,7 @@ describe("QA Docker E2E lane fixture", () => {
"update-restart-auth",
]),
);
expect(listQaDockerE2eLaneNames()).toEqual([...listQaDockerE2eLaneNames()].sort());
expect(listQaDockerE2eLaneNames()).toEqual([...listQaDockerE2eLaneNames()].toSorted());
});
it("parses help, list, and lane arguments", () => {
@@ -2,7 +2,7 @@
import { spawnSync } from "node:child_process";
import { createServer, type Server } from "node:http";
import { afterEach, describe, expect, it } from "vitest";
import { WebSocket, WebSocketServer } from "ws";
import { WebSocket, WebSocketServer, type RawData } from "ws";
import { runGatewaySmoke } from "../../../../scripts/dev/gateway-smoke.js";
let server: Server | undefined;
@@ -73,8 +73,13 @@ describe("gateway-smoke", () => {
server = createServer();
wss = new WebSocketServer({ server });
wss.on("connection", (ws: WebSocket) => {
ws.on("message", (data) => {
const frame = JSON.parse(data.toString()) as {
ws.on("message", (data: RawData) => {
const text = Array.isArray(data)
? Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8")
: Buffer.isBuffer(data)
? data.toString("utf8")
: Buffer.from(data).toString("utf8");
const frame = JSON.parse(text) as {
id: string;
method: string;
params?: unknown;
+14 -6
View File
@@ -280,7 +280,9 @@ async function waitForChatFinal(
if (finalEvent) {
return finalEvent.payload;
}
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
throw new Error(`timed out waiting for WebChat final event for run ${runId}`);
}
@@ -301,7 +303,9 @@ async function waitForWebchatAudio(params: {
if (attachment) {
return { attachment, history };
}
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
return { attachment: undefined, history };
}
@@ -434,7 +438,9 @@ async function waitForActiveTalkStatus(client: GatewayClient, sessionKey: string
return status;
} catch (error) {
lastError = error;
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
}
throw lastError instanceof Error ? lastError : new Error("timed out waiting for active Talk run");
@@ -462,7 +468,9 @@ async function waitForQueuedTalkSteer(client: GatewayClient, sessionKey: string)
} catch (error) {
lastError = error;
}
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
if (lastError instanceof Error) {
throw lastError;
@@ -513,7 +521,7 @@ async function runActiveTalkAgentRunProof(options: ProducerOptions): Promise<str
url: gateway.wsUrl,
});
const sessionKey = "agent:qa:main";
const created = await client.request<Record<string, unknown>>("talk.client.create", {
const created = await client.request("talk.client.create", {
sessionKey,
provider: FIXTURE_REALTIME_PROVIDER_ID,
});
@@ -643,7 +651,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
.then((exitCode) => {
process.exitCode = exitCode;
})
.catch((error) => {
.catch((error: unknown) => {
console.error(formatErrorMessage(error));
process.exitCode = 1;
});
@@ -221,7 +221,7 @@ describe("scripts/e2e/openwebui-probe.mjs", () => {
});
it("redacts admin credentials from sign-in error bodies", async () => {
const adminEmail = "openwebui-e2e" + "@example.com";
const adminEmail = "openwebui-e2e@example.com";
const server = createServer((request, response) => {
if (request.url === "/api/v1/auths/signin") {
response.writeHead(401, { "content-type": "application/json" });
@@ -400,33 +400,35 @@ describe("scripts/e2e/openwebui-probe.mjs", () => {
it("runs chat mode through Open WebUI chat completions and validates the nonce", async () => {
const chatRequests: unknown[] = [];
const server = createServer(async (request, response) => {
if (request.url === "/api/v1/auths/signin") {
response.writeHead(200, {
"content-type": "application/json",
"set-cookie": "openwebui-session=test; Path=/",
});
response.end(JSON.stringify({ token: "test-token" }));
return;
}
if (request.url === "/api/models") {
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({ data: [{ id: "openclaw/default" }] }));
return;
}
if (request.url === "/api/chat/completions") {
expect(request.headers.authorization).toBe("Bearer test-token");
expect(request.headers.cookie).toContain("openwebui-session=test");
chatRequests.push(JSON.parse(await readRequestBody(request)));
response.writeHead(200, { "content-type": "application/json" });
response.end(
JSON.stringify({
choices: [{ message: { content: "OpenClaw replied with nonce-123" } }],
}),
);
return;
}
response.writeHead(404).end();
const server = createServer((request, response) => {
void (async () => {
if (request.url === "/api/v1/auths/signin") {
response.writeHead(200, {
"content-type": "application/json",
"set-cookie": "openwebui-session=test; Path=/",
});
response.end(JSON.stringify({ token: "test-token" }));
return;
}
if (request.url === "/api/models") {
response.writeHead(200, { "content-type": "application/json" });
response.end(JSON.stringify({ data: [{ id: "openclaw/default" }] }));
return;
}
if (request.url === "/api/chat/completions") {
expect(request.headers.authorization).toBe("Bearer test-token");
expect(request.headers.cookie).toContain("openwebui-session=test");
chatRequests.push(JSON.parse(await readRequestBody(request)));
response.writeHead(200, { "content-type": "application/json" });
response.end(
JSON.stringify({
choices: [{ message: { content: "OpenClaw replied with nonce-123" } }],
}),
);
return;
}
response.writeHead(404).end();
})();
});
const baseUrl = await listen(server);
try {
@@ -569,7 +569,8 @@ describe("qa-otel-smoke receiver bounds", () => {
stopDockerContainer,
waitForLocalPort: async () => {},
writeFile: async (_path, config) => {
writtenConfig = String(config);
writtenConfig =
typeof config === "string" ? config : Buffer.from(config as Uint8Array).toString("utf8");
},
});
@@ -141,7 +141,9 @@ async function waitForFinalToolResult(filePath: string) {
if (final) {
return { entries, final };
}
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise((resolve) => {
setTimeout(resolve, 100);
});
}
throw new Error("timed out waiting for final Voice Call consult tool result");
}
@@ -359,7 +361,7 @@ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
.then((exitCode) => {
process.exitCode = exitCode;
})
.catch((error) => {
.catch((error: unknown) => {
console.error(formatErrorMessage(error));
process.exitCode = 1;
});
+1 -1
View File
@@ -230,7 +230,7 @@ async function startMockModelServer(): Promise<MockModelServer> {
await drainRequest(request);
responseCount += 1;
writeModelResponse(response, responseCount);
})().catch((error) => {
})().catch((error: unknown) => {
response.writeHead(500, { "content-type": "application/json" });
response.end(JSON.stringify({ error: { message: String(error) } }));
});
-8
View File
@@ -85,11 +85,6 @@ declare module "*openclaw-changelog-update/scripts/verify-release-notes.mjs" {
legacyIssues: Map<number, unknown>;
pullRequests: Map<number, ContributionRecord>;
};
export function contributionRecordTarget(section: { source: string }): string | undefined;
export function pullRequestTitleFromCommitSubject(
subject: string,
number: number,
): string | undefined;
export function recoverUnavailablePullRequests(params: {
numbers: Iterable<number>;
nodes: Map<number, unknown>;
@@ -161,9 +156,6 @@ declare module "*openclaw-changelog-update/scripts/verify-release-notes.mjs" {
associatedPullRequests: number[],
hasProvenanceOverride: boolean,
): number[];
export function recoverUnavailablePullRequests(
params: Record<string, unknown>,
): Map<number, Record<string, unknown>>;
export function validateReleaseProvenanceOverrides(
provenanceOverrides: Map<string, number[]>,
nodes: Map<number, unknown>,
@@ -295,7 +295,7 @@ describe("Gateway queued session rotation", () => {
expect(JSON.stringify(modelServer.requests[1]?.body)).toContain("OPENCLAW_E2E_AFTER_RESET");
} finally {
await client.abortChat({ sessionKey }).catch(() => undefined);
client.stop();
void client.stop();
modelServer.releaseHeldResponse();
}
},
+3 -1
View File
@@ -11,7 +11,9 @@ export function isProcessAlive(pid: number): boolean {
}
async function sleep(ms: number): Promise<void> {
await new Promise((resolve) => setTimeout(resolve, ms));
await new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
export async function waitForFile(filePath: string, timeoutMs: number): Promise<void> {
@@ -165,13 +165,18 @@ async function startFakeOpenAiServer(params: { modelId: string }): Promise<FakeO
});
const address = server.address();
if (!address || typeof address === "string") {
await new Promise<void>((resolve) => server.close(() => resolve()));
await new Promise<void>((resolve) => {
server.close(() => resolve());
});
throw new Error("fake OpenAI server did not bind a TCP port");
}
return {
baseUrl: `http://127.0.0.1:${(address as AddressInfo).port}/v1`,
requests,
close: () => new Promise<void>((resolve) => server.close(() => resolve())),
close: () =>
new Promise<void>((resolve) => {
server.close(() => resolve());
}),
};
}
+1 -1
View File
@@ -900,7 +900,7 @@ test("adding a widget preserves the existing native webview identity", async ()
message: { role: "assistant", content: [widgetBlock("first"), widgetBlock("second")] },
});
await harness.flushWidgets();
const layouts = harness.syncedWidgets().map((layout: object) => ({ ...layout }));
const layouts = harness.syncedWidgets().map((layout: object) => Object.assign({}, layout));
assert.deepEqual(layouts[0], firstLayout);
assert.equal(layouts[0].visible, true);
+3 -1
View File
@@ -58,7 +58,9 @@ async function waitForChatFinal(
return payload;
}
}
await new Promise((resolve) => setTimeout(resolve, 10));
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
}
throw new Error(
`timed out waiting for chat final runId=${runId}; events=${JSON.stringify(
+1 -1
View File
@@ -35,7 +35,7 @@ async function runOpenClaw(args: string[], env: NodeJS.ProcessEnv): Promise<stri
return result.stdout;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(message.replaceAll(openAiApiKey, "[REDACTED]"));
throw new Error(message.replaceAll(openAiApiKey, "[REDACTED]"), { cause: error });
}
}
+3 -1
View File
@@ -475,7 +475,9 @@ describe("collectPluginClawHubReleasePlan", () => {
activeRequests += 1;
maxActiveRequests = Math.max(maxActiveRequests, activeRequests);
try {
await new Promise((resolve) => setTimeout(resolve, 5));
await new Promise((resolve) => {
setTimeout(resolve, 5);
});
return await baseFetch(...args);
} finally {
activeRequests -= 1;
+2 -2
View File
@@ -321,7 +321,7 @@ describe("plugin cron registry ownership e2e", () => {
providers: {
"cron-owner": {
baseUrl: `${modelServer.baseUrl}/v1`,
["api" + "Key"]: TEST_API_KEY,
apiKey: TEST_API_KEY,
api: "openai-responses",
request: { allowPrivateNetwork: true },
models: [
@@ -360,7 +360,7 @@ describe("plugin cron registry ownership e2e", () => {
const client = await connectGatewayClient({
url: instance.url,
["to" + "ken"]: instance.gatewayToken,
token: instance.gatewayToken,
role: "operator",
scopes: ["operator.admin", "operator.read", "operator.write"],
requestTimeoutMs: 30_000,
@@ -190,7 +190,7 @@ beforeAll(() => {
status: "fulfilled",
value,
}),
(reason): PromiseRejectedResult => ({ status: "rejected", reason }),
(reason: unknown): PromiseRejectedResult => ({ status: "rejected", reason }),
),
);
}
+3 -3
View File
@@ -55,11 +55,11 @@ describe("Android app i18n resources", () => {
);
const base = await readFile("apps/android/wear/src/main/res/values/strings.xml", "utf8");
const baseKeys = [...base.matchAll(/<string name="([^"]+)"/gu)]
.map((match) => match[1])
.toSorted();
.map((match) => match[1] as string)
.toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0));
const basePlaceholders = [...base.matchAll(/%\d+\$[a-z]/giu)]
.map((match) => match[0])
.toSorted();
.toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0));
expect(wearResources).toHaveLength(NATIVE_I18N_LOCALES.length);
for (const [, content] of wearResources) {
@@ -20,8 +20,12 @@ function runScript(
return { ok: true, stdout, stderr: "" };
} catch (error) {
const e = error as { stdout?: unknown; stderr?: unknown };
const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? "");
const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? "");
const stdout = Buffer.isBuffer(e.stdout)
? e.stdout.toString("utf8")
: ((e.stdout ?? "") as string);
const stderr = Buffer.isBuffer(e.stderr)
? e.stderr.toString("utf8")
: ((e.stderr ?? "") as string);
return { ok: false, stdout, stderr };
}
}
@@ -126,7 +126,9 @@ describe("scripts/bench-sqlite-reliability", () => {
},
operation: async () => {
fs.writeFileSync(walPath, Buffer.alloc(2048));
await new Promise((resolve) => setTimeout(resolve, 25));
await new Promise((resolve) => {
setTimeout(resolve, 25);
});
fs.truncateSync(walPath, 0);
return "complete";
},
@@ -25,7 +25,7 @@ function run(cwd: string, command: string, args: string[], env?: NodeJS.ProcessE
function commandOutput(error: unknown): string {
const result = error as { stderr?: unknown; stdout?: unknown };
return `${String(result.stdout ?? "")}${String(result.stderr ?? "")}`;
return `${(result.stdout ?? "") as string}${(result.stderr ?? "") as string}`;
}
function createRepoWithPrChangelogDiff(entry: string): string {
@@ -88,13 +88,10 @@ describe("extractSwiftHandledEvents", () => {
if evt.event == "connect.challenge" { return }
`;
const handled = extractSwiftHandledEvents(source, constants);
expect([...handled].toSorted()).toEqual([
"chat",
"connect.challenge",
"exec.approval.requested",
"session.message",
"tick",
]);
const handledEvents = [...handled] as string[];
expect(
handledEvents.toSorted((left, right) => (left < right ? -1 : left > right ? 1 : 0)),
).toEqual(["chat", "connect.challenge", "exec.approval.requested", "session.message", "tick"]);
});
it("extracts only type-scoped static string constants", () => {
+3 -1
View File
@@ -203,7 +203,9 @@ describe("scripts/ci-run-node-test-shard.mjs", () => {
}
activeCaches.add(cache);
seenCaches.add(cache);
await new Promise((resolve) => setTimeout(resolve, 10));
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
activeCaches.delete(cache);
return 0;
},
+8 -4
View File
@@ -449,8 +449,8 @@ function readCriticalQualityWorkflow() {
return readFileSync(".github/workflows/codeql-critical-quality.yml", "utf8");
}
function readWorkflow(path: string) {
return parse(readFileSync(path, "utf8"));
function readWorkflow(filePath: string) {
return parse(readFileSync(filePath, "utf8"));
}
const PULL_REQUEST_EDIT_FIELDS = ["title", "body", "base"] as const;
@@ -3176,7 +3176,11 @@ describe("ci workflow guards", () => {
),
).toBe(true);
expect(
new Set(retiredDisks.map((disk) => `${disk.key}:${disk.architecture}:${disk.region}`)).size,
new Set(
retiredDisks.map(
(disk) => `${disk.key as string}:${disk.architecture as string}:${disk.region as string}`,
),
).size,
).toBe(retiredDisks.length);
expect(cleanup.on).toHaveProperty("workflow_dispatch");
expect(cleanup.permissions).toEqual({ contents: "read" });
@@ -3263,7 +3267,7 @@ describe("ci workflow guards", () => {
for (const retiredDisk of retiredDisks) {
expect(
activeKeyPatterns.some((pattern) => pattern.test(retiredDisk.key as string)),
`${retiredDisk.key} is still an active sticky-disk key`,
`${retiredDisk.key as string} is still an active sticky-disk key`,
).toBe(false);
}
});
+3 -1
View File
@@ -194,7 +194,9 @@ describe("codesign-mac-app temp file hygiene", () => {
expect(signLines[1]).toContain(`${path.join(app, "Contents", "MacOS", "OpenClaw")}\t`);
expect(signLines[2]).toContain(`${app}\t`);
for (const line of signLines) {
const [, , entitlementPath, copiedEntitlementsPath] = line.split("\t");
const columns = line.split("\t");
const entitlementPath = columns[2];
const copiedEntitlementsPath = columns[3];
const entitlementSource = expectDefined(entitlementPath, "codesign entitlement source path");
const copiedEntitlementSource = expectDefined(
copiedEntitlementsPath,
@@ -126,7 +126,10 @@ describe("dependency-vulnerability-gate", () => {
const report = await runDependencyVulnerabilityGate({
rootDir,
fetchImpl: async (_url, init) => {
const payload = JSON.parse(String(init?.body));
if (typeof init?.body !== "string") {
throw new Error("expected a JSON request body");
}
const payload = JSON.parse(init.body);
payloads.push(payload);
const packages = Object.keys(payload);
const body: Record<string, unknown[]> = {};
@@ -50,7 +50,7 @@ describe("Docker E2E observability", () => {
const script = readFileSync("scripts/e2e/cron-cli-docker.sh", "utf8");
expect(script).toMatch(
/docker_e2e_run_with_harness[\s\S]*\n -i \\\n "\$IMAGE_NAME" \\\n bash -s >"\$CLIENT_LOG" 2>&1 <<'INNER'/u,
/docker_e2e_run_with_harness[\s\S]*\n {2}-i \\\n {2}"\$IMAGE_NAME" \\\n {2}bash -s >"\$CLIENT_LOG" 2>&1 <<'INNER'/u,
);
});
+4 -2
View File
@@ -291,7 +291,7 @@ describe("e2e helper numeric env limits", () => {
headersSentResolve?.();
});
const baseUrl = await listen(server);
const realTimeout = AbortSignal.timeout;
const realTimeout = AbortSignal.timeout.bind(AbortSignal);
const timeoutSpy = vi.spyOn(AbortSignal, "timeout").mockImplementation(() => realTimeout(200));
try {
const result = runScript(clickclackPluginWritePath, [tempDir]);
@@ -328,7 +328,9 @@ describe("e2e helper numeric env limits", () => {
} finally {
timeoutSpy.mockRestore();
server.closeAllConnections();
await new Promise<void>((resolve) => server.close(() => resolve()));
await new Promise<void>((resolve) => {
server.close(() => resolve());
});
fs.rmSync(tempDir, { force: true, recursive: true });
}
});
@@ -316,24 +316,27 @@ function normalizedEvidence(options: {
["productPerformance", "204", 3, 2, "OpenClaw Performance", "openclaw-performance.yml", ""],
] as const;
const children = roles.map(
([role, childRunId, runAttempt, sourceParentAttempt, name, workflow, suffix]) => ({
conclusion: "success",
dispatchNonce: `full-release-validation-${runId}-${sourceParentAttempt}${suffix}`,
displayTitle: `${name} full-release-validation-${runId}-${sourceParentAttempt}${suffix}`,
event: "workflow_dispatch",
headBranch: workflowRef,
parentJobId: `job-${role}`,
path: `.github/workflows/${workflow}`,
role,
runAttempt,
runId: childRunId,
sourceParentAttempt,
sourceParentRunId: runId,
status: "completed",
url: `https://example.test/runs/${childRunId}`,
workflowSha: producerSha,
...(role === "productPerformance" ? { reportPublication: "artifact-only" } : {}),
}),
([role, childRunId, runAttempt, sourceParentAttempt, name, workflow, suffix]) =>
Object.assign(
{
conclusion: "success",
dispatchNonce: `full-release-validation-${runId}-${sourceParentAttempt}${suffix}`,
displayTitle: `${name} full-release-validation-${runId}-${sourceParentAttempt}${suffix}`,
event: "workflow_dispatch",
headBranch: workflowRef,
parentJobId: `job-${role}`,
path: `.github/workflows/${workflow}`,
role,
runAttempt,
runId: childRunId,
sourceParentAttempt,
sourceParentRunId: runId,
status: "completed",
url: `https://example.test/runs/${childRunId}`,
workflowSha: producerSha,
},
role === "productPerformance" ? { reportPublication: "artifact-only" } : {},
),
);
return {
children,
@@ -485,11 +488,15 @@ function runResolver(args: {
`repos/${REPOSITORY}/compare/${args.compareBaseSha}...${args.targetSha}`,
),
JSON.stringify({
files: (args.compareFiles ?? ["CHANGELOG.md"]).map((filename, index) => ({
filename,
status: args.compareRenamed && index === 0 ? "renamed" : "modified",
...(args.compareRenamed && index === 0 ? { previous_filename: "src/index.ts" } : {}),
})),
files: (args.compareFiles ?? ["CHANGELOG.md"]).map((filename, index) =>
Object.assign(
{
filename,
status: args.compareRenamed && index === 0 ? "renamed" : "modified",
},
args.compareRenamed && index === 0 ? { previous_filename: "src/index.ts" } : {},
),
),
merge_base_commit: { sha: args.compareBaseSha },
status: args.compareStatus ?? "ahead",
}),
@@ -819,10 +826,10 @@ describe("scripts/github/find-reusable-release-validation.sh", () => {
record.current.artifact.digest = "sha256:not-a-digest";
},
},
])("rejects normalized evidence that is not reusable: $label", ({ mutate }) => {
])("rejects normalized evidence that is not reusable: $label", (testCase) => {
const { clone, priorSha } = getSharedRepo();
const record = normalizedEvidence({ targetSha: priorSha });
mutate(record);
testCase.mutate(record);
const { binDir, fixtures, validatorPath } = setUpFixtures([{ record, runId: "111" }]);
const result = runResolver({
+3 -3
View File
@@ -129,7 +129,7 @@ describe("format-docs", () => {
writeDocsFixture(root);
const oxfmtFileArgs: string[][] = [];
const spawnSync = (command: string, args: string[]) => {
const runCommandSync = (command: string, args: string[]) => {
if (command === "git") {
return {
status: 0,
@@ -150,7 +150,7 @@ describe("format-docs", () => {
},
{
existsSync: fs.existsSync,
spawnSync,
spawnSync: runCommandSync,
},
),
).toEqual({ changed: [], fileCount: 2 });
@@ -164,7 +164,7 @@ describe("format-docs", () => {
},
{
existsSync: fs.existsSync,
spawnSync,
spawnSync: runCommandSync,
},
),
).toEqual({ changed: [], fileCount: 2 });
+9 -2
View File
@@ -24,7 +24,7 @@ describe("gateway network client", () => {
expect(signal).toBeInstanceOf(AbortSignal);
const requestSignal = signal as AbortSignal;
return new Promise((_, reject) => {
const rejectWithReason = () => reject(requestSignal.reason);
const rejectWithReason = () => reject(requestSignal.reason as Error);
if (requestSignal.aborted) {
rejectWithReason();
return;
@@ -207,7 +207,14 @@ describe("gateway network client", () => {
expect(Date.now() - startedAt).toBeLessThan(500);
expect(bodySignal?.aborted).toBe(true);
expect(fetchImpl).toHaveBeenCalledTimes(2);
expect(String(fetchImpl.mock.calls[1]?.[0])).toContain("/healthz");
const request = fetchImpl.mock.calls[1]?.[0];
const requestUrl =
request instanceof Request
? request.url
: request instanceof URL
? request.href
: (request ?? "");
expect(requestUrl).toContain("/healthz");
});
it("bounds a stalled post-restart admin request by the client deadline", async () => {
+10 -5
View File
@@ -2,7 +2,7 @@
import { spawn } from "node:child_process";
import { createServer, type Server } from "node:http";
import { afterEach, describe, expect, it } from "vitest";
import { WebSocket, WebSocketServer } from "ws";
import { WebSocket, WebSocketServer, type RawData } from "ws";
import { createBoundedChildOutput } from "../helpers/bounded-child-output.js";
type ScriptResult = {
@@ -92,8 +92,13 @@ async function listenGateway(params: {
server = createServer();
wss = new WebSocketServer({ server });
wss.on("connection", (ws: WebSocket) => {
ws.on("message", (data) => {
const frame = JSON.parse(String(data)) as GatewayFrame;
ws.on("message", (data: RawData) => {
const text = Array.isArray(data)
? Buffer.concat(data.map((chunk) => Buffer.from(chunk))).toString("utf8")
: Buffer.isBuffer(data)
? data.toString("utf8")
: Buffer.from(data).toString("utf8");
const frame = JSON.parse(text) as GatewayFrame;
if (frame.type !== "req") {
return;
}
@@ -138,7 +143,7 @@ async function listenGateway(params: {
ok: true,
nodeId: "ios-node",
command: frame.params?.command,
payload: invokePayload(String(frame.params?.command ?? ""), params.mode),
payload: invokePayload(frame.params?.command ?? "", params.mode),
},
}),
);
@@ -290,7 +295,7 @@ describe("ios-node-e2e", () => {
payload: {},
},
});
expect(report.results.every((entry) => entry.ok === false)).toBe(true);
expect(report.results.every((entry) => !entry.ok)).toBe(true);
expect(invokeParams.length).toBeGreaterThan(0);
});
@@ -53,7 +53,7 @@ function swiftFunctionBody(source: string, name: string): string {
}
const rest = source.slice(start + startMarker.length);
const nextFunction = rest.search(/\n (?:private )?func /);
const nextFunction = rest.search(/\n {4}(?:private )?func /);
return nextFunction < 0 ? rest : rest.slice(0, nextFunction);
}
@@ -27,8 +27,12 @@ function runScript(
return { ok: true, stdout, stderr: "" };
} catch (error) {
const e = error as { stdout?: unknown; stderr?: unknown };
const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? "");
const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? "");
const stdout = Buffer.isBuffer(e.stdout)
? e.stdout.toString("utf8")
: ((e.stdout ?? "") as string);
const stderr = Buffer.isBuffer(e.stderr)
? e.stderr.toString("utf8")
: ((e.stderr ?? "") as string);
return { ok: false, stdout, stderr };
}
}
@@ -1,5 +1,5 @@
// iOS IPA validation tests cover the App Store upload gate without real signing assets.
import { execFileSync } from "node:child_process";
import { execFileSync, spawnSync } from "node:child_process";
import {
chmodSync,
mkdirSync,
@@ -138,8 +138,11 @@ if (extractIndex < 0 || expectIndex < 0 || process.argv[expectIndex + 1] !== "st
const key = process.argv[extractIndex + 1];
const file = process.argv[process.argv.length - 1];
const xml = readFileSync(file, "utf8");
const escapedKey = key.replace(/[.*+?^\${}()|[\]\\]/g, "\\$&");
const match = xml.match(new RegExp("<key>" + escapedKey + "<\\/key>\\s*<string>([^<]*)<\\/string>"));
// Escapes are doubled for the template-literal -> emitted-file hop: the emitted script
// must contain \\] in the class and \\$& in the replacement, or keys with regex
// metacharacters interpolate unescaped into the RegExp below and stop emulating plutil.
const escapedKey = key.replace(/[.*+?^\${}()|[\\]\\\\]/g, "\\\\$&");
const match = xml.match(new RegExp("<key>" + escapedKey + "</key>\\\\s*<string>([^<]*)</string>"));
if (!match) process.exit(1);
process.stdout.write(match[1]);
`,
@@ -378,8 +381,12 @@ function runValidator(
return { ok: true, stdout, stderr: "" };
} catch (error) {
const e = error as { stdout?: unknown; stderr?: unknown };
const stdout = Buffer.isBuffer(e.stdout) ? e.stdout.toString("utf8") : String(e.stdout ?? "");
const stderr = Buffer.isBuffer(e.stderr) ? e.stderr.toString("utf8") : String(e.stderr ?? "");
const stdout = Buffer.isBuffer(e.stdout)
? e.stdout.toString("utf8")
: ((e.stdout ?? "") as string);
const stderr = Buffer.isBuffer(e.stderr)
? e.stderr.toString("utf8")
: ((e.stderr ?? "") as string);
return { ok: false, stdout, stderr };
}
}
@@ -391,6 +398,37 @@ describe("scripts/ios-validate-app-store-ipa.sh", () => {
}
});
it("fake plutil escapes regex-metacharacter keys before matching", () => {
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-"));
tempDirs.push(root);
const plutil = path.join(root, "plutil");
writeFakePlutil(plutil);
const plistPath = path.join(root, "meta.plist");
writeFileSync(
plistPath,
"<plist><dict>\n<key>Weird[Key]*</key>\n<string>metavalue</string>\n</dict></plist>",
"utf8",
);
const escaped = spawnSync(
process.execPath,
[plutil, "-extract", "Weird[Key]*", "-expect", "string", plistPath],
{
encoding: "utf8",
},
);
expect(escaped.status).toBe(0);
expect(escaped.stdout).toBe("metavalue");
// An unescaped interpolation would let this key match as a regex; it must miss instead.
const missing = spawnSync(
process.execPath,
[plutil, "-extract", "Weird.Key.*", "-expect", "string", plistPath],
{
encoding: "utf8",
},
);
expect(missing.status).toBe(1);
});
it("accepts an App Store IPA with appStore mode and production entitlements", async () => {
const root = mkdtempSync(path.join(os.tmpdir(), "openclaw-ios-ipa-"));
tempDirs.push(root);
+1 -1
View File
@@ -47,7 +47,7 @@ describe("k8s manifests", () => {
apiVersion: "kustomize.config.k8s.io/v1beta1",
kind: "Kustomization",
});
expect(asStrings(kustomization.resources, "kustomization resources").sort()).toEqual([
expect(asStrings(kustomization.resources, "kustomization resources").toSorted()).toEqual([
"configmap.yaml",
"deployment.yaml",
"pvc.yaml",
@@ -23,7 +23,7 @@ describe("Kova report publish files", () => {
"bundle.json",
"bundle.tar.gz.sha256",
]);
expect(readdirSync(destinationDir).sort()).toEqual(["bundle.json", "bundle.tar.gz.sha256"]);
expect(readdirSync(destinationDir).toSorted()).toEqual(["bundle.json", "bundle.tar.gz.sha256"]);
expect(readFileSync(join(destinationDir, "bundle.tar.gz.sha256"), "utf8")).toBe(
"abc bundle.tar.gz\n",
);
@@ -178,7 +178,9 @@ describe("scripts/mantis/publish-pr-evidence", () => {
fetchImpl: (_url, init) => {
observedSignal = init.signal;
return new Promise<Response>((_resolve, reject) => {
init.signal.addEventListener("abort", () => reject(init.signal.reason), { once: true });
init.signal.addEventListener("abort", () => reject(init.signal.reason as Error), {
once: true,
});
});
},
manifest,
@@ -276,7 +278,9 @@ describe("scripts/mantis/publish-pr-evidence", () => {
cause: { name: "TimeoutError" },
message: "Timed out uploading Mantis artifact baseline.png after 50ms.",
});
await new Promise((resolve) => setTimeout(resolve, 0));
await new Promise((resolve) => {
setTimeout(resolve, 0);
});
expect(cancelled).toBe(true);
});
@@ -45,7 +45,12 @@ function candidateOverridePattern(): RegExp {
if (!match) {
throw new Error("Missing candidate override regex");
}
return Function(`"use strict"; return ${match[1]};`)() as RegExp;
const literal = match[1];
if (!literal) {
throw new Error("Missing candidate override regex literal");
}
const flagsStart = literal.lastIndexOf("/");
return new RegExp(literal.slice(1, flagsStart), literal.slice(flagsStart + 1));
}
describe("Mantis Web UI chat proof workflow", () => {
+1 -3
View File
@@ -135,9 +135,7 @@ describe("scripts/e2e/lib/fixtures/mock-openai-config.mjs", () => {
agents: {
defaults: { models: {} },
entries: {
main: {
...(model === undefined ? {} : { model }),
},
main: model === undefined ? {} : { model },
},
},
models: { providers: {} },
+1 -1
View File
@@ -194,7 +194,7 @@ function fakeCommands(mirror: string) {
const calls: string[] = [];
return {
calls,
runCommand(command: string, args: string[]) {
runCommand: (command: string, args: string[]) => {
calls.push([command, ...args].join(" "));
if (command === "pnpm" && args[0] === "install") {
mkdirSync(path.join(mirror, "node_modules"), { recursive: true });
@@ -433,7 +433,7 @@ describe("release Telegram QA workflow", () => {
},
);
expect(result.status, result.stderr).toBe(0);
expect(result.stdout.toString().split("\0").filter(Boolean)).toEqual(paths.slice(4).reverse());
expect(result.stdout.split("\0").filter(Boolean)).toEqual(paths.slice(4).toReversed());
});
it("keeps generated SUT programs syntactically valid", () => {
+39 -29
View File
@@ -131,37 +131,47 @@ function writeFixture(repoRoot: string, relativePath: string, contents: string):
}
describe("Periphery scope workflows", () => {
it.each(WORKFLOW_CASES)("uses the synthetic merge parent for $name scope", ({ path }) => {
const workflow = readWorkflow(path);
const steps = workflow.jobs?.scope?.steps ?? [];
const checkout = steps.find((step) => step.name === "Checkout");
const script = scopeScript(path);
it.each(WORKFLOW_CASES)(
"uses the synthetic merge parent for $name scope",
({ path: workflowPath }) => {
const workflow = readWorkflow(workflowPath);
const steps = workflow.jobs?.scope?.steps ?? [];
const checkout = steps.find((step) => step.name === "Checkout");
const script = scopeScript(workflowPath);
expect(workflow.on?.pull_request?.types).toContain("converted_to_draft");
expect(workflow.on?.pull_request?.paths).toBeUndefined();
expect(checkout?.with?.["fetch-depth"]).toBe(2);
expect(steps.some((step) => step.name === "Ensure base commit")).toBe(false);
expect(script).toContain('"HEAD^1"');
expect(script).not.toContain("pulls.listFiles");
expect(() =>
compileFunction(`return (async () => {\n${script}\n})();`, ["context", "core", "exec"]),
).not.toThrow();
});
expect(workflow.on?.pull_request?.types).toContain("converted_to_draft");
expect(workflow.on?.pull_request?.paths).toBeUndefined();
expect(checkout?.with?.["fetch-depth"]).toBe(2);
expect(steps.some((step) => step.name === "Ensure base commit")).toBe(false);
expect(script).toContain('"HEAD^1"');
expect(script).not.toContain("pulls.listFiles");
expect(() =>
compileFunction(`return (async () => {\n${script}\n})();`, ["context", "core", "exec"]),
).not.toThrow();
},
);
it.each(WORKFLOW_CASES)("selects only $name scope changes", async ({ path, scopedPath }) => {
await expect(runScope(path, { files: [scopedPath] })).resolves.toBe("true");
await expect(runScope(path, { files: ["docs/index.md"] })).resolves.toBe("false");
await expect(runScope(path, { draft: true, files: [scopedPath] })).resolves.toBe("false");
await expect(runScope(path, { eventName: "workflow_dispatch" })).resolves.toBe("true");
await expect(
runScope(path, {
files: [{ filename: "docs/Moved.swift", previous_filename: scopedPath }],
}),
).resolves.toBe("true");
await expect(runScope(path, { diffExitCode: 128 })).rejects.toThrow(
"git diff failed with exit code 128",
);
});
it.each(WORKFLOW_CASES)(
"selects only $name scope changes",
async ({ path: workflowPath, scopedPath }) => {
await expect(runScope(workflowPath, { files: [scopedPath] })).resolves.toBe("true");
await expect(runScope(workflowPath, { files: ["docs/index.md"] })).resolves.toBe("false");
await expect(runScope(workflowPath, { draft: true, files: [scopedPath] })).resolves.toBe(
"false",
);
await expect(runScope(workflowPath, { eventName: "workflow_dispatch" })).resolves.toBe(
"true",
);
await expect(
runScope(workflowPath, {
files: [{ filename: "docs/Moved.swift", previous_filename: scopedPath }],
}),
).resolves.toBe("true");
await expect(runScope(workflowPath, { diffExitCode: 128 })).rejects.toThrow(
"git diff failed with exit code 128",
);
},
);
it("ignores scoped files added only by base-branch drift", async () => {
const repoRoot = makeTempRepoRoot(tempDirs, "openclaw-periphery-scope-");
+3 -1
View File
@@ -257,7 +257,9 @@ async function waitFor(predicate: () => boolean, timeoutMs: number): Promise<boo
if (predicate()) {
return true;
}
await new Promise((resolve) => setTimeout(resolve, 10));
await new Promise((resolve) => {
setTimeout(resolve, 10);
});
}
return predicate();
}
@@ -315,7 +315,7 @@ describePosix("scripts/pr review artifact validation", () => {
const archives = readdirSync(join(localDir, "superseded"));
expect(archives).toHaveLength(1);
const archive = join(localDir, "superseded", archives[0]!);
expect(readdirSync(archive).sort()).toEqual(["review.json", "review.md"]);
expect(readdirSync(archive).toSorted()).toEqual(["review.json", "review.md"]);
expect(JSON.parse(readFileSync(join(archive, "review.json"), "utf8")).pr.number).toBe(113928);
expect(readFileSync(join(archive, "review.md"), "utf8")).toBe("A) Ship another PR\n");
+3 -1
View File
@@ -216,7 +216,9 @@ describe("scripts/pr wrappers", () => {
const classifications = parseSubcommandClassifications(script);
const dispatched = parseDispatchedSubcommands(script);
expect([...classifications.keys()].sort()).toEqual([...dispatched, "lock-recover"].sort());
expect([...classifications.keys()].toSorted()).toEqual(
[...dispatched, "lock-recover"].toSorted(),
);
expect(classifications.get("ls")).toBe("advisory");
expect(classifications.get("ci-dispatch")).toBe("advisory");
for (const command of dispatched.filter((value) => !["ls", "ci-dispatch"].includes(value))) {
@@ -438,7 +438,6 @@ describe("prepare-extension-package-boundary-artifacts", () => {
tempRoots.add(rootDir);
const descendantPidPath = path.join(rootDir, "descendant.pid");
let descendantPid = 0;
let runnerPid = 0;
const moduleHref = pathToFileURL(
path.resolve("scripts/prepare-extension-package-boundary-artifacts.mjs"),
).href;
@@ -461,7 +460,7 @@ describe("prepare-extension-package-boundary-artifacts", () => {
const runner = spawn(process.execPath, ["--input-type=module", "--eval", runnerScript], {
stdio: "ignore",
});
runnerPid = runner.pid ?? 0;
const runnerPid = runner.pid ?? 0;
try {
descendantPid = Number.parseInt(await waitForFile(descendantPidPath, 10_000), 10);
@@ -79,14 +79,12 @@ describe("release candidate checklist", () => {
toolingSha: "b".repeat(40),
});
const resumed = reconcileReleaseCandidateState(
JSON.parse(
JSON.stringify({
...expected,
phase: "waiting",
fullReleaseRunId: "111",
npmPreflightRunId: "222",
}),
),
structuredClone({
...expected,
phase: "waiting",
fullReleaseRunId: "111",
npmPreflightRunId: "222",
}),
expected,
);
@@ -199,8 +199,8 @@ describe("release validation no-push transport", () => {
"validate_docker_lanes",
"validate_docker_openwebui",
]) {
const job = workflow.jobs?.[jobName];
const runStep = job?.steps?.find((candidate) =>
const workflowJob = workflow.jobs?.[jobName];
const runStep = workflowJob?.steps?.find((candidate) =>
candidate.run?.includes("test-live-build-docker.sh"),
);
@@ -20,7 +20,7 @@ import { afterEach, describe, expect, it } from "vitest";
const SCRIPT = path.resolve("scripts/release-telegram-candidate-archive.py");
const tempDirs: string[] = [];
const tarVersion = spawnSync("tar", ["--version"], { encoding: "utf8" });
const hasGnuTar = tarVersion.status === 0 && tarVersion.stdout?.includes("GNU tar") === true;
const hasGnuTar = tarVersion.status === 0 && tarVersion.stdout?.includes("GNU tar");
afterEach(() => {
for (const directory of tempDirs.splice(0)) {
@@ -472,7 +472,9 @@ describe("release Telegram candidate archive guard", () => {
try {
expectFailure(["validate-tree", root], "unsupported special entry");
} finally {
await new Promise<void>((resolve) => server.close(() => resolve()));
await new Promise<void>((resolve) => {
server.close(() => resolve());
});
}
});
@@ -50,7 +50,9 @@ async function waitUntil(matches: () => boolean, label: string): Promise<void> {
if (matches()) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 20));
await new Promise((resolve) => {
setTimeout(resolve, 20);
});
}
throw new Error(`timed out waiting for ${label}`);
}
+2 -1
View File
@@ -37,7 +37,8 @@ describe("sync-labels", () => {
const ghCalls = execFileSyncMock.mock.calls.filter(([command]) => command === "gh");
expect(ghCalls.length).toBeGreaterThan(1);
for (const [, , options] of ghCalls) {
for (const call of ghCalls) {
const options = call[2];
expect(options).toMatchObject({
timeout: 120_000,
killSignal: "SIGKILL",
+1 -3
View File
@@ -882,9 +882,7 @@ describe("scripts/test-group-report arg parsing", () => {
flag,
expectDefined(values[1], `second ${flag} value`),
];
expect(() => parseTestGroupReportArgs(args)).toThrow(
`${String(flag)} was provided more than once`,
);
expect(() => parseTestGroupReportArgs(args)).toThrow(`${flag} was provided more than once`);
}
expect(parseTestGroupReportArgs(["--config", "a.ts", "--config", "b.ts"]).configs).toEqual([
"a.ts",
+4 -2
View File
@@ -296,7 +296,7 @@ function normalizeInstallE2eAgentOutput(output: string) {
function extractInstallSmokeUpdateJsonParser(): string {
const script = readFileSync(SMOKE_RUNNER_PATH, "utf8");
const match = script.match(
/UPDATE_JSON="\$UPDATE_JSON" \\\n[\s\S]*?node - <<'NODE'\n([\s\S]*?)\nNODE\n\n echo "==> Verify updated version"/u,
/UPDATE_JSON="\$UPDATE_JSON" \\\n[\s\S]*?node - <<'NODE'\n([\s\S]*?)\nNODE\n\n {2}echo "==> Verify updated version"/u,
);
if (!match) {
throw new Error("install smoke update JSON parser was not found");
@@ -463,7 +463,9 @@ async function waitForCondition(
if (predicate()) {
return;
}
await new Promise((resolve) => setTimeout(resolve, 5));
await new Promise((resolve) => {
setTimeout(resolve, 5);
});
}
throw new Error(`timed out waiting for ${label}`);
}
@@ -208,8 +208,9 @@ describe("transitive-manifest-risk-report", () => {
version: "1.0.0",
registryBaseUrl: "https://registry.example.test",
fetchImpl: async (url, init) => {
const requestUrl = url instanceof Request ? url.url : url instanceof URL ? url.href : url;
fetchCalls.push({
url: String(url),
url: requestUrl,
accept: new Headers(init?.headers).get("accept"),
signal: init?.signal instanceof AbortSignal ? init.signal : null,
});
+3 -1
View File
@@ -1077,7 +1077,9 @@ describe("verify-pr-hosted-gates", () => {
["queued artifact run", 5],
])("does not cover queued artifacts with a stale %s", (_kind, staleRunIndex) => {
const workflowRuns = queuedBuildArtifactFallbackRuns().map((run, index) =>
index === staleRunIndex ? { ...run, updated_at: "2026-06-16T10:54:59Z" } : run,
index === staleRunIndex
? Object.assign({}, run, { updated_at: "2026-06-16T10:54:59Z" })
: run,
);
expect(() => collectHostedGateEvidence({ sha, workflowRuns })).toThrow(
"Missing successful recent Blacksmith Build Artifacts Testbox workflow",
@@ -590,7 +590,9 @@ describe("write-cli-startup-metadata", () => {
return "Usage: openclaw browser\n";
},
renderSourceSecretsHelpText: async () => {
await new Promise((resolve) => setImmediate(resolve));
await new Promise((resolve) => {
setImmediate(resolve);
});
statePresentDuringSiblingRender = existsSync(stateDir);
return "Usage: openclaw secrets\n";
},