Skip to content

impl: support for Toolbox 2.6.3 #124

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Added

- support for Toolbox 2.6.3 with improved URI handling

## 0.2.3 - 2025-05-26

### Changed
Expand Down
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ If `ide_product_code` and `ide_build_number` is missing, Toolbox will only open
page. Coder Toolbox will attempt to start the workspace if it’s not already running; however, for the most reliable
experience, it’s recommended to ensure the workspace is running prior to initiating the connection.

> ⚠️ Note: `folder` should point to a remote IDEA project that has already been opened and appears in the `Projects` tab.
> If the path refers to a project that doesn't exist, the remote IDE won’t start or load it.

> Until [TBX-14952](https://youtrack.jetbrains.com/issue/TBX-14952/) is fixed, it's best to either use a path to a previously opened project or leave it empty.

## Configuring and Testing workspace polling with HTTP & SOCKS5 Proxy

This section explains how to set up a local proxy (without authentication which is not yet supported) and verify that
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version=0.2.3
version=0.3.0
group=com.coder.toolbox
name=coder-toolbox
6 changes: 3 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[versions]
toolbox-plugin-api = "1.0.38881"
kotlin = "2.1.0"
toolbox-plugin-api = "1.1.41749"
kotlin = "2.1.10"
coroutines = "1.10.1"
serialization = "1.8.0"
okhttp = "4.12.0"
Expand All @@ -9,7 +9,7 @@ marketplace-client = "2.0.46"
gradle-wrapper = "0.14.0"
exec = "1.12"
moshi = "1.15.2"
ksp = "2.1.0-1.0.29"
ksp = "2.1.10-1.0.31"
retrofit = "2.11.0"
changelog = "2.2.1"
gettext = "0.7.0"
Expand Down
5 changes: 3 additions & 2 deletions src/main/kotlin/com/coder/toolbox/CoderRemoteEnvironment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import com.squareup.moshi.Moshi
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
Expand Down Expand Up @@ -203,7 +204,7 @@ class CoderRemoteEnvironment(

private fun File.doesNotExists(): Boolean = !this.exists()

override fun afterDisconnect() {
override fun afterDisconnect(isManual: Boolean) {
context.logger.info("Stopping the network metrics poll job for $id")
pollJob?.cancel()
this.connectionRequest.update { false }
Expand Down Expand Up @@ -269,7 +270,7 @@ class CoderRemoteEnvironment(
}
}

override fun onDelete() {
override val deleteActionFlow: StateFlow<(() -> Unit)?> = MutableStateFlow {
context.cs.launch {
try {
client.removeWorkspace(workspace)
Expand Down
46 changes: 45 additions & 1 deletion src/main/kotlin/com/coder/toolbox/CoderToolboxContext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ 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.RemoteToolsHelper
import com.jetbrains.toolbox.api.remoteDev.connection.ToolboxProxySettings
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
import com.jetbrains.toolbox.api.ui.ToolboxUi
import kotlinx.coroutines.CoroutineScope
import java.net.URL
import java.util.UUID

@Suppress("UnstableApiUsage")
data class CoderToolboxContext(
val ui: ToolboxUi,
val envPageManager: EnvironmentUiPageManager,
val envStateColorPalette: EnvironmentStateColorPalette,
val ideOrchestrator: ClientHelper,
val remoteIdeOrchestrator: RemoteToolsHelper,
val jbClientOrchestrator: ClientHelper,
val desktop: LocalDesktopManager,
val cs: CoroutineScope,
val logger: Logger,
Expand All @@ -44,4 +48,44 @@ data class CoderToolboxContext(
}
return this.settingsStore.defaultURL.toURL()
}

suspend fun logAndShowError(title: String, error: String) {
logger.error(error)
ui.showSnackbar(
UUID.randomUUID().toString(),
i18n.pnotr(title),
i18n.pnotr(error),
i18n.ptrl("OK")
)
}

suspend fun logAndShowError(title: String, error: String, exception: Exception) {
logger.error(exception, error)
ui.showSnackbar(
UUID.randomUUID().toString(),
i18n.pnotr(title),
i18n.pnotr(error),
i18n.ptrl("OK")
)
}

suspend fun logAndShowWarning(title: String, warning: String) {
logger.warn(warning)
ui.showSnackbar(
UUID.randomUUID().toString(),
i18n.pnotr(title),
i18n.pnotr(warning),
i18n.ptrl("OK")
)
}

suspend fun logAndShowInfo(title: String, info: String) {
logger.info(info)
ui.showSnackbar(
UUID.randomUUID().toString(),
i18n.pnotr(title),
i18n.pnotr(info),
i18n.ptrl("OK")
)
}
}
2 changes: 2 additions & 0 deletions src/main/kotlin/com/coder/toolbox/CoderToolboxExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.jetbrains.toolbox.api.localization.LocalizableStringFactory
import com.jetbrains.toolbox.api.remoteDev.RemoteDevExtension
import com.jetbrains.toolbox.api.remoteDev.RemoteProvider
import com.jetbrains.toolbox.api.remoteDev.connection.ClientHelper
import com.jetbrains.toolbox.api.remoteDev.connection.RemoteToolsHelper
import com.jetbrains.toolbox.api.remoteDev.connection.ToolboxProxySettings
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateColorPalette
import com.jetbrains.toolbox.api.remoteDev.ui.EnvironmentUiPageManager
Expand All @@ -31,6 +32,7 @@ class CoderToolboxExtension : RemoteDevExtension {
serviceLocator.getService<ToolboxUi>(),
serviceLocator.getService<EnvironmentUiPageManager>(),
serviceLocator.getService<EnvironmentStateColorPalette>(),
serviceLocator.getService<RemoteToolsHelper>(),
serviceLocator.getService<ClientHelper>(),
serviceLocator.getService<LocalDesktopManager>(),
serviceLocator.getService<CoroutineScope>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.coder.toolbox.sdk.v2.models.WorkspaceAgentLifecycleState
import com.coder.toolbox.sdk.v2.models.WorkspaceAgentStatus
import com.coder.toolbox.sdk.v2.models.WorkspaceStatus
import com.jetbrains.toolbox.api.core.ui.color.StateColor
import com.jetbrains.toolbox.api.remoteDev.states.CustomRemoteEnvironmentState
import com.jetbrains.toolbox.api.remoteDev.states.CustomRemoteEnvironmentStateV2
import com.jetbrains.toolbox.api.remoteDev.states.EnvironmentStateIcons
import com.jetbrains.toolbox.api.remoteDev.states.StandardRemoteEnvironmentState

Expand Down Expand Up @@ -61,9 +61,9 @@ enum class WorkspaceAndAgentStatus(val label: String, val description: String) {
* Note that a reachable environment will always display "connected" or
* "disconnected" regardless of the label we give that status.
*/
fun toRemoteEnvironmentState(context: CoderToolboxContext): CustomRemoteEnvironmentState {
return CustomRemoteEnvironmentState(
label,
fun toRemoteEnvironmentState(context: CoderToolboxContext): CustomRemoteEnvironmentStateV2 {
return CustomRemoteEnvironmentStateV2(
context.i18n.pnotr(label),
color = getStateColor(context),
reachable = ready() || unhealthy(),
// TODO@JB: How does this work? Would like a spinner for pending states.
Expand All @@ -90,10 +90,10 @@ enum class WorkspaceAndAgentStatus(val label: String, val description: String) {
else EnvironmentStateIcons.NoIcon
}

fun toSshConnectingEnvState(context: CoderToolboxContext): CustomRemoteEnvironmentState {
fun toSshConnectingEnvState(context: CoderToolboxContext): CustomRemoteEnvironmentStateV2 {
val existingState = toRemoteEnvironmentState(context)
return CustomRemoteEnvironmentState(
"SSHing",
return CustomRemoteEnvironmentStateV2(
context.i18n.pnotr("SSHing"),
existingState.color,
existingState.isReachable,
EnvironmentStateIcons.Connecting
Expand Down
6 changes: 3 additions & 3 deletions src/main/kotlin/com/coder/toolbox/sdk/CoderRestClient.kt
Original file line number Diff line number Diff line change
Expand Up @@ -192,12 +192,12 @@ open class CoderRestClient(
}

/**
* Maps the list of workspaces to the associated agents.
* Maps the available workspaces to the associated agents.
*/
suspend fun groupByAgents(workspaces: List<Workspace>): Set<Pair<Workspace, WorkspaceAgent>> {
suspend fun workspacesByAgents(): Set<Pair<Workspace, WorkspaceAgent>> {
// It is possible for there to be resources with duplicate names so we
// need to use a set.
return workspaces.flatMap { ws ->
return workspaces().flatMap { ws ->
when (ws.latestBuild.status) {
WorkspaceStatus.RUNNING -> ws.latestBuild.resources
else -> resources(ws)
Expand Down
Loading
Loading