mirror of
https://github.com/openclaw/openclaw.git
synced 2026-07-21 02:06:43 +00:00
feat(tooling): enforce noUncheckedIndexedAccess in the scripts lane (NUIA phase 5) (#105180)
* feat(tooling): enforce noUncheckedIndexedAccess in the scripts lane Burns down all 153 scripts-lane errors (bench aggregation, release checks, i18n inventories, argv indexing) and flips the flag in tsconfig.scripts.json. Direct-Node-executed release harness scripts use local narrowing instead of workspace imports, which do not resolve under plain node execution. Benchmark measured loops untouched. * fix(scripts): import expect helpers via relative package sources tsconfig path aliases resolve from cwd under tsx, so release wrapper scripts running against old release target cwds could not resolve @openclaw/normalization-core (not a linked root dependency). Relative package-source imports match the established pattern on the adjacent lines and are cwd-independent; old-target planning verified directly.
This commit is contained in:
@@ -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<readonly [string, string[]]> = translations.flatMap((strings, index) => {
|
||||
const locale = NATIVE_I18N_LOCALES[index];
|
||||
|
||||
@@ -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}`);
|
||||
|
||||
@@ -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<string>();
|
||||
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 {
|
||||
|
||||
@@ -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<string>();
|
||||
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>): 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;
|
||||
}
|
||||
|
||||
@@ -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<string, number>): 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<string, number>)
|
||||
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" &&
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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("${")) {
|
||||
|
||||
@@ -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<string, unknown> {
|
||||
const trimmed = raw.trim();
|
||||
const fenced = /^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/.exec(trimmed);
|
||||
return JSON.parse(fenced ? fenced[1] : trimmed) as Record<string, unknown>;
|
||||
const json = fenced ? expectDefined(fenced[1], "fenced translation JSON body") : trimmed;
|
||||
return JSON.parse(json);
|
||||
}
|
||||
|
||||
export function parseTranslationBatchReply(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 = <T>(signal: AbortSignal, promise: Promise<T>) => Promise<T>;
|
||||
@@ -65,7 +66,7 @@ function parseArgs(argv: string[]): Options {
|
||||
};
|
||||
const seenValueFlags = new Set<string>();
|
||||
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)) {
|
||||
|
||||
+2
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<str
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const gitDir = path.resolve(repoRoot, match[1].trim());
|
||||
const gitDir = path.resolve(repoRoot, expectDefined(match[1], "gitdir target").trim());
|
||||
const worktreeMarker = `${path.sep}.git${path.sep}worktrees${path.sep}`;
|
||||
const markerIndex = gitDir.indexOf(worktreeMarker);
|
||||
if (markerIndex < 0) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
|
||||
|
||||
export interface TranslationMap {
|
||||
[key: string]: string | TranslationMap;
|
||||
}
|
||||
@@ -266,7 +268,7 @@ function setNestedValue(root: TranslationMap, dottedKey: string, value: string):
|
||||
const parts = dottedKey.split(".");
|
||||
let cursor: TranslationMap = root;
|
||||
for (let index = 0; index < parts.length - 1; index += 1) {
|
||||
const key = parts[index];
|
||||
const key = expectDefined(parts[index], `translation key segment at index ${index}`);
|
||||
const next = cursor[key];
|
||||
if (!next || typeof next === "string") {
|
||||
const replacement: TranslationMap = {};
|
||||
@@ -276,7 +278,7 @@ function setNestedValue(root: TranslationMap, dottedKey: string, value: string):
|
||||
}
|
||||
cursor = next;
|
||||
}
|
||||
cursor[parts.at(-1)!] = value;
|
||||
cursor[expectDefined(parts.at(-1), "translation leaf key")] = value;
|
||||
}
|
||||
|
||||
function renderTranslationValue(value: string | TranslationMap, indent = 0): string {
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { request } from "node:http";
|
||||
import { createServer } from "node:net";
|
||||
import { expectDefined } from "../../packages/normalization-core/src/expect.js";
|
||||
|
||||
export async function getFreePort(): Promise<number> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
+27
-17
@@ -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<T, R>(
|
||||
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<string>();
|
||||
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)}`,
|
||||
|
||||
@@ -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] : [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<string> {
|
||||
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;
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -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("");
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -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()) {
|
||||
|
||||
@@ -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")],
|
||||
];
|
||||
|
||||
@@ -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 <item> 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");
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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" };
|
||||
}
|
||||
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<T, R>(
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<T, R>(
|
||||
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}`),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"tsBuildInfoFile": ".artifacts/tsgo-cache/scripts.tsbuildinfo"
|
||||
|
||||
Reference in New Issue
Block a user