diff --git a/.oxlintrc.json b/.oxlintrc.json index fba80200980..331ad823670 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -35,6 +35,7 @@ "eslint/no-useless-constructor": "error", "eslint/no-useless-rename": "error", "eslint/no-useless-return": "error", + "eslint/no-useless-assignment": "error", "eslint/no-unused-vars": "error", "eslint/no-warning-comments": "error", "eslint/no-unmodified-loop-condition": "error", diff --git a/extensions/acpx/src/process-reaper.ts b/extensions/acpx/src/process-reaper.ts index d03fe932f27..9297f54d9cc 100644 --- a/extensions/acpx/src/process-reaper.ts +++ b/extensions/acpx/src/process-reaper.ts @@ -302,7 +302,7 @@ export async function cleanupOpenClawOwnedAcpxProcessTree(params: { return { inspectedPids: [], terminatedPids: [], skippedReason: "missing-root" }; } - let processes: AcpxProcessInfo[] = []; + let processes: AcpxProcessInfo[]; try { processes = await (params.deps?.listProcesses ?? listPlatformProcesses)(); } catch { diff --git a/extensions/acpx/src/runtime.ts b/extensions/acpx/src/runtime.ts index b088312e7e5..d66828b3c2b 100644 --- a/extensions/acpx/src/runtime.ts +++ b/extensions/acpx/src/runtime.ts @@ -1196,7 +1196,7 @@ export class AcpxRuntime implements AcpRuntime { const record = await this.sessionStore.load( input.handle.acpxRecordId ?? input.handle.sessionKey, ); - let closeSucceeded = false; + let closeSucceeded; try { await this.resolveDelegateForLoadedRecord(input.handle, record).close({ handle: input.handle, diff --git a/extensions/browser/src/browser/chrome.diagnostics.ts b/extensions/browser/src/browser/chrome.diagnostics.ts index 1e6c9f169f2..bfb92c631d2 100644 --- a/extensions/browser/src/browser/chrome.diagnostics.ts +++ b/extensions/browser/src/browser/chrome.diagnostics.ts @@ -137,7 +137,7 @@ async function diagnoseCdpHealthCommand( if (settled) { return; } - let parsed: { id?: unknown; result?: unknown } | null = null; + let parsed: { id?: unknown; result?: unknown } | null; try { parsed = JSON.parse(rawDataToString(raw)) as { id?: unknown; result?: unknown }; } catch { diff --git a/extensions/browser/src/browser/chrome.test.ts b/extensions/browser/src/browser/chrome.test.ts index 3adc7fffdb1..869b956e5a3 100644 --- a/extensions/browser/src/browser/chrome.test.ts +++ b/extensions/browser/src/browser/chrome.test.ts @@ -559,7 +559,7 @@ describe("browser chrome helpers", () => { onConnection: (wss) => { wss.on("connection", (ws) => { ws.on("message", (raw) => { - let message: { id?: unknown; method?: unknown } | null = null; + let message: { id?: unknown; method?: unknown } | null; try { const text = typeof raw === "string" diff --git a/extensions/browser/src/browser/config.ts b/extensions/browser/src/browser/config.ts index f980170eee4..396e6d25f7d 100644 --- a/extensions/browser/src/browser/config.ts +++ b/extensions/browser/src/browser/config.ts @@ -469,7 +469,7 @@ export function resolveProfile( const rawProfileUrl = profile.cdpUrl?.trim() ?? ""; let cdpHost = resolved.cdpHost; let cdpPort = profile.cdpPort ?? 0; - let cdpUrl = ""; + let cdpUrl; const driver = profile.driver === "existing-session" ? "existing-session" : "openclaw"; const headless = profile.headless ?? resolved.headless; const headlessSource = diff --git a/extensions/browser/src/browser/pw-session.ts b/extensions/browser/src/browser/pw-session.ts index 48774233ea3..5b19fb2adf2 100644 --- a/extensions/browser/src/browser/pw-session.ts +++ b/extensions/browser/src/browser/pw-session.ts @@ -1066,7 +1066,7 @@ async function findPageByTargetId( const pages = await getAllPages(browser); let resolvedViaCdp = false; for (const page of pages) { - let tid: string | null = null; + let tid: string | null; try { tid = await pageTargetId(page); resolvedViaCdp = true; @@ -1170,7 +1170,7 @@ export async function getPageForTargetId(opts: { } function isTopLevelNavigationRequest(page: Page, request: Request): boolean { - let sameMainFrame = false; + let sameMainFrame; try { sameMainFrame = request.frame() === page.mainFrame(); } catch { @@ -1197,7 +1197,7 @@ function isTopLevelNavigationRequest(page: Page, request: Request): boolean { } function isSubframeDocumentNavigationRequest(page: Page, request: Request): boolean { - let sameMainFrame = false; + let sameMainFrame; try { sameMainFrame = request.frame() === page.mainFrame(); } catch { diff --git a/extensions/browser/src/cli/browser-cli-manage.ts b/extensions/browser/src/cli/browser-cli-manage.ts index 0b2170ac050..9b27682cc9f 100644 --- a/extensions/browser/src/cli/browser-cli-manage.ts +++ b/extensions/browser/src/cli/browser-cli-manage.ts @@ -150,7 +150,7 @@ function formatDoctorLine(check: BrowserDoctorCheck): string { async function runBrowserDoctor(parent: BrowserParentOpts, profile?: string, deep?: boolean) { const checks: BrowserDoctorCheck[] = []; - let status: BrowserStatus | null = null; + let status: BrowserStatus | null; try { status = await fetchBrowserStatus(parent, profile); diff --git a/extensions/browser/src/gateway/browser-request.ts b/extensions/browser/src/gateway/browser-request.ts index b634ffe21f4..75c49d9ef89 100644 --- a/extensions/browser/src/gateway/browser-request.ts +++ b/extensions/browser/src/gateway/browser-request.ts @@ -171,7 +171,7 @@ export async function handleBrowserGatewayRequest({ } const cfg = getRuntimeConfig(); - let nodeTarget: NodeSession | null = null; + let nodeTarget: NodeSession | null; try { nodeTarget = resolveBrowserNodeTarget({ cfg, diff --git a/extensions/canvas/src/host/server.test.ts b/extensions/canvas/src/host/server.test.ts index 06f422dac7a..33fede7b73d 100644 --- a/extensions/canvas/src/host/server.test.ts +++ b/extensions/canvas/src/host/server.test.ts @@ -388,7 +388,6 @@ describe("canvas host", () => { const linkName = `test-link-${Date.now()}-${Math.random().toString(16).slice(2)}.txt`; const linkPath = path.join(a2uiRoot, linkName); let createdBundle = false; - let createdLink = false; try { await fs.stat(bundlePath); @@ -398,7 +397,6 @@ describe("canvas host", () => { } await fs.symlink(path.join(process.cwd(), "package.json"), linkPath); - createdLink = true; try { const res = await captureA2uiResponse(`${A2UI_PATH}/`); @@ -421,9 +419,7 @@ describe("canvas host", () => { expect(symlinkRes.status).toBe(404); expect(symlinkRes.body).toBe("not found"); } finally { - if (createdLink) { - await fs.rm(linkPath, { force: true }); - } + await fs.rm(linkPath, { force: true }); if (createdBundle) { await fs.rm(bundlePath, { force: true }); } diff --git a/extensions/codex/src/app-server/attempt-startup.ts b/extensions/codex/src/app-server/attempt-startup.ts index ce68fc839df..5797afaf811 100644 --- a/extensions/codex/src/app-server/attempt-startup.ts +++ b/extensions/codex/src/app-server/attempt-startup.ts @@ -337,7 +337,6 @@ export async function startCodexAttemptThread(params: { if (startupClientForAbandonedRequestCleanup === failedClient) { startupClientForAbandonedRequestCleanup = undefined; } - attemptedClient = undefined; if (attempt >= CODEX_APP_SERVER_STARTUP_CONNECTION_CLOSE_MAX_ATTEMPTS) { embeddedAgentLog.warn( "codex app-server connection closed during startup; retries exhausted", diff --git a/extensions/codex/src/app-server/run-attempt.ts b/extensions/codex/src/app-server/run-attempt.ts index 8ca3b94135f..04e56be1f64 100644 --- a/extensions/codex/src/app-server/run-attempt.ts +++ b/extensions/codex/src/app-server/run-attempt.ts @@ -965,8 +965,19 @@ export async function runCodexAppServerAttempt( let client: CodexAppServerClient; let thread: CodexAppServerThreadLifecycleBinding; let trajectoryEndRecorded = false; + const markTrajectoryEndRecorded = () => { + trajectoryEndRecorded = true; + }; let nativeHookRelay: NativeHookRelayRegistrationHandle | undefined; let releaseSharedClientLease: (() => void) | undefined; + const releaseSharedClientLeaseOnce = () => { + const release = releaseSharedClientLease; + if (!release) { + return; + } + releaseSharedClientLease = undefined; + release(); + }; let sandboxExecEnvironmentAcquired = false; const releaseSandboxExecEnvironment = async () => { if (sandboxExecEnvironmentAcquired) { @@ -1914,7 +1925,7 @@ export async function runCodexAppServerAttempt( aborted: runAbortController.signal.aborted, promptError: turnStartErrorMessage, }); - trajectoryEndRecorded = true; + markTrajectoryEndRecorded(); runAgentHarnessLlmOutputHook({ event: { runId: params.runId, @@ -1979,8 +1990,7 @@ export async function runCodexAppServerAttempt( }, }); params.abortSignal?.removeEventListener("abort", abortFromUpstream); - releaseSharedClientLease?.(); - releaseSharedClientLease = undefined; + releaseSharedClientLeaseOnce(); if (usageLimitError) { await markCodexAuthProfileBlockedFromRateLimits({ params, @@ -2000,8 +2010,7 @@ export async function runCodexAppServerAttempt( } } if (!turn) { - releaseSharedClientLease?.(); - releaseSharedClientLease = undefined; + releaseSharedClientLeaseOnce(); throw new Error("codex app-server turn/start failed without an error"); } turnIdRef.current = turn.turn.id; @@ -2250,7 +2259,7 @@ export async function runCodexAppServerAttempt( yieldDetected, promptError: normalizeCodexTrajectoryError(finalPromptError), }); - trajectoryEndRecorded = true; + markTrajectoryEndRecorded(); await mirrorTranscriptBestEffort({ params, agentId: sessionAgentId, @@ -2427,7 +2436,7 @@ export async function runCodexAppServerAttempt( notificationCleanup(); requestCleanup(); closeCleanup?.(); - releaseSharedClientLease?.(); + releaseSharedClientLeaseOnce(); if (nativeHookRelay) { if (shouldDelayNativeHookRelayUnregister) { // Codex hook subprocesses can outlive a completed app-server turn by a diff --git a/extensions/copilot/src/tool-bridge.ts b/extensions/copilot/src/tool-bridge.ts index b0599ce88b2..58442c4f6aa 100644 --- a/extensions/copilot/src/tool-bridge.ts +++ b/extensions/copilot/src/tool-bridge.ts @@ -420,7 +420,7 @@ export function convertOpenClawToolToSdkTool( ); } - let preparedArgs = args; + let preparedArgs; try { preparedArgs = sourceTool.prepareArguments ? sourceTool.prepareArguments(args) : args; } catch (error: unknown) { diff --git a/extensions/discord/src/doctor-contract.ts b/extensions/discord/src/doctor-contract.ts index 1c7f65e0875..0b1d67251f8 100644 --- a/extensions/discord/src/doctor-contract.ts +++ b/extensions/discord/src/doctor-contract.ts @@ -478,8 +478,8 @@ export function normalizeCompatibilityConfig({ } const changes: string[] = []; - let updated = rawEntry; - let changed = false; + let updated; + let changed; const bindingsToAdd: AgentBindingConfig[] = []; const aliases = normalizeLegacyChannelAliases({ diff --git a/extensions/discord/src/monitor/auto-presence.ts b/extensions/discord/src/monitor/auto-presence.ts index c035e941422..b3b0fd61770 100644 --- a/extensions/discord/src/monitor/auto-presence.ts +++ b/extensions/discord/src/monitor/auto-presence.ts @@ -298,7 +298,7 @@ export function createDiscordAutoPresenceController(params: { let lastAppliedAt = 0; const runEvaluation = (options?: { force?: boolean }) => { - let decision: DiscordAutoPresenceDecision | null = null; + let decision: DiscordAutoPresenceDecision | null; try { decision = resolveDiscordAutoPresenceDecision({ discordConfig: params.discordConfig, diff --git a/extensions/discord/src/monitor/message-handler.draft-preview.ts b/extensions/discord/src/monitor/message-handler.draft-preview.ts index c1ab9b2672e..46a312c58c0 100644 --- a/extensions/discord/src/monitor/message-handler.draft-preview.ts +++ b/extensions/discord/src/monitor/message-handler.draft-preview.ts @@ -256,7 +256,7 @@ export function createDiscordDraftPreviewController(params: { ); } const alreadyStarted = progressDraftGate.hasStarted; - let progressActive = false; + let progressActive; if (shouldStartDiscordProgressDraftNow(line)) { await progressDraftGate.startNow(); progressActive = progressDraftGate.hasStarted; diff --git a/extensions/discord/src/monitor/message-handler.hydration.ts b/extensions/discord/src/monitor/message-handler.hydration.ts index b9a05a90116..c50632491d1 100644 --- a/extensions/discord/src/monitor/message-handler.hydration.ts +++ b/extensions/discord/src/monitor/message-handler.hydration.ts @@ -146,7 +146,7 @@ function copyRuntimeMessageFields(source: Message, target: Message): void { } function shouldHydrateDiscordMessage(params: { message: Message }) { - let currentText = ""; + let currentText; try { currentText = resolveDiscordMessageText(params.message, { includeForwarded: true, diff --git a/extensions/discord/src/monitor/provider.deploy-errors.ts b/extensions/discord/src/monitor/provider.deploy-errors.ts index f85c8594942..80d9da9b020 100644 --- a/extensions/discord/src/monitor/provider.deploy-errors.ts +++ b/extensions/discord/src/monitor/provider.deploy-errors.ts @@ -336,7 +336,7 @@ export function formatDiscordDeployErrorDetails(err: unknown): string { details.push(`code=${discordCode}`); } if (rawBody !== undefined) { - let bodyText = ""; + let bodyText; try { bodyText = JSON.stringify(rawBody); } catch { diff --git a/extensions/feishu/src/bot-content.ts b/extensions/feishu/src/bot-content.ts index 8cfdc505107..1f8ca47d58c 100644 --- a/extensions/feishu/src/bot-content.ts +++ b/extensions/feishu/src/bot-content.ts @@ -92,7 +92,7 @@ export function resolveFeishuGroupSession(params: { (replyInThread ? messageId : null)) : null; - let peerId = chatId; + let peerId; switch (groupSessionScope) { case "group_sender": peerId = buildFeishuConversationId({ chatId, scope: "group_sender", senderOpenId }); diff --git a/extensions/feishu/src/dedup-migrations.ts b/extensions/feishu/src/dedup-migrations.ts index 8acf6e3e6f3..6e7b1ccc1b9 100644 --- a/extensions/feishu/src/dedup-migrations.ts +++ b/extensions/feishu/src/dedup-migrations.ts @@ -64,7 +64,7 @@ export const detectFeishuLegacyStateMigrations: BundledChannelLegacyStateMigrati stateDir, }) => { const dedupDir = path.join(stateDir, "feishu", "dedup"); - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(dedupDir, { withFileTypes: true }); } catch { diff --git a/extensions/feishu/src/doctor.ts b/extensions/feishu/src/doctor.ts index db548d05b12..1c27e2575ac 100644 --- a/extensions/feishu/src/doctor.ts +++ b/extensions/feishu/src/doctor.ts @@ -400,7 +400,7 @@ function inspectSessionTranscript(params: { return null; } - let raw = ""; + let raw; try { raw = fs.readFileSync(params.transcriptPath, "utf-8"); } catch { diff --git a/extensions/feishu/src/monitor.account.ts b/extensions/feishu/src/monitor.account.ts index 632932e64a9..49cc788ea44 100644 --- a/extensions/feishu/src/monitor.account.ts +++ b/extensions/feishu/src/monitor.account.ts @@ -474,7 +474,7 @@ export async function monitorSingleAccount(params: MonitorSingleAccountParams): log(`feishu[${accountId}]: dedup warmup loaded ${warmupCount} entries from disk`); } - let threadBindingManager: ReturnType | null = null; + let threadBindingManager: ReturnType | null | undefined; try { const eventDispatcher = createEventDispatcher(account); const chatHistories = new Map(); diff --git a/extensions/feishu/src/setup-surface.ts b/extensions/feishu/src/setup-surface.ts index ec42e0304dc..d4c3c711c1d 100644 --- a/extensions/feishu/src/setup-surface.ts +++ b/extensions/feishu/src/setup-surface.ts @@ -347,7 +347,7 @@ async function runNewAppFlow(params: { const targetAccountId = resolveDefaultFeishuAccountId(next); // ----- QR scan flow ----- - let appId: string | null = null; + let appId: string | null; let appSecret: SecretInput | null = null; let appSecretProbeValue: string | null = null; let scanDomain: FeishuDomain | undefined; @@ -366,7 +366,6 @@ async function runNewAppFlow(params: { if (scanResult) { appId = scanResult.appId; appSecret = scanResult.appSecret; - appSecretProbeValue = scanResult.appSecret; scanDomain = scanResult.domain; scanOpenId = scanResult.openId; } else { diff --git a/extensions/file-transfer/src/tools/dir-fetch-tool.ts b/extensions/file-transfer/src/tools/dir-fetch-tool.ts index 4597813806e..68460fe1edc 100644 --- a/extensions/file-transfer/src/tools/dir-fetch-tool.ts +++ b/extensions/file-transfer/src/tools/dir-fetch-tool.ts @@ -588,7 +588,7 @@ export function createDirFetchTool(): AnyAgentTool { throw new Error(`dir.fetch UNCOMPRESSED_TOO_LARGE: ${reason}`); }; for (const { relPath, absPath } of walked) { - let size = 0; + let size; try { const st = await fs.stat(absPath); size = st.size; diff --git a/extensions/google/video-generation-provider.ts b/extensions/google/video-generation-provider.ts index d11f62b8533..057858622b3 100644 --- a/extensions/google/video-generation-provider.ts +++ b/extensions/google/video-generation-provider.ts @@ -531,7 +531,6 @@ export function buildGoogleVideoGenerationProvider(): VideoGenerationProvider { } let generatedVideos = extractGeneratedVideos(operation); if (generatedVideos.length === 0 && !hasReferenceInputs && !usedRestFallback) { - usedRestFallback = true; operation = await generateGoogleVideoViaRest({ baseUrl: restBaseUrl, headers: authHeaders, diff --git a/extensions/googlechat/src/doctor-contract.ts b/extensions/googlechat/src/doctor-contract.ts index aacb9c3e5ef..9923f8f87da 100644 --- a/extensions/googlechat/src/doctor-contract.ts +++ b/extensions/googlechat/src/doctor-contract.ts @@ -128,7 +128,7 @@ export function normalizeCompatibilityConfig({ const changes: string[] = []; let updated = rawEntry; - let changed = false; + let changed; const root = normalizeGoogleChatEntry({ entry: updated, diff --git a/extensions/googlechat/src/google-auth.runtime.ts b/extensions/googlechat/src/google-auth.runtime.ts index 804fadae445..fb9fc30c998 100644 --- a/extensions/googlechat/src/google-auth.runtime.ts +++ b/extensions/googlechat/src/google-auth.runtime.ts @@ -313,7 +313,7 @@ async function readCredentialsFile(filePath: string): Promise> | null = null; + let handle: Awaited> | null; try { handle = await fs.open(resolvedPath, "r"); } catch { diff --git a/extensions/googlechat/src/monitor-webhook.ts b/extensions/googlechat/src/monitor-webhook.ts index 1e06c7b7815..392f9cacd32 100644 --- a/extensions/googlechat/src/monitor-webhook.ts +++ b/extensions/googlechat/src/monitor-webhook.ts @@ -214,8 +214,8 @@ export function createGoogleChatWebhookRequestHandler(params: { inFlightLimiter: params.webhookInFlightLimiter, handle: async ({ targets }) => { const headerBearer = extractBearerToken(req.headers.authorization); - let selectedTarget: WebhookTarget | null = null; - let parsedEvent: GoogleChatEvent | null = null; + let selectedTarget: WebhookTarget | null; + let parsedEvent: GoogleChatEvent | null; const readAndParseEvent = async ( profile: "pre-auth" | "post-auth", ): Promise => { diff --git a/extensions/imessage/src/monitor-reply-cache.ts b/extensions/imessage/src/monitor-reply-cache.ts index 02ae86b41c5..f6ca2627ee6 100644 --- a/extensions/imessage/src/monitor-reply-cache.ts +++ b/extensions/imessage/src/monitor-reply-cache.ts @@ -86,7 +86,7 @@ function readPersistedEntries(): { if (!line) { continue; } - let parsed: Partial | null = null; + let parsed: Partial | null; try { parsed = JSON.parse(line) as Partial; } catch { diff --git a/extensions/imessage/src/send.ts b/extensions/imessage/src/send.ts index 0bda9f74335..2aa66f7df07 100644 --- a/extensions/imessage/src/send.ts +++ b/extensions/imessage/src/send.ts @@ -127,7 +127,7 @@ function isSshIMessageCliWrapper(cliPath: string): boolean { if (cached !== undefined) { return cached; } - let detected = false; + let detected; try { const content = readFileSync(expandCliPathForInspection(cliPath), "utf8"); detected = /\bssh\b[\s\S]*\bimsg\b/u.test(content); @@ -732,7 +732,7 @@ async function trySendAttachmentForTarget(params: { runCliJson: (args: readonly string[]) => Promise>; resolveMessageGuidImpl?: IMessageSendOpts["resolveMessageGuidImpl"]; }): Promise { - let attachmentChatTarget: string | null = null; + let attachmentChatTarget: string | null; try { attachmentChatTarget = await resolveAttachmentChatTarget({ target: params.target, diff --git a/extensions/matrix/src/cli.ts b/extensions/matrix/src/cli.ts index f5bf9585345..f4270e86a5d 100644 --- a/extensions/matrix/src/cli.ts +++ b/extensions/matrix/src/cli.ts @@ -399,10 +399,7 @@ async function addMatrixAccount(params: { } } - let deviceHealth: MatrixCliAccountAddResult["deviceHealth"] = { - currentDeviceId: null, - staleOpenClawDeviceIds: [], - }; + let deviceHealth: MatrixCliAccountAddResult["deviceHealth"]; try { const addedDevices = await listMatrixOwnDevices({ accountId, cfg: updated }); deviceHealth = { diff --git a/extensions/matrix/src/matrix/client/storage.ts b/extensions/matrix/src/matrix/client/storage.ts index 15b0d9bd933..957c8b57d01 100644 --- a/extensions/matrix/src/matrix/client/storage.ts +++ b/extensions/matrix/src/matrix/client/storage.ts @@ -215,7 +215,7 @@ function resolvePreferredMatrixStorageRoot(params: { }; } - let siblingEntries: fs.Dirent[] = []; + let siblingEntries: fs.Dirent[]; try { siblingEntries = fs.readdirSync(parentDir, { withFileTypes: true }); } catch { diff --git a/extensions/matrix/src/matrix/monitor/index.ts b/extensions/matrix/src/matrix/monitor/index.ts index 25a66ea8c3c..a8d25818222 100644 --- a/extensions/matrix/src/matrix/monitor/index.ts +++ b/extensions/matrix/src/matrix/monitor/index.ts @@ -41,7 +41,7 @@ import { } from "../sync-state.js"; import { createMatrixThreadBindingManager } from "../thread-bindings.js"; import { registerMatrixAutoJoin } from "./auto-join.js"; -import { resolveMatrixMonitorConfig, type MatrixResolvedAllowlistEntry } from "./config.js"; +import { resolveMatrixMonitorConfig } from "./config.js"; import { createDirectRoomTracker } from "./direct.js"; import { registerMatrixMonitorEvents } from "./events.js"; import { createMatrixRoomMessageHandler } from "./handler.js"; @@ -168,31 +168,30 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi const allowlistOnly = accountConfig.allowlistOnly === true; const accountAllowBots = accountConfig.allowBots; - let allowFrom: string[] = (accountConfig.dm?.allowFrom ?? []).map(String); - let groupAllowFrom: string[] = (accountConfig.groupAllowFrom ?? []).map(String); - let allowFromResolvedEntries: MatrixResolvedAllowlistEntry[] = []; - let groupAllowFromResolvedEntries: MatrixResolvedAllowlistEntry[] = []; let roomsConfig = accountConfig.groups ?? accountConfig.rooms; let needsRoomAliasesForConfig = false; + const initialAllowFrom = (accountConfig.dm?.allowFrom ?? []).map(String); + const initialGroupAllowFrom = (accountConfig.groupAllowFrom ?? []).map(String); const configuredBotUserIds = resolveConfiguredMatrixBotUserIds({ cfg, accountId: effectiveAccountId, }); - ({ + const { allowFrom, allowFromResolvedEntries, groupAllowFrom, groupAllowFromResolvedEntries, - roomsConfig, + roomsConfig: resolvedRoomsConfig, } = await resolveMatrixMonitorConfig({ cfg, accountId: effectiveAccountId, - allowFrom, - groupAllowFrom, + allowFrom: initialAllowFrom, + groupAllowFrom: initialGroupAllowFrom, roomsConfig, runtime, - })); + }); + roomsConfig = resolvedRoomsConfig; needsRoomAliasesForConfig = Boolean( roomsConfig && Object.keys(roomsConfig).some((key) => key.trim().startsWith("#")), ); diff --git a/extensions/matrix/src/matrix/monitor/legacy-crypto-restore.ts b/extensions/matrix/src/matrix/monitor/legacy-crypto-restore.ts index ef18de7ff29..93e23ca1e22 100644 --- a/extensions/matrix/src/matrix/monitor/legacy-crypto-restore.ts +++ b/extensions/matrix/src/matrix/monitor/legacy-crypto-restore.ts @@ -64,7 +64,7 @@ async function resolvePendingMigrationStatePath(params: { } const accountStorageDir = path.dirname(rootDir); - let siblingEntries: string[] = []; + let siblingEntries: string[]; try { siblingEntries = (await fs.readdir(accountStorageDir, { withFileTypes: true })) .filter((entry) => entry.isDirectory()) diff --git a/extensions/matrix/src/matrix/sdk.ts b/extensions/matrix/src/matrix/sdk.ts index 07d1b43d758..e05daed3ddc 100644 --- a/extensions/matrix/src/matrix/sdk.ts +++ b/extensions/matrix/src/matrix/sdk.ts @@ -1357,7 +1357,7 @@ export class MatrixClient { return await fail("Matrix recovery key is required"); } - let stagedKeyId: string | null = null; + let stagedKeyId: string | null; try { stagedKeyId = (await this.resolveDefaultSecretStorageKeyId(crypto)) ?? null; this.recoveryKeyStore.stageEncodedRecoveryKey({ diff --git a/extensions/matrix/src/matrix/sdk/verification-manager.ts b/extensions/matrix/src/matrix/sdk/verification-manager.ts index e31eabeb24b..c2178e71aba 100644 --- a/extensions/matrix/src/matrix/sdk/verification-manager.ts +++ b/extensions/matrix/src/matrix/sdk/verification-manager.ts @@ -655,7 +655,7 @@ export class MatrixVerificationManager { if (!crypto) { throw new Error("Matrix crypto is not available"); } - let request: MatrixVerificationRequestLike | null = null; + let request: MatrixVerificationRequestLike | null; if (params.ownUser) { request = await crypto.requestOwnUserVerification(); } else if (params.userId && params.deviceId && crypto.requestDeviceVerification) { diff --git a/extensions/mattermost/src/mattermost/interactions.ts b/extensions/mattermost/src/mattermost/interactions.ts index 10170d57ea3..2f7ebc13191 100644 --- a/extensions/mattermost/src/mattermost/interactions.ts +++ b/extensions/mattermost/src/mattermost/interactions.ts @@ -503,8 +503,8 @@ export function createMattermostInteractionHandler(params: { } const userName = payload.user_name ?? payload.user_id; - let originalMessage = ""; - let originalPost: MattermostPost | null = null; + let originalMessage; + let originalPost: MattermostPost | null; let clickedButtonName: string | null = null; try { originalPost = await client.request(`/posts/${payload.post_id}`); diff --git a/extensions/mattermost/src/mattermost/reconnect.ts b/extensions/mattermost/src/mattermost/reconnect.ts index 644be7d4800..57f9634d813 100644 --- a/extensions/mattermost/src/mattermost/reconnect.ts +++ b/extensions/mattermost/src/mattermost/reconnect.ts @@ -33,16 +33,15 @@ export async function runWithReconnect( const { initialDelayMs = 2000, maxDelayMs = 60_000 } = opts; const jitterRatio = Math.max(0, opts.jitterRatio ?? 0); const random = opts.random ?? Math.random; - let retryDelay = initialDelayMs; + const backoff = createReconnectBackoff(initialDelayMs, maxDelayMs); let attempt = 0; while (!opts.abortSignal?.aborted) { - let shouldIncreaseDelay = false; let outcome: ReconnectOutcome = "resolved"; let error: unknown; try { await connectFn(); - retryDelay = initialDelayMs; + backoff.reset(); } catch (err) { if (opts.abortSignal?.aborted) { return; @@ -50,12 +49,11 @@ export async function runWithReconnect( outcome = "rejected"; error = err; opts.onError?.(err); - shouldIncreaseDelay = true; } if (opts.abortSignal?.aborted) { return; } - const delayMs = withJitter(retryDelay, jitterRatio, random); + const delayMs = withJitter(backoff.current(), jitterRatio, random); const shouldReconnect = opts.shouldReconnect?.({ attempt, @@ -68,13 +66,26 @@ export async function runWithReconnect( } opts.onReconnect?.(delayMs); await sleepAbortable(delayMs, opts.abortSignal); - if (shouldIncreaseDelay) { - retryDelay = Math.min(retryDelay * 2, maxDelayMs); + if (outcome === "rejected") { + backoff.increase(); } attempt++; } } +function createReconnectBackoff(initialDelayMs: number, maxDelayMs: number) { + let retryDelay = initialDelayMs; + return { + current: () => retryDelay, + reset: () => { + retryDelay = initialDelayMs; + }, + increase: () => { + retryDelay = Math.min(retryDelay * 2, maxDelayMs); + }, + }; +} + function withJitter(baseMs: number, jitterRatio: number, random: () => number): number { if (jitterRatio <= 0) { return baseMs; diff --git a/extensions/mattermost/src/mattermost/slash-commands.ts b/extensions/mattermost/src/mattermost/slash-commands.ts index eb3494d8cf6..ec3c5778bb9 100644 --- a/extensions/mattermost/src/mattermost/slash-commands.ts +++ b/extensions/mattermost/src/mattermost/slash-commands.ts @@ -261,7 +261,7 @@ export async function registerSlashCommands(params: { } // Fetch existing commands to avoid duplicates - let existing: MattermostCommandResponse[] = []; + let existing: MattermostCommandResponse[]; try { existing = await listMattermostCommands(client, teamId); } catch (err) { diff --git a/extensions/memory-core/src/cli.runtime.ts b/extensions/memory-core/src/cli.runtime.ts index 281bc7c4244..f47d5eb5b8f 100644 --- a/extensions/memory-core/src/cli.runtime.ts +++ b/extensions/memory-core/src/cli.runtime.ts @@ -555,7 +555,7 @@ async function scanMemoryFiles( } } - let dirReadable: boolean | null = null; + let dirReadable: boolean | null; try { await fs.access(memoryDir, fsSync.constants.R_OK); dirReadable = true; @@ -587,7 +587,7 @@ async function scanMemoryFiles( } } - let totalFiles: number | null = 0; + let totalFiles: number | null; if (dirReadable === null) { totalFiles = null; } else { diff --git a/extensions/memory-core/src/dreaming-narrative.ts b/extensions/memory-core/src/dreaming-narrative.ts index 9359dd38469..93d974abf9b 100644 --- a/extensions/memory-core/src/dreaming-narrative.ts +++ b/extensions/memory-core/src/dreaming-narrative.ts @@ -819,7 +819,7 @@ async function normalizeSessionEntryPathForComparison(params: { async function scrubDreamingNarrativeArtifacts(logger: Logger): Promise { const cfg = getRuntimeConfig(); const agentsDir = path.join(resolveStateDir(), "agents"); - let agentEntries: Dirent[] = []; + let agentEntries: Dirent[]; try { agentEntries = await fs.readdir(agentsDir, { withFileTypes: true }); } catch { @@ -894,7 +894,7 @@ async function scrubDreamingNarrativeArtifacts(logger: Logger): Promise { }); } - let sessionFiles: Dirent[] = []; + let sessionFiles: Dirent[]; try { sessionFiles = await fs.readdir(sessionsDir, { withFileTypes: true }); } catch { @@ -923,7 +923,7 @@ async function scrubDreamingNarrativeArtifacts(logger: Logger): Promise { if (Date.now() - stat.mtimeMs < DREAMING_ORPHAN_MIN_AGE_MS) { continue; } - let content = ""; + let content; try { content = await fs.readFile(transcriptPath, "utf-8"); } catch { diff --git a/extensions/memory-core/src/dreaming-phases.test.ts b/extensions/memory-core/src/dreaming-phases.test.ts index a93f9c27037..24de3d69a57 100644 --- a/extensions/memory-core/src/dreaming-phases.test.ts +++ b/extensions/memory-core/src/dreaming-phases.test.ts @@ -884,7 +884,7 @@ describe("memory-core dreaming phases", () => { ); const readSpy = vi.spyOn(fs, "readFile"); - let transcriptReadCount = 0; + let transcriptReadCount; try { await withDreamingTestClock(async () => { await triggerLightDreaming(beforeAgentReply, workspaceDir, 5); diff --git a/extensions/memory-core/src/memory/index.test.ts b/extensions/memory-core/src/memory/index.test.ts index b41778e1e67..c9143742a61 100644 --- a/extensions/memory-core/src/memory/index.test.ts +++ b/extensions/memory-core/src/memory/index.test.ts @@ -504,7 +504,7 @@ describe("memory index", () => { managersForCleanup.add(first); await first.probeEmbeddingAvailability(); const closePromise = closeMemoryIndexManagersForAgent({ cfg, agentId: "main" }); - let second: MemoryIndexManager | null = null; + let second: MemoryIndexManager | null; try { await vi.waitFor(() => { expect(providerCloseCalls).toBe(1); diff --git a/extensions/memory-core/src/memory/manager-embedding-policy.ts b/extensions/memory-core/src/memory/manager-embedding-policy.ts index 3978fc902d0..30d01613518 100644 --- a/extensions/memory-core/src/memory/manager-embedding-policy.ts +++ b/extensions/memory-core/src/memory/manager-embedding-policy.ts @@ -125,9 +125,9 @@ export async function runMemoryEmbeddingRetryLoop(params: { maxAttempts: number; baseDelayMs: number; }): Promise { - let attempt = 1; - let delayMs = params.baseDelayMs; - while (true) { + const attempts = Math.max(1, params.maxAttempts); + for (const attempt of Array.from({ length: attempts }, (_, index) => index + 1)) { + const delayMs = params.baseDelayMs * 2 ** (attempt - 1); try { return await params.run(); } catch (err) { @@ -136,10 +136,9 @@ export async function runMemoryEmbeddingRetryLoop(params: { throw err; } await params.waitForRetry(delayMs); - delayMs *= 2; - attempt += 1; } } + throw new Error("retry loop exhausted"); } export async function runMemoryEmbeddingBatchRetryWithSplit(params: { diff --git a/extensions/memory-core/src/memory/manager-sync-ops.ts b/extensions/memory-core/src/memory/manager-sync-ops.ts index 8958ddd9a7d..288fe9e3f31 100644 --- a/extensions/memory-core/src/memory/manager-sync-ops.ts +++ b/extensions/memory-core/src/memory/manager-sync-ops.ts @@ -282,7 +282,7 @@ export abstract class MemoryManagerSyncOps { `sqlite-vec load timed out after ${Math.round(VECTOR_LOAD_TIMEOUT_MS / 1000)}s`, ); } - let ready = false; + let ready; try { ready = (await this.vectorReady) || false; } catch (err) { @@ -1667,7 +1667,7 @@ export abstract class MemoryManagerSyncOps { this.fts.loadError = undefined; this.ensureSchema(); - let nextMeta: MemoryIndexMeta | null = null; + let nextMeta: MemoryIndexMeta | null; try { nextMeta = await runMemoryAtomicReindex({ diff --git a/extensions/memory-core/src/memory/qmd-manager.ts b/extensions/memory-core/src/memory/qmd-manager.ts index 6e304c3e540..1c92ce1365d 100644 --- a/extensions/memory-core/src/memory/qmd-manager.ts +++ b/extensions/memory-core/src/memory/qmd-manager.ts @@ -2444,7 +2444,7 @@ export class QmdMemoryManager implements MemorySearchManager { return cached; } const db = this.ensureDb(); - let rows: Array<{ collection: string; path: string }> = []; + let rows: Array<{ collection: string; path: string }>; try { rows = db .prepare("SELECT collection, path FROM documents WHERE hash = ? AND active = 1") @@ -2506,7 +2506,7 @@ export class QmdMemoryManager implements MemorySearchManager { return null; } const exactPath = path.normalize(trimmedFile).replace(/\\/g, "/"); - let rows: Array<{ path: string }> = []; + let rows: Array<{ path: string }>; try { const db = this.ensureDb(); const exactRows = db diff --git a/extensions/memory-core/src/rem-harness.ts b/extensions/memory-core/src/rem-harness.ts index 62d40b0dddf..ad7e6fe3bce 100644 --- a/extensions/memory-core/src/rem-harness.ts +++ b/extensions/memory-core/src/rem-harness.ts @@ -82,7 +82,7 @@ function createSkippedRemPreview(): RemDreamingPreview { async function listWorkspaceDailyFiles(workspaceDir: string, limit?: number): Promise { const memoryDir = path.join(workspaceDir, "memory"); - let entries: string[] = []; + let entries: string[]; try { const dirEntries = await fs.readdir(memoryDir, { withFileTypes: true }); entries = dirEntries diff --git a/extensions/memory-core/src/short-term-promotion.test.ts b/extensions/memory-core/src/short-term-promotion.test.ts index 5b9a675b096..2c1a2af7cc4 100644 --- a/extensions/memory-core/src/short-term-promotion.test.ts +++ b/extensions/memory-core/src/short-term-promotion.test.ts @@ -587,7 +587,7 @@ describe("short-term promotion", () => { it("lets repeated dreaming-only daily signals clear the default promotion gates", async () => { await withTempWorkspace(async (workspaceDir) => { const queryDays = ["2026-04-01", "2026-04-02", "2026-04-03"]; - let candidateKey = ""; + let candidateKey; for (const [index, day] of queryDays.entries()) { const nowMs = Date.parse(`${day}T10:00:00.000Z`); diff --git a/extensions/memory-wiki/src/bridge.ts b/extensions/memory-wiki/src/bridge.ts index 88acb0815ce..4e0062083b3 100644 --- a/extensions/memory-wiki/src/bridge.ts +++ b/extensions/memory-wiki/src/bridge.ts @@ -225,14 +225,13 @@ export async function syncMemoryWikiBridgeSources(params: { const publicArtifacts = await listActiveMemoryPublicArtifacts({ cfg: params.appConfig }); const state = await readMemoryWikiSourceSyncState(params.config.vault.path); const results: Array<{ pagePath: string; changed: boolean; created: boolean }> = []; - let artifactCount = 0; const activeKeys = new Set(); const artifacts = await collectBridgeArtifacts(params.config.bridge, publicArtifacts); const agentIdsByWorkspace = new Map(); for (const artifact of publicArtifacts) { agentIdsByWorkspace.set(artifact.workspaceDir, artifact.agentIds); } - artifactCount = artifacts.length; + const artifactCount = artifacts.length; for (const artifact of artifacts) { const stats = await fs.stat(artifact.absolutePath); activeKeys.add(artifact.syncKey); diff --git a/extensions/microsoft-foundry/auth.ts b/extensions/microsoft-foundry/auth.ts index 691a6dde7e2..f4b01ab9bab 100644 --- a/extensions/microsoft-foundry/auth.ts +++ b/extensions/microsoft-foundry/auth.ts @@ -52,7 +52,7 @@ export const entraIdAuthMethod: ProviderAuthMethod = { ); } - let account = getLoggedInAccount(); + const account = getLoggedInAccount(); let tenantId = account?.tenantId; if (account) { const useExisting = await ctx.prompter.confirm({ @@ -61,7 +61,6 @@ export const entraIdAuthMethod: ProviderAuthMethod = { }); if (!useExisting) { const loginResult = await loginWithTenantFallback(ctx); - account = loginResult.account; tenantId = loginResult.tenantId ?? loginResult.account?.tenantId; } } else { @@ -70,7 +69,6 @@ export const entraIdAuthMethod: ProviderAuthMethod = { "Azure Login", ); const loginResult = await loginWithTenantFallback(ctx); - account = loginResult.account; tenantId = loginResult.tenantId ?? loginResult.account?.tenantId; } diff --git a/extensions/msteams/doctor-contract-api.ts b/extensions/msteams/doctor-contract-api.ts index 5026a581438..0a2749089ef 100644 --- a/extensions/msteams/doctor-contract-api.ts +++ b/extensions/msteams/doctor-contract-api.ts @@ -107,7 +107,7 @@ async function listLegacyLearningFiles( ): Promise< Array<{ storePath: string; sessionKey: string | null; filePath: string; learnings: string[] }> > { - let entries: Dirent[] = []; + let entries: Dirent[]; try { entries = await fs.readdir(storePath, { withFileTypes: true }); } catch { diff --git a/extensions/msteams/src/messenger.ts b/extensions/msteams/src/messenger.ts index abd463261f0..4ce95cc7f9c 100644 --- a/extensions/msteams/src/messenger.ts +++ b/extensions/msteams/src/messenger.ts @@ -442,8 +442,10 @@ export async function sendMSTeamsMessages(params: { return await sendOnce(); } - let attempt = 1; - while (true) { + for (const attempt of Array.from( + { length: retryOptions.maxAttempts }, + (_, index) => index + 1, + )) { try { return await sendOnce(); } catch (err) { @@ -465,9 +467,9 @@ export async function sendMSTeamsMessages(params: { }); await sleep(delayMs); - attempt = nextAttempt; } } + throw new Error("unreachable Teams send retry loop exit"); }; const sendMessageInContext = async ( diff --git a/extensions/music-generation-providers.live.test.ts b/extensions/music-generation-providers.live.test.ts index 34acfff8888..643167ebcaa 100644 --- a/extensions/music-generation-providers.live.test.ts +++ b/extensions/music-generation-providers.live.test.ts @@ -195,7 +195,7 @@ describeLive("music generation provider live", () => { requireProfileKeys: REQUIRE_PROFILE_KEYS, hasLiveKeys, }); - let authLabel = "unresolved"; + let authLabel; try { const auth = await resolveApiKeyForProvider({ provider: testCase.providerId, diff --git a/extensions/nostr/doctor-contract-api.ts b/extensions/nostr/doctor-contract-api.ts index 1b76b3e6481..67964805481 100644 --- a/extensions/nostr/doctor-contract-api.ts +++ b/extensions/nostr/doctor-contract-api.ts @@ -100,7 +100,7 @@ async function listLegacyFiles(params: { parse: (value: unknown) => unknown; }): Promise> { const dir = path.join(params.stateDir, "nostr"); - let entries: Dirent[] = []; + let entries: Dirent[]; try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { diff --git a/extensions/openrouter/music-generation-provider.ts b/extensions/openrouter/music-generation-provider.ts index c14df85a5f0..2b1142552dc 100644 --- a/extensions/openrouter/music-generation-provider.ts +++ b/extensions/openrouter/music-generation-provider.ts @@ -188,7 +188,6 @@ async function readOpenRouterAudioStream( buffer = lines.pop() ?? ""; for (const line of lines) { if (processOpenRouterSseLine(line.trim(), result)) { - doneSeen = true; await reader.cancel(); return { audioBuffer: Buffer.concat(result.audioBuffers), diff --git a/extensions/openshell/src/backend.e2e.test.ts b/extensions/openshell/src/backend.e2e.test.ts index e29fc00c587..1b215d53b43 100644 --- a/extensions/openshell/src/backend.e2e.test.ts +++ b/extensions/openshell/src/backend.e2e.test.ts @@ -322,7 +322,7 @@ async function runBackendExec(params: { env: {}, usePty: false, }); - let result: ExecResult | null = null; + let result: ExecResult | null | undefined; try { result = await runCommand({ command: execSpec.argv[0] ?? "ssh", @@ -372,7 +372,7 @@ describe("openshell sandbox backend e2e", () => { const scopeKey = `session:openshell-e2e-deny:${scopeSuffix}`; const allowSandboxName = `openclaw-policy-allow-${scopeSuffix}`; const gatewayPort = await allocatePort(); - let hostPolicyServer: HostPolicyServer | null = null; + let hostPolicyServer: HostPolicyServer | null | undefined; const sandboxCfg = { mode: "all" as const, backend: "openshell" as const, diff --git a/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts b/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts index 8bdf9331839..f8c5f608336 100644 --- a/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts +++ b/extensions/qa-lab/src/live-transports/whatsapp/whatsapp-live.runtime.ts @@ -623,8 +623,10 @@ async function restartWhatsAppQaDriverSession(params: { } async function startWhatsAppQaDriverSessionWithRetry(params: { authDir: string }) { - let attempt = 1; - while (true) { + for (const attempt of Array.from( + { length: WHATSAPP_QA_TRANSIENT_DRIVER_ATTEMPTS }, + (_, index) => index + 1, + )) { try { return await startWhatsAppQaDriverSession({ authDir: params.authDir }); } catch (error) { @@ -634,10 +636,10 @@ async function startWhatsAppQaDriverSessionWithRetry(params: { authDir: string } ) { throw error; } - attempt += 1; await new Promise((resolve) => setTimeout(resolve, WHATSAPP_QA_DRIVER_RECONNECT_DELAY_MS)); } } + throw new Error("unreachable WhatsApp QA driver retry loop exit"); } function formatApprovalResultValue(value: unknown) { diff --git a/extensions/qa-lab/src/node-exec.ts b/extensions/qa-lab/src/node-exec.ts index d2cd592221b..1128a3448cf 100644 --- a/extensions/qa-lab/src/node-exec.ts +++ b/extensions/qa-lab/src/node-exec.ts @@ -40,7 +40,7 @@ export async function resolveQaNodeExecPath(params?: { const locator = platform === "win32" ? "where" : "which"; const execFileImpl = params?.execFileImpl ?? execFileAsync; - let stdout = ""; + let stdout; try { ({ stdout } = await execFileImpl(locator, ["node"], { encoding: "utf8", diff --git a/extensions/qa-lab/src/providers/mock-openai/server.ts b/extensions/qa-lab/src/providers/mock-openai/server.ts index 8019b6c6315..2e16b9dbc1b 100644 --- a/extensions/qa-lab/src/providers/mock-openai/server.ts +++ b/extensions/qa-lab/src/providers/mock-openai/server.ts @@ -3187,7 +3187,7 @@ export async function startQaMockOpenAiServer(params?: { host?: string; port?: n } if (req.method === "POST" && url.pathname === "/v1/messages") { const raw = await readBody(req); - let body: AnthropicMessagesRequest = {}; + let body: AnthropicMessagesRequest; try { body = raw ? (JSON.parse(raw) as AnthropicMessagesRequest) : {}; } catch { diff --git a/extensions/qa-matrix/src/runners/contract/runtime.ts b/extensions/qa-matrix/src/runners/contract/runtime.ts index 71ebeb2d028..3a9a5eb6fd2 100644 --- a/extensions/qa-matrix/src/runners/contract/runtime.ts +++ b/extensions/qa-matrix/src/runners/contract/runtime.ts @@ -704,7 +704,7 @@ export async function runMatrixQaLive(params: { const syncState: { driver?: string; observer?: string } = {}; const syncStreams: MatrixQaSyncStreams = {}; let canaryMs: number | undefined; - let initialGatewayBootMs = 0; + let initialGatewayBootMs; let scenarioGatewayBootMs = 0; let scenarioRestartGatewayMs = 0; let scenarioTransportInterruptMs = 0; diff --git a/extensions/qa-matrix/src/runners/contract/scenario-runtime-e2ee.ts b/extensions/qa-matrix/src/runners/contract/scenario-runtime-e2ee.ts index dd44c30da4a..71c39d1eac3 100644 --- a/extensions/qa-matrix/src/runners/contract/scenario-runtime-e2ee.ts +++ b/extensions/qa-matrix/src/runners/contract/scenario-runtime-e2ee.ts @@ -1239,7 +1239,7 @@ async function withMatrixQaIsolatedE2eeDriverRoom( ); }; - let patchedGateway = false; + let patchedGateway; let client: MatrixQaE2eeScenarioClient | undefined; try { await applyPatch({ diff --git a/extensions/qa-matrix/src/substrate/request.ts b/extensions/qa-matrix/src/substrate/request.ts index 5adf728eb10..fdac0e1db6b 100644 --- a/extensions/qa-matrix/src/substrate/request.ts +++ b/extensions/qa-matrix/src/substrate/request.ts @@ -34,7 +34,7 @@ export async function requestMatrixJson(params: { ...(params.body !== undefined ? { body: JSON.stringify(params.body) } : {}), signal: AbortSignal.timeout(resolveTimerTimeoutMs(params.timeoutMs, 20_000)), }); - let body: unknown = {}; + let body: unknown; try { body = (await response.json()) as unknown; } catch { diff --git a/extensions/qqbot/src/engine/config/resolve.ts b/extensions/qqbot/src/engine/config/resolve.ts index 433d6cb411f..e66dac02f6b 100644 --- a/extensions/qqbot/src/engine/config/resolve.ts +++ b/extensions/qqbot/src/engine/config/resolve.ts @@ -142,8 +142,8 @@ export function resolveAccountBase( const resolvedAccountId = accountId ?? resolveDefaultAccountId(cfg); const qqbot = readQQBotSection(cfg); - let accountConfig: Record = {}; - let appId = ""; + let accountConfig: Record; + let appId; if (resolvedAccountId === DEFAULT_ACCOUNT_ID) { accountConfig = normalizeAccountConfig(asRecord(qqbot)); diff --git a/extensions/signal/src/monitor/event-handler.ts b/extensions/signal/src/monitor/event-handler.ts index c5be8fc5c10..ccdb25f511d 100644 --- a/extensions/signal/src/monitor/event-handler.ts +++ b/extensions/signal/src/monitor/event-handler.ts @@ -554,7 +554,7 @@ export function createSignalEventHandler(deps: SignalEventHandlerDeps) { return; } - let payload: SignalReceivePayload | null = null; + let payload: SignalReceivePayload | null; try { payload = JSON.parse(event.data) as SignalReceivePayload; } catch (err) { diff --git a/extensions/slack/src/doctor-contract.ts b/extensions/slack/src/doctor-contract.ts index 613650d60af..6d8b9b97543 100644 --- a/extensions/slack/src/doctor-contract.ts +++ b/extensions/slack/src/doctor-contract.ts @@ -99,8 +99,8 @@ export function normalizeCompatibilityConfig({ } const changes: string[] = []; - let updated = rawEntry; - let changed = false; + let updated; + let changed; const aliases = normalizeLegacyChannelAliases({ entry: rawEntry, diff --git a/extensions/slack/src/monitor/auth.ts b/extensions/slack/src/monitor/auth.ts index df1946b61ff..cdfe747d894 100644 --- a/extensions/slack/src/monitor/auth.ts +++ b/extensions/slack/src/monitor/auth.ts @@ -187,7 +187,7 @@ export async function resolveSlackEffectiveAllowFrom( if (options?.includePairingStore !== true) { return base; } - let storeAllowFrom: string[] = []; + let storeAllowFrom: string[]; try { const resolved = await readChannelIngressStoreAllowFromForDmPolicy({ provider: "slack", diff --git a/extensions/slack/src/monitor/external-arg-menu-store.ts b/extensions/slack/src/monitor/external-arg-menu-store.ts index 8427d0fb94a..e88e40a9e7f 100644 --- a/extensions/slack/src/monitor/external-arg-menu-store.ts +++ b/extensions/slack/src/monitor/external-arg-menu-store.ts @@ -39,7 +39,7 @@ function pruneSlackExternalArgMenuStore( } function createSlackExternalArgMenuToken(store: Map): string { - let token = ""; + let token; do { token = generateSecureToken(SLACK_EXTERNAL_ARG_MENU_TOKEN_BYTES); } while (store.has(token)); diff --git a/extensions/slack/src/monitor/message-handler/dispatch.ts b/extensions/slack/src/monitor/message-handler/dispatch.ts index 3a492973648..af7f35573ce 100644 --- a/extensions/slack/src/monitor/message-handler/dispatch.ts +++ b/extensions/slack/src/monitor/message-handler/dispatch.ts @@ -1405,7 +1405,7 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag return session; })(); nativeProgressStreamStartPromise = startPromise; - let startedSession: SlackStreamSession | null = null; + let startedSession: SlackStreamSession | null; try { startedSession = await startPromise; } finally { diff --git a/extensions/slack/src/send.ts b/extensions/slack/src/send.ts index 7f127ab7c8d..0abac094363 100644 --- a/extensions/slack/src/send.ts +++ b/extensions/slack/src/send.ts @@ -276,7 +276,7 @@ function delaySlackDnsRetry(attempt: number): Promise { } async function withSlackDnsRequestRetry(operation: string, fn: () => Promise): Promise { - for (let attempt = 0; ; attempt += 1) { + for (const attempt of Array.from({ length: SLACK_DNS_RETRY_ATTEMPTS + 1 }, (_, index) => index)) { try { return await fn(); } catch (err) { @@ -289,6 +289,7 @@ async function withSlackDnsRequestRetry(operation: string, fn: () => Promise< await delaySlackDnsRetry(attempt + 1); } } + throw new Error("unreachable Slack DNS retry loop exit"); } function isSlackCustomizeScopeError(err: unknown): boolean { diff --git a/extensions/sms/src/status.ts b/extensions/sms/src/status.ts index 0db251dfe3c..e61e2deb6fd 100644 --- a/extensions/sms/src/status.ts +++ b/extensions/sms/src/status.ts @@ -100,7 +100,7 @@ type ProbeOptions = { }; function addTailscaleHint(account: ResolvedSmsAccount, hints: string[]): void { - let host = ""; + let host; try { host = new URL(account.publicWebhookUrl).hostname; } catch { diff --git a/extensions/synology-chat/src/webhook-handler.ts b/extensions/synology-chat/src/webhook-handler.ts index af51a6f56c2..b851ed29f40 100644 --- a/extensions/synology-chat/src/webhook-handler.ts +++ b/extensions/synology-chat/src/webhook-handler.ts @@ -276,7 +276,7 @@ function extractTokenFromHeaders(req: IncomingMessage): string | undefined { function parsePayload(req: IncomingMessage, body: string): SynologyWebhookPayload | null { const contentType = normalizeLowercaseStringOrEmpty(req.headers["content-type"]); - let bodyFields: Record = {}; + let bodyFields: Record; if (contentType.includes("application/json")) { bodyFields = parseJsonBody(body); } else if (contentType.includes("application/x-www-form-urlencoded")) { @@ -390,7 +390,7 @@ async function parseWebhookPayloadRequest(params: { return { ok: false }; } - let payload: SynologyWebhookPayload | null = null; + let payload: SynologyWebhookPayload | null; try { payload = parsePayload(params.req, bodyResult.body); } catch (err) { diff --git a/extensions/telegram/src/bot-core.ts b/extensions/telegram/src/bot-core.ts index a1e8fcb1556..425aa512b12 100644 --- a/extensions/telegram/src/bot-core.ts +++ b/extensions/telegram/src/bot-core.ts @@ -210,12 +210,12 @@ export function createTelegramBotCore( if (!begin.accepted) { return; } - let completed = false; try { await next(); - completed = true; - } finally { - updateTracker.finishUpdate(begin.update, { completed }); + updateTracker.finishUpdate(begin.update, { completed: true }); + } catch (error) { + updateTracker.finishUpdate(begin.update, { completed: false }); + throw error; } }); diff --git a/extensions/telegram/src/bot-handlers.runtime.ts b/extensions/telegram/src/bot-handlers.runtime.ts index 4ed2e3ac469..7e9c20e5592 100644 --- a/extensions/telegram/src/bot-handlers.runtime.ts +++ b/extensions/telegram/src/bot-handlers.runtime.ts @@ -1850,7 +1850,7 @@ export const registerTelegramHandlers = ({ return; } - let media: Awaited> = null; + let media: Awaited>; try { media = await resolveMedia({ ctx, diff --git a/extensions/telegram/src/bot-message-dispatch.ts b/extensions/telegram/src/bot-message-dispatch.ts index 7aef61810a2..e845e78373b 100644 --- a/extensions/telegram/src/bot-message-dispatch.ts +++ b/extensions/telegram/src/bot-message-dispatch.ts @@ -782,7 +782,7 @@ export const dispatchTelegramMessage = async ({ let replyFenceGeneration: number | undefined; const replyAbortController = new AbortController(); let replyAbortControllerQueued = false; - let dispatchWasSuperseded = false; + let dispatchWasSuperseded; const isDispatchSuperseded = () => replyFenceGeneration !== undefined && isTelegramReplyFenceSuperseded({ diff --git a/extensions/telegram/src/bot/helpers.ts b/extensions/telegram/src/bot/helpers.ts index a040dee645e..beb448f5e48 100644 --- a/extensions/telegram/src/bot/helpers.ts +++ b/extensions/telegram/src/bot/helpers.ts @@ -602,7 +602,7 @@ export function describeReplyTarget(msg: Message): TelegramReplyTarget | null { msg.quote ?? (externalReply as (Message & { quote?: Message["quote"] }) | undefined)?.quote; const rawQuoteText = quote?.text; const quoteText = resolveTelegramTextContent(rawQuoteText); - let body = ""; + let body; let kind: TelegramReplyTarget["kind"] = "reply"; const filteredQuoteText = hadUnsafeTelegramText(rawQuoteText, quoteText); diff --git a/extensions/telegram/src/state-migrations.ts b/extensions/telegram/src/state-migrations.ts index c38efa1b664..3d725e6c921 100644 --- a/extensions/telegram/src/state-migrations.ts +++ b/extensions/telegram/src/state-migrations.ts @@ -88,7 +88,7 @@ function listTelegramLegacySidecarAccountIds(params: { prefix: string; suffix: string; }): string[] { - let persistedAccountIds: string[] = []; + let persistedAccountIds: string[]; try { persistedAccountIds = fs .readdirSync(path.join(params.stateDir, "telegram"), { withFileTypes: true }) diff --git a/extensions/tlon/src/monitor/index.ts b/extensions/tlon/src/monitor/index.ts index b6422d0b7a6..28c19febcba 100644 --- a/extensions/tlon/src/monitor/index.ts +++ b/extensions/tlon/src/monitor/index.ts @@ -92,7 +92,10 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise { - for (let attempt = 1; ; attempt++) { + for (const attempt of Array.from( + { length: Math.max(1, maxAttempts) }, + (_, index) => index + 1, + )) { if (opts.abortSignal?.aborted) { throw new Error("Aborted while waiting to authenticate"); } @@ -120,6 +123,7 @@ export async function monitorTlonProvider(opts: MonitorTlonOpts = {}): Promise { - let content = ""; + let content; try { content = await fs.readFile(filePath, "utf8"); } catch { diff --git a/extensions/voice-call/src/providers/twilio.ts b/extensions/voice-call/src/providers/twilio.ts index cab172d863d..cd11c9eb5ac 100644 --- a/extensions/voice-call/src/providers/twilio.ts +++ b/extensions/voice-call/src/providers/twilio.ts @@ -237,23 +237,21 @@ export class TwilioProvider implements VoiceCallProvider { twiml: string, operation: string, ): Promise { - let retryIndex = 0; - while (true) { + for (const retryDelayMs of TWILIO_CALL_UPDATE_RETRY_DELAYS_MS) { try { await this.apiRequest(`/Calls/${providerCallId}.json`, { Twiml: twiml }); return; } catch (err) { - const retryDelayMs = TWILIO_CALL_UPDATE_RETRY_DELAYS_MS[retryIndex]; - if (retryDelayMs === undefined || !isTwilioCallNotInProgressError(err)) { + if (!isTwilioCallNotInProgressError(err)) { throw err; } - retryIndex += 1; console.warn( `[voice-call] Twilio ${operation} update hit call state race (21220); retrying in ${retryDelayMs}ms`, ); await sleep(retryDelayMs); } } + await this.apiRequest(`/Calls/${providerCallId}.json`, { Twiml: twiml }); } /** diff --git a/extensions/voice-call/src/webhook/realtime-audio-pacer.ts b/extensions/voice-call/src/webhook/realtime-audio-pacer.ts index aef61803062..f8ea00ce495 100644 --- a/extensions/voice-call/src/webhook/realtime-audio-pacer.ts +++ b/extensions/voice-call/src/webhook/realtime-audio-pacer.ts @@ -124,7 +124,7 @@ export class RealtimeAudioPacer { } let delayMs = 0; - let sent = true; + let sent; if (item.type === "audio") { this.queuedAudioBytes = Math.max(0, this.queuedAudioBytes - item.chunk.length); sent = this.params.send(this.params.serializer.media(item.chunk.toString("base64"))); diff --git a/packages/agent-core/src/agent-loop.ts b/packages/agent-core/src/agent-loop.ts index 0e35a495681..bb680831687 100644 --- a/packages/agent-core/src/agent-loop.ts +++ b/packages/agent-core/src/agent-loop.ts @@ -245,7 +245,6 @@ async function runLoop( currentContext.messages.push(message); newMessages.push(message); } - pendingMessages = []; } // Stream assistant response diff --git a/qa/convex-credential-broker/convex/credentials.ts b/qa/convex-credential-broker/convex/credentials.ts index 992d1f2c010..cd2246c5b43 100644 --- a/qa/convex-credential-broker/convex/credentials.ts +++ b/qa/convex-credential-broker/convex/credentials.ts @@ -725,7 +725,7 @@ export const listCredentialSets = internalQuery({ ); } - let rows: CredentialSetRecord[] = []; + let rows: CredentialSetRecord[]; const kind = args.kind?.trim(); if (kind) { if (normalizedStatus === "all") { diff --git a/scripts/anthropic-prompt-probe.ts b/scripts/anthropic-prompt-probe.ts index ac648bed5d5..0345754d601 100644 --- a/scripts/anthropic-prompt-probe.ts +++ b/scripts/anthropic-prompt-probe.ts @@ -265,7 +265,7 @@ function extractProxyCapture(rawBody: string, req: http.IncomingMessage): ProxyC let parsed: { system?: Array<{ text?: string }>; messages?: Array<{ role?: string; content?: unknown }>; - } | null = null; + } | null; try { parsed = JSON.parse(rawBody) as typeof parsed; } catch { diff --git a/scripts/audit-seams.mjs b/scripts/audit-seams.mjs index 310492e9723..6ad14966455 100644 --- a/scripts/audit-seams.mjs +++ b/scripts/audit-seams.mjs @@ -125,7 +125,7 @@ async function walkAllCodeFiles(rootDir, options = {}) { const includeTests = options.includeTests === true; async function walk(dir) { - let entries = []; + let entries; try { entries = await fs.readdir(dir, { withFileTypes: true }); } catch { diff --git a/scripts/check-file-utils.ts b/scripts/check-file-utils.ts index 583d2cf9e58..2ffa5f0a122 100644 --- a/scripts/check-file-utils.ts +++ b/scripts/check-file-utils.ts @@ -26,7 +26,7 @@ export function collectFilesSync( if (!current) { continue; } - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(current, { withFileTypes: true }); } catch { diff --git a/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts b/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts index 880ef262ec8..0ca6fd52520 100644 --- a/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts +++ b/scripts/check-no-monolithic-plugin-sdk-entry-imports.ts @@ -53,7 +53,7 @@ function collectSharedExtensionSourceFiles(): string[] { function collectBundledExtensionSourceFiles(): string[] { const extensionsDir = path.join(process.cwd(), "extensions"); - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(extensionsDir, { withFileTypes: true }); } catch { @@ -93,7 +93,7 @@ function main() { const legacyCompatOffenders: string[] = []; const legacyBroadSubpathOffenders = new Map(); for (const entryFile of filesToCheck) { - let content = ""; + let content; try { content = fs.readFileSync(entryFile, "utf8"); } catch { diff --git a/scripts/check-no-raw-http2-imports.mjs b/scripts/check-no-raw-http2-imports.mjs index 5c1b2c8c7d9..79440611834 100644 --- a/scripts/check-no-raw-http2-imports.mjs +++ b/scripts/check-no-raw-http2-imports.mjs @@ -20,7 +20,7 @@ function collectFilesSync(rootDir, options) { if (!current) { continue; } - let entries = []; + let entries; try { entries = fs.readdirSync(current, { withFileTypes: true }); } catch { diff --git a/scripts/control-ui-i18n.ts b/scripts/control-ui-i18n.ts index b6fadb9b7fc..8639a129111 100644 --- a/scripts/control-ui-i18n.ts +++ b/scripts/control-ui-i18n.ts @@ -877,7 +877,7 @@ async function syncControlUiRawCopyBaseline(options: { checkOnly: boolean; write await writeFile(RAW_COPY_BASELINE_PATH, expected, "utf8"); } if (options.checkOnly && current !== expected) { - let currentEntries: RawCopyBaselineEntry[] = []; + let currentEntries: RawCopyBaselineEntry[]; try { const parsed = JSON.parse(current) as Partial; currentEntries = Array.isArray(parsed.entries) ? parsed.entries : []; diff --git a/scripts/crabbox-wrapper.mjs b/scripts/crabbox-wrapper.mjs index d3e803a32cd..34d6110afb4 100755 --- a/scripts/crabbox-wrapper.mjs +++ b/scripts/crabbox-wrapper.mjs @@ -2025,10 +2025,9 @@ let childCwd = repoRoot; let cleanupChildCwd = () => {}; let cleanupDone = false; let remoteChangedGateBase = ""; -let scriptStdinPrepared = false; const scriptBootstrap = prepareAwsMacosScriptStdinBootstrap(normalizedArgs, provider); normalizedArgs = scriptBootstrap.args; -scriptStdinPrepared = scriptBootstrap.prepared; +const scriptStdinPrepared = scriptBootstrap.prepared; try { if (shouldUseFullCheckoutForCleanSparseRemoteSync(normalizedArgs, provider)) { const runWords = runCommandArgs(normalizedArgs); diff --git a/scripts/dev/discord-acp-plain-language-smoke.ts b/scripts/dev/discord-acp-plain-language-smoke.ts index 5e0acf8c3d0..39531d9dacb 100644 --- a/scripts/dev/discord-acp-plain-language-smoke.ts +++ b/scripts/dev/discord-acp-plain-language-smoke.ts @@ -761,10 +761,10 @@ async function run(): Promise { }); let readAuthHeader = ""; - let sentMessageId = ""; + let sentMessageId; let setupStage: "discord-api" | "send-message" = "discord-api"; let senderAuthorId: string | undefined; - let minBindingBoundAt = startedAt - 3_000; + let minBindingBoundAt; let webhookForCleanup: WebhookForCleanup | undefined; try { diff --git a/scripts/dev/gateway-ws-client.ts b/scripts/dev/gateway-ws-client.ts index 2e0a5f21e2d..ff172e4b715 100644 --- a/scripts/dev/gateway-ws-client.ts +++ b/scripts/dev/gateway-ws-client.ts @@ -124,7 +124,7 @@ export function createGatewayWsClient(params: { ws.on("message", (data) => { const text = toText(data); - let frame: GatewayFrame | null = null; + let frame: GatewayFrame | null; try { frame = JSON.parse(text) as GatewayFrame; } catch { diff --git a/scripts/docs-sync-publish.mjs b/scripts/docs-sync-publish.mjs index f88bc7a80b8..e1bf3db1193 100644 --- a/scripts/docs-sync-publish.mjs +++ b/scripts/docs-sync-publish.mjs @@ -542,7 +542,7 @@ function rewriteClawHubMarkdownLinkTarget(rawTarget, relativeSourceDir, source) return rawTarget; } - let normalizedRelative = ""; + let normalizedRelative; if (pathPart.startsWith("docs/")) { normalizedRelative = normalizeSlashes(pathPart.slice("docs/".length)); } else if ( diff --git a/scripts/e2e/lib/openai-chat-tools/client.mjs b/scripts/e2e/lib/openai-chat-tools/client.mjs index 44dd37de6a3..428f7d380bc 100644 --- a/scripts/e2e/lib/openai-chat-tools/client.mjs +++ b/scripts/e2e/lib/openai-chat-tools/client.mjs @@ -123,7 +123,7 @@ if (toolCall?.type !== "function" || toolCall?.function?.name !== "get_weather") throw new Error(`unexpected tool call: ${JSON.stringify(toolCall)}`); } -let args = {}; +let args; try { args = JSON.parse(toolCall.function.arguments || "{}"); } catch { diff --git a/scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs b/scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs index 1b56acdb312..a8f9939d853 100644 --- a/scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs +++ b/scripts/e2e/lib/openai-web-search-minimal/mock-server.mjs @@ -97,7 +97,7 @@ const server = http.createServer((req, res) => { } const bodyText = await readBody(req); - let body = {}; + let body; try { body = bodyText ? JSON.parse(bodyText) : {}; } catch { diff --git a/scripts/e2e/mock-openai-server.mjs b/scripts/e2e/mock-openai-server.mjs index 8c8787fe12a..516805789ee 100644 --- a/scripts/e2e/mock-openai-server.mjs +++ b/scripts/e2e/mock-openai-server.mjs @@ -305,7 +305,7 @@ const server = http.createServer((req, res) => { `${JSON.stringify({ method: req.method, path: url.pathname, body: bodyText })}\n`, ); } - let body = {}; + let body; try { body = bodyText ? JSON.parse(bodyText) : {}; } catch { diff --git a/scripts/e2e/parallels/package-artifact.ts b/scripts/e2e/parallels/package-artifact.ts index 3cebb333359..6b1748785e5 100644 --- a/scripts/e2e/parallels/package-artifact.ts +++ b/scripts/e2e/parallels/package-artifact.ts @@ -198,7 +198,8 @@ async function acquirePackageLock(lockDir: string, ownerToken: string): Promise< const timeoutMs = readPositiveIntEnv("OPENCLAW_PARALLELS_PACKAGE_LOCK_TIMEOUT_MS", 30 * 60_000); const staleMs = readPositiveIntEnv("OPENCLAW_PARALLELS_PACKAGE_LOCK_STALE_MS", 2 * 60 * 60_000); const startedAt = Date.now(); - let announcedWait = false; + let waitAnnouncementBudget = 1; + const consumeWaitAnnouncement = () => waitAnnouncementBudget-- > 0; while (Date.now() - startedAt < timeoutMs) { try { await mkdir(lockDir); @@ -210,9 +211,8 @@ async function acquirePackageLock(lockDir: string, ownerToken: string): Promise< } } await removeStalePackageLock(lockDir, staleMs); - if (!announcedWait) { + if (consumeWaitAnnouncement()) { say(`Wait for Parallels package lock: ${lockDir}`); - announcedWait = true; } await delay(1_000); } diff --git a/scripts/e2e/telegram-user-crabbox-proof.ts b/scripts/e2e/telegram-user-crabbox-proof.ts index b49bf45a8b7..7c8306db3d4 100644 --- a/scripts/e2e/telegram-user-crabbox-proof.ts +++ b/scripts/e2e/telegram-user-crabbox-proof.ts @@ -765,7 +765,7 @@ export function readLogTail(logPath: string, maxBytes = LOG_READY_TAIL_BYTES): s const bytesToRead = Math.min(Math.max(1, maxBytes), stat.size); const buffer = Buffer.alloc(bytesToRead); const fd = fs.openSync(logPath, "r"); - let bytesRead = 0; + let bytesRead; try { bytesRead = fs.readSync(fd, buffer, 0, bytesToRead, stat.size - bytesToRead); } finally { diff --git a/scripts/firecrawl-compare.ts b/scripts/firecrawl-compare.ts index c14424ba485..5082b397e3b 100644 --- a/scripts/firecrawl-compare.ts +++ b/scripts/firecrawl-compare.ts @@ -91,7 +91,7 @@ async function run() { for (const url of targets) { console.log(`\n=== ${url}`); - let localStatus = "skipped"; + let localStatus; let localTitle = ""; let localText = ""; let localError: string | undefined; diff --git a/scripts/full-release-validation-at-sha.mjs b/scripts/full-release-validation-at-sha.mjs index c2117872204..73ed28b184a 100755 --- a/scripts/full-release-validation-at-sha.mjs +++ b/scripts/full-release-validation-at-sha.mjs @@ -202,7 +202,7 @@ function main() { stdio: "inherit", }); - let parentRunId = ""; + let parentRunId; try { const dispatchArgs = ["workflow", "run", WORKFLOW, "--ref", branch]; for (const [key, value] of Object.entries(dispatchInputs)) { diff --git a/scripts/generate-npm-shrinkwrap.mjs b/scripts/generate-npm-shrinkwrap.mjs index a98a573c1dd..588fa1e4297 100644 --- a/scripts/generate-npm-shrinkwrap.mjs +++ b/scripts/generate-npm-shrinkwrap.mjs @@ -1234,7 +1234,7 @@ function updateOrCheckPackage(packageDir, check, changedPaths = []) { return; } - let current = ""; + let current; try { current = readFileSync(shrinkwrapPath, "utf8"); } catch { diff --git a/scripts/ios-pin-version.ts b/scripts/ios-pin-version.ts index 2669f815d30..a3dfe0bf62c 100644 --- a/scripts/ios-pin-version.ts +++ b/scripts/ios-pin-version.ts @@ -87,7 +87,7 @@ export function parseArgs(argv: string[]): CliOptions { export function pinIosVersion(params: CliOptions): PinIosVersionResult { const rootDir = path.resolve(params.rootDir); - let previousVersion: string | null = null; + let previousVersion: string | null; try { previousVersion = resolveIosVersion(rootDir).canonicalVersion; } catch { diff --git a/scripts/lib/local-heavy-check-runtime.mjs b/scripts/lib/local-heavy-check-runtime.mjs index 6beb8bf58d1..e2ca524ed9b 100644 --- a/scripts/lib/local-heavy-check-runtime.mjs +++ b/scripts/lib/local-heavy-check-runtime.mjs @@ -212,8 +212,16 @@ export function acquireLocalHeavyCheckLockSync(params) { DEFAULT_STALE_LOCK_MS, ); const startedAt = Date.now(); - let waitingLogged = false; - let lastProgressAt = 0; + let waitLogBudget = 1; + let lastProgressAt = startedAt; + const consumeInitialWaitLog = () => waitLogBudget-- > 0; + const consumeProgressLog = (now) => { + if (now - lastProgressAt < progressMs) { + return false; + } + lastProgressAt = now; + return true; + }; fs.mkdirSync(locksDir, { recursive: true }); if (!params.lockName) { @@ -255,23 +263,20 @@ export function acquireLocalHeavyCheckLockSync(params) { ); } - if (!waitingLogged) { + if (consumeInitialWaitLog()) { const ownerLabel = describeOwner(owner); console.error( `[${params.toolName}] queued behind the local heavy-check lock${ ownerLabel ? ` held by ${ownerLabel}` : "" }...`, ); - waitingLogged = true; - lastProgressAt = Date.now(); - } else if (Date.now() - lastProgressAt >= progressMs) { + } else if (consumeProgressLog(Date.now())) { const ownerLabel = describeOwner(owner); console.error( `[${params.toolName}] still waiting ${formatElapsedMs(elapsedMs)} for the local heavy-check lock${ ownerLabel ? ` held by ${ownerLabel}` : "" }...`, ); - lastProgressAt = Date.now(); } sleepSync(pollMs); diff --git a/scripts/lib/source-file-scan-cache.mjs b/scripts/lib/source-file-scan-cache.mjs index 28da5c8ccef..5d1bec61975 100644 --- a/scripts/lib/source-file-scan-cache.mjs +++ b/scripts/lib/source-file-scan-cache.mjs @@ -10,7 +10,7 @@ function normalizeRepoPath(repoRoot, filePath) { async function walkFiles(params, rootDir) { const out = []; - let entries = []; + let entries; try { entries = await fs.readdir(rootDir, { withFileTypes: true }); } catch (error) { @@ -90,18 +90,14 @@ export async function collectSourceFileContents(params) { normalizeRepoPath(params.repoRoot, left).localeCompare( normalizeRepoPath(params.repoRoot, right), ), - ); + ); const readFile = params.readFile ?? fs.readFile; - return await mapWithConcurrency( - files, - params.maxConcurrentReads, - async (filePath) => ({ - filePath, - relativeFile: normalizeRepoPath(params.repoRoot, filePath), - content: await readFile(filePath, "utf8"), - }), - ); + return await mapWithConcurrency(files, params.maxConcurrentReads, async (filePath) => ({ + filePath, + relativeFile: normalizeRepoPath(params.repoRoot, filePath), + content: await readFile(filePath, "utf8"), + })); })(); if (useCache) { diff --git a/scripts/openclaw-cross-os-release-checks.ts b/scripts/openclaw-cross-os-release-checks.ts index c23af238145..65e2e0c763b 100644 --- a/scripts/openclaw-cross-os-release-checks.ts +++ b/scripts/openclaw-cross-os-release-checks.ts @@ -654,7 +654,7 @@ function collectLegacyPluginDependencyStagingDebrisPaths(packageRoot) { continue; } const distDir = join(packageRoot, rootEntry.name); - let distEntries = []; + let distEntries; try { distEntries = readdirSync(distDir, { withFileTypes: true }); } catch (error) { @@ -668,7 +668,7 @@ function collectLegacyPluginDependencyStagingDebrisPaths(packageRoot) { continue; } const extensionsDir = join(distDir, distEntry.name); - let extensionEntries = []; + let extensionEntries; try { extensionEntries = readdirSync(extensionsDir, { withFileTypes: true }); } catch (error) { @@ -683,7 +683,7 @@ function collectLegacyPluginDependencyStagingDebrisPaths(packageRoot) { continue; } const extensionPath = join(extensionsDir, extensionEntry.name); - let stagingEntries = []; + let stagingEntries; try { stagingEntries = readdirSync(extensionPath, { withFileTypes: true }); } catch (error) { @@ -2311,7 +2311,7 @@ async function runInstalledAgentTurn(params) { } export function verifyDevUpdateStatus(stdout, options = {}) { - let payload = null; + let payload; try { payload = JSON.parse(stdout); } catch { diff --git a/scripts/openclaw-npm-release-check.ts b/scripts/openclaw-npm-release-check.ts index 0c93d73e248..eae4775a176 100644 --- a/scripts/openclaw-npm-release-check.ts +++ b/scripts/openclaw-npm-release-check.ts @@ -648,7 +648,7 @@ export function collectControlUiPackErrors(paths: Iterable): string[] { function collectPackedTarballErrors(): string[] { const errors: string[] = []; - let stdout = ""; + let stdout; try { stdout = runNpmCommand(["pack", "--json", "--dry-run", "--ignore-scripts"]); } catch (error) { diff --git a/scripts/package-openclaw-for-docker.mjs b/scripts/package-openclaw-for-docker.mjs index 4b04e17ed6d..8670e519c48 100644 --- a/scripts/package-openclaw-for-docker.mjs +++ b/scripts/package-openclaw-for-docker.mjs @@ -262,7 +262,7 @@ export async function packOpenClawPackageForDocker(sourceDir, outputDir, options const restoreChangelog = options.restoreChangelog ?? restorePackageChangelog; console.error("==> Packing OpenClaw package"); await prepareChangelog(sourceDir); - let packOutput = ""; + let packOutput; try { packOutput = await runCaptureImpl( "npm", diff --git a/scripts/resolve-openclaw-package-candidate.mjs b/scripts/resolve-openclaw-package-candidate.mjs index 3d779f534d5..13a454519be 100644 --- a/scripts/resolve-openclaw-package-candidate.mjs +++ b/scripts/resolve-openclaw-package-candidate.mjs @@ -269,7 +269,7 @@ async function findSingleTarball(dir) { export async function readArtifactPackageCandidateMetadata(dir) { const metadataPath = path.join(path.resolve(ROOT_DIR, dir), "package-candidate.json"); - let raw = ""; + let raw; try { raw = await fs.readFile(metadataPath, "utf8"); } catch (error) { diff --git a/scripts/run-node.mjs b/scripts/run-node.mjs index d99d05cc731..e7511312c8c 100644 --- a/scripts/run-node.mjs +++ b/scripts/run-node.mjs @@ -96,7 +96,7 @@ const findLatestMtime = (dirPath, shouldSkip, deps) => { if (!current) { continue; } - let entries = []; + let entries; try { entries = deps.fs.readdirSync(current, { withFileTypes: true }); } catch { @@ -344,7 +344,7 @@ const listBuiltBundledPluginEntries = (deps) => { const listBuiltBundledPluginRuntimeOverlayDirs = (deps) => { const distExtensionsRoot = path.join(resolveRuntimePostBuildDistRoot(deps), "extensions"); - let entries = []; + let entries; try { entries = deps.fs.readdirSync(distExtensionsRoot, { withFileTypes: true }); } catch { @@ -377,7 +377,7 @@ const listRuntimeOverlaySourcePaths = (sourceDir, deps) => { if (!current) { continue; } - let entries = []; + let entries; try { entries = deps.fs.readdirSync(current, { withFileTypes: true }); } catch { @@ -452,7 +452,7 @@ const listRequiredOpenClawExtensionAliasOutputs = (deps) => { return []; } const pluginSdkDir = path.join(distRoot, "plugin-sdk"); - let dirents = []; + let dirents; try { dirents = deps.fs.readdirSync(pluginSdkDir, { withFileTypes: true }); } catch { @@ -838,7 +838,7 @@ const parsePositiveInteger = (value) => { }; const listRunNodeCpuProfiles = (deps, absoluteProfileDir, commandName) => { - let entries = []; + let entries; try { entries = deps.fs.readdirSync(absoluteProfileDir, { withFileTypes: true }); } catch { @@ -1145,7 +1145,8 @@ export const acquireRunNodeBuildLock = async (deps) => { DEFAULT_BUILD_LOCK_STALE_MS, ); const startedAt = Date.now(); - let loggedWait = false; + let waitLogBudget = 1; + const consumeWaitLog = () => waitLogBudget-- > 0; while (Date.now() - startedAt < timeoutMs) { try { @@ -1199,9 +1200,8 @@ export const acquireRunNodeBuildLock = async (deps) => { if (removeStaleBuildLock(deps, lockDir, staleMs)) { continue; } - if (!loggedWait) { + if (consumeWaitLog()) { logRunner("Waiting for TypeScript/runtime artifact lock.", deps); - loggedWait = true; } await sleep(pollMs); } diff --git a/scripts/runtime-postbuild.mjs b/scripts/runtime-postbuild.mjs index 7a977b38af8..97ed4c97295 100644 --- a/scripts/runtime-postbuild.mjs +++ b/scripts/runtime-postbuild.mjs @@ -148,7 +148,7 @@ export function listOfficialChannelCatalogOutputs() { function collectStableRootRuntimeAliasCandidates(params) { const distDir = params.distDir; const fsImpl = params.fs; - let entries = []; + let entries; try { entries = fsImpl.readdirSync(distDir, { withFileTypes: true }); } catch { @@ -298,7 +298,7 @@ export function rewriteRootRuntimeImportsToStableAliases(params = {}) { const rootDir = params.rootDir ?? ROOT; const distDir = path.join(rootDir, "dist"); const fsImpl = params.fs ?? fs; - let entries = []; + let entries; try { entries = fsImpl.readdirSync(distDir, { withFileTypes: true }); } catch { @@ -371,7 +371,7 @@ function resolveRootRuntimeCandidateByMarkers(params) { } const aliasBaseFileName = params.aliasFileName.replace(/\.js$/u, ""); const hashedPattern = new RegExp(`^${escapeRegExp(aliasBaseFileName)}-[A-Za-z0-9_-]+\\.js$`, "u"); - let entries = []; + let entries; try { entries = params.fsImpl.readdirSync(params.distDir, { withFileTypes: true }); } catch { diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index 5f3195899f0..a70564b7365 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -1297,7 +1297,7 @@ function findDirectImportersWithGitGrep(cwd, importedFile, fileSet) { if (file === importedFile || !fileSet.has(file) || importers.includes(file)) { continue; } - let source = ""; + let source; try { source = fs.readFileSync(path.join(cwd, file), "utf8"); } catch { @@ -1372,7 +1372,7 @@ function getImportGraph(cwd) { ); for (const file of files) { - let source = ""; + let source; try { source = fs.readFileSync(path.join(cwd, file), "utf8"); } catch { diff --git a/scripts/tool-search-gateway-e2e.ts b/scripts/tool-search-gateway-e2e.ts index 95bbcf17120..1c0cbde7238 100644 --- a/scripts/tool-search-gateway-e2e.ts +++ b/scripts/tool-search-gateway-e2e.ts @@ -137,7 +137,7 @@ async function readSessionLogMentions(params: { tool_search_code: 0, [params.targetTool]: 0, }; - let files: string[] = []; + let files: string[]; try { files = await fs.readdir(sessionsDir); } catch { diff --git a/scripts/tsdown-build.mjs b/scripts/tsdown-build.mjs index 906a78657d6..3b7082e3598 100644 --- a/scripts/tsdown-build.mjs +++ b/scripts/tsdown-build.mjs @@ -109,7 +109,7 @@ function hasProtectedChild({ rootPath, protectedPaths }) { } function cleanOutputRootExcept(rootPath, protectedPaths, fsImpl) { - let entries = []; + let entries; try { entries = fsImpl.readdirSync(rootPath, { withFileTypes: true }); } catch { @@ -162,7 +162,7 @@ function listExistingPreservedOutputPaths({ cwd, env, fs: fsImpl }) { } function collectDeclarationOutputPaths(rootPath, protectedPaths, fsImpl) { - let entries = []; + let entries; try { entries = fsImpl.readdirSync(rootPath, { withFileTypes: true }); } catch { @@ -184,7 +184,7 @@ export function pruneStaleRootChunkFiles(params = {}) { const fsImpl = params.fs ?? fs; const roots = listTsdownOutputRoots({ cwd, fs: fsImpl }).map((root) => path.join(cwd, root)); for (const root of roots) { - let entries = []; + let entries; try { entries = fsImpl.readdirSync(root, { withFileTypes: true }); } catch { diff --git a/src/acp/control-plane/manager.status.ts b/src/acp/control-plane/manager.status.ts index 67e908aa00d..15c582010ae 100644 --- a/src/acp/control-plane/manager.status.ts +++ b/src/acp/control-plane/manager.status.ts @@ -38,14 +38,13 @@ export async function runManagerGetSessionStatus(params: { const { runtime, handle: ensuredHandle, - meta: ensuredMeta, + meta: initialMeta, } = await params.ensureRuntimeHandle({ cfg: params.cfg, sessionKey: params.sessionKey, meta: resolvedMeta, }); let handle = ensuredHandle; - let meta = ensuredMeta; const capabilities = await params.resolveRuntimeCapabilities({ runtime, handle }); let runtimeStatus: AcpRuntimeStatus | undefined; if (runtime.getStatus) { @@ -63,15 +62,18 @@ export async function runManagerGetSessionStatus(params: { fallbackMessage: "Could not read ACP runtime status.", }); } - ({ handle, meta, runtimeStatus } = await params.reconcileRuntimeSessionIdentifiers({ + const reconciledSession = await params.reconcileRuntimeSessionIdentifiers({ cfg: params.cfg, sessionKey: params.sessionKey, runtime, handle, - meta, + meta: initialMeta, runtimeStatus, failOnStatusError: true, - })); + }); + handle = reconciledSession.handle; + const meta = reconciledSession.meta; + runtimeStatus = reconciledSession.runtimeStatus; const identity = resolveSessionIdentityFromMeta(meta); return { sessionKey: params.sessionKey, diff --git a/src/agents/agent-hooks/context-pruning/pruner.ts b/src/agents/agent-hooks/context-pruning/pruner.ts index e381d51d332..4f7a0757c6d 100644 --- a/src/agents/agent-hooks/context-pruning/pruner.ts +++ b/src/agents/agent-hooks/context-pruning/pruner.ts @@ -119,7 +119,6 @@ function takeTailFromJoinedText(parts: string[], maxChars: number): string { remaining -= p.length; } else { out.push(p.slice(p.length - remaining)); - remaining = 0; break; } if (remaining > 0 && i > 0) { diff --git a/src/agents/bash-tools.exec-host-gateway.ts b/src/agents/bash-tools.exec-host-gateway.ts index ebae9bb8f8e..219154ecbe8 100644 --- a/src/agents/bash-tools.exec-host-gateway.ts +++ b/src/agents/bash-tools.exec-host-gateway.ts @@ -605,17 +605,18 @@ export async function processGatewayAllowlist( const { baseDecision, - approvedByAsk: initialApprovedByAsk, - deniedReason: initialDeniedReason, + approvedByAsk: baseApprovedByAsk, + deniedReason: baseDeniedReason, } = createExecApprovalDecisionState({ decision, askFallback, }); - let approvedByAsk = initialApprovedByAsk; - let deniedReason = initialDeniedReason; + let approvedByAsk = baseApprovedByAsk; + let deniedReason = baseDeniedReason; if (baseDecision.timedOut && askFallback === "allowlist") { if (!analysisOk || !allowlistSatisfied) { + approvedByAsk = false; // Use a colon separator rather than nested parens so the // `Exec denied (gateway id=..., ): cmd` wire format // stays unambiguous for parsers that close on the first `):`. @@ -643,13 +644,15 @@ export async function processGatewayAllowlist( } } - ({ approvedByAsk, deniedReason } = enforceStrictInlineEvalApprovalBoundary({ + const strictBoundaryDecision = enforceStrictInlineEvalApprovalBoundary({ baseDecision, approvedByAsk, deniedReason, requiresInlineEvalApproval, requiresAutoReviewHumanApproval: autoReviewRequiresHumanApproval, - })); + }); + approvedByAsk = strictBoundaryDecision.approvedByAsk; + deniedReason = strictBoundaryDecision.deniedReason; if ( !approvedByAsk && @@ -721,7 +724,7 @@ export async function processGatewayAllowlist( recordMatchedAllowlistUse(resolvedPath ?? undefined); - let run: Awaited> | null = null; + let run: Awaited> | null; try { run = await runExecProcess({ command: params.command, diff --git a/src/agents/bash-tools.exec-host-node.ts b/src/agents/bash-tools.exec-host-node.ts index faafc173af2..8df9ba4c215 100644 --- a/src/agents/bash-tools.exec-host-node.ts +++ b/src/agents/bash-tools.exec-host-node.ts @@ -319,14 +319,14 @@ export async function executeNodeHostCommand( const { baseDecision, approvedByAsk: initialApprovedByAsk, - deniedReason: initialDeniedReason, + deniedReason: baseDeniedReason, } = execHostShared.createExecApprovalDecisionState({ decision, askFallback, }); let approvedByAsk = initialApprovedByAsk; let approvalDecision: "allow-once" | "allow-always" | null = null; - let deniedReason = initialDeniedReason; + let deniedReason = baseDeniedReason; if (baseDecision.timedOut && askFallback === "full" && approvedByAsk) { approvalDecision = "allow-once"; @@ -338,15 +338,15 @@ export async function executeNodeHostCommand( approvalDecision = "allow-always"; } - ({ approvedByAsk, deniedReason } = execHostShared.enforceStrictInlineEvalApprovalBoundary( - { - baseDecision, - approvedByAsk, - deniedReason, - requiresInlineEvalApproval: inlineEvalHit !== null, - requiresAutoReviewHumanApproval: autoReviewRequiresHumanApproval, - }, - )); + const strictBoundaryDecision = execHostShared.enforceStrictInlineEvalApprovalBoundary({ + baseDecision, + approvedByAsk, + deniedReason, + requiresInlineEvalApproval: inlineEvalHit !== null, + requiresAutoReviewHumanApproval: autoReviewRequiresHumanApproval, + }); + approvedByAsk = strictBoundaryDecision.approvedByAsk; + deniedReason = strictBoundaryDecision.deniedReason; if (deniedReason) { approvalDecision = null; } diff --git a/src/agents/btw.ts b/src/agents/btw.ts index b8387435a07..933d09c6477 100644 --- a/src/agents/btw.ts +++ b/src/agents/btw.ts @@ -628,7 +628,7 @@ export async function runBtwSideQuestion( answerText = collectTextContent(finalMessage.content); } if (!reasoningText) { - reasoningText = collectThinkingContent(finalMessage.content); + collectThinkingContent(finalMessage.content); } } diff --git a/src/agents/code-mode.worker.ts b/src/agents/code-mode.worker.ts index 77f05bc94f7..dba19124e49 100644 --- a/src/agents/code-mode.worker.ts +++ b/src/agents/code-mode.worker.ts @@ -392,7 +392,7 @@ function createHostRequestHandler(params: { ) { throw new Error("unsupported code mode bridge method"); } - let args: unknown = []; + let args: unknown; try { args = JSON.parse(argsHandle.toString()) as unknown; } catch { diff --git a/src/agents/embedded-agent-runner/compact.queued.ts b/src/agents/embedded-agent-runner/compact.queued.ts index 467db144cb7..c9f704361f2 100644 --- a/src/agents/embedded-agent-runner/compact.queued.ts +++ b/src/agents/embedded-agent-runner/compact.queued.ts @@ -295,7 +295,7 @@ export async function compactEmbeddedAgentSession( params.enqueue ?? ((task, opts) => enqueueCommandInLane(globalLane, task, opts)); return enqueueCommandInLane(sessionLane, () => enqueueGlobal(async () => { - let checkpointSnapshot: CapturedCompactionCheckpointSnapshot | null = null; + let checkpointSnapshot: CapturedCompactionCheckpointSnapshot | null | undefined; let checkpointSnapshotRetained = false; try { // When the context engine owns compaction, its compact() implementation diff --git a/src/agents/embedded-agent-runner/compact.ts b/src/agents/embedded-agent-runner/compact.ts index 236b88ef50c..cd09ef5e33b 100644 --- a/src/agents/embedded-agent-runner/compact.ts +++ b/src/agents/embedded-agent-runner/compact.ts @@ -597,7 +597,7 @@ async function compactEmbeddedAgentSessionDirectOnce( return fail(reason); } let runtimeModel = model; - let apiKeyInfo: Awaited> | null = null; + let apiKeyInfo: Awaited> | null; let hasRuntimeAuthExchange = false; try { apiKeyInfo = await getApiKeyForModel({ diff --git a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts index 0b08a2a7b1c..0e3889f9380 100644 --- a/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts +++ b/src/agents/embedded-agent-runner/run/attempt.model-diagnostic-events.ts @@ -568,7 +568,7 @@ function observeModelCallStream>( ): T { const observedIterator = () => observeModelCallIterator(createIterator(), eventBase, startedAt, state)[Symbol.asyncIterator](); - let hasNonConfigurableIterator = false; + let hasNonConfigurableIterator; try { hasNonConfigurableIterator = Object.getOwnPropertyDescriptor(stream, Symbol.asyncIterator)?.configurable === false; diff --git a/src/agents/embedded-agent-runner/run/attempt.ts b/src/agents/embedded-agent-runner/run/attempt.ts index c5184692225..d50575e1d52 100644 --- a/src/agents/embedded-agent-runner/run/attempt.ts +++ b/src/agents/embedded-agent-runner/run/attempt.ts @@ -5131,7 +5131,6 @@ export async function runEmbeddedAttempt( ); } retainedSessionFileOwner?.release(); - retainedSessionFileOwner = undefined; emitDiagnosticRunCompleted?.( aborted ? "aborted" : "error", promptError ?? new Error("run exited before diagnostic completion"), diff --git a/src/agents/embedded-agent-subscribe.handlers.messages.ts b/src/agents/embedded-agent-subscribe.handlers.messages.ts index f28f3c82963..2e55c2da376 100644 --- a/src/agents/embedded-agent-subscribe.handlers.messages.ts +++ b/src/agents/embedded-agent-subscribe.handlers.messages.ts @@ -741,7 +741,7 @@ export function handleMessageUpdate( const { mediaUrls, hasMedia } = resolveSendableOutboundReplyParts(parsedStreamDirectives ?? {}); const hasAudio = Boolean(parsedStreamDirectives?.audioAsVoice); - let shouldEmit = false; + let shouldEmit; let deltaText = ""; let replace = false; if (!hasAssistantVisibleReply({ text: cleanedText, mediaUrls, audioAsVoice: hasAudio })) { diff --git a/src/agents/provider-transport-fetch.test.ts b/src/agents/provider-transport-fetch.test.ts index ba939c0a476..ef0c3257549 100644 --- a/src/agents/provider-transport-fetch.test.ts +++ b/src/agents/provider-transport-fetch.test.ts @@ -209,7 +209,7 @@ describe("buildGuardedModelFetch", () => { } as unknown as Model<"anthropic-messages">; const fetcher = buildGuardedModelFetch(model, undefined, { sanitizeSse: false }); - let response = await fetcher("https://api.anthropic.com/v1/messages", { + const response = await fetcher("https://api.anthropic.com/v1/messages", { method: "POST", headers: { "content-type": "application/json" }, body: '{"stream":true}', @@ -218,8 +218,6 @@ describe("buildGuardedModelFetch", () => { expect(reader).toBeDefined(); const firstChunk = await reader?.read(); expect(firstChunk?.done).toBe(false); - - response = undefined as unknown as Response; const registration = managedStreamCleanupRegistrations.at(-1); expect(registration).toBeDefined(); await registration?.held.finalize(); diff --git a/src/agents/session-dirs.ts b/src/agents/session-dirs.ts index 90f42cdebb9..72fe865dd2c 100644 --- a/src/agents/session-dirs.ts +++ b/src/agents/session-dirs.ts @@ -10,7 +10,7 @@ function mapAgentSessionDirs(agentsDir: string, entries: Dirent[]): string[] { } export async function resolveAgentSessionDirsFromAgentsDir(agentsDir: string): Promise { - let entries: Dirent[] = []; + let entries: Dirent[]; try { entries = await fs.readdir(agentsDir, { withFileTypes: true }); } catch (err) { @@ -25,7 +25,7 @@ export async function resolveAgentSessionDirsFromAgentsDir(agentsDir: string): P } export function resolveAgentSessionDirsFromAgentsDirSync(agentsDir: string): string[] { - let entries: Dirent[] = []; + let entries: Dirent[]; try { entries = fsSync.readdirSync(agentsDir, { withFileTypes: true }); } catch (err) { diff --git a/src/agents/session-file-repair.ts b/src/agents/session-file-repair.ts index d16d6ef16f0..bbb2e01056c 100644 --- a/src/agents/session-file-repair.ts +++ b/src/agents/session-file-repair.ts @@ -320,7 +320,7 @@ export async function repairSessionFileIfNeeded(params: { let rewrittenAssistantMessages = 0; let droppedBlankUserMessages = 0; let rewrittenUserMessages = 0; - let insertedToolResults = 0; + let insertedToolResults; for (const line of lines) { if (!line.trim()) { diff --git a/src/agents/session-write-lock.ts b/src/agents/session-write-lock.ts index 010ec7c9c9f..fb9efeaabdf 100644 --- a/src/agents/session-write-lock.ts +++ b/src/agents/session-write-lock.ts @@ -818,7 +818,7 @@ export async function cleanStaleLockFiles(params: { return args; }; - let entries: fsSync.Dirent[] = []; + let entries: fsSync.Dirent[]; try { entries = await fs.readdir(sessionsDir, { withFileTypes: true }); } catch (err) { diff --git a/src/agents/sessions/tools/find.ts b/src/agents/sessions/tools/find.ts index b1937dcca8c..7c5176f639b 100644 --- a/src/agents/sessions/tools/find.ts +++ b/src/agents/sessions/tools/find.ts @@ -337,7 +337,7 @@ export function createFindToolDefinition( continue; } const hadTrailingSlash = line.endsWith("/") || line.endsWith("\\"); - let relativePath = line; + let relativePath; if (line.startsWith(searchPath)) { relativePath = line.slice(searchPath.length + 1); } else { diff --git a/src/agents/subagent-announce-delivery.ts b/src/agents/subagent-announce-delivery.ts index fa65339b6fa..a3fa227da1e 100644 --- a/src/agents/subagent-announce-delivery.ts +++ b/src/agents/subagent-announce-delivery.ts @@ -452,16 +452,14 @@ export async function runAnnounceDeliveryWithRetry(params: { run: () => Promise; }): Promise { const retryDelaysMs = resolveDirectAnnounceTransientRetryDelaysMs(); - let retryIndex = 0; - for (;;) { + for (const [retryIndex, delayMs] of retryDelaysMs.entries()) { if (params.signal?.aborted) { throw new Error("announce delivery aborted"); } try { return await params.run(); } catch (err) { - const delayMs = retryDelaysMs[retryIndex]; - if (delayMs == null || !isTransientAnnounceDeliveryError(err) || params.signal?.aborted) { + if (!isTransientAnnounceDeliveryError(err) || params.signal?.aborted) { throw err; } const nextAttempt = retryIndex + 2; @@ -469,10 +467,13 @@ export async function runAnnounceDeliveryWithRetry(params: { defaultRuntime.log( `[warn] Subagent announce ${params.operation} transient failure, retrying ${nextAttempt}/${maxAttempts} in ${Math.round(delayMs / 1000)}s: ${summarizeDeliveryError(err)}`, ); - retryIndex += 1; await waitForAnnounceRetryDelay(delayMs, params.signal); } } + if (params.signal?.aborted) { + throw new Error("announce delivery aborted"); + } + return await params.run(); } export async function resolveSubagentCompletionOrigin(params: { diff --git a/src/agents/subagent-announce.timeout.test.ts b/src/agents/subagent-announce.timeout.test.ts index efaefe23a00..80f2cf4f05c 100644 --- a/src/agents/subagent-announce.timeout.test.ts +++ b/src/agents/subagent-announce.timeout.test.ts @@ -134,8 +134,7 @@ vi.mock("./subagent-announce-delivery.js", () => ({ clampTimerTimeoutMs(configOverride.agents?.defaults?.subagents?.announceTimeoutMs) ?? 120_000; const retryDelaysMs = process.env.OPENCLAW_TEST_FAST === "1" ? [8, 16, 32] : [5_000, 10_000, 20_000]; - let retryIndex = 0; - for (;;) { + for (const delayMs of [...retryDelaysMs, undefined]) { const request = buildRequest(); gatewayCalls.push(request); try { @@ -143,13 +142,12 @@ vi.mock("./subagent-announce-delivery.js", () => ({ return { delivered: true, path: "direct" }; } catch (error) { const message = error instanceof Error ? error.message : String(error); - const delayMs = retryDelaysMs[retryIndex]; if (!/gateway timeout/i.test(message) || delayMs == null) { return { delivered: false, path: "direct", error: message }; } - retryIndex += 1; } } + throw new Error("unreachable direct delivery retry loop exit"); }, loadRequesterSessionEntry: (sessionKey: string) => ({ cfg: configOverride, diff --git a/src/agents/subagent-announce.ts b/src/agents/subagent-announce.ts index e9ecd22fc2d..3cf3adaa8c6 100644 --- a/src/agents/subagent-announce.ts +++ b/src/agents/subagent-announce.ts @@ -183,7 +183,7 @@ async function wakeSubagentRunAfterDescendants(params: { taskLabel: params.taskLabel, }); - let wakeRunId = ""; + let wakeRunId; try { const wakeResponse = await runAnnounceDeliveryWithRetry<{ runId?: string }>({ operation: "descendant wake agent call", diff --git a/src/agents/system-prompt-report.ts b/src/agents/system-prompt-report.ts index b0f98823e5a..95ce3996046 100644 --- a/src/agents/system-prompt-report.ts +++ b/src/agents/system-prompt-report.ts @@ -52,7 +52,7 @@ function buildToolSchemaStats( if (cached) { return cached; } - let schemaJson = ""; + let schemaJson; try { schemaJson = JSON.stringify(parameters); } catch { diff --git a/src/agents/tools/sessions-send-tool.ts b/src/agents/tools/sessions-send-tool.ts index 5e9bde9271a..87786ea5c46 100644 --- a/src/agents/tools/sessions-send-tool.ts +++ b/src/agents/tools/sessions-send-tool.ts @@ -390,7 +390,7 @@ export function createSessionsSendTool(opts?: { ...(requestedAgentId ? { agentId: requestedAgentId } : {}), ...(restrictToSpawned ? { spawnedBy: effectiveRequesterKey } : {}), }; - let resolvedKey = ""; + let resolvedKey; try { const resolved = await gatewayCall<{ key: string }>({ method: "sessions.resolve", diff --git a/src/agents/tools/web-fetch.ts b/src/agents/tools/web-fetch.ts index b4de1b90d9b..1b33fc978ab 100644 --- a/src/agents/tools/web-fetch.ts +++ b/src/agents/tools/web-fetch.ts @@ -444,7 +444,7 @@ async function runWebFetch(params: WebFetchRuntimeParams): Promise Promise) | null = null; + let release: (() => Promise) | null; let finalUrl = params.url; try { const fetchWithWebToolsNetworkGuard = await loadWebGuardedFetch(); diff --git a/src/agents/utils/git.ts b/src/agents/utils/git.ts index e6fff3c2f8f..1a7374a3451 100644 --- a/src/agents/utils/git.ts +++ b/src/agents/utils/git.ts @@ -79,8 +79,8 @@ function splitRef(url: string): { repo: string; ref?: string } { function parseGenericGitUrl(url: string): GitSource | null { const { repo: repoWithoutRef, ref } = splitRef(url); let repo = repoWithoutRef; - let host = ""; - let path = ""; + let host; + let path; const scpLikeMatch = repoWithoutRef.match(/^git@([^:]+):(.+)$/); if (scpLikeMatch) { diff --git a/src/auto-reply/chunk.ts b/src/auto-reply/chunk.ts index 89b1b63afcf..88fdb9ace10 100644 --- a/src/auto-reply/chunk.ts +++ b/src/auto-reply/chunk.ts @@ -412,7 +412,6 @@ export function chunkMarkdownText(text: string, limit: number): string[] { const maxIdxIfNeedNewline = start + (contentLimit - (closeLine.length + 1)); if (maxIdxIfNeedNewline <= start) { - fenceToSplit = undefined; breakIdx = windowEnd; } else { const minProgressIdx = Math.min( @@ -439,7 +438,6 @@ export function chunkMarkdownText(text: string, limit: number): string[] { if (!pickedNewline) { if (minProgressIdx > maxIdxIfAlreadyNewline) { - fenceToSplit = undefined; breakIdx = windowEnd; } else { breakIdx = Math.max(minProgressIdx, maxIdxIfNeedNewline); diff --git a/src/auto-reply/commands-registry.ts b/src/auto-reply/commands-registry.ts index 0e10c1deb9e..20f587de8b6 100644 --- a/src/auto-reply/commands-registry.ts +++ b/src/auto-reply/commands-registry.ts @@ -178,7 +178,6 @@ function parsePositionalArgs(definitions: CommandArgDefinition[], raw: string): } if (definition.captureRemaining) { values[definition.name] = tokens.slice(index).join(" "); - index = tokens.length; break; } values[definition.name] = tokens[index]; diff --git a/src/auto-reply/reply/agent-runner-execution.ts b/src/auto-reply/reply/agent-runner-execution.ts index 1735ff33303..c1336534734 100644 --- a/src/auto-reply/reply/agent-runner-execution.ts +++ b/src/auto-reply/reply/agent-runner-execution.ts @@ -1632,7 +1632,8 @@ export async function runAgentTurnWithFallback(params: { let attemptedRuntimeProvider = fallbackProvider; let attemptedRuntimeModel = fallbackModel; let fallbackAttempts: RuntimeFallbackAttempt[] = []; - let didRetryTransientHttpError = false; + let transientHttpRetriesRemaining = 1; + const consumeTransientHttpRetry = () => transientHttpRetriesRemaining-- > 0; let liveModelSwitchRetries = 0; let bootstrapPromptWarningSignaturesSeen = resolveBootstrapWarningSignaturesSeen( params.getActiveSessionEntry()?.systemPromptReport, @@ -2820,8 +2821,7 @@ export async function runAgentTurnWithFallback(params: { }; } - if (isTransientHttp && !didRetryTransientHttpError) { - didRetryTransientHttpError = true; + if (isTransientHttp && consumeTransientHttpRetry()) { // Retry the full runWithModelFallback() cycle — transient errors // (502/521/etc.) typically affect the whole provider, so falling // back to an alternate model first would not help. Instead we wait diff --git a/src/auto-reply/reply/agent-runner-memory.test.ts b/src/auto-reply/reply/agent-runner-memory.test.ts index 82c105c27a0..d97621c87dc 100644 --- a/src/auto-reply/reply/agent-runner-memory.test.ts +++ b/src/auto-reply/reply/agent-runner-memory.test.ts @@ -1450,7 +1450,7 @@ describe("runMemoryFlushIfNeeded", () => { totalTokensFresh: false, }; - let directTranscriptStats: unknown[] = []; + let directTranscriptStats: unknown[]; try { await runMemoryFlushIfNeeded({ cfg: { agents: { defaults: { compaction: { memoryFlush: {} } } } }, @@ -1920,7 +1920,7 @@ describe("runMemoryFlushIfNeeded", () => { .mockImplementation(async (target, options) => originalStat(target, options)); let entry: SessionEntry | undefined; - let directTranscriptStats: unknown[] = []; + let directTranscriptStats: unknown[]; try { entry = await runPreflightCompactionIfNeeded({ cfg: { diff --git a/src/auto-reply/reply/agent-runner.ts b/src/auto-reply/reply/agent-runner.ts index 2c288a121ef..40caea7cedc 100644 --- a/src/auto-reply/reply/agent-runner.ts +++ b/src/auto-reply/reply/agent-runner.ts @@ -1475,7 +1475,7 @@ export async function runReplyAgent(params: { } }; const prePreflightCompactionCount = activeSessionEntry?.compactionCount ?? 0; - let preflightCompactionApplied = false; + let preflightCompactionApplied; try { await typingSignals.signalRunStart(); diff --git a/src/auto-reply/reply/bash-command.ts b/src/auto-reply/reply/bash-command.ts index 0cac05c10f6..a626481acf6 100644 --- a/src/auto-reply/reply/bash-command.ts +++ b/src/auto-reply/reply/bash-command.ts @@ -66,7 +66,7 @@ function formatOutputBlock(text: string) { function parseBashRequest(raw: string): BashRequest | null { const trimmed = raw.trimStart(); - let restSource = ""; + let restSource; if (normalizeLowercaseStringOrEmpty(trimmed).startsWith("/bash")) { const match = trimmed.match(/^\/bash(?:\s*:\s*|\s+|$)([\s\S]*)$/i); if (!match) { diff --git a/src/auto-reply/reply/commands-acp/lifecycle.ts b/src/auto-reply/reply/commands-acp/lifecycle.ts index b350dafb143..ca843459fb2 100644 --- a/src/auto-reply/reply/commands-acp/lifecycle.ts +++ b/src/auto-reply/reply/commands-acp/lifecycle.ts @@ -542,7 +542,7 @@ export async function handleAcpSpawnAction( ); } - let initializedBackend = ""; + let initializedBackend; let initializedMeta: SessionAcpMeta | undefined; let initializedRuntime: AcpSpawnRuntimeCloseHandle | undefined; try { @@ -860,7 +860,7 @@ export async function handleAcpCloseAction( commandParams: params, restTokens, run: async ({ acpManager, sessionKey }) => { - let runtimeNotice = ""; + let runtimeNotice; try { const closed = await acpManager.closeSession({ cfg: params.cfg, diff --git a/src/auto-reply/reply/commands-info.ts b/src/auto-reply/reply/commands-info.ts index cf9878c12d1..364bec89d8f 100644 --- a/src/auto-reply/reply/commands-info.ts +++ b/src/auto-reply/reply/commands-info.ts @@ -162,7 +162,7 @@ export const handleToolsCommand: CommandHandler = async (params, allowTextComman return null; } const normalized = params.command.commandBodyNormalized; - let verbose = false; + let verbose; if (normalized === "/tools" || normalized === "/tools compact") { verbose = false; } else if (normalized === "/tools verbose") { diff --git a/src/auto-reply/reply/get-reply-run.ts b/src/auto-reply/reply/get-reply-run.ts index 5762fb526dd..a487130b497 100644 --- a/src/auto-reply/reply/get-reply-run.ts +++ b/src/auto-reply/reply/get-reply-run.ts @@ -536,7 +536,7 @@ export async function runPreparedReply( defaultLevel: resolvedElevatedLevel ?? "off", }, }); - let currentSystemSent = systemSent; + const currentSystemSent = systemSent; const isFirstTurnInSession = isNewSession || !currentSystemSent; const isGroupChat = @@ -846,7 +846,6 @@ export async function runPreparedReply( }); }); sessionEntry = skillResult.sessionEntry ?? sessionEntry; - currentSystemSent = skillResult.systemSent; const skillsSnapshot = skillResult.skillsSnapshot; let { prefixedCommandBody, @@ -1112,8 +1111,8 @@ export async function runPreparedReply( isReplyRunStreamingForSessionId(replyOperationActiveSessionId)), }; }; - let { activeSessionId, isActive, isStreaming } = resolveQueueBusyState(); - let activeRunAcceptsCurrentThread = resolveActiveRunAcceptsCurrentThread({ isActive }); + const { activeSessionId, isActive, isStreaming } = resolveQueueBusyState(); + const activeRunAcceptsCurrentThread = resolveActiveRunAcceptsCurrentThread({ isActive }); const isHeartbeatRun = opts?.isHeartbeat === true; const shouldSteer = !isRoomEvent && @@ -1170,8 +1169,7 @@ export async function runPreparedReply( typing.cleanup(); return queueState.reply; } - ({ activeSessionId, isActive, isStreaming } = queueState.busyState); - activeRunAcceptsCurrentThread = resolveActiveRunAcceptsCurrentThread({ isActive }); + resolveActiveRunAcceptsCurrentThread({ isActive }); } const runHasSessionModelOverride = Boolean( normalizeOptionalString(preparedSessionState.sessionEntry?.modelOverride) || diff --git a/src/auto-reply/reply/session.ts b/src/auto-reply/reply/session.ts index 702cf05a3a0..9a846de2f21 100644 --- a/src/auto-reply/reply/session.ts +++ b/src/auto-reply/reply/session.ts @@ -297,8 +297,8 @@ export async function initSessionState(params: { let sessionId: string | undefined; let isNewSession = false; let bodyStripped: string | undefined; - let systemSent = false; - let abortedLastRun = false; + let systemSent; + let abortedLastRun; let resetTriggered = false; let persistedThinking: string | undefined; diff --git a/src/channels/message/live.ts b/src/channels/message/live.ts index 65f82e4e274..8ad0859c4e2 100644 --- a/src/channels/message/live.ts +++ b/src/channels/message/live.ts @@ -198,7 +198,7 @@ export async function deliverFinalizableLivePreview(params } liveState = markLiveMessageCancelled(liveState); - let delivered = false; + let delivered; try { const result = await params.deliverNormally(params.payload); delivered = result !== false; diff --git a/src/channels/plugins/session-conversation.ts b/src/channels/plugins/session-conversation.ts index 3adb33821f7..e5303dca659 100644 --- a/src/channels/plugins/session-conversation.ts +++ b/src/channels/plugins/session-conversation.ts @@ -136,7 +136,7 @@ function resolveBundledSessionConversationFallback(params: { return null; } const dirName = normalizeResolvedChannel(params.channel); - let loaded: BundledSessionKeyModule | null = null; + let loaded: BundledSessionKeyModule | null; try { loaded = tryLoadActivatedBundledPluginPublicSurfaceModuleSync({ dirName, diff --git a/src/cli/daemon-cli/install.ts b/src/cli/daemon-cli/install.ts index 29a676a223d..61e28d7d566 100644 --- a/src/cli/daemon-cli/install.ts +++ b/src/cli/daemon-cli/install.ts @@ -153,8 +153,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) { } const service = resolveGatewayService(); - let loaded = false; - let existingServiceCommand: GatewayServiceCommandConfig | null = null; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { @@ -165,7 +164,7 @@ export async function runDaemonInstall(opts: DaemonInstallOptions) { return; } } - existingServiceCommand = await service.readCommand(process.env).catch(() => null); + const existingServiceCommand = await service.readCommand(process.env).catch(() => null); const existingServiceEnv: Record | undefined = existingServiceCommand?.environment; const installEnv = mergeInstallInvocationEnv({ diff --git a/src/cli/daemon-cli/lifecycle-core.ts b/src/cli/daemon-cli/lifecycle-core.ts index a37870e5792..8bbf4fdedf2 100644 --- a/src/cli/daemon-cli/lifecycle-core.ts +++ b/src/cli/daemon-cli/lifecycle-core.ts @@ -203,7 +203,7 @@ export async function runServiceUninstall(params: { } } - let loaded = false; + let loaded; try { loaded = await params.service.isLoaded({ env: process.env }); } catch { @@ -222,8 +222,6 @@ export async function runServiceUninstall(params: { fail(`${params.serviceNoun} uninstall failed: ${String(err)}`); return; } - - loaded = false; try { loaded = await params.service.isLoaded({ env: process.env }); } catch { @@ -447,7 +445,7 @@ export async function runServiceStop(params: { return; } - let stopped = false; + let stopped; try { stopped = await params.service.isLoaded({ env: process.env }); } catch { diff --git a/src/cli/daemon-cli/response.ts b/src/cli/daemon-cli/response.ts index 5c4d3ce67ad..ed7777e4604 100644 --- a/src/cli/daemon-cli/response.ts +++ b/src/cli/daemon-cli/response.ts @@ -167,7 +167,7 @@ export async function installDaemonServiceAndEmit(params: { return; } - let installed = true; + let installed; try { installed = await params.service.isLoaded({ env: process.env }); } catch { diff --git a/src/cli/daemon-cli/start-repair.ts b/src/cli/daemon-cli/start-repair.ts index 62a1de32523..1c813b7855d 100644 --- a/src/cli/daemon-cli/start-repair.ts +++ b/src/cli/daemon-cli/start-repair.ts @@ -78,7 +78,7 @@ export async function repairLoadedGatewayServiceForStart(params: { environment, }); - let loaded = true; + let loaded; try { loaded = await params.service.isLoaded({ env: installEnv }); } catch { diff --git a/src/cli/gateway-cli/shared.ts b/src/cli/gateway-cli/shared.ts index 101675dc805..3d743588481 100644 --- a/src/cli/gateway-cli/shared.ts +++ b/src/cli/gateway-cli/shared.ts @@ -32,7 +32,7 @@ function renderGatewayServiceStopHints(env: NodeJS.ProcessEnv = process.env): st export async function maybeExplainGatewayServiceStop() { const service = resolveGatewayService(); - let loaded: boolean | null = null; + let loaded: boolean | null; try { loaded = await service.isLoaded({ env: process.env }); } catch { diff --git a/src/cli/node-cli/daemon.ts b/src/cli/node-cli/daemon.ts index 7a0f52f857b..4359ec6ff21 100644 --- a/src/cli/node-cli/daemon.ts +++ b/src/cli/node-cli/daemon.ts @@ -111,7 +111,7 @@ export async function runNodeDaemonInstall(opts: NodeDaemonInstallOptions) { } const service = resolveNodeService(); - let loaded = false; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { diff --git a/src/cli/nodes-cli/rpc.ts b/src/cli/nodes-cli/rpc.ts index b9d00cea5a6..6fa1065ce1a 100644 --- a/src/cli/nodes-cli/rpc.ts +++ b/src/cli/nodes-cli/rpc.ts @@ -146,7 +146,7 @@ export async function resolveNodeId(opts: NodesRpcOpts, query: string) { } export async function resolveNode(opts: NodesRpcOpts, query: string): Promise { - let nodes: NodeListNode[] = []; + let nodes: NodeListNode[]; try { const res = await callGatewayCli("node.list", opts, {}); nodes = parseNodeList(res); diff --git a/src/cli/update-cli.test.ts b/src/cli/update-cli.test.ts index 953ab68ec4f..470a81aee9d 100644 --- a/src/cli/update-cli.test.ts +++ b/src/cli/update-cli.test.ts @@ -465,7 +465,7 @@ describe("update-cli", () => { !repo.startsWith("@") && repo.split("/").length === 2 && repo.split("/").every((part) => /^[^\s/:@]+$/u.test(part)); - let isHttpGitUrl = false; + let isHttpGitUrl; try { const url = new URL(target); const pathname = url.pathname.replace(/\/+$/u, ""); @@ -2763,7 +2763,7 @@ describe("update-cli", () => { }; }); - let writes = ""; + let writes; try { await updateCommand({ yes: true, json: true }); writes = stdoutWrite.mock.calls.map((call) => String(call[0])).join("\n"); diff --git a/src/cli/update-cli/restart-helper.ts b/src/cli/update-cli/restart-helper.ts index 50c2fd18c9d..0f4a9f8b8d8 100644 --- a/src/cli/update-cli/restart-helper.ts +++ b/src/cli/update-cli/restart-helper.ts @@ -71,8 +71,8 @@ export async function prepareRestartScript( const timestamp = Date.now(); const platform = process.platform; - let scriptContent = ""; - let filename = ""; + let scriptContent; + let filename; try { if (platform === "linux") { diff --git a/src/commands/agent-via-gateway.ts b/src/commands/agent-via-gateway.ts index 2dd4d550b8e..006c3efd69c 100644 --- a/src/commands/agent-via-gateway.ts +++ b/src/commands/agent-via-gateway.ts @@ -728,18 +728,18 @@ async function agentViaGatewayCommand( }), ); - let retriedWithShellEnvFallback = false; + let shellEnvFallbackRetriesRemaining = 1; + const consumeShellEnvFallbackRetry = () => shellEnvFallbackRetriesRemaining-- > 0; for (;;) { try { response = await dispatchGatewayAgentCall(cfg); break; } catch (err) { if ( - !retriedWithShellEnvFallback && !acceptedGatewayRun && - shouldRetryGatewayDispatchWithShellEnvFallback(err) + shouldRetryGatewayDispatchWithShellEnvFallback(err) && + consumeShellEnvFallbackRetry() ) { - retriedWithShellEnvFallback = true; cfg = await getGatewayDispatchConfig({ skipShellEnvFallback: false }); continue; } diff --git a/src/commands/channels/resolve.ts b/src/commands/channels/resolve.ts index da97e728f74..5e9b47dcc01 100644 --- a/src/commands/channels/resolve.ts +++ b/src/commands/channels/resolve.ts @@ -207,7 +207,7 @@ export async function channelsResolveCommand(opts: ChannelsResolveOptions, runti } const preferredKind = resolvePreferredKind(opts.kind); - let results: ResolveResult[] = []; + let results: ResolveResult[]; if (preferredKind) { const resolved = await plugin.resolver.resolveTargets({ cfg, diff --git a/src/commands/configure.daemon.ts b/src/commands/configure.daemon.ts index 4643b06120b..a6038ee1074 100644 --- a/src/commands/configure.daemon.ts +++ b/src/commands/configure.daemon.ts @@ -22,7 +22,7 @@ export async function maybeInstallDaemon(params: { daemonRuntime?: GatewayDaemonRuntime; }) { const service = resolveGatewayService(); - let loaded = false; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch (error) { diff --git a/src/commands/doctor-gateway-daemon-flow.ts b/src/commands/doctor-gateway-daemon-flow.ts index 23b70b3332e..4802b5c06c6 100644 --- a/src/commands/doctor-gateway-daemon-flow.ts +++ b/src/commands/doctor-gateway-daemon-flow.ts @@ -171,7 +171,7 @@ export async function maybeRepairGatewayDaemon(params: { const serviceRepairExternal = isServiceRepairExternallyManaged(serviceRepairPolicy); const service = resolveGatewayService(); // systemd can throw in containers/WSL; treat as "not loaded" and fall back to hints. - let loaded = false; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch { diff --git a/src/commands/doctor-gateway-services.ts b/src/commands/doctor-gateway-services.ts index 60229471596..e72f5ab499c 100644 --- a/src/commands/doctor-gateway-services.ts +++ b/src/commands/doctor-gateway-services.ts @@ -367,7 +367,7 @@ export async function maybeRepairGatewayServiceConfig( } const service = resolveGatewayService(); - let command: Awaited> | null = null; + let command: Awaited> | null; try { command = await service.readCommand(process.env); } catch { diff --git a/src/commands/doctor-session-locks.ts b/src/commands/doctor-session-locks.ts index b9b3c4b49a9..e868e3852a5 100644 --- a/src/commands/doctor-session-locks.ts +++ b/src/commands/doctor-session-locks.ts @@ -48,7 +48,7 @@ export async function noteSessionLockHealth(params?: { }) { const shouldRepair = params?.shouldRepair === true; const staleMs = params?.staleMs ?? resolveSessionWriteLockStaleMs(params?.config, params?.env); - let sessionDirs: string[] = []; + let sessionDirs: string[]; try { sessionDirs = await resolveAgentSessionDirs(resolveStateDir(process.env)); } catch (err) { diff --git a/src/commands/doctor-session-snapshots.ts b/src/commands/doctor-session-snapshots.ts index 6c25c4c47b1..00035b096e2 100644 --- a/src/commands/doctor-session-snapshots.ts +++ b/src/commands/doctor-session-snapshots.ts @@ -248,7 +248,7 @@ export function scanSessionStoreForStaleRuntimeSnapshotPaths(params: { async function listSessionStorePaths(stateDir: string): Promise { const agentsDir = path.join(stateDir, "agents"); - let agentEntries: fs.Dirent[] = []; + let agentEntries: fs.Dirent[]; try { agentEntries = await fs.promises.readdir(agentsDir, { withFileTypes: true }); } catch { diff --git a/src/commands/doctor-session-transcripts.ts b/src/commands/doctor-session-transcripts.ts index 7ca72e38354..b6eb75fc68e 100644 --- a/src/commands/doctor-session-transcripts.ts +++ b/src/commands/doctor-session-transcripts.ts @@ -294,7 +294,7 @@ export async function repairBrokenSessionTranscriptFile(params: { async function listSessionTranscriptFiles(sessionDirs: string[]): Promise { const files: string[] = []; for (const sessionsDir of sessionDirs) { - let entries: Dirent[] = []; + let entries: Dirent[]; try { entries = await fs.readdir(sessionsDir, { withFileTypes: true }); } catch { diff --git a/src/commands/doctor-state-integrity.ts b/src/commands/doctor-state-integrity.ts index 96e443fd524..3cb549ed7ab 100644 --- a/src/commands/doctor-state-integrity.ts +++ b/src/commands/doctor-state-integrity.ts @@ -238,7 +238,7 @@ function findOtherStateDirs(stateDir: string): string[] { process.platform === "darwin" ? ["/Users"] : process.platform === "linux" ? ["/home"] : []; const found: string[] = []; for (const root of roots) { - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(root, { withFileTypes: true }); } catch { diff --git a/src/commands/doctor-state-migrations.test.ts b/src/commands/doctor-state-migrations.test.ts index 2ef0da492f2..30506ae6d8b 100644 --- a/src/commands/doctor-state-migrations.test.ts +++ b/src/commands/doctor-state-migrations.test.ts @@ -83,7 +83,7 @@ vi.mock("../channels/plugins/bundled.js", async () => { } function detectWhatsAppLegacyStateMigrations(params: { oauthDir: string }) { - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(params.oauthDir, { withFileTypes: true }); } catch { diff --git a/src/commands/doctor/cron/index.ts b/src/commands/doctor/cron/index.ts index 40cb46d6bde..5115276db15 100644 --- a/src/commands/doctor/cron/index.ts +++ b/src/commands/doctor/cron/index.ts @@ -59,10 +59,10 @@ export async function maybeRepairLegacyCronStore(params: { const storePath = resolveCronJobsStorePath(params.cfg.cron?.store); const quarantinePath = resolveCronQuarantinePath(storePath); let store: Awaited>["store"]; - let legacyStoreDetected = false; - let legacyRunLogDetected = false; + let legacyStoreDetected; + let legacyRunLogDetected; let legacyImportCount = 0; - let sqliteProjectionBackfillCount = 0; + let sqliteProjectionBackfillCount; try { legacyStoreDetected = await legacyCronStoreFilesExist(storePath); legacyRunLogDetected = await legacyCronRunLogFilesExist(storePath); diff --git a/src/commands/doctor/shared/codex-route-warnings.ts b/src/commands/doctor/shared/codex-route-warnings.ts index 3d72c57da8d..907233ebfac 100644 --- a/src/commands/doctor/shared/codex-route-warnings.ts +++ b/src/commands/doctor/shared/codex-route-warnings.ts @@ -2882,7 +2882,6 @@ function rewriteSessionModelPair(params: { typeof params.entry[params.modelKey] === "string" ? params.entry[params.modelKey] : undefined; if (provider === "openai-codex") { params.entry[params.providerKey] = "openai"; - changed = true; if (model) { const modelId = toOpenAIModelId(model); if (modelId) { diff --git a/src/commands/gateway-status/discovery.ts b/src/commands/gateway-status/discovery.ts index a4ebeae50d1..7150e5e1077 100644 --- a/src/commands/gateway-status/discovery.ts +++ b/src/commands/gateway-status/discovery.ts @@ -13,7 +13,7 @@ export function inferSshTargetFromRemoteUrl(rawUrl?: string | null): string | nu if (!trimmed) { return null; } - let host: string | null = null; + let host: string | null; try { host = new URL(trimmed).hostname || null; } catch { diff --git a/src/commands/onboard-non-interactive/local/gateway-config.ts b/src/commands/onboard-non-interactive/local/gateway-config.ts index 94fc9aa5166..ca12761cee2 100644 --- a/src/commands/onboard-non-interactive/local/gateway-config.ts +++ b/src/commands/onboard-non-interactive/local/gateway-config.ts @@ -95,7 +95,6 @@ export function applyNonInteractiveGatewayConfig(params: { runtime.exit(1); return null; } - gatewayToken = resolvedFromEnv; nextConfig = { ...nextConfig, gateway: { diff --git a/src/commands/reset.ts b/src/commands/reset.ts index 142d04c76e8..b308d80823b 100644 --- a/src/commands/reset.ts +++ b/src/commands/reset.ts @@ -30,7 +30,7 @@ async function stopGatewayIfRunning(runtime: RuntimeEnv) { return; } const service = resolveGatewayService(); - let loaded = false; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { diff --git a/src/commands/status.format.ts b/src/commands/status.format.ts index e5b1a0ec972..9945e7ddcb2 100644 --- a/src/commands/status.format.ts +++ b/src/commands/status.format.ts @@ -24,7 +24,7 @@ export const formatTokensCompact = ( const used = sess.totalTokens; const ctx = sess.contextTokens; - let result = ""; + let result; if (used == null) { result = ctx ? `unknown/${formatKTokens(ctx)} (?%)` : "unknown used"; } else if (!ctx) { diff --git a/src/commands/uninstall.ts b/src/commands/uninstall.ts index 1b7be2a9e2c..0cbb1001d97 100644 --- a/src/commands/uninstall.ts +++ b/src/commands/uninstall.ts @@ -65,7 +65,7 @@ async function stopAndUninstallService(runtime: RuntimeEnv): Promise { return false; } const service = resolveGatewayService(); - let loaded = false; + let loaded; try { loaded = await service.isLoaded({ env: process.env }); } catch (err) { diff --git a/src/config/io.ts b/src/config/io.ts index d66a1a4eeeb..8a47a919db0 100644 --- a/src/config/io.ts +++ b/src/config/io.ts @@ -2754,7 +2754,7 @@ export async function writeConfigFile( // never touched any plugin entry. let canonicalSourceConfig: OpenClawConfig = nextCfg; const envBeforeCanonicalRead = snapshotEnv(process.env); - let envAfterCanonicalRead = envBeforeCanonicalRead; + let envAfterCanonicalRead; try { const freshSnapshot = await io.readConfigFileSnapshot(); if (freshSnapshot.exists && freshSnapshot.valid) { diff --git a/src/cron/isolated-agent/delivery-dispatch.ts b/src/cron/isolated-agent/delivery-dispatch.ts index 590d67e29f9..9bdc131470e 100644 --- a/src/cron/isolated-agent/delivery-dispatch.ts +++ b/src/cron/isolated-agent/delivery-dispatch.ts @@ -725,16 +725,14 @@ async function retryTransientDirectCronDelivery(params: { run: () => Promise; }): Promise { const retryDelaysMs = resolveDirectCronRetryDelaysMs(); - let retryIndex = 0; - for (;;) { + for (const [retryIndex, delayMs] of retryDelaysMs.entries()) { if (params.signal?.aborted) { throw new Error("cron delivery aborted"); } try { return await params.run(); } catch (err) { - const delayMs = retryDelaysMs[retryIndex]; - if (delayMs == null || !isTransientDirectCronDeliveryError(err) || params.signal?.aborted) { + if (!isTransientDirectCronDeliveryError(err) || params.signal?.aborted) { throw err; } const nextAttempt = retryIndex + 2; @@ -742,10 +740,13 @@ async function retryTransientDirectCronDelivery(params: { await logCronDeliveryWarn( `[cron:${params.jobId}] transient direct announce delivery failure, retrying ${nextAttempt}/${maxAttempts} in ${Math.round(delayMs / 1000)}s: ${summarizeDirectCronDeliveryError(err)}`, ); - retryIndex += 1; await sleepWithAbort(delayMs, params.signal); } } + if (params.signal?.aborted) { + throw new Error("cron delivery aborted"); + } + return await params.run(); } export async function dispatchCronDelivery( diff --git a/src/cron/service/jobs.ts b/src/cron/service/jobs.ts index 6b19807b45d..18926222843 100644 --- a/src/cron/service/jobs.ts +++ b/src/cron/service/jobs.ts @@ -213,7 +213,7 @@ function shouldRepairFutureCronNextRunAtMs(params: { if (!isFiniteTimestamp(naturalNext)) { return false; } - let isScheduledSlot = false; + let isScheduledSlot; try { isScheduledSlot = isStaggeredCronRunAtMs(job, nextRun); } catch { diff --git a/src/cron/service/ops.ts b/src/cron/service/ops.ts index ae572ef12c0..ac7913f5a85 100644 --- a/src/cron/service/ops.ts +++ b/src/cron/service/ops.ts @@ -309,7 +309,7 @@ function resolveJobLastRunStatus(job: CronJob): CronJobsLastRunStatusFilter { function sortJobs(jobs: CronJob[], sortBy: CronJobsSortBy, sortDir: CronSortDir) { const dir = sortDir === "desc" ? -1 : 1; return jobs.toSorted((a, b) => { - let cmp = 0; + let cmp; if (sortBy === "name") { const aName = typeof a.name === "string" ? a.name : ""; const bName = typeof b.name === "string" ? b.name : ""; diff --git a/src/daemon/service-audit.ts b/src/daemon/service-audit.ts index 1a50f62b657..2828d36ffc3 100644 --- a/src/daemon/service-audit.ts +++ b/src/daemon/service-audit.ts @@ -165,7 +165,7 @@ async function auditSystemdUnit( issues: ServiceConfigIssue[], ) { const unitPath = resolveSystemdUserUnitPath(env); - let content = ""; + let content; try { content = await fs.readFile(unitPath, "utf8"); } catch { @@ -214,7 +214,7 @@ async function auditLaunchdPlist( issues: ServiceConfigIssue[], ) { const plistPath = resolveLaunchAgentPlistPath(env); - let content = ""; + let content; try { content = await fs.readFile(plistPath, "utf8"); } catch { diff --git a/src/daemon/systemd.ts b/src/daemon/systemd.ts index fd9c840dec5..c5f76baa92f 100644 --- a/src/daemon/systemd.ts +++ b/src/daemon/systemd.ts @@ -137,7 +137,7 @@ export async function findInstalledSystemdGatewayScope( env: GatewayServiceEnv, ): Promise { const canonicalUnitName = `${resolveSystemdServiceName(env)}.service`; - let userPath: string | null = null; + let userPath: string | null; try { userPath = resolveSystemdUnitPath(env); } catch { @@ -970,7 +970,7 @@ async function removeNodeSystemdManagedEnvironmentKeys(env: GatewayServiceEnv): stateDir, environment: env, }); - let existing: Record = {}; + let existing: Record; try { existing = await readSystemdEnvironmentFile(envFilePath); } catch { diff --git a/src/flows/doctor-health-contributions.ts b/src/flows/doctor-health-contributions.ts index 0bcf507ec9d..ce099d43728 100644 --- a/src/flows/doctor-health-contributions.ts +++ b/src/flows/doctor-health-contributions.ts @@ -701,7 +701,7 @@ async function runSystemdLingerHealth(ctx: DoctorHealthFlowContext): Promise { it("surfaces connect assembly errors instead of waiting for the wrapper timeout", async () => { vi.useFakeTimers(); - let client: GatewayClientInstance | null = null; + let client: GatewayClientInstance | null | undefined; try { const onClose = vi.fn(); const onConnectError = vi.fn(); diff --git a/src/gateway/control-ui.ts b/src/gateway/control-ui.ts index e5211e9ee5c..cc52c7c7f32 100644 --- a/src/gateway/control-ui.ts +++ b/src/gateway/control-ui.ts @@ -588,7 +588,7 @@ export async function handleControlUiAssistantMediaRequest( } let opened: Awaited> | null = null; - let localPath = source; + let localPath; let handleClosed = false; const closeOpenedHandle = async () => { if (!opened || handleClosed) { diff --git a/src/gateway/gateway-cli-backend.live-probe-helpers.ts b/src/gateway/gateway-cli-backend.live-probe-helpers.ts index eed337bef6b..cbd17f6592e 100644 --- a/src/gateway/gateway-cli-backend.live-probe-helpers.ts +++ b/src/gateway/gateway-cli-backend.live-probe-helpers.ts @@ -214,7 +214,7 @@ async function callLoopbackJsonRpc(params: { const controller = new AbortController(); const timer = setTimeout(() => controller.abort(), timeoutMs); let response: Response | undefined; - let text = ""; + let text; try { response = await fetch(`http://127.0.0.1:${runtime.port}/mcp`, { method: "POST", diff --git a/src/gateway/managed-image-attachments.ts b/src/gateway/managed-image-attachments.ts index a37fbaa2aab..5910a4b22f8 100644 --- a/src/gateway/managed-image-attachments.ts +++ b/src/gateway/managed-image-attachments.ts @@ -366,7 +366,7 @@ async function deleteOrphanManagedImageFiles(params: { }) { let deletedFileCount = 0; for (const dir of [resolveOutgoingOriginalsDir(params.stateDir)]) { - let names: string[] = []; + let names: string[]; try { names = await fs.readdir(dir); } catch { @@ -413,7 +413,7 @@ export async function cleanupManagedOutgoingImageRecords(params?: { sessionKeyFilter === "global" ? resolveDefaultAgentId(getRuntimeConfig()) : undefined; const forceDeleteSessionRecords = params?.forceDeleteSessionRecords === true; const recordsDir = resolveOutgoingRecordsDir(stateDir); - let names: string[] = []; + let names: string[]; try { names = await fs.readdir(recordsDir); } catch { @@ -464,7 +464,7 @@ export async function cleanupManagedOutgoingImageRecords(params?: { continue; } - let shouldDelete = false; + let shouldDelete; if ( forceDeleteSessionRecords && (!sessionKeyFilter || record.sessionKey === sessionKeyFilter) diff --git a/src/gateway/node-registry.ts b/src/gateway/node-registry.ts index 66406d85800..da2118236b5 100644 --- a/src/gateway/node-registry.ts +++ b/src/gateway/node-registry.ts @@ -479,7 +479,7 @@ export class NodeRegistry { } const connId = params.connId; this.pruneAuthorizedSystemRunEvents(); - let match: { key: string; event: AuthorizedSystemRunEvent } | null = null; + let match: { key: string; event: AuthorizedSystemRunEvent } | null; if (params.runId) { match = this.matchAuthorizedSystemRunEvent({ nodeId: params.nodeId, diff --git a/src/gateway/openai-http.ts b/src/gateway/openai-http.ts index 938858c202b..b5e36a58d8a 100644 --- a/src/gateway/openai-http.ts +++ b/src/gateway/openai-http.ts @@ -981,7 +981,7 @@ export async function handleOpenAiHttpRequest( } const activeTurnContext = resolveActiveTurnContext(payload.messages); const prompt = buildAgentPrompt(payload.messages, activeTurnContext.activeUserMessageIndex); - let resolvedClientTools: ClientToolDefinition[] = []; + let resolvedClientTools: ClientToolDefinition[]; let toolChoicePrompt: string | undefined; let toolChoiceConstraint: ToolChoiceConstraint | undefined; try { @@ -1002,7 +1002,7 @@ export async function handleOpenAiHttpRequest( }); return true; } - let images: ImageContent[] = []; + let images: ImageContent[]; try { images = await resolveImagesForRequest(activeTurnContext, limits); } catch (err) { diff --git a/src/gateway/security-path.ts b/src/gateway/security-path.ts index 6c51f3258f5..ab11723ce3e 100644 --- a/src/gateway/security-path.ts +++ b/src/gateway/security-path.ts @@ -63,7 +63,7 @@ export function buildCanonicalPathCandidates( let malformedEncoding = false; let decodePasses = 0; for (let pass = 0; pass < maxDecodePasses; pass++) { - let nextDecoded = decoded; + let nextDecoded; try { nextDecoded = decodeURIComponent(decoded); } catch { diff --git a/src/gateway/server-aux-handlers.ts b/src/gateway/server-aux-handlers.ts index f74416f3e19..16f3097b800 100644 --- a/src/gateway/server-aux-handlers.ts +++ b/src/gateway/server-aux-handlers.ts @@ -133,7 +133,7 @@ export function createGatewayAuxHandlers(params: { params.sharedGatewaySessionGenerationState.current; const previousSharedGatewaySessionGenerationRequired = params.sharedGatewaySessionGenerationState.required; - let nextSharedGatewaySessionGeneration = previousSharedGatewaySessionGeneration; + let nextSharedGatewaySessionGeneration; let sharedGatewaySessionGenerationChanged = false; const stoppedChannels: ChannelKind[] = []; const restartedChannels = new Set(); diff --git a/src/gateway/server-methods/doctor.ts b/src/gateway/server-methods/doctor.ts index 74d0f81c4d6..fb8dd3f10c5 100644 --- a/src/gateway/server-methods/doctor.ts +++ b/src/gateway/server-methods/doctor.ts @@ -241,7 +241,7 @@ function groundedMarkdownToDiaryLines(markdown: string): string[] { } async function listWorkspaceDailyFiles(memoryDir: string): Promise { - let entries: string[] = []; + let entries: string[]; try { entries = await fs.readdir(memoryDir); } catch (err) { diff --git a/src/gateway/server-methods/update.ts b/src/gateway/server-methods/update.ts index 6994baf2feb..ce68100b0f5 100644 --- a/src/gateway/server-methods/update.ts +++ b/src/gateway/server-methods/update.ts @@ -240,7 +240,7 @@ export const updateHandlers: GatewayRequestHandlers = { meta: sentinelMeta, }); - let sentinelPath: string | null = null; + let sentinelPath: string | null; try { sentinelPath = await writeRestartSentinel(payload); recordLatestUpdateRestartSentinel(payload); diff --git a/src/gateway/server-node-events.ts b/src/gateway/server-node-events.ts index db93f7fc3d9..5d9a81a55ba 100644 --- a/src/gateway/server-node-events.ts +++ b/src/gateway/server-node-events.ts @@ -459,7 +459,7 @@ export const handleNodeEvent = async ( key?: string | null; }; - let link: AgentDeepLink | null = null; + let link: AgentDeepLink | null; try { link = JSON.parse(evt.payloadJSON) as AgentDeepLink; } catch { @@ -743,7 +743,7 @@ export const handleNodeEvent = async ( (normalizeOptionalString(obj.reason) ?? "").replace(/[()]/g, ""), ); - let text = ""; + let text; if (evt.event === "exec.started") { text = `Exec started (node=${nodeId}${runId ? ` id=${runId}` : ""})`; if (command) { diff --git a/src/gateway/server-startup-plugins.ts b/src/gateway/server-startup-plugins.ts index b240c3bae12..32cfa2b7d44 100644 --- a/src/gateway/server-startup-plugins.ts +++ b/src/gateway/server-startup-plugins.ts @@ -118,7 +118,7 @@ export async function prepareGatewayPluginBootstrap(params: { const baseMethods = listGatewayMethods(); const coreGatewayMethodNames = listCoreGatewayMethodNames(); const emptyPluginRegistry = createEmptyPluginRegistry(); - let pluginRegistry = emptyPluginRegistry; + let pluginRegistry; let baseGatewayMethods = baseMethods; const shouldLoadRuntimePlugins = params.loadRuntimePlugins !== false; const shouldLoadSetupRuntimePlugins = diff --git a/src/gateway/server.shared-auth-rotation.test.ts b/src/gateway/server.shared-auth-rotation.test.ts index 1835418d362..dae75e9dbaa 100644 --- a/src/gateway/server.shared-auth-rotation.test.ts +++ b/src/gateway/server.shared-auth-rotation.test.ts @@ -78,7 +78,7 @@ async function openDeviceTokenWsWithDetails( await approveDevicePairing(pending.request.requestId, { callerScopes: ["operator.admin"], }); - let issuedDeviceToken = ""; + let issuedDeviceToken; if (params.issuerGeneration) { const deviceToken = await ensureDeviceToken({ deviceId: identity.deviceId, diff --git a/src/gateway/server/http-listen.ts b/src/gateway/server/http-listen.ts index 2472092965f..42ddfaaa6cf 100644 --- a/src/gateway/server/http-listen.ts +++ b/src/gateway/server/http-listen.ts @@ -22,7 +22,7 @@ export async function listenGatewayHttpServer(params: { }) { const { httpServer, bindHost, port } = params; - for (let attempt = 0; ; attempt++) { + for (const attempt of Array.from({ length: EADDRINUSE_MAX_RETRIES + 1 }, (_, index) => index)) { try { await new Promise((resolve, reject) => { const onError = (err: NodeJS.ErrnoException) => { diff --git a/src/gateway/server/ws-connection/message-handler.ts b/src/gateway/server/ws-connection/message-handler.ts index c81dfaf1bec..d92926cec6b 100644 --- a/src/gateway/server/ws-connection/message-handler.ts +++ b/src/gateway/server/ws-connection/message-handler.ts @@ -1236,7 +1236,7 @@ export function attachGatewayWsMessageHandler(params: GatewayWsMessageHandlerPar const context = buildRequestContext(); let approved: Awaited> | undefined; let resolvedByConcurrentApproval = false; - let recoveryRequestId: string | undefined = pairing.request.requestId; + let recoveryRequestId: string | undefined; const resolveLivePendingRequestId = async (): Promise => { const pendingList = await listDevicePairing(); const exactPending = pendingList.pending.find( diff --git a/src/gateway/session-compaction-checkpoints.test.ts b/src/gateway/session-compaction-checkpoints.test.ts index a90ac5181c5..1b5c6065ec1 100644 --- a/src/gateway/session-compaction-checkpoints.test.ts +++ b/src/gateway/session-compaction-checkpoints.test.ts @@ -130,7 +130,7 @@ describe("session-compaction-checkpoints", () => { const copyFileSyncSpy = vi.spyOn(fsSync, "copyFileSync"); const sessionManagerOpenSpy = vi.spyOn(SessionManager, "open"); - let snapshot: Awaited> = null; + let snapshot: Awaited> | undefined; try { expect(await readSessionLeafIdFromTranscriptAsync(sessionFile)).toBe(leafId); snapshot = await captureCompactionCheckpointSnapshotAsync({ @@ -216,7 +216,7 @@ describe("session-compaction-checkpoints", () => { const openSpy = vi.spyOn(SessionManager, "open"); const forkSpy = vi.spyOn(SessionManager, "forkFrom"); - let forked: Awaited> = null; + let forked: Awaited>; try { forked = await forkCompactionCheckpointTranscriptAsync({ sourceFile: sessionFile, diff --git a/src/infra/exec-approvals-analysis.ts b/src/infra/exec-approvals-analysis.ts index 330f0c01d63..47ee1b70572 100644 --- a/src/infra/exec-approvals-analysis.ts +++ b/src/infra/exec-approvals-analysis.ts @@ -409,8 +409,6 @@ function splitShellPipeline(command: string): { ok: boolean; reason?: string; se } } else if (line === current.delimiter) { pendingHeredocs.shift(); - unquotedHeredocLogicalChunks = []; - unquotedHeredocLogicalLength = 0; if (pendingHeredocs.length === 0) { inHeredocBody = false; } diff --git a/src/infra/exec-approvals.ts b/src/infra/exec-approvals.ts index 0ba1505e6d6..fd3eb7f2258 100644 --- a/src/infra/exec-approvals.ts +++ b/src/infra/exec-approvals.ts @@ -737,7 +737,7 @@ export function readExecApprovalsSnapshot(): ExecApprovalsSnapshot { }; } const raw = fs.readFileSync(filePath, "utf8"); - let parsed: ExecApprovalsFile | null = null; + let parsed: ExecApprovalsFile | null; try { parsed = JSON.parse(raw) as ExecApprovalsFile; } catch { diff --git a/src/infra/heartbeat-runner.test-utils.ts b/src/infra/heartbeat-runner.test-utils.ts index b952b9b8809..4506184e395 100644 --- a/src/infra/heartbeat-runner.test-utils.ts +++ b/src/infra/heartbeat-runner.test-utils.ts @@ -40,7 +40,7 @@ export async function seedSessionStore( sessionKey: string, session: HeartbeatSessionSeed, ): Promise { - let existingStore: Record = {}; + let existingStore: Record; try { existingStore = JSON.parse(await fs.readFile(storePath, "utf-8")) as Record; } catch { diff --git a/src/infra/install-flow.ts b/src/infra/install-flow.ts index dc188e7324d..24c5663808c 100644 --- a/src/infra/install-flow.ts +++ b/src/infra/install-flow.ts @@ -52,7 +52,7 @@ export async function withExtractedArchiveRoot( return { ok: false, error: `failed to extract archive: ${String(err)}` }; } - let rootDir = ""; + let rootDir; try { rootDir = await resolvePackedRootDir(extractDir, { rootMarkers: params.rootMarkers ? [...params.rootMarkers] : undefined, diff --git a/src/infra/install-package-dir.test.ts b/src/infra/install-package-dir.test.ts index 2684703c031..6f94f98bb9b 100644 --- a/src/infra/install-package-dir.test.ts +++ b/src/infra/install-package-dir.test.ts @@ -45,7 +45,7 @@ function normalizeDarwinTmpPath(filePath: string): string { function normalizeComparablePath(filePath: string): string { const resolved = normalizeDarwinTmpPath(path.resolve(filePath)); const parent = normalizeDarwinTmpPath(path.dirname(resolved)); - let comparableParent = parent; + let comparableParent; try { comparableParent = normalizeDarwinTmpPath(fsSync.realpathSync.native(parent)); } catch { diff --git a/src/infra/outbound/channel-selection.ts b/src/infra/outbound/channel-selection.ts index 123cc5c4f4d..c06499e6bc7 100644 --- a/src/infra/outbound/channel-selection.ts +++ b/src/infra/outbound/channel-selection.ts @@ -182,7 +182,7 @@ async function isPluginConfigured(plugin: ChannelPlugin, cfg: OpenClawConfig): P if (!plugin.config.isConfigured) { return true; } - let configured = false; + let configured; try { configured = await plugin.config.isConfigured(account, cfg); } catch (error) { diff --git a/src/infra/package-update-steps.ts b/src/infra/package-update-steps.ts index 89f301ba79d..f49b43d04cc 100644 --- a/src/infra/package-update-steps.ts +++ b/src/infra/package-update-steps.ts @@ -335,7 +335,7 @@ async function replaceNpmBinShims(params: { targetLayout: NpmGlobalPrefixLayout; packageName: string; }): Promise { - let entries: string[] = []; + let entries: string[]; try { entries = await fs.readdir(params.stageLayout.binDir); } catch { @@ -499,7 +499,7 @@ export async function runGlobalPackageUpdateSteps(params: { }> { const installCwd = params.installCwd === undefined ? {} : { cwd: params.installCwd }; const installEnv = params.env === undefined ? {} : { env: params.env }; - let stagedInstall: StagedNpmInstall | null = null; + let stagedInstall: StagedNpmInstall | null | undefined; let packedInstallDir: string | null = null; try { @@ -679,7 +679,7 @@ export async function runGlobalPackageUpdateSteps(params: { failedStep, }; } finally { - await cleanupStagedNpmInstall(stagedInstall); + await cleanupStagedNpmInstall(stagedInstall ?? null); if (packedInstallDir) { await removePathBestEffort(packedInstallDir); } diff --git a/src/infra/ports-probe.test.ts b/src/infra/ports-probe.test.ts index b077e2809fc..ff25f389ce6 100644 --- a/src/infra/ports-probe.test.ts +++ b/src/infra/ports-probe.test.ts @@ -29,7 +29,7 @@ async function withListeningServer(cb: (address: net.AddressInfo) => Promise { it("can bind and release an ephemeral loopback port", async () => { - let listened = false; + let listened; try { await tryListenOnPort({ port: 0, host: "127.0.0.1", exclusive: true }); listened = true; diff --git a/src/infra/push-apns.relay.ts b/src/infra/push-apns.relay.ts index 67128b36c0a..a65fc52a492 100644 --- a/src/infra/push-apns.relay.ts +++ b/src/infra/push-apns.relay.ts @@ -248,7 +248,7 @@ async function sendApnsRelayRequest(params: { }; } - let json: unknown = null; + let json: unknown; try { json = (await response.json()) as unknown; } catch { diff --git a/src/infra/push-apns.test.ts b/src/infra/push-apns.test.ts index b4fe79b72e0..f6eeb815e38 100644 --- a/src/infra/push-apns.test.ts +++ b/src/infra/push-apns.test.ts @@ -321,7 +321,7 @@ describe("push APNs send semantics", () => { it("routes direct APNs HTTP/2 requests through the active managed proxy", async () => { const apnsServer = await startFakeApnsServer(); const proxy = await startConnectProxy(apnsServer.port); - let proxyHandle: ProxyHandle | null = null; + let proxyHandle: ProxyHandle | null | undefined; const previousTlsRejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED; process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; @@ -366,7 +366,7 @@ describe("push APNs send semantics", () => { } else { process.env.NODE_TLS_REJECT_UNAUTHORIZED = previousTlsRejectUnauthorized; } - await stopProxy(proxyHandle); + await stopProxy(proxyHandle ?? null); await proxy.stop(); await apnsServer.stop(); } diff --git a/src/infra/sqlite-transaction.ts b/src/infra/sqlite-transaction.ts index 052e028de1f..96b24830baa 100644 --- a/src/infra/sqlite-transaction.ts +++ b/src/infra/sqlite-transaction.ts @@ -30,7 +30,7 @@ function isRetryableCommitError(error: unknown): boolean { } function commitImmediateTransaction(db: DatabaseSync): void { - for (let attempt = 1; ; attempt += 1) { + for (const attempt of Array.from({ length: MAX_COMMIT_ATTEMPTS }, (_, index) => index + 1)) { try { db.exec("COMMIT"); return; diff --git a/src/infra/state-migrations.test.ts b/src/infra/state-migrations.test.ts index 19b6537adc5..e3bfff39d24 100644 --- a/src/infra/state-migrations.test.ts +++ b/src/infra/state-migrations.test.ts @@ -35,7 +35,7 @@ vi.mock("../channels/plugins/bundled.js", () => { ]), listBundledChannelLegacyStateMigrationDetectors: vi.fn(() => [ ({ oauthDir }: { oauthDir: string }) => { - let entries: fsSync.Dirent[] = []; + let entries: fsSync.Dirent[]; try { entries = fsSync.readdirSync(oauthDir, { withFileTypes: true }); } catch { diff --git a/src/infra/state-migrations.ts b/src/infra/state-migrations.ts index 8b01a2f60f3..fe1e0f10b73 100644 --- a/src/infra/state-migrations.ts +++ b/src/infra/state-migrations.ts @@ -1252,8 +1252,8 @@ async function runLegacyMigrationPlans( for (const plan of plans) { if (plan.kind === "plugin-state-import") { await withPluginStateImportEnv(plan, async () => { - let storeEntries: Array<{ key: string; value: unknown }> = []; - let pluginEntryCount = 0; + let storeEntries: Array<{ key: string; value: unknown }>; + let pluginEntryCount; const store = createPluginStateKeyedStore(plan.pluginId, { namespace: plan.namespace, maxEntries: plan.maxEntries, @@ -2021,7 +2021,7 @@ export async function autoMigrateLegacyStateDir(params: { const warnings: string[] = []; const changes: string[] = []; - let legacyStat: fs.Stats | null = null; + let legacyStat: fs.Stats | null; try { legacyStat = legacyDir ? fs.lstatSync(legacyDir) : null; } catch { diff --git a/src/infra/update-global.ts b/src/infra/update-global.ts index 82378fc8133..9a951fc2913 100644 --- a/src/infra/update-global.ts +++ b/src/infra/update-global.ts @@ -837,7 +837,7 @@ export async function cleanupGlobalRenameDirs(params: { return { removed }; } const prefix = `${GLOBAL_RENAME_PREFIX}${name}-`; - let entries: string[] = []; + let entries: string[]; try { entries = await fs.readdir(root); } catch { diff --git a/src/infra/update-runner.ts b/src/infra/update-runner.ts index 2b8617aa429..bbf0beee577 100644 --- a/src/infra/update-runner.ts +++ b/src/infra/update-runner.ts @@ -230,7 +230,7 @@ function buildStartDirs(opts: UpdateRunnerOptions): string[] { dirs.push(packageRoot); } } - let proc: string | null = null; + let proc: string | null; try { proc = normalizeDir(process.cwd()); } catch { @@ -907,8 +907,8 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise< if (fetchFailure) { return fetchFailure; } - let preflightBaseSha: string | null = null; - let candidatesLocal: string[] = []; + let preflightBaseSha: string | null; + let candidatesLocal: string[]; if (devTargetRef) { let targetSha: string | null = null; for (const targetRefCandidate of buildDevTargetRefResolutionCandidates(devTargetRef)) { diff --git a/src/llm/providers/openai-chatgpt-responses.ts b/src/llm/providers/openai-chatgpt-responses.ts index 2728a568c17..76c05b5c704 100644 --- a/src/llm/providers/openai-chatgpt-responses.ts +++ b/src/llm/providers/openai-chatgpt-responses.ts @@ -925,7 +925,7 @@ async function getWebSocketConstructor(): Promise { cachedWebsocket = class extends WebSocket { constructor(url: string | URL, options?: string | string[] | Record) { - let opts: Record = {}; + let opts: Record; if (Array.isArray(options) || typeof options === "string") { opts = { protocols: options }; } else { diff --git a/src/logging/log-tail.ts b/src/logging/log-tail.ts index 9f4287ae108..01fa8a1c496 100644 --- a/src/logging/log-tail.ts +++ b/src/logging/log-tail.ts @@ -79,7 +79,7 @@ async function readLogSlice(params: { : undefined; let reset = false; let truncated = false; - let start = 0; + let start; if (cursor != null) { if (cursor > size) { diff --git a/src/logging/timestamps.ts b/src/logging/timestamps.ts index 44f292d29b3..d21124525c4 100644 --- a/src/logging/timestamps.ts +++ b/src/logging/timestamps.ts @@ -7,7 +7,7 @@ export function isValidTimeZone(tz: string): boolean { if (cached !== undefined) { return cached; } - let valid = false; + let valid; try { new Intl.DateTimeFormat("en", { timeZone: tz }).format(); valid = true; diff --git a/src/mcp/channel-server.test.ts b/src/mcp/channel-server.test.ts index db1ed92aff2..f8c3ab5d066 100644 --- a/src/mcp/channel-server.test.ts +++ b/src/mcp/channel-server.test.ts @@ -268,7 +268,7 @@ describe("openclaw channel mcp server", () => { test("emits Claude channel and permission notifications", async () => { const sessionKey = "agent:main:main"; - let mcp: Awaited> | null = null; + let mcp: Awaited> | null | undefined; try { const channelNotifications: Array<{ content: string; meta: Record }> = []; const permissionNotifications: Array<{ diff --git a/src/pairing/allow-from-store-file.ts b/src/pairing/allow-from-store-file.ts index f76e0de850b..0204acaa679 100644 --- a/src/pairing/allow-from-store-file.ts +++ b/src/pairing/allow-from-store-file.ts @@ -233,7 +233,7 @@ export async function readAllowFromFileWithExists(params: { return { entries: [], exists: false }; } - let raw = ""; + let raw; try { raw = await fs.promises.readFile(params.filePath, "utf8"); } catch (err) { @@ -244,7 +244,7 @@ export async function readAllowFromFileWithExists(params: { throw err; } - let entries: string[] = []; + let entries: string[]; try { entries = params.normalizeStore(JSON.parse(raw) as AllowFromStore); } catch { @@ -290,7 +290,7 @@ export function readAllowFromFileSyncWithExists(params: { return { entries: [], exists: false }; } - let raw = ""; + let raw; try { raw = fs.readFileSync(params.filePath, "utf8"); } catch (err) { diff --git a/src/plugin-sdk/access-groups.ts b/src/plugin-sdk/access-groups.ts index 03e15f2faa6..e2023b22234 100644 --- a/src/plugin-sdk/access-groups.ts +++ b/src/plugin-sdk/access-groups.ts @@ -100,7 +100,7 @@ export async function resolveAccessGroupAllowFromState(params: { continue; } - let allowed = false; + let allowed; try { allowed = await params.resolveMembership({ name, diff --git a/src/plugin-sdk/root-alias.cjs b/src/plugin-sdk/root-alias.cjs index 28dd1b5490d..af8710ffb02 100644 --- a/src/plugin-sdk/root-alias.cjs +++ b/src/plugin-sdk/root-alias.cjs @@ -240,7 +240,7 @@ function listPluginSdkExportedSubpaths() { return pluginSdkSubpathsCache.get(cacheKey); } - let subpaths = []; + let subpaths; try { const packageJsonPath = path.join(packageRoot, "package.json"); const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8")); diff --git a/src/plugins/bundled-capability-runtime.ts b/src/plugins/bundled-capability-runtime.ts index f963f266504..d5d772768b4 100644 --- a/src/plugins/bundled-capability-runtime.ts +++ b/src/plugins/bundled-capability-runtime.ts @@ -300,7 +300,7 @@ export function loadBundledCapabilityRuntimeRegistry(params: { const safeSource = opened.path; fs.closeSync(opened.fd); - let mod: OpenClawPluginModule | null = null; + let mod: OpenClawPluginModule | null; try { mod = getModuleLoader(safeSource)(safeSource) as OpenClawPluginModule; } catch (error) { diff --git a/src/plugins/discovery.ts b/src/plugins/discovery.ts index 3fad2e20c7a..6f7e9735f0e 100644 --- a/src/plugins/discovery.ts +++ b/src/plugins/discovery.ts @@ -943,7 +943,7 @@ function discoverInDirectory(params: { } params.visitedDirectories?.add(resolvedDir); } - let entries: fs.Dirent[] = []; + let entries: fs.Dirent[]; try { entries = fs.readdirSync(params.dir, { withFileTypes: true }); } catch (err) { diff --git a/src/plugins/loader.ts b/src/plugins/loader.ts index 1eb5ddc030f..18762f6ae7c 100644 --- a/src/plugins/loader.ts +++ b/src/plugins/loader.ts @@ -3034,7 +3034,7 @@ export async function loadOpenClawPluginCliRegistry( const safeSource = opened.path; fs.closeSync(opened.fd); - let mod: OpenClawPluginModule | null = null; + let mod: OpenClawPluginModule | null; try { mod = withProfile( { pluginId: record.id, source: safeSource }, diff --git a/src/plugins/manifest-registry-installed.test.ts b/src/plugins/manifest-registry-installed.test.ts index a8c5b5d105a..ce509f58df4 100644 --- a/src/plugins/manifest-registry-installed.test.ts +++ b/src/plugins/manifest-registry-installed.test.ts @@ -300,7 +300,7 @@ describe("loadPluginManifestRegistryForInstalledIndex", () => { includeDisabled: true, }); const realpathSpy = vi.spyOn(fs, "realpathSync"); - let packagePathCalls: unknown[][] = []; + let packagePathCalls: unknown[][]; try { loadPluginManifestRegistryForInstalledIndex({ index, diff --git a/src/plugins/plugin-lifecycle-trace.ts b/src/plugins/plugin-lifecycle-trace.ts index f70f2505942..e2c7d97239e 100644 --- a/src/plugins/plugin-lifecycle-trace.ts +++ b/src/plugins/plugin-lifecycle-trace.ts @@ -38,13 +38,16 @@ export function tracePluginLifecyclePhase( return fn(); } const start = process.hrtime.bigint(); - let status: "error" | "ok" = "error"; + let status: "error" | "ok" | undefined; try { const result = fn(); status = "ok"; return result; + } catch (error) { + status = "error"; + throw error; } finally { - emitPluginLifecycleTrace({ phase, start, status, details }); + emitPluginLifecycleTrace({ phase, start, status: status ?? "error", details }); } } @@ -57,12 +60,15 @@ export async function tracePluginLifecyclePhaseAsync( return fn(); } const start = process.hrtime.bigint(); - let status: "error" | "ok" = "error"; + let status: "error" | "ok" | undefined; try { const result = await fn(); status = "ok"; return result; + } catch (error) { + status = "error"; + throw error; } finally { - emitPluginLifecycleTrace({ phase, start, status, details }); + emitPluginLifecycleTrace({ phase, start, status: status ?? "error", details }); } } diff --git a/src/plugins/plugin-registry-snapshot.ts b/src/plugins/plugin-registry-snapshot.ts index f78d69fb848..ebbc78cb8af 100644 --- a/src/plugins/plugin-registry-snapshot.ts +++ b/src/plugins/plugin-registry-snapshot.ts @@ -442,7 +442,7 @@ export function loadPluginRegistrySnapshotWithMetadata( const disabledByEnv = hasEnvFlag(env, DISABLE_PERSISTED_PLUGIN_REGISTRY_ENV); const persistedReadsEnabled = !disabledByCaller && !disabledByEnv; const persistedInstallRecordReadsEnabled = !disabledByEnv; - let persistedIndex: InstalledPluginIndex | null = null; + let persistedIndex: InstalledPluginIndex | null; if (persistedInstallRecordReadsEnabled) { persistedIndex = readPersistedInstalledPluginIndexSync(params); if (persistedReadsEnabled && persistedIndex) { diff --git a/src/plugins/status-snapshot.ts b/src/plugins/status-snapshot.ts index bf7b5a17d74..b577077532d 100644 --- a/src/plugins/status-snapshot.ts +++ b/src/plugins/status-snapshot.ts @@ -38,20 +38,19 @@ function formatTraceValue(value: boolean | number | string): string { return JSON.stringify(value); } -function tracePluginLifecyclePhase( - phase: string, - fn: () => T, - details?: TraceDetails, -): T { +function tracePluginLifecyclePhase(phase: string, fn: () => T, details?: TraceDetails): T { if (!isPluginLifecycleTraceEnabled()) { return fn(); } const start = process.hrtime.bigint(); - let status: "error" | "ok" = "error"; + let status: "error" | "ok" | undefined; try { const result = fn(); status = "ok"; return result; + } catch (error) { + status = "error"; + throw error; } finally { const elapsedMs = Number(process.hrtime.bigint() - start) / 1_000_000; const detailText = Object.entries(details ?? {}) @@ -60,7 +59,7 @@ function tracePluginLifecyclePhase( .join(" "); const suffix = detailText ? ` ${detailText}` : ""; console.error( - `[plugins:lifecycle] phase=${JSON.stringify(phase)} ms=${elapsedMs.toFixed(2)} status=${status}${suffix}`, + `[plugins:lifecycle] phase=${JSON.stringify(phase)} ms=${elapsedMs.toFixed(2)} status=${status ?? "error"}${suffix}`, ); } } diff --git a/src/plugins/update.ts b/src/plugins/update.ts index 392aca95810..0b719354051 100644 --- a/src/plugins/update.ts +++ b/src/plugins/update.ts @@ -1391,7 +1391,6 @@ export async function updateNpmInstalledPlugins(params: { activeClawHubInstallSpec = clawhubSpecs.fallbackSpec; if (officialNpmFallbackSpecs?.fallbackSpec) { officialNpmFallbackInstallSpec = officialNpmFallbackSpecs.fallbackSpec; - officialNpmFallbackRecordSpec = officialNpmFallbackSpecs.fallbackSpec; } } if ( diff --git a/src/proxy-capture/proxy-server.ts b/src/proxy-capture/proxy-server.ts index 6e22c2df68a..c1ebd455029 100644 --- a/src/proxy-capture/proxy-server.ts +++ b/src/proxy-capture/proxy-server.ts @@ -232,7 +232,7 @@ export async function startDebugProxyServer(params: { server.on("connect", (req, clientSocket, head) => { const flowId = randomUUID(); let hostname = "127.0.0.1"; - let port = 443; + let port; try { const parsed = parseConnectTarget(req.url); hostname = parsed.hostname; diff --git a/src/shared/json-schema-defaults.ts b/src/shared/json-schema-defaults.ts index 467df6c6e6b..e3e38fa714e 100644 --- a/src/shared/json-schema-defaults.ts +++ b/src/shared/json-schema-defaults.ts @@ -204,7 +204,7 @@ function validateTypeKeyword(type: unknown, path: string): string | undefined { } function decodePointerSegment(segment: string): string { - let decodedSegment = segment; + let decodedSegment; try { decodedSegment = decodeURIComponent(segment); } catch { diff --git a/src/skills/lifecycle/install-download.ts b/src/skills/lifecycle/install-download.ts index 5afba1d01ad..b3615a729cd 100644 --- a/src/skills/lifecycle/install-download.ts +++ b/src/skills/lifecycle/install-download.ts @@ -140,7 +140,7 @@ export async function installDownloadSpec(params: { }; } - let filename = ""; + let filename; try { const parsed = new URL(url); filename = path.basename(parsed.pathname); @@ -151,8 +151,8 @@ export async function installDownloadSpec(params: { filename = "download"; } - let canonicalRoot = ""; - let targetDir = ""; + let canonicalRoot; + let targetDir; try { await ensureDir(root); await assertCanonicalPathWithinBase({ @@ -192,7 +192,7 @@ export async function installDownloadSpec(params: { code: null, }; } - let downloaded = 0; + let downloaded; try { const result = await downloadFile({ url, diff --git a/src/skills/loading/workspace.ts b/src/skills/loading/workspace.ts index e9f174fe99e..1f7dacc5691 100644 --- a/src/skills/loading/workspace.ts +++ b/src/skills/loading/workspace.ts @@ -1552,7 +1552,7 @@ export async function syncSkillsToWorkspace(params: { const usedDirNames = new Set(); for (const entry of entries) { - let dest: string | null = null; + let dest: string | null; try { dest = resolveSyncedSkillDestinationPath({ targetSkillsDir, diff --git a/src/skills/security/scanner.ts b/src/skills/security/scanner.ts index a72e276e9cb..e215d2e1bf9 100644 --- a/src/skills/security/scanner.ts +++ b/src/skills/security/scanner.ts @@ -588,7 +588,7 @@ async function walkDirWithLimit( } async function readDirEntriesWithCache(dirPath: string): Promise { - let st: Awaited> | null = null; + let st: Awaited> | null; try { st = await fs.stat(dirPath); } catch (err) { @@ -645,7 +645,7 @@ async function resolveForcedFiles(params: { continue; } - let st: Awaited> | null = null; + let st: Awaited> | null; try { st = await fs.stat(includePath); } catch (err) { @@ -713,7 +713,7 @@ async function scanFileWithCache(params: { maxFileBytes: number; }): Promise<{ scanned: boolean; findings: SkillScanFinding[] }> { const { filePath, maxFileBytes } = params; - let st: Awaited> | null = null; + let st: Awaited> | null; try { st = await fs.stat(filePath); } catch (err) { diff --git a/src/skills/security/workspace-audit.ts b/src/skills/security/workspace-audit.ts index 098b17416cf..3a9db2daa5c 100644 --- a/src/skills/security/workspace-audit.ts +++ b/src/skills/security/workspace-audit.ts @@ -65,9 +65,11 @@ async function listWorkspaceSkillMarkdownFiles( const skillFiles: string[] = []; const queue: string[] = [skillsRoot]; const visitedDirs = new Set(); - let totalDirVisits = 0; - while (queue.length > 0 && skillFiles.length < maxFiles && totalDirVisits++ < maxTotalDirVisits) { + for (const _ of Array.from({ length: maxTotalDirVisits })) { + if (queue.length === 0 || skillFiles.length >= maxFiles) { + break; + } const dir = queue.shift()!; const dirRealPath = (await realpathWithTimeout(dir)) ?? path.resolve(dir); if (visitedDirs.has(dirRealPath)) { diff --git a/src/tasks/task-registry.maintenance.ts b/src/tasks/task-registry.maintenance.ts index 541443130ef..bc8b4eaf3b8 100644 --- a/src/tasks/task-registry.maintenance.ts +++ b/src/tasks/task-registry.maintenance.ts @@ -367,7 +367,7 @@ function getCronRunLogEntries(context: CronRecoveryContext, jobId: string): Cron if (cached) { return cached; } - let entries: CronRunLogEntry[] = []; + let entries: CronRunLogEntry[]; try { entries = taskRegistryMaintenanceRuntime.readCronRunLogEntriesSync({ storePath: context.storePath, diff --git a/src/wizard/setup.post-install-migration.ts b/src/wizard/setup.post-install-migration.ts index 1c4fda4106d..cbb67310b47 100644 --- a/src/wizard/setup.post-install-migration.ts +++ b/src/wizard/setup.post-install-migration.ts @@ -183,7 +183,7 @@ export async function offerPostInstallMigrations( continue; } const description = describeCandidate(candidate); - let accepted = false; + let accepted; try { accepted = await prompter.confirm({ message: `Migrate ${description} into this agent now?`, diff --git a/src/wizard/setup.ts b/src/wizard/setup.ts index 93f16416de7..db653fde3a6 100644 --- a/src/wizard/setup.ts +++ b/src/wizard/setup.ts @@ -623,7 +623,7 @@ export async function runSetupWizard( nextConfig = applySkipBootstrapConfig(nextConfig); } nextConfig = onboardHelpers.applyWizardMetadata(nextConfig, { command: "onboard", mode }); - nextConfig = await writeSetupConfigFile(nextConfig, { + await writeSetupConfigFile(nextConfig, { allowConfigSizeDrop: configResetPerformed, }); logConfigUpdated(runtime); diff --git a/test/image-generation.runtime.live.test.ts b/test/image-generation.runtime.live.test.ts index 3c6d7be240f..9e1774de5b7 100644 --- a/test/image-generation.runtime.live.test.ts +++ b/test/image-generation.runtime.live.test.ts @@ -225,7 +225,7 @@ describeLive("image generation live (provider sweep)", () => { requireProfileKeys: REQUIRE_PROFILE_KEYS, hasLiveKeys, }); - let authLabel = "unresolved"; + let authLabel; try { const auth = await resolveApiKeyForProvider({ provider: providerCase.providerId, diff --git a/test/scripts/install-cli.test.ts b/test/scripts/install-cli.test.ts index f9436b1639c..1bff6d60cf0 100644 --- a/test/scripts/install-cli.test.ts +++ b/test/scripts/install-cli.test.ts @@ -974,7 +974,7 @@ describe("install-cli.sh", () => { writeNpmFreshnessConflictFixture(join(nodeBin, "npm"), argsLog); let result: ReturnType | undefined; - let argsOutput = ""; + let argsOutput; try { result = runInstallCliShell( [ @@ -1012,7 +1012,7 @@ describe("install-cli.sh", () => { writeNpmBeforePolicyFixture(join(nodeBin, "npm"), argsLog); let result: ReturnType | undefined; - let argsOutput = ""; + let argsOutput; try { result = runInstallCliShell( [ diff --git a/test/scripts/install-sh.test.ts b/test/scripts/install-sh.test.ts index 1f7d109ab6b..e19133b8957 100644 --- a/test/scripts/install-sh.test.ts +++ b/test/scripts/install-sh.test.ts @@ -677,7 +677,7 @@ describe("install.sh", () => { writeNpmFreshnessConflictFixture(join(bin, "npm"), argsLog); let result: ReturnType | undefined; - let argsOutput = ""; + let argsOutput; try { result = runInstallShell( [ @@ -714,7 +714,7 @@ describe("install.sh", () => { writeNpmBeforePolicyFixture(join(bin, "npm"), argsLog); let result: ReturnType | undefined; - let argsOutput = ""; + let argsOutput; try { result = runInstallShell( [ diff --git a/test/scripts/package-openclaw-for-docker.test.ts b/test/scripts/package-openclaw-for-docker.test.ts index f86546c9077..061891852c7 100644 --- a/test/scripts/package-openclaw-for-docker.test.ts +++ b/test/scripts/package-openclaw-for-docker.test.ts @@ -173,7 +173,7 @@ describe("package-openclaw-for-docker", () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-package-timeout-")); const childPidPath = path.join(tempDir, "child.pid"); - let childPid = 0; + let childPid; try { const childScript = ["process.on('SIGTERM', () => {});", "setInterval(() => {}, 1000);"].join( "", @@ -212,7 +212,7 @@ describe("package-openclaw-for-docker", () => { const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-package-descendant-")); const childPidPath = path.join(tempDir, "child.pid"); - let childPid = 0; + let childPid; try { const childScript = ["process.on('SIGTERM', () => {});", "setInterval(() => {}, 1000);"].join( "", @@ -270,7 +270,7 @@ describe("package-openclaw-for-docker", () => { const childPidPath = path.join(tempDir, "child.pid"); const scriptUrl = pathToFileURL(path.resolve("scripts/package-openclaw-for-docker.mjs")).href; let childPid = 0; - let runnerPid = 0; + let runnerPid; try { const childScript = "setInterval(() => {}, 1000);"; const parentScript = [ diff --git a/test/scripts/plugin-lifecycle-measure.test.ts b/test/scripts/plugin-lifecycle-measure.test.ts index c8e3f520176..0aae4a1dc64 100644 --- a/test/scripts/plugin-lifecycle-measure.test.ts +++ b/test/scripts/plugin-lifecycle-measure.test.ts @@ -128,7 +128,7 @@ describe("plugin lifecycle resource sampler", () => { const dir = makeTempDir(); const summary = path.join(dir, "summary.tsv"); const pidFile = path.join(dir, "descendant.pid"); - let descendantPid = 0; + let descendantPid; try { const result = spawnSync( @@ -174,7 +174,7 @@ describe("plugin lifecycle resource sampler", () => { const dir = makeTempDir(); const summary = path.join(dir, "summary.tsv"); const pidFile = path.join(dir, "descendant.pid"); - let descendantPid = 0; + let descendantPid; try { const result = spawnSync( diff --git a/test/vitest/vitest.unit-fast-paths.mjs b/test/vitest/vitest.unit-fast-paths.mjs index 0b1d9078dd8..d2424660888 100644 --- a/test/vitest/vitest.unit-fast-paths.mjs +++ b/test/vitest/vitest.unit-fast-paths.mjs @@ -414,7 +414,7 @@ export function collectUnitFastTestFileAnalysis(cwd = process.cwd(), options = { : collectUnitFastTestCandidates(cwd); const analysis = candidates.map((file) => { const absolutePath = path.join(cwd, file); - let source = ""; + let source; try { source = fs.readFileSync(absolutePath, "utf8"); } catch { @@ -491,7 +491,7 @@ function isUnitFastTestFileOnDemand(file, cwd = process.cwd()) { return false; } - let source = ""; + let source; try { source = fs.readFileSync(path.join(cwd, normalized), "utf8"); } catch { diff --git a/ui/src/ui/app-gateway.ts b/ui/src/ui/app-gateway.ts index 635de57340e..126b578ceec 100644 --- a/ui/src/ui/app-gateway.ts +++ b/ui/src/ui/app-gateway.ts @@ -273,7 +273,7 @@ async function verifyPendingUpdateVersion( } const deadline = Date.now() + 10_000; while (host.client === client && host.connected && Date.now() < deadline) { - let response: UpdateRestartStatusResponse | null = null; + let response: UpdateRestartStatusResponse | null; try { response = await client.request("update.status", {}); } catch { diff --git a/ui/src/ui/custom-theme.ts b/ui/src/ui/custom-theme.ts index e2b1553edc1..1f9af862d23 100644 --- a/ui/src/ui/custom-theme.ts +++ b/ui/src/ui/custom-theme.ts @@ -618,7 +618,7 @@ export function syncCustomThemeStyleTag(theme: ImportedCustomTheme | null | unde style?.remove(); return; } - let cssText = ""; + let cssText; try { cssText = buildCustomThemeStyles(theme); } catch {