@@ -46,13 +46,7 @@ import com.intellij.ui.RelativeFont
46
46
import com.intellij.ui.ToolbarDecorator
47
47
import com.intellij.ui.components.JBTextField
48
48
import com.intellij.ui.components.dialog
49
- import com.intellij.ui.dsl.builder.AlignX
50
- import com.intellij.ui.dsl.builder.AlignY
51
- import com.intellij.ui.dsl.builder.BottomGap
52
- import com.intellij.ui.dsl.builder.RightGap
53
- import com.intellij.ui.dsl.builder.TopGap
54
- import com.intellij.ui.dsl.builder.bindText
55
- import com.intellij.ui.dsl.builder.panel
49
+ import com.intellij.ui.dsl.builder.*
56
50
import com.intellij.ui.table.TableView
57
51
import com.intellij.util.ui.ColumnInfo
58
52
import com.intellij.util.ui.JBFont
@@ -76,8 +70,12 @@ import java.awt.event.MouseListener
76
70
import java.awt.event.MouseMotionListener
77
71
import java.awt.font.TextAttribute
78
72
import java.awt.font.TextAttribute.UNDERLINE_ON
73
+ import java.nio.file.Files
74
+ import java.nio.file.Path
75
+ import java.nio.file.Paths
79
76
import java.net.SocketTimeoutException
80
77
import javax.swing.Icon
78
+ import javax.swing.JCheckBox
81
79
import javax.swing.JTable
82
80
import javax.swing.JTextField
83
81
import javax.swing.ListSelectionModel
@@ -100,6 +98,7 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
100
98
private val appPropertiesService: PropertiesComponent = service()
101
99
102
100
private var tfUrl: JTextField ? = null
101
+ private var cbExistingToken: JCheckBox ? = null
103
102
private var listTableModelOfWorkspaces = ListTableModel <WorkspaceAgentModel >(
104
103
WorkspaceIconColumnInfo (" " ),
105
104
WorkspaceNameColumnInfo (" Name" ),
@@ -201,13 +200,13 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
201
200
font = JBFont .h3().asBold()
202
201
icon = CoderIcons .LOGO_16
203
202
}
204
- }.topGap(TopGap .SMALL ).bottomGap( BottomGap . MEDIUM )
203
+ }.topGap(TopGap .SMALL )
205
204
row {
206
205
cell(ComponentPanelBuilder .createCommentComponent(CoderGatewayBundle .message(" gateway.connector.view.coder.workspaces.comment" ), false , - 1 , true ))
207
206
}
208
207
row {
209
208
browserLink(CoderGatewayBundle .message(" gateway.connector.view.login.documentation.action" ), " https://coder.com/docs/coder-oss/latest/workspaces" )
210
- }.bottomGap( BottomGap . MEDIUM )
209
+ }
211
210
row(CoderGatewayBundle .message(" gateway.connector.view.login.url.label" )) {
212
211
tfUrl = textField().resizableColumn().align(AlignX .FILL ).gap(RightGap .SMALL ).bindText(localWizardModel::coderURL).applyToComponent {
213
212
addActionListener {
@@ -223,6 +222,17 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
223
222
}
224
223
cell()
225
224
}
225
+ row {
226
+ cbExistingToken = checkBox(CoderGatewayBundle .message(" gateway.connector.view.login.existing-token.label" ))
227
+ .bindSelected(localWizardModel::useExistingToken)
228
+ .component
229
+ }
230
+ row {
231
+ cell(ComponentPanelBuilder .createCommentComponent(
232
+ CoderGatewayBundle .message(" gateway.connector.view.login.existing-token.tooltip" ,
233
+ CoderGatewayBundle .message(" gateway.connector.view.login.existing-token.label" )),
234
+ false , - 1 , true ))
235
+ }
226
236
row {
227
237
scrollCell(toolbar.createPanel().apply {
228
238
add(notificationBanner.component.apply { isVisible = false }, " South" )
@@ -313,18 +323,70 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
313
323
if (localWizardModel.coderURL.isNotBlank() && localWizardModel.token.isNotBlank()) {
314
324
triggerWorkspacePolling()
315
325
} else {
316
- val url = appPropertiesService.getValue(CODER_URL_KEY )
317
- val token = appPropertiesService.getValue(SESSION_TOKEN )
318
- if (! url.isNullOrBlank() && ! token.isNullOrBlank()) {
326
+ val (url, token) = readStorageOrConfig()
327
+ if (! url.isNullOrBlank()) {
319
328
localWizardModel.coderURL = url
320
- localWizardModel.token = token
321
329
tfUrl?.text = url
330
+ }
331
+ if (! token.isNullOrBlank()) {
332
+ localWizardModel.token = token
333
+ }
334
+ if (! url.isNullOrBlank() && ! token.isNullOrBlank()) {
322
335
loginAndLoadWorkspace(token, true )
323
336
}
324
337
}
325
338
updateWorkspaceActions()
326
339
}
327
340
341
+ /* *
342
+ * Return the URL and token from storage or the CLI config.
343
+ */
344
+ private fun readStorageOrConfig (): Pair <String ?, String ?> {
345
+ val url = appPropertiesService.getValue(CODER_URL_KEY )
346
+ val token = appPropertiesService.getValue(SESSION_TOKEN )
347
+ if (! url.isNullOrBlank() && ! token.isNullOrBlank()) {
348
+ return url to token
349
+ }
350
+ return readConfig()
351
+ }
352
+
353
+ /* *
354
+ * Return the URL and token from the CLI config.
355
+ */
356
+ private fun readConfig (): Pair <String ?, String ?> {
357
+ val configDir = getConfigDir()
358
+ try {
359
+ val url = Files .readString(configDir.resolve(" url" ))
360
+ val token = Files .readString(configDir.resolve(" session" ))
361
+ return url to token
362
+ } catch (e: Exception ) {
363
+ return null to null // Probably has not configured the CLI yet.
364
+ }
365
+ }
366
+
367
+ /* *
368
+ * Return the config directory used by the CLI.
369
+ */
370
+ private fun getConfigDir (): Path {
371
+ var dir = System .getenv(" CODER_CONFIG_DIR" )
372
+ if (! dir.isNullOrBlank()) {
373
+ return Path .of(dir)
374
+ }
375
+ // The Coder CLI uses https://github.com/kirsle/configdir so this should
376
+ // match how it behaves.
377
+ return when (getOS()) {
378
+ OS .WINDOWS -> Paths .get(System .getenv(" APPDATA" ), " coderv2" )
379
+ OS .MAC -> Paths .get(System .getenv(" HOME" ), " Library/Application Support/coderv2" )
380
+ else -> {
381
+ dir = System .getenv(" XDG_CACHE_HOME" )
382
+ if (! dir.isNullOrBlank()) {
383
+ return Paths .get(dir, " coderv2" )
384
+ }
385
+ return Paths .get(System .getenv(" HOME" ), " .config/coderv2" )
386
+ }
387
+ }
388
+ }
389
+
328
390
private fun updateWorkspaceActions () {
329
391
goToDashboardAction.isEnabled = coderClient.isReady
330
392
createWorkspaceAction.isEnabled = coderClient.isReady
@@ -440,8 +502,13 @@ class CoderWorkspacesStepView(val enableNextButtonCallback: (Boolean) -> Unit) :
440
502
441
503
private fun askToken (openBrowser : Boolean ): String? {
442
504
val getTokenUrl = localWizardModel.coderURL.toURL().withPath(" /login?redirect=%2Fcli-auth" )
443
- if (openBrowser) {
505
+ if (openBrowser && ! localWizardModel.useExistingToken ) {
444
506
BrowserUtil .browse(getTokenUrl)
507
+ } else if (localWizardModel.useExistingToken) {
508
+ val (url, token) = readConfig()
509
+ if (url == localWizardModel.coderURL && ! token.isNullOrBlank()) {
510
+ localWizardModel.token = token
511
+ }
445
512
}
446
513
var tokenFromUser: String? = null
447
514
ApplicationManager .getApplication().invokeAndWait({
0 commit comments