@wrongstack/webui-server 0.301.0 → 0.302.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/index.js +147 -64
- package/dist/server/embedded-lifecycle.d.ts +17 -2
- package/dist/server/entry.js +37 -37
- package/dist/server/index.d.ts +1 -1
- package/dist/server/instance-registry.d.ts +51 -2
- package/package.json +11 -11
package/dist/index.js
CHANGED
|
@@ -6463,28 +6463,28 @@ async function restartMailboxServer(projectRoot) {
|
|
|
6463
6463
|
var RESTART_POLL_INTERVAL_MS = 250;
|
|
6464
6464
|
var RESTART_DEADLINE_MS = 3e3;
|
|
6465
6465
|
function isEndpointAlive(endpoint) {
|
|
6466
|
-
return new Promise((
|
|
6466
|
+
return new Promise((resolve17) => {
|
|
6467
6467
|
const sock = net.createConnection(endpoint);
|
|
6468
6468
|
const timer = setTimeout(() => {
|
|
6469
6469
|
sock.destroy();
|
|
6470
|
-
|
|
6470
|
+
resolve17(false);
|
|
6471
6471
|
}, 500);
|
|
6472
6472
|
timer.unref?.();
|
|
6473
6473
|
sock.once("connect", () => {
|
|
6474
6474
|
clearTimeout(timer);
|
|
6475
6475
|
sock.destroy();
|
|
6476
|
-
|
|
6476
|
+
resolve17(true);
|
|
6477
6477
|
});
|
|
6478
6478
|
sock.once("error", () => {
|
|
6479
6479
|
clearTimeout(timer);
|
|
6480
6480
|
sock.destroy();
|
|
6481
|
-
|
|
6481
|
+
resolve17(false);
|
|
6482
6482
|
});
|
|
6483
6483
|
});
|
|
6484
6484
|
}
|
|
6485
6485
|
async function waitForShutdown(probe) {
|
|
6486
6486
|
if (!probe) {
|
|
6487
|
-
await new Promise((
|
|
6487
|
+
await new Promise((resolve17) => setTimeout(resolve17, RESTART_POLL_INTERVAL_MS));
|
|
6488
6488
|
return;
|
|
6489
6489
|
}
|
|
6490
6490
|
const deadline = Date.now() + RESTART_DEADLINE_MS;
|
|
@@ -6495,7 +6495,7 @@ async function waitForShutdown(probe) {
|
|
|
6495
6495
|
} catch {
|
|
6496
6496
|
return;
|
|
6497
6497
|
}
|
|
6498
|
-
await new Promise((
|
|
6498
|
+
await new Promise((resolve17) => setTimeout(resolve17, RESTART_POLL_INTERVAL_MS));
|
|
6499
6499
|
}
|
|
6500
6500
|
}
|
|
6501
6501
|
function failureService(id, label, required, mode, error2, latencyMs) {
|
|
@@ -6658,9 +6658,9 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
6658
6658
|
const cwd = projectRoot || void 0;
|
|
6659
6659
|
try {
|
|
6660
6660
|
const { execFile: ef } = await import("node:child_process");
|
|
6661
|
-
const git = (args) => new Promise((
|
|
6661
|
+
const git = (args) => new Promise((resolve17) => {
|
|
6662
6662
|
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
6663
|
-
|
|
6663
|
+
resolve17(err ? "" : stdout.trim());
|
|
6664
6664
|
});
|
|
6665
6665
|
});
|
|
6666
6666
|
const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
|
|
@@ -6686,12 +6686,12 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
6686
6686
|
function makeGit(cwd) {
|
|
6687
6687
|
return async (args) => {
|
|
6688
6688
|
const { execFile: ef } = await import("node:child_process");
|
|
6689
|
-
return new Promise((
|
|
6689
|
+
return new Promise((resolve17) => {
|
|
6690
6690
|
ef(
|
|
6691
6691
|
"git",
|
|
6692
6692
|
args,
|
|
6693
6693
|
{ cwd, timeout: 5e3, maxBuffer: 1024 * 1024 * 16 },
|
|
6694
|
-
(err, stdout) =>
|
|
6694
|
+
(err, stdout) => resolve17(err ? "" : stdout)
|
|
6695
6695
|
);
|
|
6696
6696
|
});
|
|
6697
6697
|
};
|
|
@@ -6856,7 +6856,7 @@ import { execFile } from "node:child_process";
|
|
|
6856
6856
|
var GIT_TIMEOUT_MS = 1e4;
|
|
6857
6857
|
var GIT_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
6858
6858
|
function gitStdout(cwd, args) {
|
|
6859
|
-
return new Promise((
|
|
6859
|
+
return new Promise((resolve17) => {
|
|
6860
6860
|
execFile(
|
|
6861
6861
|
"git",
|
|
6862
6862
|
[...args],
|
|
@@ -6867,7 +6867,7 @@ function gitStdout(cwd, args) {
|
|
|
6867
6867
|
timeout: GIT_TIMEOUT_MS,
|
|
6868
6868
|
maxBuffer: GIT_MAX_OUTPUT_BYTES
|
|
6869
6869
|
},
|
|
6870
|
-
(error2, stdout) =>
|
|
6870
|
+
(error2, stdout) => resolve17(error2 ? null : stdout)
|
|
6871
6871
|
);
|
|
6872
6872
|
});
|
|
6873
6873
|
}
|
|
@@ -7158,14 +7158,14 @@ var GoalWebSocketHandler = class {
|
|
|
7158
7158
|
const cwd = env?.cwd ?? this.projectRoot;
|
|
7159
7159
|
try {
|
|
7160
7160
|
const { execFile: execFile2 } = await import("node:child_process");
|
|
7161
|
-
const result = await new Promise((
|
|
7161
|
+
const result = await new Promise((resolve17) => {
|
|
7162
7162
|
const npxCommand = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
7163
7163
|
execFile2(npxCommand, ["tsc", "--noEmit"], { cwd, timeout: 6e4 }, (err, stdout, stderr) => {
|
|
7164
7164
|
if (err && err.code === "ENOENT") {
|
|
7165
|
-
|
|
7165
|
+
resolve17("[verify] tsc not found \u2014 skipping");
|
|
7166
7166
|
return;
|
|
7167
7167
|
}
|
|
7168
|
-
|
|
7168
|
+
resolve17(stdout + stderr);
|
|
7169
7169
|
});
|
|
7170
7170
|
});
|
|
7171
7171
|
if (result.includes("[verify]") || result.trim().length === 0) {
|
|
@@ -7903,7 +7903,7 @@ function pushEvent(event) {
|
|
|
7903
7903
|
}
|
|
7904
7904
|
}
|
|
7905
7905
|
function parseBody(req) {
|
|
7906
|
-
return new Promise((
|
|
7906
|
+
return new Promise((resolve17, reject) => {
|
|
7907
7907
|
let body = "";
|
|
7908
7908
|
let bodyBytes = 0;
|
|
7909
7909
|
let tooLarge = false;
|
|
@@ -7923,7 +7923,7 @@ function parseBody(req) {
|
|
|
7923
7923
|
return;
|
|
7924
7924
|
}
|
|
7925
7925
|
try {
|
|
7926
|
-
|
|
7926
|
+
resolve17(JSON.parse(body));
|
|
7927
7927
|
} catch {
|
|
7928
7928
|
reject(new Error("Invalid JSON"));
|
|
7929
7929
|
}
|
|
@@ -8018,7 +8018,7 @@ import * as path10 from "node:path";
|
|
|
8018
8018
|
import { runDeadCodeScan } from "@wrongstack/tools/codebase-index";
|
|
8019
8019
|
var MAX_BODY_BYTES = 10 * 1024 * 1024;
|
|
8020
8020
|
function readJsonBody(req) {
|
|
8021
|
-
return new Promise((
|
|
8021
|
+
return new Promise((resolve17, reject) => {
|
|
8022
8022
|
const chunks = [];
|
|
8023
8023
|
let total = 0;
|
|
8024
8024
|
req.on("data", (chunk) => {
|
|
@@ -8030,7 +8030,7 @@ function readJsonBody(req) {
|
|
|
8030
8030
|
}
|
|
8031
8031
|
chunks.push(chunk);
|
|
8032
8032
|
});
|
|
8033
|
-
req.on("end", () =>
|
|
8033
|
+
req.on("end", () => resolve17(Buffer.concat(chunks).toString("utf8")));
|
|
8034
8034
|
req.on("error", (err) => reject(err));
|
|
8035
8035
|
});
|
|
8036
8036
|
}
|
|
@@ -8476,7 +8476,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
8476
8476
|
}
|
|
8477
8477
|
}
|
|
8478
8478
|
function readJsonBody2(req) {
|
|
8479
|
-
return new Promise((
|
|
8479
|
+
return new Promise((resolve17, reject) => {
|
|
8480
8480
|
const contentType = (req.headers["content-type"] ?? "").split(";")[0]?.trim().toLowerCase();
|
|
8481
8481
|
if (contentType !== "application/json") {
|
|
8482
8482
|
reject(new Error(`Unsupported Content-Type: ${contentType || "(absent)"}`));
|
|
@@ -8492,7 +8492,7 @@ function readJsonBody2(req) {
|
|
|
8492
8492
|
});
|
|
8493
8493
|
req.on("end", () => {
|
|
8494
8494
|
try {
|
|
8495
|
-
|
|
8495
|
+
resolve17(data ? JSON.parse(data) : {});
|
|
8496
8496
|
} catch (err) {
|
|
8497
8497
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
8498
8498
|
}
|
|
@@ -8768,14 +8768,14 @@ async function readJsonBody3(res, req) {
|
|
|
8768
8768
|
});
|
|
8769
8769
|
return null;
|
|
8770
8770
|
}
|
|
8771
|
-
return new Promise((
|
|
8771
|
+
return new Promise((resolve17) => {
|
|
8772
8772
|
let data = "";
|
|
8773
8773
|
let failed = false;
|
|
8774
8774
|
const fail2 = (message) => {
|
|
8775
8775
|
if (failed) return;
|
|
8776
8776
|
failed = true;
|
|
8777
8777
|
sendJson2(res, 400, { error: { code: "INVALID_BODY", message } });
|
|
8778
|
-
|
|
8778
|
+
resolve17(null);
|
|
8779
8779
|
};
|
|
8780
8780
|
req.on("data", (chunk) => {
|
|
8781
8781
|
if (failed) return;
|
|
@@ -8788,7 +8788,7 @@ async function readJsonBody3(res, req) {
|
|
|
8788
8788
|
req.on("end", () => {
|
|
8789
8789
|
if (failed) return;
|
|
8790
8790
|
try {
|
|
8791
|
-
|
|
8791
|
+
resolve17(data.trim().length === 0 ? {} : JSON.parse(data));
|
|
8792
8792
|
} catch {
|
|
8793
8793
|
fail2("Request body is not valid JSON");
|
|
8794
8794
|
}
|
|
@@ -9627,7 +9627,7 @@ function strictDecodeParam(segment, res) {
|
|
|
9627
9627
|
function createHttpServer(opts) {
|
|
9628
9628
|
const port = opts.port ?? Number.parseInt(process.env["PORT"] ?? "3456", 10);
|
|
9629
9629
|
const distDir = path13.resolve(opts.distDir);
|
|
9630
|
-
const requireAccessToken =
|
|
9630
|
+
const requireAccessToken = Boolean(opts.requireToken) || !isLoopbackBind(opts.host);
|
|
9631
9631
|
const secureCookies = opts.secureCookies ?? (opts.publicWsUrl?.trim().toLowerCase().startsWith("wss:") ?? false);
|
|
9632
9632
|
const trustedHostnames = (() => {
|
|
9633
9633
|
const names = [...opts.allowedHostnames ?? []];
|
|
@@ -10173,10 +10173,78 @@ function createHttpServer(opts) {
|
|
|
10173
10173
|
}
|
|
10174
10174
|
|
|
10175
10175
|
// src/server/instance-registry.ts
|
|
10176
|
+
import * as fs11 from "node:fs/promises";
|
|
10176
10177
|
import * as os from "node:os";
|
|
10177
10178
|
import * as path14 from "node:path";
|
|
10178
|
-
import * as fs11 from "node:fs/promises";
|
|
10179
10179
|
import { atomicWrite as atomicWrite4 } from "@wrongstack/core/utils";
|
|
10180
|
+
function normalizeRoot(root) {
|
|
10181
|
+
const resolved = path14.resolve(root);
|
|
10182
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
10183
|
+
}
|
|
10184
|
+
function isLiveSessionStatus(status) {
|
|
10185
|
+
return status === "active" || status === "idle";
|
|
10186
|
+
}
|
|
10187
|
+
function instanceRole(instance) {
|
|
10188
|
+
return instance.role ?? "standalone";
|
|
10189
|
+
}
|
|
10190
|
+
function resolveAttachability(input) {
|
|
10191
|
+
const { session, instance } = input;
|
|
10192
|
+
if (!isLiveSessionStatus(session.status)) {
|
|
10193
|
+
return { attachable: false, degradedReason: "session-not-live" };
|
|
10194
|
+
}
|
|
10195
|
+
if (!instance) {
|
|
10196
|
+
return { attachable: false, degradedReason: "live-session-no-webui-endpoint" };
|
|
10197
|
+
}
|
|
10198
|
+
if (instance.pid !== session.pid) {
|
|
10199
|
+
return { attachable: false, degradedReason: "endpoint-owner-mismatch" };
|
|
10200
|
+
}
|
|
10201
|
+
if (!instance.sessionId) {
|
|
10202
|
+
return { attachable: false, instance, degradedReason: "endpoint-missing-session-id" };
|
|
10203
|
+
}
|
|
10204
|
+
if (instance.sessionId !== session.sessionId) {
|
|
10205
|
+
return { attachable: false, instance, degradedReason: "endpoint-session-mismatch" };
|
|
10206
|
+
}
|
|
10207
|
+
if (instanceRole(instance) !== "session-child") {
|
|
10208
|
+
return { attachable: false, instance, degradedReason: "endpoint-not-session-child" };
|
|
10209
|
+
}
|
|
10210
|
+
if (instance.attachable === false) {
|
|
10211
|
+
return { attachable: false, instance, degradedReason: "endpoint-not-attachable" };
|
|
10212
|
+
}
|
|
10213
|
+
return {
|
|
10214
|
+
attachable: true,
|
|
10215
|
+
endpoint: {
|
|
10216
|
+
host: instance.host,
|
|
10217
|
+
httpPort: instance.httpPort,
|
|
10218
|
+
url: instance.url,
|
|
10219
|
+
...instance.authToken ? { authToken: instance.authToken } : {}
|
|
10220
|
+
}
|
|
10221
|
+
};
|
|
10222
|
+
}
|
|
10223
|
+
function joinSessionRegistryWithWebUIInstances(input) {
|
|
10224
|
+
const targetRoot = input.projectRoot ? normalizeRoot(input.projectRoot) : void 0;
|
|
10225
|
+
const sessions = input.sessions.filter((session) => {
|
|
10226
|
+
if (input.projectSlug && session.projectSlug !== input.projectSlug) return false;
|
|
10227
|
+
if (targetRoot && normalizeRoot(session.projectRoot) !== targetRoot) return false;
|
|
10228
|
+
return true;
|
|
10229
|
+
});
|
|
10230
|
+
return sessions.map((session) => {
|
|
10231
|
+
const instance = input.instances.find((candidate) => candidate.sessionId === session.sessionId) ?? input.instances.find(
|
|
10232
|
+
(candidate) => candidate.pid === session.pid && normalizeRoot(candidate.projectRoot) === normalizeRoot(session.projectRoot)
|
|
10233
|
+
);
|
|
10234
|
+
const resolved = resolveAttachability({ session, instance });
|
|
10235
|
+
return {
|
|
10236
|
+
sessionId: session.sessionId,
|
|
10237
|
+
projectRoot: session.projectRoot,
|
|
10238
|
+
workingDir: session.workingDir,
|
|
10239
|
+
sessionPid: session.pid,
|
|
10240
|
+
status: session.status,
|
|
10241
|
+
...instance ? { instance } : {},
|
|
10242
|
+
...resolved.endpoint ? { endpoint: resolved.endpoint } : {},
|
|
10243
|
+
attachable: resolved.attachable,
|
|
10244
|
+
...resolved.degradedReason ? { degradedReason: resolved.degradedReason } : {}
|
|
10245
|
+
};
|
|
10246
|
+
});
|
|
10247
|
+
}
|
|
10180
10248
|
function defaultBaseDir() {
|
|
10181
10249
|
return path14.join(os.homedir(), ".wrongstack");
|
|
10182
10250
|
}
|
|
@@ -13723,16 +13791,16 @@ function getSurfaceDefaultPorts(surface) {
|
|
|
13723
13791
|
return { http: SURFACE_DEFAULT_PORTS[surface].http };
|
|
13724
13792
|
}
|
|
13725
13793
|
function isPortFree(host, port) {
|
|
13726
|
-
return new Promise((
|
|
13794
|
+
return new Promise((resolve17) => {
|
|
13727
13795
|
const srv = net2.createServer();
|
|
13728
|
-
srv.once("error", () =>
|
|
13796
|
+
srv.once("error", () => resolve17(false));
|
|
13729
13797
|
srv.once("listening", () => {
|
|
13730
|
-
srv.close(() =>
|
|
13798
|
+
srv.close(() => resolve17(true));
|
|
13731
13799
|
});
|
|
13732
13800
|
try {
|
|
13733
13801
|
srv.listen(port, host);
|
|
13734
13802
|
} catch {
|
|
13735
|
-
|
|
13803
|
+
resolve17(false);
|
|
13736
13804
|
}
|
|
13737
13805
|
});
|
|
13738
13806
|
}
|
|
@@ -13888,7 +13956,7 @@ async function startStaticServe(opts, deps2 = {}) {
|
|
|
13888
13956
|
return { server, port: opts.httpPort };
|
|
13889
13957
|
}
|
|
13890
13958
|
function runPnpmBuild(cwd, workspace, timeoutMs) {
|
|
13891
|
-
return new Promise((
|
|
13959
|
+
return new Promise((resolve17, reject) => {
|
|
13892
13960
|
const child = spawn2("pnpm", ["--filter", workspace, "build"], {
|
|
13893
13961
|
cwd,
|
|
13894
13962
|
shell: process.platform === "win32",
|
|
@@ -13906,7 +13974,7 @@ function runPnpmBuild(cwd, workspace, timeoutMs) {
|
|
|
13906
13974
|
});
|
|
13907
13975
|
child.once("close", (code) => {
|
|
13908
13976
|
clearTimeout(timer);
|
|
13909
|
-
if (code === 0)
|
|
13977
|
+
if (code === 0) resolve17();
|
|
13910
13978
|
else reject(new Error(`pnpm build exited with code ${String(code)}`));
|
|
13911
13979
|
});
|
|
13912
13980
|
});
|
|
@@ -13970,27 +14038,41 @@ function formatExternalAccessUrls(opts) {
|
|
|
13970
14038
|
}
|
|
13971
14039
|
|
|
13972
14040
|
// src/server/embedded-lifecycle.ts
|
|
13973
|
-
function registerWebuiInstance(p, deps2 = {}) {
|
|
14041
|
+
async function registerWebuiInstance(p, deps2 = {}) {
|
|
13974
14042
|
const register = deps2.registerFn ?? registerInstance;
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
|
|
13978
|
-
|
|
13979
|
-
|
|
13980
|
-
|
|
13981
|
-
projectRoot: p.projectRoot,
|
|
13982
|
-
projectName: path16.basename(p.projectRoot) || p.projectRoot,
|
|
13983
|
-
startedAt: p.startedAt,
|
|
13984
|
-
url: buildWebUIAccessUrl({
|
|
14043
|
+
try {
|
|
14044
|
+
await register(
|
|
14045
|
+
{
|
|
14046
|
+
pid: p.pid,
|
|
14047
|
+
surface: p.surface,
|
|
14048
|
+
httpPort: p.httpPort,
|
|
13985
14049
|
host: p.host,
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
|
|
14050
|
+
projectRoot: p.projectRoot,
|
|
14051
|
+
projectName: path16.basename(p.projectRoot) || p.projectRoot,
|
|
14052
|
+
startedAt: p.startedAt,
|
|
14053
|
+
url: buildWebUIAccessUrl({
|
|
14054
|
+
host: p.host,
|
|
14055
|
+
port: p.httpPort,
|
|
14056
|
+
publicUrl: p.publicUrl
|
|
14057
|
+
}),
|
|
14058
|
+
...p.authToken ? { authToken: p.authToken } : {},
|
|
14059
|
+
...p.role ? { role: p.role } : {},
|
|
14060
|
+
...p.sessionId ? { sessionId: p.sessionId } : {},
|
|
14061
|
+
...p.parentPid !== void 0 ? { parentPid: p.parentPid } : {},
|
|
14062
|
+
...p.parentShellId ? { parentShellId: p.parentShellId } : {},
|
|
14063
|
+
...p.runtimeId ? { runtimeId: p.runtimeId } : {},
|
|
14064
|
+
...p.attachable !== void 0 ? { attachable: p.attachable } : {},
|
|
14065
|
+
...p.authToken ? { auth: { scheme: "registry-token", tokenPresent: true } } : {},
|
|
14066
|
+
...p.lastReadyAt ? { lastReadyAt: p.lastReadyAt } : {},
|
|
14067
|
+
...p.protocolVersion !== void 0 ? { protocolVersion: p.protocolVersion } : {},
|
|
14068
|
+
...p.capabilities ? { capabilities: p.capabilities } : {}
|
|
14069
|
+
},
|
|
14070
|
+
p.registryBaseDir
|
|
14071
|
+
);
|
|
14072
|
+
return true;
|
|
14073
|
+
} catch {
|
|
14074
|
+
return false;
|
|
14075
|
+
}
|
|
13994
14076
|
}
|
|
13995
14077
|
function announceWebuiReady(p) {
|
|
13996
14078
|
const log = p.log ?? ((m) => console.log(m));
|
|
@@ -14028,10 +14110,10 @@ async function runBounded(work, timeoutMs, label, debug) {
|
|
|
14028
14110
|
Promise.resolve().then(() => work()).catch((err) => {
|
|
14029
14111
|
debug(`[webui-server] ${label} failed: ${err}`);
|
|
14030
14112
|
}),
|
|
14031
|
-
new Promise((
|
|
14113
|
+
new Promise((resolve17) => {
|
|
14032
14114
|
timer = setTimeout(() => {
|
|
14033
14115
|
debug(`[webui-server] ${label} timed out after ${timeoutMs}ms`);
|
|
14034
|
-
|
|
14116
|
+
resolve17();
|
|
14035
14117
|
}, timeoutMs);
|
|
14036
14118
|
timer.unref?.();
|
|
14037
14119
|
})
|
|
@@ -14064,8 +14146,8 @@ function createWebuiShutdown(res) {
|
|
|
14064
14146
|
const unregistered = unregister(res.pid, res.registryBaseDir).catch(
|
|
14065
14147
|
(err) => debug(`[webui-server] unregister failed: ${err}`)
|
|
14066
14148
|
);
|
|
14067
|
-
await new Promise((
|
|
14068
|
-
res.wss.close(() =>
|
|
14149
|
+
await new Promise((resolve17) => {
|
|
14150
|
+
res.wss.close(() => resolve17());
|
|
14069
14151
|
});
|
|
14070
14152
|
await unregistered;
|
|
14071
14153
|
log("[WebUI] Server stopped");
|
|
@@ -20458,8 +20540,8 @@ function createConfigWriteLock() {
|
|
|
20458
20540
|
acquire() {
|
|
20459
20541
|
const prev = lock;
|
|
20460
20542
|
let release = () => void 0;
|
|
20461
|
-
lock = new Promise((
|
|
20462
|
-
release =
|
|
20543
|
+
lock = new Promise((resolve17) => {
|
|
20544
|
+
release = resolve17;
|
|
20463
20545
|
});
|
|
20464
20546
|
return { prev, release };
|
|
20465
20547
|
}
|
|
@@ -23045,7 +23127,7 @@ function findWorkspaceCliEntry(projectRoot) {
|
|
|
23045
23127
|
return null;
|
|
23046
23128
|
}
|
|
23047
23129
|
function sleep(ms) {
|
|
23048
|
-
return new Promise((
|
|
23130
|
+
return new Promise((resolve17) => setTimeout(resolve17, ms));
|
|
23049
23131
|
}
|
|
23050
23132
|
|
|
23051
23133
|
// src/server/terminal-ws-handler.ts
|
|
@@ -23288,7 +23370,7 @@ function clampDim(value, fallback) {
|
|
|
23288
23370
|
}
|
|
23289
23371
|
|
|
23290
23372
|
// src/server/worktree-ws-handler.ts
|
|
23291
|
-
import { join as join14, resolve as
|
|
23373
|
+
import { join as join14, resolve as resolve14, sep as sep5 } from "node:path";
|
|
23292
23374
|
import { toErrorMessage as toErrorMessage9 } from "@wrongstack/core/utils";
|
|
23293
23375
|
import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core/worktree";
|
|
23294
23376
|
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
|
|
@@ -23373,11 +23455,11 @@ var WorktreeWebSocketHandler = class {
|
|
|
23373
23455
|
// ── orphan management ─────────────────────────────────────────────────────
|
|
23374
23456
|
/** Absolute managed-worktrees root for this project. */
|
|
23375
23457
|
worktreesRoot() {
|
|
23376
|
-
return
|
|
23458
|
+
return resolve14(join14(this.management.projectRoot, ".wrongstack", "worktrees"));
|
|
23377
23459
|
}
|
|
23378
23460
|
/** True iff `dir` resolves strictly inside the managed worktrees root. */
|
|
23379
23461
|
underRoot(dir) {
|
|
23380
|
-
const abs =
|
|
23462
|
+
const abs = resolve14(dir);
|
|
23381
23463
|
const root = this.worktreesRoot();
|
|
23382
23464
|
return abs !== root && abs.startsWith(root + sep5);
|
|
23383
23465
|
}
|
|
@@ -23601,7 +23683,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
23601
23683
|
}
|
|
23602
23684
|
const base = baseBranch && MANAGED_BRANCH_RE.test(baseBranch) ? baseBranch : void 0;
|
|
23603
23685
|
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
23604
|
-
const summary = await wt.diffSummary(
|
|
23686
|
+
const summary = await wt.diffSummary(resolve14(dir), base);
|
|
23605
23687
|
this.broadcast({ type: "worktree.diff_result", payload: { dir, summary } });
|
|
23606
23688
|
}
|
|
23607
23689
|
// ── internals ───────────────────────────────────────────────────────────
|
|
@@ -26231,7 +26313,7 @@ async function startWebUI(opts = {}) {
|
|
|
26231
26313
|
if (events.listenerCount("tool.confirm_needed") === 0) {
|
|
26232
26314
|
throw new Error("No permission confirmation surface is connected");
|
|
26233
26315
|
}
|
|
26234
|
-
const decision = await new Promise((
|
|
26316
|
+
const decision = await new Promise((resolve17) => {
|
|
26235
26317
|
events.emit("tool.confirm_needed", {
|
|
26236
26318
|
sessionId: context.session.id,
|
|
26237
26319
|
tool: confirmTool,
|
|
@@ -26241,7 +26323,7 @@ async function startWebUI(opts = {}) {
|
|
|
26241
26323
|
decisionSource: pending.decisionSource,
|
|
26242
26324
|
riskTier: pending.riskTier,
|
|
26243
26325
|
boundaryReason: pending.boundaryReason,
|
|
26244
|
-
resolve:
|
|
26326
|
+
resolve: resolve17
|
|
26245
26327
|
});
|
|
26246
26328
|
});
|
|
26247
26329
|
const rule = { tool: "language_package", pattern: pending.suggestedPattern };
|
|
@@ -26883,6 +26965,7 @@ export {
|
|
|
26883
26965
|
isPortFree,
|
|
26884
26966
|
isRegisteredMessageType,
|
|
26885
26967
|
isWildcardBind,
|
|
26968
|
+
joinSessionRegistryWithWebUIInstances,
|
|
26886
26969
|
listInstances,
|
|
26887
26970
|
loadManifest,
|
|
26888
26971
|
loadSavedProviders,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type WebUIInstanceRecord } from './instance-registry.js';
|
|
1
|
+
import { type WebUIInstanceRecord, type WebUIInstanceRole } from './instance-registry.js';
|
|
2
2
|
import type { SurfaceKind } from './port-utils.js';
|
|
3
3
|
/**
|
|
4
4
|
* PR 7 of Issue #30 (webui-server 8-PR refactor): process lifecycle.
|
|
@@ -38,6 +38,21 @@ export interface RegisterWebuiInstanceParams {
|
|
|
38
38
|
* authenticate `POST /api/fleet/ping` (H3). See `WebUIInstanceRecord`.
|
|
39
39
|
*/
|
|
40
40
|
authToken?: string | undefined;
|
|
41
|
+
/** Runtime role. Missing means the legacy standalone WebUI/SimpleUI role. */
|
|
42
|
+
role?: WebUIInstanceRole | undefined;
|
|
43
|
+
/** Live session owned by this endpoint when `role === 'session-child'`. */
|
|
44
|
+
sessionId?: string | undefined;
|
|
45
|
+
/** Parent shell process identity, when this endpoint was spawned by a parent. */
|
|
46
|
+
parentPid?: number | undefined;
|
|
47
|
+
parentShellId?: string | undefined;
|
|
48
|
+
/** Stable child runtime id, distinct from the session id. */
|
|
49
|
+
runtimeId?: string | undefined;
|
|
50
|
+
/** Whether a parent shell should treat this endpoint as attachable. */
|
|
51
|
+
attachable?: boolean | undefined;
|
|
52
|
+
/** Health/protocol hints for future parent shells. */
|
|
53
|
+
lastReadyAt?: string | undefined;
|
|
54
|
+
protocolVersion?: number | undefined;
|
|
55
|
+
capabilities?: string[] | undefined;
|
|
41
56
|
}
|
|
42
57
|
export interface RegisterWebuiInstanceDeps {
|
|
43
58
|
registerFn?: (record: WebUIInstanceRecord, baseDir?: string) => Promise<void>;
|
|
@@ -48,7 +63,7 @@ export interface RegisterWebuiInstanceDeps {
|
|
|
48
63
|
* is swallowed (the registry is a convenience index, not a source of
|
|
49
64
|
* truth). Caller guards on `projectRoot` being known.
|
|
50
65
|
*/
|
|
51
|
-
export declare function registerWebuiInstance(p: RegisterWebuiInstanceParams, deps?: RegisterWebuiInstanceDeps):
|
|
66
|
+
export declare function registerWebuiInstance(p: RegisterWebuiInstanceParams, deps?: RegisterWebuiInstanceDeps): Promise<boolean>;
|
|
52
67
|
export interface AnnounceWebuiReadyParams {
|
|
53
68
|
/** Surface kind — 'webui' or 'simpleui'. */
|
|
54
69
|
surface: SurfaceKind;
|
package/dist/server/entry.js
CHANGED
|
@@ -6377,28 +6377,28 @@ async function restartMailboxServer(projectRoot) {
|
|
|
6377
6377
|
var RESTART_POLL_INTERVAL_MS = 250;
|
|
6378
6378
|
var RESTART_DEADLINE_MS = 3e3;
|
|
6379
6379
|
function isEndpointAlive(endpoint) {
|
|
6380
|
-
return new Promise((
|
|
6380
|
+
return new Promise((resolve16) => {
|
|
6381
6381
|
const sock = net.createConnection(endpoint);
|
|
6382
6382
|
const timer = setTimeout(() => {
|
|
6383
6383
|
sock.destroy();
|
|
6384
|
-
|
|
6384
|
+
resolve16(false);
|
|
6385
6385
|
}, 500);
|
|
6386
6386
|
timer.unref?.();
|
|
6387
6387
|
sock.once("connect", () => {
|
|
6388
6388
|
clearTimeout(timer);
|
|
6389
6389
|
sock.destroy();
|
|
6390
|
-
|
|
6390
|
+
resolve16(true);
|
|
6391
6391
|
});
|
|
6392
6392
|
sock.once("error", () => {
|
|
6393
6393
|
clearTimeout(timer);
|
|
6394
6394
|
sock.destroy();
|
|
6395
|
-
|
|
6395
|
+
resolve16(false);
|
|
6396
6396
|
});
|
|
6397
6397
|
});
|
|
6398
6398
|
}
|
|
6399
6399
|
async function waitForShutdown(probe) {
|
|
6400
6400
|
if (!probe) {
|
|
6401
|
-
await new Promise((
|
|
6401
|
+
await new Promise((resolve16) => setTimeout(resolve16, RESTART_POLL_INTERVAL_MS));
|
|
6402
6402
|
return;
|
|
6403
6403
|
}
|
|
6404
6404
|
const deadline = Date.now() + RESTART_DEADLINE_MS;
|
|
@@ -6409,7 +6409,7 @@ async function waitForShutdown(probe) {
|
|
|
6409
6409
|
} catch {
|
|
6410
6410
|
return;
|
|
6411
6411
|
}
|
|
6412
|
-
await new Promise((
|
|
6412
|
+
await new Promise((resolve16) => setTimeout(resolve16, RESTART_POLL_INTERVAL_MS));
|
|
6413
6413
|
}
|
|
6414
6414
|
}
|
|
6415
6415
|
function failureService(id, label, required, mode, error2, latencyMs) {
|
|
@@ -6572,9 +6572,9 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
6572
6572
|
const cwd = projectRoot || void 0;
|
|
6573
6573
|
try {
|
|
6574
6574
|
const { execFile: ef } = await import("node:child_process");
|
|
6575
|
-
const git = (args) => new Promise((
|
|
6575
|
+
const git = (args) => new Promise((resolve16) => {
|
|
6576
6576
|
ef("git", args, { cwd, timeout: 3e3 }, (err, stdout) => {
|
|
6577
|
-
|
|
6577
|
+
resolve16(err ? "" : stdout.trim());
|
|
6578
6578
|
});
|
|
6579
6579
|
});
|
|
6580
6580
|
const [branchRaw, diffRaw, statusRaw, upstreamRaw] = await Promise.all([
|
|
@@ -6600,12 +6600,12 @@ async function handleGitInfo(ws, projectRoot) {
|
|
|
6600
6600
|
function makeGit(cwd) {
|
|
6601
6601
|
return async (args) => {
|
|
6602
6602
|
const { execFile: ef } = await import("node:child_process");
|
|
6603
|
-
return new Promise((
|
|
6603
|
+
return new Promise((resolve16) => {
|
|
6604
6604
|
ef(
|
|
6605
6605
|
"git",
|
|
6606
6606
|
args,
|
|
6607
6607
|
{ cwd, timeout: 5e3, maxBuffer: 1024 * 1024 * 16 },
|
|
6608
|
-
(err, stdout) =>
|
|
6608
|
+
(err, stdout) => resolve16(err ? "" : stdout)
|
|
6609
6609
|
);
|
|
6610
6610
|
});
|
|
6611
6611
|
};
|
|
@@ -6770,7 +6770,7 @@ import { execFile } from "node:child_process";
|
|
|
6770
6770
|
var GIT_TIMEOUT_MS = 1e4;
|
|
6771
6771
|
var GIT_MAX_OUTPUT_BYTES = 1024 * 1024;
|
|
6772
6772
|
function gitStdout(cwd, args) {
|
|
6773
|
-
return new Promise((
|
|
6773
|
+
return new Promise((resolve16) => {
|
|
6774
6774
|
execFile(
|
|
6775
6775
|
"git",
|
|
6776
6776
|
[...args],
|
|
@@ -6781,7 +6781,7 @@ function gitStdout(cwd, args) {
|
|
|
6781
6781
|
timeout: GIT_TIMEOUT_MS,
|
|
6782
6782
|
maxBuffer: GIT_MAX_OUTPUT_BYTES
|
|
6783
6783
|
},
|
|
6784
|
-
(error2, stdout) =>
|
|
6784
|
+
(error2, stdout) => resolve16(error2 ? null : stdout)
|
|
6785
6785
|
);
|
|
6786
6786
|
});
|
|
6787
6787
|
}
|
|
@@ -7072,14 +7072,14 @@ var GoalWebSocketHandler = class {
|
|
|
7072
7072
|
const cwd = env?.cwd ?? this.projectRoot;
|
|
7073
7073
|
try {
|
|
7074
7074
|
const { execFile: execFile2 } = await import("node:child_process");
|
|
7075
|
-
const result = await new Promise((
|
|
7075
|
+
const result = await new Promise((resolve16) => {
|
|
7076
7076
|
const npxCommand = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
7077
7077
|
execFile2(npxCommand, ["tsc", "--noEmit"], { cwd, timeout: 6e4 }, (err, stdout, stderr) => {
|
|
7078
7078
|
if (err && err.code === "ENOENT") {
|
|
7079
|
-
|
|
7079
|
+
resolve16("[verify] tsc not found \u2014 skipping");
|
|
7080
7080
|
return;
|
|
7081
7081
|
}
|
|
7082
|
-
|
|
7082
|
+
resolve16(stdout + stderr);
|
|
7083
7083
|
});
|
|
7084
7084
|
});
|
|
7085
7085
|
if (result.includes("[verify]") || result.trim().length === 0) {
|
|
@@ -7817,7 +7817,7 @@ function pushEvent(event) {
|
|
|
7817
7817
|
}
|
|
7818
7818
|
}
|
|
7819
7819
|
function parseBody(req) {
|
|
7820
|
-
return new Promise((
|
|
7820
|
+
return new Promise((resolve16, reject) => {
|
|
7821
7821
|
let body = "";
|
|
7822
7822
|
let bodyBytes = 0;
|
|
7823
7823
|
let tooLarge = false;
|
|
@@ -7837,7 +7837,7 @@ function parseBody(req) {
|
|
|
7837
7837
|
return;
|
|
7838
7838
|
}
|
|
7839
7839
|
try {
|
|
7840
|
-
|
|
7840
|
+
resolve16(JSON.parse(body));
|
|
7841
7841
|
} catch {
|
|
7842
7842
|
reject(new Error("Invalid JSON"));
|
|
7843
7843
|
}
|
|
@@ -7924,7 +7924,7 @@ import * as path10 from "node:path";
|
|
|
7924
7924
|
import { runDeadCodeScan } from "@wrongstack/tools/codebase-index";
|
|
7925
7925
|
var MAX_BODY_BYTES = 10 * 1024 * 1024;
|
|
7926
7926
|
function readJsonBody(req) {
|
|
7927
|
-
return new Promise((
|
|
7927
|
+
return new Promise((resolve16, reject) => {
|
|
7928
7928
|
const chunks = [];
|
|
7929
7929
|
let total = 0;
|
|
7930
7930
|
req.on("data", (chunk) => {
|
|
@@ -7936,7 +7936,7 @@ function readJsonBody(req) {
|
|
|
7936
7936
|
}
|
|
7937
7937
|
chunks.push(chunk);
|
|
7938
7938
|
});
|
|
7939
|
-
req.on("end", () =>
|
|
7939
|
+
req.on("end", () => resolve16(Buffer.concat(chunks).toString("utf8")));
|
|
7940
7940
|
req.on("error", (err) => reject(err));
|
|
7941
7941
|
});
|
|
7942
7942
|
}
|
|
@@ -8382,7 +8382,7 @@ async function handleApiSessionEvents(res, globalRoot, sessionId, limit) {
|
|
|
8382
8382
|
}
|
|
8383
8383
|
}
|
|
8384
8384
|
function readJsonBody2(req) {
|
|
8385
|
-
return new Promise((
|
|
8385
|
+
return new Promise((resolve16, reject) => {
|
|
8386
8386
|
const contentType = (req.headers["content-type"] ?? "").split(";")[0]?.trim().toLowerCase();
|
|
8387
8387
|
if (contentType !== "application/json") {
|
|
8388
8388
|
reject(new Error(`Unsupported Content-Type: ${contentType || "(absent)"}`));
|
|
@@ -8398,7 +8398,7 @@ function readJsonBody2(req) {
|
|
|
8398
8398
|
});
|
|
8399
8399
|
req.on("end", () => {
|
|
8400
8400
|
try {
|
|
8401
|
-
|
|
8401
|
+
resolve16(data ? JSON.parse(data) : {});
|
|
8402
8402
|
} catch (err) {
|
|
8403
8403
|
reject(err instanceof Error ? err : new Error(String(err)));
|
|
8404
8404
|
}
|
|
@@ -8674,14 +8674,14 @@ async function readJsonBody3(res, req) {
|
|
|
8674
8674
|
});
|
|
8675
8675
|
return null;
|
|
8676
8676
|
}
|
|
8677
|
-
return new Promise((
|
|
8677
|
+
return new Promise((resolve16) => {
|
|
8678
8678
|
let data = "";
|
|
8679
8679
|
let failed = false;
|
|
8680
8680
|
const fail2 = (message) => {
|
|
8681
8681
|
if (failed) return;
|
|
8682
8682
|
failed = true;
|
|
8683
8683
|
sendJson2(res, 400, { error: { code: "INVALID_BODY", message } });
|
|
8684
|
-
|
|
8684
|
+
resolve16(null);
|
|
8685
8685
|
};
|
|
8686
8686
|
req.on("data", (chunk) => {
|
|
8687
8687
|
if (failed) return;
|
|
@@ -8694,7 +8694,7 @@ async function readJsonBody3(res, req) {
|
|
|
8694
8694
|
req.on("end", () => {
|
|
8695
8695
|
if (failed) return;
|
|
8696
8696
|
try {
|
|
8697
|
-
|
|
8697
|
+
resolve16(data.trim().length === 0 ? {} : JSON.parse(data));
|
|
8698
8698
|
} catch {
|
|
8699
8699
|
fail2("Request body is not valid JSON");
|
|
8700
8700
|
}
|
|
@@ -9533,7 +9533,7 @@ function strictDecodeParam(segment, res) {
|
|
|
9533
9533
|
function createHttpServer(opts) {
|
|
9534
9534
|
const port = opts.port ?? Number.parseInt(process.env["PORT"] ?? "3456", 10);
|
|
9535
9535
|
const distDir = path13.resolve(opts.distDir);
|
|
9536
|
-
const requireAccessToken =
|
|
9536
|
+
const requireAccessToken = Boolean(opts.requireToken) || !isLoopbackBind(opts.host);
|
|
9537
9537
|
const secureCookies = opts.secureCookies ?? (opts.publicWsUrl?.trim().toLowerCase().startsWith("wss:") ?? false);
|
|
9538
9538
|
const trustedHostnames = (() => {
|
|
9539
9539
|
const names = [...opts.allowedHostnames ?? []];
|
|
@@ -10079,9 +10079,9 @@ function createHttpServer(opts) {
|
|
|
10079
10079
|
}
|
|
10080
10080
|
|
|
10081
10081
|
// src/server/instance-registry.ts
|
|
10082
|
+
import * as fs11 from "node:fs/promises";
|
|
10082
10083
|
import * as os from "node:os";
|
|
10083
10084
|
import * as path14 from "node:path";
|
|
10084
|
-
import * as fs11 from "node:fs/promises";
|
|
10085
10085
|
import { atomicWrite as atomicWrite4 } from "@wrongstack/core/utils";
|
|
10086
10086
|
function defaultBaseDir() {
|
|
10087
10087
|
return path14.join(os.homedir(), ".wrongstack");
|
|
@@ -13503,16 +13503,16 @@ function createModelOperations(context) {
|
|
|
13503
13503
|
import * as net2 from "node:net";
|
|
13504
13504
|
import { ToolValidationError as ToolValidationError4 } from "@wrongstack/core/types";
|
|
13505
13505
|
function isPortFree(host, port) {
|
|
13506
|
-
return new Promise((
|
|
13506
|
+
return new Promise((resolve16) => {
|
|
13507
13507
|
const srv = net2.createServer();
|
|
13508
|
-
srv.once("error", () =>
|
|
13508
|
+
srv.once("error", () => resolve16(false));
|
|
13509
13509
|
srv.once("listening", () => {
|
|
13510
|
-
srv.close(() =>
|
|
13510
|
+
srv.close(() => resolve16(true));
|
|
13511
13511
|
});
|
|
13512
13512
|
try {
|
|
13513
13513
|
srv.listen(port, host);
|
|
13514
13514
|
} catch {
|
|
13515
|
-
|
|
13515
|
+
resolve16(false);
|
|
13516
13516
|
}
|
|
13517
13517
|
});
|
|
13518
13518
|
}
|
|
@@ -20724,7 +20724,7 @@ function findWorkspaceCliEntry(projectRoot) {
|
|
|
20724
20724
|
return null;
|
|
20725
20725
|
}
|
|
20726
20726
|
function sleep(ms) {
|
|
20727
|
-
return new Promise((
|
|
20727
|
+
return new Promise((resolve16) => setTimeout(resolve16, ms));
|
|
20728
20728
|
}
|
|
20729
20729
|
|
|
20730
20730
|
// src/server/terminal-ws-handler.ts
|
|
@@ -20967,7 +20967,7 @@ function clampDim(value, fallback) {
|
|
|
20967
20967
|
}
|
|
20968
20968
|
|
|
20969
20969
|
// src/server/worktree-ws-handler.ts
|
|
20970
|
-
import { join as join11, resolve as
|
|
20970
|
+
import { join as join11, resolve as resolve13, sep as sep5 } from "node:path";
|
|
20971
20971
|
import { toErrorMessage as toErrorMessage8 } from "@wrongstack/core/utils";
|
|
20972
20972
|
import { WorktreeManager as WorktreeManager3 } from "@wrongstack/core/worktree";
|
|
20973
20973
|
import { cleanupStaleSddWorktrees as cleanupStaleSddWorktrees2 } from "@wrongstack/sdd";
|
|
@@ -21052,11 +21052,11 @@ var WorktreeWebSocketHandler = class {
|
|
|
21052
21052
|
// ── orphan management ─────────────────────────────────────────────────────
|
|
21053
21053
|
/** Absolute managed-worktrees root for this project. */
|
|
21054
21054
|
worktreesRoot() {
|
|
21055
|
-
return
|
|
21055
|
+
return resolve13(join11(this.management.projectRoot, ".wrongstack", "worktrees"));
|
|
21056
21056
|
}
|
|
21057
21057
|
/** True iff `dir` resolves strictly inside the managed worktrees root. */
|
|
21058
21058
|
underRoot(dir) {
|
|
21059
|
-
const abs =
|
|
21059
|
+
const abs = resolve13(dir);
|
|
21060
21060
|
const root = this.worktreesRoot();
|
|
21061
21061
|
return abs !== root && abs.startsWith(root + sep5);
|
|
21062
21062
|
}
|
|
@@ -21280,7 +21280,7 @@ var WorktreeWebSocketHandler = class {
|
|
|
21280
21280
|
}
|
|
21281
21281
|
const base = baseBranch && MANAGED_BRANCH_RE.test(baseBranch) ? baseBranch : void 0;
|
|
21282
21282
|
const wt = new WorktreeManager3({ projectRoot: this.management.projectRoot });
|
|
21283
|
-
const summary = await wt.diffSummary(
|
|
21283
|
+
const summary = await wt.diffSummary(resolve13(dir), base);
|
|
21284
21284
|
this.broadcast({ type: "worktree.diff_result", payload: { dir, summary } });
|
|
21285
21285
|
}
|
|
21286
21286
|
// ── internals ───────────────────────────────────────────────────────────
|
|
@@ -23910,7 +23910,7 @@ async function startWebUI(opts = {}) {
|
|
|
23910
23910
|
if (events.listenerCount("tool.confirm_needed") === 0) {
|
|
23911
23911
|
throw new Error("No permission confirmation surface is connected");
|
|
23912
23912
|
}
|
|
23913
|
-
const decision = await new Promise((
|
|
23913
|
+
const decision = await new Promise((resolve16) => {
|
|
23914
23914
|
events.emit("tool.confirm_needed", {
|
|
23915
23915
|
sessionId: context.session.id,
|
|
23916
23916
|
tool: confirmTool,
|
|
@@ -23920,7 +23920,7 @@ async function startWebUI(opts = {}) {
|
|
|
23920
23920
|
decisionSource: pending.decisionSource,
|
|
23921
23921
|
riskTier: pending.riskTier,
|
|
23922
23922
|
boundaryReason: pending.boundaryReason,
|
|
23923
|
-
resolve:
|
|
23923
|
+
resolve: resolve16
|
|
23924
23924
|
});
|
|
23925
23925
|
});
|
|
23926
23926
|
const rule = { tool: "language_package", pattern: pending.suggestedPattern };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export { type HostRouteHandlers, handleHostRoute } from './host-routes.js';
|
|
|
42
42
|
export { clearAnalyticsBuffer, getAnalyticsBuffer, handleApiAnalyticsGet, handleApiAnalyticsPost, handleApiAnalyticsSummary, } from './http-server/analytics-handler.js';
|
|
43
43
|
export type { CreateHttpServerOptions } from './http-server.js';
|
|
44
44
|
export { buildCspHeader, createHttpServer, decodeSessionId, injectWsConfig, isInsideDist, } from './http-server.js';
|
|
45
|
-
export { defaultBaseDir, formatInstances, isPidAlive, listInstances, registerInstance, registryPath, unregisterInstance, type WebUIInstanceRecord, } from './instance-registry.js';
|
|
45
|
+
export { defaultBaseDir, formatInstances, isPidAlive, joinSessionRegistryWithWebUIInstances, listInstances, registerInstance, registryPath, unregisterInstance, type WebUIInstanceAuthInfo, type WebUIInstanceRecord, type WebUIInstanceRole, type WebUISessionAttachCandidate, type WebUISessionAttachDegradedReason, type WebUISessionAttachEndpoint, } from './instance-registry.js';
|
|
46
46
|
export { handleIntrospectionRoute, type IntrospectionRouteContext, } from './introspection-routes.js';
|
|
47
47
|
export { handleKanbanTaskDispatch, type KanbanDispatchContext, type KanbanDispatchResult, type KanbanTaskDispatcher, parseResolvedDispatchRoute, type ResolvedDispatchRoute, } from './kanban-dispatch.js';
|
|
48
48
|
export { handleKanbanHostRoute, type KanbanHostRouteHandlers } from './kanban-host-routes.js';
|
|
@@ -18,12 +18,20 @@
|
|
|
18
18
|
* - **Best-effort**: a failure to read/write the registry must NEVER take the
|
|
19
19
|
* server down. Callers wrap these in `.catch()`.
|
|
20
20
|
*/
|
|
21
|
+
import type { SessionRegistryEntry, SessionLiveStatus } from '@wrongstack/core/storage';
|
|
22
|
+
export type WebUIInstanceRole = 'standalone' | 'parent-shell' | 'session-child';
|
|
23
|
+
export interface WebUIInstanceAuthInfo {
|
|
24
|
+
/** How a same-user parent/sibling process can authenticate to this endpoint. */
|
|
25
|
+
scheme: 'registry-token' | 'cookie-bootstrap' | 'none';
|
|
26
|
+
/** Whether the record has a usable token in `authToken`. */
|
|
27
|
+
tokenPresent: boolean;
|
|
28
|
+
}
|
|
21
29
|
/** One running WebUI / SimpleUI process. */
|
|
22
30
|
export interface WebUIInstanceRecord {
|
|
23
31
|
/** OS process id — also the liveness key. */
|
|
24
32
|
pid: number;
|
|
25
|
-
/** Surface kind — 'webui' or 'simpleui'. */
|
|
26
|
-
surface: 'webui' | 'simpleui';
|
|
33
|
+
/** Surface kind — 'webui' or 'simpleui'. Additional strings are tolerated for new surfaces. */
|
|
34
|
+
surface: 'webui' | 'simpleui' | string;
|
|
27
35
|
/** Port serving both HTTP and WebSocket. */
|
|
28
36
|
httpPort: number;
|
|
29
37
|
/** Bind host (e.g. 127.0.0.1 or 0.0.0.0). */
|
|
@@ -57,7 +65,48 @@ export interface WebUIInstanceRecord {
|
|
|
57
65
|
* Optional so an older record (or a surface that has no token) still parses.
|
|
58
66
|
*/
|
|
59
67
|
authToken?: string | undefined;
|
|
68
|
+
/** Runtime role. Missing means the legacy standalone WebUI/SimpleUI role. */
|
|
69
|
+
role?: WebUIInstanceRole | undefined;
|
|
70
|
+
/** Live session owned by this endpoint when `role === 'session-child'`. */
|
|
71
|
+
sessionId?: string | undefined;
|
|
72
|
+
/** Parent shell process identity, when this endpoint was spawned by a parent. */
|
|
73
|
+
parentPid?: number | undefined;
|
|
74
|
+
parentShellId?: string | undefined;
|
|
75
|
+
/** Stable child runtime id, distinct from the session id. */
|
|
76
|
+
runtimeId?: string | undefined;
|
|
77
|
+
/** Whether a parent shell should treat this endpoint as attachable. */
|
|
78
|
+
attachable?: boolean | undefined;
|
|
79
|
+
/** Descriptive auth metadata; the same-user token remains in `authToken`. */
|
|
80
|
+
auth?: WebUIInstanceAuthInfo | undefined;
|
|
81
|
+
/** Health/protocol hints for future parent shells. */
|
|
82
|
+
lastReadyAt?: string | undefined;
|
|
83
|
+
protocolVersion?: number | undefined;
|
|
84
|
+
capabilities?: string[] | undefined;
|
|
85
|
+
}
|
|
86
|
+
export interface WebUISessionAttachEndpoint {
|
|
87
|
+
host: string;
|
|
88
|
+
httpPort: number;
|
|
89
|
+
url: string;
|
|
90
|
+
authToken?: string | undefined;
|
|
91
|
+
}
|
|
92
|
+
export type WebUISessionAttachDegradedReason = 'live-session-no-webui-endpoint' | 'endpoint-owner-mismatch' | 'endpoint-missing-session-id' | 'endpoint-session-mismatch' | 'endpoint-not-session-child' | 'endpoint-not-attachable' | 'session-not-live';
|
|
93
|
+
export interface WebUISessionAttachCandidate {
|
|
94
|
+
sessionId: string;
|
|
95
|
+
projectRoot: string;
|
|
96
|
+
workingDir: string;
|
|
97
|
+
sessionPid: number;
|
|
98
|
+
status: SessionLiveStatus;
|
|
99
|
+
instance?: WebUIInstanceRecord | undefined;
|
|
100
|
+
endpoint?: WebUISessionAttachEndpoint | undefined;
|
|
101
|
+
attachable: boolean;
|
|
102
|
+
degradedReason?: WebUISessionAttachDegradedReason | undefined;
|
|
60
103
|
}
|
|
104
|
+
export declare function joinSessionRegistryWithWebUIInstances(input: {
|
|
105
|
+
sessions: SessionRegistryEntry[];
|
|
106
|
+
instances: WebUIInstanceRecord[];
|
|
107
|
+
projectRoot?: string | undefined;
|
|
108
|
+
projectSlug?: string | undefined;
|
|
109
|
+
}): WebUISessionAttachCandidate[];
|
|
61
110
|
/** Default wstack home dir (`~/.wrongstack`). Callers may override the base. */
|
|
62
111
|
export declare function defaultBaseDir(): string;
|
|
63
112
|
/** Resolve the registry file path for a given base dir. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wrongstack/webui-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.302.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "WrongStack WebUI HTTP/WebSocket server module — extracted from @wrongstack/webui in PR #243/244 to remove the CLI -> @wrongstack/webui/server cross-package edge (audit §3.1.1). Pure backend: HTTP routes, WebSocket handlers, MCP tool wrappers, HTML serving. The web frontend lives in @wrongstack/webui; this package is the standalone server it can run on.",
|
|
6
6
|
"keywords": [
|
|
@@ -40,16 +40,16 @@
|
|
|
40
40
|
],
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"ws": "^8.21.1",
|
|
43
|
-
"@wrongstack/core": "0.
|
|
44
|
-
"@wrongstack/
|
|
45
|
-
"@wrongstack/
|
|
46
|
-
"@wrongstack/
|
|
47
|
-
"@wrongstack/
|
|
48
|
-
"@wrongstack/sage": "0.
|
|
49
|
-
"@wrongstack/providers": "0.
|
|
50
|
-
"@wrongstack/
|
|
51
|
-
"@wrongstack/
|
|
52
|
-
"@wrongstack/
|
|
43
|
+
"@wrongstack/core": "0.302.0",
|
|
44
|
+
"@wrongstack/mcp": "0.302.0",
|
|
45
|
+
"@wrongstack/techstack": "0.302.0",
|
|
46
|
+
"@wrongstack/kanban": "0.302.0",
|
|
47
|
+
"@wrongstack/runtime": "0.302.0",
|
|
48
|
+
"@wrongstack/sage": "0.302.0",
|
|
49
|
+
"@wrongstack/providers": "0.302.0",
|
|
50
|
+
"@wrongstack/requirement-intake": "0.302.0",
|
|
51
|
+
"@wrongstack/tools": "0.302.0",
|
|
52
|
+
"@wrongstack/sdd": "0.302.0"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
55
55
|
"@types/node": "^26.1.2",
|