diff --git a/scripts/android-app-i18n.ts b/scripts/android-app-i18n.ts index f72c5e0a44c..588bf3b977b 100644 --- a/scripts/android-app-i18n.ts +++ b/scripts/android-app-i18n.ts @@ -1,6 +1,7 @@ import { readdir, readFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { NATIVE_I18N_LOCALES } from "./native-app-i18n.ts"; const HERE = path.dirname(fileURLToPath(import.meta.url)); @@ -63,7 +64,8 @@ export async function checkAndroidAppI18n() { readAndroidSource(), Promise.all(LOCALES.map(readStrings)), ]); - const [base, ...translations] = localeStrings; + const base = expectDefined(localeStrings[0], "English Android string resources"); + const translations = localeStrings.slice(1); const baseKeys = new Set(base.keys()); const problems: Array = translations.flatMap((strings, index) => { const locale = NATIVE_I18N_LOCALES[index]; diff --git a/scripts/apple-app-i18n.ts b/scripts/apple-app-i18n.ts index 005e2c1f57c..628d7353fdc 100644 --- a/scripts/apple-app-i18n.ts +++ b/scripts/apple-app-i18n.ts @@ -1,6 +1,7 @@ import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { NATIVE_I18N_LOCALES } from "./native-app-i18n.ts"; const HERE = path.dirname(fileURLToPath(import.meta.url)); @@ -196,7 +197,7 @@ export async function checkAppleAppI18n() { export async function compileMacosLocalizations(outputDir: string) { await checkAppleAppI18n(); - const spec = CATALOGS[1]; + const spec = expectDefined(CATALOGS[1], "macOS localization catalog specification"); const catalog = JSON.parse(await readFile(path.join(ROOT, spec.path), "utf8")) as Catalog; if (!catalog.strings) { throw new Error(`invalid Apple string catalog: ${spec.path}`); diff --git a/scripts/bench-cli-startup.ts b/scripts/bench-cli-startup.ts index 691a122495a..affb00029ac 100644 --- a/scripts/bench-cli-startup.ts +++ b/scripts/bench-cli-startup.ts @@ -4,6 +4,7 @@ import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "nod import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts"; type CommandCase = { @@ -474,7 +475,7 @@ function parseRepeatableFlag(flag: string): string[] { for (let i = 0; i < process.argv.length; i += 1) { const value = process.argv[i + 1]; if (process.argv[i] === flag && value && !value.startsWith("-")) { - values.push(process.argv[i + 1]); + values.push(value); } } return values; @@ -483,7 +484,7 @@ function parseRepeatableFlag(flag: string): string[] { function validateCliArgs(argv: readonly string[] = process.argv.slice(2)): void { const seenSingleValueFlags = new Set(); for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + const arg = expectDefined(argv[index], `CLI benchmark argument at index ${index}`); if (VALUE_FLAGS.has(arg)) { if (arg !== "--case") { if (seenSingleValueFlags.has(arg)) { @@ -575,9 +576,13 @@ function median(values: number[]): number { const sorted = [...values].toSorted((a, b) => a - b); const mid = Math.floor(sorted.length / 2); if (sorted.length % 2 === 0) { - return (sorted[mid - 1] + sorted[mid]) / 2; + return ( + (expectDefined(sorted[mid - 1], "lower middle CLI benchmark sample") + + expectDefined(sorted[mid], "upper middle CLI benchmark sample")) / + 2 + ); } - return sorted[mid]; + return expectDefined(sorted[mid], "middle CLI benchmark sample"); } function percentile(values: number[], p: number): number { diff --git a/scripts/bench-gateway-restart.ts b/scripts/bench-gateway-restart.ts index 0354b2f401d..781b88b1a17 100644 --- a/scripts/bench-gateway-restart.ts +++ b/scripts/bench-gateway-restart.ts @@ -6,6 +6,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { performance } from "node:perf_hooks"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { writeGatewayRestartIntentSync } from "../src/infra/restart.js"; import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts"; import { delay, stopChild, type StopChildResult } from "./lib/gateway-bench-child.ts"; @@ -342,7 +343,7 @@ function resolveOutputPath(raw: string | undefined): string | undefined { function resolveCases(caseIds: string[]): GatewayBenchCase[] { if (caseIds.length === 0) { - return [GATEWAY_CASES[0]]; + return [expectDefined(GATEWAY_CASES[0], "default gateway restart benchmark case")]; } const seenIds = new Set(); const byId = new Map(GATEWAY_CASES.map((benchCase) => [benchCase.id, benchCase])); @@ -412,7 +413,11 @@ function median(values: number[]): number { const sorted = [...values].toSorted((a, b) => a - b); const middle = Math.floor(sorted.length / 2); if (sorted.length % 2 === 0) { - return (sorted[middle - 1] + sorted[middle]) / 2; + return ( + (expectDefined(sorted[middle - 1], "lower middle gateway restart sample") + + expectDefined(sorted[middle], "upper middle gateway restart sample")) / + 2 + ); } return sorted[middle] ?? 0; } @@ -510,8 +515,8 @@ function slope(values: Array): number | null { if (points.length < 2) { return null; } - const first = points[0]; - const last = points[points.length - 1]; + const first = expectDefined(points[0], "first gateway restart slope point"); + const last = expectDefined(points[points.length - 1], "last gateway restart slope point"); const denominator = Math.max(1, last.index - first.index); return (last.value - first.value) / denominator; } @@ -910,10 +915,11 @@ function collectTraceLine( "u", ).exec(line); if (phaseMatch) { - trace[phaseMatch[1]] = Number(phaseMatch[2]); - trace[`${phaseMatch[1]}.total`] = Number(phaseMatch[3]); + const phase = expectDefined(phaseMatch[1], `${prefix} phase name`); + trace[phase] = Number(expectDefined(phaseMatch[2], `${prefix} phase duration`)); + trace[`${phase}.total`] = Number(expectDefined(phaseMatch[3], `${prefix} total duration`)); for (const metric of parseTraceMetrics(phaseMatch[4] ?? "")) { - trace[`${phaseMatch[1]}.${metric.key}`] = metric.value; + trace[`${phase}.${metric.key}`] = metric.value; } return true; } @@ -921,8 +927,10 @@ function collectTraceLine( if (!detailMatch) { return false; } - for (const metric of parseTraceMetrics(detailMatch[2])) { - trace[`${detailMatch[1]}.${metric.key}`] = metric.value; + const phase = expectDefined(detailMatch[1], `${prefix} detail phase name`); + const metrics = expectDefined(detailMatch[2], `${prefix} detail metrics`); + for (const metric of parseTraceMetrics(metrics)) { + trace[`${phase}.${metric.key}`] = metric.value; } return true; } @@ -934,8 +942,8 @@ function parseTraceMetrics(raw: string): Array<{ key: string; value: number }> { if (!metricMatch) { continue; } - const key = metricMatch[1]; - const value = Number(metricMatch[2]); + const key = expectDefined(metricMatch[1], "gateway restart trace metric key"); + const value = Number(expectDefined(metricMatch[2], `gateway restart ${key} metric value`)); if (!Number.isFinite(value)) { continue; } diff --git a/scripts/bench-gateway-startup.ts b/scripts/bench-gateway-startup.ts index 56fbea9fefe..89efc9f179c 100644 --- a/scripts/bench-gateway-startup.ts +++ b/scripts/bench-gateway-startup.ts @@ -5,6 +5,7 @@ import { tmpdir } from "node:os"; import path from "node:path"; import { performance } from "node:perf_hooks"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts"; import { delay, stopChild } from "./lib/gateway-bench-child.ts"; import { @@ -351,7 +352,11 @@ function median(values: number[]): number { const sorted = [...values].toSorted((a, b) => a - b); const middle = Math.floor(sorted.length / 2); if (sorted.length % 2 === 0) { - return (sorted[middle - 1] + sorted[middle]) / 2; + return ( + (expectDefined(sorted[middle - 1], "lower middle gateway startup sample") + + expectDefined(sorted[middle], "upper middle gateway startup sample")) / + 2 + ); } return sorted[middle] ?? 0; } @@ -694,10 +699,13 @@ function sanitizedEnv( function collectStartupTrace(line: string, startupTrace: Record): void { const phaseMatch = /startup trace: ([^ ]+) ([0-9.]+)ms total=([0-9.]+)ms(?: (.*))?/u.exec(line); if (phaseMatch) { - startupTrace[phaseMatch[1]] = Number(phaseMatch[2]); - startupTrace[`${phaseMatch[1]}.total`] = Number(phaseMatch[3]); + const phase = expectDefined(phaseMatch[1], "gateway startup trace phase name"); + startupTrace[phase] = Number(expectDefined(phaseMatch[2], `${phase} startup duration`)); + startupTrace[`${phase}.total`] = Number( + expectDefined(phaseMatch[3], `${phase} total startup duration`), + ); for (const metric of parseStartupTraceMetrics(phaseMatch[4] ?? "")) { - startupTrace[`${phaseMatch[1]}.${metric.key}`] = metric.value; + startupTrace[`${phase}.${metric.key}`] = metric.value; } return; } @@ -705,8 +713,10 @@ function collectStartupTrace(line: string, startupTrace: Record) if (!detailMatch) { return; } - for (const metric of parseStartupTraceMetrics(detailMatch[2])) { - startupTrace[`${detailMatch[1]}.${metric.key}`] = metric.value; + const phase = expectDefined(detailMatch[1], "gateway startup detail phase name"); + const detail = expectDefined(detailMatch[2], `${phase} startup detail metrics`); + for (const metric of parseStartupTraceMetrics(detail)) { + startupTrace[`${phase}.${metric.key}`] = metric.value; } } @@ -727,8 +737,8 @@ function parseStartupTraceMetrics(raw: string): Array<{ key: string; value: numb if (!metricMatch) { continue; } - const key = metricMatch[1]; - const value = Number(metricMatch[2]); + const key = expectDefined(metricMatch[1], "gateway startup trace metric key"); + const value = Number(expectDefined(metricMatch[2], `${key} startup trace metric value`)); if ( !Number.isFinite(value) || (key !== "eventLoopMax" && diff --git a/scripts/bench-model.ts b/scripts/bench-model.ts index a1d79e2cdf1..3809aa2e1e2 100644 --- a/scripts/bench-model.ts +++ b/scripts/bench-model.ts @@ -1,6 +1,7 @@ // Bench Model script supports OpenClaw repository automation. import { pathToFileURL } from "node:url"; import { completeSimple, type Model } from "openclaw/plugin-sdk/llm"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts"; type Usage = { @@ -111,9 +112,13 @@ function median(values: number[]): number { const sorted = [...values].toSorted((a, b) => a - b); const mid = Math.floor(sorted.length / 2); if (sorted.length % 2 === 0) { - return Math.round((sorted[mid - 1] + sorted[mid]) / 2); + return Math.round( + (expectDefined(sorted[mid - 1], "lower middle model benchmark sample") + + expectDefined(sorted[mid], "upper middle model benchmark sample")) / + 2, + ); } - return sorted[mid]; + return expectDefined(sorted[mid], "middle model benchmark sample"); } async function runModel(opts: { diff --git a/scripts/bench-sqlite-state.ts b/scripts/bench-sqlite-state.ts index 29361c34c53..1a7d1862f29 100644 --- a/scripts/bench-sqlite-state.ts +++ b/scripts/bench-sqlite-state.ts @@ -4,6 +4,7 @@ import os from "node:os"; import path from "node:path"; import type { DatabaseSync, SQLInputValue } from "node:sqlite"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { openOpenClawAgentDatabase, closeOpenClawAgentDatabasesForTest, @@ -459,7 +460,7 @@ function percentile(values: number[], pct: number): number { } const sorted = values.toSorted((left, right) => left - right); const index = Math.min(sorted.length - 1, Math.ceil((pct / 100) * sorted.length) - 1); - return Number(sorted[index].toFixed(3)); + return Number(expectDefined(sorted[index], `SQLite benchmark percentile ${pct}`).toFixed(3)); } function runTimedQuery( diff --git a/scripts/check-temp-path-guardrails.ts b/scripts/check-temp-path-guardrails.ts index 0750d403e6c..1d4f98e73a7 100644 --- a/scripts/check-temp-path-guardrails.ts +++ b/scripts/check-temp-path-guardrails.ts @@ -74,7 +74,7 @@ function findMatchingParen(source: string, openIndex: number): number { let depth = 1; const quoteState: QuoteScanState = { quote: null, escaped: false }; for (let i = openIndex + 1; i < source.length; i += 1) { - const ch = source[i]; + const ch = source.charAt(i); if (consumeQuotedChar(quoteState, ch)) { continue; } @@ -192,7 +192,8 @@ function hasDynamicTmpdirJoin(source: string): boolean { if (closeParenIndex !== -1) { const argsSource = scanSource.slice(openParenIndex + 1, closeParenIndex); const args = splitTopLevelArguments(argsSource); - if (args.length >= 2 && isOsTmpdirExpression(args[0])) { + const firstArg = args[0]; + if (firstArg && isOsTmpdirExpression(firstArg)) { for (const arg of args.slice(1)) { const trimmed = arg.trim(); if (trimmed.startsWith("`") && trimmed.includes("${")) { diff --git a/scripts/control-ui-i18n.ts b/scripts/control-ui-i18n.ts index 80c42cbc00f..778a7f5ed36 100644 --- a/scripts/control-ui-i18n.ts +++ b/scripts/control-ui-i18n.ts @@ -7,6 +7,7 @@ import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; import { completeSimple, type AssistantMessage, type Model } from "openclaw/plugin-sdk/llm"; import * as ts from "typescript"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { formatErrorMessage } from "../src/infra/errors.ts"; import { compareStringArrays, @@ -1322,7 +1323,8 @@ function extractTranslationResult(message: AssistantMessage): string { function parseTranslationReply(raw: string): Record { const trimmed = raw.trim(); const fenced = /^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/.exec(trimmed); - return JSON.parse(fenced ? fenced[1] : trimmed) as Record; + const json = fenced ? expectDefined(fenced[1], "fenced translation JSON body") : trimmed; + return JSON.parse(json); } export function parseTranslationBatchReply( diff --git a/scripts/control-ui-mock-dev.ts b/scripts/control-ui-mock-dev.ts index c1d25e0cba6..f5c27816f40 100644 --- a/scripts/control-ui-mock-dev.ts +++ b/scripts/control-ui-mock-dev.ts @@ -4,6 +4,7 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import qrcode from "qrcode"; import { createServer, type Plugin, type ViteDevServer } from "vite"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { CONTROL_UI_BOOTSTRAP_CONFIG_PATH } from "../src/gateway/control-ui-contract.js"; import { createControlUiMockBootstrapConfig, @@ -43,7 +44,7 @@ function mockFileHash(value: string): string { function parseArgs(args: string[]): CliOptions { const options: CliOptions = { allowedHosts: [], host: "127.0.0.1", port: 5187 }; for (let i = 0; i < args.length; i += 1) { - const arg = args[i]; + const arg = expectDefined(args[i], `control UI mock argument at index ${i}`); if (arg === "--allowed-host") { const allowedHost = args[++i]?.trim(); if (allowedHost) { diff --git a/scripts/debug-claude-usage.ts b/scripts/debug-claude-usage.ts index a6a6f84975e..d31a213ea37 100644 --- a/scripts/debug-claude-usage.ts +++ b/scripts/debug-claude-usage.ts @@ -5,6 +5,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { normalizeOptionalString } from "../packages/normalization-core/src/string-coerce.js"; import { readBoundedResponseText as readBoundedResponseTextWithLimit } from "./lib/bounded-response.ts"; import { @@ -44,7 +45,7 @@ const parseArgs = (args = process.argv.slice(2)): Args => { let sessionKey: string | undefined; for (let i = 0; i < args.length; i++) { - const arg = args[i]; + const arg = expectDefined(args[i], `Claude usage argument at index ${i}`); if (arg === "--agent") { agentId = parseNonBlankArgValue(parseRequiredArgValue(args, i, "--agent"), "--agent"); i += 1; diff --git a/scripts/dev/discord-acp-plain-language-smoke.ts b/scripts/dev/discord-acp-plain-language-smoke.ts index 9044324b2ff..24223f1567f 100644 --- a/scripts/dev/discord-acp-plain-language-smoke.ts +++ b/scripts/dev/discord-acp-plain-language-smoke.ts @@ -283,7 +283,7 @@ function validateCliArgs(argv: string[]): void { continue; } if (arg.startsWith("--") && arg.includes("=")) { - const [flag] = arg.split("=", 1); + const flag = arg.slice(0, arg.indexOf("=")); if (VALUE_OPTIONS.has(flag)) { continue; } diff --git a/scripts/e2e/lib/session-log-mentions.ts b/scripts/e2e/lib/session-log-mentions.ts index bcb8c24af34..23512ad9f36 100644 --- a/scripts/e2e/lib/session-log-mentions.ts +++ b/scripts/e2e/lib/session-log-mentions.ts @@ -176,7 +176,7 @@ export async function countSessionLogMentions(params: { continue; } for (const [key, needle] of Object.entries(params.needles)) { - counts[key] += countOccurrences(scanText, needle); + counts[key] = (counts[key] ?? 0) + countOccurrences(scanText, needle); } } } @@ -246,7 +246,7 @@ async function countSqliteTranscriptMentions(params: { continue; } for (const [key, needle] of Object.entries(params.needles)) { - counts[key] += countOccurrences(scanText, needle); + counts[key] = (counts[key] ?? 0) + countOccurrences(scanText, needle); } } return counts; diff --git a/scripts/embedded-run-abort-leak.ts b/scripts/embedded-run-abort-leak.ts index 76d3062cb0c..c534a8cf910 100644 --- a/scripts/embedded-run-abort-leak.ts +++ b/scripts/embedded-run-abort-leak.ts @@ -19,6 +19,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; import * as v8 from "node:v8"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; type Mode = "production" | "closure-extracted" | "closure-inline" | "synthetic-leak"; type Abortable = (signal: AbortSignal, promise: Promise) => Promise; @@ -65,7 +66,7 @@ function parseArgs(argv: string[]): Options { }; const seenValueFlags = new Set(); for (let i = 0; i < argv.length; i += 1) { - const arg = argv[i]; + const arg = expectDefined(argv[i], `embedded abort benchmark argument at index ${i}`); const next = argv[i + 1]; if (VALUE_FLAGS.has(arg)) { if (seenValueFlags.has(arg)) { diff --git a/scripts/gh-read.ts b/scripts/gh-read.ts index 6f774d91be1..060ccd23ed5 100644 --- a/scripts/gh-read.ts +++ b/scripts/gh-read.ts @@ -3,6 +3,7 @@ import { execFileSync, spawnSync } from "node:child_process"; import { createPrivateKey, createSign } from "node:crypto"; import { readFileSync } from "node:fs"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { readBoundedResponseText } from "./lib/bounded-response.ts"; import { parseStrictIntegerOption } from "./lib/dev-tooling-safety.ts"; @@ -50,7 +51,7 @@ type GitHubBodyReadOptions = { export function parseRepoArg(args: string[]): string | null { for (let i = 0; i < args.length; i += 1) { - const arg = args[i]; + const arg = expectDefined(args[i], `GitHub CLI argument at index ${i}`); if (arg === "-R" || arg === "--repo") { return normalizeRepo(args[i + 1] ?? null); } diff --git a/scripts/lib/codex-app-server-protocol-source.ts b/scripts/lib/codex-app-server-protocol-source.ts index b8883744e2f..1ef75aaf1f2 100644 --- a/scripts/lib/codex-app-server-protocol-source.ts +++ b/scripts/lib/codex-app-server-protocol-source.ts @@ -2,6 +2,7 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; +import { expectDefined } from "../../packages/normalization-core/src/expect.js"; import { resolvePnpmRunner } from "../pnpm-runner.mjs"; import { stageCodexAppServerProtocolArtifacts } from "./codex-app-server-protocol-artifacts.js"; @@ -246,7 +247,7 @@ async function resolvePrimaryWorktreeSiblingCodex(repoRoot: string): Promise { return new Promise((resolve, reject) => { @@ -98,9 +99,9 @@ export function readProcessTreeCpuMs(rootPid: number | undefined): number | null if (!match) { continue; } - const pid = Number(match[1]); - const ppid = Number(match[2]); - const cpuMs = parsePsCpuTimeMs(match[3]); + const pid = Number(expectDefined(match[1], "process id from ps output")); + const ppid = Number(expectDefined(match[2], "parent process id from ps output")); + const cpuMs = parsePsCpuTimeMs(expectDefined(match[3], "CPU time from ps output")); if (!Number.isInteger(pid) || !Number.isInteger(ppid) || cpuMs === null) { continue; } @@ -153,10 +154,21 @@ function parsePsCpuTimeMs(raw: string): number | null { return null; } if (parts.length === 2) { - return Math.round((parts[0] * 60 + parts[1]) * 1000); + const [minutes, seconds] = parts; + return Math.round( + (expectDefined(minutes, "process CPU minutes") * 60 + + expectDefined(seconds, "process CPU seconds")) * + 1000, + ); } if (parts.length === 3) { - return Math.round((parts[0] * 60 * 60 + parts[1] * 60 + parts[2]) * 1000); + const [hours, minutes, seconds] = parts; + return Math.round( + (expectDefined(hours, "process CPU hours") * 60 * 60 + + expectDefined(minutes, "process CPU minutes") * 60 + + expectDefined(seconds, "process CPU seconds")) * + 1000, + ); } return null; } diff --git a/scripts/lib/plugin-npm-release.ts b/scripts/lib/plugin-npm-release.ts index ae2773a6f99..053b9affe31 100644 --- a/scripts/lib/plugin-npm-release.ts +++ b/scripts/lib/plugin-npm-release.ts @@ -3,6 +3,7 @@ import { execFileSync } from "node:child_process"; import { mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join, resolve } from "node:path"; +import { expectDefined } from "../../packages/normalization-core/src/expect.js"; import { normalizeOptionalString } from "../../packages/normalization-core/src/string-coerce.js"; import { validateExternalCodePluginPackageJson } from "../../packages/plugin-package-contract/src/index.ts"; import { @@ -259,7 +260,7 @@ export function parsePluginReleaseArgs(argv: string[]): ParsedPluginReleaseArgs let headRef: string | undefined; for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + const arg = expectDefined(argv[index], `plugin release argument at index ${index}`); if (arg === "--") { continue; } @@ -312,7 +313,7 @@ export function parsePluginNpmReleaseArgs(argv: string[]): ParsedPluginNpmReleas const baseArgs: string[] = []; let npmDistTag: "extended-stable" | undefined; for (let index = 0; index < argv.length; index += 1) { - const arg = argv[index]; + const arg = expectDefined(argv[index], `plugin npm release argument at index ${index}`); if (arg !== "--npm-dist-tag") { baseArgs.push(arg); continue; diff --git a/scripts/lib/sqlite-session-schema-baseline.ts b/scripts/lib/sqlite-session-schema-baseline.ts index afb371ce7d6..b1fbed87151 100644 --- a/scripts/lib/sqlite-session-schema-baseline.ts +++ b/scripts/lib/sqlite-session-schema-baseline.ts @@ -2,6 +2,7 @@ import { createHash } from "node:crypto"; import fs from "node:fs/promises"; import path from "node:path"; +import { expectDefined } from "../../packages/normalization-core/src/expect.js"; /** Rendered baseline artifact for the sessions/transcripts SQLite schema. */ export type SqliteSessionSchemaBaselineRender = { @@ -62,14 +63,14 @@ function readCreatedTableName(statement: string): string | null { const match = statement.match( /^CREATE\s+TABLE\s+IF\s+NOT\s+EXISTS\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\b/iu, ); - return match ? normalizeIdentifier(match[1]) : null; + return match ? normalizeIdentifier(expectDefined(match[1], "created table name")) : null; } function readIndexedTableName(statement: string): string | null { const match = statement.match( /^CREATE\s+(?:UNIQUE\s+)?INDEX\s+IF\s+NOT\s+EXISTS\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\s+ON\s+("[^"]+"|[A-Za-z_][A-Za-z0-9_]*)\b/isu, ); - return match ? normalizeIdentifier(match[2]) : null; + return match ? normalizeIdentifier(expectDefined(match[2], "indexed table name")) : null; } function isTargetSessionSchemaStatement(statement: string): boolean { diff --git a/scripts/lib/ts-topology/analyze.ts b/scripts/lib/ts-topology/analyze.ts index 1279a84e139..350fc042ac0 100644 --- a/scripts/lib/ts-topology/analyze.ts +++ b/scripts/lib/ts-topology/analyze.ts @@ -1,6 +1,7 @@ // Analyze script supports OpenClaw repository automation. import path from "node:path"; import ts from "typescript"; +import { expectDefined } from "../../../packages/normalization-core/src/expect.js"; import { canonicalSymbolInfo, countIdentifierUsages, @@ -298,8 +299,12 @@ function finalizeRecords(records: TopologyRecord[]) { return byRefs; } return ( - left.publicSpecifiers[0].localeCompare(right.publicSpecifiers[0]) || - left.exportNames[0].localeCompare(right.exportNames[0]) + expectDefined(left.publicSpecifiers[0], "left topology public specifier").localeCompare( + expectDefined(right.publicSpecifiers[0], "right topology public specifier"), + ) || + expectDefined(left.exportNames[0], "left topology export name").localeCompare( + expectDefined(right.exportNames[0], "right topology export name"), + ) ); }); } diff --git a/scripts/native-app-i18n.ts b/scripts/native-app-i18n.ts index 27eb080b394..28f0b1c1014 100644 --- a/scripts/native-app-i18n.ts +++ b/scripts/native-app-i18n.ts @@ -2,6 +2,7 @@ import { createHash } from "node:crypto"; import { mkdir, readdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { translateNativeEntries } from "./control-ui-i18n.ts"; type NativeI18nSurface = "android" | "apple"; @@ -211,18 +212,18 @@ function isAsciiAlphaNumeric(character: string): boolean { export function isConditionalBranchIdentifier(source: string): boolean { let index = 0; - while (index < source.length && isAsciiLowercaseLetter(source[index])) { + while (index < source.length && isAsciiLowercaseLetter(source.charAt(index))) { index += 1; } // Keep this scanner linear: PR-controlled native source passes through CI, // so a backtracking regex here can become a cheap native-i18n DoS trigger. - if (index === 0 || index >= source.length || !isAsciiUppercaseLetter(source[index])) { + if (index === 0 || index >= source.length || !isAsciiUppercaseLetter(source.charAt(index))) { return false; } for (index += 1; index < source.length; index += 1) { - if (!isAsciiAlphaNumeric(source[index])) { + if (!isAsciiAlphaNumeric(source.charAt(index))) { return false; } } @@ -622,15 +623,18 @@ function identifierBefore(source: string, offset: number): string | null { cursor -= 1; } const end = cursor + 1; - while (cursor >= 0 && (isAsciiAlphaNumeric(source[cursor]) || source[cursor] === "_")) { + while ( + cursor >= 0 && + (isAsciiAlphaNumeric(source.charAt(cursor)) || source.charAt(cursor) === "_") + ) { cursor -= 1; } const start = cursor + 1; if ( start === end || - (!isAsciiLowercaseLetter(source[start]) && - !isAsciiUppercaseLetter(source[start]) && - source[start] !== "_") + (!isAsciiLowercaseLetter(source.charAt(start)) && + !isAsciiUppercaseLetter(source.charAt(start)) && + source.charAt(start) !== "_") ) { return null; } @@ -739,18 +743,18 @@ function addCapturedLiteralCandidates( function skipWhitespaceAndBrace(source: string, offset: number): number { let cursor = offset; - while (cursor < source.length && /\s/u.test(source[cursor])) { + while (cursor < source.length && /\s/u.test(source.charAt(cursor))) { cursor += 1; } - if (source[cursor] === "{") { + if (source.charAt(cursor) === "{") { cursor += 1; - while (cursor < source.length && /\s/u.test(source[cursor])) { + while (cursor < source.length && /\s/u.test(source.charAt(cursor))) { cursor += 1; } } if (source.startsWith("return", cursor) && !isAsciiAlphaNumeric(source[cursor + 6] ?? "")) { cursor += 6; - while (cursor < source.length && /\s/u.test(source[cursor])) { + while (cursor < source.length && /\s/u.test(source.charAt(cursor))) { cursor += 1; } } @@ -1189,7 +1193,9 @@ async function mapWithConcurrency( if (index >= values.length) { return; } - results[index] = await run(values[index]); + results[index] = await run( + expectDefined(values[index], `native i18n concurrency input at index ${index}`), + ); } }), ); @@ -1306,10 +1312,14 @@ function adjacentDuplicateWords(value: string, locale: string): string[] { const duplicates = new Set(); for (let index = 1; index < words.length; index += 1) { if ( - words[index - 1].normalize("NFKC").toLocaleLowerCase(locale) === - words[index].normalize("NFKC").toLocaleLowerCase(locale) + expectDefined(words[index - 1], `native i18n word before index ${index}`) + .normalize("NFKC") + .toLocaleLowerCase(locale) === + expectDefined(words[index], `native i18n word at index ${index}`) + .normalize("NFKC") + .toLocaleLowerCase(locale) ) { - duplicates.add(words[index]); + duplicates.add(expectDefined(words[index], `duplicate native i18n word at index ${index}`)); } } return [...duplicates].toSorted(compareCodePoints); @@ -1443,8 +1453,8 @@ export function validateNativeLocaleArtifact( errors.push(`entry count must be ${inventory.length}, got ${rawEntries.length}`); } for (let index = 0; index < Math.min(entries.length, inventory.length); index += 1) { - const actual = entries[index]; - const expected = inventory[index]; + const actual = expectDefined(entries[index], `native locale entry at index ${index}`); + const expected = expectDefined(inventory[index], `native inventory entry at index ${index}`); if (actual.id !== expected.id) { errors.push( `entries[${index}].id must be ${JSON.stringify(expected.id)}, got ${JSON.stringify(actual.id)}`, diff --git a/scripts/openclaw-cross-os-release-checks.ts b/scripts/openclaw-cross-os-release-checks.ts index f79687b06ab..201c64e43b6 100644 --- a/scripts/openclaw-cross-os-release-checks.ts +++ b/scripts/openclaw-cross-os-release-checks.ts @@ -349,6 +349,9 @@ export function parseArgs(argv: string[]): ParsedArgs { const parsed: ParsedArgs = {}; for (let index = 0; index < argv.length; index += 1) { const token = argv[index]; + if (token === undefined) { + throw new Error(`Missing cross-OS release argument at index ${index}`); + } if (!token.startsWith("--")) { continue; } @@ -4081,16 +4084,16 @@ function parseAgentPayloadTexts(stdout: string) { : Array.isArray(payload?.result?.payloads) ? payload.result.payloads : []; - const payloadTexts = Array.isArray(entries) - ? entries.flatMap((entry) => (typeof entry?.text === "string" ? [entry.text] : [])) - : []; + const payloadTexts = entries.flatMap((entry) => + typeof entry?.text === "string" ? [entry.text] : [], + ); return [...directTexts, ...payloadTexts]; } catch { const finalTextMatches = [ ...stdout.matchAll( /"(?:finalAssistantVisibleText|finalAssistantRawText|text)"\s*:\s*"([^"]*)"/gu, ), - ].map((match) => match[1]); + ].flatMap((match) => (match[1] === undefined ? [] : [match[1]])); return finalTextMatches.length > 0 ? finalTextMatches : stdout.trim() ? [stdout] : []; } } diff --git a/scripts/openclaw-npm-postpublish-verify.ts b/scripts/openclaw-npm-postpublish-verify.ts index 097fcdbffe0..59b46f3b507 100644 --- a/scripts/openclaw-npm-postpublish-verify.ts +++ b/scripts/openclaw-npm-postpublish-verify.ts @@ -24,6 +24,7 @@ import { win32 as pathWin32, } from "node:path"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { formatErrorMessage } from "../src/infra/errors.ts"; import { ALWAYS_ALLOWED_RUNTIME_DIR_NAMES } from "../src/plugin-sdk/facade-activation-contract.ts"; import { BUNDLED_RUNTIME_SIDECAR_PATHS } from "../src/plugins/runtime-sidecar-paths.ts"; @@ -459,7 +460,10 @@ export function collectInstalledBundledRuntimeSidecarPaths(packageRoot: string): const installedExtensionIds = collectInstalledBundledExtensionIds(packageRoot); return PUBLISHED_BUNDLED_RUNTIME_SIDECAR_PATHS.filter((relativePath) => { const match = /^dist\/extensions\/([^/]+)\//u.exec(relativePath); - return match !== null && installedExtensionIds.has(match[1]); + return ( + match !== null && + installedExtensionIds.has(expectDefined(match[1], "bundled runtime extension id")) + ); }); } @@ -896,7 +900,7 @@ function collectExpectedBundledExtensionPackageIds(): ReadonlySet { for (const relativePath of listBundledPluginPackArtifacts()) { const match = /^dist\/extensions\/([^/]+)\/package\.json$/u.exec(relativePath); if (match) { - ids.add(match[1]); + ids.add(expectDefined(match[1], "bundled package extension id")); } } return ids; diff --git a/scripts/openclaw-npm-prepublish-verify.ts b/scripts/openclaw-npm-prepublish-verify.ts index 09d05d0ffcd..9ef88ae8383 100644 --- a/scripts/openclaw-npm-prepublish-verify.ts +++ b/scripts/openclaw-npm-prepublish-verify.ts @@ -5,6 +5,7 @@ import { mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, writeFileSy import { tmpdir } from "node:os"; import { join } from "node:path"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { formatErrorMessage } from "../src/infra/errors.ts"; import { type NpmVerifyCommandInvocation, runNpmVerifyCommand } from "./lib/npm-verify-exec.ts"; import { runInstalledWorkspaceBootstrapSmoke } from "./lib/workspace-bootstrap-smoke.mjs"; @@ -105,7 +106,11 @@ function main(argv = process.argv.slice(2)): void { { private: true, dependencies: { - "@openclaw/ai": pathToFileURL(realpathSync(args.dependencyTarballPaths[0])).href, + "@openclaw/ai": pathToFileURL( + realpathSync( + expectDefined(args.dependencyTarballPaths[0], "prepared dependency tarball"), + ), + ).href, openclaw: pathToFileURL(realpathSync(args.tarballPath)).href, }, }, diff --git a/scripts/openclaw-npm-release-check.ts b/scripts/openclaw-npm-release-check.ts index 4b670446cd7..deede407d8f 100644 --- a/scripts/openclaw-npm-release-check.ts +++ b/scripts/openclaw-npm-release-check.ts @@ -163,7 +163,7 @@ function isNodeModulesPackageRoot(segments: string[], index: number): boolean { if (parent === "node_modules") { return true; } - return parent?.startsWith("@") && segments[index - 2] === "node_modules"; + return parent !== undefined && parent.startsWith("@") && segments[index - 2] === "node_modules"; } function pathContainsPackedTestCargo(packedPath: string): boolean { diff --git a/scripts/prepare-codex-ci-config.ts b/scripts/prepare-codex-ci-config.ts index 3eaac1bd66a..c2412da4c73 100644 --- a/scripts/prepare-codex-ci-config.ts +++ b/scripts/prepare-codex-ci-config.ts @@ -1,6 +1,7 @@ // Prepare Codex Ci Config script supports OpenClaw repository automation. import fs from "node:fs/promises"; import path from "node:path"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; function tomlString(value: string): string { return JSON.stringify(value); @@ -50,7 +51,7 @@ export async function writeCiSafeCodexConfig(params: { } if (path.basename(process.argv[1] ?? "") === "prepare-codex-ci-config.ts") { - const outputPath = process.argv[2]; + const outputPath = expectDefined(process.argv[2], "Codex CI config output path"); const projectPath = process.argv[3] ?? process.cwd(); await writeCiSafeCodexConfig({ outputPath, projectPath }); } diff --git a/scripts/protocol-gen-swift.ts b/scripts/protocol-gen-swift.ts index 6f1d3049280..11d7a92821a 100644 --- a/scripts/protocol-gen-swift.ts +++ b/scripts/protocol-gen-swift.ts @@ -84,7 +84,7 @@ function camelCase(input: string) { .trim() .toLowerCase() .split(/\s+/) - .map((p, i) => (i === 0 ? p : p[0].toUpperCase() + p.slice(1))) + .map((p, i) => (i === 0 ? p : p.charAt(0).toUpperCase() + p.slice(1))) .join(""); } diff --git a/scripts/qa-e2e.ts b/scripts/qa-e2e.ts index 41e5240e2b0..3cc2b763241 100644 --- a/scripts/qa-e2e.ts +++ b/scripts/qa-e2e.ts @@ -118,7 +118,7 @@ export async function main( function isMainModule() { const entry = process.argv[1]; - return Boolean(entry) && import.meta.url === pathToFileURL(entry).href; + return entry !== undefined && import.meta.url === pathToFileURL(entry).href; } if (isMainModule()) { diff --git a/scripts/qa/render-maturity-docs.ts b/scripts/qa/render-maturity-docs.ts index b4f7eca7a50..1a1ad292f86 100644 --- a/scripts/qa/render-maturity-docs.ts +++ b/scripts/qa/render-maturity-docs.ts @@ -841,7 +841,7 @@ function copyStaticSourceAssets({ taxonomyPath: string; }): string[] { fs.mkdirSync(staticAssetsDir, { recursive: true }); - const copied = [ + const copied: Array<[string, string]> = [ [taxonomyPath, path.join(staticAssetsDir, "taxonomy.yaml")], [scoresPath, path.join(staticAssetsDir, "maturity-scores.yaml")], ]; diff --git a/scripts/release-check.ts b/scripts/release-check.ts index 84baf2a1bee..51cc8314e9c 100755 --- a/scripts/release-check.ts +++ b/scripts/release-check.ts @@ -18,6 +18,7 @@ import type { Dirent } from "node:fs"; import { tmpdir } from "node:os"; import { basename, dirname, join, resolve, win32 } from "node:path"; import { pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { COMPLETION_SKIP_PLUGIN_COMMANDS_ENV } from "../src/cli/completion-runtime.ts"; import { isLegacyPluginDependencyInstallStagePath, @@ -89,7 +90,12 @@ const requiredPathGroups = [ ...listBundledPluginPackArtifacts(), ...listStaticExtensionAssetOutputs().filter((relativePath) => { const match = /^dist\/extensions\/([^/]+)\//u.exec(relativePath); - return !match || !rootPackageExcludedExtensionDirs.has(match[1]); + return ( + !match || + !rootPackageExcludedExtensionDirs.has( + expectDefined(match[1], "release-check extension artifact id"), + ) + ); }), ...WORKSPACE_TEMPLATE_PACK_PATHS, "scripts/npm-runner.mjs", @@ -422,7 +428,7 @@ export function resolvePackedTarballPath(packDestination: string, results: PackR `release-check: npm pack produced ${filenames.length} tarballs; expected exactly one.`, ); } - const filename = filenames[0]; + const filename = expectDefined(filenames[0], "npm pack tarball filename"); const filenameBasename = basename(filename); const resolvedDestination = resolve(packDestination); const resolvedTarball = resolve(resolvedDestination, filenameBasename); @@ -1112,7 +1118,8 @@ export function collectAppcastSparkleVersionErrors(xml: string): string[] { errors.push("appcast.xml contains no entries."); } - for (const [, item] of itemMatches) { + for (const [index, match] of itemMatches.entries()) { + const item = expectDefined(match[1], `appcast item body at index ${index}`); const title = extractTag(item, "title") ?? "unknown"; const shortVersion = extractTag(item, "sparkle:shortVersionString"); const sparkleVersion = extractTag(item, "sparkle:version"); diff --git a/scripts/release-version.ts b/scripts/release-version.ts index 8eea9213699..5b1c9af3092 100644 --- a/scripts/release-version.ts +++ b/scripts/release-version.ts @@ -1,6 +1,7 @@ // Release Version keeps the core and explicitly selected native release trains aligned. import fs from "node:fs"; import path from "node:path"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { canonicalAndroidVersionCode, normalizeAndroidVersionCode, @@ -131,7 +132,10 @@ export function applyReleaseVersionPlan(plan: ReleaseVersionPlan): void { tempPaths.push(tempPath); } for (const [index, change] of plan.changes.entries()) { - fs.renameSync(tempPaths[index], change.path); + fs.renameSync( + expectDefined(tempPaths[index], `release temp path at index ${index}`), + change.path, + ); } } finally { for (const tempPath of tempPaths) { diff --git a/scripts/sync-labels.ts b/scripts/sync-labels.ts index 4a574fadb8a..968429c8122 100644 --- a/scripts/sync-labels.ts +++ b/scripts/sync-labels.ts @@ -109,7 +109,7 @@ function resolveLabelMetadata(label: string): { color: string; description?: str if (extraMetadata) { return extraMetadata; } - const prefix = label.includes(":") ? label.split(":", 1)[0].trim() : label.trim(); + const prefix = label.includes(":") ? label.slice(0, label.indexOf(":")).trim() : label.trim(); return { color: COLOR_BY_PREFIX.get(prefix) ?? "ededed" }; } diff --git a/scripts/test-shell-completion.ts b/scripts/test-shell-completion.ts index d229dd52848..680119ea093 100644 --- a/scripts/test-shell-completion.ts +++ b/scripts/test-shell-completion.ts @@ -27,6 +27,7 @@ import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import { confirm, isCancel } from "@clack/prompts"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { stylePromptMessage } from "../packages/terminal-core/src/prompt-style.js"; import { theme } from "../packages/terminal-core/src/theme.js"; import { @@ -58,7 +59,7 @@ function parseArgs(args: string[]): Options { }; for (let index = 0; index < args.length; index += 1) { - const arg = args[index]; + const arg = expectDefined(args[index], `shell completion argument at index ${index}`); if (arg === "--check-only") { options.checkOnly = true; } else if (arg === "--force") { diff --git a/scripts/ts-topology.ts b/scripts/ts-topology.ts index 87e9d208003..47968ec2fc1 100644 --- a/scripts/ts-topology.ts +++ b/scripts/ts-topology.ts @@ -1,6 +1,7 @@ #!/usr/bin/env node // Ts Topology script supports OpenClaw repository automation. import path from "node:path"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import { formatErrorMessage } from "../src/infra/errors.ts"; import { parsePositiveInt } from "./lib/numeric-options.mjs"; import { analyzeTopology } from "./lib/ts-topology/analyze.js"; @@ -88,7 +89,7 @@ function parseArgs(argv: string[]): CliOptions { options.report = (value as TopologyReportName | undefined) ?? options.report; break; case "--limit": - options.limit = parsePositiveInt(value, "--limit"); + options.limit = parsePositiveInt(expectDefined(value, "--limit value"), "--limit"); break; case "--repo-root": options.repoRoot = path.resolve(value ?? options.repoRoot); diff --git a/scripts/update-clawtributors.ts b/scripts/update-clawtributors.ts index fc7c6aede3e..e8392e5cc1a 100644 --- a/scripts/update-clawtributors.ts +++ b/scripts/update-clawtributors.ts @@ -2,6 +2,7 @@ import { execFileSync, execSync } from "node:child_process"; import { readFileSync, writeFileSync } from "node:fs"; import { resolve } from "node:path"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import type { ApiContributor, Entry, MapConfig, User } from "./update-clawtributors.types.js"; const REPO = "openclaw/openclaw"; @@ -99,13 +100,13 @@ for (const line of log.split("\n")) { } // Skip docs paths so bulk-generated i18n scaffolds don't inflate rankings - const filePath = parts[2]; + const filePath = expectDefined(parts[2], "git numstat file path"); if (filePath.startsWith("docs/")) { continue; } - const adds = parseCount(parts[0]); - const dels = parseCount(parts[1]); + const adds = parseCount(expectDefined(parts[0], "git numstat additions")); + const dels = parseCount(expectDefined(parts[1], "git numstat deletions")); const total = adds + dels; if (!total) { continue; @@ -649,7 +650,10 @@ async function mapConcurrent( const workers = Array.from({ length: Math.max(1, Math.min(limit, items.length)) }, async () => { while (nextIndex < items.length) { const index = nextIndex++; - results[index] = await mapper(items[index], index); + results[index] = await mapper( + expectDefined(items[index], `clawtributor concurrency input at index ${index}`), + index, + ); } }); await Promise.all(workers); @@ -702,7 +706,7 @@ function readJpegDimensions(buffer: Buffer): { width: number; height: number } | continue; } - const marker = buffer[offset + 1]; + const marker = expectDefined(buffer[offset + 1], `JPEG marker at byte ${offset + 1}`); offset += 2; if (marker === 0xd8 || marker === 0xd9) { @@ -759,13 +763,15 @@ function resolveLogin( } if (email && email.endsWith("@users.noreply.github.com")) { - const local = email.split("@", 1)[0]; - const login = local.includes("+") ? local.split("+")[1] : local; + const local = expectDefined(email.split("@", 1)[0], "GitHub noreply email local part"); + const login = local.includes("+") + ? expectDefined(local.split("+")[1], "GitHub noreply email login suffix") + : local; return normalizeLogin(login); } if (email && email.endsWith("@github.com")) { - const login = email.split("@", 1)[0]; + const login = expectDefined(email.split("@", 1)[0], "GitHub email local part"); if (apiByLoginValue.has(login.toLowerCase())) { return normalizeLogin(login); } diff --git a/scripts/write-cli-startup-metadata.ts b/scripts/write-cli-startup-metadata.ts index 2bfd7f3606b..871676d8966 100644 --- a/scripts/write-cli-startup-metadata.ts +++ b/scripts/write-cli-startup-metadata.ts @@ -4,6 +4,7 @@ import { createHash } from "node:crypto"; import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; +import { expectDefined } from "../packages/normalization-core/src/expect.js"; import type { RootHelpRenderOptions } from "../src/cli/program/root-help.js"; import type { OpenClawConfig } from "../src/config/config.js"; import { resolveWindowsTaskkillPath } from "./lib/windows-taskkill.mjs"; @@ -364,7 +365,9 @@ async function mapWithConcurrency( if (index >= values.length) { return; } - results[index] = await run(values[index]); + results[index] = await run( + expectDefined(values[index], `CLI metadata concurrency input at index ${index}`), + ); } }), ); diff --git a/tsconfig.scripts.json b/tsconfig.scripts.json index c253fa9a762..2639e794702 100644 --- a/tsconfig.scripts.json +++ b/tsconfig.scripts.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "noUncheckedIndexedAccess": true, "noUnusedLocals": true, "noUnusedParameters": true, "tsBuildInfoFile": ".artifacts/tsgo-cache/scripts.tsbuildinfo"