fix(ci): annotate unsafe boundary casts

This commit is contained in:
Peter Steinberger
2026-05-31 03:51:09 -04:00
parent ae651e7210
commit 8a40f90f62
8 changed files with 33 additions and 9 deletions

View File

@@ -120,6 +120,7 @@ function resolveAnthropicVertexProjectIdFromAdc(
return undefined;
}
try {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- ADC credentials are external JSON; only optional project ids are consumed.
const parsed = JSON.parse(readFileSync(credentialsPath, "utf8")) as AdcProjectFile;
return (
normalizeOptionalSecretInput(parsed.project_id) ||

View File

@@ -115,6 +115,7 @@ function createAnthropicVertexOnPayload(params: {
function applyPolicy(payload: unknown): unknown {
if (payload && typeof payload === "object" && !Array.isArray(payload)) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Anthropic payload policy mutates open provider JSON params.
applyAnthropicPayloadPolicyToParams(payload as Record<string, unknown>, policy);
}
return payload;
@@ -149,6 +150,7 @@ export function createAnthropicVertexStreamFn(
});
return (model, context, options) => {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- This stream adapter is registered only for Anthropic Messages models.
const transportModel = model as Model<"anthropic-messages"> & {
api: string;
baseUrl?: string;
@@ -188,7 +190,8 @@ export function createAnthropicVertexStreamFn(
const budgets = options.thinkingBudgets;
opts.thinkingBudgetTokens =
(budgets && options.reasoning in budgets
? budgets[options.reasoning as keyof typeof budgets]
? // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- The key presence check above narrows runtime indexing for configured budgets.
budgets[options.reasoning as keyof typeof budgets]
: undefined) ?? 10000;
}
} else {

View File

@@ -67,7 +67,9 @@ function mergeMattermostAccountConfig(
accountId: string,
): MattermostAccountConfig {
return resolveMergedAccountConfig<MattermostAccountConfig>({
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Mattermost channel config is plugin-owned open config merged by the account resolver.
channelConfig: cfg.channels?.mattermost as MattermostAccountConfig | undefined,
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Mattermost account entries are plugin-owned open config records.
accounts: cfg.channels?.mattermost?.accounts as
| Record<string, Partial<MattermostAccountConfig>>
| undefined,

View File

@@ -82,6 +82,7 @@ function buildMattermostApiUrl(baseUrl: string, path: string): string {
export async function readMattermostError(res: Response): Promise<string> {
const contentType = res.headers.get("content-type") ?? "";
if (contentType.includes("application/json")) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Mattermost error payloads are external JSON; only optional message is consumed.
const data = (await res.json()) as { message?: string } | undefined;
if (data?.message) {
return data.message;
@@ -151,14 +152,17 @@ export function createMattermostClient(params: {
}
if (res.status === 204) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Generic request callers type the expected endpoint body; 204 has no body.
return undefined as T;
}
const contentType = res.headers.get("content-type") ?? "";
if (contentType.includes("application/json")) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Generic request callers type the expected Mattermost endpoint body.
return (await res.json()) as T;
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Generic request callers type the expected Mattermost endpoint body.
return (await res.text()) as T;
};
@@ -594,6 +598,7 @@ export async function uploadMattermostFile(
throw new Error(`Mattermost API ${res.status} ${res.statusText}: ${detail || "unknown error"}`);
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Mattermost upload response is external JSON; required file id is checked below.
const data = (await res.json()) as { file_infos?: MattermostFileInfo[] };
const info = data.file_infos?.[0];
if (!info?.id) {

View File

@@ -165,6 +165,7 @@ export async function listMicrosoftVoices(): Promise<SpeechVoiceOption[]> {
});
}
await assertOkOrThrowProviderError(response, "Microsoft voices API error");
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Microsoft voice-list payload is external JSON filtered before returning options.
const voices = (await response.json()) as MicrosoftVoiceListEntry[];
return Array.isArray(voices)
? voices

View File

@@ -42,7 +42,8 @@ type VydraJobPayload = {
function asObject(value: unknown): Record<string, unknown> | undefined {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as Record<string, unknown>)
? // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Vydra webhook payloads are external JSON objects walked defensively by key.
(value as Record<string, unknown>)
: undefined;
}

View File

@@ -351,13 +351,15 @@ function resolveTtsRuntimeConfig(cfg: OpenClawConfig): OpenClawConfig {
function asProviderConfig(value: unknown): SpeechProviderConfig {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as SpeechProviderConfig)
? // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Speech provider configs are open JSON records normalized at the plugin boundary.
(value as SpeechProviderConfig)
: {};
}
function asProviderConfigMap(value: unknown): Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value)
? (value as Record<string, unknown>)
? // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- TTS provider config maps are open JSON records keyed by provider id.
(value as Record<string, unknown>)
: {};
}
@@ -728,6 +730,7 @@ function readPrefs(prefsPath: string): TtsUserPrefs {
if (!existsSync(prefsPath)) {
return {};
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- User prefs are trusted local JSON and unknown keys are ignored by readers.
return JSON.parse(readFileSync(prefsPath, "utf8")) as TtsUserPrefs;
} catch {
return {};
@@ -900,12 +903,14 @@ export function resolveExplicitTtsOverrides(params: {
`TTS provider "${selectedProvider}" ignored the requested model or voice overrides.`,
);
}
if (!providerOverrides) {
throw new Error(`TTS provider "${selectedProvider}" did not return talk overrides.`);
}
const overridesRecord = providerOverrides as SpeechProviderOverrides;
return {
provider: selectedProvider,
providerOverrides: {
[provider.id]: overridesRecord,
[provider.id]: providerOverrides,
},
};
}
@@ -1815,9 +1820,10 @@ export async function textToSpeechTelephony(params: {
config,
provider: resolvedProvider.provider,
});
const synthesizeTelephony = resolvedProvider.provider.synthesizeTelephony as NonNullable<
typeof resolvedProvider.provider.synthesizeTelephony
>;
const synthesizeTelephony = resolvedProvider.provider.synthesizeTelephony;
if (!synthesizeTelephony) {
throw new Error(`TTS provider "${resolvedProvider.provider.id}" lost telephony support`);
}
const prepared = await prepareSpeechSynthesis({
provider: resolvedProvider.provider,
text: params.text,
@@ -2044,6 +2050,7 @@ export async function maybeApplyTtsToPayload(params: {
textForAudio = `${textForAudio.slice(0, config.maxTextLength - 3)}...`;
}
} catch (err) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Only the message is used for verbose diagnostics after a caught summarizer failure.
const error = err as Error;
logVerbose(`TTS: summarization failed, truncating instead: ${error.message}`);
textForAudio = `${textForAudio.slice(0, maxLength - 3)}...`;

View File

@@ -101,6 +101,7 @@ export function voiceProviderSupportsModel(
}
export function resolveVoiceModelRefs(config: unknown): VoiceModelRef[] {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Voice model config is an open config object; fields are normalized individually below.
const voiceModel = config as VoiceModelConfig | undefined;
if (typeof voiceModel === "string") {
const parsed = parseVoiceModelRef(voiceModel);
@@ -212,6 +213,7 @@ export function getVoiceProviderConfig<TConfig extends Record<string, unknown>>(
const configuredKeys = Object.keys(params.providerConfigs);
for (const candidate of candidates) {
if (Object.hasOwn(params.providerConfigs, candidate)) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Empty provider config preserves the caller-specific config record type.
return params.providerConfigs[candidate] ?? ({} as TConfig);
}
const normalizedCandidate = normalizeLowercaseString(candidate);
@@ -219,9 +221,11 @@ export function getVoiceProviderConfig<TConfig extends Record<string, unknown>>(
(key) => normalizeLowercaseString(key) === normalizedCandidate,
);
if (matchingKey) {
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Empty provider config preserves the caller-specific config record type.
return params.providerConfigs[matchingKey] ?? ({} as TConfig);
}
}
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Empty provider config preserves the caller-specific config record type.
return {} as TConfig;
}