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"
@@ -90,6 +92,58 @@ export async function makeCoderSdk(baseUrl: string, token: string | undefined, s
90
92
return restClient
91
93
}
92
94
95
+ /**
96
+ * Creates a fetch adapter using an Axios instance that returns streaming responses.
97
+ * This can be used with APIs that accept fetch-like interfaces.
98
+ */
99
+ export function createStreamingFetchAdapter ( axiosInstance :AxiosInstance ) {
100
+ return async ( url :string | URL , init ?:FetchLikeInit ) => {
101
+ const urlStr = url . toString ( )
102
+
103
+ const response = await axiosInstance . request ( {
104
+ url :urlStr ,
105
+ headers :init ?. headers as Record < string , string > ,
106
+ responseType :"stream" ,
107
+ validateStatus :( ) => true , // Don't throw on any status code
108
+ } )
109
+ const stream = new ReadableStream ( {
110
+ start ( controller ) {
111
+ response . data . on ( "data" , ( chunk :Buffer ) => {
112
+ controller . enqueue ( chunk )
113
+ } )
114
+
115
+ response . data . on ( "end" , ( ) => {
116
+ controller . close ( )
117
+ } )
118
+
119
+ response . data . on ( "error" , ( err :Error ) => {
120
+ controller . error ( err )
121
+ } )
122
+ } ,
123
+
124
+ cancel ( ) {
125
+ response . data . destroy ( )
126
+ return Promise . resolve ( )
127
+ } ,
128
+ } )
129
+
130
+ return {
131
+ body :{
132
+ getReader :( ) => stream . getReader ( ) ,
133
+ } ,
134
+ url :urlStr ,
135
+ status :response . status ,
136
+ redirected :response . request . res . responseUrl !== urlStr ,
137
+ headers :{
138
+ get :( name :string ) => {
139
+ const value = response . headers [ name . toLowerCase ( ) ]
140
+ return value === undefined ?null :String ( value )
141
+ } ,
142
+ } ,
143
+ }
144
+ }
145
+ }
146
+
93
147
/**
94
148
* Start or update a workspace and return the updated workspace.
95
149
*/
@@ -182,6 +236,7 @@ export async function waitForBuild(
182
236
path += `&after=${ logs [ logs . length - 1 ] . id } `
183
237
}
184
238
239
+ const agent = await createHttpAgent ( )
185
240
await new Promise < void > ( ( resolve , reject ) => {
186
241
try {
187
242
const baseUrl = new URL ( baseUrlRaw )
@@ -194,6 +249,7 @@ export async function waitForBuild(
194
249
| undefined ,
195
250
} ,
196
251
followRedirects :true ,
252
+ agent :agent ,
197
253
} )
198
254
socket . binaryType = "nodebuffer"
199
255
socket . on ( "message" , ( data ) => {