From fc9705962391df356a34ea583804e8e7cc8ba31d Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Fri, 15 Aug 2025 00:56:52 +0300
Subject: [PATCH 1/3] Changelog update - `v0.6.2` (#175)

Current pull request contains patched `CHANGELOG.md` file for the
`v0.6.2` version.

Co-authored-by: GitHub Action <action@github.com>
---
 CHANGELOG.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1dc0b6a..574103d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+## 0.6.2 - 2025-08-14
+
 ### Changed
 
 - content-type is now enforced when downloading the CLI to accept only binary responses

From 6ab431e26b3dfb60ea832065731fc23020b4e2ed Mon Sep 17 00:00:00 2001
From: Faur Ioan-Aurel <fioan89@gmail.com>
Date: Sun, 17 Aug 2025 18:35:34 +0300
Subject: [PATCH 2/3] impl: poll workspaces when Toolbox screen becomes visible
 (#176)

Some users complained that Coder Toolbox has a noticeable delay in
rendering the workspaces
status compared with the web Dashboard, even though they have the same
polling frequency,
which is every 5 seconds.
However, the web Dashboard also reacts and when the tab receives focus
from the user which
improves the experience. This PR tries to implement the same behavior,
when Coder Toolbox
screen becomes visible the polling is triggered immediately which should
result in a status update.
---
 CHANGELOG.md                                     |  4 ++++
 gradle.properties                                |  2 +-
 .../com/coder/toolbox/CoderRemoteProvider.kt     | 16 +++++++++++++---
 3 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 574103d..abee5fe 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## Unreleased
 
+### Changed
+
+- workspaces status is now refresh every time Coder Toolbox becomes visible
+
 ## 0.6.2 - 2025-08-14
 
 ### Changed
diff --git a/gradle.properties b/gradle.properties
index 846b191..b10e0c2 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,3 @@
-version=0.6.2
+version=0.6.3
 group=com.coder.toolbox
 name=coder-toolbox
\ No newline at end of file
diff --git a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt
index 2e5d557..596255e 100644
--- a/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt
+++ b/src/main/kotlin/com/coder/toolbox/CoderRemoteProvider.kt
@@ -54,8 +54,8 @@ class CoderRemoteProvider(
 
     private val settings = context.settingsStore.readOnly()
 
-    // Create our services from the Toolbox ones.
     private val triggerSshConfig = Channel<Boolean>(Channel.CONFLATED)
+    private val triggerProviderVisible = Channel<Boolean>(Channel.CONFLATED)
     private val settingsPage: CoderSettingsPage = CoderSettingsPage(context, triggerSshConfig)
     private val dialogUi = DialogUi(context)
 
@@ -177,14 +177,19 @@ class CoderRemoteProvider(
 
             select {
                 onTimeout(POLL_INTERVAL) {
-                    context.logger.trace("workspace poller waked up by the $POLL_INTERVAL timeout")
+                    context.logger.debug("workspace poller waked up by the $POLL_INTERVAL timeout")
                 }
                 triggerSshConfig.onReceive { shouldTrigger ->
                     if (shouldTrigger) {
-                        context.logger.trace("workspace poller waked up because it should reconfigure the ssh configurations")
+                        context.logger.debug("workspace poller waked up because it should reconfigure the ssh configurations")
                         cli.configSsh(lastEnvironments.map { it.asPairOfWorkspaceAndAgent() }.toSet())
                     }
                 }
+                triggerProviderVisible.onReceive { isCoderProviderVisible ->
+                    if (isCoderProviderVisible) {
+                        context.logger.debug("workspace poller waked up by Coder Toolbox which is currently visible, fetching latest workspace statuses")
+                    }
+                }
             }
             lastPollTime = TimeSource.Monotonic.markNow()
         }
@@ -293,6 +298,11 @@ class CoderRemoteProvider(
         visibilityState.update {
             visibility
         }
+        if (visibility.providerVisible) {
+            context.cs.launch {
+                triggerProviderVisible.send(true)
+            }
+        }
     }
 
     /**

From acd057859ffa94656feb3cfa455971a043cb41fc Mon Sep 17 00:00:00 2001
From: Faur Ioan-Aurel <fioan89@gmail.com>
Date: Tue, 19 Aug 2025 23:32:58 +0300
Subject: [PATCH 3/3] fix: support for downloading the CLI when proxy is
 configured (#177)

Until this commit, the CLI download manager relied on a separately configured HTTP
client that lacked proxy support, unlike the REST client which was refactored and modularized.
Now we have the same support for proxy and a proper user agent and custom logging interceptor.
---
 CHANGELOG.md                                  |  4 +
 README.md                                     | 21 +++++
 .../com/coder/toolbox/cli/CoderCLIManager.kt  | 23 +++--
 .../toolbox/sdk/CoderHttpClientBuilder.kt     | 56 ++++++++++++
 .../com/coder/toolbox/sdk/CoderRestClient.kt  | 85 +++----------------
 .../toolbox/sdk/interceptors/Interceptors.kt  | 64 ++++++++++++++
 .../coder/toolbox/cli/CoderCLIManagerTest.kt  | 25 ++++--
 src/test/resources/extension.json             |  4 +
 8 files changed, 192 insertions(+), 90 deletions(-)
 create mode 100644 src/main/kotlin/com/coder/toolbox/sdk/CoderHttpClientBuilder.kt
 create mode 100644 src/main/kotlin/com/coder/toolbox/sdk/interceptors/Interceptors.kt
 create mode 100644 src/test/resources/extension.json

diff --git a/CHANGELOG.md b/CHANGELOG.md
index abee5fe..e87ca97 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,10 @@
 
 - workspaces status is now refresh every time Coder Toolbox becomes visible
 
+### Fixed
+
+- support for downloading the CLI when proxy is configured
+
 ## 0.6.2 - 2025-08-14
 
 ### Changed
diff --git a/README.md b/README.md
index 3e5da52..74e9cd5 100644
--- a/README.md
+++ b/README.md
@@ -220,6 +220,27 @@ mitmweb --ssl-insecure --set stream_large_bodies="10m" --mode socks5
 >
 in: https://youtrack.jetbrains.com/issue/TBX-14532/Missing-proxy-authentication-settings#focus=Comments-27-12265861.0-0
 
+### Mitmproxy returns 502 Bad Gateway to the client
+
+When running traffic through mitmproxy, you may encounter 502 Bad Gateway errors that mention HTTP/2 protocol error: *
+*Received header value surrounded by whitespace**.
+This happens because some upstream servers (including dev.coder.com) send back headers such as Content-Security-Policy
+with leading or trailing spaces.
+While browsers and many HTTP clients accept these headers, mitmproxy enforces the stricter HTTP/2 and HTTP/1.1 RFCs,
+which forbid whitespace around header values.
+As a result, mitmproxy rejects the response and surfaces a 502 to the client.
+
+The workaround is to disable HTTP/2 in mitmproxy and force HTTP/1.1 on both the client and upstream sides. This avoids
+the strict header validation path and allows
+mitmproxy to pass responses through unchanged. You can do this by starting mitmproxy with:
+
+```bash
+mitmproxy --set http2=false --set upstream_http_version=HTTP/1.1
+```
+
+This ensures coder toolbox http client ↔ mitmproxy ↔ server connections all run over HTTP/1.1, preventing the whitespace
+error.
+
 ## Debugging and Reporting issues
 
 Enabling debug logging is essential for diagnosing issues with the Toolbox plugin, especially when SSH
diff --git a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt
index 582a85b..67947c3 100644
--- a/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt
+++ b/src/main/kotlin/com/coder/toolbox/cli/CoderCLIManager.kt
@@ -12,14 +12,14 @@ import com.coder.toolbox.cli.gpg.GPGVerifier
 import com.coder.toolbox.cli.gpg.VerificationResult
 import com.coder.toolbox.cli.gpg.VerificationResult.Failed
 import com.coder.toolbox.cli.gpg.VerificationResult.Invalid
+import com.coder.toolbox.plugin.PluginManager
+import com.coder.toolbox.sdk.CoderHttpClientBuilder
+import com.coder.toolbox.sdk.interceptors.Interceptors
 import com.coder.toolbox.sdk.v2.models.Workspace
 import com.coder.toolbox.sdk.v2.models.WorkspaceAgent
 import com.coder.toolbox.settings.SignatureFallbackStrategy.ALLOW
-import com.coder.toolbox.util.CoderHostnameVerifier
 import com.coder.toolbox.util.InvalidVersionException
 import com.coder.toolbox.util.SemVer
-import com.coder.toolbox.util.coderSocketFactory
-import com.coder.toolbox.util.coderTrustManagers
 import com.coder.toolbox.util.escape
 import com.coder.toolbox.util.escapeSubcommand
 import com.coder.toolbox.util.safeHost
@@ -29,7 +29,6 @@ import com.squareup.moshi.JsonDataException
 import com.squareup.moshi.Moshi
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.withContext
-import okhttp3.OkHttpClient
 import org.zeroturnaround.exec.ProcessExecutor
 import retrofit2.Retrofit
 import java.io.EOFException
@@ -37,7 +36,6 @@ import java.io.FileNotFoundException
 import java.net.URL
 import java.nio.file.Files
 import java.nio.file.Path
-import javax.net.ssl.X509TrustManager
 
 /**
  * Version output from the CLI's version command.
@@ -148,13 +146,14 @@ class CoderCLIManager(
     val coderConfigPath: Path = context.settingsStore.dataDir(deploymentURL).resolve("config")
 
     private fun createDownloadService(): CoderDownloadService {
-        val okHttpClient = OkHttpClient.Builder()
-            .sslSocketFactory(
-                coderSocketFactory(context.settingsStore.tls),
-                coderTrustManagers(context.settingsStore.tls.caPath)[0] as X509TrustManager
-            )
-            .hostnameVerifier(CoderHostnameVerifier(context.settingsStore.tls.altHostname))
-            .build()
+        val interceptors = buildList {
+            add((Interceptors.userAgent(PluginManager.pluginInfo.version)))
+            add(Interceptors.logging(context))
+        }
+        val okHttpClient = CoderHttpClientBuilder.build(
+            context,
+            interceptors
+        )
 
         val retrofit = Retrofit.Builder()
             .baseUrl(deploymentURL.toString())
diff --git a/src/main/kotlin/com/coder/toolbox/sdk/CoderHttpClientBuilder.kt b/src/main/kotlin/com/coder/toolbox/sdk/CoderHttpClientBuilder.kt
new file mode 100644
index 0000000..f80d60c
--- /dev/null
+++ b/src/main/kotlin/com/coder/toolbox/sdk/CoderHttpClientBuilder.kt
@@ -0,0 +1,56 @@
+package com.coder.toolbox.sdk
+
+import com.coder.toolbox.CoderToolboxContext
+import com.coder.toolbox.util.CoderHostnameVerifier
+import com.coder.toolbox.util.coderSocketFactory
+import com.coder.toolbox.util.coderTrustManagers
+import com.jetbrains.toolbox.api.remoteDev.connection.ProxyAuth
+import okhttp3.Credentials
+import okhttp3.Interceptor
+import okhttp3.OkHttpClient
+import javax.net.ssl.X509TrustManager
+
+object CoderHttpClientBuilder {
+    fun build(
+        context: CoderToolboxContext,
+        interceptors: List<Interceptor>
+    ): OkHttpClient {
+        val settings = context.settingsStore.readOnly()
+
+        val socketFactory = coderSocketFactory(settings.tls)
+        val trustManagers = coderTrustManagers(settings.tls.caPath)
+        var builder = OkHttpClient.Builder()
+
+        if (context.proxySettings.getProxy() != null) {
+            context.logger.info("proxy: ${context.proxySettings.getProxy()}")
+            builder.proxy(context.proxySettings.getProxy())
+        } else if (context.proxySettings.getProxySelector() != null) {
+            context.logger.info("proxy selector: ${context.proxySettings.getProxySelector()}")
+            builder.proxySelector(context.proxySettings.getProxySelector()!!)
+        }
+
+        // Note: This handles only HTTP/HTTPS proxy authentication.
+        // SOCKS5 proxy authentication is currently not supported due to limitations described in:
+        // https://youtrack.jetbrains.com/issue/TBX-14532/Missing-proxy-authentication-settings#focus=Comments-27-12265861.0-0
+        builder.proxyAuthenticator { _, response ->
+            val proxyAuth = context.proxySettings.getProxyAuth()
+            if (proxyAuth == null || proxyAuth !is ProxyAuth.Basic) {
+                return@proxyAuthenticator null
+            }
+            val credentials = Credentials.basic(proxyAuth.username, proxyAuth.password)
+            response.request.newBuilder()
+                .header("Proxy-Authorization", credentials)
+                .build()
+        }
+
+        builder.sslSocketFactory(socketFactory, trustManagers[0] as X509TrustManager)
+            .hostnameVerifier(CoderHostnameVerifier(settings.tls.altHostname))
+            .retryOnConnectionFailure(true)
+
+        interceptors.forEach { interceptor ->
+            builder.addInterceptor(interceptor)
+
+        }
+        return builder.build()
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt b/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt
index 9b2e7b3..803472c 100644
--- a/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt
+++ b/src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt
@@ -7,7 +7,7 @@ import com.coder.toolbox.sdk.convertors.LoggingConverterFactory
 import com.coder.toolbox.sdk.convertors.OSConverter
 import com.coder.toolbox.sdk.convertors.UUIDConverter
 import com.coder.toolbox.sdk.ex.APIResponseException
-import com.coder.toolbox.sdk.interceptors.LoggingInterceptor
+import com.coder.toolbox.sdk.interceptors.Interceptors
 import com.coder.toolbox.sdk.v2.CoderV2RestFacade
 import com.coder.toolbox.sdk.v2.models.ApiErrorResponse
 import com.coder.toolbox.sdk.v2.models.BuildInfo
@@ -21,15 +21,7 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceBuildReason
 import com.coder.toolbox.sdk.v2.models.WorkspaceResource
 import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
 import com.coder.toolbox.sdk.v2.models.WorkspaceTransition
-import com.coder.toolbox.util.CoderHostnameVerifier
-import com.coder.toolbox.util.coderSocketFactory
-import com.coder.toolbox.util.coderTrustManagers
-import com.coder.toolbox.util.getArch
-import com.coder.toolbox.util.getHeaders
-import com.coder.toolbox.util.getOS
-import com.jetbrains.toolbox.api.remoteDev.connection.ProxyAuth
 import com.squareup.moshi.Moshi
-import okhttp3.Credentials
 import okhttp3.OkHttpClient
 import retrofit2.Response
 import retrofit2.Retrofit
@@ -37,7 +29,6 @@ import retrofit2.converter.moshi.MoshiConverterFactory
 import java.net.HttpURLConnection
 import java.net.URL
 import java.util.UUID
-import javax.net.ssl.X509TrustManager
 
 /**
  * An HTTP client that can make requests to the Coder API.
@@ -50,7 +41,6 @@ open class CoderRestClient(
     val token: String?,
     private val pluginVersion: String = "development",
 ) {
-    private val settings = context.settingsStore.readOnly()
     private lateinit var moshi: Moshi
     private lateinit var httpClient: OkHttpClient
     private lateinit var retroRestClient: CoderV2RestFacade
@@ -70,69 +60,22 @@ open class CoderRestClient(
                 .add(OSConverter())
                 .add(UUIDConverter())
                 .build()
-
-        val socketFactory = coderSocketFactory(settings.tls)
-        val trustManagers = coderTrustManagers(settings.tls.caPath)
-        var builder = OkHttpClient.Builder()
-
-        if (context.proxySettings.getProxy() != null) {
-            context.logger.info("proxy: ${context.proxySettings.getProxy()}")
-            builder.proxy(context.proxySettings.getProxy())
-        } else if (context.proxySettings.getProxySelector() != null) {
-            context.logger.info("proxy selector: ${context.proxySettings.getProxySelector()}")
-            builder.proxySelector(context.proxySettings.getProxySelector()!!)
-        }
-
-        // Note: This handles only HTTP/HTTPS proxy authentication.
-        // SOCKS5 proxy authentication is currently not supported due to limitations described in:
-        // https://youtrack.jetbrains.com/issue/TBX-14532/Missing-proxy-authentication-settings#focus=Comments-27-12265861.0-0
-        builder.proxyAuthenticator { _, response ->
-            val proxyAuth = context.proxySettings.getProxyAuth()
-            if (proxyAuth == null || proxyAuth !is ProxyAuth.Basic) {
-                return@proxyAuthenticator null
-            }
-            val credentials = Credentials.basic(proxyAuth.username, proxyAuth.password)
-            response.request.newBuilder()
-                .header("Proxy-Authorization", credentials)
-                .build()
-        }
-
-        if (context.settingsStore.requireTokenAuth) {
-            if (token.isNullOrBlank()) {
-                throw IllegalStateException("Token is required for $url deployment")
-            }
-            builder = builder.addInterceptor {
-                it.proceed(
-                    it.request().newBuilder().addHeader("Coder-Session-Token", token).build()
-                )
+        val interceptors = buildList {
+            if (context.settingsStore.requireTokenAuth) {
+                if (token.isNullOrBlank()) {
+                    throw IllegalStateException("Token is required for $url deployment")
+                }
+                add(Interceptors.tokenAuth(token))
             }
+            add((Interceptors.userAgent(pluginVersion)))
+            add(Interceptors.externalHeaders(context, url))
+            add(Interceptors.logging(context))
         }
 
-        httpClient =
-            builder
-                .sslSocketFactory(socketFactory, trustManagers[0] as X509TrustManager)
-                .hostnameVerifier(CoderHostnameVerifier(settings.tls.altHostname))
-                .retryOnConnectionFailure(true)
-                .addInterceptor {
-                    it.proceed(
-                        it.request().newBuilder().addHeader(
-                            "User-Agent",
-                            "Coder Toolbox/$pluginVersion (${getOS()}; ${getArch()})",
-                        ).build(),
-                    )
-                }
-                .addInterceptor {
-                    var request = it.request()
-                    val headers = getHeaders(url, settings.headerCommand)
-                    if (headers.isNotEmpty()) {
-                        val reqBuilder = request.newBuilder()
-                        headers.forEach { h -> reqBuilder.addHeader(h.key, h.value) }
-                        request = reqBuilder.build()
-                    }
-                    it.proceed(request)
-                }
-                .addInterceptor(LoggingInterceptor(context))
-                .build()
+        httpClient = CoderHttpClientBuilder.build(
+            context,
+            interceptors
+        )
 
         retroRestClient =
             Retrofit.Builder().baseUrl(url.toString()).client(httpClient)
diff --git a/src/main/kotlin/com/coder/toolbox/sdk/interceptors/Interceptors.kt b/src/main/kotlin/com/coder/toolbox/sdk/interceptors/Interceptors.kt
new file mode 100644
index 0000000..9c9f3ee
--- /dev/null
+++ b/src/main/kotlin/com/coder/toolbox/sdk/interceptors/Interceptors.kt
@@ -0,0 +1,64 @@
+package com.coder.toolbox.sdk.interceptors
+
+import com.coder.toolbox.CoderToolboxContext
+import com.coder.toolbox.util.getArch
+import com.coder.toolbox.util.getHeaders
+import com.coder.toolbox.util.getOS
+import okhttp3.Interceptor
+import java.net.URL
+
+/**
+ * Factory of okhttp interceptors
+ */
+object Interceptors {
+
+    /**
+     * Creates a token authentication interceptor
+     */
+    fun tokenAuth(token: String): Interceptor {
+        return Interceptor { chain ->
+            chain.proceed(
+                chain.request().newBuilder()
+                    .addHeader("Coder-Session-Token", token)
+                    .build()
+            )
+        }
+    }
+
+    /**
+     * Creates a User-Agent header interceptor
+     */
+    fun userAgent(pluginVersion: String): Interceptor {
+        return Interceptor { chain ->
+            chain.proceed(
+                chain.request().newBuilder()
+                    .addHeader("User-Agent", "Coder Toolbox/$pluginVersion (${getOS()}; ${getArch()})")
+                    .build()
+            )
+        }
+    }
+
+    /**
+     * Adds headers generated by executing a native command
+     */
+    fun externalHeaders(context: CoderToolboxContext, url: URL): Interceptor {
+        val settings = context.settingsStore.readOnly()
+        return Interceptor { chain ->
+            var request = chain.request()
+            val headers = getHeaders(url, settings.headerCommand)
+            if (headers.isNotEmpty()) {
+                val reqBuilder = request.newBuilder()
+                headers.forEach { h -> reqBuilder.addHeader(h.key, h.value) }
+                request = reqBuilder.build()
+            }
+            chain.proceed(request)
+        }
+    }
+
+    /**
+     * Creates a logging interceptor
+     */
+    fun logging(context: CoderToolboxContext): Interceptor {
+        return LoggingInterceptor(context)
+    }
+}
\ No newline at end of file
diff --git a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt
index b9deab7..7f5c831 100644
--- a/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt
+++ b/src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt
@@ -35,6 +35,7 @@ import com.jetbrains.toolbox.api.core.diagnostics.Logger
 import com.jetbrains.toolbox.api.core.os.LocalDesktopManager
 import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
 import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper
+import com.jetbrains.toolbox.api.remoteDev.connection.ProxyAuth
 import com.jetbrains.toolbox.api.remoteDev.connection.RemoteToolsHelper
 import com.jetbrains.toolbox.api.remoteDev.connection.ToolboxProxySettings
 import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
@@ -52,6 +53,8 @@ import org.zeroturnaround.exec.InvalidExitValueException
 import org.zeroturnaround.exec.ProcessInitException
 import java.net.HttpURLConnection
 import java.net.InetSocketAddress
+import java.net.Proxy
+import java.net.ProxySelector
 import java.net.URI
 import java.net.URL
 import java.nio.file.AccessDeniedException
@@ -87,8 +90,17 @@ internal class CoderCLIManagerTest {
             mockk<Logger>(relaxed = true)
         ),
         mockk<CoderSecretsStore>(),
-        mockk<ToolboxProxySettings>()
-    )
+        object : ToolboxProxySettings {
+            override fun getProxy(): Proxy? = null
+            override fun getProxySelector(): ProxySelector? = null
+            override fun getProxyAuth(): ProxyAuth? = null
+
+            override fun addProxyChangeListener(listener: Runnable) {
+            }
+
+            override fun removeProxyChangeListener(listener: Runnable) {
+            }
+        })
 
     @BeforeTest
     fun setup() {
@@ -547,11 +559,10 @@ internal class CoderCLIManagerTest {
                     context.logger,
                 )
 
-            val ccm =
-                CoderCLIManager(
-                    context.copy(settingsStore = settings),
-                    it.url ?: URI.create("https://test.coder.invalid").toURL()
-                )
+            val ccm = CoderCLIManager(
+                context.copy(settingsStore = settings),
+                it.url ?: URI.create("https://test.coder.invalid").toURL()
+            )
 
             val sshConfigPath = Path.of(settings.sshConfigPath)
             // Input is the configuration that we start with, if any.
diff --git a/src/test/resources/extension.json b/src/test/resources/extension.json
new file mode 100644
index 0000000..3f897e2
--- /dev/null
+++ b/src/test/resources/extension.json
@@ -0,0 +1,4 @@
+{
+  "id": "com.coder.toolbox",
+  "version": "development"
+}
\ No newline at end of file
