(reqPath, body, opts = {})
| 1081 | // auth. The access token comes from EVOMAP_VERTEX_ACCESS_TOKEN (provisioned/refreshed by the daemon via |
| 1082 | // `gcloud auth print-access-token` or a token sidecar); SA-key auto-minting is a follow-up. No translation. |
| 1083 | async _proxyVertex(reqPath, body, opts = {}) { |
| 1084 | const baseUrl = String(opts.baseUrl || '').replace(/\/+$/, ''); |
| 1085 | if (!baseUrl) { const e = new Error('vertex base url required'); e.statusCode = 500; throw e; } |
| 1086 | const token = process.env.EVOMAP_VERTEX_ACCESS_TOKEN || ''; |
| 1087 | if (!token) { const e = new Error('vertex access token required'); e.statusCode = 401; throw e; } |
| 1088 | const timeoutMs = opts.timeoutMs || 60_000; |
| 1089 | const fwd = { 'content-type': 'application/json', authorization: `Bearer ${token}` }; |
| 1090 | |
| 1091 | const endpoint = `${baseUrl}${reqPath}`; |
| 1092 | const abortController = new AbortController(); |
| 1093 | const timeoutErr = new Error('vertex upstream timed out'); |
| 1094 | timeoutErr.name = 'TimeoutError'; |
| 1095 | const abortTimer = setTimeout(() => abortController.abort(timeoutErr), timeoutMs); |
| 1096 | abortTimer.unref?.(); |
| 1097 | let res; |
| 1098 | try { |
| 1099 | res = await fetch(endpoint, { method: 'POST', headers: fwd, body: JSON.stringify(body || {}), signal: abortController.signal }); |
| 1100 | } catch (err) { |
| 1101 | clearTimeout(abortTimer); |
| 1102 | throw makeVertexGatewayError(err); |
| 1103 | } |
| 1104 | |
| 1105 | const headers = Object.fromEntries(res.headers.entries()); |
| 1106 | const contentType = (headers['content-type'] || '').toLowerCase(); |
| 1107 | const isStream = contentType.includes('text/event-stream') || /:streamGenerateContent(\b|\?|$)/.test(reqPath); |
| 1108 | if (isStream) clearTimeout(abortTimer); |
| 1109 | |
| 1110 | const readText = async () => { |
| 1111 | try { return await res.text(); } catch (err) { throw makeVertexGatewayError(err); } finally { clearTimeout(abortTimer); } |
| 1112 | }; |
| 1113 | |
| 1114 | return { |
| 1115 | status: res.status, |
| 1116 | headers, |
| 1117 | stream: isStream ? res.body : null, |
| 1118 | json: isStream ? null : async () => JSON.parse(await readText()), |
| 1119 | text: isStream ? null : readText, |
| 1120 | }; |
| 1121 | } |
| 1122 | |
| 1123 | // Bedrock upstream mode: same return contract as _proxyAnthropic so |
| 1124 | // messages_route.js and ProxyHttpServer._streamResponse don't change. |
no test coverage detected