194 lines · 7.8 KB
import * as vscode from "vscode";
import { EDITOR_WS_PROTOCOL } from "../lib/editor-protocol";
import type { ForgeApi } from "./forge";
import type { RoomClient } from "./room-client";

type ContainerStatus = {
  state: "stopped" | "starting" | "running";
  allowed: boolean;
  startedAt: number | null;
  idleMinutes: number;
  capHours: number;
  minutesUsed: number;
  quotaMinutes: number;
};

const NORMAL_CLOSE = 1000;
const MAX_PORT = 65535;
const encoder = new TextEncoder();
const decoder = new TextDecoder();

/** A Pseudoterminal backed by a process in the session's container, streamed over a WebSocket. */
function containerPty(api: ForgeApi, command: string | null): vscode.Pseudoterminal {
  const write = new vscode.EventEmitter<string>();
  const close = new vscode.EventEmitter<number>();
  let socket: WebSocket | null = null;
  return {
    onDidWrite: write.event,
    onDidClose: close.event,
    open(dimensions) {
      const url = new URL(api.roomUrl().replace(/\/room$/, "/container/terminal"));
      if (command) url.searchParams.set("command", command);
      if (dimensions) {
        url.searchParams.set("cols", String(dimensions.columns));
        url.searchParams.set("rows", String(dimensions.rows));
      }
      socket = new WebSocket(url, [EDITOR_WS_PROTOCOL, api.token]);
      socket.binaryType = "arraybuffer";
      socket.onmessage = (event) => {
        if (typeof event.data === "string") {
          const message = JSON.parse(event.data) as { type: string; code?: number; message?: string };