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