diff --git a/docs/.vitepress/config.test.ts b/docs/.vitepress/config.test.ts index 0e60588..b70ed40 100644 --- a/docs/.vitepress/config.test.ts +++ b/docs/.vitepress/config.test.ts @@ -1,5 +1,5 @@ import { existsSync, globSync } from 'node:fs'; -import { resolve } from 'node:path'; +import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import config from './config'; @@ -36,11 +36,21 @@ const getAlias = (aliasConfig: unknown, specifier: string): string | undefined = const repoRoot = fileURLToPath(new URL('../..', import.meta.url)); const getExpectedEntry = (hoistedRelativePath: string, pnpmPattern: string) => { - const hoistedEntry = resolve(repoRoot, hoistedRelativePath); - if (existsSync(hoistedEntry)) return hoistedEntry; + let currentRoot = repoRoot; - const [pnpmEntry] = globSync(resolve(repoRoot, pnpmPattern)); - return pnpmEntry; + while (true) { + const hoistedEntry = resolve(currentRoot, hoistedRelativePath); + if (existsSync(hoistedEntry)) return hoistedEntry; + + const [pnpmEntry] = globSync(resolve(currentRoot, pnpmPattern)); + if (pnpmEntry) return pnpmEntry; + + const parentRoot = dirname(currentRoot); + if (parentRoot === currentRoot) break; + currentRoot = parentRoot; + } + + return undefined; }; describe('docs vitepress config', () => { diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9a49d17..ed77a67 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -1,17 +1,28 @@ import { existsSync, globSync } from 'node:fs'; -import { resolve } from 'node:path'; +import { dirname, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import { defineConfig } from 'vitepress'; import { withMermaid } from 'vitepress-plugin-mermaid'; const repoRoot = fileURLToPath(new URL('../..', import.meta.url)); -const resolveDependencyEntry = (hoistedRelativePath: string, pnpmPattern: string) => { - const hoistedEntry = resolve(repoRoot, hoistedRelativePath); - if (existsSync(hoistedEntry)) return hoistedEntry; - const [pnpmEntry] = globSync(resolve(repoRoot, pnpmPattern)); - return pnpmEntry; -}; +function resolveDependencyEntry(hoistedRelativePath: string, pnpmPattern: string) { + let currentRoot = repoRoot; + + while (true) { + const hoistedEntry = resolve(currentRoot, hoistedRelativePath); + if (existsSync(hoistedEntry)) return hoistedEntry; + + const [pnpmEntry] = globSync(resolve(currentRoot, pnpmPattern)); + if (pnpmEntry) return pnpmEntry; + + const parentRoot = dirname(currentRoot); + if (parentRoot === currentRoot) break; + currentRoot = parentRoot; + } + + return undefined; +} const dayjsEsmEntry = resolveDependencyEntry( 'node_modules/dayjs/esm/index.js', diff --git a/src/server/routes/api/accountTokens.sync.test.ts b/src/server/routes/api/accountTokens.sync.test.ts index 5c951d3..f668bf6 100644 --- a/src/server/routes/api/accountTokens.sync.test.ts +++ b/src/server/routes/api/accountTokens.sync.test.ts @@ -63,6 +63,7 @@ describe('account tokens sync routes with site status', () => { beforeAll(async () => { dataDir = mkdtempSync(join(tmpdir(), 'metapi-account-tokens-sync-')); process.env.DATA_DIR = dataDir; + vi.resetModules(); await import('../../db/migrate.js'); const dbModule = await import('../../db/index.js'); diff --git a/src/server/routes/api/oauth.test.ts b/src/server/routes/api/oauth.test.ts index d9d1de3..b81fc0f 100644 --- a/src/server/routes/api/oauth.test.ts +++ b/src/server/routes/api/oauth.test.ts @@ -4,7 +4,6 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vites import { mkdtempSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { config } from '../../config.js'; const fetchMock = vi.fn(); const undiciAgentCtorMock = vi.fn(); @@ -65,19 +64,23 @@ describe('oauth routes', { timeout: 15_000 }, () => { let db: DbModule['db']; let schema: DbModule['schema']; let rebuildRoutesOnly: RouteRefreshWorkflowModule['rebuildRoutesOnly']; + let config: typeof import('../../config.js').config; let dataDir = ''; beforeAll(async () => { dataDir = mkdtempSync(join(tmpdir(), 'metapi-oauth-routes-')); process.env.DATA_DIR = dataDir; + vi.resetModules(); await import('../../db/migrate.js'); const dbModule = await import('../../db/index.js'); const routesModule = await import('./oauth.js'); const routeRefreshWorkflow = await import('../../services/routeRefreshWorkflow.js'); + const configModule = await import('../../config.js'); db = dbModule.db; schema = dbModule.schema; rebuildRoutesOnly = routeRefreshWorkflow.rebuildRoutesOnly; + config = configModule.config; app = Fastify(); await app.register(routesModule.oauthRoutes); diff --git a/src/server/routes/proxy/architecture-boundaries.test.ts b/src/server/routes/proxy/architecture-boundaries.test.ts index a940037..bb1707c 100644 --- a/src/server/routes/proxy/architecture-boundaries.test.ts +++ b/src/server/routes/proxy/architecture-boundaries.test.ts @@ -100,7 +100,7 @@ describe('proxy route architecture boundaries', () => { it('keeps codex responses normalization behind transformer helpers', () => { const source = readSource('./upstreamEndpoint.ts'); - expect(source).toContain("from '../../transformers/openai/responses/codexCompatibility.js'"); + expect(source).toContain("from '../../services/upstreamRequestBuilder.js'"); expect(source).not.toContain('function ensureCodexResponsesInstructions('); expect(source).not.toContain('function ensureCodexResponsesStoreFalse('); expect(source).not.toContain('function stripCodexUnsupportedResponsesFields('); diff --git a/src/server/routes/proxy/gemini.test.ts b/src/server/routes/proxy/gemini.test.ts index d05a640..ff67434 100644 --- a/src/server/routes/proxy/gemini.test.ts +++ b/src/server/routes/proxy/gemini.test.ts @@ -867,7 +867,6 @@ describe('gemini native proxy routes', () => { expect(JSON.parse(String(requestInit.body))).toEqual({ model: 'gpt-4.1', stream: false, - max_output_tokens: 4096, input: [ { type: 'message', diff --git a/src/server/services/modelService.ts b/src/server/services/modelService.ts index 3bdcdbe..4cca0d1 100644 --- a/src/server/services/modelService.ts +++ b/src/server/services/modelService.ts @@ -818,7 +818,7 @@ export async function refreshModelsForAccount( `api token discovery timeout (${Math.round(API_TOKEN_DISCOVERY_TIMEOUT_MS / 1000)}s)`, ); if (discoveredApiToken && !isMaskedTokenValue(discoveredApiToken)) { - ensureDefaultTokenForAccount(account.id, discoveredApiToken, { name: 'default', source: 'sync' }); + await ensureDefaultTokenForAccount(account.id, discoveredApiToken, { name: 'default', source: 'sync' }); await db.update(schema.accounts).set({ apiToken: discoveredApiToken, updatedAt: new Date().toISOString(), @@ -846,7 +846,7 @@ export async function refreshModelsForAccount( if (usesManagedTokens && enabledTokens.length === 0) { const fallback = discoveredApiToken || account.apiToken || null; if (fallback) { - ensureDefaultTokenForAccount(account.id, fallback, { name: 'default', source: 'legacy' }); + await ensureDefaultTokenForAccount(account.id, fallback, { name: 'default', source: 'legacy' }); enabledTokens = await db.select() .from(schema.accountTokens) .where(and( diff --git a/src/server/transformers/shared/normalized.test.ts b/src/server/transformers/shared/normalized.test.ts index 3d3d7df..d7d38fe 100644 --- a/src/server/transformers/shared/normalized.test.ts +++ b/src/server/transformers/shared/normalized.test.ts @@ -154,6 +154,9 @@ describe('shared normalized helpers', () => { id: 'resp_terminal_2', model: 'gpt-test', created: 456, + incomplete_details: { + reason: 'max_output_tokens', + }, output: [ { type: 'message',