fix(opencode): hydrate Proxy Pool references for no-auth connections (#11584)

Merged via /merge-batch (lote 2026-08-26, v3.8.51). Boarded no worktree combinado junto com outras ~30 PRs; validação única: typecheck/complexity/cognitive-complexity/changelog-integrity verdes, file-size rebaseado onde necessário (crescimento legítimo), lint com os mesmos 228 achados pré-existentes confirmados via sonda contra o tip puro (não introduzidos por este lote), e ~370 testes focados (unit + vitest) passando. Obrigado pela contribuição.
This commit is contained in:
AStupidBear
2026-08-26 19:10:16 +08:00
committed by GitHub
parent fe12208e30
commit 4449ba3173
2 changed files with 54 additions and 8 deletions

View File

@@ -1128,13 +1128,40 @@ function planLastUsedCommit(
};
}
function materializeConnection(
/**
* Resolve Proxy Pool references on a real connection row at the same boundary
* where credentials become request-ready. The synthetic no-auth fallback above
* already performs this hydration, but a persisted connection (for example the
* OpenCode card's `opencode` row selected through the `opencode-zen` alias)
* bypasses that fallback. Keep inline/legacy entries untouched and only incur a
* registry lookup when at least one by-id reference is present.
*/
async function hydrateAccountProxyReferences(
providerSpecificData: JsonRecord
): Promise<JsonRecord> {
const entries = providerSpecificData.accountProxies;
if (!Array.isArray(entries)) return providerSpecificData;
const containsProxyReference = entries.some((entry) => {
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return false;
const proxyId = (entry as Record<string, unknown>).proxyId;
return typeof proxyId === "string" && proxyId.trim().length > 0;
});
if (!containsProxyReference) return providerSpecificData;
return {
...providerSpecificData,
accountProxies: await resolveAccountProxiesFromRegistry(entries),
};
}
async function materializeConnection(
connection: ProviderConnectionView,
options: CredentialSelectionOptions,
extra: DeferredLeaseSelection & { exclusiveLease?: ExclusiveConnectionLease } = {}
) {
const apiKeyHealth = connection.providerSpecificData?.apiKeyHealth as
Record<string, KeyHealth> | undefined;
const providerSpecificData = await hydrateAccountProxyReferences(connection.providerSpecificData);
const apiKeyHealth = providerSpecificData.apiKeyHealth as Record<string, KeyHealth> | undefined;
if (apiKeyHealth) syncHealthFromDB(connection.id, apiKeyHealth);
const releaseOAuthSession =
options.reserveOAuthSession === true && connection.authType === "oauth" && options.sessionKey
@@ -1148,10 +1175,10 @@ function materializeConnection(
projectId: connection.projectId,
defaultModel: connection.defaultModel || null,
copilotToken:
typeof connection.providerSpecificData.copilotToken === "string"
? connection.providerSpecificData.copilotToken
typeof providerSpecificData.copilotToken === "string"
? providerSpecificData.copilotToken
: null,
providerSpecificData: connection.providerSpecificData,
providerSpecificData,
id: connection.id,
provider: connection.provider,
authType: connection.authType,

View File

@@ -27,6 +27,7 @@ process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const { getProviderCredentials } = await import("../../src/sse/services/auth.ts");
const { createProviderConnection } = await import("../../src/lib/db/providers.ts");
const { createProxy } = await import("../../src/lib/db/proxies.ts");
const { OpencodeExecutor } = await import("../../open-sse/executors/opencode.ts");
const { resolveProxyForRequest } = await import("../../open-sse/utils/proxyFetch.ts");
@@ -47,10 +48,17 @@ function listen(server: net.Server): Promise<number> {
test.before(async () => {
proxyServer = net.createServer((s) => s.destroy());
proxyPort = await listen(proxyServer);
const proxy = await createProxy({
name: "opencode-noauth-test-proxy",
type: "http",
host: "127.0.0.1",
port: proxyPort,
});
assert.ok(proxy?.id, "test proxy must be persisted in the registry");
// Mirror exactly what the NoAuthAccountCard UI writes: a `provider_connections`
// row filed under the no-auth id "opencode" (NOT "opencode-zen"), carrying the
// configured account proxy.
// configured account proxy as a Proxy Pool reference.
await createProviderConnection({
provider: "opencode",
authType: "no-auth",
@@ -61,7 +69,7 @@ test.before(async () => {
accountProxies: [
{
fingerprint: FINGERPRINT,
proxy: { type: "http", host: "127.0.0.1", port: proxyPort },
proxyId: proxy.id,
},
],
},
@@ -95,6 +103,17 @@ test("#7993 getProviderCredentials('opencode-zen') hydrates the proxy saved unde
Array.isArray(psd.accountProxies) && psd.accountProxies.length === 1,
`expected the sibling opencode connection's accountProxies to be hydrated, got ${JSON.stringify(psd)}`
);
const accountProxy = (psd.accountProxies as Array<Record<string, unknown>>)[0];
assert.equal(
accountProxy.proxyId,
undefined,
"request credentials must not retain a raw proxyId"
);
assert.deepEqual(accountProxy.proxy, {
type: "http",
host: "127.0.0.1",
port: proxyPort,
});
});
test("#7993 a canonical 'opencode/<model>' resolved combo/catalog target egresses through the assigned proxy, not direct", async () => {