|
| 1 | +import { AxiosInstance } from "axios" |
1 | 2 | import { spawn } from "child_process"
|
2 | 3 | import { Api } from "coder/site/src/api/api"
|
3 | 4 | import { ProvisionerJobLog, Workspace } from "coder/site/src/api/typesGenerated"
|
| 5 | +import { FetchLikeInit } from "eventsource" |
4 | 6 | import fs from "fs/promises"
|
5 | 7 | import { ProxyAgent } from "proxy-agent"
|
6 | 8 | import * as vscode from "vscode"
|
@@ -120,6 +122,60 @@ export async function makeCoderSdk(baseUrl: string, token: string | undefined, s
|
120 | 122 | return restClient
|
121 | 123 | }
|
122 | 124 |
|
| 125 | +/** |
| 126 | + * Creates a fetch adapter using an Axios instance that returns streaming responses. |
| 127 | + * This can be used with APIs that accept fetch-like interfaces. |
| 128 | + */ |
| 129 | +export function createStreamingFetchAdapter(axiosInstance: AxiosInstance) { |
| 130 | + return async (url: string | URL, init?: FetchLikeInit) => { |
| 131 | + const urlStr = url.toString() |
| 132 | + |
| 133 | + const response = await axiosInstance.request({ |
| 134 | + url: urlStr, |
| 135 | + headers: init?.headers as Record<string, string>, |
| 136 | + responseType: "stream", |
| 137 | + validateStatus: () => true, // Don't throw on any status code |
| 138 | + }) |
| 139 | + const stream = new ReadableStream({ |
| 140 | + start(controller) { |
| 141 | + response.data.on("data", (chunk: Buffer) => { |
| 142 | + controller.enqueue(chunk) |
| 143 | + }) |
| 144 | + |
| 145 | + response.data.on("end", () => { |
| 146 | + controller.close() |
| 147 | + }) |
| 148 | + |
| 149 | + response.data.on("error", (err: Error) => { |
| 150 | + controller.error(err) |
| 151 | + }) |
| 152 | + }, |
| 153 | + |
| 154 | + cancel() { |
| 155 | + response.data.destroy() |
| 156 | + return Promise.resolve() |
| 157 | + }, |
| 158 | + }) |
| 159 | + |
| 160 | + const createReader = () => stream.getReader() |
| 161 | + |
| 162 | + return { |
| 163 | + body: { |
| 164 | + getReader: () => createReader(), |
| 165 | + }, |
| 166 | + url: urlStr, |
| 167 | + status: response.status, |
| 168 | + redirected: response.request.res.responseUrl !== urlStr, |
| 169 | + headers: { |
| 170 | + get: (name: string) => { |
| 171 | + const value = response.headers[name.toLowerCase()] |
| 172 | + return value === undefined ? null : String(value) |
| 173 | + }, |
| 174 | + }, |
| 175 | + } |
| 176 | + } |
| 177 | +} |
| 178 | + |
123 | 179 | /**
|
124 | 180 | * Start or update a workspace and return the updated workspace.
|
125 | 181 | */
|
@@ -224,6 +280,7 @@ export async function waitForBuild(
|
224 | 280 | | undefined,
|
225 | 281 | },
|
226 | 282 | followRedirects: true,
|
| 283 | + agent: restClient.getAxiosInstance().defaults.httpAgent, |
227 | 284 | })
|
228 | 285 | socket.binaryType = "nodebuffer"
|
229 | 286 | socket.on("message", (data) => {
|
|
0 commit comments