chore: apply core lint cleanups

This commit is contained in:
Peter Steinberger
2026-04-23 05:28:11 +01:00
parent cc9dcd3d69
commit 596b88986d
41 changed files with 85 additions and 82 deletions

View File

@@ -456,7 +456,8 @@ export function collectForbiddenPackPaths(paths: Iterable<string>): string[] {
return [...paths]
.filter(
(path) =>
forbiddenPrefixes.some((prefix) => path.startsWith(prefix)) || /node_modules\//.test(path),
forbiddenPrefixes.some((prefix) => path.startsWith(prefix)) ||
path.includes("node_modules/"),
)
.toSorted((left, right) => left.localeCompare(right));
}

View File

@@ -13,7 +13,7 @@ import {
const logLevel = process.env.OPENCLAW_BUILD_VERBOSE ? "info" : "warn";
const extraArgs = process.argv.slice(2);
const INEFFECTIVE_DYNAMIC_IMPORT_RE = /\[INEFFECTIVE_DYNAMIC_IMPORT\]/;
const INEFFECTIVE_DYNAMIC_IMPORT_MARKER = "[INEFFECTIVE_DYNAMIC_IMPORT]";
const UNRESOLVED_IMPORT_RE = /\[UNRESOLVED_IMPORT\]/;
const ANSI_ESCAPE_RE = new RegExp(String.raw`\u001B\[[0-9;]*m`, "g");
const HASHED_ROOT_JS_RE = /^(?<base>.+)-[A-Za-z0-9_-]+\.js$/u;
@@ -170,7 +170,7 @@ export function createTsdownOutputScanner(params = {}) {
return {
append(chunk) {
const text = Buffer.isBuffer(chunk) ? chunk.toString("utf8") : String(chunk);
if (INEFFECTIVE_DYNAMIC_IMPORT_RE.test(text)) {
if (text.includes(INEFFECTIVE_DYNAMIC_IMPORT_MARKER)) {
hasIneffectiveDynamicImport = true;
}
scanLines(text);

View File

@@ -44,7 +44,7 @@ describe("getSoonestCooldownExpiry", () => {
cooldownUntil: Infinity,
},
"openai:p3": {
disabledUntil: NaN,
disabledUntil: Number.NaN,
},
"openai:p4": {
cooldownUntil: 1_700_000_005_000,

View File

@@ -249,7 +249,7 @@ describe("resolveApiKeyForProfile fallback to main agent", () => {
profileId,
access: "secondary-stale",
refresh: "secondary-refresh",
expires: NaN,
expires: Number.NaN,
}),
);

View File

@@ -521,7 +521,7 @@ describe("clearExpiredCooldowns", () => {
it("ignores NaN and Infinity cooldown values", () => {
const store = makeStore({
"anthropic:default": {
cooldownUntil: NaN,
cooldownUntil: Number.NaN,
errorCount: 2,
},
"openai:default": {

View File

@@ -181,7 +181,7 @@ export function formatUserTime(
if (!map.weekday || !map.year || !map.month || !map.day || !map.hour || !map.minute) {
return undefined;
}
const dayNum = parseInt(map.day, 10);
const dayNum = Number.parseInt(map.day, 10);
const suffix = ordinalSuffix(dayNum);
const timePart = use24Hour
? `${map.hour}:${map.minute}`

View File

@@ -539,7 +539,7 @@ describe("runWithModelFallback probe logic", () => {
it("handles NaN soonest safely (treats as probe-worthy)", async () => {
const cfg = makeCfg();
mockedGetSoonestCooldownExpiry.mockReturnValue(NaN);
mockedGetSoonestCooldownExpiry.mockReturnValue(Number.NaN);
const run = vi.fn().mockResolvedValue("ok-nan");

View File

@@ -66,7 +66,7 @@ function parseLspMessages(buffer: string): { messages: unknown[]; remaining: str
continue;
}
const contentLength = parseInt(match[1], 10);
const contentLength = Number.parseInt(match[1], 10);
const bodyStart = headerEnd + 4;
const bodyEnd = bodyStart + contentLength;

View File

@@ -146,7 +146,7 @@ describe("resolveCompactionTimeoutMs", () => {
it("returns default for NaN", () => {
expect(
resolveCompactionTimeoutMs({
agents: { defaults: { compaction: { timeoutSeconds: NaN } } },
agents: { defaults: { compaction: { timeoutSeconds: Number.NaN } } },
}),
).toBe(EMBEDDED_COMPACTION_TIMEOUT_MS);
});

View File

@@ -171,10 +171,10 @@ function parseModel(model: OpenRouterApiModel): OpenRouterModelCapabilities {
model.max_output_tokens ??
8192,
cost: {
input: parseFloat(model.pricing?.prompt || "0") * 1_000_000,
output: parseFloat(model.pricing?.completion || "0") * 1_000_000,
cacheRead: parseFloat(model.pricing?.input_cache_read || "0") * 1_000_000,
cacheWrite: parseFloat(model.pricing?.input_cache_write || "0") * 1_000_000,
input: Number.parseFloat(model.pricing?.prompt || "0") * 1_000_000,
output: Number.parseFloat(model.pricing?.completion || "0") * 1_000_000,
cacheRead: Number.parseFloat(model.pricing?.input_cache_read || "0") * 1_000_000,
cacheWrite: Number.parseFloat(model.pricing?.input_cache_write || "0") * 1_000_000,
},
};
}

View File

@@ -163,22 +163,21 @@ export async function runBeforeToolCallHook(args: {
blocked: true,
reason: loopResult.message,
};
} else {
const warningKey = loopResult.warningKey ?? `${loopResult.detector}:${toolName}`;
if (shouldEmitLoopWarning(sessionState, warningKey, loopResult.count)) {
log.warn(`Loop warning for ${toolName}: ${loopResult.message}`);
logToolLoopAction({
sessionKey: args.ctx.sessionKey,
sessionId: args.ctx?.agentId,
toolName,
level: "warning",
action: "warn",
detector: loopResult.detector,
count: loopResult.count,
message: loopResult.message,
pairedToolName: loopResult.pairedToolName,
});
}
}
const warningKey = loopResult.warningKey ?? `${loopResult.detector}:${toolName}`;
if (shouldEmitLoopWarning(sessionState, warningKey, loopResult.count)) {
log.warn(`Loop warning for ${toolName}: ${loopResult.message}`);
logToolLoopAction({
sessionKey: args.ctx.sessionKey,
sessionId: args.ctx?.agentId,
toolName,
level: "warning",
action: "warn",
detector: loopResult.detector,
count: loopResult.count,
message: loopResult.message,
pairedToolName: loopResult.pairedToolName,
});
}
}

View File

@@ -13,7 +13,7 @@ const DEFAULT_MAX_SDK_RETRY_WAIT_SECONDS = 60;
function parseRetryAfterSeconds(headers: Headers): number | undefined {
const retryAfterMs = headers.get("retry-after-ms");
if (retryAfterMs) {
const milliseconds = parseFloat(retryAfterMs);
const milliseconds = Number.parseFloat(retryAfterMs);
if (Number.isFinite(milliseconds) && milliseconds >= 0) {
return milliseconds / 1000;
}
@@ -24,7 +24,7 @@ function parseRetryAfterSeconds(headers: Headers): number | undefined {
return undefined;
}
const seconds = parseFloat(retryAfter);
const seconds = Number.parseFloat(retryAfter);
if (Number.isFinite(seconds) && seconds >= 0) {
return seconds;
}
@@ -47,7 +47,7 @@ function resolveMaxSdkRetryWaitSeconds(): number | undefined {
return undefined;
}
const seconds = parseFloat(raw);
const seconds = Number.parseFloat(raw);
if (Number.isFinite(seconds) && seconds > 0) {
return seconds;
}

View File

@@ -156,7 +156,7 @@ let sweepInProgress = false;
let listenerStarted = false;
let listenerStop: (() => void) | null = null;
// Use var to avoid TDZ when init runs across circular imports during bootstrap.
var restoreAttempted = false;
let restoreAttempted = false;
const ORPHAN_RECOVERY_DEBOUNCE_MS = 1_000;
let lastOrphanRecoveryScheduleAt = 0;
const SUBAGENT_ANNOUNCE_TIMEOUT_MS = 120_000;

View File

@@ -193,7 +193,7 @@ export function isoToPerplexityDate(iso: string): string | undefined {
return undefined;
}
const [, year, month, day] = match;
return `${parseInt(month, 10)}/${parseInt(day, 10)}/${year}`;
return `${Number.parseInt(month, 10)}/${Number.parseInt(day, 10)}/${year}`;
}
export function normalizeToIsoDate(value: string): string | undefined {

View File

@@ -42,7 +42,7 @@ export const splitTrailingDirective = (
// 1. Unclosed `[[…` reply/audio directive tail.
const openIndex = text.lastIndexOf("[[");
if (openIndex >= 0 && text.indexOf("]]", openIndex + 2) < 0) {
if (openIndex >= 0 && !text.includes("]]", openIndex + 2)) {
if (openIndex < bufferStart) {
bufferStart = openIndex;
}

View File

@@ -28,7 +28,7 @@ export function resolveGroupAllowFromSources(params: {
export function firstDefined<T>(...values: Array<T | undefined>) {
for (const value of values) {
if (typeof value !== "undefined") {
if (value !== undefined) {
return value;
}
}

View File

@@ -98,9 +98,9 @@ export function resolveSingleAccountKeysToMove(params: {
channelKey: string;
channel: Record<string, unknown>;
}): string[] {
const hasNamedAccounts =
Object.keys((params.channel.accounts as Record<string, unknown>) ?? {}).filter(Boolean).length >
0;
const hasNamedAccounts = Object.keys(
(params.channel.accounts as Record<string, unknown>) ?? {},
).some(Boolean);
const entries = Object.entries(params.channel)
.filter(
([key, value]) =>

View File

@@ -17,7 +17,7 @@ const SECRET_TARGET_CALLSITES = [
function hasSupportedTargetIdsWiring(source: string): boolean {
return (
/resolveAgentRuntimeConfig\(/.test(source) ||
source.includes("resolveAgentRuntimeConfig(") ||
/targetIds:\s*get[A-Za-z0-9_]+\(\)/m.test(source) ||
/targetIds:\s*getAgentRuntimeCommandSecretTargetIds\(/m.test(source) ||
/targetIds:\s*scopedTargets\.targetIds/m.test(source) ||
@@ -27,15 +27,15 @@ function hasSupportedTargetIdsWiring(source: string): boolean {
function hasSupportedSecretResolutionWiring(source: string): boolean {
return (
/resolveAgentRuntimeConfig\(/.test(source) ||
/resolveCommandConfigWithSecrets\(/.test(source) ||
/resolveCommandSecretRefsViaGateway\(/.test(source) ||
/collectStatusScanOverview\(/.test(source)
source.includes("resolveAgentRuntimeConfig(") ||
source.includes("resolveCommandConfigWithSecrets(") ||
source.includes("resolveCommandSecretRefsViaGateway(") ||
source.includes("collectStatusScanOverview(")
);
}
function usesDelegatedStatusOverviewFlow(source: string): boolean {
return /collectStatusScanOverview\(/.test(source);
return source.includes("collectStatusScanOverview(");
}
describe("command secret resolution coverage", () => {

View File

@@ -170,7 +170,7 @@ const truncate = (value: string, width: number) => {
const formatIsoMinute = (iso: string) => {
const parsed = parseAbsoluteTimeMs(iso);
const d = new Date(parsed ?? NaN);
const d = new Date(parsed ?? Number.NaN);
if (Number.isNaN(d.getTime())) {
return "-";
}

View File

@@ -94,7 +94,7 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) {
const config = await loadNodeHostConfig();
const { host, port } = resolveNodeDefaults(opts, config);
if (!Number.isFinite(port ?? NaN) || (port ?? 0) <= 0) {
if (!Number.isFinite(port ?? Number.NaN) || (port ?? 0) <= 0) {
fail("Invalid port");
return;
}

View File

@@ -8,7 +8,7 @@ import {
} from "./routed-command-definitions.js";
export type RouteSpec = {
match: (path: string[]) => boolean;
matches: (path: string[]) => boolean;
loadPlugins?: boolean | ((argv: string[]) => boolean);
run: (argv: string[]) => Promise<boolean>;
};
@@ -25,7 +25,7 @@ function createParsedRoute(params: {
definition: AnyRoutedCommandDefinition;
}): RouteSpec {
return {
match: (path) =>
matches: (path) =>
matchesCommandPath(path, params.entry.commandPath, { exact: params.entry.exact }),
loadPlugins: params.entry.route?.preloadPlugins
? createCommandLoadPlugins(params.entry.commandPath)

View File

@@ -4,7 +4,7 @@ export type { RouteSpec } from "./route-specs.js";
export function findRoutedCommand(path: string[]): RouteSpec | null {
for (const route of routedCommands) {
if (route.match(path)) {
if (route.matches(path)) {
return route;
}
}

View File

@@ -178,7 +178,7 @@ vi.mock("../config/sessions/transcript-resolve.runtime.js", () => {
return lastSlash >= 0 ? filePath.slice(0, lastSlash) : ".";
};
const joinPath = (...parts: string[]): string => {
const separator = parts.find((part) => part.includes("\\")) ? "\\" : "/";
const separator = parts.some((part) => part.includes("\\")) ? "\\" : "/";
return parts
.map((part, index) =>
index === 0 ? part.replace(/[\\/]+$/u, "") : part.replace(/^[\\/]+|[\\/]+$/gu, ""),

View File

@@ -551,7 +551,7 @@ vi.mock("../channels/plugins/setup-promotion-helpers.js", () => {
channel.accounts && typeof channel.accounts === "object" && !Array.isArray(channel.accounts)
? (channel.accounts as Record<string, unknown>)
: {};
const hasNamedAccounts = Object.keys(accounts).filter(Boolean).length > 0;
const hasNamedAccounts = Object.keys(accounts).some(Boolean);
const allowedNamedKeys = namedAccountPromotionKeys[channelKey];
return Object.entries(channel)
.filter(([key, value]) => {

View File

@@ -19,9 +19,8 @@ const ENV_VAR_PLACEHOLDER_PATTERN = /^\$\{[^}]*\}$/;
function isSensitivePath(path: string): boolean {
if (path.endsWith("[]")) {
return isSensitiveConfigPath(path.slice(0, -2));
} else {
return isSensitiveConfigPath(path);
}
return isSensitiveConfigPath(path);
}
function isEnvVarPlaceholder(value: string): boolean {
@@ -146,9 +145,8 @@ function redactObject(obj: unknown, hints?: ConfigUiHints): unknown {
return lookup.has("")
? redactObjectWithLookup(obj, lookup, "", [], hints)
: redactObjectGuessing(obj, "", [], hints);
} else {
return redactObjectGuessing(obj, "", []);
}
return redactObjectGuessing(obj, "", []);
}
/**

View File

@@ -210,7 +210,7 @@ describe("coerceFiniteScheduleNumber", () => {
it("returns undefined for invalid inputs", () => {
expect(coerceFiniteScheduleNumber("")).toBeUndefined();
expect(coerceFiniteScheduleNumber("abc")).toBeUndefined();
expect(coerceFiniteScheduleNumber(NaN)).toBeUndefined();
expect(coerceFiniteScheduleNumber(Number.NaN)).toBeUndefined();
expect(coerceFiniteScheduleNumber(Infinity)).toBeUndefined();
expect(coerceFiniteScheduleNumber(null)).toBeUndefined();
expect(coerceFiniteScheduleNumber(undefined)).toBeUndefined();

View File

@@ -34,7 +34,7 @@ export async function startGatewayDiscovery(params: {
? await resolveTailnetDnsHint({ enabled: tailscaleEnabled })
: undefined;
const sshPortEnv = mdnsMinimal ? undefined : process.env.OPENCLAW_SSH_PORT?.trim();
const sshPortParsed = sshPortEnv ? Number.parseInt(sshPortEnv, 10) : NaN;
const sshPortParsed = sshPortEnv ? Number.parseInt(sshPortEnv, 10) : Number.NaN;
const sshPort = Number.isFinite(sshPortParsed) && sshPortParsed > 0 ? sshPortParsed : undefined;
const cliPath = mdnsMinimal ? undefined : resolveBonjourCliPath();

View File

@@ -6,7 +6,7 @@ import { formatZonedTimestamp } from "../../infra/format-time/format-datetime.ts
* Cron jobs inject "Current time: ..." into their messages.
* Skip injection for those.
*/
const CRON_TIME_PATTERN = /Current time: /;
const CRON_TIME_MARKER = "Current time: ";
/**
* Matches a leading `[... YYYY-MM-DD HH:MM ...]` envelope — either from
@@ -49,7 +49,7 @@ export function injectTimestamp(message: string, opts?: TimestampInjectionOption
}
// Already has a cron-injected timestamp
if (CRON_TIME_PATTERN.test(message)) {
if (message.includes(CRON_TIME_MARKER)) {
return message;
}

View File

@@ -278,15 +278,14 @@ export function createGatewayReloadHandlers(params: GatewayReloadHandlerParams)
},
});
return true;
} else {
// No active operations or pending replies, restart immediately
params.logReload.warn(`config change requires gateway restart (${reasons})`);
const emitted = emitGatewayRestart();
if (!emitted) {
params.logReload.info("gateway restart already scheduled; skipping duplicate signal");
}
return true;
}
// No active operations or pending replies, restart immediately
params.logReload.warn(`config change requires gateway restart (${reasons})`);
const emitted = emitGatewayRestart();
if (!emitted) {
params.logReload.info("gateway restart already scheduled; skipping duplicate signal");
}
return true;
};
return { applyHotReload, requestGatewayRestart };

View File

@@ -218,7 +218,7 @@ export function installOpenAiResponsesMock(params?: { baseUrl?: string }) {
if (isResponsesRequest(url)) {
const bodyText =
typeof (init as { body?: unknown } | undefined)?.body !== "undefined"
(init as { body?: unknown } | undefined)?.body !== undefined
? decodeBodyText((init as { body?: unknown }).body)
: input instanceof Request
? await input.clone().text()

View File

@@ -91,7 +91,7 @@ describe("format-duration", () => {
{ input: 1000, expected: "1s" },
{ input: 1500, expected: "1.5s" },
{ input: 1234, expected: "1.23s" },
{ input: NaN, expected: "unknown" },
{ input: Number.NaN, expected: "unknown" },
{ input: Infinity, expected: "unknown" },
])("formats precise duration for %j", ({ input, expected }) => {
expect(formatDurationPrecise(input)).toBe(expected);
@@ -105,7 +105,7 @@ describe("format-duration", () => {
{ input: 1000, options: { decimals: 0 }, expected: "1s" },
{ input: 2000, options: { unit: "seconds" as const }, expected: "2 seconds" },
{ input: -1500, options: { decimals: 1 }, expected: "0s" },
{ input: NaN, options: undefined, expected: "unknown" },
{ input: Number.NaN, options: undefined, expected: "unknown" },
{ input: Infinity, options: undefined, expected: "unknown" },
])("formats seconds duration for %j", ({ input, options, expected }) => {
expect(formatDurationSeconds(input, options)).toBe(expected);

View File

@@ -219,7 +219,7 @@ function parseNetstatListeners(output: string, port: number): PortListener[] {
continue;
}
const pidRaw = parts.at(-1);
const pid = pidRaw ? Number.parseInt(pidRaw, 10) : NaN;
const pid = pidRaw ? Number.parseInt(pidRaw, 10) : Number.NaN;
const localAddr = parts[1];
const listener: PortListener = {};
if (Number.isFinite(pid)) {

View File

@@ -110,7 +110,7 @@ export async function fetchCodexUsage(
const balance =
typeof data.credits.balance === "number"
? data.credits.balance
: parseFloat(data.credits.balance) || 0;
: Number.parseFloat(data.credits.balance) || 0;
plan = plan ? `${plan} ($${balance.toFixed(2)})` : `$${balance.toFixed(2)}`;
}

View File

@@ -380,7 +380,7 @@ function parseOptionalNonNegativeInteger(value: unknown, field: string): number
return undefined;
}
const parsed =
typeof value === "number" ? value : typeof value === "string" ? Number(value) : NaN;
typeof value === "number" ? value : typeof value === "string" ? Number(value) : Number.NaN;
if (!Number.isInteger(parsed) || parsed < 0) {
throw new Error(`${field} must be a non-negative integer`);
}

View File

@@ -243,7 +243,7 @@ export type ChannelOutboundSessionRouteParams = Parameters<
NonNullable<ChannelMessagingAdapter["resolveOutboundSessionRoute"]>
>[0];
var cachedSdkChatChannelMeta:
let cachedSdkChatChannelMeta:
| {
cacheKey: string;
metaById: ReturnType<typeof buildChatChannelMetaById>;

View File

@@ -22,7 +22,7 @@ import type { OpenClawPluginCommandDefinition } from "./types.js";
* output chunk, so any module-level const/let would be uninitialized when
* first accessed during plugin registration.
*/
var reservedCommands: Set<string> | undefined;
let reservedCommands: Set<string> | undefined;
export type CommandRegistrationResult = {
ok: boolean;

View File

@@ -241,7 +241,7 @@ export function scanSource(source: string, filePath: string): SkillScanFinding[]
// Special handling for suspicious-network: check port
if (rule.ruleId === "suspicious-network") {
const port = parseInt(match[1], 10);
const port = Number.parseInt(match[1], 10);
if (STANDARD_PORTS.has(port)) {
continue;
}

View File

@@ -497,7 +497,7 @@ export function createCommandHandlers(context: CommandHandlerContext) {
chatLog.addSystem(`elevated failed: ${String(err)}`);
}
break;
case "activation":
case "activation": {
if (!args) {
chatLog.addSystem("usage: /activation <mention|always>");
break;
@@ -519,6 +519,7 @@ export function createCommandHandlers(context: CommandHandlerContext) {
chatLog.addSystem(`activation failed: ${String(err)}`);
}
break;
}
case "new":
try {
// Clear token counts immediately to avoid stale display (#1523)

View File

@@ -144,7 +144,7 @@ function normalizeTieredPricing(raw: RawPricingTier[] | undefined): PricingTier[
if (!Array.isArray(range) || range.length < 1) {
continue;
}
const start = typeof range[0] === "number" ? range[0] : NaN;
const start = typeof range[0] === "number" ? range[0] : Number.NaN;
if (!Number.isFinite(start)) {
continue;
}

View File

@@ -7,7 +7,11 @@ function parseHexRgb(hex: string): [number, number, number] | null {
if (!/^[0-9a-fA-F]{6}$/.test(h)) {
return null;
}
return [parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16)];
return [
Number.parseInt(h.slice(0, 2), 16),
Number.parseInt(h.slice(2, 4), 16),
Number.parseInt(h.slice(4, 6), 16),
];
}
let cachedThemeNoticeColors: {

View File

@@ -219,7 +219,8 @@ function renderDailyChartCompact(
const isSelected = selectedDays.includes(d.date);
const label = formatDayLabel(d.date);
// Shorter label for many days (just day number)
const shortLabel = daily.length > 20 ? String(parseInt(d.date.slice(8), 10)) : label;
const shortLabel =
daily.length > 20 ? String(Number.parseInt(d.date.slice(8), 10)) : label;
const labelClass =
daily.length > 20 ? "daily-bar-label daily-bar-label--compact" : "daily-bar-label";
const segments =