@wrongstack/webui 0.6.0 → 0.6.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.
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  // src/server/index.ts
3
3
  import * as fs2 from "fs/promises";
4
+ import * as http from "http";
4
5
  import * as path from "path";
5
6
  import {
6
7
  Agent,
@@ -85,7 +86,13 @@ async function startWebUI(opts = {}) {
85
86
  const { config: baseConfig, vault, globalConfigPath, projectRoot, wpaths, logger } = boot;
86
87
  let config = baseConfig;
87
88
  let configWriteLock = Promise.resolve();
88
- console.log("[WebUI] Config loaded:", config.provider, "/", config.model);
89
+ console.log("[WebUI] Config loaded:", config.provider ?? "(none)", "/", config.model ?? "(none)");
90
+ if (!config.provider && config.providers && Object.keys(config.providers).length > 0) {
91
+ const firstKey = Object.keys(config.providers)[0];
92
+ config = patchConfig(config, { provider: firstKey });
93
+ console.log("[WebUI] No active provider \u2014 auto-selected:", firstKey);
94
+ }
95
+ const needsProvider = !config.provider || !config.model;
89
96
  const modelsRegistry = new DefaultModelsRegistry({
90
97
  cacheFile: wpaths.modelsCache,
91
98
  ttlSeconds: 24 * 3600
@@ -153,22 +160,46 @@ async function startWebUI(opts = {}) {
153
160
  provider: config.provider,
154
161
  model: config.model
155
162
  });
156
- const providerConfig = config.providers?.[config.provider] ?? {
157
- type: config.provider,
158
- apiKey: config.apiKey,
159
- baseUrl: config.baseUrl
160
- };
161
163
  let provider;
162
- try {
163
- const cfgWithType = { ...providerConfig, type: config.provider };
164
- if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {
165
- provider = providerRegistry.create(cfgWithType);
164
+ if (!needsProvider) {
165
+ const providerConfig = config.providers?.[config.provider] ?? {
166
+ type: config.provider,
167
+ apiKey: config.apiKey,
168
+ baseUrl: config.baseUrl
169
+ };
170
+ try {
171
+ const cfgWithType = { ...providerConfig, type: config.provider };
172
+ if (config.features.modelsRegistry && providerRegistry.has(config.provider)) {
173
+ provider = providerRegistry.create(cfgWithType);
174
+ } else {
175
+ provider = makeProviderFromConfig(config.provider, cfgWithType);
176
+ }
177
+ } catch (err) {
178
+ console.error("[WebUI] Failed to create provider:", err);
179
+ throw err;
180
+ }
181
+ } else {
182
+ const savedProviders = config.providers ?? {};
183
+ const firstKey = Object.keys(savedProviders)[0];
184
+ if (firstKey) {
185
+ const firstProvider = savedProviders[firstKey];
186
+ try {
187
+ provider = makeProviderFromConfig(firstKey, {
188
+ ...firstProvider,
189
+ type: firstKey,
190
+ family: firstProvider.family,
191
+ apiKey: firstProvider.apiKey
192
+ });
193
+ console.log("[WebUI] Using saved provider:", firstKey);
194
+ } catch (err) {
195
+ console.error("[WebUI] Could not create provider stub:", err);
196
+ throw err;
197
+ }
166
198
  } else {
167
- provider = makeProviderFromConfig(config.provider, cfgWithType);
199
+ throw new Error(
200
+ "No provider configured. Run `wrongstack init` first, or configure via the WebUI."
201
+ );
168
202
  }
169
- } catch (err) {
170
- console.error("[WebUI] Failed to create provider:", err);
171
- throw err;
172
203
  }
173
204
  const context = new Context({
174
205
  systemPrompt,
@@ -473,8 +504,8 @@ async function startWebUI(opts = {}) {
473
504
  rateLimits.delete(ws);
474
505
  console.log("[WebUI] Client disconnected, total:", clients.size);
475
506
  if (pendingConfirms.size > 0) {
476
- for (const [id, resolve] of pendingConfirms) {
477
- resolve("no");
507
+ for (const [id, resolve2] of pendingConfirms) {
508
+ resolve2("no");
478
509
  pendingConfirms.delete(id);
479
510
  }
480
511
  }
@@ -554,10 +585,10 @@ async function startWebUI(opts = {}) {
554
585
  }
555
586
  case "tool.confirm_result": {
556
587
  const { id, decision } = msg.payload;
557
- const resolve = pendingConfirms.get(id);
558
- if (resolve) {
588
+ const resolve2 = pendingConfirms.get(id);
589
+ if (resolve2) {
559
590
  pendingConfirms.delete(id);
560
- resolve(decision);
591
+ resolve2(decision);
561
592
  }
562
593
  break;
563
594
  }
@@ -1419,6 +1450,58 @@ async function startWebUI(opts = {}) {
1419
1450
  sendResult(ws, false, err instanceof Error ? err.message : String(err));
1420
1451
  }
1421
1452
  }
1453
+ const httpPort = Number.parseInt(process.env["PORT"] ?? "3456", 10);
1454
+ const DIST_DIR = path.resolve(import.meta.dirname, "../../dist");
1455
+ const mimeTypes = {
1456
+ ".html": "text/html",
1457
+ ".js": "application/javascript",
1458
+ ".css": "text/css",
1459
+ ".json": "application/json",
1460
+ ".svg": "image/svg+xml",
1461
+ ".png": "image/png",
1462
+ ".ico": "image/x-icon"
1463
+ };
1464
+ const httpServer = http.createServer(async (req, res) => {
1465
+ try {
1466
+ const url = new URL(req.url ?? "/", `http://127.0.0.1:${httpPort}`);
1467
+ let filePath;
1468
+ if (url.pathname === "/" || url.pathname === "") {
1469
+ filePath = path.join(DIST_DIR, "index.html");
1470
+ } else if (url.pathname.startsWith("/assets/")) {
1471
+ filePath = path.join(DIST_DIR, url.pathname);
1472
+ } else if (url.pathname.startsWith("/")) {
1473
+ filePath = path.join(DIST_DIR, url.pathname);
1474
+ } else {
1475
+ filePath = path.join(DIST_DIR, "index.html");
1476
+ }
1477
+ const ext = path.extname(filePath);
1478
+ const contentType = mimeTypes[ext] ?? "application/octet-stream";
1479
+ res.setHeader("Content-Type", contentType);
1480
+ if (ext === ".html") {
1481
+ res.setHeader("Cache-Control", "no-cache");
1482
+ }
1483
+ const fileContent = await fs2.readFile(filePath);
1484
+ res.writeHead(200);
1485
+ res.end(fileContent);
1486
+ } catch (err) {
1487
+ if (err.code === "ENOENT") {
1488
+ try {
1489
+ const fileContent = await fs2.readFile(path.join(DIST_DIR, "index.html"));
1490
+ res.writeHead(200, { "Content-Type": "text/html" });
1491
+ res.end(fileContent);
1492
+ } catch {
1493
+ res.writeHead(404);
1494
+ res.end("Not found");
1495
+ }
1496
+ } else {
1497
+ res.writeHead(500);
1498
+ res.end("Server error");
1499
+ }
1500
+ }
1501
+ });
1502
+ httpServer.listen(httpPort, wsHost2, () => {
1503
+ console.log(`[WebUI] HTTP server running on http://${wsHost2}:${httpPort}`);
1504
+ });
1422
1505
  const shutdown = async () => {
1423
1506
  console.log("[WebUI] Shutting down...");
1424
1507
  try {
@@ -1432,6 +1515,7 @@ async function startWebUI(opts = {}) {
1432
1515
  console.warn("[WebUI] Error closing session:", e);
1433
1516
  }
1434
1517
  for (const [ws] of clients) ws.close();
1518
+ httpServer.close();
1435
1519
  wssPrimary.close();
1436
1520
  wssSecondary?.close();
1437
1521
  process.exit(0);