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