mirror of
https://github.com/NanmiCoder/claude-code-haha.git
synced 2026-10-10 03:43:11 +08:00
fe10efb65f
OpenCode Go binds the wire format to the URL path and translates nothing between formats, so one provider record serves /chat/completions, /messages and /responses depending on the model, each accepting a different credential header. A record carries only one apiFormat, so presets can now declare ordered per-model prefix rules (modelApiFormats) and the proxy resolves the effective format from the request body. Only the exceptions are listed; anything unmatched keeps the provider's format, which is the endpoint with the broadest compatibility. A preset with rules is authoritative for apiFormat, because a value written by a cc-switch import or the edit form would otherwise silently disable every rule and point the CLI straight at the upstream. The gateway also rejects any request without a stable per-conversation x-opencode-session, so presets can declare upstreamHeaders with $SESSION_ID and $VERSION placeholders. The id is the one the CLI already sends; it is redacted in traces the same way the credential is, since it also names the local transcript files. Title generation builds its own upstream request and bypassed all of the above, which left AI titles failing for every provider that needs local request handling; it now goes through the same proxy the CLI uses. Verified against the live gateway: glm/kimi reach /chat/completions, minimax/qwen/union-alpha reach /messages, grok/gpt reach /responses, each with the credential that endpoint accepts.
77 lines
2.9 KiB
TypeScript
77 lines
2.9 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
import { mkdtempSync, rmSync } from 'node:fs'
|
|
import { tmpdir } from 'node:os'
|
|
import { join } from 'node:path'
|
|
import { rootBunTestFilter } from './bun-test-filter'
|
|
import { createSandboxedTestEnvironment } from './test-environment'
|
|
|
|
const root = join(import.meta.dir, '..', '..')
|
|
// Keep this lane deterministic. Real credentials and third-party connectivity
|
|
// belong to the trusted provider-smoke lanes, never to external PR checks.
|
|
const testFiles = [
|
|
'src/server/__tests__/provider-settings-isolation.test.ts',
|
|
'src/server/__tests__/provider-presets.test.ts',
|
|
'src/server/__tests__/provider-runtime-env.test.ts',
|
|
'src/server/services/ccSwitchImport.test.ts',
|
|
'src/server/services/providerModelCatalog.test.ts',
|
|
'src/shared/modelReasoning.test.ts',
|
|
'src/shared/modelApiFormats.test.ts',
|
|
'src/server/proxy/upstreamHeaders.test.ts',
|
|
'src/services/api/client.test.ts',
|
|
'src/services/api/claudeRequiredThinking.test.ts',
|
|
'src/services/compact/autoCompact.test.ts',
|
|
'src/services/openaiAuth/client.test.ts',
|
|
'src/services/openaiAuth/fetch.test.ts',
|
|
'src/services/openaiAuth/models.test.ts',
|
|
'src/services/openaiAuth/storage.test.ts',
|
|
'src/utils/__tests__/providerManagedEnvCompat.test.ts',
|
|
'src/utils/managedEnv.test.ts',
|
|
'src/utils/model/modelContextWindows.test.ts',
|
|
'src/server/__tests__/providers.test.ts',
|
|
'src/server/services/providerRequestCompatibility.test.ts',
|
|
'src/server/proxy/transform/requestCompatibility.test.ts',
|
|
'src/server/proxy/handlerRequestCompatibility.test.ts',
|
|
'src/server/proxy/protocolTrace.test.ts',
|
|
'src/server/proxy/handlerProtocolTrace.test.ts',
|
|
'src/server/proxy/streaming/openaiChatStreamToAnthropic.test.ts',
|
|
'src/server/proxy/streaming/openaiResponsesContract.test.ts',
|
|
'src/server/proxy/transform/openaiChatToAnthropic.test.ts',
|
|
'src/services/api/outputBudget.test.ts',
|
|
'src/services/api/streamResponseBoundary.test.ts',
|
|
'src/utils/sideQuery.test.ts',
|
|
'src/query/proxyToolCommit.test.ts',
|
|
'src/server/__tests__/proxy-transform.test.ts',
|
|
'src/server/__tests__/proxy-streaming.test.ts',
|
|
'src/server/__tests__/network-settings.test.ts',
|
|
'src/server/__tests__/proxy-network-settings.test.ts',
|
|
'src/utils/proxy.test.ts',
|
|
]
|
|
|
|
for (const testFile of testFiles) {
|
|
console.log(`\n[provider-contract] ${testFile}`)
|
|
const sandboxHome = mkdtempSync(join(tmpdir(), 'cc-haha-provider-contract-'))
|
|
let exitCode = 1
|
|
try {
|
|
const proc = Bun.spawn([
|
|
'bun',
|
|
'--no-env-file',
|
|
'test',
|
|
rootBunTestFilter(testFile),
|
|
], {
|
|
cwd: root,
|
|
env: createSandboxedTestEnvironment(sandboxHome),
|
|
stdout: 'inherit',
|
|
stderr: 'inherit',
|
|
})
|
|
exitCode = await proc.exited
|
|
} finally {
|
|
rmSync(sandboxHome, { recursive: true, force: true })
|
|
}
|
|
if (exitCode !== 0) {
|
|
process.exit(exitCode)
|
|
}
|
|
}
|
|
|
|
console.log(`\n[provider-contract] ${testFiles.length} deterministic suites passed`)
|