diff --git a/eslint.config.mjs b/eslint.config.mjs index aa9dfe97..d8aa4a91 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -96,11 +96,6 @@ export default [ "@typescript-eslint/unbound-method": "off", // 68 violations // Enabled in the TS-only block below. - // --- Quick wins: small enough to fix and enable in a single PR --- - "@typescript-eslint/no-unsafe-enum-comparison": "off", // 11 violations - "@typescript-eslint/no-base-to-string": "off", // 7 violations - "@typescript-eslint/no-redundant-type-constituents": "off", // 5 violations - "@typescript-eslint/restrict-template-expressions": "off", // 1 violation // no-deprecated: defer — surface the warnings, but don't fail CI yet "@typescript-eslint/no-deprecated": "off", diff --git a/src/LLMProviders/BedrockChatModel.ts b/src/LLMProviders/BedrockChatModel.ts index a7e42745..6816d66a 100644 --- a/src/LLMProviders/BedrockChatModel.ts +++ b/src/LLMProviders/BedrockChatModel.ts @@ -398,7 +398,7 @@ export class BedrockChatModel extends BaseChatModel } } - private safeJsonParse(value: string): any | null { + private safeJsonParse(value: string): any { try { return JSON.parse(value); } catch { diff --git a/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts b/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts index 9044ff80..5ee1f71e 100644 --- a/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts +++ b/src/LLMProviders/githubCopilot/GitHubCopilotResponsesModel.ts @@ -38,7 +38,9 @@ export function buildGitHubCopilotAuthedFetch( ? input : typeof Request !== "undefined" && input instanceof Request ? input.url - : input.toString(); + : input instanceof URL + ? input.href + : input.url; const doRequest = async (token: string): Promise => { const copilotHeaders = provider.buildCopilotRequestHeaders(token); diff --git a/src/LLMProviders/projectManager.ts b/src/LLMProviders/projectManager.ts index 9278ff9a..65c96414 100644 --- a/src/LLMProviders/projectManager.ts +++ b/src/LLMProviders/projectManager.ts @@ -69,7 +69,8 @@ export default class ProjectManager { const settings = getSettings(); const shouldAutoIndex = settings.enableSemanticSearchV3 && - settings.indexVaultToVectorStore === VAULT_VECTOR_STORE_STRATEGY.ON_MODE_SWITCH && + (settings.indexVaultToVectorStore as VAULT_VECTOR_STORE_STRATEGY) === + VAULT_VECTOR_STORE_STRATEGY.ON_MODE_SWITCH && (getChainType() === ChainType.VAULT_QA_CHAIN || getChainType() === ChainType.COPILOT_PLUS_CHAIN); void this.getCurrentChainManager().createChainWithNewModel({ @@ -936,7 +937,7 @@ modified: ${stat ? new Date(stat.mtime).toISOString() : "unknown"}`; await this.retryNonMarkdownFile(project, failedItem.path); break; default: - logWarn(`[retryFailedItem] Unknown item type: ${failedItem.type}`); + logWarn(`[retryFailedItem] Unknown item type: ${String(failedItem.type)}`); return; } diff --git a/src/components/ui/ModelParametersEditor.tsx b/src/components/ui/ModelParametersEditor.tsx index f8b06f1b..a4629164 100644 --- a/src/components/ui/ModelParametersEditor.tsx +++ b/src/components/ui/ModelParametersEditor.tsx @@ -47,7 +47,8 @@ export function ModelParametersEditor({ onReset, showTokenLimit = true, }: ModelParametersEditorProps) { - const isOllamaModel = model.provider === ChatModelProviders.OLLAMA; + const provider = model.provider as ChatModelProviders; + const isOllamaModel = provider === ChatModelProviders.OLLAMA; // Parameter values: model.xxx ?? settings.xxx const temperature = model.temperature ?? settings.temperature; @@ -66,7 +67,7 @@ export function ModelParametersEditor({ model.name.startsWith("o3") || model.name.startsWith("o4") || model.name.startsWith("gpt-5")) && - model.provider === ChatModelProviders.OPENAI; + provider === ChatModelProviders.OPENAI; // Check if model has REASONING capability enabled const hasReasoningCapability = model.capabilities?.includes(ModelCapability.REASONING) ?? false; @@ -74,12 +75,11 @@ export function ModelParametersEditor({ // Show reasoning effort for: OpenAI reasoning models, OpenRouter, LM Studio, or any model with REASONING capability const showReasoningEffort = isOpenAIReasoningModel || - model.provider === ChatModelProviders.OPENROUTERAI || + provider === ChatModelProviders.OPENROUTERAI || model.provider === "lm_studio" || - model.provider === ChatModelProviders.LM_STUDIO || + provider === ChatModelProviders.LM_STUDIO || hasReasoningCapability; - const showVerbosity = - model.name.startsWith("gpt-5") && model.provider === ChatModelProviders.OPENAI; + const showVerbosity = model.name.startsWith("gpt-5") && provider === ChatModelProviders.OPENAI; return (
diff --git a/src/core/ChatPersistenceManager.test.ts b/src/core/ChatPersistenceManager.test.ts index 58696b96..3e4116e4 100644 --- a/src/core/ChatPersistenceManager.test.ts +++ b/src/core/ChatPersistenceManager.test.ts @@ -38,7 +38,7 @@ jest.mock("@/utils", () => ({ .join(""); } if (content && typeof content === "object" && "text" in content) { - return String((content as { text?: unknown }).text ?? ""); + return String((content as { text?: string }).text ?? ""); } return String(content ?? ""); }), diff --git a/src/core/ChatPersistenceManager.ts b/src/core/ChatPersistenceManager.ts index 06d2d7f1..4567cd52 100644 --- a/src/core/ChatPersistenceManager.ts +++ b/src/core/ChatPersistenceManager.ts @@ -848,6 +848,15 @@ ${chatContent}`; } } + /** + * Convert an unknown error to a safe lowercase string for substring matching. + */ + private errorToMessage(error: unknown): string { + if (error instanceof Error) return error.message; + if (typeof error === "string") return error; + return JSON.stringify(error); + } + /** * Determine whether an error corresponds to an ENAMETOOLONG filesystem failure. * @param error - The thrown error. @@ -858,8 +867,7 @@ ${chatContent}`; return false; } - const message = error instanceof Error ? error.message : String(error); - const normalized = message.toLowerCase(); + const normalized = this.errorToMessage(error).toLowerCase(); return normalized.includes("enametoolong") || normalized.includes("name too long"); } @@ -870,8 +878,7 @@ ${chatContent}`; if (!error) { return false; } - const message = error instanceof Error ? error.message : String(error); - return message.toLowerCase().includes("already exists"); + return this.errorToMessage(error).toLowerCase().includes("already exists"); } /** diff --git a/src/encryptionService.ts b/src/encryptionService.ts index 05e6cde6..0a8578d8 100644 --- a/src/encryptionService.ts +++ b/src/encryptionService.ts @@ -1,8 +1,7 @@ import { type CopilotSettings } from "@/settings/model"; import { Platform } from "obsidian"; -// @ts-ignore -let safeStorageInternal: Electron.SafeStorage | null = null; +let safeStorageInternal: any = null; function getSafeStorage() { if (Platform.isDesktop && safeStorageInternal) { diff --git a/src/projects/projectMigration.ts b/src/projects/projectMigration.ts index 73e09bc5..130e9627 100644 --- a/src/projects/projectMigration.ts +++ b/src/projects/projectMigration.ts @@ -324,9 +324,8 @@ export async function migrateProjectsFromSettingsToVault(app: App): Promise)[COPILOT_PROJECT_ID] ?? "" - ).trim(); + const raw = (parsed as Record)[COPILOT_PROJECT_ID]; + existingId = (typeof raw === "string" ? raw : "").trim(); } } } catch { diff --git a/src/search/dbOperations.ts b/src/search/dbOperations.ts index 38d8b6fd..356bc042 100644 --- a/src/search/dbOperations.ts +++ b/src/search/dbOperations.ts @@ -368,7 +368,7 @@ export class DBOperations { }; } - async upsert(docToSave: any): Promise { + async upsert(docToSave: any): Promise { if (!this.oramaDb) throw new Error("DB not initialized"); const db = this.oramaDb; diff --git a/src/search/v3/utils/ScoreNormalizer.ts b/src/search/v3/utils/ScoreNormalizer.ts index 97b8efe8..a4e348ff 100644 --- a/src/search/v3/utils/ScoreNormalizer.ts +++ b/src/search/v3/utils/ScoreNormalizer.ts @@ -29,11 +29,7 @@ export class ScoreNormalizer { /** * Update explanation with normalized scores */ - private updateExplanation( - explanation: any | undefined, - originalScore: number, - normalizedScore: number - ): any | undefined { + private updateExplanation(explanation: any, originalScore: number, normalizedScore: number): any { if (!explanation) return undefined; return { diff --git a/src/services/webViewerService/webViewerServiceHelpers.ts b/src/services/webViewerService/webViewerServiceHelpers.ts index 2091ffa8..89a65154 100644 --- a/src/services/webViewerService/webViewerServiceHelpers.ts +++ b/src/services/webViewerService/webViewerServiceHelpers.ts @@ -37,7 +37,11 @@ export function toStringSafe(value: unknown): string { if (value === null || value === undefined) return ""; try { if (typeof value === "object") return JSON.stringify(value); - return String(value); + if (typeof value === "function") return value.toString(); + if (typeof value === "symbol") return value.toString(); + if (typeof value === "bigint") return value.toString(); + if (typeof value === "number" || typeof value === "boolean") return String(value); + return ""; } catch { return ""; } diff --git a/src/settings/v2/utils/modelActions.ts b/src/settings/v2/utils/modelActions.ts index 92e87d4a..6d18cb2d 100644 --- a/src/settings/v2/utils/modelActions.ts +++ b/src/settings/v2/utils/modelActions.ts @@ -123,7 +123,7 @@ export async function verifyAndAddModel( // Check if model already exists const existingModel = activeModels.find( - (m) => m.name === model.name && m.provider === model.provider + (m) => m.name === model.name && (m.provider as SettingKeyProviders) === model.provider ); const alreadyExists = Boolean(existingModel); diff --git a/src/utils.ts b/src/utils.ts index 99714fa0..33bf7d77 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -823,7 +823,8 @@ export async function safeFetch( contentType: "application/json", headers: headers, method: method, - ...(methodsWithBody.includes(method) && { body: options.body?.toString() }), + ...(methodsWithBody.includes(method) && + typeof options.body === "string" && { body: options.body }), throw: false, // Don't throw so we can get the response body }); @@ -1194,7 +1195,7 @@ export function isCodexModel(model: BaseChatModel | string): boolean { export function shouldUseGitHubCopilotResponsesApi( model: Pick ): boolean { - if (model.provider !== ChatModelProviders.GITHUB_COPILOT) { + if ((model.provider as ChatModelProviders) !== ChatModelProviders.GITHUB_COPILOT) { return false; } @@ -1264,7 +1265,8 @@ export function checkModelApiKey( hasApiKey: boolean; errorNotice?: string; } { - if (model.provider === ChatModelProviders.AMAZON_BEDROCK) { + const provider = model.provider as ChatModelProviders; + if (provider === ChatModelProviders.AMAZON_BEDROCK) { const apiKey = model.apiKey || settings.amazonBedrockApiKey; if (!apiKey) { return { @@ -1279,7 +1281,7 @@ export function checkModelApiKey( } // GitHub Copilot uses OAuth, not API key - if (model.provider === ChatModelProviders.GITHUB_COPILOT) { + if (provider === ChatModelProviders.GITHUB_COPILOT) { const hasAuth = Boolean( model.apiKey || settings.githubCopilotToken || settings.githubCopilotAccessToken ); @@ -1293,7 +1295,7 @@ export function checkModelApiKey( return { hasApiKey: true }; } - const needSetKeyPath = !!getNeedSetKeyProvider().find((provider) => provider === model.provider); + const needSetKeyPath = !!getNeedSetKeyProvider().find((p) => p === provider); const hasNoApiKey = !getApiKeyForProvider(model.provider as SettingKeyProviders, model); // For Providers that require setting a key in the dialog, an inspection is necessary.