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