@wrongstack/cli 1.0.1 → 1.0.4
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/data/providers.json +21 -21
- package/dist/{acp-3LARESEO.js → acp-AB54FWM2.js} +122 -39
- package/dist/acp-agent-list.d.ts +14 -0
- package/dist/acp-hq-telemetry.d.ts +9 -2
- package/dist/acp-mcp-servers.d.ts +49 -0
- package/dist/acp-server-agent.d.ts +18 -6
- package/dist/{auth-ZUMCDNE5.js → auth-WDMSNZ3S.js} +79 -55
- package/dist/auth-menu/index.d.ts +1 -0
- package/dist/auth-menu/oauth-menu.d.ts +3 -1
- package/dist/auth-menu/panel-service.d.ts +2 -0
- package/dist/auth-menu/provider-auth-login.d.ts +12 -0
- package/dist/auth-menu/types.d.ts +3 -0
- package/dist/auto-discover-providers-DXR25GTG.js +7 -0
- package/dist/boot/dispatch-webui.d.ts +1 -0
- package/dist/boot/short-circuit-hq.d.ts +2 -0
- package/dist/boot/system-prompt-builder.d.ts +7 -0
- package/dist/boot/system-prompt.d.ts +2 -1
- package/dist/boot/tool-registry.d.ts +18 -1
- package/dist/chimera-reviewer-policy.d.ts +1 -1
- package/dist/{chunk-C2E24IXZ.js → chunk-3I5ZJXM5.js} +147 -241
- package/dist/{chunk-JHPXJRRI.js → chunk-4XCBEUBX.js} +13 -3
- package/dist/{chunk-FPIWTJ2G.js → chunk-6WRKAACA.js} +4 -4
- package/dist/chunk-AUQDGU5D.js +32 -0
- package/dist/{chunk-D2VCUN7M.js → chunk-BIWE22O5.js} +17 -17
- package/dist/{chunk-VOTTENR5.js → chunk-CDY4D5QK.js} +2 -2
- package/dist/chunk-D5GB5MHA.js +87 -0
- package/dist/chunk-DAKVBMD4.js +36 -0
- package/dist/{chunk-E6RFXHWD.js → chunk-EK2P53GL.js} +3 -2
- package/dist/chunk-I7V2OYVH.js +242 -0
- package/dist/{chunk-MXEJEYT7.js → chunk-JX6KL7OF.js} +15 -5
- package/dist/{chunk-ZSZ3EYWH.js → chunk-KEPABQYT.js} +109 -6
- package/dist/{chunk-IMWXXF4J.js → chunk-KNMIBKLV.js} +79 -1
- package/dist/{chunk-MNQQ4ZNF.js → chunk-ONOT5OJ2.js} +290 -687
- package/dist/chunk-Q7E3BPDU.js +62 -0
- package/dist/chunk-SIGUWXAA.js +43 -0
- package/dist/{chunk-TEBMCYZN.js → chunk-SIS37L3W.js} +58 -2
- package/dist/{chunk-KHWDTNBZ.js → chunk-U6PGWDMZ.js} +5 -5
- package/dist/{chunk-5IA5337I.js → chunk-XJ7K67PP.js} +8 -2
- package/dist/{cli-context-7X3M6U73.js → cli-context-WFC3BNGM.js} +131 -25
- package/dist/{cli-main-53KG6HAV.js → cli-main-GU3YZLMO.js} +3986 -2827
- package/dist/{diag-doctor-7LYUYU35.js → diag-doctor-PCER6KHH.js} +2 -2
- package/dist/execute-deps.d.ts +1 -0
- package/dist/{execution-MIBWBYB6.js → execution-RAZDDQFC.js} +115 -16
- package/dist/execution-specialist-trigger.d.ts +28 -0
- package/dist/fleet/acp-live-progress.d.ts +19 -0
- package/dist/fleet/host-acp-runner-cache.d.ts +3 -1
- package/dist/fleet/host-acp.d.ts +14 -2
- package/dist/fleet/host.d.ts +1 -0
- package/dist/{hq-IG3YRGCO.js → hq-5XHEIIH6.js} +127 -8
- package/dist/hq-command-controller.d.ts +31 -2
- package/dist/hq-server/client-address.d.ts +2 -0
- package/dist/hq-server/ip-allowlist.d.ts +16 -0
- package/dist/hq-server/preflight.d.ts +1 -0
- package/dist/hq-server/routes/auth/password-routes.d.ts +1 -1
- package/dist/hq-server/routes.d.ts +5 -4
- package/dist/hq-server/secret-input.d.ts +7 -0
- package/dist/hq-server/types.d.ts +3 -2
- package/dist/hq-server/upgrade-handler.d.ts +2 -0
- package/dist/hq-server/utils.d.ts +6 -0
- package/dist/{hq-server-OILU3NPI.js → hq-server-CNUTBWTK.js} +4 -3
- package/dist/hq-server.d.ts +4 -0
- package/dist/hq-service-L5INQFM4.js +507 -0
- package/dist/hq-service.d.ts +22 -0
- package/dist/index.js +4 -4
- package/dist/ip-allowlist-NHXO6AKJ.js +9 -0
- package/dist/live-settings-input.d.ts +4 -0
- package/dist/{mailbox-serve-VUCWZ2SJ.js → mailbox-serve-X3CR6VFW.js} +4 -3
- package/dist/{modeldiag-FMJT37SN.js → modeldiag-46CMWUNM.js} +31 -24
- package/dist/{per-subcommand-help-BWBHYVJO.js → per-subcommand-help-DHTYU524.js} +2 -2
- package/dist/{plugin-usage-HFBI3TFT.js → plugin-usage-M6KET4OZ.js} +2 -2
- package/dist/{plugins-PZ2VZL7V.js → plugins-WJLW6ULV.js} +3 -3
- package/dist/projects-LMNAGGEU.js +36 -0
- package/dist/{providers-models-YVMVYWRS.js → providers-models-6MOGAOVK.js} +2 -2
- package/dist/{proxy-wiring-MGRSKZZB.js → proxy-wiring-K2DORFFL.js} +3 -3
- package/dist/short-circuit-flags-YDMLAQ5R.js +10 -0
- package/dist/slash-commands/acp.d.ts +3 -0
- package/dist/slash-commands/codebase-map.d.ts +11 -0
- package/dist/slash-commands/command-context.d.ts +29 -0
- package/dist/slash-commands/context.d.ts +10 -0
- package/dist/slash-commands/ensemble.d.ts +1 -1
- package/dist/slash-commands/openai-quota.d.ts +31 -0
- package/dist/slash-commands/profile.d.ts +7 -0
- package/dist/subcommands/handlers/hq.d.ts +1 -0
- package/dist/subcommands/handlers/projects.d.ts +14 -0
- package/dist/{subcommands-WJLBHVKC.js → subcommands-7N4RIK6H.js} +2 -2
- package/dist/{version-help-MYKF724O.js → version-help-WXI42PZ3.js} +2 -2
- package/dist/{webui-server-5R35BHWZ.js → webui-server-OBV5JYJV.js} +27 -4
- package/dist/webui-server-options.d.ts +1 -0
- package/dist/wiring/atlas-prompt-contributor.d.ts +54 -0
- package/dist/wiring/cli-execute-builder.d.ts +1 -0
- package/dist/wiring/cli-slash-commands-setup.d.ts +7 -0
- package/dist/wiring/codebase-embeddings.d.ts +31 -0
- package/dist/wiring/command-host-state.d.ts +9 -0
- package/dist/wiring/concept-summarizer.d.ts +32 -0
- package/dist/wiring/external-plugins.d.ts +1 -0
- package/dist/wiring/lifecycle-plugins.d.ts +2 -1
- package/dist/wiring/pipeline.d.ts +2 -0
- package/dist/wiring/plugins.d.ts +2 -1
- package/dist/wiring/provider-runtime-setup.d.ts +1 -1
- package/dist/wiring/provider.d.ts +2 -1
- package/dist/wiring/session-command-handlers.d.ts +2 -1
- package/dist/wiring/slash-commands.d.ts +1 -0
- package/package.json +29 -29
- package/dist/auto-discover-providers-VQU3IZMR.js +0 -7
- package/dist/chunk-52C7PD2H.js +0 -61
- package/dist/chunk-Q5GTM25S.js +0 -17
- package/dist/chunk-TNK6237M.js +0 -262
- package/dist/projects-L6C6XLDY.js +0 -37
- package/dist/short-circuit-flags-UREFSYBL.js +0 -10
|
@@ -3,14 +3,37 @@ import {
|
|
|
3
3
|
terminalText
|
|
4
4
|
} from "./chunk-HXJYHAR4.js";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
MAX_AGENT_RINGS,
|
|
7
|
+
MAX_TRANSCRIPT_SESSIONS,
|
|
8
|
+
TRANSCRIPT_RING_MAX,
|
|
9
|
+
agentMessageToEntry,
|
|
10
|
+
agentRingKey,
|
|
11
|
+
buildBootstrapHttpUrl,
|
|
12
|
+
buildClientWsUrl,
|
|
13
|
+
buildHttpUrl,
|
|
14
|
+
decodePathSegment,
|
|
15
|
+
decodeSessionId,
|
|
16
|
+
evictOldest,
|
|
17
|
+
hqMachineKey,
|
|
18
|
+
hqRuntimeMarkerPath,
|
|
19
|
+
lanIPv4Addresses,
|
|
20
|
+
readLocalSubagentTranscript,
|
|
21
|
+
readRequestBody,
|
|
22
|
+
recordTimeseriesSignal,
|
|
23
|
+
resolveAuditActor,
|
|
24
|
+
sanitizeApiError,
|
|
25
|
+
truncateHqSummary,
|
|
26
|
+
writeInvalidBody
|
|
27
|
+
} from "./chunk-I7V2OYVH.js";
|
|
28
|
+
import {
|
|
29
|
+
createHqIpAllowlist
|
|
30
|
+
} from "./chunk-Q7E3BPDU.js";
|
|
8
31
|
|
|
9
32
|
// src/hq-server.ts
|
|
10
33
|
import { randomUUID as randomUUID6 } from "node:crypto";
|
|
11
|
-
import * as
|
|
34
|
+
import * as fs4 from "node:fs/promises";
|
|
12
35
|
import * as http from "node:http";
|
|
13
|
-
import * as
|
|
36
|
+
import * as path6 from "node:path";
|
|
14
37
|
import { getSharedProjectMailbox } from "@wrongstack/core/coordination";
|
|
15
38
|
import {
|
|
16
39
|
assessHqExposure as assessHqExposure2,
|
|
@@ -290,7 +313,7 @@ function authenticateBrowserRequest(req, url, mutableAuth, sessions) {
|
|
|
290
313
|
if (liveToken.capabilities !== void 0) ctx.capabilities = liveToken.capabilities;
|
|
291
314
|
return ctx;
|
|
292
315
|
}
|
|
293
|
-
return { kind: "cookie" };
|
|
316
|
+
return session.capabilities === void 0 ? { kind: "cookie" } : { kind: "cookie", capabilities: session.capabilities };
|
|
294
317
|
}
|
|
295
318
|
if (session) sessions.delete(sessionId);
|
|
296
319
|
}
|
|
@@ -609,6 +632,7 @@ var MIME = {
|
|
|
609
632
|
".mjs": "application/javascript; charset=utf-8",
|
|
610
633
|
".css": "text/css; charset=utf-8",
|
|
611
634
|
".json": "application/json; charset=utf-8",
|
|
635
|
+
".webmanifest": "application/manifest+json; charset=utf-8",
|
|
612
636
|
".svg": "image/svg+xml",
|
|
613
637
|
".png": "image/png",
|
|
614
638
|
".jpg": "image/jpeg",
|
|
@@ -711,7 +735,6 @@ function isLoopbackRequest(req) {
|
|
|
711
735
|
var HQ_AUTH_ADMIN_CAPABILITY = "auth.admin";
|
|
712
736
|
function callerCanAdministerAuth(auth) {
|
|
713
737
|
if (auth === void 0) return false;
|
|
714
|
-
if (auth.kind === "cookie" && auth.tokenId === void 0) return true;
|
|
715
738
|
if (auth.capabilities === void 0) return true;
|
|
716
739
|
return auth.capabilities.includes(HQ_AUTH_ADMIN_CAPABILITY);
|
|
717
740
|
}
|
|
@@ -762,6 +785,9 @@ function normalizeAddress(raw) {
|
|
|
762
785
|
const trimmed = raw.trim().replace(/^\[(.*)\]$/, "$1");
|
|
763
786
|
return trimmed.replace(/^::ffff:/i, "");
|
|
764
787
|
}
|
|
788
|
+
function resolveSocketAddress(req) {
|
|
789
|
+
return req.socket.remoteAddress ? normalizeAddress(req.socket.remoteAddress) : void 0;
|
|
790
|
+
}
|
|
765
791
|
function parseForwardedEntry(entry) {
|
|
766
792
|
const value = entry.trim();
|
|
767
793
|
if (!value) return void 0;
|
|
@@ -786,7 +812,7 @@ function forwardedChain(req) {
|
|
|
786
812
|
return raw.split(",").map((entry) => parseForwardedEntry(entry)).filter((entry) => entry !== void 0);
|
|
787
813
|
}
|
|
788
814
|
function resolveClientAddress(req, trustedProxyHops = 0) {
|
|
789
|
-
const socketAddress =
|
|
815
|
+
const socketAddress = resolveSocketAddress(req);
|
|
790
816
|
const hops = Number.isFinite(trustedProxyHops) ? Math.max(0, Math.trunc(trustedProxyHops)) : 0;
|
|
791
817
|
if (hops === 0) return socketAddress ?? "unknown";
|
|
792
818
|
const chain = [
|
|
@@ -797,201 +823,6 @@ function resolveClientAddress(req, trustedProxyHops = 0) {
|
|
|
797
823
|
return chain[hops] ?? socketAddress ?? "unknown";
|
|
798
824
|
}
|
|
799
825
|
|
|
800
|
-
// src/hq-server/utils.ts
|
|
801
|
-
import * as fs3 from "node:fs/promises";
|
|
802
|
-
import * as os from "node:os";
|
|
803
|
-
import * as path3 from "node:path";
|
|
804
|
-
import { resolveHqDataDir } from "@wrongstack/core/hq";
|
|
805
|
-
import { sanitizeApiError, scrubErrorDetail } from "@wrongstack/core/security";
|
|
806
|
-
var TRANSCRIPT_RING_MAX = 4e3;
|
|
807
|
-
var MAX_TRANSCRIPT_SESSIONS = 400;
|
|
808
|
-
var MAX_AGENT_RINGS = 800;
|
|
809
|
-
function evictOldest(map, max) {
|
|
810
|
-
while (map.size > max) {
|
|
811
|
-
const oldest = map.keys().next().value;
|
|
812
|
-
if (oldest === void 0) break;
|
|
813
|
-
map.delete(oldest);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
function agentRingKey(sessionId, subId) {
|
|
817
|
-
return sessionId ? `${sessionId}::${subId}` : subId;
|
|
818
|
-
}
|
|
819
|
-
function agentMessageToEntry(p) {
|
|
820
|
-
const kind = typeof p["kind"] === "string" ? p["kind"] : "text";
|
|
821
|
-
const role = kind === "thinking" ? "thinking" : kind === "tool_use" || kind === "tool_result" ? "tool" : kind === "error" ? "error" : kind === "status" || kind === "system" ? "system" : "assistant";
|
|
822
|
-
return {
|
|
823
|
-
ts: typeof p["ts"] === "string" ? p["ts"] : (/* @__PURE__ */ new Date()).toISOString(),
|
|
824
|
-
role,
|
|
825
|
-
text: typeof p["content"] === "string" ? p["content"] : "",
|
|
826
|
-
...typeof p["toolName"] === "string" ? { tool: p["toolName"] } : {},
|
|
827
|
-
...kind === "error" ? { isError: true } : {}
|
|
828
|
-
};
|
|
829
|
-
}
|
|
830
|
-
async function readLocalSubagentTranscript(sessionId, subagentId) {
|
|
831
|
-
try {
|
|
832
|
-
const { getSessionRegistry } = await import("@wrongstack/core/storage");
|
|
833
|
-
const { resolveWstackPaths, sessionScopedPath } = await import("@wrongstack/core/utils");
|
|
834
|
-
const globalRoot = path3.dirname(resolveHqDataDir());
|
|
835
|
-
const registry = getSessionRegistry(globalRoot);
|
|
836
|
-
const entry = await registry.get(sessionId).catch(() => null);
|
|
837
|
-
if (!entry) return null;
|
|
838
|
-
const paths = resolveWstackPaths({ projectRoot: entry.projectRoot, globalRoot });
|
|
839
|
-
const sessionDir = sessionScopedPath(paths.projectSessions, sessionId, "");
|
|
840
|
-
if (!isSafePathSegment(subagentId)) return null;
|
|
841
|
-
const transcriptsRoot = path3.resolve(sessionDir, "subagents", "transcripts");
|
|
842
|
-
const file = path3.resolve(transcriptsRoot, subagentId, "transcript.jsonl");
|
|
843
|
-
if (!file.startsWith(transcriptsRoot + path3.sep)) return null;
|
|
844
|
-
const raw = await fs3.readFile(file, "utf8").catch(() => null);
|
|
845
|
-
if (raw === null) return null;
|
|
846
|
-
const out = [];
|
|
847
|
-
for (const line of raw.split("\n")) {
|
|
848
|
-
const trimmed = line.trim();
|
|
849
|
-
if (trimmed.length === 0) continue;
|
|
850
|
-
try {
|
|
851
|
-
out.push(agentMessageToEntry(JSON.parse(trimmed)));
|
|
852
|
-
} catch {
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
return out;
|
|
856
|
-
} catch {
|
|
857
|
-
return null;
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
function decodePathSegment(value) {
|
|
861
|
-
let decoded;
|
|
862
|
-
try {
|
|
863
|
-
decoded = decodeURIComponent(value);
|
|
864
|
-
} catch {
|
|
865
|
-
return null;
|
|
866
|
-
}
|
|
867
|
-
return isSafePathSegment(decoded) ? decoded : null;
|
|
868
|
-
}
|
|
869
|
-
function decodeSessionId(value) {
|
|
870
|
-
let decoded;
|
|
871
|
-
try {
|
|
872
|
-
decoded = decodeURIComponent(value);
|
|
873
|
-
} catch {
|
|
874
|
-
return null;
|
|
875
|
-
}
|
|
876
|
-
return isSafeSessionId(decoded) ? decoded : null;
|
|
877
|
-
}
|
|
878
|
-
var MAX_PATH_SEGMENT_LENGTH = 256;
|
|
879
|
-
function isSafePathSegment(value) {
|
|
880
|
-
if (value.length === 0 || value.length > MAX_PATH_SEGMENT_LENGTH) return false;
|
|
881
|
-
if (value === "." || value === "..") return false;
|
|
882
|
-
if (value.includes("/") || value.includes("\\")) return false;
|
|
883
|
-
if (value.includes("\0")) return false;
|
|
884
|
-
if (value.includes(":")) return false;
|
|
885
|
-
return true;
|
|
886
|
-
}
|
|
887
|
-
function isSafeSessionId(value) {
|
|
888
|
-
if (value.length === 0 || value.length > MAX_PATH_SEGMENT_LENGTH) return false;
|
|
889
|
-
if (value.includes("\\") || value.includes("\0") || value.includes(":")) return false;
|
|
890
|
-
const parts = value.split("/");
|
|
891
|
-
if (parts.length > 2) return false;
|
|
892
|
-
return parts.every((p) => p.length > 0 && p !== "." && p !== "..");
|
|
893
|
-
}
|
|
894
|
-
function displayHost(host) {
|
|
895
|
-
return host === "0.0.0.0" ? "127.0.0.1" : host;
|
|
896
|
-
}
|
|
897
|
-
var RequestBodyTooLargeError = class extends Error {
|
|
898
|
-
};
|
|
899
|
-
function readRequestBody(req, maxBytes = 1e6) {
|
|
900
|
-
return new Promise((resolve2, reject) => {
|
|
901
|
-
const chunks = [];
|
|
902
|
-
let size = 0;
|
|
903
|
-
let settled = false;
|
|
904
|
-
req.on("data", (chunk) => {
|
|
905
|
-
if (settled) return;
|
|
906
|
-
size += chunk.length;
|
|
907
|
-
if (size > maxBytes) {
|
|
908
|
-
settled = true;
|
|
909
|
-
chunks.length = 0;
|
|
910
|
-
reject(new RequestBodyTooLargeError("request body too large"));
|
|
911
|
-
return;
|
|
912
|
-
}
|
|
913
|
-
chunks.push(chunk);
|
|
914
|
-
});
|
|
915
|
-
req.on("end", () => {
|
|
916
|
-
if (!settled) resolve2(Buffer.concat(chunks).toString("utf8"));
|
|
917
|
-
});
|
|
918
|
-
req.on("error", (error) => {
|
|
919
|
-
if (!settled) reject(error);
|
|
920
|
-
});
|
|
921
|
-
});
|
|
922
|
-
}
|
|
923
|
-
function writeInvalidBody(res, error) {
|
|
924
|
-
const tooLarge = error instanceof RequestBodyTooLargeError;
|
|
925
|
-
res.writeHead(tooLarge ? 413 : 400, { "Content-Type": "application/json" });
|
|
926
|
-
res.end(JSON.stringify({ error: tooLarge ? "request body too large" : "invalid json body" }));
|
|
927
|
-
}
|
|
928
|
-
function buildHttpUrl(host, port, token) {
|
|
929
|
-
const url = new URL(`http://${displayHost(host)}:${port}/`);
|
|
930
|
-
if (token) url.searchParams.set("token", token);
|
|
931
|
-
return url.toString();
|
|
932
|
-
}
|
|
933
|
-
function buildBootstrapHttpUrl(host, port, bootstrapCode) {
|
|
934
|
-
const url = new URL(`http://${displayHost(host)}:${port}/`);
|
|
935
|
-
url.hash = `bootstrap=${bootstrapCode}`;
|
|
936
|
-
return url.toString();
|
|
937
|
-
}
|
|
938
|
-
function buildClientWsUrl(host, port, token) {
|
|
939
|
-
const url = new URL(`ws://${displayHost(host)}:${port}/ws/client`);
|
|
940
|
-
if (token) url.searchParams.set("token", token);
|
|
941
|
-
return url.toString();
|
|
942
|
-
}
|
|
943
|
-
function hqRuntimeMarkerPath(dataDir) {
|
|
944
|
-
return path3.join(dataDir, "runtime.json");
|
|
945
|
-
}
|
|
946
|
-
function lanIPv4Addresses() {
|
|
947
|
-
const out = [];
|
|
948
|
-
try {
|
|
949
|
-
const ifaces = os.networkInterfaces();
|
|
950
|
-
for (const name of Object.keys(ifaces)) {
|
|
951
|
-
for (const ni of ifaces[name] ?? []) {
|
|
952
|
-
if (ni.family === "IPv4" && !ni.internal) out.push(ni.address);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
} catch {
|
|
956
|
-
}
|
|
957
|
-
return out;
|
|
958
|
-
}
|
|
959
|
-
function truncateHqSummary(value, maxLength) {
|
|
960
|
-
if (typeof value !== "string" || value.length === 0) return void 0;
|
|
961
|
-
if (value.length <= maxLength) return value;
|
|
962
|
-
const suffix = `\u2026[truncated:${value.length - maxLength}]`;
|
|
963
|
-
if (maxLength <= suffix.length) return "\u2026";
|
|
964
|
-
const prefixLen = maxLength - suffix.length;
|
|
965
|
-
return `${value.slice(0, prefixLen)}${suffix}`;
|
|
966
|
-
}
|
|
967
|
-
function hqMachineKey(hostname, machineId) {
|
|
968
|
-
const hn = hostname?.trim();
|
|
969
|
-
return hn ? `host:${hn.toLowerCase()}` : `mid:${machineId || "local"}`;
|
|
970
|
-
}
|
|
971
|
-
function recordTimeseriesSignal(persistence, event) {
|
|
972
|
-
try {
|
|
973
|
-
if (event.type === "session.usage") {
|
|
974
|
-
const p = event.payload;
|
|
975
|
-
persistence.timeseries.record({
|
|
976
|
-
ts: Date.parse(event.timestamp) || Date.now(),
|
|
977
|
-
...typeof p.costUsd === "number" ? { costUsd: p.costUsd } : {},
|
|
978
|
-
...typeof p.inputTokens === "number" ? { inputTokens: p.inputTokens } : {},
|
|
979
|
-
...typeof p.outputTokens === "number" ? { outputTokens: p.outputTokens } : {},
|
|
980
|
-
...typeof p.model === "string" && p.model.length > 0 ? { model: p.model } : {},
|
|
981
|
-
...typeof p.provider === "string" && p.provider.length > 0 ? { provider: p.provider } : {},
|
|
982
|
-
...typeof p.cacheRead === "number" ? { cacheRead: p.cacheRead } : {},
|
|
983
|
-
...typeof p.cacheWrite === "number" ? { cacheWrite: p.cacheWrite } : {}
|
|
984
|
-
});
|
|
985
|
-
} else if (event.type === "tool.completed") {
|
|
986
|
-
persistence.timeseries.record({
|
|
987
|
-
ts: Date.parse(event.timestamp) || Date.now(),
|
|
988
|
-
toolCalls: 1
|
|
989
|
-
});
|
|
990
|
-
}
|
|
991
|
-
} catch {
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
|
|
995
826
|
// src/hq-server/routes/auth/password-routes.ts
|
|
996
827
|
async function handleApiAuthStatus(req, res, url, mutableAuth, sessions, requireBrowserAuth, trustedPublicOrigins, secureCookies) {
|
|
997
828
|
const auth = authenticateBrowserRequest(req, url, mutableAuth, sessions);
|
|
@@ -1013,7 +844,7 @@ async function handleApiAuthStatus(req, res, url, mutableAuth, sessions, require
|
|
|
1013
844
|
})
|
|
1014
845
|
);
|
|
1015
846
|
}
|
|
1016
|
-
async function handleApiLogin(req, res, mutableAuth, sessions, loginAttempts, secureCookies, trustedProxyHops) {
|
|
847
|
+
async function handleApiLogin(req, res, mutableAuth, sessions, loginAttempts, secureCookies, trustedProxyHops, sessionCapabilities) {
|
|
1017
848
|
if (!mutableAuth.passwordHash) {
|
|
1018
849
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
1019
850
|
res.end(
|
|
@@ -1076,7 +907,8 @@ async function handleApiLogin(req, res, mutableAuth, sessions, loginAttempts, se
|
|
|
1076
907
|
createdAt: Date.now(),
|
|
1077
908
|
kind: "password",
|
|
1078
909
|
pending2fa: true,
|
|
1079
|
-
lastSeenAt: Date.now()
|
|
910
|
+
lastSeenAt: Date.now(),
|
|
911
|
+
...sessionCapabilities !== void 0 ? { capabilities: sessionCapabilities } : {}
|
|
1080
912
|
});
|
|
1081
913
|
setHqSessionCookie(
|
|
1082
914
|
res,
|
|
@@ -1089,7 +921,12 @@ async function handleApiLogin(req, res, mutableAuth, sessions, loginAttempts, se
|
|
|
1089
921
|
}
|
|
1090
922
|
loginAttempts.clearOnSuccess(clientIp, body.password);
|
|
1091
923
|
const sessionId = randomUUID();
|
|
1092
|
-
sessions.set(sessionId, {
|
|
924
|
+
sessions.set(sessionId, {
|
|
925
|
+
createdAt: Date.now(),
|
|
926
|
+
kind: "password",
|
|
927
|
+
lastSeenAt: Date.now(),
|
|
928
|
+
...sessionCapabilities !== void 0 ? { capabilities: sessionCapabilities } : {}
|
|
929
|
+
});
|
|
1093
930
|
setHqSessionCookie(
|
|
1094
931
|
res,
|
|
1095
932
|
serializeHqSessionCookie(sessionId, mutableAuth.cookieSecret),
|
|
@@ -1359,7 +1196,8 @@ async function handleApiLoginVerify(req, res, _url, mutableAuth, sessions, login
|
|
|
1359
1196
|
sessions.set(fullSessionId, {
|
|
1360
1197
|
createdAt: Date.now(),
|
|
1361
1198
|
kind: "password",
|
|
1362
|
-
lastSeenAt: Date.now()
|
|
1199
|
+
lastSeenAt: Date.now(),
|
|
1200
|
+
...session.capabilities !== void 0 ? { capabilities: session.capabilities } : {}
|
|
1363
1201
|
});
|
|
1364
1202
|
setHqSessionCookie(
|
|
1365
1203
|
res,
|
|
@@ -1399,8 +1237,8 @@ async function handleApiLoginVerify(req, res, _url, mutableAuth, sessions, login
|
|
|
1399
1237
|
await recoveryCodeLock;
|
|
1400
1238
|
let releaseLock = () => {
|
|
1401
1239
|
};
|
|
1402
|
-
recoveryCodeLock = new Promise((
|
|
1403
|
-
releaseLock =
|
|
1240
|
+
recoveryCodeLock = new Promise((resolve) => {
|
|
1241
|
+
releaseLock = resolve;
|
|
1404
1242
|
});
|
|
1405
1243
|
try {
|
|
1406
1244
|
const next = await mutateHqAuthFile2(dataDir, (current) => {
|
|
@@ -1438,7 +1276,8 @@ async function handleApiLoginVerify(req, res, _url, mutableAuth, sessions, login
|
|
|
1438
1276
|
sessions.set(fullSessionId, {
|
|
1439
1277
|
createdAt: Date.now(),
|
|
1440
1278
|
kind: "password",
|
|
1441
|
-
lastSeenAt: Date.now()
|
|
1279
|
+
lastSeenAt: Date.now(),
|
|
1280
|
+
...session.capabilities !== void 0 ? { capabilities: session.capabilities } : {}
|
|
1442
1281
|
});
|
|
1443
1282
|
setHqSessionCookie(
|
|
1444
1283
|
res,
|
|
@@ -2255,7 +2094,7 @@ async function authorizeHqCommand(input) {
|
|
|
2255
2094
|
}
|
|
2256
2095
|
|
|
2257
2096
|
// src/hq-server/routes/mailbox-handlers.ts
|
|
2258
|
-
import * as
|
|
2097
|
+
import * as path3 from "node:path";
|
|
2259
2098
|
import { resolveProjectDir } from "@wrongstack/core/coordination";
|
|
2260
2099
|
|
|
2261
2100
|
// src/hq-server/project-root.ts
|
|
@@ -2311,7 +2150,7 @@ async function handleMailboxGateway(_req, res, match, authorizeMailboxGateway, g
|
|
|
2311
2150
|
);
|
|
2312
2151
|
return;
|
|
2313
2152
|
}
|
|
2314
|
-
const gatewayGlobalRoot =
|
|
2153
|
+
const gatewayGlobalRoot = path3.dirname(dataDir);
|
|
2315
2154
|
const preliminaryAccess = authorizeMailboxGateway(_req, `project:${projectId}`);
|
|
2316
2155
|
if (!preliminaryAccess.allowed) {
|
|
2317
2156
|
res.writeHead(preliminaryAccess.status ?? 401, {
|
|
@@ -2377,7 +2216,7 @@ async function handleApiMailboxSend(_req, res, mutableAuth, sessions, dataDir, g
|
|
|
2377
2216
|
res.end(JSON.stringify({ error: "audience must be all or leaders" }));
|
|
2378
2217
|
return;
|
|
2379
2218
|
}
|
|
2380
|
-
const mbGlobalRoot =
|
|
2219
|
+
const mbGlobalRoot = path3.dirname(dataDir);
|
|
2381
2220
|
const projectRoot = await resolveHqProjectRoot(mbGlobalRoot, {
|
|
2382
2221
|
sessionId: mbody.sessionId,
|
|
2383
2222
|
projectId: mbody.projectId
|
|
@@ -2497,7 +2336,7 @@ async function handleMailboxAction(_req, res, match, mutableAuth, sessions, data
|
|
|
2497
2336
|
res.end(JSON.stringify({ error: "missing sessionId or projectId" }));
|
|
2498
2337
|
return;
|
|
2499
2338
|
}
|
|
2500
|
-
const actGlobalRoot =
|
|
2339
|
+
const actGlobalRoot = path3.dirname(dataDir);
|
|
2501
2340
|
const projectRoot = await resolveHqProjectRoot(actGlobalRoot, {
|
|
2502
2341
|
sessionId: abody.sessionId,
|
|
2503
2342
|
projectId: abody.projectId
|
|
@@ -2593,6 +2432,15 @@ async function handleApiCommand(req, res, url, mutableAuth, sessions, clients, b
|
|
|
2593
2432
|
res.end(JSON.stringify({ error: "unrecognized or malformed command", type: body.type }));
|
|
2594
2433
|
return;
|
|
2595
2434
|
}
|
|
2435
|
+
if (validated.type === "run-command" && isCookieAuth(auth) && auth.capabilities !== void 0 && !auth.capabilities.includes("control.execute")) {
|
|
2436
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2437
|
+
res.end(
|
|
2438
|
+
JSON.stringify({
|
|
2439
|
+
error: "forbidden: browser session lacks control.execute capability"
|
|
2440
|
+
})
|
|
2441
|
+
);
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2596
2444
|
if (validated.type === "run-command" && !tokenHasCapability(target.authToken, "control.execute")) {
|
|
2597
2445
|
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2598
2446
|
res.end(
|
|
@@ -2678,14 +2526,14 @@ async function handleApiTrendsCost(req, res, persistence) {
|
|
|
2678
2526
|
}
|
|
2679
2527
|
|
|
2680
2528
|
// src/hq-server/routes/session-handlers.ts
|
|
2681
|
-
import * as
|
|
2529
|
+
import * as path4 from "node:path";
|
|
2682
2530
|
import {
|
|
2683
2531
|
buildTranscriptFromEvents,
|
|
2684
|
-
resolveHqDataDir
|
|
2532
|
+
resolveHqDataDir
|
|
2685
2533
|
} from "@wrongstack/core/hq";
|
|
2686
2534
|
async function handleApiSessions(res) {
|
|
2687
2535
|
const { getSessionRegistry } = await import("@wrongstack/core/storage");
|
|
2688
|
-
const globalRoot =
|
|
2536
|
+
const globalRoot = path4.dirname(resolveHqDataDir());
|
|
2689
2537
|
try {
|
|
2690
2538
|
const registry = getSessionRegistry(globalRoot);
|
|
2691
2539
|
const sessions = await registry.list();
|
|
@@ -2730,7 +2578,7 @@ async function handleApiSessionEvents(req, res, match, transcripts) {
|
|
|
2730
2578
|
res.end(JSON.stringify({ error: "invalid sessionId encoding" }));
|
|
2731
2579
|
return;
|
|
2732
2580
|
}
|
|
2733
|
-
const globalRoot =
|
|
2581
|
+
const globalRoot = path4.dirname(resolveHqDataDir());
|
|
2734
2582
|
try {
|
|
2735
2583
|
const registry = getSessionRegistry(globalRoot);
|
|
2736
2584
|
const entry = await registry.get(sessionId).catch(() => null);
|
|
@@ -2957,12 +2805,26 @@ function createHqRouter(deps) {
|
|
|
2957
2805
|
trustBoundary,
|
|
2958
2806
|
bootstrapStore,
|
|
2959
2807
|
applyAuthFile,
|
|
2960
|
-
trustedProxyHops = 0
|
|
2808
|
+
trustedProxyHops = 0,
|
|
2809
|
+
ipAllowlist
|
|
2961
2810
|
} = deps;
|
|
2962
2811
|
return async (req, res) => {
|
|
2963
2812
|
try {
|
|
2964
2813
|
const url = new URL(req.url ?? "/", `http://${host}:${listeningPort()}`);
|
|
2965
2814
|
setHqSecurityHeaders(res);
|
|
2815
|
+
if (ipAllowlist && !ipAllowlist.allows(resolveSocketAddress(req))) {
|
|
2816
|
+
res.writeHead(403, { "Content-Type": "application/json" });
|
|
2817
|
+
res.end(JSON.stringify({ error: "forbidden: source IP is not allowed" }));
|
|
2818
|
+
return;
|
|
2819
|
+
}
|
|
2820
|
+
if (url.pathname === "/healthz" && req.method === "GET") {
|
|
2821
|
+
res.writeHead(200, {
|
|
2822
|
+
"Content-Type": "application/json",
|
|
2823
|
+
"Cache-Control": "no-store"
|
|
2824
|
+
});
|
|
2825
|
+
res.end(JSON.stringify({ status: "ok" }));
|
|
2826
|
+
return;
|
|
2827
|
+
}
|
|
2966
2828
|
if (!hasTrustedBrowserOrigin(
|
|
2967
2829
|
req,
|
|
2968
2830
|
host,
|
|
@@ -2974,7 +2836,7 @@ function createHqRouter(deps) {
|
|
|
2974
2836
|
res.end(JSON.stringify({ error: "forbidden: untrusted request origin" }));
|
|
2975
2837
|
return;
|
|
2976
2838
|
}
|
|
2977
|
-
if (url.pathname.startsWith("/api/") && url.pathname !== "/api/auth/status" && url.pathname !== "/api/login" && url.pathname !== "/api/login/verify" && url.pathname !== "/api/auth/bootstrap" && hqAuthRequired(mutableAuth, requireBrowserAuth)) {
|
|
2839
|
+
if (url.pathname.startsWith("/api/") && url.pathname !== "/api/auth/status" && url.pathname !== "/api/login" && url.pathname !== "/api/login/verify" && url.pathname !== "/api/mobile/login" && url.pathname !== "/api/mobile/login/verify" && url.pathname !== "/api/auth/bootstrap" && hqAuthRequired(mutableAuth, requireBrowserAuth)) {
|
|
2978
2840
|
const auth = authenticateBrowserRequest(req, url, mutableAuth, sessions);
|
|
2979
2841
|
if (!auth) {
|
|
2980
2842
|
const tokenOnly = mutableAuth.browserTokens.size > 0 && !mutableAuth.passwordHash;
|
|
@@ -3041,7 +2903,7 @@ function createHqRouter(deps) {
|
|
|
3041
2903
|
await handleApiAuthSessionsRevoke(req, res, url, sessions);
|
|
3042
2904
|
return;
|
|
3043
2905
|
}
|
|
3044
|
-
if (url.pathname === "/api/login" && req.method === "POST") {
|
|
2906
|
+
if ((url.pathname === "/api/login" || url.pathname === "/api/mobile/login") && req.method === "POST") {
|
|
3045
2907
|
await handleApiLogin(
|
|
3046
2908
|
req,
|
|
3047
2909
|
res,
|
|
@@ -3049,7 +2911,8 @@ function createHqRouter(deps) {
|
|
|
3049
2911
|
sessions,
|
|
3050
2912
|
loginAttempts,
|
|
3051
2913
|
secureCookies,
|
|
3052
|
-
trustedProxyHops
|
|
2914
|
+
trustedProxyHops,
|
|
2915
|
+
url.pathname === "/api/mobile/login" ? ["control.enqueue"] : void 0
|
|
3053
2916
|
);
|
|
3054
2917
|
return;
|
|
3055
2918
|
}
|
|
@@ -3077,7 +2940,7 @@ function createHqRouter(deps) {
|
|
|
3077
2940
|
await handleApiLogout(req, res, mutableAuth, sessions, secureCookies);
|
|
3078
2941
|
return;
|
|
3079
2942
|
}
|
|
3080
|
-
if (url.pathname === "/api/login/verify" && req.method === "POST") {
|
|
2943
|
+
if ((url.pathname === "/api/login/verify" || url.pathname === "/api/mobile/login/verify") && req.method === "POST") {
|
|
3081
2944
|
await handleApiLoginVerify(
|
|
3082
2945
|
req,
|
|
3083
2946
|
res,
|
|
@@ -3380,12 +3243,12 @@ import {
|
|
|
3380
3243
|
ensureHqFirstRunAuthFile,
|
|
3381
3244
|
HqInsecureExposureError,
|
|
3382
3245
|
isTokenExpired as isTokenExpired3,
|
|
3383
|
-
resolveHqDataDir as
|
|
3246
|
+
resolveHqDataDir as resolveHqDataDir2
|
|
3384
3247
|
} from "@wrongstack/core/hq";
|
|
3385
3248
|
async function prepareHqServerStart(options, defaults) {
|
|
3386
3249
|
const host = options.host ?? defaults.host;
|
|
3387
3250
|
const port = options.port ?? defaults.port;
|
|
3388
|
-
const dataDir =
|
|
3251
|
+
const dataDir = resolveHqDataDir2(options.dataDir);
|
|
3389
3252
|
const firstRunAuth = await ensureHqFirstRunAuthFile(dataDir, {
|
|
3390
3253
|
warn: (message) => console.warn(
|
|
3391
3254
|
JSON.stringify({
|
|
@@ -3397,6 +3260,7 @@ async function prepareHqServerStart(options, defaults) {
|
|
|
3397
3260
|
),
|
|
3398
3261
|
actor: resolveAuditActor(),
|
|
3399
3262
|
...options.password !== void 0 ? { password: options.password } : {},
|
|
3263
|
+
...options.bootstrapPasswordOnly !== void 0 ? { bootstrapPasswordOnly: options.bootstrapPasswordOnly } : {},
|
|
3400
3264
|
...options.tokenTtlMs !== void 0 ? { tokenTtlMs: options.tokenTtlMs } : {}
|
|
3401
3265
|
});
|
|
3402
3266
|
const { authFile } = firstRunAuth;
|
|
@@ -3435,21 +3299,21 @@ async function prepareHqServerStart(options, defaults) {
|
|
|
3435
3299
|
}
|
|
3436
3300
|
|
|
3437
3301
|
// src/hq-server/startup.ts
|
|
3438
|
-
import * as
|
|
3439
|
-
import * as
|
|
3302
|
+
import * as fs3 from "node:fs/promises";
|
|
3303
|
+
import * as path5 from "node:path";
|
|
3440
3304
|
import { writeHqRuntimeFile } from "@wrongstack/core/hq";
|
|
3441
3305
|
var hqRuntimeMarkerPath2 = hqRuntimeMarkerPath;
|
|
3442
3306
|
async function writeHqRuntimeMarker(dataDir, url) {
|
|
3443
|
-
await
|
|
3307
|
+
await fs3.mkdir(dataDir, { recursive: true });
|
|
3444
3308
|
await writeHqRuntimeFile(dataDir, { url, pid: process.pid });
|
|
3445
3309
|
}
|
|
3446
3310
|
async function clearHqRuntimeMarker(dataDir, url) {
|
|
3447
3311
|
const file = hqRuntimeMarkerPath2(dataDir);
|
|
3448
3312
|
try {
|
|
3449
|
-
const raw = await
|
|
3313
|
+
const raw = await fs3.readFile(file, "utf8");
|
|
3450
3314
|
const parsed = JSON.parse(raw);
|
|
3451
3315
|
if (parsed.url === url && parsed.pid === process.pid) {
|
|
3452
|
-
await
|
|
3316
|
+
await fs3.rm(file, { force: true });
|
|
3453
3317
|
}
|
|
3454
3318
|
} catch {
|
|
3455
3319
|
}
|
|
@@ -3458,6 +3322,8 @@ function writeHqStartupInfo(write, handle) {
|
|
|
3458
3322
|
const startup = handle.firstRunSetup;
|
|
3459
3323
|
const browserUrl = buildHttpUrl(handle.host, handle.port);
|
|
3460
3324
|
const clientUrl = buildClientWsUrl(handle.host, handle.port);
|
|
3325
|
+
const mobileUrl = new URL(browserUrl);
|
|
3326
|
+
mobileUrl.pathname = "/mobile";
|
|
3461
3327
|
write(
|
|
3462
3328
|
`${terminalText("WrongStack HQ", "magenta")} ${terminalText("listening on", "green")} ${terminalLink(browserUrl)}
|
|
3463
3329
|
`
|
|
@@ -3465,13 +3331,39 @@ function writeHqStartupInfo(write, handle) {
|
|
|
3465
3331
|
if (!startup) {
|
|
3466
3332
|
write(`${terminalText("Browser endpoint:", "blue")} ${terminalLink(browserUrl)}
|
|
3467
3333
|
`);
|
|
3334
|
+
write(
|
|
3335
|
+
`${terminalText("Mobile endpoint:", "blue")} ${terminalLink(mobileUrl.toString())} (password required)
|
|
3336
|
+
`
|
|
3337
|
+
);
|
|
3338
|
+
write(`${terminalText("Client endpoint:", "blue")} ${terminalLink(clientUrl)}
|
|
3339
|
+
`);
|
|
3340
|
+
writeHqLanEndpoints(write, handle, void 0);
|
|
3341
|
+
return;
|
|
3342
|
+
}
|
|
3343
|
+
if (process.env.WRONGSTACK_HQ_SUPPRESS_STARTUP_SECRETS === "1") {
|
|
3344
|
+
write(`${terminalText("Browser endpoint:", "blue")} ${terminalLink(browserUrl)}
|
|
3345
|
+
`);
|
|
3346
|
+
write(
|
|
3347
|
+
`${terminalText("Mobile endpoint:", "blue")} ${terminalLink(mobileUrl.toString())} (password required)
|
|
3348
|
+
`
|
|
3349
|
+
);
|
|
3468
3350
|
write(`${terminalText("Client endpoint:", "blue")} ${terminalLink(clientUrl)}
|
|
3351
|
+
`);
|
|
3352
|
+
write(`Startup credentials suppressed; auth state is stored in ${startup.dataDir}.
|
|
3469
3353
|
`);
|
|
3470
3354
|
writeHqLanEndpoints(write, handle, void 0);
|
|
3471
3355
|
return;
|
|
3472
3356
|
}
|
|
3357
|
+
const firstRunMobileUrl = new URL(startup.browserUrl);
|
|
3358
|
+
firstRunMobileUrl.pathname = "/mobile";
|
|
3359
|
+
firstRunMobileUrl.search = "";
|
|
3360
|
+
firstRunMobileUrl.hash = "";
|
|
3473
3361
|
write(`${terminalText("Browser endpoint:", "blue")} ${terminalLink(startup.browserUrl)}
|
|
3474
3362
|
`);
|
|
3363
|
+
write(
|
|
3364
|
+
`${terminalText("Mobile endpoint:", "blue")} ${terminalLink(firstRunMobileUrl.toString())} (password required)
|
|
3365
|
+
`
|
|
3366
|
+
);
|
|
3475
3367
|
write(`${terminalText("Client endpoint:", "blue")} ${terminalLink(startup.clientUrl)}
|
|
3476
3368
|
`);
|
|
3477
3369
|
if (startup.createdAuth) {
|
|
@@ -3489,7 +3381,7 @@ HQ auth loaded from ${startup.dataDir}
|
|
|
3489
3381
|
if (startup.clientEnv.WRONGSTACK_HQ_TOKEN) {
|
|
3490
3382
|
write(` WRONGSTACK_HQ_TOKEN=${startup.clientEnv.WRONGSTACK_HQ_TOKEN}
|
|
3491
3383
|
`);
|
|
3492
|
-
write(`Credentials are stored in ${
|
|
3384
|
+
write(`Credentials are stored in ${path5.join(startup.dataDir, "auth.json")}
|
|
3493
3385
|
`);
|
|
3494
3386
|
}
|
|
3495
3387
|
writeHqLanEndpoints(write, handle, void 0);
|
|
@@ -3567,7 +3459,7 @@ function createHqServerShutdown(params) {
|
|
|
3567
3459
|
}
|
|
3568
3460
|
|
|
3569
3461
|
// src/hq-server/upgrade-handler.ts
|
|
3570
|
-
import {
|
|
3462
|
+
import { hqTokenVerifier as hqTokenVerifier2, isLoopbackHost as isLoopbackHost3 } from "@wrongstack/core/hq";
|
|
3571
3463
|
|
|
3572
3464
|
// src/hq-server/ws.ts
|
|
3573
3465
|
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
@@ -4345,6 +4237,15 @@ function handleHqUpgrade(req, socket, head, deps) {
|
|
|
4345
4237
|
socket.destroy();
|
|
4346
4238
|
return;
|
|
4347
4239
|
}
|
|
4240
|
+
if (deps.ipAllowlist && !deps.ipAllowlist.allows(resolveSocketAddress(req))) {
|
|
4241
|
+
socket.write(
|
|
4242
|
+
"HTTP/1.1 403 Forbidden\r\nContent-Type: application/json\r\nConnection: close\r\n\r\n" + JSON.stringify({
|
|
4243
|
+
error: { code: "IP_NOT_ALLOWED", message: "Source IP is not allowed." }
|
|
4244
|
+
})
|
|
4245
|
+
);
|
|
4246
|
+
socket.destroy();
|
|
4247
|
+
return;
|
|
4248
|
+
}
|
|
4348
4249
|
if (!hasTrustedBrowserOrigin2(
|
|
4349
4250
|
req,
|
|
4350
4251
|
deps.host,
|
|
@@ -4471,6 +4372,7 @@ async function startHqServer(options = {}) {
|
|
|
4471
4372
|
}
|
|
4472
4373
|
async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth) {
|
|
4473
4374
|
const trustBoundary = options.trustBoundary ?? createCompatibilityTrustBoundary({ policyId: "hq-trusted-host-compat-v1" });
|
|
4375
|
+
const ipAllowlist = createHqIpAllowlist(options.ipAllowlist);
|
|
4474
4376
|
const authFile = firstRunAuth.authFile;
|
|
4475
4377
|
const reassessExposureFloor = (live) => {
|
|
4476
4378
|
const exposure = assessHqExposure2({
|
|
@@ -4528,10 +4430,12 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4528
4430
|
browserTokenMode: mutableAuth.browserTokens.size > 0,
|
|
4529
4431
|
clientTokenMode: mutableAuth.clientTokens.size > 0,
|
|
4530
4432
|
passwordMode: mutableAuth.passwordHash !== void 0,
|
|
4433
|
+
ipAllowlistActive: ipAllowlist !== void 0,
|
|
4434
|
+
ipAllowlistRules: ipAllowlist?.entries.length ?? 0,
|
|
4531
4435
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
4532
4436
|
})
|
|
4533
4437
|
);
|
|
4534
|
-
return new Promise((
|
|
4438
|
+
return new Promise((resolve, reject) => {
|
|
4535
4439
|
const trustedPublicOrigins = /* @__PURE__ */ new Set();
|
|
4536
4440
|
let listeningPort = port;
|
|
4537
4441
|
const clients = /* @__PURE__ */ new Map();
|
|
@@ -4577,13 +4481,13 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4577
4481
|
const persistence = createHqPersistence(dataDir);
|
|
4578
4482
|
const auditLog = new HqCommandAuditLog(1e3, (entry) => persistence.commandLog.append(entry));
|
|
4579
4483
|
void (async () => {
|
|
4580
|
-
const projectsDir =
|
|
4581
|
-
const entries = await
|
|
4484
|
+
const projectsDir = path6.join(path6.dirname(dataDir), "projects");
|
|
4485
|
+
const entries = await fs4.readdir(projectsDir, { withFileTypes: true }).catch(() => []);
|
|
4582
4486
|
await Promise.all(
|
|
4583
4487
|
entries.filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
4584
|
-
const projectDir =
|
|
4585
|
-
const metadataPath =
|
|
4586
|
-
const ownerPid = await
|
|
4488
|
+
const projectDir = path6.join(projectsDir, entry.name);
|
|
4489
|
+
const metadataPath = path6.join(projectDir, ".mailbox-server.json");
|
|
4490
|
+
const ownerPid = await fs4.readFile(metadataPath, "utf8").then((raw) => JSON.parse(raw).pid).catch(() => void 0);
|
|
4587
4491
|
if (ownerPid === void 0) return;
|
|
4588
4492
|
let ownerAlive = false;
|
|
4589
4493
|
try {
|
|
@@ -4593,7 +4497,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4593
4497
|
ownerAlive = false;
|
|
4594
4498
|
}
|
|
4595
4499
|
if (!ownerAlive) {
|
|
4596
|
-
await
|
|
4500
|
+
await fs4.rm(metadataPath, { force: true }).catch(() => {
|
|
4597
4501
|
});
|
|
4598
4502
|
return;
|
|
4599
4503
|
}
|
|
@@ -4722,6 +4626,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4722
4626
|
getTokenStats: () => authState.tokenStats(),
|
|
4723
4627
|
applyAuthFile: (next) => authState.apply(next),
|
|
4724
4628
|
trustedProxyHops: options.trustedProxyHops ?? 0,
|
|
4629
|
+
ipAllowlist,
|
|
4725
4630
|
bootstrapStore
|
|
4726
4631
|
};
|
|
4727
4632
|
const handleRequest = createHqRouter(routerDeps);
|
|
@@ -4757,7 +4662,8 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4757
4662
|
persistence,
|
|
4758
4663
|
auditLog,
|
|
4759
4664
|
snapshotBroadcaster,
|
|
4760
|
-
wss
|
|
4665
|
+
wss,
|
|
4666
|
+
ipAllowlist
|
|
4761
4667
|
};
|
|
4762
4668
|
httpServer.on("upgrade", (req, socket, head) => {
|
|
4763
4669
|
handleHqUpgrade(req, socket, head, upgradeDeps);
|
|
@@ -4907,7 +4813,7 @@ async function startHqServerWithAuth(options, host, port, dataDir, firstRunAuth)
|
|
|
4907
4813
|
port: actualPort,
|
|
4908
4814
|
firstRunSetup: startupInfo
|
|
4909
4815
|
});
|
|
4910
|
-
|
|
4816
|
+
resolve(handle);
|
|
4911
4817
|
})();
|
|
4912
4818
|
});
|
|
4913
4819
|
});
|
|
@@ -4926,4 +4832,4 @@ export {
|
|
|
4926
4832
|
startHqServer,
|
|
4927
4833
|
HqInsecureExposureError2 as HqInsecureExposureError
|
|
4928
4834
|
};
|
|
4929
|
-
//# sourceMappingURL=chunk-
|
|
4835
|
+
//# sourceMappingURL=chunk-3I5ZJXM5.js.map
|