@wrongstack/cli 0.299.0 → 0.300.0
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/{acp-S4LZQB66.js → acp-IDNS2YVE.js} +3 -3
- package/dist/{auth-G5YKKSRH.js → auth-6T6ZOT2R.js} +5 -5
- package/dist/auth-menu/loopback-server.d.ts +41 -55
- package/dist/auth-menu/openai-codex-oauth.d.ts +36 -123
- package/dist/auto-discover-providers-DRW4OZHG.js +8 -0
- package/dist/boot/dispatch-webui.d.ts +3 -0
- package/dist/{chunk-PEKUKUCH.js → chunk-3MWUZMOL.js} +19 -12
- package/dist/{chunk-TGDHN4LM.js → chunk-4GVH7V3S.js} +4 -24
- package/dist/{chunk-4U3DMA2Y.js → chunk-4WJKVQJY.js} +15 -20
- package/dist/{chunk-2SLSUDYS.js → chunk-GYDQABMA.js} +269 -180
- package/dist/{chunk-4HH3YGG4.js → chunk-MRLYDHQ7.js} +8 -5
- package/dist/{chunk-66T43Q4Y.js → chunk-N7ULWBO5.js} +4 -3
- package/dist/{chunk-ZNZJ34RF.js → chunk-PEMN4T5O.js} +2 -2
- package/dist/chunk-PYTFS4MC.js +238 -0
- package/dist/{chunk-L5CE52XW.js → chunk-V76R7DC5.js} +25 -19
- package/dist/{cli-main-3MQDFSYK.js → cli-main-7B3G2YSG.js} +528 -156
- package/dist/{execution-ANOYMWPO.js → execution-SHGM7BVC.js} +50 -5
- package/dist/fleet/budget-source.d.ts +18 -0
- package/dist/fleet/host-status.d.ts +31 -0
- package/dist/fleet/host-types.d.ts +8 -0
- package/dist/fleet/host.d.ts +5 -0
- package/dist/{hq-BDO56CX6.js → hq-DVDD4XPV.js} +22 -3
- package/dist/hq-server/auth-state.d.ts +29 -1
- package/dist/hq-server/auth.d.ts +16 -0
- package/dist/hq-server/client-address.d.ts +56 -0
- package/dist/hq-server/login-attempt-store.d.ts +30 -10
- package/dist/hq-server/routes/auth-handlers.d.ts +66 -26
- package/dist/hq-server/routes.d.ts +13 -0
- package/dist/hq-server/types.d.ts +10 -0
- package/dist/{hq-server-RJLJJZU7.js → hq-server-DQHLNHDL.js} +2 -2
- package/dist/hq-server.d.ts +19 -0
- package/dist/index.js +20 -20
- package/dist/{modeldiag-HENYJ4N7.js → modeldiag-YOQZHBQV.js} +2 -2
- package/dist/{plugin-usage-VTL7ZTVC.js → plugin-usage-EOG4ET4S.js} +2 -2
- package/dist/{plugins-ETY6W3DC.js → plugins-56CMWMPR.js} +3 -3
- package/dist/{providers-models-GDELBHZK.js → providers-models-K6RT7ABI.js} +34 -9
- package/dist/slash-commands/command-context.d.ts +19 -0
- package/dist/webui-server/setup-events.d.ts +17 -0
- package/dist/webui-server/terminal-log-view.d.ts +29 -12
- package/dist/{webui-server-PCLSXX6L.js → webui-server-G3EKFXLL.js} +109 -38
- package/dist/webui-server-options.d.ts +16 -0
- package/dist/wiring/brain-and-orchestration.d.ts +3 -0
- package/dist/wiring/director-setup.d.ts +4 -0
- package/dist/wiring/fallback-gate.d.ts +31 -0
- package/dist/wiring/fleet-command-handlers.d.ts +1 -1
- package/dist/wiring/plugins.d.ts +2 -8
- package/dist/wiring/provider-utility-tools.d.ts +26 -1
- package/package.json +23 -23
- package/dist/chunk-GO3TJICK.js +0 -489
package/dist/hq-server.d.ts
CHANGED
|
@@ -60,6 +60,25 @@ export interface HqServerOptions {
|
|
|
60
60
|
* `--hq-token-ttl-ms` CLI flag.
|
|
61
61
|
*/
|
|
62
62
|
tokenTtlMs?: number;
|
|
63
|
+
/**
|
|
64
|
+
* How many trusted reverse proxies sit between a browser and this server.
|
|
65
|
+
* Controls which address the login backoff keys on (WS-106).
|
|
66
|
+
*
|
|
67
|
+
* Default `0`: `X-Forwarded-For` is ignored entirely and the socket peer is
|
|
68
|
+
* used — correct for a direct bind, and the only safe default, because the
|
|
69
|
+
* header is client-supplied and honouring it unasked would let an attacker
|
|
70
|
+
* mint a fresh rate-limit identity per request.
|
|
71
|
+
*
|
|
72
|
+
* Set it to the real hop count when HQ runs behind a tunnel or relay
|
|
73
|
+
* (`--hq-public-url`, `--hq-require-browser-auth`). Without it every request
|
|
74
|
+
* arrives from the tunnel's own address, so all users share one backoff
|
|
75
|
+
* bucket: one attacker locks out everyone, and a distributed attacker is not
|
|
76
|
+
* limited per-source at all. `1` covers the common single-tunnel case.
|
|
77
|
+
*
|
|
78
|
+
* Only the rightmost `n` forwarded entries are ever read — see
|
|
79
|
+
* `hq-server/client-address.ts`. Set via `--hq-trusted-proxy-hops`.
|
|
80
|
+
*/
|
|
81
|
+
trustedProxyHops?: number;
|
|
63
82
|
}
|
|
64
83
|
export interface HqStartupConnectionInfo {
|
|
65
84
|
dataDir: string;
|
package/dist/index.js
CHANGED
|
@@ -13,20 +13,20 @@ import {
|
|
|
13
13
|
import {
|
|
14
14
|
patchConfig
|
|
15
15
|
} from "./chunk-TYO2OVAD.js";
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_PORT
|
|
18
|
+
} from "./chunk-GYDQABMA.js";
|
|
19
|
+
import "./chunk-Q5GTM25S.js";
|
|
16
20
|
import {
|
|
17
21
|
helpCmd,
|
|
18
22
|
versionCmd
|
|
19
23
|
} from "./chunk-ARWGQUB3.js";
|
|
20
|
-
import {
|
|
21
|
-
discoverAndMergeProviders
|
|
22
|
-
} from "./chunk-TGDHN4LM.js";
|
|
23
|
-
import {
|
|
24
|
-
DEFAULT_PORT
|
|
25
|
-
} from "./chunk-2SLSUDYS.js";
|
|
26
|
-
import "./chunk-Q5GTM25S.js";
|
|
27
24
|
import {
|
|
28
25
|
CLI_VERSION
|
|
29
26
|
} from "./chunk-XJXDOF63.js";
|
|
27
|
+
import {
|
|
28
|
+
discoverAndMergeProviders
|
|
29
|
+
} from "./chunk-4GVH7V3S.js";
|
|
30
30
|
import {
|
|
31
31
|
runUpdateCommand
|
|
32
32
|
} from "./chunk-I5MEUFDI.js";
|
|
@@ -36,11 +36,11 @@ import {
|
|
|
36
36
|
parseArgs,
|
|
37
37
|
runPicker,
|
|
38
38
|
saveToGlobalConfig
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-PEMN4T5O.js";
|
|
40
40
|
import {
|
|
41
41
|
isKeylessLocalProvider,
|
|
42
42
|
visibleModelIds
|
|
43
|
-
} from "./chunk-
|
|
43
|
+
} from "./chunk-N7ULWBO5.js";
|
|
44
44
|
import {
|
|
45
45
|
mutateConfigProviders,
|
|
46
46
|
normalizeKeys,
|
|
@@ -1495,9 +1495,9 @@ function compactSingleLine(text) {
|
|
|
1495
1495
|
|
|
1496
1496
|
// src/subcommands/index.ts
|
|
1497
1497
|
var loaders = {
|
|
1498
|
-
acp: async () => (await import("./acp-
|
|
1498
|
+
acp: async () => (await import("./acp-IDNS2YVE.js")).acpCmd,
|
|
1499
1499
|
init: async () => (await import("./init-E2NDDOHI.js")).initCmd,
|
|
1500
|
-
auth: async () => (await import("./auth-
|
|
1500
|
+
auth: async () => (await import("./auth-6T6ZOT2R.js")).authCmd,
|
|
1501
1501
|
update: async () => (await import("./update-MV6TUACD.js")).updateCmd,
|
|
1502
1502
|
sessions: async () => (await import("./sessions-config-NLCMK3BY.js")).sessionsCmd,
|
|
1503
1503
|
config: async () => (await import("./sessions-config-NLCMK3BY.js")).configCmd,
|
|
@@ -1506,23 +1506,23 @@ var loaders = {
|
|
|
1506
1506
|
audit: async () => (await import("./audit-2ZSFGNIF.js")).auditCmd,
|
|
1507
1507
|
tools: async () => (await import("./tools-skills-UNKLOB7M.js")).toolsCmd,
|
|
1508
1508
|
skills: async () => (await import("./tools-skills-UNKLOB7M.js")).skillsCmd,
|
|
1509
|
-
providers: async () => (await import("./providers-models-
|
|
1510
|
-
models: async () => (await import("./providers-models-
|
|
1509
|
+
providers: async () => (await import("./providers-models-K6RT7ABI.js")).providersCmd,
|
|
1510
|
+
models: async () => (await import("./providers-models-K6RT7ABI.js")).modelsCmd,
|
|
1511
1511
|
mcp: async () => (await import("./mcp-CYLPUEHC.js")).mcpCmd,
|
|
1512
|
-
plugin: async () => (await import("./plugin-usage-
|
|
1513
|
-
plugins: async () => (await import("./plugin-usage-
|
|
1512
|
+
plugin: async () => (await import("./plugin-usage-EOG4ET4S.js")).pluginCmd,
|
|
1513
|
+
plugins: async () => (await import("./plugin-usage-EOG4ET4S.js")).pluginCmd,
|
|
1514
1514
|
diag: async () => (await import("./diag-doctor-OQ3AA63O.js")).diagCmd,
|
|
1515
1515
|
doctor: async () => (await import("./diag-doctor-OQ3AA63O.js")).doctorCmd,
|
|
1516
1516
|
export: async () => (await import("./export-TPORYVRZ.js")).exportCmd,
|
|
1517
|
-
usage: async () => (await import("./plugin-usage-
|
|
1517
|
+
usage: async () => (await import("./plugin-usage-EOG4ET4S.js")).usageCmd,
|
|
1518
1518
|
version: async () => (await import("./version-help-ASKQYXDJ.js")).versionCmd,
|
|
1519
1519
|
help: async () => (await import("./version-help-ASKQYXDJ.js")).helpCmd,
|
|
1520
1520
|
projects: async () => (await import("./projects-XE76NFNZ.js")).projectsCmd,
|
|
1521
|
-
modeldiag: async () => (await import("./modeldiag-
|
|
1521
|
+
modeldiag: async () => (await import("./modeldiag-YOQZHBQV.js")).modeldiagCmd,
|
|
1522
1522
|
quick: async () => (await import("./quick-VIO2BEHS.js")).quickCmd,
|
|
1523
1523
|
bench: async () => (await import("./bench-7ZDWSNEF.js")).benchCmd,
|
|
1524
1524
|
chronicle: async () => (await import("./chronicle-QQEQFSSE.js")).chronicleCmd,
|
|
1525
|
-
hq: async () => (await import("./hq-
|
|
1525
|
+
hq: async () => (await import("./hq-DVDD4XPV.js")).hqCmd,
|
|
1526
1526
|
mailbox: async () => (await import("./mailbox-serve-DBOOOOWZ.js")).mailboxServeCmd,
|
|
1527
1527
|
permissions: async () => (await import("./permissions-FNYLZIL4.js")).permissionsCmd,
|
|
1528
1528
|
project: async () => (await import("./project-BLQ3ALVL.js")).projectCmd,
|
|
@@ -2159,7 +2159,7 @@ async function isPortInUse(host, port) {
|
|
|
2159
2159
|
}
|
|
2160
2160
|
async function handleHqShortCircuit(flags) {
|
|
2161
2161
|
if (flags["hq"] !== true) return null;
|
|
2162
|
-
const { startHqServer } = await import("./hq-server-
|
|
2162
|
+
const { startHqServer } = await import("./hq-server-DQHLNHDL.js");
|
|
2163
2163
|
const tunnelRequested = flags["tunnel"] === true;
|
|
2164
2164
|
const host = typeof flags["host"] === "string" ? flags["host"] : tunnelRequested ? "127.0.0.1" : HQ_CLI_DEFAULT_HOST;
|
|
2165
2165
|
if (tunnelRequested && !isLoopbackHost(host)) {
|
|
@@ -3149,7 +3149,7 @@ async function initializeCli(argv) {
|
|
|
3149
3149
|
async function main(argv) {
|
|
3150
3150
|
const cliCtx = await initializeCli(argv);
|
|
3151
3151
|
if (typeof cliCtx === "number") return cliCtx;
|
|
3152
|
-
const { runInteractive } = await import("./cli-main-
|
|
3152
|
+
const { runInteractive } = await import("./cli-main-7B3G2YSG.js");
|
|
3153
3153
|
return runInteractive(cliCtx);
|
|
3154
3154
|
}
|
|
3155
3155
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
discoverAndMergeProviders
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-4GVH7V3S.js";
|
|
4
4
|
import {
|
|
5
5
|
mutateConfigProviders,
|
|
6
6
|
normalizeKeys,
|
|
@@ -1287,4 +1287,4 @@ ${color2.dim('Enter numbers or provider IDs (comma-separated, or "all"):')}
|
|
|
1287
1287
|
export {
|
|
1288
1288
|
modeldiagCmd
|
|
1289
1289
|
};
|
|
1290
|
-
//# sourceMappingURL=modeldiag-
|
|
1290
|
+
//# sourceMappingURL=modeldiag-YOQZHBQV.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
runPluginManagementCommand
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-3MWUZMOL.js";
|
|
4
4
|
import {
|
|
5
5
|
activeProfileConfigPath
|
|
6
6
|
} from "./chunk-DPJZQCVP.js";
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
pluginCmd,
|
|
38
38
|
usageCmd
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=plugin-usage-
|
|
40
|
+
//# sourceMappingURL=plugin-usage-EOG4ET4S.js.map
|
|
@@ -7,8 +7,8 @@ import {
|
|
|
7
7
|
pluginNameFromSpec,
|
|
8
8
|
setupPlugins,
|
|
9
9
|
warnIfDeprecatedPluginName
|
|
10
|
-
} from "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-4WJKVQJY.js";
|
|
11
|
+
import "./chunk-3MWUZMOL.js";
|
|
12
12
|
import "./chunk-TYO2OVAD.js";
|
|
13
13
|
import "./chunk-7OCVIDC7.js";
|
|
14
14
|
export {
|
|
@@ -21,4 +21,4 @@ export {
|
|
|
21
21
|
setupPlugins,
|
|
22
22
|
warnIfDeprecatedPluginName
|
|
23
23
|
};
|
|
24
|
-
//# sourceMappingURL=plugins-
|
|
24
|
+
//# sourceMappingURL=plugins-56CMWMPR.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
visibleModelIds
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-N7ULWBO5.js";
|
|
4
4
|
import {
|
|
5
5
|
mutateConfigProviders
|
|
6
6
|
} from "./chunk-PJHET45A.js";
|
|
@@ -127,7 +127,7 @@ var modelsCmd = async (args, deps) => {
|
|
|
127
127
|
return 1;
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
|
-
const flags =
|
|
130
|
+
const flags = subcommandFlags(args, deps);
|
|
131
131
|
const search = typeof flags["search"] === "string" ? flags["search"].toLowerCase() : "";
|
|
132
132
|
const perPage = Number(flags["per-page"]) > 0 ? Number(flags["per-page"]) : DEFAULT_PER_PAGE;
|
|
133
133
|
const page = Math.max(1, Number(flags["page"]) || 1);
|
|
@@ -138,10 +138,10 @@ var modelsCmd = async (args, deps) => {
|
|
|
138
138
|
);
|
|
139
139
|
return 1;
|
|
140
140
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
141
|
+
const { providerId: lookupId, provider } = await getCatalogProviderForConfigProvider(
|
|
142
|
+
providerId,
|
|
143
|
+
deps
|
|
144
|
+
);
|
|
145
145
|
if (!provider) {
|
|
146
146
|
deps.renderer.writeError(
|
|
147
147
|
lookupId !== providerId ? `Alias "${providerId}" points at catalog id "${lookupId}" which is not in the cache.` : `Provider "${providerId}" not in catalog.`
|
|
@@ -234,11 +234,33 @@ ${navLines.join(" \xB7 ")}
|
|
|
234
234
|
function aliasProviderId(configProviderId, cfg) {
|
|
235
235
|
return cfg?.type && cfg.type !== configProviderId ? cfg.type : configProviderId;
|
|
236
236
|
}
|
|
237
|
+
async function discoverProviderModels(deps, ids) {
|
|
238
|
+
if (!("mergeOverlay" in deps.modelsRegistry)) return void 0;
|
|
239
|
+
try {
|
|
240
|
+
const { discoverAndMergeProviders } = await import("./auto-discover-providers-DRW4OZHG.js");
|
|
241
|
+
await discoverAndMergeProviders({
|
|
242
|
+
config: deps.config,
|
|
243
|
+
registry: deps.modelsRegistry,
|
|
244
|
+
cacheDir: deps.paths.cacheDir
|
|
245
|
+
});
|
|
246
|
+
} catch {
|
|
247
|
+
return void 0;
|
|
248
|
+
}
|
|
249
|
+
for (const id of ids) {
|
|
250
|
+
const found = await deps.modelsRegistry.getProvider(id);
|
|
251
|
+
if (found) return found;
|
|
252
|
+
}
|
|
253
|
+
return void 0;
|
|
254
|
+
}
|
|
237
255
|
async function getCatalogProviderForConfigProvider(providerId, deps) {
|
|
238
256
|
const cfg = deps.config.providers?.[providerId];
|
|
239
257
|
const lookupId = aliasProviderId(providerId, cfg);
|
|
240
258
|
const provider = await deps.modelsRegistry.getProvider(lookupId);
|
|
241
|
-
return { providerId: lookupId, provider };
|
|
259
|
+
if (provider) return { providerId: lookupId, provider };
|
|
260
|
+
return {
|
|
261
|
+
providerId: lookupId,
|
|
262
|
+
provider: await discoverProviderModels(deps, [lookupId, providerId])
|
|
263
|
+
};
|
|
242
264
|
}
|
|
243
265
|
function uniqueStrings(values) {
|
|
244
266
|
return [...new Set(values.map((v) => v.trim()).filter(Boolean))];
|
|
@@ -478,13 +500,16 @@ function parseSizeFlag(raw) {
|
|
|
478
500
|
if (unit === "k") return Math.round(num * 1e3);
|
|
479
501
|
return Math.round(num);
|
|
480
502
|
}
|
|
503
|
+
function subcommandFlags(args, deps) {
|
|
504
|
+
return { ...deps.flags ?? {}, ...parseFlags(args) };
|
|
505
|
+
}
|
|
481
506
|
function parseBoolFlag(flags, key) {
|
|
482
507
|
if (flags[key] === true || flags[key] === "true") return true;
|
|
483
508
|
if (flags[`no-${key}`] !== void 0) return false;
|
|
484
509
|
return void 0;
|
|
485
510
|
}
|
|
486
511
|
async function modelsAdd(args, deps) {
|
|
487
|
-
const flags =
|
|
512
|
+
const flags = subcommandFlags(args, deps);
|
|
488
513
|
const pos = positionals(args);
|
|
489
514
|
const modelId = pos[0];
|
|
490
515
|
if (!modelId) {
|
|
@@ -586,4 +611,4 @@ export {
|
|
|
586
611
|
modelsCmd,
|
|
587
612
|
providersCmd
|
|
588
613
|
};
|
|
589
|
-
//# sourceMappingURL=providers-models-
|
|
614
|
+
//# sourceMappingURL=providers-models-K6RT7ABI.js.map
|
|
@@ -130,6 +130,25 @@ export interface SlashCommandContext {
|
|
|
130
130
|
* Get live coordinator status for /fleet. Returns null when no fleet is active.
|
|
131
131
|
*/
|
|
132
132
|
onFleetStatus?: (() => import('@wrongstack/core/types').CoordinatorStatus | null) | undefined;
|
|
133
|
+
/**
|
|
134
|
+
* Read-only concurrency + lifetime spawn budget for `/fleet status`.
|
|
135
|
+
* Returns null when no fleet host is available.
|
|
136
|
+
*/
|
|
137
|
+
onFleetBudget?: (() => {
|
|
138
|
+
maxConcurrent: number;
|
|
139
|
+
activeAgents: number;
|
|
140
|
+
maxSpawns: number;
|
|
141
|
+
usedSpawns: number;
|
|
142
|
+
remainingSpawns: number;
|
|
143
|
+
maxTokens?: number | undefined;
|
|
144
|
+
usedTokens?: number | undefined;
|
|
145
|
+
remainingTokens?: number | undefined;
|
|
146
|
+
maxCostUsd?: number | undefined;
|
|
147
|
+
usedCostUsd?: number | undefined;
|
|
148
|
+
remainingCostUsd?: number | undefined;
|
|
149
|
+
checkpointMaxSpawns?: number | undefined;
|
|
150
|
+
ceilingMismatch?: boolean | undefined;
|
|
151
|
+
} | null) | undefined;
|
|
133
152
|
/**
|
|
134
153
|
* Get fleet usage summary for /fleet usage.
|
|
135
154
|
*/
|
|
@@ -29,6 +29,23 @@ export interface SetupEventsDeps {
|
|
|
29
29
|
eventUnsubscribers: Array<() => void>;
|
|
30
30
|
globalConfigPath?: string | undefined;
|
|
31
31
|
onFleetBroadcaster?: ((fn: () => Promise<void>) => void) | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Live fleet budget snapshot (issue #323). Merged into
|
|
34
|
+
* `fleet.concurrency_update` so the WebUI can show used/remaining spawns
|
|
35
|
+
* without a probe spawn.
|
|
36
|
+
*/
|
|
37
|
+
getFleetBudget?: (() => {
|
|
38
|
+
maxSpawns?: number | undefined;
|
|
39
|
+
usedSpawns?: number | undefined;
|
|
40
|
+
remainingSpawns?: number | undefined;
|
|
41
|
+
maxConcurrent?: number | undefined;
|
|
42
|
+
activeAgents?: number | undefined;
|
|
43
|
+
maxSpawnsSource?: string | undefined;
|
|
44
|
+
maxConcurrentSource?: string | undefined;
|
|
45
|
+
effectiveSource?: string | undefined;
|
|
46
|
+
checkpointMaxSpawns?: number | undefined;
|
|
47
|
+
ceilingMismatch?: boolean | undefined;
|
|
48
|
+
} | null) | undefined;
|
|
32
49
|
}
|
|
33
50
|
/** CLI adapter around the canonical EventBus→WebSocket subscription graph. */
|
|
34
51
|
export declare function createSetupEvents(deps: SetupEventsDeps): () => void;
|
|
@@ -6,25 +6,42 @@ interface ConsoleTarget {
|
|
|
6
6
|
error: ConsoleMethod;
|
|
7
7
|
debug: ConsoleMethod;
|
|
8
8
|
}
|
|
9
|
-
export interface
|
|
10
|
-
maxLines?: number | undefined;
|
|
11
|
-
refreshMs?: number | undefined;
|
|
9
|
+
export interface QuietSurfaceConsoleOptions {
|
|
12
10
|
isTTY?: boolean | undefined;
|
|
11
|
+
/** Pass everything through untouched (the `WEBUI_VERBOSE=1` escape hatch). */
|
|
12
|
+
verbose?: boolean | undefined;
|
|
13
13
|
consoleTarget?: ConsoleTarget | undefined;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
clearIntervalFn?: ((timer: NodeJS.Timeout) => void) | undefined;
|
|
14
|
+
/** Ring-buffer size for {@link QuietSurfaceConsole.recent}. */
|
|
15
|
+
bufferLines?: number | undefined;
|
|
17
16
|
}
|
|
18
|
-
export interface
|
|
17
|
+
export interface QuietSurfaceConsole {
|
|
18
|
+
/** `false` when the console was left untouched (non-TTY or verbose). */
|
|
19
19
|
readonly enabled: boolean;
|
|
20
|
-
|
|
20
|
+
/** Chatter lines dropped from the terminal so far. */
|
|
21
|
+
readonly mutedCount: number;
|
|
22
|
+
/** Newest muted lines, oldest first — for a diagnostic dump on failure. */
|
|
23
|
+
recent(): readonly string[];
|
|
21
24
|
stop(): void;
|
|
22
25
|
}
|
|
23
26
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
+
* Keep an interactive WebUI/SimpleUI terminal quiet while the browser owns
|
|
28
|
+
* the output surface.
|
|
29
|
+
*
|
|
30
|
+
* Two things fill that terminal, and this handles both:
|
|
31
|
+
*
|
|
32
|
+
* - **Chatter.** Every `console.log`/`info`/`debug` from the server is
|
|
33
|
+
* progress the browser already shows. Those are dropped from the
|
|
34
|
+
* terminal (still counted, and the newest are kept in memory for a
|
|
35
|
+
* failure dump); `console.warn`/`error` are forwarded untouched.
|
|
36
|
+
*
|
|
37
|
+
* - **Repetition.** A warning that fires on a timer used to append an
|
|
38
|
+
* identical line every tick until the terminal was pages deep in the
|
|
39
|
+
* same text. Consecutive identical lines now collapse into a single
|
|
40
|
+
* `↑ previous line repeated N×` notice emitted when the streak ends.
|
|
41
|
+
*
|
|
42
|
+
* Non-TTY output is deliberately untouched so redirected output and log
|
|
43
|
+
* collectors retain the complete append-only log, as is `WEBUI_VERBOSE=1`.
|
|
27
44
|
*/
|
|
28
|
-
export declare function
|
|
45
|
+
export declare function startQuietSurfaceConsole(options?: QuietSurfaceConsoleOptions): QuietSurfaceConsole;
|
|
29
46
|
export {};
|
|
30
47
|
//# sourceMappingURL=terminal-log-view.d.ts.map
|
|
@@ -679,9 +679,23 @@ function createSetupEvents(deps) {
|
|
|
679
679
|
let fleetConcurrency = 0;
|
|
680
680
|
let fleetConcurrencyMax = 4;
|
|
681
681
|
const emitConcurrency = () => {
|
|
682
|
+
const budget = deps.getFleetBudget?.() ?? null;
|
|
682
683
|
deps.broadcast({
|
|
683
684
|
type: "fleet.concurrency_update",
|
|
684
|
-
payload: deps.sessionPayload({
|
|
685
|
+
payload: deps.sessionPayload({
|
|
686
|
+
fleetConcurrency: budget?.activeAgents ?? fleetConcurrency,
|
|
687
|
+
fleetConcurrencyMax: budget?.maxConcurrent ?? fleetConcurrencyMax,
|
|
688
|
+
...budget ? {
|
|
689
|
+
maxSpawns: budget.maxSpawns,
|
|
690
|
+
usedSpawns: budget.usedSpawns,
|
|
691
|
+
remainingSpawns: budget.remainingSpawns,
|
|
692
|
+
maxSpawnsSource: budget.maxSpawnsSource,
|
|
693
|
+
maxConcurrentSource: budget.maxConcurrentSource,
|
|
694
|
+
effectiveSource: budget.effectiveSource,
|
|
695
|
+
checkpointMaxSpawns: budget.checkpointMaxSpawns,
|
|
696
|
+
ceilingMismatch: budget.ceilingMismatch
|
|
697
|
+
} : {}
|
|
698
|
+
})
|
|
685
699
|
});
|
|
686
700
|
};
|
|
687
701
|
return function setupEvents() {
|
|
@@ -761,24 +775,35 @@ import {
|
|
|
761
775
|
|
|
762
776
|
// src/webui-server/terminal-log-view.ts
|
|
763
777
|
import { format } from "node:util";
|
|
764
|
-
var
|
|
765
|
-
var
|
|
766
|
-
var
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
778
|
+
var DEFAULT_BUFFER_LINES = 200;
|
|
779
|
+
var MUTED_LEVELS = ["log", "info", "debug"];
|
|
780
|
+
var FORWARDED_LEVELS = ["warn", "error"];
|
|
781
|
+
var DISABLED = {
|
|
782
|
+
enabled: false,
|
|
783
|
+
mutedCount: 0,
|
|
784
|
+
recent: () => [],
|
|
785
|
+
stop: () => {
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
function envVerbose() {
|
|
789
|
+
for (const name of ["WEBUI_VERBOSE", "WRONGSTACK_WEBUI_VERBOSE"]) {
|
|
790
|
+
const value = process.env[name]?.trim().toLowerCase();
|
|
791
|
+
if (value === "1" || value === "true" || value === "yes" || value === "on") return true;
|
|
773
792
|
}
|
|
774
|
-
|
|
775
|
-
|
|
793
|
+
return false;
|
|
794
|
+
}
|
|
795
|
+
function startQuietSurfaceConsole(options = {}) {
|
|
796
|
+
const isTTY = options.isTTY ?? process.stdout.isTTY === true;
|
|
797
|
+
const verbose = options.verbose ?? envVerbose();
|
|
798
|
+
if (!isTTY || verbose) return DISABLED;
|
|
799
|
+
const bufferLines = Math.max(1, Math.trunc(options.bufferLines ?? DEFAULT_BUFFER_LINES));
|
|
776
800
|
const target = options.consoleTarget ?? console;
|
|
777
|
-
const
|
|
778
|
-
|
|
779
|
-
const clearIntervalFn = options.clearIntervalFn ?? clearInterval;
|
|
780
|
-
const lines = [];
|
|
801
|
+
const muted = [];
|
|
802
|
+
let mutedCount = 0;
|
|
781
803
|
let stopped = false;
|
|
804
|
+
let lastLine = null;
|
|
805
|
+
let lastEmit = null;
|
|
806
|
+
let repeats = 0;
|
|
782
807
|
const originals = {
|
|
783
808
|
log: target.log,
|
|
784
809
|
info: target.info,
|
|
@@ -787,35 +812,47 @@ function startBoundedTerminalLogView(options = {}) {
|
|
|
787
812
|
debug: target.debug
|
|
788
813
|
};
|
|
789
814
|
const wrappers = {};
|
|
790
|
-
const
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
}
|
|
815
|
+
const flushRepeats = () => {
|
|
816
|
+
if (repeats === 0 || !lastEmit) return;
|
|
817
|
+
const count = repeats;
|
|
818
|
+
repeats = 0;
|
|
819
|
+
lastEmit.call(target, ` \u2191 previous line repeated ${count}\xD7`);
|
|
795
820
|
};
|
|
796
|
-
for (const level of
|
|
821
|
+
for (const level of MUTED_LEVELS) {
|
|
822
|
+
const wrapper = (...args) => {
|
|
823
|
+
mutedCount += 1;
|
|
824
|
+
muted.push(format(...args));
|
|
825
|
+
if (muted.length > bufferLines) muted.shift();
|
|
826
|
+
};
|
|
827
|
+
wrappers[level] = wrapper;
|
|
828
|
+
target[level] = wrapper;
|
|
829
|
+
}
|
|
830
|
+
for (const level of FORWARDED_LEVELS) {
|
|
797
831
|
const original = originals[level];
|
|
798
832
|
const wrapper = (...args) => {
|
|
799
|
-
|
|
833
|
+
const line = format(...args);
|
|
834
|
+
if (line === lastLine) {
|
|
835
|
+
repeats += 1;
|
|
836
|
+
return;
|
|
837
|
+
}
|
|
838
|
+
flushRepeats();
|
|
839
|
+
lastLine = line;
|
|
840
|
+
lastEmit = original;
|
|
800
841
|
original.apply(target, args);
|
|
801
842
|
};
|
|
802
843
|
wrappers[level] = wrapper;
|
|
803
844
|
target[level] = wrapper;
|
|
804
845
|
}
|
|
805
|
-
const redraw = () => {
|
|
806
|
-
if (stopped || lines.length === 0) return;
|
|
807
|
-
write(`${CLEAR_VISIBLE_TERMINAL}${lines.join("\n")}
|
|
808
|
-
`);
|
|
809
|
-
};
|
|
810
|
-
const timer = setIntervalFn(redraw, refreshMs);
|
|
811
|
-
timer.unref?.();
|
|
812
846
|
return {
|
|
813
847
|
enabled: true,
|
|
814
|
-
|
|
848
|
+
get mutedCount() {
|
|
849
|
+
return mutedCount;
|
|
850
|
+
},
|
|
851
|
+
recent: () => [...muted],
|
|
815
852
|
stop: () => {
|
|
816
853
|
if (stopped) return;
|
|
817
854
|
stopped = true;
|
|
818
|
-
|
|
855
|
+
flushRepeats();
|
|
819
856
|
for (const level of Object.keys(originals)) {
|
|
820
857
|
if (target[level] === wrappers[level]) target[level] = originals[level];
|
|
821
858
|
}
|
|
@@ -825,6 +862,7 @@ function startBoundedTerminalLogView(options = {}) {
|
|
|
825
862
|
|
|
826
863
|
// src/webui-server.ts
|
|
827
864
|
async function runWebUI(opts) {
|
|
865
|
+
const terminalLogView = startQuietSurfaceConsole();
|
|
828
866
|
const trustBoundary = opts.trustBoundary ?? createCompatibilityTrustBoundary({ policyId: "cli-webui-trusted-host-compat-v1" });
|
|
829
867
|
const host = opts.host ?? process.env["WEBUI_HOST"] ?? process.env["WS_HOST"] ?? "127.0.0.1";
|
|
830
868
|
const publicUrl = opts.publicUrl ?? process.env["WEBUI_PUBLIC_URL"];
|
|
@@ -1060,7 +1098,10 @@ async function runWebUI(opts) {
|
|
|
1060
1098
|
publicUrl,
|
|
1061
1099
|
projectRoot: opts.projectRoot,
|
|
1062
1100
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1063
|
-
registryBaseDir
|
|
1101
|
+
registryBaseDir,
|
|
1102
|
+
// Lets a same-project TUI/REPL authenticate `POST /api/fleet/ping` now
|
|
1103
|
+
// that the API requires a token on every bind (H3).
|
|
1104
|
+
authToken: wsToken
|
|
1064
1105
|
});
|
|
1065
1106
|
}
|
|
1066
1107
|
const eventUnsubscribers = [];
|
|
@@ -1096,7 +1137,8 @@ async function runWebUI(opts) {
|
|
|
1096
1137
|
globalConfigPath: path5.join(globalRoot, "config.json"),
|
|
1097
1138
|
onFleetBroadcaster: (fn) => {
|
|
1098
1139
|
fleetBroadcastCli = fn;
|
|
1099
|
-
}
|
|
1140
|
+
},
|
|
1141
|
+
...opts.getFleetBudget ? { getFleetBudget: opts.getFleetBudget } : {}
|
|
1100
1142
|
});
|
|
1101
1143
|
const wsHandlerCtx = {
|
|
1102
1144
|
providerStore: createProviderConfigStore(
|
|
@@ -1172,7 +1214,10 @@ async function runWebUI(opts) {
|
|
|
1172
1214
|
...snapshot.baseUrl !== void 0 ? { baseUrl: snapshot.baseUrl } : {}
|
|
1173
1215
|
};
|
|
1174
1216
|
const oldMax = opts.agent.ctx.provider.capabilities?.maxContext;
|
|
1175
|
-
const prov = makeProviderFromConfig(activeId, {
|
|
1217
|
+
const prov = makeProviderFromConfig(activeId, {
|
|
1218
|
+
...newCfg,
|
|
1219
|
+
type: newCfg.type ?? activeId
|
|
1220
|
+
});
|
|
1176
1221
|
if (oldMax != null && prov.capabilities) prov.capabilities.maxContext = oldMax;
|
|
1177
1222
|
opts.agent.ctx.provider = prov;
|
|
1178
1223
|
console.log(`[WebUI] Provider credentials reloaded from config.json (${activeId})`);
|
|
@@ -1352,7 +1397,6 @@ async function runWebUI(opts) {
|
|
|
1352
1397
|
kanbanHostRoutes,
|
|
1353
1398
|
statusTracker: opts.statusTracker
|
|
1354
1399
|
});
|
|
1355
|
-
const terminalLogView = startBoundedTerminalLogView();
|
|
1356
1400
|
const stopped = new Promise((resolve) => {
|
|
1357
1401
|
let listeningAnnounced = false;
|
|
1358
1402
|
const announceListening = () => {
|
|
@@ -1431,6 +1475,28 @@ async function runWebUI(opts) {
|
|
|
1431
1475
|
flushAllStreamBuffers();
|
|
1432
1476
|
for (const unsub of eventUnsubscribers) unsub();
|
|
1433
1477
|
},
|
|
1478
|
+
// Issue #322: kill session-owned children (MCP, bash/exec, tool-spawned
|
|
1479
|
+
// dev servers) before closing the listen socket so Windows process trees
|
|
1480
|
+
// do not outlive the parent and strand the event loop on open stdio.
|
|
1481
|
+
stopOwnedChildren: async () => {
|
|
1482
|
+
try {
|
|
1483
|
+
const { getProcessRegistry } = await import("@wrongstack/tools");
|
|
1484
|
+
getProcessRegistry().killAll({ force: true, includeProtected: true });
|
|
1485
|
+
} catch (err) {
|
|
1486
|
+
console.debug(
|
|
1487
|
+
`[webui-server] process-registry killAll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1488
|
+
);
|
|
1489
|
+
}
|
|
1490
|
+
if (opts.mcpRegistry) {
|
|
1491
|
+
try {
|
|
1492
|
+
await opts.mcpRegistry.stopAll();
|
|
1493
|
+
} catch (err) {
|
|
1494
|
+
console.debug(
|
|
1495
|
+
`[webui-server] mcpRegistry.stopAll failed: ${err instanceof Error ? err.message : String(err)}`
|
|
1496
|
+
);
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
},
|
|
1434
1500
|
disposeResources: () => {
|
|
1435
1501
|
credentialWatcherClose?.();
|
|
1436
1502
|
credentialWatcherClose = void 0;
|
|
@@ -1455,8 +1521,13 @@ async function runWebUI(opts) {
|
|
|
1455
1521
|
pid: process.pid,
|
|
1456
1522
|
registryBaseDir,
|
|
1457
1523
|
onStopped: () => {
|
|
1458
|
-
terminalLogView.
|
|
1524
|
+
const muted = terminalLogView.mutedCount;
|
|
1459
1525
|
terminalLogView.stop();
|
|
1526
|
+
if (muted > 0) {
|
|
1527
|
+
console.log(
|
|
1528
|
+
`[WebUI] ${muted} progress line(s) kept out of this terminal \u2014 set WEBUI_VERBOSE=1 to see them.`
|
|
1529
|
+
);
|
|
1530
|
+
}
|
|
1460
1531
|
opts.onExit?.();
|
|
1461
1532
|
resolve();
|
|
1462
1533
|
}
|
|
@@ -1487,4 +1558,4 @@ async function runWebUI(opts) {
|
|
|
1487
1558
|
export {
|
|
1488
1559
|
runWebUI
|
|
1489
1560
|
};
|
|
1490
|
-
//# sourceMappingURL=webui-server-
|
|
1561
|
+
//# sourceMappingURL=webui-server-G3EKFXLL.js.map
|
|
@@ -36,6 +36,22 @@ export interface CliWebUIOptions {
|
|
|
36
36
|
surface?: 'webui' | 'simpleui' | undefined;
|
|
37
37
|
/** Fixed access token/password. Defaults to WEBUI_TOKEN or random per process. */
|
|
38
38
|
accessToken?: string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* Live fleet concurrency + lifetime spawn budget for WebUI (issue #323).
|
|
41
|
+
* Merged into `fleet.concurrency_update` broadcasts.
|
|
42
|
+
*/
|
|
43
|
+
getFleetBudget?: (() => {
|
|
44
|
+
maxSpawns?: number | undefined;
|
|
45
|
+
usedSpawns?: number | undefined;
|
|
46
|
+
remainingSpawns?: number | undefined;
|
|
47
|
+
maxConcurrent?: number | undefined;
|
|
48
|
+
activeAgents?: number | undefined;
|
|
49
|
+
maxSpawnsSource?: string | undefined;
|
|
50
|
+
maxConcurrentSource?: string | undefined;
|
|
51
|
+
effectiveSource?: string | undefined;
|
|
52
|
+
checkpointMaxSpawns?: number | undefined;
|
|
53
|
+
ceilingMismatch?: boolean | undefined;
|
|
54
|
+
} | null) | undefined;
|
|
39
55
|
/** Browser-facing HTTP URL, used when WebUI is exposed behind a tunnel/proxy. */
|
|
40
56
|
publicUrl?: string | undefined;
|
|
41
57
|
/** Browser-facing WebSocket URL injected into the frontend. */
|
|
@@ -49,6 +49,9 @@ export interface BrainOrchestrationDeps {
|
|
|
49
49
|
stateCheckpointPath: string | undefined;
|
|
50
50
|
fleetRootForPromotion: string;
|
|
51
51
|
maxConcurrent: number | undefined;
|
|
52
|
+
maxSpawns?: number | undefined;
|
|
53
|
+
maxConcurrentSource?: import('../fleet/budget-source.js').FleetBudgetSource | undefined;
|
|
54
|
+
maxSpawnsSource?: import('../fleet/budget-source.js').FleetBudgetSource | undefined;
|
|
52
55
|
effectiveMaxContextRef: {
|
|
53
56
|
current: number;
|
|
54
57
|
};
|
|
@@ -23,6 +23,10 @@ export interface DirectorAutonomyResult {
|
|
|
23
23
|
director: Director | null;
|
|
24
24
|
directorMode: boolean;
|
|
25
25
|
maxConcurrent: number | undefined;
|
|
26
|
+
/** Lifetime spawn ceiling resolved from flag/env/profile/default. */
|
|
27
|
+
maxSpawns: number;
|
|
28
|
+
maxConcurrentSource: import('../fleet/budget-source.js').FleetBudgetSource;
|
|
29
|
+
maxSpawnsSource: import('../fleet/budget-source.js').FleetBudgetSource;
|
|
26
30
|
autonomyMode: import('../services/autonomy-mode.js').AutonomyMode;
|
|
27
31
|
nextPredictEnabled: boolean;
|
|
28
32
|
currentSuggestions: string[];
|