@rubytech/create-realagent 1.0.628 → 1.0.631
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 +51 -4
- 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/cloudflare/references/manual-setup.md +12 -1
- package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +4 -1
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +70 -40
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +1 -1
- 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-BntwbBs-.js +352 -0
- package/payload/server/public/index.html +1 -1
- package/payload/server/server.js +349 -31
- 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>Real Agent</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-BntwbBs-.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";
|
|
@@ -4167,7 +4359,7 @@ import { spawn as spawn2, spawnSync as spawnSync2 } from "child_process";
|
|
|
4167
4359
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4168
4360
|
import { resolve as resolve6, join as join4 } from "path";
|
|
4169
4361
|
import { platform as osPlatform } from "os";
|
|
4170
|
-
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync2, cpSync, rmSync } from "fs";
|
|
4362
|
+
import { readFileSync as readFileSync7, writeFileSync as writeFileSync5, readdirSync as readdirSync2, existsSync as existsSync6, mkdirSync as mkdirSync4, createWriteStream, statSync as statSync3, unlinkSync as unlinkSync2, cpSync, rmSync, appendFileSync as appendFileSync2 } from "fs";
|
|
4171
4363
|
import { lookup as dnsLookup } from "dns/promises";
|
|
4172
4364
|
import { createConnection as netConnect } from "net";
|
|
4173
4365
|
import { StringDecoder } from "string_decoder";
|
|
@@ -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();
|
|
@@ -6181,14 +6373,41 @@ function agentLogStream(name, accountDir, conversationId) {
|
|
|
6181
6373
|
const logDir = resolve6(accountDir, "logs");
|
|
6182
6374
|
mkdirSync4(logDir, { recursive: true });
|
|
6183
6375
|
purgeOldLogs(logDir, `${name}-`);
|
|
6184
|
-
|
|
6376
|
+
const logPath2 = resolve6(logDir, `${name}-${conversationId}.log`);
|
|
6377
|
+
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6378
|
+
registerStreamLog(stream, { path: logPath2, conversationId, name });
|
|
6379
|
+
return stream;
|
|
6185
6380
|
}
|
|
6186
6381
|
function preConversationLogStream(name, accountDir) {
|
|
6187
6382
|
const logDir = resolve6(accountDir, "logs");
|
|
6188
6383
|
mkdirSync4(logDir, { recursive: true });
|
|
6189
6384
|
const date5 = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
6190
6385
|
purgeOldLogs(logDir, `preconversation-${name}-`);
|
|
6191
|
-
|
|
6386
|
+
const logPath2 = resolve6(logDir, `preconversation-${name}-${date5}.log`);
|
|
6387
|
+
const stream = createWriteStream(logPath2, { flags: "a" });
|
|
6388
|
+
registerStreamLog(stream, { path: logPath2, conversationId: null, name: `preconversation-${name}` });
|
|
6389
|
+
return stream;
|
|
6390
|
+
}
|
|
6391
|
+
var openStreamLogs = /* @__PURE__ */ new Map();
|
|
6392
|
+
function registerStreamLog(stream, entry) {
|
|
6393
|
+
openStreamLogs.set(stream, entry);
|
|
6394
|
+
stream.once("close", () => {
|
|
6395
|
+
openStreamLogs.delete(stream);
|
|
6396
|
+
});
|
|
6397
|
+
}
|
|
6398
|
+
function sigtermFlushStreamLogs(reason, source) {
|
|
6399
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
6400
|
+
for (const entry of openStreamLogs.values()) {
|
|
6401
|
+
const convPart = entry.conversationId ? ` conversationId=${entry.conversationId}` : "";
|
|
6402
|
+
const line = `[${ts}] [server-sigterm] reason=${reason}${convPart} name=${entry.name} source=${source}
|
|
6403
|
+
`;
|
|
6404
|
+
try {
|
|
6405
|
+
appendFileSync2(entry.path, line);
|
|
6406
|
+
} catch (err) {
|
|
6407
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
6408
|
+
console.error(`[server-sigterm-flush-err] path=${entry.path} reason=${msg}`);
|
|
6409
|
+
}
|
|
6410
|
+
}
|
|
6192
6411
|
}
|
|
6193
6412
|
function purgeOldLogs(logDir, prefix) {
|
|
6194
6413
|
const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
|
|
@@ -7531,6 +7750,18 @@ function buildSpawnEnv(accountId, accountDir, conversationId) {
|
|
|
7531
7750
|
STREAM_LOG_PATH: streamLogPath
|
|
7532
7751
|
};
|
|
7533
7752
|
}
|
|
7753
|
+
var cachedBrandHostname = null;
|
|
7754
|
+
function readBrandHostname() {
|
|
7755
|
+
if (cachedBrandHostname !== null) return cachedBrandHostname;
|
|
7756
|
+
try {
|
|
7757
|
+
const brandPath3 = resolve6(PLATFORM_ROOT4, "config", "brand.json");
|
|
7758
|
+
const parsed = JSON.parse(readFileSync7(brandPath3, "utf-8"));
|
|
7759
|
+
cachedBrandHostname = typeof parsed.hostname === "string" && parsed.hostname.length > 0 ? parsed.hostname : "maxy";
|
|
7760
|
+
} catch {
|
|
7761
|
+
cachedBrandHostname = "maxy";
|
|
7762
|
+
}
|
|
7763
|
+
return cachedBrandHostname;
|
|
7764
|
+
}
|
|
7534
7765
|
function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
7535
7766
|
if (!conversationId) {
|
|
7536
7767
|
throw new Error(`getMcpServers: conversationId is required (accountId=${accountId.slice(0, 8)})`);
|
|
@@ -7582,6 +7813,26 @@ function getMcpServers(accountId, conversationId, userId, enabledPlugins) {
|
|
|
7582
7813
|
"plugin_playwright_playwright": {
|
|
7583
7814
|
command: "npx",
|
|
7584
7815
|
args: ["-y", "@playwright/mcp@latest", "--cdp-endpoint", "http://127.0.0.1:9222", "--caps", "pdf"]
|
|
7816
|
+
},
|
|
7817
|
+
// Graph MCP shim (Task 557) — spawns upstream `uvx mcp-neo4j-cypher` locked
|
|
7818
|
+
// into read-only mode and namespaced `maxy-graph`, wraps stderr with our
|
|
7819
|
+
// tee, and emits one `[graph-query]` line per tool call. Credentials flow
|
|
7820
|
+
// from the brand's own NEO4J_URI + config/.neo4j-password so a per-brand
|
|
7821
|
+
// admin session only ever sees its own Neo4j instance (isolation is
|
|
7822
|
+
// enforced upstream by the installer's process/port boundary per
|
|
7823
|
+
// MAXY-PRD.md:627, not in any application-layer filter).
|
|
7824
|
+
"graph": {
|
|
7825
|
+
command: "node",
|
|
7826
|
+
args: [resolve6(PLATFORM_ROOT4, "lib/graph-mcp/dist/index.js")],
|
|
7827
|
+
env: {
|
|
7828
|
+
...baseEnv,
|
|
7829
|
+
BRAND: readBrandHostname(),
|
|
7830
|
+
NEO4J_URI: process.env.NEO4J_URI ?? "bolt://localhost:7687",
|
|
7831
|
+
NEO4J_USERNAME: process.env.NEO4J_USERNAME ?? process.env.NEO4J_USER ?? "neo4j",
|
|
7832
|
+
NEO4J_NAMESPACE: "maxy-graph",
|
|
7833
|
+
NEO4J_READ_ONLY: "true",
|
|
7834
|
+
NEO4J_RESPONSE_TOKEN_LIMIT: "20000"
|
|
7835
|
+
}
|
|
7585
7836
|
}
|
|
7586
7837
|
};
|
|
7587
7838
|
const tgConfig = resolveAccount()?.config?.telegram;
|
|
@@ -7645,6 +7896,12 @@ var ADMIN_CORE_TOOLS = [
|
|
|
7645
7896
|
"Glob",
|
|
7646
7897
|
"Grep",
|
|
7647
7898
|
"Agent",
|
|
7899
|
+
// Task 557 — upstream mcp-neo4j-cypher (namespaced maxy-graph, read-only).
|
|
7900
|
+
// maxy-graph_write_neo4j_cypher is intentionally absent: writes go through
|
|
7901
|
+
// the schema-aware memory-write tool, which validates labels, properties,
|
|
7902
|
+
// and embeddings. Admin-only by virtue of not being in the public allow list.
|
|
7903
|
+
"mcp__graph__maxy-graph_read_neo4j_cypher",
|
|
7904
|
+
"mcp__graph__maxy-graph_get_neo4j_schema",
|
|
7648
7905
|
"mcp__memory__memory-search",
|
|
7649
7906
|
"mcp__memory__memory-rank",
|
|
7650
7907
|
"mcp__memory__memory-write",
|
|
@@ -10217,6 +10474,15 @@ ${body}`;
|
|
|
10217
10474
|
|
|
10218
10475
|
${manifest}`;
|
|
10219
10476
|
}
|
|
10477
|
+
const graphRefPath = resolve6(PLATFORM_ROOT4, "plugins/memory/references/graph-primitives.md");
|
|
10478
|
+
try {
|
|
10479
|
+
const graphRef = readFileSync7(graphRefPath, "utf-8");
|
|
10480
|
+
baseSystemPrompt += `
|
|
10481
|
+
|
|
10482
|
+
${graphRef}`;
|
|
10483
|
+
} catch (err) {
|
|
10484
|
+
console.error(`[graph-primitives] reference missing at ${graphRefPath} \u2014 admin session will have no Cypher cookbook: ${err instanceof Error ? err.message : String(err)}`);
|
|
10485
|
+
}
|
|
10220
10486
|
}
|
|
10221
10487
|
if (agentConfig?.budget) {
|
|
10222
10488
|
const pluginTokens = embeddedPlugins.reduce((sum, p) => sum + estimateTokens(p.body), 0);
|
|
@@ -11102,7 +11368,7 @@ function sourceKey(file2) {
|
|
|
11102
11368
|
}
|
|
11103
11369
|
|
|
11104
11370
|
// app/lib/review-detector/writer.ts
|
|
11105
|
-
import { appendFileSync as
|
|
11371
|
+
import { appendFileSync as appendFileSync3, existsSync as existsSync9, mkdirSync as mkdirSync7, readFileSync as readFileSync10, writeFileSync as writeFileSync8, renameSync as renameSync3, statSync as statSync6 } from "fs";
|
|
11106
11372
|
import { resolve as resolve9, dirname as dirname4 } from "path";
|
|
11107
11373
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
11108
11374
|
function reviewLogPath(configDir2) {
|
|
@@ -11119,7 +11385,7 @@ function reviewLog(configDir2, event) {
|
|
|
11119
11385
|
typeof event.ts === "number" ? event.ts : Date.now()
|
|
11120
11386
|
).toISOString()} [review] ${JSON.stringify(event)}
|
|
11121
11387
|
`;
|
|
11122
|
-
|
|
11388
|
+
appendFileSync3(path2, line, "utf-8");
|
|
11123
11389
|
} catch (err) {
|
|
11124
11390
|
console.error(`[review] failed to write review log at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11125
11391
|
}
|
|
@@ -11233,7 +11499,7 @@ function queueAlert(configDir2, accountId, match2) {
|
|
|
11233
11499
|
try {
|
|
11234
11500
|
mkdirSync7(dirname4(path2), { recursive: true });
|
|
11235
11501
|
const line = JSON.stringify({ accountId, match: match2 }) + "\n";
|
|
11236
|
-
|
|
11502
|
+
appendFileSync3(path2, line, "utf-8");
|
|
11237
11503
|
} catch (err) {
|
|
11238
11504
|
console.error(`[review] failed to queue alert at ${path2}: ${err instanceof Error ? err.message : String(err)}`);
|
|
11239
11505
|
}
|
|
@@ -27923,7 +28189,7 @@ async function probeApiKey() {
|
|
|
27923
28189
|
}
|
|
27924
28190
|
function checkPort(port2, timeoutMs = 500) {
|
|
27925
28191
|
return new Promise((resolve29) => {
|
|
27926
|
-
const socket =
|
|
28192
|
+
const socket = createConnection4(port2, "127.0.0.1");
|
|
27927
28193
|
socket.setTimeout(timeoutMs);
|
|
27928
28194
|
socket.once("connect", () => {
|
|
27929
28195
|
socket.destroy();
|
|
@@ -31304,6 +31570,40 @@ function startScriptStreamTailer(opts) {
|
|
|
31304
31570
|
};
|
|
31305
31571
|
}
|
|
31306
31572
|
|
|
31573
|
+
// app/lib/admin-sse-registry.ts
|
|
31574
|
+
var activeAdminSSEControllers = /* @__PURE__ */ new Set();
|
|
31575
|
+
function registerAdminSSE(entry) {
|
|
31576
|
+
activeAdminSSEControllers.add(entry);
|
|
31577
|
+
}
|
|
31578
|
+
function unregisterAdminSSE(entry) {
|
|
31579
|
+
activeAdminSSEControllers.delete(entry);
|
|
31580
|
+
}
|
|
31581
|
+
function broadcastAdminShutdown(reason) {
|
|
31582
|
+
const encoder = new TextEncoder();
|
|
31583
|
+
const payload = JSON.stringify({ type: "server_shutdown", reason });
|
|
31584
|
+
const frame = encoder.encode(`data: ${payload}
|
|
31585
|
+
|
|
31586
|
+
`);
|
|
31587
|
+
const done = encoder.encode(`data: [DONE]
|
|
31588
|
+
|
|
31589
|
+
`);
|
|
31590
|
+
for (const entry of activeAdminSSEControllers) {
|
|
31591
|
+
try {
|
|
31592
|
+
entry.controller.enqueue(frame);
|
|
31593
|
+
} catch {
|
|
31594
|
+
}
|
|
31595
|
+
try {
|
|
31596
|
+
entry.controller.enqueue(done);
|
|
31597
|
+
} catch {
|
|
31598
|
+
}
|
|
31599
|
+
try {
|
|
31600
|
+
entry.controller.close();
|
|
31601
|
+
} catch {
|
|
31602
|
+
}
|
|
31603
|
+
}
|
|
31604
|
+
activeAdminSSEControllers.clear();
|
|
31605
|
+
}
|
|
31606
|
+
|
|
31307
31607
|
// app/api/admin/chat/route.ts
|
|
31308
31608
|
function isComponentDone(parsed) {
|
|
31309
31609
|
return typeof parsed === "object" && parsed !== null && parsed._componentDone === true && typeof parsed.component === "string" && typeof parsed.payload === "string";
|
|
@@ -31499,29 +31799,31 @@ async function POST21(req) {
|
|
|
31499
31799
|
const readable = new ReadableStream({
|
|
31500
31800
|
async start(controller) {
|
|
31501
31801
|
let controllerOpen = true;
|
|
31502
|
-
|
|
31503
|
-
|
|
31504
|
-
|
|
31505
|
-
|
|
31506
|
-
|
|
31507
|
-
|
|
31508
|
-
|
|
31509
|
-
|
|
31802
|
+
const sseEntry = { controller, conversationId: sseConvId ?? null, sessionKey: session_key };
|
|
31803
|
+
try {
|
|
31804
|
+
registerAdminSSE(sseEntry);
|
|
31805
|
+
if (sseConvId) {
|
|
31806
|
+
const streamLogPath = resolve19(account.accountDir, "logs", `claude-agent-stream-${sseConvId}.log`);
|
|
31807
|
+
tailer = startScriptStreamTailer({
|
|
31808
|
+
path: streamLogPath,
|
|
31809
|
+
onEvent: (event) => {
|
|
31810
|
+
if (!controllerOpen) return;
|
|
31811
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().slice(11, 23);
|
|
31812
|
+
sseLog.write(`[${ts}] [${sk}] admin: ${JSON.stringify(event)}
|
|
31510
31813
|
`);
|
|
31511
|
-
|
|
31512
|
-
|
|
31814
|
+
try {
|
|
31815
|
+
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}
|
|
31513
31816
|
|
|
31514
31817
|
`));
|
|
31515
|
-
|
|
31516
|
-
|
|
31818
|
+
} catch {
|
|
31819
|
+
controllerOpen = false;
|
|
31820
|
+
}
|
|
31821
|
+
},
|
|
31822
|
+
onError: (err) => {
|
|
31823
|
+
console.error(`[script-stream-tailer] ${streamLogPath}: ${err.message}`);
|
|
31517
31824
|
}
|
|
31518
|
-
}
|
|
31519
|
-
|
|
31520
|
-
console.error(`[script-stream-tailer] ${streamLogPath}: ${err.message}`);
|
|
31521
|
-
}
|
|
31522
|
-
});
|
|
31523
|
-
}
|
|
31524
|
-
try {
|
|
31825
|
+
});
|
|
31826
|
+
}
|
|
31525
31827
|
for await (const event of invokeAgent(
|
|
31526
31828
|
{ type: "admin", skipTopicCheck },
|
|
31527
31829
|
message,
|
|
@@ -31580,6 +31882,7 @@ async function POST21(req) {
|
|
|
31580
31882
|
controller.close();
|
|
31581
31883
|
} catch {
|
|
31582
31884
|
}
|
|
31885
|
+
unregisterAdminSSE(sseEntry);
|
|
31583
31886
|
}
|
|
31584
31887
|
}
|
|
31585
31888
|
});
|
|
@@ -33202,6 +33505,9 @@ app.get("/:slug", async (c, next) => {
|
|
|
33202
33505
|
}
|
|
33203
33506
|
await next();
|
|
33204
33507
|
});
|
|
33508
|
+
app.use("/graph/*", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
|
|
33509
|
+
app.use("/graph", graphAuthMiddleware({ isPublicHost, canAccessAdmin }));
|
|
33510
|
+
attachGraphHttpRoutes(app);
|
|
33205
33511
|
app.use("/*", serveStatic({ root: "./public" }));
|
|
33206
33512
|
var port = parseInt(process.env.PORT ?? "19200", 10);
|
|
33207
33513
|
var hostname3 = process.env.HOSTNAME ?? "0.0.0.0";
|
|
@@ -33211,6 +33517,7 @@ attachVncWsProxy(httpServer, {
|
|
|
33211
33517
|
upstreamHost: "127.0.0.1",
|
|
33212
33518
|
upstreamPort: 6080
|
|
33213
33519
|
});
|
|
33520
|
+
attachGraphWsProxy(httpServer, { isPublicHost, canAccessAdmin });
|
|
33214
33521
|
console.log(`${BRAND.productName} listening on http://${hostname3}:${port}`);
|
|
33215
33522
|
(async () => {
|
|
33216
33523
|
try {
|
|
@@ -33355,6 +33662,17 @@ process.on("SIGTERM", async () => {
|
|
|
33355
33662
|
}
|
|
33356
33663
|
shuttingDown = true;
|
|
33357
33664
|
console.error("[server] SIGTERM received \u2014 starting graceful shutdown");
|
|
33665
|
+
try {
|
|
33666
|
+
sigtermFlushStreamLogs("systemd-stop", "server-index");
|
|
33667
|
+
} catch (err) {
|
|
33668
|
+
console.error(`[server] sigterm flush error: ${String(err)}`);
|
|
33669
|
+
}
|
|
33670
|
+
try {
|
|
33671
|
+
broadcastAdminShutdown("systemd-stop");
|
|
33672
|
+
} catch (err) {
|
|
33673
|
+
console.error(`[server] sigterm broadcast error: ${String(err)}`);
|
|
33674
|
+
}
|
|
33675
|
+
await new Promise((res) => setImmediate(res));
|
|
33358
33676
|
try {
|
|
33359
33677
|
await shutdown();
|
|
33360
33678
|
} catch (err) {
|