@wrongstack/webui 0.272.0 → 0.272.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1105,6 +1105,9 @@ function isTrustedLoopbackOrigin(origin) {
1105
1105
  function isLoopbackBind(wsHost) {
1106
1106
  return wsHost === "127.0.0.1" || wsHost === "::1" || wsHost === "localhost";
1107
1107
  }
1108
+ function isWildcardBind(wsHost) {
1109
+ return wsHost === "0.0.0.0" || wsHost === "::" || wsHost === "[::]";
1110
+ }
1108
1111
  function tokenMatches(provided, expected) {
1109
1112
  if (!provided) return false;
1110
1113
  const a = Buffer.from(provided);
@@ -1153,13 +1156,13 @@ function verifyClient(input) {
1153
1156
  if (!origin) {
1154
1157
  const remoteIp = remoteAddress ?? "";
1155
1158
  const isRemoteLoopback = remoteIp === "127.0.0.1" || remoteIp === "::1";
1156
- if (!isRemoteLoopback && wsHost === "0.0.0.0") return false;
1159
+ if (!isRemoteLoopback && isWildcardBind(wsHost)) return false;
1157
1160
  return urlTokenOk || cookieTokenOk || isLoopbackBind(wsHost);
1158
1161
  }
1159
1162
  try {
1160
1163
  const { hostname } = new URL(origin);
1161
1164
  if (isLoopbackHostname(hostname)) {
1162
- if (wsHost === "0.0.0.0" && !isTrustedLoopbackOrigin(origin)) {
1165
+ if (isWildcardBind(wsHost) && !isTrustedLoopbackOrigin(origin)) {
1163
1166
  return false;
1164
1167
  }
1165
1168
  return true;
@@ -2821,7 +2824,7 @@ import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as
2821
2824
  import { buildProviderFactoriesFromRegistry, makeProviderFromConfig } from "@wrongstack/providers";
2822
2825
  import { builtinToolsPack, forgetTool, rememberTool, searchMemoryTool, relatedMemoryTool } from "@wrongstack/tools";
2823
2826
  import { MCPRegistry } from "@wrongstack/mcp";
2824
- import { WebSocketServer } from "ws";
2827
+ import { WebSocket as WebSocket2, WebSocketServer } from "ws";
2825
2828
 
2826
2829
  // ../runtime/src/container.ts
2827
2830
  import {
@@ -4025,18 +4028,22 @@ async function ensureProjectDataDir(slug, globalConfigPath) {
4025
4028
  }
4026
4029
 
4027
4030
  // src/server/terminal-ws-handler.ts
4028
- import { spawn } from "node-pty";
4031
+ import { createRequire } from "module";
4029
4032
  import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
4030
4033
  var MAX_SESSIONS_PER_CLIENT = 8;
4031
4034
  var DEFAULT_COLS = 80;
4032
4035
  var DEFAULT_ROWS = 24;
4036
+ var requireFromHere = createRequire(import.meta.url);
4037
+ var cachedNodePty;
4033
4038
  var TerminalWebSocketHandler = class {
4034
- constructor(getCwd, logger) {
4039
+ constructor(getCwd, logger, loadNodePty = defaultLoadNodePty) {
4035
4040
  this.getCwd = getCwd;
4036
4041
  this.logger = logger;
4042
+ this.loadNodePty = loadNodePty;
4037
4043
  }
4038
4044
  getCwd;
4039
4045
  logger;
4046
+ loadNodePty;
4040
4047
  /** ws → (terminalId → pty). */
4041
4048
  sessions = /* @__PURE__ */ new Map();
4042
4049
  addClient(ws) {
@@ -4053,7 +4060,8 @@ var TerminalWebSocketHandler = class {
4053
4060
  const p = msg.payload ?? {};
4054
4061
  switch (msg.type) {
4055
4062
  case "terminal.create":
4056
- if (isStr(p.id)) this.create(ws, { id: p.id, cols: numOrUndef(p.cols), rows: numOrUndef(p.rows) });
4063
+ if (isStr(p.id))
4064
+ this.create(ws, { id: p.id, cols: numOrUndef(p.cols), rows: numOrUndef(p.rows) });
4057
4065
  return true;
4058
4066
  case "terminal.input":
4059
4067
  if (isStr(p.id) && isStr(p.data)) this.input(ws, { id: p.id, data: p.data });
@@ -4081,9 +4089,18 @@ var TerminalWebSocketHandler = class {
4081
4089
  return;
4082
4090
  }
4083
4091
  const shell = process.platform === "win32" ? process.env.COMSPEC || "cmd.exe" : process.env.SHELL || "/bin/bash";
4092
+ const nodePty = this.loadNodePty();
4093
+ if (!nodePty) {
4094
+ const msg = "Integrated terminal unavailable: optional dependency node-pty is not installed. Install node-pty to enable WebUI terminal sessions.";
4095
+ this.logger.warn?.(msg);
4096
+ this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
4097
+ ` } });
4098
+ this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
4099
+ return;
4100
+ }
4084
4101
  let pty;
4085
4102
  try {
4086
- pty = spawn(shell, [], {
4103
+ pty = nodePty.spawn(shell, [], {
4087
4104
  name: "xterm-color",
4088
4105
  cols: clampDim(payload.cols, DEFAULT_COLS),
4089
4106
  rows: clampDim(payload.rows, DEFAULT_ROWS),
@@ -4147,6 +4164,15 @@ var TerminalWebSocketHandler = class {
4147
4164
  }
4148
4165
  }
4149
4166
  };
4167
+ function defaultLoadNodePty() {
4168
+ if (cachedNodePty !== void 0) return cachedNodePty;
4169
+ try {
4170
+ cachedNodePty = requireFromHere("node-pty");
4171
+ } catch {
4172
+ cachedNodePty = null;
4173
+ }
4174
+ return cachedNodePty;
4175
+ }
4150
4176
  function isStr(v) {
4151
4177
  return typeof v === "string";
4152
4178
  }
@@ -4532,7 +4558,7 @@ async function findFreePort(host, startPort, opts = {}) {
4532
4558
  }
4533
4559
 
4534
4560
  // src/server/open-browser.ts
4535
- import { spawn as spawn2 } from "child_process";
4561
+ import { spawn } from "child_process";
4536
4562
  function browserOpenCommand(url, platform = process.platform) {
4537
4563
  if (platform === "win32") {
4538
4564
  return { command: "cmd", args: ["/c", "start", "", url] };
@@ -4545,7 +4571,7 @@ function browserOpenCommand(url, platform = process.platform) {
4545
4571
  function openBrowser(url, platform = process.platform) {
4546
4572
  try {
4547
4573
  const { command, args } = browserOpenCommand(url, platform);
4548
- const child = spawn2(command, args, { stdio: "ignore", detached: true, windowsHide: true });
4574
+ const child = spawn(command, args, { stdio: "ignore", detached: true, windowsHide: true });
4549
4575
  child.on("error", () => {
4550
4576
  });
4551
4577
  child.unref();
@@ -6764,7 +6790,7 @@ function createEternalSubscription(subscribe, broadcast2, clientsRef) {
6764
6790
  // src/server/shell-open.ts
6765
6791
  import * as fs12 from "fs/promises";
6766
6792
  import * as path14 from "path";
6767
- import { spawn as spawn3 } from "child_process";
6793
+ import { spawn as spawn2 } from "child_process";
6768
6794
  var METACHAR_REGEX = /[&|<>^"'`\n\r]/;
6769
6795
  async function handleShellOpen(req, logger) {
6770
6796
  try {
@@ -6775,7 +6801,7 @@ async function handleShellOpen(req, logger) {
6775
6801
  }
6776
6802
  const platform = process.platform;
6777
6803
  const launch = (cmd, args, onError) => {
6778
- const child = spawn3(cmd, args, {
6804
+ const child = spawn2(cmd, args, {
6779
6805
  detached: true,
6780
6806
  stdio: "ignore",
6781
6807
  windowsHide: true
@@ -7216,7 +7242,8 @@ async function startWebUI(opts = {}) {
7216
7242
  clientKind: "webui",
7217
7243
  projectRoot,
7218
7244
  projectName: path15.basename(projectRoot),
7219
- appConfig: config
7245
+ appConfig: config,
7246
+ socketFactory: (url) => new WebSocket2(url)
7220
7247
  });
7221
7248
  if (hqTelemetry) {
7222
7249
  hqTelemetry.connect();
@@ -7228,6 +7255,7 @@ async function startWebUI(opts = {}) {
7228
7255
  projectRoot,
7229
7256
  projectName: path15.basename(projectRoot),
7230
7257
  globalRoot: wpaths.globalRoot,
7258
+ initialAgents: statusTracker?.getAgents(),
7231
7259
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
7232
7260
  });
7233
7261
  }