diff --git a/open-sse/config/audioRegistry.js b/open-sse/config/audioRegistry.ts similarity index 83% rename from open-sse/config/audioRegistry.js rename to open-sse/config/audioRegistry.ts index 5f7456b6a9..cf6e11e0b0 100644 --- a/open-sse/config/audioRegistry.js +++ b/open-sse/config/audioRegistry.ts @@ -6,7 +6,22 @@ * - /v1/audio/speech (TTS API) */ -export const AUDIO_TRANSCRIPTION_PROVIDERS = { +interface AudioModel { + id: string; + name: string; +} + +interface AudioProvider { + id: string; + baseUrl: string; + authType: string; + authHeader: string; + format?: string; + async?: boolean; + models: AudioModel[]; +} + +export const AUDIO_TRANSCRIPTION_PROVIDERS: Record = { openai: { id: "openai", baseUrl: "https://api.openai.com/v1/audio/transcriptions", @@ -57,7 +72,7 @@ export const AUDIO_TRANSCRIPTION_PROVIDERS = { }, }; -export const AUDIO_SPEECH_PROVIDERS = { +export const AUDIO_SPEECH_PROVIDERS: Record = { openai: { id: "openai", baseUrl: "https://api.openai.com/v1/audio/speech", @@ -96,21 +111,21 @@ export const AUDIO_SPEECH_PROVIDERS = { /** * Get transcription provider config by ID */ -export function getTranscriptionProvider(providerId) { +export function getTranscriptionProvider(providerId: string): AudioProvider | null { return AUDIO_TRANSCRIPTION_PROVIDERS[providerId] || null; } /** * Get speech provider config by ID */ -export function getSpeechProvider(providerId) { +export function getSpeechProvider(providerId: string): AudioProvider | null { return AUDIO_SPEECH_PROVIDERS[providerId] || null; } /** * Parse audio model string (format: "provider/model" or just "model") */ -function parseAudioModel(modelStr, registry) { +function parseAudioModel(modelStr: string | null, registry: Record): { provider: string | null; model: string | null } { if (!modelStr) return { provider: null, model: null }; for (const [providerId, config] of Object.entries(registry)) { @@ -128,11 +143,11 @@ function parseAudioModel(modelStr, registry) { return { provider: null, model: modelStr }; } -export function parseTranscriptionModel(modelStr) { +export function parseTranscriptionModel(modelStr: string | null) { return parseAudioModel(modelStr, AUDIO_TRANSCRIPTION_PROVIDERS); } -export function parseSpeechModel(modelStr) { +export function parseSpeechModel(modelStr: string | null) { return parseAudioModel(modelStr, AUDIO_SPEECH_PROVIDERS); } diff --git a/open-sse/config/codexInstructions.js b/open-sse/config/codexInstructions.ts similarity index 100% rename from open-sse/config/codexInstructions.js rename to open-sse/config/codexInstructions.ts diff --git a/open-sse/config/constants.js b/open-sse/config/constants.ts similarity index 100% rename from open-sse/config/constants.js rename to open-sse/config/constants.ts diff --git a/open-sse/config/credentialLoader.js b/open-sse/config/credentialLoader.ts similarity index 100% rename from open-sse/config/credentialLoader.js rename to open-sse/config/credentialLoader.ts diff --git a/open-sse/config/defaultThinkingSignature.js b/open-sse/config/defaultThinkingSignature.ts similarity index 100% rename from open-sse/config/defaultThinkingSignature.js rename to open-sse/config/defaultThinkingSignature.ts diff --git a/open-sse/config/embeddingRegistry.js b/open-sse/config/embeddingRegistry.ts similarity index 100% rename from open-sse/config/embeddingRegistry.js rename to open-sse/config/embeddingRegistry.ts diff --git a/open-sse/config/imageRegistry.js b/open-sse/config/imageRegistry.ts similarity index 100% rename from open-sse/config/imageRegistry.js rename to open-sse/config/imageRegistry.ts diff --git a/open-sse/config/moderationRegistry.js b/open-sse/config/moderationRegistry.ts similarity index 100% rename from open-sse/config/moderationRegistry.js rename to open-sse/config/moderationRegistry.ts diff --git a/open-sse/config/ollamaModels.js b/open-sse/config/ollamaModels.ts similarity index 100% rename from open-sse/config/ollamaModels.js rename to open-sse/config/ollamaModels.ts diff --git a/open-sse/config/providerModels.js b/open-sse/config/providerModels.ts similarity index 100% rename from open-sse/config/providerModels.js rename to open-sse/config/providerModels.ts diff --git a/open-sse/config/providerRegistry.js b/open-sse/config/providerRegistry.ts similarity index 94% rename from open-sse/config/providerRegistry.js rename to open-sse/config/providerRegistry.ts index 063c46abee..ecfc9db06e 100644 --- a/open-sse/config/providerRegistry.js +++ b/open-sse/config/providerRegistry.ts @@ -6,9 +6,51 @@ * is auto-generated from this registry. */ +// ── Types ───────────────────────────────────────────────────────────────── + +export interface RegistryModel { + id: string; + name: string; + targetFormat?: string; +} + +export interface RegistryOAuth { + clientIdEnv?: string; + clientIdDefault?: string; + clientSecretEnv?: string; + clientSecretDefault?: string; + tokenUrl?: string; + refreshUrl?: string; + authUrl?: string; + initiateUrl?: string; + pollUrlBase?: string; +} + +export interface RegistryEntry { + id: string; + alias: string; + format: string; + executor: string; + baseUrl?: string; + baseUrls?: string[]; + responsesBaseUrl?: string; + urlSuffix?: string; + urlBuilder?: (base: string, model: string, stream: boolean) => string; + authType: string; + authHeader: string; + authPrefix?: string; + headers?: Record; + extraHeaders?: Record; + oauth?: RegistryOAuth; + models: RegistryModel[]; + chatPath?: string; + clientVersion?: string; + passthroughModels?: boolean; +} + // ── Registry ────────────────────────────────────────────────────────────── -export const REGISTRY = { +export const REGISTRY: Record = { // ─── OAuth Providers ─────────────────────────────────────────────────── claude: { id: "claude", @@ -760,10 +802,10 @@ export const REGISTRY = { // ── Generator Functions ─────────────────────────────────────────────────── /** Generate legacy PROVIDERS object shape for constants.js backward compatibility */ -export function generateLegacyProviders() { - const providers = {}; +export function generateLegacyProviders(): Record { + const providers: Record = {}; for (const [id, entry] of Object.entries(REGISTRY)) { - const p = { format: entry.format }; + const p: Record = { format: entry.format }; // URL(s) if (entry.baseUrls) { @@ -808,8 +850,8 @@ export function generateLegacyProviders() { } /** Generate PROVIDER_MODELS map (alias → model list) */ -export function generateModels() { - const models = {}; +export function generateModels(): Record { + const models: Record = {}; for (const entry of Object.values(REGISTRY)) { if (entry.models && entry.models.length > 0) { const key = entry.alias || entry.id; @@ -823,8 +865,8 @@ export function generateModels() { } /** Generate PROVIDER_ID_TO_ALIAS map */ -export function generateAliasMap() { - const map = {}; +export function generateAliasMap(): Record { + const map: Record = {}; for (const entry of Object.values(REGISTRY)) { map[entry.id] = entry.alias || entry.id; } @@ -841,12 +883,12 @@ for (const entry of Object.values(REGISTRY)) { } /** Get registry entry by provider ID or alias */ -export function getRegistryEntry(provider) { +export function getRegistryEntry(provider: string): RegistryEntry | null { return REGISTRY[provider] || _byAlias.get(provider) || null; } /** Get all registered provider IDs */ -export function getRegisteredProviders() { +export function getRegisteredProviders(): string[] { return Object.keys(REGISTRY); } @@ -856,7 +898,7 @@ export function getRegisteredProviders() { * @param {string} provider - Provider ID or alias * @returns {"oauth"|"apikey"} */ -export function getProviderCategory(provider) { +export function getProviderCategory(provider: string): "oauth" | "apikey" { const entry = getRegistryEntry(provider); if (!entry) return "apikey"; // Safe default for unknown providers return entry.authType === "apikey" ? "apikey" : "oauth"; diff --git a/open-sse/config/rerankRegistry.js b/open-sse/config/rerankRegistry.ts similarity index 100% rename from open-sse/config/rerankRegistry.js rename to open-sse/config/rerankRegistry.ts diff --git a/open-sse/executors/antigravity.js b/open-sse/executors/antigravity.ts similarity index 100% rename from open-sse/executors/antigravity.js rename to open-sse/executors/antigravity.ts diff --git a/open-sse/executors/base.js b/open-sse/executors/base.ts similarity index 97% rename from open-sse/executors/base.js rename to open-sse/executors/base.ts index 9c0791e4b9..c55653bcb1 100644 --- a/open-sse/executors/base.js +++ b/open-sse/executors/base.ts @@ -6,7 +6,10 @@ import { HTTP_STATUS, FETCH_TIMEOUT_MS } from "../config/constants.js"; * (buildUrl, buildHeaders, transformRequest, etc.) for each provider. */ export class BaseExecutor { - constructor(provider, config) { + provider: any; + config: any; + + constructor(provider: any, config: any) { this.provider = provider; this.config = config; } @@ -96,7 +99,7 @@ export class BaseExecutor { ? AbortSignal.any([signal, timeoutSignal]) : signal || timeoutSignal; - const fetchOptions = { + const fetchOptions: Record = { method: "POST", headers, body: JSON.stringify(transformedBody), diff --git a/open-sse/executors/codex.js b/open-sse/executors/codex.ts similarity index 100% rename from open-sse/executors/codex.js rename to open-sse/executors/codex.ts diff --git a/open-sse/executors/cursor.js b/open-sse/executors/cursor.ts similarity index 99% rename from open-sse/executors/cursor.js rename to open-sse/executors/cursor.ts index 7f9a018dc1..5782998ed9 100644 --- a/open-sse/executors/cursor.js +++ b/open-sse/executors/cursor.ts @@ -1,3 +1,4 @@ +declare var EdgeRuntime: any; /** * CursorExecutor — Handles communication with the Cursor IDE API. * @@ -226,7 +227,7 @@ export class CursorExecutor extends BaseExecutor { return { status: response.status, - headers: Object.fromEntries(response.headers.entries()), + headers: Object.fromEntries((response.headers as any).entries()), body: Buffer.from(await response.arrayBuffer()), }; } @@ -290,7 +291,7 @@ export class CursorExecutor extends BaseExecutor { const transformedBody = this.transformRequest(model, body, stream, credentials); try { - const response = http2 + const response: any = http2 ? await this.makeHttp2Request(url, headers, transformedBody, signal) : await this.makeFetchRequest(url, headers, transformedBody, signal); @@ -458,8 +459,7 @@ export class CursorExecutor extends BaseExecutor { console.log(`[CURSOR BUFFER] Final toolCalls count: ${toolCalls.length}`); - const message = { - role: "assistant", + const message: Record = { role: "assistant", content: totalContent || null, }; diff --git a/open-sse/executors/default.js b/open-sse/executors/default.ts similarity index 100% rename from open-sse/executors/default.js rename to open-sse/executors/default.ts diff --git a/open-sse/executors/gemini-cli.js b/open-sse/executors/gemini-cli.ts similarity index 100% rename from open-sse/executors/gemini-cli.js rename to open-sse/executors/gemini-cli.ts diff --git a/open-sse/executors/github.js b/open-sse/executors/github.ts similarity index 100% rename from open-sse/executors/github.js rename to open-sse/executors/github.ts diff --git a/open-sse/executors/index.js b/open-sse/executors/index.ts similarity index 100% rename from open-sse/executors/index.js rename to open-sse/executors/index.ts diff --git a/open-sse/executors/kiro.js b/open-sse/executors/kiro.ts similarity index 98% rename from open-sse/executors/kiro.js rename to open-sse/executors/kiro.ts index a9fe3a670e..f48e326973 100644 --- a/open-sse/executors/kiro.js +++ b/open-sse/executors/kiro.ts @@ -83,8 +83,7 @@ export class KiroExecutor extends BaseExecutor { let chunkIndex = 0; const responseId = `chatcmpl-${Date.now()}`; const created = Math.floor(Date.now() / 1000); - const state = { - endDetected: false, + const state: Record = { endDetected: false, finishEmitted: false, hasToolCalls: false, toolCallIndex: 0, @@ -126,7 +125,7 @@ export class KiroExecutor extends BaseExecutor { const content = event.payload.content; state.totalContentLength += content.length; - const chunk = { + const chunk: Record = { id: responseId, object: "chat.completion.chunk", created, @@ -145,7 +144,7 @@ export class KiroExecutor extends BaseExecutor { // Handle codeEvent if (eventType === "codeEvent" && event.payload?.content) { - const chunk = { + const chunk: Record = { id: responseId, object: "chat.completion.chunk", created, @@ -257,7 +256,7 @@ export class KiroExecutor extends BaseExecutor { // Handle messageStopEvent if (eventType === "messageStopEvent") { - const chunk = { + const chunk: Record = { id: responseId, object: "chat.completion.chunk", created, @@ -330,7 +329,7 @@ export class KiroExecutor extends BaseExecutor { }; } - const finishChunk = { + const finishChunk: Record = { id: responseId, object: "chat.completion.chunk", created, diff --git a/open-sse/handlers/audioSpeech.js b/open-sse/handlers/audioSpeech.ts similarity index 93% rename from open-sse/handlers/audioSpeech.js rename to open-sse/handlers/audioSpeech.ts index bb1196e299..05299f902c 100644 --- a/open-sse/handlers/audioSpeech.js +++ b/open-sse/handlers/audioSpeech.ts @@ -100,12 +100,13 @@ async function handleDeepgramSpeech(providerConfig, body, modelId, token) { * @param {Object} options.credentials - Provider credentials { apiKey } * @returns {Response} */ +/** @returns {Promise} */ export async function handleAudioSpeech({ body, credentials }) { if (!body.model) { - return errorResponse("model is required", 400); + return errorResponse(400, "model is required"); } if (!body.input) { - return errorResponse("input is required", 400); + return errorResponse(400, "input is required"); } const { provider: providerId, model: modelId } = parseSpeechModel(body.model); @@ -113,14 +114,14 @@ export async function handleAudioSpeech({ body, credentials }) { if (!providerConfig) { return errorResponse( - `No speech provider found for model "${body.model}". Available: openai, hyperbolic, deepgram`, - 400 + 400, + `No speech provider found for model "${body.model}". Available: openai, hyperbolic, deepgram` ); } const token = credentials?.apiKey || credentials?.accessToken; if (!token) { - return errorResponse(`No credentials for speech provider: ${providerId}`, 401); + return errorResponse(401, `No credentials for speech provider: ${providerId}`); } try { @@ -168,6 +169,6 @@ export async function handleAudioSpeech({ body, credentials }) { }, }); } catch (err) { - return errorResponse(`Speech request failed: ${err.message}`, 500); + return errorResponse(500, `Speech request failed: ${err.message}`); } } diff --git a/open-sse/handlers/audioTranscription.js b/open-sse/handlers/audioTranscription.ts similarity index 89% rename from open-sse/handlers/audioTranscription.js rename to open-sse/handlers/audioTranscription.ts index c25eac19f3..3c511f738f 100644 --- a/open-sse/handlers/audioTranscription.js +++ b/open-sse/handlers/audioTranscription.ts @@ -129,11 +129,11 @@ async function handleAssemblyAITranscription(providerConfig, file, modelId, toke } if (result.status === "error") { - return errorResponse(result.error || "AssemblyAI transcription failed", 500); + return errorResponse(500, result.error || "AssemblyAI transcription failed"); } } - return errorResponse("AssemblyAI transcription timed out after 120s", 504); + return errorResponse(504, "AssemblyAI transcription timed out after 120s"); } /** @@ -144,15 +144,16 @@ async function handleAssemblyAITranscription(providerConfig, file, modelId, toke * @param {Object} options.credentials - Provider credentials { apiKey } * @returns {Response} */ +/** @returns {Promise} */ export async function handleAudioTranscription({ formData, credentials }) { const model = formData.get("model"); if (!model) { - return errorResponse("model is required", 400); + return errorResponse(400, "model is required"); } const file = formData.get("file"); if (!file) { - return errorResponse("file is required", 400); + return errorResponse(400, "file is required"); } const { provider: providerId, model: modelId } = parseTranscriptionModel(model); @@ -160,14 +161,14 @@ export async function handleAudioTranscription({ formData, credentials }) { if (!providerConfig) { return errorResponse( - `No transcription provider found for model "${model}". Available: openai, groq, deepgram, assemblyai`, - 400 + 400, + `No transcription provider found for model "${model}". Available: openai, groq, deepgram, assemblyai` ); } const token = credentials?.apiKey || credentials?.accessToken; if (!token) { - return errorResponse(`No credentials for transcription provider: ${providerId}`, 401); + return errorResponse(401, `No credentials for transcription provider: ${providerId}`); } // Route to provider-specific handler @@ -181,7 +182,7 @@ export async function handleAudioTranscription({ formData, credentials }) { // Default: OpenAI/Groq-compatible multipart proxy const upstreamForm = new FormData(); - upstreamForm.append("file", file, file.name || "audio.wav"); + upstreamForm.append("file", /** @type {Blob} */ (file), /** @type {any} */ (file).name || "audio.wav"); upstreamForm.append("model", modelId); // Forward optional parameters @@ -194,7 +195,7 @@ export async function handleAudioTranscription({ formData, credentials }) { ]) { const val = formData.get(key); if (val !== null && val !== undefined) { - upstreamForm.append(key, val); + upstreamForm.append(key, /** @type {string} */ (val)); } } @@ -221,6 +222,6 @@ export async function handleAudioTranscription({ formData, credentials }) { headers: { "Content-Type": contentType, "Access-Control-Allow-Origin": "*" }, }); } catch (err) { - return errorResponse(`Transcription request failed: ${err.message}`, 500); + return errorResponse(500, `Transcription request failed: ${err.message}`); } } diff --git a/open-sse/handlers/chatCore.js b/open-sse/handlers/chatCore.ts similarity index 99% rename from open-sse/handlers/chatCore.js rename to open-sse/handlers/chatCore.ts index d260159aaa..12a77e4c71 100644 --- a/open-sse/handlers/chatCore.js +++ b/open-sse/handlers/chatCore.ts @@ -52,6 +52,7 @@ import { createProgressTransform, wantsProgress } from "../utils/progressTracker * @param {string} options.connectionId - Connection ID for usage tracking * @param {object} options.apiKeyInfo - API key metadata for usage attribution */ +/** @param {any} options */ export async function handleChatCore({ body, modelInfo, diff --git a/open-sse/handlers/embeddings.js b/open-sse/handlers/embeddings.ts similarity index 99% rename from open-sse/handlers/embeddings.js rename to open-sse/handlers/embeddings.ts index 871ee4d123..96ed05d955 100644 --- a/open-sse/handlers/embeddings.js +++ b/open-sse/handlers/embeddings.ts @@ -52,7 +52,7 @@ export async function handleEmbedding({ body, credentials, log }) { } // Build upstream request - const upstreamBody = { + const upstreamBody: Record = { model: model, input: body.input, }; diff --git a/open-sse/handlers/imageGeneration.js b/open-sse/handlers/imageGeneration.ts similarity index 99% rename from open-sse/handlers/imageGeneration.js rename to open-sse/handlers/imageGeneration.ts index 4aed218826..71b50f5785 100644 --- a/open-sse/handlers/imageGeneration.js +++ b/open-sse/handlers/imageGeneration.ts @@ -232,7 +232,7 @@ async function handleOpenAIImageGeneration({ }; // Build upstream request (OpenAI-compatible format) - const upstreamBody = { + const upstreamBody: Record = { model: model, prompt: body.prompt, }; diff --git a/open-sse/handlers/moderations.js b/open-sse/handlers/moderations.ts similarity index 86% rename from open-sse/handlers/moderations.js rename to open-sse/handlers/moderations.ts index 32509dada3..64bce58aba 100644 --- a/open-sse/handlers/moderations.js +++ b/open-sse/handlers/moderations.ts @@ -15,9 +15,10 @@ import { errorResponse } from "../utils/error.js"; * @param {Object} options.credentials - Provider credentials { apiKey } * @returns {Response} */ +/** @returns {Promise} */ export async function handleModeration({ body, credentials }) { if (!body.input) { - return errorResponse("input is required", 400); + return errorResponse(400, "input is required"); } // Default to latest moderation model @@ -27,14 +28,14 @@ export async function handleModeration({ body, credentials }) { if (!providerConfig) { return errorResponse( - `No moderation provider found for model "${model}". Available: openai`, - 400 + 400, + `No moderation provider found for model "${model}". Available: openai` ); } const token = credentials?.apiKey || credentials?.accessToken; if (!token) { - return errorResponse(`No credentials for moderation provider: ${providerId}`, 401); + return errorResponse(401, `No credentials for moderation provider: ${providerId}`); } try { @@ -63,6 +64,6 @@ export async function handleModeration({ body, credentials }) { headers: { "Access-Control-Allow-Origin": "*" }, }); } catch (err) { - return errorResponse(`Moderation request failed: ${err.message}`, 500); + return errorResponse(500, `Moderation request failed: ${err.message}`); } } diff --git a/open-sse/handlers/rerank.js b/open-sse/handlers/rerank.ts similarity index 88% rename from open-sse/handlers/rerank.js rename to open-sse/handlers/rerank.ts index 7ec8abf6d0..5851f8d3b3 100644 --- a/open-sse/handlers/rerank.js +++ b/open-sse/handlers/rerank.ts @@ -69,6 +69,7 @@ function transformResponseFromProvider(providerConfig, data) { * @param {Object} options.credentials - Provider credentials { apiKey, accessToken } * @returns {Response} */ +/** @returns {Promise} */ export async function handleRerank({ model, query, @@ -77,10 +78,10 @@ export async function handleRerank({ return_documents, credentials, }) { - if (!model) return errorResponse("model is required", 400); - if (!query) return errorResponse("query is required", 400); + if (!model) return errorResponse(400, "model is required"); + if (!query) return errorResponse(400, "query is required"); if (!documents || !Array.isArray(documents) || documents.length === 0) { - return errorResponse("documents must be a non-empty array", 400); + return errorResponse(400, "documents must be a non-empty array"); } const { provider: providerId, model: modelId } = parseRerankModel(model); @@ -88,14 +89,14 @@ export async function handleRerank({ if (!providerConfig) { return errorResponse( - `No rerank provider found for model "${model}". Available: cohere, together, nvidia, fireworks`, - 400 + 400, + `No rerank provider found for model "${model}". Available: cohere, together, nvidia, fireworks` ); } const token = credentials?.apiKey || credentials?.accessToken; if (!token) { - return errorResponse(`No credentials for rerank provider: ${providerId}`, 401); + return errorResponse(401, `No credentials for rerank provider: ${providerId}`); } const requestBody = transformRequestForProvider(providerConfig, { @@ -119,8 +120,8 @@ export async function handleRerank({ if (!res.ok) { const errData = await res.json().catch(() => ({})); return errorResponse( - errData.message || errData.error?.message || `Provider returned HTTP ${res.status}`, - res.status + res.status, + errData.message || errData.error?.message || `Provider returned HTTP ${res.status}` ); } @@ -131,6 +132,6 @@ export async function handleRerank({ headers: { "Access-Control-Allow-Origin": "*" }, }); } catch (err) { - return errorResponse(`Rerank request failed: ${err.message}`, 500); + return errorResponse(500, `Rerank request failed: ${err.message}`); } } diff --git a/open-sse/handlers/responseTranslator.js b/open-sse/handlers/responseTranslator.ts similarity index 96% rename from open-sse/handlers/responseTranslator.js rename to open-sse/handlers/responseTranslator.ts index 79dc42b388..10e46a24de 100644 --- a/open-sse/handlers/responseTranslator.js +++ b/open-sse/handlers/responseTranslator.ts @@ -56,7 +56,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source } } - const message = { role: "assistant" }; + const message: Record = { role: "assistant" }; if (textContent) { message.content = textContent; } @@ -74,7 +74,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source const model = response?.model || responseBody?.model || "openai-responses"; const finishReason = toolCalls.length > 0 ? "tool_calls" : "stop"; - const result = { + const result: Record = { id: `chatcmpl-${response?.id || Date.now()}`, object: "chat.completion", created: createdAt, @@ -162,7 +162,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source } // Build OpenAI format message - const message = { role: "assistant" }; + const message: Record = { role: "assistant" }; if (textContent) { message.content = textContent; } @@ -183,7 +183,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source finishReason = "tool_calls"; } - const result = { + const result: Record = { id: `chatcmpl-${response.responseId || Date.now()}`, object: "chat.completion", created: Math.floor(new Date(response.createTime || Date.now()).getTime() / 1000), @@ -241,7 +241,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source } } - const message = { role: "assistant" }; + const message: Record = { role: "assistant" }; if (textContent) { message.content = textContent; } @@ -259,7 +259,7 @@ export function translateNonStreamingResponse(responseBody, targetFormat, source if (finishReason === "end_turn") finishReason = "stop"; if (finishReason === "tool_use") finishReason = "tool_calls"; - const result = { + const result: Record = { id: `chatcmpl-${responseBody.id || Date.now()}`, object: "chat.completion", created: Math.floor(Date.now() / 1000), diff --git a/open-sse/handlers/responsesHandler.js b/open-sse/handlers/responsesHandler.ts similarity index 96% rename from open-sse/handlers/responsesHandler.js rename to open-sse/handlers/responsesHandler.ts index 1066726ecd..3eaa34f373 100644 --- a/open-sse/handlers/responsesHandler.js +++ b/open-sse/handlers/responsesHandler.ts @@ -45,8 +45,11 @@ export async function handleResponsesCore({ onCredentialsRefreshed, onRequestSuccess, onDisconnect, + clientRawRequest: null, connectionId, - }); + userAgent: null, + comboName: null, + } as any); if (!result.success || !result.response) { return result; diff --git a/open-sse/handlers/sseParser.js b/open-sse/handlers/sseParser.ts similarity index 97% rename from open-sse/handlers/sseParser.js rename to open-sse/handlers/sseParser.ts index 608f5c349e..43392904be 100644 --- a/open-sse/handlers/sseParser.js +++ b/open-sse/handlers/sseParser.ts @@ -44,15 +44,14 @@ export function parseSSEToOpenAIResponse(rawSSE, fallbackModel) { } } - const message = { - role: "assistant", + const message: Record = { role: "assistant", content: contentParts.join(""), }; if (reasoningParts.length > 0) { message.reasoning_content = reasoningParts.join(""); } - const result = { + const result: Record = { id: first.id || `chatcmpl-${Date.now()}`, object: "chat.completion", created: first.created || Math.floor(Date.now() / 1000), diff --git a/open-sse/handlers/usageExtractor.js b/open-sse/handlers/usageExtractor.ts similarity index 100% rename from open-sse/handlers/usageExtractor.js rename to open-sse/handlers/usageExtractor.ts diff --git a/open-sse/index.js b/open-sse/index.ts similarity index 100% rename from open-sse/index.js rename to open-sse/index.ts diff --git a/open-sse/services/accountFallback.js b/open-sse/services/accountFallback.ts similarity index 99% rename from open-sse/services/accountFallback.js rename to open-sse/services/accountFallback.ts index c7bca2cd8d..59e2aed06d 100644 --- a/open-sse/services/accountFallback.js +++ b/open-sse/services/accountFallback.ts @@ -504,7 +504,7 @@ export function applyErrorState(account, status, errorText, provider = null) { * @param {object} account * @returns {number} score 0 = unhealthy, 100 = perfectly healthy */ -export function getAccountHealth(account) { +export function getAccountHealth(account, model?: any) { if (!account) return 0; let score = 100; score -= (account.backoffLevel || 0) * 10; diff --git a/open-sse/services/accountSelector.js b/open-sse/services/accountSelector.ts similarity index 98% rename from open-sse/services/accountSelector.js rename to open-sse/services/accountSelector.ts index a43886ed70..a4d392bb8c 100644 --- a/open-sse/services/accountSelector.js +++ b/open-sse/services/accountSelector.ts @@ -43,7 +43,7 @@ export function selectAccountP2C(accounts, model = null) { * @param {string} [model] - Model name * @returns {{ account: object|null, state: object }} */ -export function selectAccount(accounts, strategy = "fill-first", state = {}, model = null) { +export function selectAccount(accounts, strategy = "fill-first", state: any = {}, model = null) { if (!accounts || accounts.length === 0) { return { account: null, state }; } diff --git a/open-sse/services/combo.js b/open-sse/services/combo.ts similarity index 99% rename from open-sse/services/combo.js rename to open-sse/services/combo.ts index 90e75de9af..5e0f33f9be 100644 --- a/open-sse/services/combo.js +++ b/open-sse/services/combo.ts @@ -221,6 +221,7 @@ function sortModelsByUsage(models, comboName) { * @param {Object} options.log - Logger object * @returns {Promise} */ +/** @param {any} options */ export async function handleComboChat({ body, combo, diff --git a/open-sse/services/comboConfig.js b/open-sse/services/comboConfig.ts similarity index 95% rename from open-sse/services/comboConfig.js rename to open-sse/services/comboConfig.ts index 2e2ad77c7e..27519f10e7 100644 --- a/open-sse/services/comboConfig.js +++ b/open-sse/services/comboConfig.ts @@ -27,7 +27,7 @@ const DEFAULT_COMBO_CONFIG = { * @param {string} [provider] - Optional provider to apply provider-level overrides * @returns {Object} Resolved config */ -export function resolveComboConfig(combo, settings, provider) { +export function resolveComboConfig(combo, settings, provider?: any) { const global = settings?.comboDefaults || {}; const providerOverride = provider ? settings?.providerOverrides?.[provider] || {} : {}; const comboConfig = combo?.config || {}; diff --git a/open-sse/services/comboMetrics.js b/open-sse/services/comboMetrics.ts similarity index 93% rename from open-sse/services/comboMetrics.js rename to open-sse/services/comboMetrics.ts index 021e913e74..0b1366595c 100644 --- a/open-sse/services/comboMetrics.js +++ b/open-sse/services/comboMetrics.ts @@ -35,7 +35,7 @@ export function recordComboRequest( }); } - const combo = metrics.get(comboName); + const combo: any = metrics.get(comboName); combo.totalRequests++; combo.totalLatencyMs += latencyMs; combo.totalFallbacks += fallbackCount; @@ -81,7 +81,7 @@ export function recordComboRequest( * @returns {Object|null} */ export function getComboMetrics(comboName) { - const combo = metrics.get(comboName); + const combo: any = metrics.get(comboName); if (!combo) return null; return { @@ -93,7 +93,7 @@ export function getComboMetrics(comboName) { fallbackRate: combo.totalRequests > 0 ? Math.round((combo.totalFallbacks / combo.totalRequests) * 100) : 0, byModel: Object.fromEntries( - Object.entries(combo.byModel).map(([model, m]) => [ + Object.entries(combo.byModel).map(([model, m]: [string, any]) => [ model, { ...m, @@ -110,7 +110,7 @@ export function getComboMetrics(comboName) { * @returns {Object} Map of comboName → metrics */ export function getAllComboMetrics() { - const result = {}; + const result: Record = {}; for (const [name] of metrics) { result[name] = getComboMetrics(name); } diff --git a/open-sse/services/contextManager.js b/open-sse/services/contextManager.ts similarity index 99% rename from open-sse/services/contextManager.js rename to open-sse/services/contextManager.ts index 373331ce63..1c2234a533 100644 --- a/open-sse/services/contextManager.js +++ b/open-sse/services/contextManager.ts @@ -51,7 +51,7 @@ export function getTokenLimit(provider, model = null) { * @param {object} options - { provider?, model?, maxTokens?, reserveTokens? } * @returns {{ body: object, compressed: boolean, stats: object }} */ -export function compressContext(body, options = {}) { +export function compressContext(body, options: any = {}) { if (!body || !body.messages || !Array.isArray(body.messages)) { return { body, compressed: false, stats: {} }; } diff --git a/open-sse/services/ipFilter.js b/open-sse/services/ipFilter.ts similarity index 100% rename from open-sse/services/ipFilter.js rename to open-sse/services/ipFilter.ts diff --git a/open-sse/services/model.js b/open-sse/services/model.ts similarity index 100% rename from open-sse/services/model.js rename to open-sse/services/model.ts diff --git a/open-sse/services/provider.js b/open-sse/services/provider.ts similarity index 99% rename from open-sse/services/provider.js rename to open-sse/services/provider.ts index 5d640ba208..d0cce5260f 100644 --- a/open-sse/services/provider.js +++ b/open-sse/services/provider.ts @@ -156,7 +156,7 @@ export function getProviderFallbackCount(provider) { } // Build provider URL -export function buildProviderUrl(provider, model, stream = true, options = {}) { +export function buildProviderUrl(provider, model, stream = true, options: any = {}) { if (isOpenAICompatible(provider)) { const apiType = getOpenAICompatibleType(provider); const baseUrl = options?.baseUrl || OPENAI_COMPATIBLE_DEFAULTS.baseUrl; diff --git a/open-sse/services/rateLimitManager.js b/open-sse/services/rateLimitManager.ts similarity index 99% rename from open-sse/services/rateLimitManager.js rename to open-sse/services/rateLimitManager.ts index 381d458e5c..42e261a164 100644 --- a/open-sse/services/rateLimitManager.js +++ b/open-sse/services/rateLimitManager.ts @@ -293,7 +293,7 @@ export function updateFromHeaders(provider, connectionId, headers, status, model // Calculate optimal minTime from RPM limit const minTime = Math.max(0, Math.floor(60000 / limit) - 10); // Small buffer - const updates = { minTime }; + const updates: Record = { minTime }; // If remaining is low (< 10% of limit), set reservoir to throttle immediately if (!isNaN(remaining)) { @@ -348,7 +348,7 @@ export function getRateLimitStatus(provider, connectionId) { * Get all active limiters status (for dashboard overview) */ export function getAllRateLimitStatus() { - const result = {}; + const result: Record = {}; for (const [key, limiter] of limiters) { const counts = limiter.counts(); result[key] = { diff --git a/open-sse/services/rateLimitSemaphore.js b/open-sse/services/rateLimitSemaphore.ts similarity index 99% rename from open-sse/services/rateLimitSemaphore.js rename to open-sse/services/rateLimitSemaphore.ts index b6c149321f..472c71bfae 100644 --- a/open-sse/services/rateLimitSemaphore.js +++ b/open-sse/services/rateLimitSemaphore.ts @@ -117,7 +117,7 @@ export function acquire(modelStr, { maxConcurrency = 3, timeoutMs = 30000 } = {} const idx = gate.queue.findIndex((item) => item.timer === timer); if (idx !== -1) gate.queue.splice(idx, 1); const err = new Error(`Semaphore timeout after ${timeoutMs}ms for ${modelStr}`); - err.code = "SEMAPHORE_TIMEOUT"; + (err as any).code = "SEMAPHORE_TIMEOUT"; reject(err); }, timeoutMs); diff --git a/open-sse/services/sessionManager.js b/open-sse/services/sessionManager.ts similarity index 98% rename from open-sse/services/sessionManager.js rename to open-sse/services/sessionManager.ts index 11ffd11be3..b74580af88 100644 --- a/open-sse/services/sessionManager.js +++ b/open-sse/services/sessionManager.ts @@ -36,7 +36,7 @@ _cleanupTimer.unref(); * @param {object} [options] - Extra context * @returns {string} Session ID (hex hash) */ -export function generateSessionId(body, options = {}) { +export function generateSessionId(body, options: any = {}) { const parts = []; // Model contributes to fingerprint diff --git a/open-sse/services/signatureCache.js b/open-sse/services/signatureCache.ts similarity index 96% rename from open-sse/services/signatureCache.js rename to open-sse/services/signatureCache.ts index 82b810bb26..a188dc261c 100644 --- a/open-sse/services/signatureCache.js +++ b/open-sse/services/signatureCache.ts @@ -34,7 +34,7 @@ const MAX_PATTERNS_PER_KEY = 50; * @param {object} context - { tool?, modelFamily?, sessionId? } * @returns {string[]} Array of unique signature patterns */ -export function getSignatures(context = {}) { +export function getSignatures(context: any = {}) { const patterns = new Set(DEFAULT_SIGNATURES); // Layer 1: Tool (e.g., "cursor", "cline", "antigravity") @@ -61,7 +61,7 @@ export function getSignatures(context = {}) { * @param {string} pattern - The signature pattern (e.g., "") * @param {object} context - { tool?, modelFamily?, sessionId? } */ -export function addSignature(pattern, context = {}) { +export function addSignature(pattern: any, context: any = {}) { if (!pattern || typeof pattern !== "string") return; const addToLayer = (layer, key) => { @@ -93,7 +93,7 @@ export function addSignature(pattern, context = {}) { * @param {object} context - { tool?, modelFamily?, sessionId? } * @returns {{ found: string[], cleaned: string }} Detected tags and cleaned text */ -export function detectAndLearn(text, context = {}) { +export function detectAndLearn(text: any, context: any = {}) { if (!text || typeof text !== "string") return { found: [], cleaned: text }; const found = []; diff --git a/open-sse/services/systemPrompt.js b/open-sse/services/systemPrompt.ts similarity index 100% rename from open-sse/services/systemPrompt.js rename to open-sse/services/systemPrompt.ts diff --git a/open-sse/services/thinkingBudget.js b/open-sse/services/thinkingBudget.ts similarity index 100% rename from open-sse/services/thinkingBudget.js rename to open-sse/services/thinkingBudget.ts diff --git a/open-sse/services/tokenRefresh.js b/open-sse/services/tokenRefresh.ts similarity index 100% rename from open-sse/services/tokenRefresh.js rename to open-sse/services/tokenRefresh.ts diff --git a/open-sse/services/usage.js b/open-sse/services/usage.ts similarity index 99% rename from open-sse/services/usage.js rename to open-sse/services/usage.ts index 185dd9918d..cca9e702f9 100644 --- a/open-sse/services/usage.js +++ b/open-sse/services/usage.ts @@ -38,7 +38,7 @@ const CLAUDE_CONFIG = { /** * Get usage data for a provider connection * @param {Object} connection - Provider connection with accessToken - * @returns {Object} Usage data with quotas + * @returns {Promise} Usage data with quotas */ export async function getUsageForProvider(connection) { const { provider, accessToken, providerSpecificData } = connection; @@ -49,7 +49,7 @@ export async function getUsageForProvider(connection) { case "gemini-cli": return await getGeminiUsage(accessToken); case "antigravity": - return await getAntigravityUsage(accessToken); + return await getAntigravityUsage(accessToken, undefined); case "claude": return await getClaudeUsage(accessToken); case "codex": @@ -311,7 +311,7 @@ async function getAntigravityUsage(accessToken, providerSpecificData) { } const data = await response.json(); - const quotas = {}; + const quotas: Record = {}; // Parse model quotas (inspired by vscode-antigravity-cockpit) if (data.models) { @@ -328,7 +328,7 @@ async function getAntigravityUsage(accessToken, providerSpecificData) { "gemini-2.5-flash", ]; - for (const [modelKey, info] of Object.entries(data.models)) { + for (const [modelKey, info] of Object.entries(data.models) as [string, any][]) { // Skip models without quota info if (!info.quotaInfo) { continue; diff --git a/open-sse/services/wildcardRouter.js b/open-sse/services/wildcardRouter.ts similarity index 100% rename from open-sse/services/wildcardRouter.js rename to open-sse/services/wildcardRouter.ts diff --git a/open-sse/transformer/responsesTransformer.js b/open-sse/transformer/responsesTransformer.ts similarity index 99% rename from open-sse/transformer/responsesTransformer.js rename to open-sse/transformer/responsesTransformer.ts index e1c4318c20..f9e9aa23bb 100644 --- a/open-sse/transformer/responsesTransformer.js +++ b/open-sse/transformer/responsesTransformer.ts @@ -1,3 +1,5 @@ +import * as fs from 'fs'; +import * as path from 'path'; /** * Responses API Transformer * Converts OpenAI Chat Completions SSE to Codex Responses API SSE format diff --git a/open-sse/translator/formats.js b/open-sse/translator/formats.ts similarity index 100% rename from open-sse/translator/formats.js rename to open-sse/translator/formats.ts diff --git a/open-sse/translator/helpers/claudeHelper.js b/open-sse/translator/helpers/claudeHelper.ts similarity index 100% rename from open-sse/translator/helpers/claudeHelper.js rename to open-sse/translator/helpers/claudeHelper.ts diff --git a/open-sse/translator/helpers/geminiHelper.js b/open-sse/translator/helpers/geminiHelper.ts similarity index 99% rename from open-sse/translator/helpers/geminiHelper.js rename to open-sse/translator/helpers/geminiHelper.ts index 0b9241152f..63cee758b1 100644 --- a/open-sse/translator/helpers/geminiHelper.js +++ b/open-sse/translator/helpers/geminiHelper.ts @@ -193,7 +193,7 @@ function mergeAllOf(obj) { if (!obj || typeof obj !== "object") return; if (obj.allOf && Array.isArray(obj.allOf)) { - const merged = {}; + const merged: Record = {}; for (const item of obj.allOf) { if (item.properties) { diff --git a/open-sse/translator/helpers/maxTokensHelper.js b/open-sse/translator/helpers/maxTokensHelper.ts similarity index 100% rename from open-sse/translator/helpers/maxTokensHelper.js rename to open-sse/translator/helpers/maxTokensHelper.ts diff --git a/open-sse/translator/helpers/openaiHelper.js b/open-sse/translator/helpers/openaiHelper.ts similarity index 100% rename from open-sse/translator/helpers/openaiHelper.js rename to open-sse/translator/helpers/openaiHelper.ts diff --git a/open-sse/translator/helpers/responsesApiHelper.js b/open-sse/translator/helpers/responsesApiHelper.ts similarity index 100% rename from open-sse/translator/helpers/responsesApiHelper.js rename to open-sse/translator/helpers/responsesApiHelper.ts diff --git a/open-sse/translator/helpers/toolCallHelper.js b/open-sse/translator/helpers/toolCallHelper.ts similarity index 100% rename from open-sse/translator/helpers/toolCallHelper.js rename to open-sse/translator/helpers/toolCallHelper.ts diff --git a/open-sse/translator/index.js b/open-sse/translator/index.ts similarity index 99% rename from open-sse/translator/index.js rename to open-sse/translator/index.ts index 3215da0ce9..78731d640f 100644 --- a/open-sse/translator/index.js +++ b/open-sse/translator/index.ts @@ -234,7 +234,7 @@ export function translateResponse(targetFormat, sourceFormat, chunk, state) { // Attach OpenAI intermediate results for logging if (openaiResults && sourceFormat !== FORMATS.OPENAI && targetFormat !== FORMATS.OPENAI) { - results._openaiIntermediate = openaiResults; + (results as any)._openaiIntermediate = openaiResults; } return results; diff --git a/open-sse/translator/request/antigravity-to-openai.js b/open-sse/translator/request/antigravity-to-openai.ts similarity index 97% rename from open-sse/translator/request/antigravity-to-openai.js rename to open-sse/translator/request/antigravity-to-openai.ts index 4a6402d17e..859051f3e9 100644 --- a/open-sse/translator/request/antigravity-to-openai.js +++ b/open-sse/translator/request/antigravity-to-openai.ts @@ -6,7 +6,7 @@ import { adjustMaxTokens } from "../helpers/maxTokensHelper.js"; // Antigravity body: { project, model, userAgent, requestType, requestId, request: { contents, systemInstruction, tools, toolConfig, generationConfig, sessionId } } export function antigravityToOpenAIRequest(model, body, stream) { const req = body.request || body; - const result = { + const result: Record = { model: model, messages: [], stream: stream, @@ -190,7 +190,7 @@ function convertContent(content) { // Assistant with tool calls if (toolCalls.length > 0) { - const msg = { role: "assistant" }; + const msg: Record = { role: "assistant" }; if (textParts.length > 0) { msg.content = textParts.length === 1 && textParts[0].type === "text" ? textParts[0].text : textParts; @@ -204,7 +204,7 @@ function convertContent(content) { // Regular message if (textParts.length > 0 || reasoningContent) { - const msg = { role }; + const msg: Record = { role }; if (textParts.length > 0) { msg.content = textParts.length === 1 && textParts[0].type === "text" ? textParts[0].text : textParts; diff --git a/open-sse/translator/request/claude-to-gemini.js b/open-sse/translator/request/claude-to-gemini.ts similarity index 99% rename from open-sse/translator/request/claude-to-gemini.js rename to open-sse/translator/request/claude-to-gemini.ts index 3b5c7f21ba..a2ddf9ace6 100644 --- a/open-sse/translator/request/claude-to-gemini.js +++ b/open-sse/translator/request/claude-to-gemini.ts @@ -9,7 +9,7 @@ import { DEFAULT_THINKING_GEMINI_SIGNATURE } from "../../config/defaultThinkingS * skipping the OpenAI hub intermediate step. */ export function claudeToGeminiRequest(model, body, stream) { - const result = { + const result: Record = { model: model, contents: [], generationConfig: {}, diff --git a/open-sse/translator/request/claude-to-openai.js b/open-sse/translator/request/claude-to-openai.ts similarity index 98% rename from open-sse/translator/request/claude-to-openai.js rename to open-sse/translator/request/claude-to-openai.ts index 040b54d2ae..65d5be281b 100644 --- a/open-sse/translator/request/claude-to-openai.js +++ b/open-sse/translator/request/claude-to-openai.ts @@ -4,7 +4,7 @@ import { adjustMaxTokens } from "../helpers/maxTokensHelper.js"; // Convert Claude request to OpenAI format export function claudeToOpenAIRequest(model, body, stream) { - const result = { + const result: Record = { model: model, messages: [], stream: stream, @@ -186,7 +186,7 @@ function convertClaudeMessage(msg) { // If has tool calls, return assistant message with tool_calls if (toolCalls.length > 0) { - const result = { role: "assistant" }; + const result: Record = { role: "assistant" }; if (parts.length > 0) { result.content = parts.length === 1 && parts[0].type === "text" ? parts[0].text : parts; } diff --git a/open-sse/translator/request/gemini-to-openai.js b/open-sse/translator/request/gemini-to-openai.ts similarity index 97% rename from open-sse/translator/request/gemini-to-openai.js rename to open-sse/translator/request/gemini-to-openai.ts index ad067e070e..13a46d0c56 100644 --- a/open-sse/translator/request/gemini-to-openai.js +++ b/open-sse/translator/request/gemini-to-openai.ts @@ -4,7 +4,7 @@ import { adjustMaxTokens } from "../helpers/maxTokensHelper.js"; // Convert Gemini request to OpenAI format export function geminiToOpenAIRequest(model, body, stream) { - const result = { + const result: Record = { model: model, messages: [], stream: stream, @@ -116,7 +116,7 @@ function convertGeminiContent(content) { } if (toolCalls.length > 0) { - const result = { role: "assistant" }; + const result: Record = { role: "assistant" }; if (parts.length > 0) { result.content = parts.length === 1 ? parts[0].text : parts; } diff --git a/open-sse/translator/request/openai-responses.js b/open-sse/translator/request/openai-responses.ts similarity index 97% rename from open-sse/translator/request/openai-responses.js rename to open-sse/translator/request/openai-responses.ts index c3176ee2d4..1ac99d36ec 100644 --- a/open-sse/translator/request/openai-responses.js +++ b/open-sse/translator/request/openai-responses.ts @@ -21,8 +21,8 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials) const error = new Error( `Unsupported Responses API feature: ${tool.type} tool type is not supported by omniroute` ); - error.statusCode = 400; - error.errorType = "unsupported_feature"; + (error as any).statusCode = 400; + (error as any).errorType = "unsupported_feature"; throw error; } } @@ -31,12 +31,12 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials) const error = new Error( "Unsupported Responses API feature: background mode is not supported by omniroute" ); - error.statusCode = 400; - error.errorType = "unsupported_feature"; + (error as any).statusCode = 400; + (error as any).errorType = "unsupported_feature"; throw error; } - const result = { ...body }; + const result: Record = { ...body }; result.messages = []; // Convert instructions to system message @@ -159,7 +159,7 @@ export function openaiResponsesToOpenAIRequest(model, body, stream, credentials) * Convert OpenAI Chat Completions to OpenAI Responses API format */ export function openaiToOpenAIResponsesRequest(model, body, stream, credentials) { - const result = { + const result: Record = { model, input: [], stream: true, diff --git a/open-sse/translator/request/openai-to-claude.js b/open-sse/translator/request/openai-to-claude.ts similarity index 99% rename from open-sse/translator/request/openai-to-claude.js rename to open-sse/translator/request/openai-to-claude.ts index 5a2ccb3b0a..62aac8e6f8 100644 --- a/open-sse/translator/request/openai-to-claude.js +++ b/open-sse/translator/request/openai-to-claude.ts @@ -11,7 +11,7 @@ const CLAUDE_OAUTH_TOOL_PREFIX = "proxy_"; export function openaiToClaudeRequest(model, body, stream) { // Tool name mapping for Claude OAuth (capitalizedName → originalName) const toolNameMap = new Map(); - const result = { + const result: Record = { model: model, max_tokens: adjustMaxTokens(body), stream: stream, diff --git a/open-sse/translator/request/openai-to-cursor.js b/open-sse/translator/request/openai-to-cursor.ts similarity index 95% rename from open-sse/translator/request/openai-to-cursor.js rename to open-sse/translator/request/openai-to-cursor.ts index e8e7485dfc..6228257957 100644 --- a/open-sse/translator/request/openai-to-cursor.js +++ b/open-sse/translator/request/openai-to-cursor.ts @@ -66,7 +66,7 @@ function convertMessages(messages) { // Keep tool_calls structure for assistant messages if (msg.role === "assistant" && msg.tool_calls && msg.tool_calls.length > 0) { - const assistantMsg = { role: "assistant" }; + const assistantMsg: Record = { role: "assistant" }; if (content) { assistantMsg.content = content; } @@ -80,8 +80,7 @@ function convertMessages(messages) { result.push(assistantMsg); } else if (content || pendingToolResults.length > 0) { - const msgObj = { - role: msg.role, + const msgObj: Record = { role: msg.role, content: content || "", }; diff --git a/open-sse/translator/request/openai-to-gemini.js b/open-sse/translator/request/openai-to-gemini.ts similarity index 98% rename from open-sse/translator/request/openai-to-gemini.js rename to open-sse/translator/request/openai-to-gemini.ts index 3945852499..d9dc479d23 100644 --- a/open-sse/translator/request/openai-to-gemini.js +++ b/open-sse/translator/request/openai-to-gemini.ts @@ -21,7 +21,7 @@ import { // Core: Convert OpenAI request to Gemini format (base for all variants) function openaiToGeminiBase(model, body, stream) { - const result = { + const result: Record = { model: model, contents: [], generationConfig: {}, @@ -253,7 +253,7 @@ export function openaiToGeminiCLIRequest(model, body, stream) { function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigravity = false) { const projectId = credentials?.projectId || generateProjectId(); - const envelope = { + const envelope: Record = { project: projectId, model: model, userAgent: isAntigravity ? "antigravity" : "gemini-cli", @@ -272,7 +272,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra envelope.requestType = "agent"; // Inject required default system prompt for Antigravity - const defaultPart = { text: ANTIGRAVITY_DEFAULT_SYSTEM }; + const defaultPart: Record = { text: ANTIGRAVITY_DEFAULT_SYSTEM }; if (envelope.request.systemInstruction?.parts) { envelope.request.systemInstruction.parts.unshift(defaultPart); } else { @@ -297,7 +297,7 @@ function wrapInCloudCodeEnvelope(model, geminiCLI, credentials = null, isAntigra function wrapInCloudCodeEnvelopeForClaude(model, claudeRequest, credentials = null) { const projectId = credentials?.projectId || generateProjectId(); - const envelope = { + const envelope: Record = { project: projectId, model: model, userAgent: "antigravity", diff --git a/open-sse/translator/request/openai-to-kiro.js b/open-sse/translator/request/openai-to-kiro.ts similarity index 99% rename from open-sse/translator/request/openai-to-kiro.js rename to open-sse/translator/request/openai-to-kiro.ts index 8529b2cf3f..7bb5e6c43e 100644 --- a/open-sse/translator/request/openai-to-kiro.js +++ b/open-sse/translator/request/openai-to-kiro.ts @@ -22,7 +22,7 @@ function convertMessages(messages, tools, model) { const flushPending = () => { if (currentRole === "user") { const content = pendingUserContent.join("\n\n").trim() || "continue"; - const userMsg = { + const userMsg: Record = { userInputMessage: { content: content, modelId: "", @@ -255,7 +255,7 @@ export function buildKiroPayload(model, body, stream, credentials) { const timestamp = new Date().toISOString(); finalContent = `[Context: Current time is ${timestamp}]\n\n${finalContent}`; - const payload = { + const payload: Record = { conversationState: { chatTriggerType: "MANUAL", conversationId: uuidv4(), diff --git a/open-sse/translator/response/claude-to-openai.js b/open-sse/translator/response/claude-to-openai.ts similarity index 99% rename from open-sse/translator/response/claude-to-openai.js rename to open-sse/translator/response/claude-to-openai.ts index 57e5f9f9b9..47cb022f70 100644 --- a/open-sse/translator/response/claude-to-openai.js +++ b/open-sse/translator/response/claude-to-openai.ts @@ -133,7 +133,7 @@ export function claudeToOpenAIResponse(chunk, state) { if (chunk.delta?.stop_reason) { state.finishReason = convertStopReason(chunk.delta.stop_reason); - const finalChunk = { + const finalChunk: Record = { id: `chatcmpl-${state.messageId}`, object: "chat.completion.chunk", created: Math.floor(Date.now() / 1000), diff --git a/open-sse/translator/response/cursor-to-openai.js b/open-sse/translator/response/cursor-to-openai.ts similarity index 100% rename from open-sse/translator/response/cursor-to-openai.js rename to open-sse/translator/response/cursor-to-openai.ts diff --git a/open-sse/translator/response/gemini-to-claude.js b/open-sse/translator/response/gemini-to-claude.ts similarity index 100% rename from open-sse/translator/response/gemini-to-claude.js rename to open-sse/translator/response/gemini-to-claude.ts diff --git a/open-sse/translator/response/gemini-to-openai.js b/open-sse/translator/response/gemini-to-openai.ts similarity index 99% rename from open-sse/translator/response/gemini-to-openai.js rename to open-sse/translator/response/gemini-to-openai.ts index a678618b8d..8e57ffefc8 100644 --- a/open-sse/translator/response/gemini-to-openai.js +++ b/open-sse/translator/response/gemini-to-openai.ts @@ -226,7 +226,7 @@ export function geminiToOpenAIResponse(chunk, state) { finishReason = "tool_calls"; } - const finalChunk = { + const finalChunk: Record = { id: `chatcmpl-${state.messageId}`, object: "chat.completion.chunk", created: Math.floor(Date.now() / 1000), diff --git a/open-sse/translator/response/kiro-to-openai.js b/open-sse/translator/response/kiro-to-openai.ts similarity index 99% rename from open-sse/translator/response/kiro-to-openai.js rename to open-sse/translator/response/kiro-to-openai.ts index 5e9e8a840d..969595b01b 100644 --- a/open-sse/translator/response/kiro-to-openai.js +++ b/open-sse/translator/response/kiro-to-openai.ts @@ -155,7 +155,7 @@ export function convertKiroToOpenAI(chunk, state) { if (eventType === "messageStopEvent" || eventType === "done" || data.messageStopEvent) { state.finishReason = "stop"; // Mark for usage injection in stream.js - const openaiChunk = { + const openaiChunk: Record = { id: state.responseId, object: "chat.completion.chunk", created: state.created, diff --git a/open-sse/translator/response/openai-responses.js b/open-sse/translator/response/openai-responses.ts similarity index 99% rename from open-sse/translator/response/openai-responses.js rename to open-sse/translator/response/openai-responses.ts index bd4efa4a0e..417a133bd9 100644 --- a/open-sse/translator/response/openai-responses.js +++ b/open-sse/translator/response/openai-responses.ts @@ -519,7 +519,7 @@ export function openaiResponsesToOpenAIResponse(chunk, state) { state.finishReasonSent = true; state.finishReason = "stop"; // Mark for usage injection in stream.js - const finalChunk = { + const finalChunk: Record = { id: state.chatId, object: "chat.completion.chunk", created: state.created, diff --git a/open-sse/translator/response/openai-to-antigravity.js b/open-sse/translator/response/openai-to-antigravity.ts similarity index 96% rename from open-sse/translator/response/openai-to-antigravity.js rename to open-sse/translator/response/openai-to-antigravity.ts index 30bee0ac54..82f17bedac 100644 --- a/open-sse/translator/response/openai-to-antigravity.js +++ b/open-sse/translator/response/openai-to-antigravity.ts @@ -81,7 +81,7 @@ export function openaiToAntigravityResponse(chunk, state) { } // Build candidate - const candidate = { content: { role: "model", parts } }; + const candidate: Record = { content: { role: "model", parts } }; // Finish reason mapping if (finishReason) { @@ -95,7 +95,7 @@ export function openaiToAntigravityResponse(chunk, state) { } // Build response - const response = { + const response: Record = { candidates: [candidate], modelVersion: state._modelVersion, responseId: state._responseId, diff --git a/open-sse/translator/response/openai-to-claude.js b/open-sse/translator/response/openai-to-claude.ts similarity index 100% rename from open-sse/translator/response/openai-to-claude.js rename to open-sse/translator/response/openai-to-claude.ts diff --git a/open-sse/tsconfig.json b/open-sse/tsconfig.json new file mode 100644 index 0000000000..bffe1dbf28 --- /dev/null +++ b/open-sse/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "esnext", + "moduleResolution": "bundler", + "allowJs": true, + "checkJs": true, + "noEmit": true, + "skipLibCheck": true, + "esModuleInterop": true, + "strict": false, + "jsx": "react-jsx", + "lib": ["dom", "esnext"], + "baseUrl": "..", + "paths": { + "@/*": ["./src/*"], + "@omniroute/open-sse": ["./open-sse"], + "@omniroute/open-sse/*": ["./open-sse/*"] + } + }, + "include": [ + "**/*.ts", + "**/*.js" + ] +} diff --git a/open-sse/utils/bypassHandler.js b/open-sse/utils/bypassHandler.ts similarity index 100% rename from open-sse/utils/bypassHandler.js rename to open-sse/utils/bypassHandler.ts diff --git a/open-sse/utils/cursorChecksum.js b/open-sse/utils/cursorChecksum.ts similarity index 100% rename from open-sse/utils/cursorChecksum.js rename to open-sse/utils/cursorChecksum.ts diff --git a/open-sse/utils/cursorProtobuf.js b/open-sse/utils/cursorProtobuf.ts similarity index 100% rename from open-sse/utils/cursorProtobuf.js rename to open-sse/utils/cursorProtobuf.ts diff --git a/open-sse/utils/error.js b/open-sse/utils/error.ts similarity index 96% rename from open-sse/utils/error.js rename to open-sse/utils/error.ts index 12888e057d..0787c0face 100644 --- a/open-sse/utils/error.js +++ b/open-sse/utils/error.ts @@ -131,8 +131,8 @@ export async function parseUpstreamError(response, provider = null) { * @param {number|null} retryAfterMs - Optional retry-after time in milliseconds * @returns {{ success: false, status: number, error: string, response: Response, retryAfterMs?: number }} */ -export function createErrorResult(statusCode, message, retryAfterMs = null) { - const result = { +export function createErrorResult(statusCode: number, message: string, retryAfterMs: number | null = null) { + const result: Record = { success: false, status: statusCode, error: message, @@ -155,7 +155,7 @@ export function createErrorResult(statusCode, message, retryAfterMs = null) { * @param {string} retryAfterHuman - Human-readable retry info e.g. "reset after 30s" * @returns {Response} */ -export function unavailableResponse(statusCode, message, retryAfter, retryAfterHuman) { +export function unavailableResponse(statusCode, message, retryAfter?: any, retryAfterHuman?: any) { const retryAfterSec = Math.max( Math.ceil((new Date(retryAfter).getTime() - Date.now()) / 1000), 1 diff --git a/open-sse/utils/logger.js b/open-sse/utils/logger.ts similarity index 98% rename from open-sse/utils/logger.js rename to open-sse/utils/logger.ts index eca067b9f8..ab205c9c6b 100644 --- a/open-sse/utils/logger.js +++ b/open-sse/utils/logger.ts @@ -96,7 +96,7 @@ export function logger(tag) { const consoleFn = getConsoleFn(level); if (jsonFormat) { - const entry = { + const entry: Record = { ts: new Date().toISOString(), level, tag, @@ -132,7 +132,7 @@ export function createLogger(requestId = null) { const consoleFn = getConsoleFn(level); if (jsonFormat) { - const entry = { + const entry: Record = { ts: new Date().toISOString(), level, tag, diff --git a/open-sse/utils/networkProxy.js b/open-sse/utils/networkProxy.ts similarity index 98% rename from open-sse/utils/networkProxy.js rename to open-sse/utils/networkProxy.ts index 9fbc3ea28e..b0c4e33058 100644 --- a/open-sse/utils/networkProxy.js +++ b/open-sse/utils/networkProxy.ts @@ -34,6 +34,7 @@ async function getConfig() { * @param {string} providerId - Provider ID (e.g., "openai", "anthropic") * @returns {string|null} Proxy URL or null if no proxy configured */ +/** @returns {Promise} */ export async function resolveProxy(providerId) { const config = await getConfig(); diff --git a/open-sse/utils/ollamaTransform.js b/open-sse/utils/ollamaTransform.ts similarity index 96% rename from open-sse/utils/ollamaTransform.js rename to open-sse/utils/ollamaTransform.ts index 6f1c788ec9..a993d081af 100644 --- a/open-sse/utils/ollamaTransform.js +++ b/open-sse/utils/ollamaTransform.ts @@ -1,7 +1,7 @@ // Transform OpenAI SSE stream to Ollama JSON lines format export function transformToOllama(response, model) { let buffer = ""; - let pendingToolCalls = {}; + let pendingToolCalls: Record = {}; const transform = new TransformStream({ transform(chunk, controller) { @@ -51,7 +51,7 @@ export function transformToOllama(response, model) { if (finishReason === "tool_calls" || finishReason === "stop") { const toolCallsArr = Object.values(pendingToolCalls); if (toolCallsArr.length > 0) { - const formattedCalls = toolCallsArr.map((tc) => ({ + const formattedCalls = toolCallsArr.map((tc: any) => ({ function: { name: tc.function.name, arguments: JSON.parse(tc.function.arguments || "{}"), diff --git a/open-sse/utils/progressTracker.js b/open-sse/utils/progressTracker.ts similarity index 97% rename from open-sse/utils/progressTracker.js rename to open-sse/utils/progressTracker.ts index d42dcdd612..20845c390d 100644 --- a/open-sse/utils/progressTracker.js +++ b/open-sse/utils/progressTracker.ts @@ -21,7 +21,7 @@ const DEFAULT_INTERVAL_MS = 2000; * @param {AbortSignal} [options.signal] - Abort signal for cancellation * @returns {TransformStream} */ -export function createProgressTransform({ intervalMs = DEFAULT_INTERVAL_MS, signal } = {}) { +export function createProgressTransform({ intervalMs = DEFAULT_INTERVAL_MS, signal }: { intervalMs?: number; signal?: AbortSignal } = {}) { let tokenCount = 0; let startTime = Date.now(); let intervalId; diff --git a/open-sse/utils/proxyDispatcher.js b/open-sse/utils/proxyDispatcher.ts similarity index 97% rename from open-sse/utils/proxyDispatcher.js rename to open-sse/utils/proxyDispatcher.ts index 0a4f235e1c..e82b279e73 100644 --- a/open-sse/utils/proxyDispatcher.js +++ b/open-sse/utils/proxyDispatcher.ts @@ -112,14 +112,14 @@ export function createProxyDispatcher(proxyUrl) { const parsed = new URL(normalizedUrl); if (parsed.protocol === "socks5:") { - const socksOptions = { + const socksOptions: Record = { type: 5, host: parsed.hostname, port: Number(normalizePort(parsed.port, parsed.protocol)), }; if (parsed.username) socksOptions.userId = decodeURIComponent(parsed.username); if (parsed.password) socksOptions.password = decodeURIComponent(parsed.password); - dispatcher = socksDispatcher(socksOptions); + dispatcher = socksDispatcher(socksOptions as any); } else { dispatcher = new ProxyAgent(normalizedUrl); } diff --git a/open-sse/utils/proxyFetch.js b/open-sse/utils/proxyFetch.ts similarity index 96% rename from open-sse/utils/proxyFetch.js rename to open-sse/utils/proxyFetch.ts index 92a62c7497..5bcce7a285 100644 --- a/open-sse/utils/proxyFetch.js +++ b/open-sse/utils/proxyFetch.ts @@ -126,7 +126,7 @@ export async function runWithProxyContext(proxyConfig, fn) { }); } -async function patchedFetch(input, options = {}) { +async function patchedFetch(input: any, options: any = {}) { if (options?.dispatcher) { return originalFetch(input, options); } @@ -146,7 +146,7 @@ async function patchedFetch(input, options = {}) { // TLS fingerprint spoofing for direct connections (no proxy configured) if (isTlsFingerprintEnabled() && tlsClient.available) { try { - const store = tlsFingerprintContext.getStore(); + const store: any = tlsFingerprintContext.getStore(); if (store) store.used = true; return await tlsClient.fetch(targetUrl, options); } catch (error) { @@ -154,7 +154,7 @@ async function patchedFetch(input, options = {}) { console.warn( `[ProxyFetch] TLS fingerprint failed, falling back to native fetch: ${message}` ); - const store = tlsFingerprintContext.getStore(); + const store: any = tlsFingerprintContext.getStore(); if (store) store.used = false; } } diff --git a/open-sse/utils/requestLogger.js b/open-sse/utils/requestLogger.ts similarity index 100% rename from open-sse/utils/requestLogger.js rename to open-sse/utils/requestLogger.ts diff --git a/open-sse/utils/stream.js b/open-sse/utils/stream.ts similarity index 97% rename from open-sse/utils/stream.js rename to open-sse/utils/stream.ts index c4e4d896a4..7e910b5c5a 100644 --- a/open-sse/utils/stream.js +++ b/open-sse/utils/stream.ts @@ -44,13 +44,15 @@ const STREAM_MODE = { * @param {object} options.body - Request body (for input token estimation) * @param {function} options.onComplete - Callback when stream finishes: ({ status, usage }) => void */ -export function createSSEStream(options = {}) { +/** @param {any} options */ +export function createSSEStream(options: any = {}) { const { mode = STREAM_MODE.TRANSLATE, targetFormat, sourceFormat, provider = null, reqLogger = null, + /** @type {any} */ toolNameMap = null, model = null, connectionId = null, @@ -231,8 +233,8 @@ export function createSSEStream(options = {}) { const translated = translateResponse(targetFormat, sourceFormat, parsed, state); // Log OpenAI intermediate chunks (if available) - if (translated?._openaiIntermediate) { - for (const item of translated._openaiIntermediate) { + if ((translated as any)?._openaiIntermediate) { + for (const item of (translated as any)._openaiIntermediate) { const openaiOutput = formatSSE(item, FORMATS.OPENAI); reqLogger?.appendOpenAIChunk?.(openaiOutput); } @@ -327,8 +329,8 @@ export function createSSEStream(options = {}) { const translated = translateResponse(targetFormat, sourceFormat, parsed, state); // Log OpenAI intermediate chunks - if (translated?._openaiIntermediate) { - for (const item of translated._openaiIntermediate) { + if ((translated as any)?._openaiIntermediate) { + for (const item of (translated as any)._openaiIntermediate) { const openaiOutput = formatSSE(item, FORMATS.OPENAI); reqLogger?.appendOpenAIChunk?.(openaiOutput); } @@ -348,8 +350,8 @@ export function createSSEStream(options = {}) { const flushed = translateResponse(targetFormat, sourceFormat, null, state); // Log OpenAI intermediate chunks for flushed events - if (flushed?._openaiIntermediate) { - for (const item of flushed._openaiIntermediate) { + if ((flushed as any)?._openaiIntermediate) { + for (const item of (flushed as any)._openaiIntermediate) { const openaiOutput = formatSSE(item, FORMATS.OPENAI); reqLogger?.appendOpenAIChunk?.(openaiOutput); } diff --git a/open-sse/utils/streamHandler.js b/open-sse/utils/streamHandler.ts similarity index 98% rename from open-sse/utils/streamHandler.js rename to open-sse/utils/streamHandler.ts index 2f6bffe144..ff92aa5cbf 100644 --- a/open-sse/utils/streamHandler.js +++ b/open-sse/utils/streamHandler.ts @@ -18,7 +18,8 @@ function getTimeString() { * @param {string} options.provider - Provider name * @param {string} options.model - Model name */ -export function createStreamController({ onDisconnect, log, provider, model } = {}) { +/** @param {any} options */ +export function createStreamController({ onDisconnect, log, provider, model }: any = {}) { const abortController = new AbortController(); const startTime = Date.now(); let disconnected = false; diff --git a/open-sse/utils/streamHelpers.js b/open-sse/utils/streamHelpers.ts similarity index 100% rename from open-sse/utils/streamHelpers.js rename to open-sse/utils/streamHelpers.ts diff --git a/open-sse/utils/thinkTagParser.js b/open-sse/utils/thinkTagParser.ts similarity index 100% rename from open-sse/utils/thinkTagParser.js rename to open-sse/utils/thinkTagParser.ts diff --git a/open-sse/utils/tlsClient.js b/open-sse/utils/tlsClient.ts similarity index 84% rename from open-sse/utils/tlsClient.js rename to open-sse/utils/tlsClient.ts index a8e08b4268..5894179557 100644 --- a/open-sse/utils/tlsClient.js +++ b/open-sse/utils/tlsClient.ts @@ -2,7 +2,7 @@ import { createRequire } from "module"; const require = createRequire(import.meta.url); -let createSession; +let createSession: any; try { ({ createSession } = require("wreq-js")); } catch { @@ -13,7 +13,7 @@ try { * Get proxy URL from environment variables. * Priority: HTTPS_PROXY > HTTP_PROXY > ALL_PROXY */ -function getProxyFromEnv() { +function getProxyFromEnv(): string | undefined { return ( process.env.HTTPS_PROXY || process.env.https_proxy || @@ -25,6 +25,14 @@ function getProxyFromEnv() { ); } +interface FetchOptions { + method?: string; + headers?: Record; + body?: any; + redirect?: string; + signal?: AbortSignal; +} + /** * TLS Client — Chrome 124 TLS fingerprint spoofing via wreq-js * Singleton instance used to disguise Node.js TLS handshake as Chrome browser. @@ -33,8 +41,10 @@ function getProxyFromEnv() { * Proxy URL is read from environment variables (HTTPS_PROXY, HTTP_PROXY, ALL_PROXY). */ class TlsClient { + session: any = null; + available: boolean; + constructor() { - this.session = null; this.available = !!createSession; } @@ -43,7 +53,7 @@ class TlsClient { if (this.session) return this.session; const proxy = getProxyFromEnv(); - const sessionOpts = { + const sessionOpts: Record = { browser: "chrome_124", os: "macos", }; @@ -61,13 +71,13 @@ class TlsClient { * Fetch with Chrome 124 TLS fingerprint. * wreq-js Response is already fetch-compatible (headers, text(), json(), clone(), body). */ - async fetch(url, options = {}) { + async fetch(url: string, options: FetchOptions = {}) { const session = await this.getSession(); if (!session) throw new Error("wreq-js not available"); const method = (options.method || "GET").toUpperCase(); - const wreqOptions = { + const wreqOptions: Record = { method, headers: options.headers, body: options.body, @@ -92,3 +102,4 @@ class TlsClient { } export default new TlsClient(); + diff --git a/open-sse/utils/usageTracking.js b/open-sse/utils/usageTracking.ts similarity index 100% rename from open-sse/utils/usageTracking.js rename to open-sse/utils/usageTracking.ts diff --git a/package-lock.json b/package-lock.json index b4f8dbddad..f128d8b998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "omniroute", - "version": "0.8.0", + "version": "0.8.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "omniroute", - "version": "0.8.0", + "version": "0.8.5", "license": "MIT", "workspaces": [ "open-sse" diff --git a/package.json b/package.json index 9bb0cfff30..f3379e97fe 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "omniroute", - "version": "0.8.0", + "version": "0.8.5", "description": "Smart AI Router with auto fallback — route to FREE & cheap models, zero downtime. Works with Cursor, Cline, Claude Desktop, Codex, and any OpenAI-compatible tool.", "type": "module", "bin": {