Skip to content

Commit a024e2d

Browse files
committed
Merge branch 'main' into lilac/specify-header-command
2 parents 2f9f989 + 3cf861a commit a024e2d

File tree

11 files changed

+970
-202
lines changed

11 files changed

+970
-202
lines changed

CHANGELOG.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,35 @@
22

33
## Unreleased
44

5+
## [v1.9.1](https://github.com/coder/vscode-coder/releases/tag/v1.9.1) 2025-05-27
6+
7+
### Fixed
8+
9+
- Missing or otherwise malformed `START CODER VSCODE` / `END CODER VSCODE`
10+
blocks in `${HOME}/.ssh/config` will now result in an error when attempting to
11+
update the file. These will need to be manually fixed before proceeding.
12+
- Multiple open instances of the extension could potentially clobber writes to
13+
`~/.ssh/config`. Updates to this file are now atomic.
14+
- Add support for `anysphere.remote-ssh` Remote SSH extension.
15+
16+
## [v1.9.0](https://github.com/coder/vscode-coder/releases/tag/v1.9.0) 2025-05-15
17+
18+
### Fixed
19+
20+
- The connection indicator will now show for VS Code on Windows, Windsurf, and
21+
when using the `jeanp413.open-remote-ssh` extension.
22+
23+
### Changed
24+
25+
- The connection indicator now shows if connecting through Coder Desktop.
26+
27+
## [v1.8.0](https://github.com/coder/vscode-coder/releases/tag/v1.8.0) (2025-04-22)
28+
29+
### Added
30+
31+
- Coder extension sidebar now displays available app statuses, and let's
32+
the user click them to drop into a session with a running AI Agent.
33+
534
## [v1.7.1](https://github.com/coder/vscode-coder/releases/tag/v1.7.1) (2025-04-14)
635

736
### Fixed

package.json

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"displayName": "Coder",
55
"description": "Open any workspace with a single click.",
66
"repository": "https://github.com/coder/vscode-coder",
7-
"version": "1.7.1",
7+
"version": "1.9.1",
88
"engines": {
99
"vscode": "^1.73.0"
1010
},
@@ -204,6 +204,12 @@
204204
"title": "Coder: View Logs",
205205
"icon": "$(list-unordered)",
206206
"when": "coder.authenticated"
207+
},
208+
{
209+
"command": "coder.openAppStatus",
210+
"title": "Coder: Open App Status",
211+
"icon": "$(robot)",
212+
"when": "coder.authenticated"
207213
}
208214
],
209215
"menus": {
@@ -277,14 +283,14 @@
277283
"devDependencies": {
278284
"@types/eventsource": "^3.0.0",
279285
"@types/glob": "^7.1.3",
280-
"@types/node": "^22.14.0",
286+
"@types/node": "^22.14.1",
281287
"@types/node-forge": "^1.3.11",
282288
"@types/ua-parser-js": "^0.7.39",
283289
"@types/vscode": "^1.73.0",
284290
"@types/ws": "^8.18.1",
285291
"@typescript-eslint/eslint-plugin": "^7.0.0",
286292
"@typescript-eslint/parser": "^6.21.0",
287-
"@vscode/test-electron": "^2.4.1",
293+
"@vscode/test-electron": "^2.5.2",
288294
"@vscode/vsce": "^2.21.1",
289295
"bufferutil": "^4.0.9",
290296
"coder": "https://github.com/coder/coder#main",
@@ -293,36 +299,36 @@
293299
"eslint-config-prettier": "^9.1.0",
294300
"eslint-plugin-import": "^2.31.0",
295301
"eslint-plugin-md": "^1.0.19",
296-
"eslint-plugin-prettier": "^5.2.1",
302+
"eslint-plugin-prettier": "^5.4.0",
297303
"glob": "^10.4.2",
298304
"nyc": "^17.1.0",
299-
"prettier": "^3.3.3",
305+
"prettier": "^3.5.3",
300306
"ts-loader": "^9.5.1",
301-
"tsc-watch": "^6.2.0",
307+
"tsc-watch": "^6.2.1",
302308
"typescript": "^5.4.5",
303309
"utf-8-validate": "^6.0.5",
304310
"vitest": "^0.34.6",
305311
"vscode-test": "^1.5.0",
306-
"webpack": "^5.98.0",
312+
"webpack": "^5.99.6",
307313
"webpack-cli": "^5.1.4"
308314
},
309315
"dependencies": {
310316
"axios": "1.8.4",
311317
"date-fns": "^3.6.0",
312318
"eventsource": "^3.0.6",
313-
"find-process": "^1.4.7",
319+
"find-process": "https://github.com/coder/find-process#fix/sequoia-compat",
314320
"jsonc-parser": "^3.3.1",
315-
"memfs": "^4.9.3",
321+
"memfs": "^4.17.1",
316322
"node-forge": "^1.3.1",
317323
"pretty-bytes": "^6.1.1",
318324
"proxy-agent": "^6.4.0",
319-
"semver": "^7.6.2",
320-
"ua-parser-js": "^1.0.38",
321-
"ws": "^8.18.1",
322-
"zod": "^3.23.8"
325+
"semver": "^7.7.1",
326+
"ua-parser-js": "^2.0.3",
327+
"ws": "^8.18.2",
328+
"zod": "^3.25.1"
323329
},
324330
"resolutions": {
325-
"semver": "7.6.2",
331+
"semver": "7.7.1",
326332
"trim": "0.0.3",
327333
"word-wrap": "1.2.5"
328334
},

src/commands.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Api } from "coder/site/src/api/api"
22
import { getErrorMessage } from "coder/site/src/api/errors"
33
import { User, Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated"
4+
import path from "node:path"
45
import * as vscode from "vscode"
56
import { makeCoderSdk, needToken } from "./api"
67
import { extractAgents } from "./api-helper"
@@ -407,6 +408,63 @@ export class Commands {
407408
}
408409
}
409410

411+
public async openAppStatus(app: {
412+
name?: string
413+
url?: string
414+
agent_name?: string
415+
command?: string
416+
workspace_name: string
417+
}): Promise<void> {
418+
// Launch and run command in terminal if command is provided
419+
if (app.command) {
420+
return vscode.window.withProgress(
421+
{
422+
location: vscode.ProgressLocation.Notification,
423+
title: `Connecting to AI Agent...`,
424+
cancellable: false,
425+
},
426+
async () => {
427+
const terminal = vscode.window.createTerminal(app.name)
428+
429+
// If workspace_name is provided, run coder ssh before the command
430+
431+
const url = this.storage.getUrl()
432+
if (!url) {
433+
throw new Error("No coder url found for sidebar")
434+
}
435+
const binary = await this.storage.fetchBinary(this.restClient, toSafeHost(url))
436+
const escape = (str: string): string => `"${str.replace(/"/g, '\\"')}"`
437+
terminal.sendText(
438+
`${escape(binary)} ssh --global-config ${escape(
439+
path.dirname(this.storage.getSessionTokenPath(toSafeHost(url))),
440+
)} ${app.workspace_name}`,
441+
)
442+
await new Promise((resolve) => setTimeout(resolve, 5000))
443+
terminal.sendText(app.command ?? "")
444+
terminal.show(false)
445+
},
446+
)
447+
}
448+
// Check if app has a URL to open
449+
if (app.url) {
450+
return vscode.window.withProgress(
451+
{
452+
location: vscode.ProgressLocation.Notification,
453+
title: `Opening ${app.name || "application"} in browser...`,
454+
cancellable: false,
455+
},
456+
async () => {
457+
await vscode.env.openExternal(vscode.Uri.parse(app.url!))
458+
},
459+
)
460+
}
461+
462+
// If no URL or command, show information about the app status
463+
vscode.window.showInformationMessage(`${app.name}`, {
464+
detail: `Agent: ${app.agent_name || "Unknown"}`,
465+
})
466+
}
467+
410468
/**
411469
* Open a workspace belonging to the currently logged-in deployment.
412470
*

src/extension.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
2424
const remoteSSHExtension =
2525
vscode.extensions.getExtension("jeanp413.open-remote-ssh") ||
2626
vscode.extensions.getExtension("codeium.windsurf-remote-openssh") ||
27+
vscode.extensions.getExtension("anysphere.remote-ssh") ||
2728
vscode.extensions.getExtension("ms-vscode-remote.remote-ssh")
2829
if (!remoteSSHExtension) {
2930
vscode.window.showErrorMessage("Remote SSH extension not found, cannot activate Coder extension")
@@ -181,6 +182,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
181182
vscode.commands.registerCommand("coder.open", commands.open.bind(commands))
182183
vscode.commands.registerCommand("coder.openDevContainer", commands.openDevContainer.bind(commands))
183184
vscode.commands.registerCommand("coder.openFromSidebar", commands.openFromSidebar.bind(commands))
185+
vscode.commands.registerCommand("coder.openAppStatus", commands.openAppStatus.bind(commands))
184186
vscode.commands.registerCommand("coder.workspace.update", commands.updateWorkspace.bind(commands))
185187
vscode.commands.registerCommand("coder.createWorkspace", commands.createWorkspace.bind(commands))
186188
vscode.commands.registerCommand("coder.navigateToWorkspace", commands.navigateToWorkspace.bind(commands))

src/remote.ts

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { Inbox } from "./inbox"
1919
import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
2020
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
2121
import { Storage } from "./storage"
22-
import { AuthorityPrefix, escapeCommandArg, expandPath, parseRemoteAuthority } from "./util"
22+
import { AuthorityPrefix, escapeCommandArg, expandPath, findPort, parseRemoteAuthority } from "./util"
2323
import { WorkspaceMonitor } from "./workspaceMonitor"
2424

2525
export interface RemoteDetails extends vscode.Disposable {
@@ -684,8 +684,18 @@ export class Remote {
684684
derp_latency: { [key: string]: number }
685685
upload_bytes_sec: number
686686
download_bytes_sec: number
687+
using_coder_connect: boolean
687688
}) => {
688689
let statusText = "$(globe) "
690+
691+
// Coder Connect doesn't populate any other stats
692+
if (network.using_coder_connect) {
693+
networkStatus.text = statusText + "Coder Connect "
694+
networkStatus.tooltip = "You're connected using Coder Connect."
695+
networkStatus.show()
696+
return
697+
}
698+
689699
if (network.p2p) {
690700
statusText += "Direct "
691701
networkStatus.tooltip = "You're connected peer-to-peer ✨."
@@ -769,14 +779,7 @@ export class Remote {
769779
// this to find the SSH process that is powering this connection. That SSH
770780
// process will be logging network information periodically to a file.
771781
const text = await fs.readFile(logPath, "utf8")
772-
const matches = text.match(/-> socksPort (\d+) ->/)
773-
if (!matches) {
774-
return
775-
}
776-
if (matches.length < 2) {
777-
return
778-
}
779-
const port = Number.parseInt(matches[1])
782+
const port = await findPort(text)
780783
if (!port) {
781784
return
782785
}

0 commit comments

Comments
 (0)