Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function needToken(): boolean {
/**
* Create a new agent based off the current settings.
*/
async function createHttpAgent(): Promise<ProxyAgent> {
export async function createHttpAgent(): Promise<ProxyAgent> {
const cfg = vscode.workspace.getConfiguration()
const insecure = Boolean(cfg.get("coder.insecure"))
const certFile = expandPath(String(cfg.get("coder.tlsCertFile") ?? "").trim())
Expand Down
92 changes: 92 additions & 0 deletions src/inbox.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Api } from "coder/site/src/api/api"
import * as vscode from "vscode"
import { WebSocket } from "ws"
import { errToStr } from "./api-helper"
import { Storage } from "./storage"
import { ProxyAgent } from "proxy-agent"

Check failure on line 6 in src/inbox.ts

View workflow job for this annotation

GitHub Actions / lint

`proxy-agent` import should occur before import of `vscode`

type InboxMessage = {
unread_count: number
notification: {
id: string
user_id: string
template_id: string
targets: string[]
title: string
content: string
actions: {
[key: string]: string
}
read_at: string
created_at: string
}
}

const TemplateWorkspaceOutOfMemory = "a9d027b4-ac49-4fb1-9f6d-45af15f64e7a"
const TemplateWorkspaceOutOfDisk = "f047f6a3-5713-40f7-85aa-0394cce9fa3a"

export class Inbox implements vscode.Disposable {
private disposed = false
private socket: WebSocket

constructor(
httpAgent: ProxyAgent,
restClient: Api,
private readonly storage: Storage,
) {
const baseUrlRaw = restClient.getAxiosInstance().defaults.baseURL
if (!baseUrlRaw) {
throw new Error("No base URL set on REST client")
}

const baseUrl = new URL(baseUrlRaw)
const socketProto = baseUrl.protocol === "https:" ? "wss:" : "ws:"
const socketUrlRaw = `${socketProto}//${baseUrl.host}/api/v2/notifications/watch`

this.socket = new WebSocket(new URL(socketUrlRaw), {
followRedirects: true,
agent: httpAgent,
headers: {
"Coder-Session-Token": restClient.getAxiosInstance().defaults.headers.common["Coder-Session-Token"] as
| string
| undefined,
},
})

this.socket.on("open", () => {
this.storage.writeToCoderOutputChannel("Listening to Coder Inbox")
})

this.socket.on("error", (error) => {
this.notifyError(error)
})

this.socket.on("message", (data) => {
try {
const inboxMessage = JSON.parse(data.toString()) as InboxMessage

if (
inboxMessage.notification.template_id === TemplateWorkspaceOutOfDisk ||
inboxMessage.notification.template_id === TemplateWorkspaceOutOfMemory
) {
vscode.window.showWarningMessage(inboxMessage.notification.title)
}
} catch (error) {
this.notifyError(error)
}
})
}

dispose() {
if (!this.disposed) {
this.storage.writeToCoderOutputChannel("No longer listening to Coder Inbox")
this.socket.close()
this.disposed = true
}
}

private notifyError(error: unknown) {
const message = errToStr(error, "Got empty error while monitoring Coder Inbox")
this.storage.writeToCoderOutputChannel(message)
}
}
8 changes: 7 additions & 1 deletion src/remote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@
import prettyBytes from "pretty-bytes"
import * as semver from "semver"
import * as vscode from "vscode"
import { makeCoderSdk, needToken, startWorkspaceIfStoppedOrFailed, waitForBuild } from "./api"
import { createHttpAgent, makeCoderSdk, needToken, startWorkspaceIfStoppedOrFailed, waitForBuild } from "./api"
import { extractAgents } from "./api-helper"
import * as cli from "./cliManager"
import { Commands } from "./commands"
import { featureSetForVersion, FeatureSet } from "./featureSet"
import { getHeaderCommand } from "./headers"
import { Inbox } from "./inbox"
import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
import { Storage } from "./storage"
Expand Down Expand Up @@ -403,6 +404,11 @@
disposables.push(monitor)
disposables.push(monitor.onChange.event((w) => (this.commands.workspace = w)))

// Watch coder inbox for messages
const httpAgent = await createHttpAgent();

Check failure on line 408 in src/remote.ts

View workflow job for this annotation

GitHub Actions / lint

Delete `;`
const inbox = new Inbox(httpAgent, workspaceRestClient, this.storage)
disposables.push(inbox)

// Wait for the agent to connect.
if (agent.status === "connecting") {
this.storage.writeToCoderOutputChannel(`Waiting for ${workspaceName}/${agent.name}...`)
Expand Down
Loading