@wrongstack/webui 0.272.0 → 0.272.1

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.
@@ -1104,6 +1104,9 @@ function isTrustedLoopbackOrigin(origin) {
1104
1104
  function isLoopbackBind(wsHost) {
1105
1105
  return wsHost === "127.0.0.1" || wsHost === "::1" || wsHost === "localhost";
1106
1106
  }
1107
+ function isWildcardBind(wsHost) {
1108
+ return wsHost === "0.0.0.0" || wsHost === "::" || wsHost === "[::]";
1109
+ }
1107
1110
  function tokenMatches(provided, expected) {
1108
1111
  if (!provided) return false;
1109
1112
  const a = Buffer.from(provided);
@@ -1152,13 +1155,13 @@ function verifyClient(input) {
1152
1155
  if (!origin) {
1153
1156
  const remoteIp = remoteAddress ?? "";
1154
1157
  const isRemoteLoopback = remoteIp === "127.0.0.1" || remoteIp === "::1";
1155
- if (!isRemoteLoopback && wsHost === "0.0.0.0") return false;
1158
+ if (!isRemoteLoopback && isWildcardBind(wsHost)) return false;
1156
1159
  return urlTokenOk || cookieTokenOk || isLoopbackBind(wsHost);
1157
1160
  }
1158
1161
  try {
1159
1162
  const { hostname } = new URL(origin);
1160
1163
  if (isLoopbackHostname(hostname)) {
1161
- if (wsHost === "0.0.0.0" && !isTrustedLoopbackOrigin(origin)) {
1164
+ if (isWildcardBind(wsHost) && !isTrustedLoopbackOrigin(origin)) {
1162
1165
  return false;
1163
1166
  }
1164
1167
  return true;
@@ -2820,7 +2823,7 @@ import { decryptConfigSecrets as decryptConfigSecrets2, encryptConfigSecrets as
2820
2823
  import { buildProviderFactoriesFromRegistry, makeProviderFromConfig } from "@wrongstack/providers";
2821
2824
  import { builtinToolsPack, forgetTool, rememberTool, searchMemoryTool, relatedMemoryTool } from "@wrongstack/tools";
2822
2825
  import { MCPRegistry } from "@wrongstack/mcp";
2823
- import { WebSocketServer } from "ws";
2826
+ import { WebSocket as WebSocket2, WebSocketServer } from "ws";
2824
2827
 
2825
2828
  // ../runtime/src/container.ts
2826
2829
  import {
@@ -4024,18 +4027,22 @@ async function ensureProjectDataDir(slug, globalConfigPath) {
4024
4027
  }
4025
4028
 
4026
4029
  // src/server/terminal-ws-handler.ts
4027
- import { spawn } from "node-pty";
4030
+ import { createRequire } from "module";
4028
4031
  import { toErrorMessage as toErrorMessage3 } from "@wrongstack/core/utils";
4029
4032
  var MAX_SESSIONS_PER_CLIENT = 8;
4030
4033
  var DEFAULT_COLS = 80;
4031
4034
  var DEFAULT_ROWS = 24;
4035
+ var requireFromHere = createRequire(import.meta.url);
4036
+ var cachedNodePty;
4032
4037
  var TerminalWebSocketHandler = class {
4033
- constructor(getCwd, logger) {
4038
+ constructor(getCwd, logger, loadNodePty = defaultLoadNodePty) {
4034
4039
  this.getCwd = getCwd;
4035
4040
  this.logger = logger;
4041
+ this.loadNodePty = loadNodePty;
4036
4042
  }
4037
4043
  getCwd;
4038
4044
  logger;
4045
+ loadNodePty;
4039
4046
  /** ws → (terminalId → pty). */
4040
4047
  sessions = /* @__PURE__ */ new Map();
4041
4048
  addClient(ws) {
@@ -4052,7 +4059,8 @@ var TerminalWebSocketHandler = class {
4052
4059
  const p = msg.payload ?? {};
4053
4060
  switch (msg.type) {
4054
4061
  case "terminal.create":
4055
- if (isStr(p.id)) this.create(ws, { id: p.id, cols: numOrUndef(p.cols), rows: numOrUndef(p.rows) });
4062
+ if (isStr(p.id))
4063
+ this.create(ws, { id: p.id, cols: numOrUndef(p.cols), rows: numOrUndef(p.rows) });
4056
4064
  return true;
4057
4065
  case "terminal.input":
4058
4066
  if (isStr(p.id) && isStr(p.data)) this.input(ws, { id: p.id, data: p.data });
@@ -4080,9 +4088,18 @@ var TerminalWebSocketHandler = class {
4080
4088
  return;
4081
4089
  }
4082
4090
  const shell = process.platform === "win32" ? process.env.COMSPEC || "cmd.exe" : process.env.SHELL || "/bin/bash";
4091
+ const nodePty = this.loadNodePty();
4092
+ if (!nodePty) {
4093
+ const msg = "Integrated terminal unavailable: optional dependency node-pty is not installed. Install node-pty to enable WebUI terminal sessions.";
4094
+ this.logger.warn?.(msg);
4095
+ this.send(ws, { type: "terminal.output", payload: { id: payload.id, data: `${msg}\r
4096
+ ` } });
4097
+ this.send(ws, { type: "terminal.exit", payload: { id: payload.id, exitCode: -1 } });
4098
+ return;
4099
+ }
4083
4100
  let pty;
4084
4101
  try {
4085
- pty = spawn(shell, [], {
4102
+ pty = nodePty.spawn(shell, [], {
4086
4103
  name: "xterm-color",
4087
4104
  cols: clampDim(payload.cols, DEFAULT_COLS),
4088
4105
  rows: clampDim(payload.rows, DEFAULT_ROWS),
@@ -4146,6 +4163,15 @@ var TerminalWebSocketHandler = class {
4146
4163
  }
4147
4164
  }
4148
4165
  };
4166
+ function defaultLoadNodePty() {
4167
+ if (cachedNodePty !== void 0) return cachedNodePty;
4168
+ try {
4169
+ cachedNodePty = requireFromHere("node-pty");
4170
+ } catch {
4171
+ cachedNodePty = null;
4172
+ }
4173
+ return cachedNodePty;
4174
+ }
4149
4175
  function isStr(v) {
4150
4176
  return typeof v === "string";
4151
4177
  }
@@ -4531,7 +4557,7 @@ async function findFreePort(host, startPort, opts = {}) {
4531
4557
  }
4532
4558
 
4533
4559
  // src/server/open-browser.ts
4534
- import { spawn as spawn2 } from "child_process";
4560
+ import { spawn } from "child_process";
4535
4561
  function browserOpenCommand(url, platform = process.platform) {
4536
4562
  if (platform === "win32") {
4537
4563
  return { command: "cmd", args: ["/c", "start", "", url] };
@@ -4544,7 +4570,7 @@ function browserOpenCommand(url, platform = process.platform) {
4544
4570
  function openBrowser(url, platform = process.platform) {
4545
4571
  try {
4546
4572
  const { command, args } = browserOpenCommand(url, platform);
4547
- const child = spawn2(command, args, { stdio: "ignore", detached: true, windowsHide: true });
4573
+ const child = spawn(command, args, { stdio: "ignore", detached: true, windowsHide: true });
4548
4574
  child.on("error", () => {
4549
4575
  });
4550
4576
  child.unref();
@@ -6771,7 +6797,7 @@ function createEternalSubscription(subscribe, broadcast2, clientsRef) {
6771
6797
  // src/server/shell-open.ts
6772
6798
  import * as fs12 from "fs/promises";
6773
6799
  import * as path14 from "path";
6774
- import { spawn as spawn3 } from "child_process";
6800
+ import { spawn as spawn2 } from "child_process";
6775
6801
  var METACHAR_REGEX = /[&|<>^"'`\n\r]/;
6776
6802
  async function handleShellOpen(req, logger) {
6777
6803
  try {
@@ -6782,7 +6808,7 @@ async function handleShellOpen(req, logger) {
6782
6808
  }
6783
6809
  const platform = process.platform;
6784
6810
  const launch = (cmd, args, onError) => {
6785
- const child = spawn3(cmd, args, {
6811
+ const child = spawn2(cmd, args, {
6786
6812
  detached: true,
6787
6813
  stdio: "ignore",
6788
6814
  windowsHide: true
@@ -7223,7 +7249,8 @@ async function startWebUI(opts = {}) {
7223
7249
  clientKind: "webui",
7224
7250
  projectRoot,
7225
7251
  projectName: path15.basename(projectRoot),
7226
- appConfig: config
7252
+ appConfig: config,
7253
+ socketFactory: (url) => new WebSocket2(url)
7227
7254
  });
7228
7255
  if (hqTelemetry) {
7229
7256
  hqTelemetry.connect();
@@ -7235,6 +7262,7 @@ async function startWebUI(opts = {}) {
7235
7262
  projectRoot,
7236
7263
  projectName: path15.basename(projectRoot),
7237
7264
  globalRoot: wpaths.globalRoot,
7265
+ initialAgents: statusTracker?.getAgents(),
7238
7266
  startedAt: (/* @__PURE__ */ new Date()).toISOString()
7239
7267
  });
7240
7268
  }