@rubytech/create-maxy 1.0.629 → 1.0.630
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 +34 -0
- package/package.json +1 -1
- package/payload/platform/lib/graph-mcp/dist/index.d.ts +26 -0
- package/payload/platform/lib/graph-mcp/dist/index.d.ts.map +1 -0
- package/payload/platform/lib/graph-mcp/dist/index.js +193 -0
- package/payload/platform/lib/graph-mcp/dist/index.js.map +1 -0
- package/payload/platform/lib/graph-mcp/src/index.ts +225 -0
- package/payload/platform/lib/graph-mcp/tsconfig.json +8 -0
- package/payload/platform/package.json +2 -2
- package/payload/platform/plugins/docs/references/memory-guide.md +8 -0
- package/payload/platform/plugins/memory/mcp/scripts/graph/accept.sh +129 -0
- package/payload/platform/plugins/memory/mcp/scripts/graph/fixture.cypher +59 -0
- package/payload/platform/plugins/memory/references/graph-primitives.md +195 -0
- package/payload/server/public/assets/admin-DirN63aF.js +352 -0
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +249 -6
- package/payload/server/public/assets/admin-CGIu9HnV.js +0 -352
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Maxy</title>
|
|
7
7
|
<link rel="icon" href="/favicon.ico">
|
|
8
|
-
<script type="module" crossorigin src="/assets/admin-
|
|
8
|
+
<script type="module" crossorigin src="/assets/admin-DirN63aF.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/chunk-Be6NvmcD.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/preload-helper-rov5CBGT.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/useVoiceRecorder-tbj4tUsl.js">
|
package/payload/server/server.js
CHANGED
|
@@ -3610,8 +3610,6 @@ function handleUpgrade(req, clientSocket, head, opts) {
|
|
|
3610
3610
|
const qsIndex = url2.indexOf("?");
|
|
3611
3611
|
const pathname = qsIndex === -1 ? url2 : url2.slice(0, qsIndex);
|
|
3612
3612
|
if (pathname !== WS_PATH) {
|
|
3613
|
-
vncLog("ws-upgrade", { decision: "rejected", reason: "wrong-path", path: pathname });
|
|
3614
|
-
clientSocket.destroy();
|
|
3615
3613
|
return;
|
|
3616
3614
|
}
|
|
3617
3615
|
const corrId = newCorrId();
|
|
@@ -3828,9 +3826,203 @@ Content-Length: 0\r
|
|
|
3828
3826
|
socket.destroy();
|
|
3829
3827
|
}
|
|
3830
3828
|
|
|
3829
|
+
// server/graph-proxy.ts
|
|
3830
|
+
import { createConnection as createConnection2 } from "net";
|
|
3831
|
+
var GRAPH_PREFIX = "/graph";
|
|
3832
|
+
var UPSTREAM_TIMEOUT_MS2 = 5e3;
|
|
3833
|
+
var HOP_BY_HOP2 = /* @__PURE__ */ new Set([
|
|
3834
|
+
"connection",
|
|
3835
|
+
"keep-alive",
|
|
3836
|
+
"proxy-authenticate",
|
|
3837
|
+
"proxy-authorization",
|
|
3838
|
+
"te",
|
|
3839
|
+
"trailer",
|
|
3840
|
+
"transfer-encoding",
|
|
3841
|
+
"upgrade"
|
|
3842
|
+
]);
|
|
3843
|
+
function resolveUpstreamPort() {
|
|
3844
|
+
const uri = process.env.NEO4J_URI ?? "bolt://localhost:7687";
|
|
3845
|
+
const m = uri.match(/:(\d+)$/);
|
|
3846
|
+
const boltPort = m ? parseInt(m[1], 10) : 7687;
|
|
3847
|
+
return boltPort - 213;
|
|
3848
|
+
}
|
|
3849
|
+
var UPSTREAM_HOST = "127.0.0.1";
|
|
3850
|
+
var UPSTREAM_PORT = resolveUpstreamPort();
|
|
3851
|
+
function attachGraphHttpRoutes(app2) {
|
|
3852
|
+
const handler = async (c) => {
|
|
3853
|
+
const raw2 = c.req.raw;
|
|
3854
|
+
const url2 = new URL(raw2.url);
|
|
3855
|
+
const pathAfterPrefix = url2.pathname.slice(GRAPH_PREFIX.length) || "/";
|
|
3856
|
+
const upstreamUrl = `http://${UPSTREAM_HOST}:${UPSTREAM_PORT}${pathAfterPrefix}${url2.search}`;
|
|
3857
|
+
const upstreamHeaders = new Headers(raw2.headers);
|
|
3858
|
+
for (const h of HOP_BY_HOP2) upstreamHeaders.delete(h);
|
|
3859
|
+
upstreamHeaders.set("host", `${UPSTREAM_HOST}:${UPSTREAM_PORT}`);
|
|
3860
|
+
try {
|
|
3861
|
+
const upstream = await fetch(upstreamUrl, {
|
|
3862
|
+
method: raw2.method,
|
|
3863
|
+
headers: upstreamHeaders,
|
|
3864
|
+
body: raw2.body,
|
|
3865
|
+
// `duplex: 'half'` is required when forwarding a streaming body; TS
|
|
3866
|
+
// typings do not yet expose it but the undici runtime supports it.
|
|
3867
|
+
...raw2.body ? { duplex: "half" } : {},
|
|
3868
|
+
redirect: "manual"
|
|
3869
|
+
});
|
|
3870
|
+
const resHeaders = new Headers(upstream.headers);
|
|
3871
|
+
for (const h of HOP_BY_HOP2) resHeaders.delete(h);
|
|
3872
|
+
return new Response(upstream.body, {
|
|
3873
|
+
status: upstream.status,
|
|
3874
|
+
statusText: upstream.statusText,
|
|
3875
|
+
headers: resHeaders
|
|
3876
|
+
});
|
|
3877
|
+
} catch (err) {
|
|
3878
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
3879
|
+
console.error(`[graph-proxy] upstream fetch failed for ${upstreamUrl}: ${msg}`);
|
|
3880
|
+
return c.text(`Graph proxy upstream unreachable: ${msg}`, 502);
|
|
3881
|
+
}
|
|
3882
|
+
};
|
|
3883
|
+
app2.all(GRAPH_PREFIX, handler);
|
|
3884
|
+
app2.all(`${GRAPH_PREFIX}/*`, handler);
|
|
3885
|
+
}
|
|
3886
|
+
function attachGraphWsProxy(server, opts) {
|
|
3887
|
+
server.on("upgrade", (req, clientSocket, head) => {
|
|
3888
|
+
try {
|
|
3889
|
+
const url2 = req.url ?? "";
|
|
3890
|
+
const qsIndex = url2.indexOf("?");
|
|
3891
|
+
const pathname = qsIndex === -1 ? url2 : url2.slice(0, qsIndex);
|
|
3892
|
+
const isGraphPath = pathname === GRAPH_PREFIX || pathname.startsWith(`${GRAPH_PREFIX}/`);
|
|
3893
|
+
if (!isGraphPath) {
|
|
3894
|
+
if (pathname !== "/websockify") {
|
|
3895
|
+
clientSocket.destroy();
|
|
3896
|
+
}
|
|
3897
|
+
return;
|
|
3898
|
+
}
|
|
3899
|
+
const hostHeader = (req.headers.host ?? "").split(":")[0];
|
|
3900
|
+
const remote = req.socket.remoteAddress;
|
|
3901
|
+
const xff = headerString2(req.headers["x-forwarded-for"]);
|
|
3902
|
+
const cookie = headerString2(req.headers.cookie);
|
|
3903
|
+
const decision = opts.canAccessAdmin({
|
|
3904
|
+
host: hostHeader,
|
|
3905
|
+
remoteAddress: remote,
|
|
3906
|
+
xForwardedFor: xff,
|
|
3907
|
+
cookieHeader: cookie,
|
|
3908
|
+
isPublicHost: opts.isPublicHost
|
|
3909
|
+
});
|
|
3910
|
+
if (!decision.allow) {
|
|
3911
|
+
const status = decision.reason === "public-host" ? 404 : 401;
|
|
3912
|
+
writeStatusAndDestroy2(clientSocket, status, decision.reason === "public-host" ? "Not Found" : "Unauthorized");
|
|
3913
|
+
return;
|
|
3914
|
+
}
|
|
3915
|
+
const originHeader = headerString2(req.headers.origin);
|
|
3916
|
+
const originHost = parseOriginHost2(originHeader);
|
|
3917
|
+
if (!originHost || originHost !== hostHeader || opts.isPublicHost(originHost)) {
|
|
3918
|
+
writeStatusAndDestroy2(clientSocket, 403, "Forbidden");
|
|
3919
|
+
return;
|
|
3920
|
+
}
|
|
3921
|
+
const rewrittenPath = pathname === GRAPH_PREFIX ? "/" : pathname.slice(GRAPH_PREFIX.length);
|
|
3922
|
+
const rewrittenUrl = qsIndex === -1 ? rewrittenPath : rewrittenPath + url2.slice(qsIndex);
|
|
3923
|
+
const upstream = createConnection2({ host: UPSTREAM_HOST, port: UPSTREAM_PORT });
|
|
3924
|
+
upstream.setTimeout(UPSTREAM_TIMEOUT_MS2);
|
|
3925
|
+
upstream.once("connect", () => {
|
|
3926
|
+
upstream.setTimeout(0);
|
|
3927
|
+
const lines = [];
|
|
3928
|
+
lines.push(`${req.method ?? "GET"} ${rewrittenUrl} HTTP/${req.httpVersion}`);
|
|
3929
|
+
lines.push(`host: ${UPSTREAM_HOST}:${UPSTREAM_PORT}`);
|
|
3930
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
3931
|
+
if (name === "host") continue;
|
|
3932
|
+
if (HOP_BY_HOP2.has(name)) continue;
|
|
3933
|
+
if (value == null) continue;
|
|
3934
|
+
if (Array.isArray(value)) {
|
|
3935
|
+
for (const v of value) lines.push(`${name}: ${v}`);
|
|
3936
|
+
} else {
|
|
3937
|
+
lines.push(`${name}: ${value}`);
|
|
3938
|
+
}
|
|
3939
|
+
}
|
|
3940
|
+
const upgradeHeader = headerString2(req.headers.upgrade);
|
|
3941
|
+
const connectionHeader = headerString2(req.headers.connection);
|
|
3942
|
+
if (upgradeHeader) lines.push(`upgrade: ${upgradeHeader}`);
|
|
3943
|
+
if (connectionHeader) lines.push(`connection: ${connectionHeader}`);
|
|
3944
|
+
upstream.write(lines.join("\r\n") + "\r\n\r\n");
|
|
3945
|
+
if (head && head.length > 0) upstream.write(head);
|
|
3946
|
+
clientSocket.pipe(upstream);
|
|
3947
|
+
upstream.pipe(clientSocket);
|
|
3948
|
+
const teardown = () => {
|
|
3949
|
+
clientSocket.destroy();
|
|
3950
|
+
upstream.destroy();
|
|
3951
|
+
};
|
|
3952
|
+
clientSocket.once("close", teardown);
|
|
3953
|
+
upstream.once("close", teardown);
|
|
3954
|
+
clientSocket.once("error", teardown);
|
|
3955
|
+
upstream.once("error", teardown);
|
|
3956
|
+
});
|
|
3957
|
+
upstream.once("timeout", () => {
|
|
3958
|
+
writeStatusAndDestroy2(clientSocket, 504, "Gateway Timeout");
|
|
3959
|
+
upstream.destroy();
|
|
3960
|
+
});
|
|
3961
|
+
upstream.once("error", (err) => {
|
|
3962
|
+
console.error(`[graph-proxy] ws upstream error: ${err.message}`);
|
|
3963
|
+
writeStatusAndDestroy2(clientSocket, 502, "Bad Gateway");
|
|
3964
|
+
upstream.destroy();
|
|
3965
|
+
});
|
|
3966
|
+
} catch (err) {
|
|
3967
|
+
console.error(`[graph-proxy] upgrade handler exception: ${err.message}`);
|
|
3968
|
+
clientSocket.destroy();
|
|
3969
|
+
}
|
|
3970
|
+
});
|
|
3971
|
+
}
|
|
3972
|
+
function graphAuthMiddleware(opts) {
|
|
3973
|
+
return async (c, next) => {
|
|
3974
|
+
const url2 = new URL(c.req.raw.url);
|
|
3975
|
+
if (!url2.pathname.startsWith(GRAPH_PREFIX)) return next();
|
|
3976
|
+
const hostHeader = (c.req.header("host") ?? "").split(":")[0];
|
|
3977
|
+
const incoming = c.env?.incoming;
|
|
3978
|
+
const remote = incoming?.socket?.remoteAddress;
|
|
3979
|
+
const xff = c.req.header("x-forwarded-for");
|
|
3980
|
+
const cookie = c.req.header("cookie");
|
|
3981
|
+
const decision = opts.canAccessAdmin({
|
|
3982
|
+
host: hostHeader,
|
|
3983
|
+
remoteAddress: remote,
|
|
3984
|
+
xForwardedFor: xff,
|
|
3985
|
+
cookieHeader: cookie,
|
|
3986
|
+
isPublicHost: opts.isPublicHost
|
|
3987
|
+
});
|
|
3988
|
+
if (!decision.allow) {
|
|
3989
|
+
return c.text(
|
|
3990
|
+
decision.reason === "public-host" ? "Not Found" : "Unauthorized",
|
|
3991
|
+
decision.reason === "public-host" ? 404 : 401
|
|
3992
|
+
);
|
|
3993
|
+
}
|
|
3994
|
+
return next();
|
|
3995
|
+
};
|
|
3996
|
+
}
|
|
3997
|
+
function headerString2(value) {
|
|
3998
|
+
if (value == null) return void 0;
|
|
3999
|
+
return Array.isArray(value) ? value[0] : value;
|
|
4000
|
+
}
|
|
4001
|
+
function parseOriginHost2(origin) {
|
|
4002
|
+
if (!origin) return null;
|
|
4003
|
+
try {
|
|
4004
|
+
return new URL(origin).hostname;
|
|
4005
|
+
} catch {
|
|
4006
|
+
return null;
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
function writeStatusAndDestroy2(socket, status, statusText) {
|
|
4010
|
+
try {
|
|
4011
|
+
socket.write(
|
|
4012
|
+
`HTTP/1.1 ${status} ${statusText}\r
|
|
4013
|
+
Connection: close\r
|
|
4014
|
+
Content-Length: 0\r
|
|
4015
|
+
\r
|
|
4016
|
+
`
|
|
4017
|
+
);
|
|
4018
|
+
} catch {
|
|
4019
|
+
}
|
|
4020
|
+
socket.destroy();
|
|
4021
|
+
}
|
|
4022
|
+
|
|
3831
4023
|
// app/api/health/route.ts
|
|
3832
4024
|
import { existsSync as existsSync11, readFileSync as readFileSync12 } from "fs";
|
|
3833
|
-
import { createConnection as
|
|
4025
|
+
import { createConnection as createConnection4 } from "net";
|
|
3834
4026
|
|
|
3835
4027
|
// app/lib/claude-auth.ts
|
|
3836
4028
|
import { readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -4187,7 +4379,7 @@ function contextWindow(model) {
|
|
|
4187
4379
|
|
|
4188
4380
|
// app/lib/vnc.ts
|
|
4189
4381
|
import { spawnSync, execFileSync } from "child_process";
|
|
4190
|
-
import { createConnection as
|
|
4382
|
+
import { createConnection as createConnection3 } from "net";
|
|
4191
4383
|
import { mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
|
|
4192
4384
|
import { resolve as resolve4 } from "path";
|
|
4193
4385
|
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
@@ -4282,7 +4474,7 @@ async function waitForPort(port2, timeoutMs = 12e3) {
|
|
|
4282
4474
|
const deadline = Date.now() + timeoutMs;
|
|
4283
4475
|
while (Date.now() < deadline) {
|
|
4284
4476
|
const ready = await new Promise((res) => {
|
|
4285
|
-
const socket =
|
|
4477
|
+
const socket = createConnection3(port2, "127.0.0.1");
|
|
4286
4478
|
socket.setTimeout(500);
|
|
4287
4479
|
socket.once("connect", () => {
|
|
4288
4480
|
socket.destroy();
|
|
@@ -7531,6 +7723,18 @@ function buildSpawnEnv(accountId, accountDir, conversationId) {
|
|
|
7531
7723
|
STREAM_LOG_PATH: streamLogPath
|
|
7532
7724
|
};
|
|
7533
7725
|
}
|
|
7726
|
+
var cachedBrandHostname = null;
|
|
7727
|
+
function readBrandHostname() {
|
|
7728
|
+
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7729
|
+
try {
|
|
7730
|
+
const brandPath3 = resolve6(PLATFORM_ROOT4, "config", "brand.json");
|
|
7731
|
+
const parsed = JSON.parse(readFileSync7(brandPath3, "utf-8"));
|
|
7732
|
+
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7733
|
+
} catch {
|
|
7734
|
+
cachedBrandHostname = "maxy";
|
|
7735
|
+
}
|
|
7736
|
+
return cachedBrandHostname;
|
|
7737
|
+
}
|
|
7534
7738
|
function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
7535
7739
|
if (!conversationId) {
|
|
7536
7740
|
throw new Error(`getMcpServers: conversationId is required (accountId=${accountId.slice(0, 8)})`);
|
|
@@ -7582,6 +7786,26 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7582
7786
|
"plugin_playwright_playwright": {
|
|
7583
7787
|
command: "npx",
|
|
7584
7788
|
args: ["-y", "@playwright/mcp@latest", "--cdp-endpoint", "http://127.0.0.1:9222", "--caps", "pdf"]
|
|
7789
|
+
},
|
|
7790
|
+
// Graph MCP shim (Task 557) — spawns upstream `uvx mcp-neo4j-cypher` locked
|
|
7791
|
+
// into read-only mode and namespaced `maxy-graph`, wraps stderr with our
|
|
7792
|
+
// tee, and emits one `[graph-query]` line per tool call. Credentials flow
|
|
7793
|
+
// from the brand's own NEO4J_URI + config/.neo4j-password so a per-brand
|
|
7794
|
+
// admin session only ever sees its own Neo4j instance (isolation is
|
|
7795
|
+
// enforced upstream by the installer's process/port boundary per
|
|
7796
|
+
// MAXY-PRD.md:627, not in any application-layer filter).
|
|
7797
|
+
"graph": {
|
|
7798
|
+
command: "node",
|
|
7799
|
+
args: [resolve6(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
|
|
7800
|
+
env: {
|
|
7801
|
+
...baseEnv,
|
|
7802
|
+
BRAND: readBrandHostname(),
|
|
7803
|
+
NEO4J_URI: process.env.NEO4J_URI ?? "bolt://localhost:7687",
|
|
7804
|
+
NEO4J_USERNAME: process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j",
|
|
7805
|
+
NEO4J_NAMESPACE: "maxy-graph",
|
|
7806
|
+
NEO4J_READ_ONLY: "true",
|
|
7807
|
+
NEO4J_RESPONSE_TOKEN_LIMIT: "20000"
|
|
7808
|
+
}
|
|
7585
7809
|
}
|
|
7586
7810
|
};
|
|
7587
7811
|
const tgConfig = resolveAccount()?.config?.telegram;
|
|
@@ -7645,6 +7869,12 @@ var ADMIN_CORE_TOOLS = [
|
|
|
7645
7869
|
"Glob",
|
|
7646
7870
|
"Grep",
|
|
7647
7871
|
"Agent",
|
|
7872
|
+
// Task 557 — upstream mcp-neo4j-cypher (namespaced maxy-graph, read-only).
|
|
7873
|
+
// maxy-graph_write_neo4j_cypher is intentionally absent: writes go through
|
|
7874
|
+
// the schema-aware memory-write tool, which validates labels, properties,
|
|
7875
|
+
// and embeddings. Admin-only by virtue of not being in the public allow list.
|
|
7876
|
+
"mcp__graph__maxy-graph_read_neo4j_cypher",
|
|
7877
|
+
"mcp__graph__maxy-graph_get_neo4j_schema",
|
|
7648
7878
|
"mcp__memory__memory-search",
|
|
7649
7879
|
"mcp__memory__memory-rank",
|
|
7650
7880
|
"mcp__memory__memory-write",
|
|
@@ -10217,6 +10447,15 @@ ${body}`;
|
|
|
10217
10447
|
|
|
10218
10448
|
${manifest}`;
|
|
10219
10449
|
}
|
|
10450
|
+
const graphRefPath = resolve6(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
|
|
10451
|
+
try {
|
|
10452
|
+
const graphRef = readFileSync7(graphRefPath, "utf-8");
|
|
10453
|
+
baseSystemPrompt += `
|
|
10454
|
+
|
|
10455
|
+
${graphRef}`;
|
|
10456
|
+
} catch (err) {
|
|
10457
|
+
console.error(`[graph-primitives] reference missing at ${graphRefPath} \u2014 admin session will have no Cypher cookbook: ${err instanceof Error ? err.message : String(err)}`);
|
|
10458
|
+
}
|
|
10220
10459
|
}
|
|
10221
10460
|
if (agentConfig?.budget) {
|
|
10222
10461
|
const pluginTokens = embeddedPlugins.reduce((sum, p) => sum + estimateTokens(p.body), 0);
|
|
@@ -27923,7 +28162,7 @@ async function probeApiKey() {
|
|
|
27923
28162
|
}
|
|
27924
28163
|
function checkPort(port2, timeoutMs = 500) {
|
|
27925
28164
|
return new Promise((resolve29) => {
|
|
27926
|
-
const socket =
|
|
28165
|
+
const socket = createConnection4(port2, "127.0.0.1");
|
|
27927
28166
|
socket.setTimeout(timeoutMs);
|
|
27928
28167
|
socket.once("connect", () => {
|
|
27929
28168
|
socket.destroy();
|
|
@@ -33202,6 +33441,9 @@ app.get("/:slug", async (c, next) => {
|
|
|
33202
33441
|
}
|
|
33203
33442
|
await next();
|
|
33204
33443
|
});
|
|
33444
|
+
app.use("/graph/*", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
|
|
33445
|
+
app.use("/graph", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
|
|
33446
|
+
attachGraphHttpRoutes(app);
|
|
33205
33447
|
app.use("/*", serveStatic({ root: "./public" }));
|
|
33206
33448
|
var port = parseInt(process.env.PORT ?? "19200", 10);
|
|
33207
33449
|
var hostname3 = process.env.HOSTNAME ?? "0.0.0.0";
|
|
@@ -33211,6 +33453,7 @@ attachVncWsProxy(httpServer, {
|
|
|
33211
33453
|
upstreamHost: "127.0.0.1",
|
|
33212
33454
|
upstreamPort: 6080
|
|
33213
33455
|
});
|
|
33456
|
+
attachGraphWsProxy(httpServer, { isPublicHost, canAccessAdmin });
|
|
33214
33457
|
console.log(`${BRAND.productName} listening on http://${hostname3}:${port}`);
|
|
33215
33458
|
(async () => {
|
|
33216
33459
|
try {
|