@wrongstack/webui 0.66.13 → 0.73.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.
@@ -1624,6 +1624,68 @@ function computeUsageCost(usage, rates) {
1624
1624
  return (usage.input * rates.input + usage.output * rates.output + (usage.cacheRead ?? 0) * rates.cacheRead) / 1e6;
1625
1625
  }
1626
1626
 
1627
+ // src/server/provider-config-io.ts
1628
+ import * as fs3 from "fs/promises";
1629
+ import * as path3 from "path";
1630
+ import { atomicWrite as atomicWrite2 } from "@wrongstack/core";
1631
+ import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
1632
+ import { DefaultSecretVault } from "@wrongstack/core";
1633
+ async function loadSavedProviders(configPath, vault) {
1634
+ let raw;
1635
+ try {
1636
+ raw = await fs3.readFile(configPath, "utf8");
1637
+ } catch {
1638
+ return {};
1639
+ }
1640
+ let parsed = {};
1641
+ try {
1642
+ parsed = JSON.parse(raw);
1643
+ } catch {
1644
+ return {};
1645
+ }
1646
+ if (!parsed.providers) return {};
1647
+ return decryptConfigSecrets(parsed.providers, vault);
1648
+ }
1649
+ async function saveProviders(configPath, vault, providers) {
1650
+ let raw;
1651
+ let fileExists = true;
1652
+ try {
1653
+ raw = await fs3.readFile(configPath, "utf8");
1654
+ } catch (err) {
1655
+ if (err.code !== "ENOENT") {
1656
+ throw new Error(
1657
+ `Refusing to mutate ${configPath}: ${err.message}`,
1658
+ { cause: err }
1659
+ );
1660
+ }
1661
+ fileExists = false;
1662
+ raw = "{}";
1663
+ }
1664
+ let parsed;
1665
+ try {
1666
+ parsed = JSON.parse(raw);
1667
+ } catch (err) {
1668
+ if (fileExists) {
1669
+ throw new Error(
1670
+ `Refusing to overwrite corrupt config at ${configPath} (${err.message}). Fix or move the file aside before retrying.`,
1671
+ { cause: err }
1672
+ );
1673
+ }
1674
+ parsed = {};
1675
+ }
1676
+ parsed.providers = providers;
1677
+ const encrypted = encryptConfigSecrets(parsed, vault);
1678
+ await atomicWrite2(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
1679
+ }
1680
+ function createProviderConfigIO(configPath) {
1681
+ const keyFile = path3.join(path3.dirname(configPath), ".key");
1682
+ const vault = new DefaultSecretVault({ keyFile });
1683
+ return {
1684
+ load: () => loadSavedProviders(configPath, vault),
1685
+ save: (providers) => saveProviders(configPath, vault, providers)
1686
+ };
1687
+ }
1688
+
1627
1689
  // src/server/provider-keys.ts
1628
1690
  function normalizeKeys(cfg) {
1629
1691
  if (Array.isArray(cfg.apiKeys) && cfg.apiKeys.length > 0) {
@@ -1719,68 +1781,6 @@ function removeProvider(providers, providerId) {
1719
1781
  return { ok: true, message: `Provider "${providerId}" removed` };
1720
1782
  }
1721
1783
 
1722
- // src/server/provider-config-io.ts
1723
- import * as fs3 from "fs/promises";
1724
- import * as path3 from "path";
1725
- import { atomicWrite as atomicWrite2 } from "@wrongstack/core";
1726
- import { decryptConfigSecrets, encryptConfigSecrets } from "@wrongstack/core/security";
1727
- import { DefaultSecretVault } from "@wrongstack/core";
1728
- async function loadSavedProviders(configPath, vault) {
1729
- let raw;
1730
- try {
1731
- raw = await fs3.readFile(configPath, "utf8");
1732
- } catch {
1733
- return {};
1734
- }
1735
- let parsed = {};
1736
- try {
1737
- parsed = JSON.parse(raw);
1738
- } catch {
1739
- return {};
1740
- }
1741
- if (!parsed.providers) return {};
1742
- return decryptConfigSecrets(parsed.providers, vault);
1743
- }
1744
- async function saveProviders(configPath, vault, providers) {
1745
- let raw;
1746
- let fileExists = true;
1747
- try {
1748
- raw = await fs3.readFile(configPath, "utf8");
1749
- } catch (err) {
1750
- if (err.code !== "ENOENT") {
1751
- throw new Error(
1752
- `Refusing to mutate ${configPath}: ${err.message}`,
1753
- { cause: err }
1754
- );
1755
- }
1756
- fileExists = false;
1757
- raw = "{}";
1758
- }
1759
- let parsed;
1760
- try {
1761
- parsed = JSON.parse(raw);
1762
- } catch (err) {
1763
- if (fileExists) {
1764
- throw new Error(
1765
- `Refusing to overwrite corrupt config at ${configPath} (${err.message}). Fix or move the file aside before retrying.`,
1766
- { cause: err }
1767
- );
1768
- }
1769
- parsed = {};
1770
- }
1771
- parsed.providers = providers;
1772
- const encrypted = encryptConfigSecrets(parsed, vault);
1773
- await atomicWrite2(configPath, JSON.stringify(encrypted, null, 2), { mode: 384 });
1774
- }
1775
- function createProviderConfigIO(configPath) {
1776
- const keyFile = path3.join(path3.dirname(configPath), ".key");
1777
- const vault = new DefaultSecretVault({ keyFile });
1778
- return {
1779
- load: () => loadSavedProviders(configPath, vault),
1780
- save: (providers) => saveProviders(configPath, vault, providers)
1781
- };
1782
- }
1783
-
1784
1784
  // src/server/ws-utils.ts
1785
1785
  import { randomBytes } from "crypto";
1786
1786
  import { WebSocket } from "ws";
@@ -1810,6 +1810,130 @@ function generateAuthToken() {
1810
1810
  return randomBytes(16).toString("hex");
1811
1811
  }
1812
1812
 
1813
+ // src/server/provider-handlers.ts
1814
+ function createProviderHandlers(deps) {
1815
+ const { globalConfigPath, vault } = deps;
1816
+ let configWriteLock = deps.getConfigWriteLock();
1817
+ async function loadConfigProviders() {
1818
+ return loadSavedProviders(globalConfigPath, vault);
1819
+ }
1820
+ async function saveConfigProviders(providers) {
1821
+ const next = configWriteLock.then(() => saveProviders(globalConfigPath, vault, providers));
1822
+ configWriteLock = next;
1823
+ deps.setConfigWriteLock(next);
1824
+ await next;
1825
+ }
1826
+ async function handleKeyUpsert(ws, providerId, label, apiKey) {
1827
+ try {
1828
+ const providers = await loadConfigProviders();
1829
+ const result = upsertKey(providers, providerId, label, apiKey, (/* @__PURE__ */ new Date()).toISOString());
1830
+ if (result.ok) await saveConfigProviders(providers);
1831
+ sendResult(ws, result.ok, result.message);
1832
+ } catch (err) {
1833
+ sendResult(ws, false, errMessage(err));
1834
+ }
1835
+ }
1836
+ async function handleKeyDelete(ws, providerId, label) {
1837
+ try {
1838
+ const providers = await loadConfigProviders();
1839
+ const result = deleteKey(providers, providerId, label);
1840
+ if (result.ok) await saveConfigProviders(providers);
1841
+ sendResult(ws, result.ok, result.message);
1842
+ } catch (err) {
1843
+ sendResult(ws, false, errMessage(err));
1844
+ }
1845
+ }
1846
+ async function handleKeySetActive(ws, providerId, label) {
1847
+ try {
1848
+ const providers = await loadConfigProviders();
1849
+ const result = setActiveKey(providers, providerId, label);
1850
+ if (result.ok) await saveConfigProviders(providers);
1851
+ sendResult(ws, result.ok, result.message);
1852
+ } catch (err) {
1853
+ sendResult(ws, false, errMessage(err));
1854
+ }
1855
+ }
1856
+ async function handleProviderAdd(ws, payload) {
1857
+ try {
1858
+ const providers = await loadConfigProviders();
1859
+ const result = addProvider(providers, payload, (/* @__PURE__ */ new Date()).toISOString());
1860
+ if (result.ok) await saveConfigProviders(providers);
1861
+ sendResult(ws, result.ok, result.message);
1862
+ } catch (err) {
1863
+ sendResult(ws, false, errMessage(err));
1864
+ }
1865
+ }
1866
+ async function handleProviderRemove(ws, providerId) {
1867
+ try {
1868
+ const providers = await loadConfigProviders();
1869
+ const result = removeProvider(providers, providerId);
1870
+ if (result.ok) await saveConfigProviders(providers);
1871
+ sendResult(ws, result.ok, result.message);
1872
+ } catch (err) {
1873
+ sendResult(ws, false, errMessage(err));
1874
+ }
1875
+ }
1876
+ return { handleKeyUpsert, handleKeyDelete, handleKeySetActive, handleProviderAdd, handleProviderRemove, loadConfigProviders };
1877
+ }
1878
+
1879
+ // src/server/setup-events.ts
1880
+ function setupEvents(deps) {
1881
+ const { events, broadcast: broadcast2, clients, config, context, pendingConfirms } = deps;
1882
+ events.on("iteration.started", (e) => {
1883
+ broadcast2(clients, {
1884
+ type: "iteration.started",
1885
+ payload: { index: e.index, maxIterations: config.tools?.maxIterations ?? 100 }
1886
+ });
1887
+ });
1888
+ events.on("provider.text_delta", (e) => {
1889
+ broadcast2(clients, { type: "provider.text_delta", payload: { text: e.text, messageId: "current" } });
1890
+ });
1891
+ events.on("provider.thinking_delta", (e) => {
1892
+ broadcast2(clients, { type: "provider.thinking_delta", payload: { text: e.text } });
1893
+ });
1894
+ events.on("tool.started", (e) => {
1895
+ broadcast2(clients, {
1896
+ type: "tool.started",
1897
+ payload: { id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` }
1898
+ });
1899
+ });
1900
+ events.on("tool.progress", (e) => {
1901
+ broadcast2(clients, {
1902
+ type: "tool.progress",
1903
+ payload: { id: e.id, name: e.name, eventType: e.event.type, text: e.event.text }
1904
+ });
1905
+ });
1906
+ events.on("tool.executed", (e) => {
1907
+ broadcast2(clients, {
1908
+ type: "tool.executed",
1909
+ payload: { id: e.id, name: e.name, durationMs: e.durationMs, ok: e.ok, input: e.input, output: e.output }
1910
+ });
1911
+ broadcast2(clients, { type: "todos.updated", payload: { todos: [...context.todos] } });
1912
+ });
1913
+ events.on("provider.response", (e) => {
1914
+ broadcast2(clients, { type: "provider.response", payload: { usage: e.usage, stopReason: e.stopReason, messageId: "current" } });
1915
+ });
1916
+ events.on("context.repaired", (e) => {
1917
+ broadcast2(clients, { type: "context.repaired", payload: { removedToolUses: e.removedToolUses, removedToolResults: e.removedToolResults, removedMessages: e.removedMessages } });
1918
+ });
1919
+ events.on("tool.confirm_needed", (e) => {
1920
+ const id = e.toolUseId ?? `confirm_${Date.now()}`;
1921
+ pendingConfirms.set(id, e.resolve);
1922
+ broadcast2(clients, { type: "tool.confirm_needed", payload: { id, toolName: e.tool?.name ?? "unknown", input: e.input, suggestedPattern: e.suggestedPattern } });
1923
+ });
1924
+ events.on("error", (e) => {
1925
+ broadcast2(clients, { type: "error", payload: { phase: e.phase, message: e.err instanceof Error ? e.err.message : String(e.err) } });
1926
+ });
1927
+ const forwardSubagent = (kind, payload) => broadcast2(clients, { type: "subagent.event", payload: { kind, ...payload } });
1928
+ events.on("subagent.spawned", (e) => forwardSubagent("spawned", { subagentId: e.subagentId, taskId: e.taskId, name: e.name, provider: e.provider, model: e.model, description: e.description }));
1929
+ events.on("subagent.task_started", (e) => forwardSubagent("task_started", { subagentId: e.subagentId, taskId: e.taskId, description: e.description }));
1930
+ events.on("subagent.tool_executed", (e) => forwardSubagent("tool_executed", { subagentId: e.subagentId, toolName: e.name, durationMs: e.durationMs, ok: e.ok }));
1931
+ events.on("subagent.iteration_summary", (e) => forwardSubagent("iteration_summary", { subagentId: e.subagentId, iteration: e.iteration, toolCalls: e.toolCalls, costUsd: e.costUsd, currentTool: e.currentTool }));
1932
+ events.on("subagent.budget_extended", (e) => forwardSubagent("budget_extended", { subagentId: e.subagentId, totalExtensions: e.totalExtensions }));
1933
+ events.on("subagent.ctx_pct", (e) => forwardSubagent("ctx_pct", { subagentId: e.subagentId, load: e.load, tokens: e.tokens, maxContext: e.maxContext }));
1934
+ events.on("subagent.task_completed", (e) => forwardSubagent("task_completed", { subagentId: e.subagentId, status: e.status, iterations: e.iterations, toolCalls: e.toolCalls, error: e.error ? { kind: e.error.kind, message: e.error.message } : void 0 }));
1935
+ }
1936
+
1813
1937
  // src/server/token-estimator.ts
1814
1938
  function estimateTokens(s) {
1815
1939
  return Math.ceil(s.length / 4);
@@ -2190,165 +2314,6 @@ async function startWebUI(opts = {}) {
2190
2314
  `[WebUI] WebSocket server running on ws://${wsHost}:${wsPort}` + (wssSecondary ? ` (and ws://[::1]:${wsPort})` : "")
2191
2315
  );
2192
2316
  const pendingConfirms = /* @__PURE__ */ new Map();
2193
- function setupEvents() {
2194
- events.on("iteration.started", (e) => {
2195
- broadcast(clients, {
2196
- type: "iteration.started",
2197
- payload: { index: e.index, maxIterations: config.tools?.maxIterations ?? 100 }
2198
- });
2199
- });
2200
- events.on("provider.text_delta", (e) => {
2201
- broadcast(clients, { type: "provider.text_delta", payload: { text: e.text, messageId: "current" } });
2202
- });
2203
- events.on("provider.thinking_delta", (e) => {
2204
- broadcast(clients, { type: "provider.thinking_delta", payload: { text: e.text } });
2205
- });
2206
- events.on("tool.started", (e) => {
2207
- broadcast(clients, {
2208
- type: "tool.started",
2209
- payload: { id: e.id, name: e.name, input: e.input, messageId: `tool_${e.id}` }
2210
- });
2211
- });
2212
- events.on("tool.progress", (e) => {
2213
- broadcast(clients, {
2214
- type: "tool.progress",
2215
- payload: {
2216
- id: e.id,
2217
- name: e.name,
2218
- eventType: e.event.type,
2219
- text: e.event.text
2220
- }
2221
- });
2222
- });
2223
- events.on("tool.executed", (e) => {
2224
- broadcast(clients, {
2225
- type: "tool.executed",
2226
- payload: {
2227
- // Forward the tool_use id so frontend can correlate with the
2228
- // matching tool.started bubble — without this, parallel tool calls
2229
- // all stay stuck on "Running…" because the frontend can't tell
2230
- // which bubble this result belongs to.
2231
- id: e.id,
2232
- name: e.name,
2233
- durationMs: e.durationMs,
2234
- ok: e.ok,
2235
- input: e.input,
2236
- output: e.output
2237
- }
2238
- });
2239
- broadcast(clients, {
2240
- type: "todos.updated",
2241
- payload: { todos: [...context.todos] }
2242
- });
2243
- });
2244
- events.on("provider.response", (e) => {
2245
- broadcast(clients, {
2246
- type: "provider.response",
2247
- payload: {
2248
- usage: e.usage,
2249
- stopReason: e.stopReason,
2250
- messageId: "current"
2251
- }
2252
- });
2253
- });
2254
- events.on("context.repaired", (e) => {
2255
- broadcast(clients, {
2256
- type: "context.repaired",
2257
- payload: {
2258
- removedToolUses: e.removedToolUses,
2259
- removedToolResults: e.removedToolResults,
2260
- removedMessages: e.removedMessages
2261
- }
2262
- });
2263
- });
2264
- events.on("tool.confirm_needed", (e) => {
2265
- const id = e.toolUseId ?? `confirm_${Date.now()}`;
2266
- pendingConfirms.set(id, e.resolve);
2267
- broadcast(clients, {
2268
- type: "tool.confirm_needed",
2269
- payload: {
2270
- id,
2271
- toolName: e.tool?.name ?? "unknown",
2272
- input: e.input,
2273
- suggestedPattern: e.suggestedPattern
2274
- }
2275
- });
2276
- });
2277
- events.on("error", (e) => {
2278
- broadcast(clients, {
2279
- type: "error",
2280
- payload: {
2281
- phase: e.phase,
2282
- message: e.err instanceof Error ? e.err.message : String(e.err)
2283
- }
2284
- });
2285
- });
2286
- const forwardSubagent = (kind, payload) => broadcast(clients, { type: "subagent.event", payload: { kind, ...payload } });
2287
- events.on(
2288
- "subagent.spawned",
2289
- (e) => forwardSubagent("spawned", {
2290
- subagentId: e.subagentId,
2291
- taskId: e.taskId,
2292
- name: e.name,
2293
- provider: e.provider,
2294
- model: e.model,
2295
- description: e.description
2296
- })
2297
- );
2298
- events.on(
2299
- "subagent.task_started",
2300
- (e) => forwardSubagent("task_started", {
2301
- subagentId: e.subagentId,
2302
- taskId: e.taskId,
2303
- description: e.description
2304
- })
2305
- );
2306
- events.on(
2307
- "subagent.tool_executed",
2308
- (e) => forwardSubagent("tool_executed", {
2309
- subagentId: e.subagentId,
2310
- toolName: e.name,
2311
- durationMs: e.durationMs,
2312
- ok: e.ok
2313
- })
2314
- );
2315
- events.on(
2316
- "subagent.iteration_summary",
2317
- (e) => forwardSubagent("iteration_summary", {
2318
- subagentId: e.subagentId,
2319
- iteration: e.iteration,
2320
- toolCalls: e.toolCalls,
2321
- costUsd: e.costUsd,
2322
- currentTool: e.currentTool
2323
- })
2324
- );
2325
- events.on(
2326
- "subagent.budget_extended",
2327
- (e) => forwardSubagent("budget_extended", {
2328
- subagentId: e.subagentId,
2329
- totalExtensions: e.totalExtensions
2330
- })
2331
- );
2332
- events.on(
2333
- "subagent.ctx_pct",
2334
- (e) => forwardSubagent("ctx_pct", {
2335
- subagentId: e.subagentId,
2336
- load: e.load,
2337
- tokens: e.tokens,
2338
- maxContext: e.maxContext
2339
- })
2340
- );
2341
- events.on(
2342
- "subagent.task_completed",
2343
- (e) => forwardSubagent("task_completed", {
2344
- subagentId: e.subagentId,
2345
- status: e.status,
2346
- iterations: e.iterations,
2347
- toolCalls: e.toolCalls,
2348
- error: e.error ? { kind: e.error.kind, message: e.error.message } : void 0
2349
- })
2350
- );
2351
- }
2352
2317
  const handleConnection = (ws) => {
2353
2318
  const client = { ws, sessionId: session.id, connectedAt: Date.now() };
2354
2319
  clients.set(ws, client);
@@ -2406,7 +2371,7 @@ async function startWebUI(opts = {}) {
2406
2371
  if (eventsArmed) return;
2407
2372
  eventsArmed = true;
2408
2373
  console.log(`[WebUI] Backend ready (${label})`);
2409
- setupEvents();
2374
+ setupEvents({ events, broadcast, clients, config, context, pendingConfirms });
2410
2375
  };
2411
2376
  wssPrimary.on("listening", () => armOnce(`${wsHost}:${wsPort}`));
2412
2377
  wssPrimary.on("connection", handleConnection);
@@ -2643,7 +2608,7 @@ async function startWebUI(opts = {}) {
2643
2608
  break;
2644
2609
  }
2645
2610
  case "providers.saved": {
2646
- const saved = await loadConfigProviders();
2611
+ const saved = await providerHandlers.loadConfigProviders();
2647
2612
  send(ws, {
2648
2613
  type: "providers.saved",
2649
2614
  payload: {
@@ -2732,27 +2697,27 @@ async function startWebUI(opts = {}) {
2732
2697
  case "key.add":
2733
2698
  case "key.update": {
2734
2699
  const { providerId, label, apiKey } = msg.payload;
2735
- await handleKeyUpsert(ws, providerId, label, apiKey);
2700
+ await providerHandlers.handleKeyUpsert(ws, providerId, label, apiKey);
2736
2701
  break;
2737
2702
  }
2738
2703
  case "key.delete": {
2739
2704
  const { providerId, label } = msg.payload;
2740
- await handleKeyDelete(ws, providerId, label);
2705
+ await providerHandlers.handleKeyDelete(ws, providerId, label);
2741
2706
  break;
2742
2707
  }
2743
2708
  case "key.set_active": {
2744
2709
  const { providerId, label } = msg.payload;
2745
- await handleKeySetActive(ws, providerId, label);
2710
+ await providerHandlers.handleKeySetActive(ws, providerId, label);
2746
2711
  break;
2747
2712
  }
2748
2713
  case "provider.add": {
2749
2714
  const p = msg.payload;
2750
- await handleProviderAdd(ws, p);
2715
+ await providerHandlers.handleProviderAdd(ws, p);
2751
2716
  break;
2752
2717
  }
2753
2718
  case "provider.remove": {
2754
2719
  const { providerId } = msg.payload;
2755
- await handleProviderRemove(ws, providerId);
2720
+ await providerHandlers.handleProviderRemove(ws, providerId);
2756
2721
  break;
2757
2722
  }
2758
2723
  case "sessions.list": {
@@ -3137,65 +3102,14 @@ async function startWebUI(opts = {}) {
3137
3102
  }
3138
3103
  }
3139
3104
  }
3140
- async function loadConfigProviders() {
3141
- return loadSavedProviders(globalConfigPath, vault);
3142
- }
3143
- async function saveConfigProviders(providers) {
3144
- configWriteLock = configWriteLock.then(
3145
- () => saveProviders(globalConfigPath, vault, providers)
3146
- );
3147
- await configWriteLock;
3148
- }
3149
- async function handleKeyUpsert(ws, providerId, label, apiKey) {
3150
- try {
3151
- const providers = await loadConfigProviders();
3152
- const result = upsertKey(providers, providerId, label, apiKey, (/* @__PURE__ */ new Date()).toISOString());
3153
- if (result.ok) await saveConfigProviders(providers);
3154
- sendResult(ws, result.ok, result.message);
3155
- } catch (err) {
3156
- sendResult(ws, false, errMessage(err));
3105
+ const providerHandlers = createProviderHandlers({
3106
+ globalConfigPath,
3107
+ vault,
3108
+ getConfigWriteLock: () => configWriteLock,
3109
+ setConfigWriteLock: (p) => {
3110
+ configWriteLock = p;
3157
3111
  }
3158
- }
3159
- async function handleKeyDelete(ws, providerId, label) {
3160
- try {
3161
- const providers = await loadConfigProviders();
3162
- const result = deleteKey(providers, providerId, label);
3163
- if (result.ok) await saveConfigProviders(providers);
3164
- sendResult(ws, result.ok, result.message);
3165
- } catch (err) {
3166
- sendResult(ws, false, errMessage(err));
3167
- }
3168
- }
3169
- async function handleKeySetActive(ws, providerId, label) {
3170
- try {
3171
- const providers = await loadConfigProviders();
3172
- const result = setActiveKey(providers, providerId, label);
3173
- if (result.ok) await saveConfigProviders(providers);
3174
- sendResult(ws, result.ok, result.message);
3175
- } catch (err) {
3176
- sendResult(ws, false, errMessage(err));
3177
- }
3178
- }
3179
- async function handleProviderAdd(ws, payload) {
3180
- try {
3181
- const providers = await loadConfigProviders();
3182
- const result = addProvider(providers, payload, (/* @__PURE__ */ new Date()).toISOString());
3183
- if (result.ok) await saveConfigProviders(providers);
3184
- sendResult(ws, result.ok, result.message);
3185
- } catch (err) {
3186
- sendResult(ws, false, errMessage(err));
3187
- }
3188
- }
3189
- async function handleProviderRemove(ws, providerId) {
3190
- try {
3191
- const providers = await loadConfigProviders();
3192
- const result = removeProvider(providers, providerId);
3193
- if (result.ok) await saveConfigProviders(providers);
3194
- sendResult(ws, result.ok, result.message);
3195
- } catch (err) {
3196
- sendResult(ws, false, errMessage(err));
3197
- }
3198
- }
3112
+ });
3199
3113
  const httpServer = createHttpServer({
3200
3114
  host: wsHost,
3201
3115
  distDir: path4.resolve(import.meta.dirname, "../../dist"),