@rubytech/create-realagent-code 0.1.191 → 0.1.193
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/__tests__/brew-resolve.test.js +1 -1
- package/dist/__tests__/macos-darwin-branch.test.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.d.ts +55 -0
- package/payload/platform/lib/mcp-eager/dist/index.d.ts.map +1 -1
- package/payload/platform/lib/mcp-eager/dist/index.js +76 -0
- package/payload/platform/lib/mcp-eager/dist/index.js.map +1 -1
- package/payload/platform/lib/mcp-eager/package.json +7 -0
- package/payload/platform/lib/mcp-eager/src/index.ts +101 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-admin-tool-gate.test.sh +102 -0
- package/payload/platform/plugins/admin/hooks/__tests__/pre-tool-use-base64-guard.test.sh +4 -4
- package/payload/platform/plugins/admin/hooks/pre-tool-use.sh +56 -0
- package/payload/platform/plugins/browser/mcp/dist/index.js +19 -19
- package/payload/platform/plugins/browser/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/docs/references/projects-guide.md +21 -0
- package/payload/platform/plugins/email/mcp/dist/index.js +12 -12
- package/payload/platform/plugins/email/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/index.js +8 -8
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js +35 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/memory-ingest.js.map +1 -1
- package/payload/platform/plugins/prompt-optimiser/skills/prompt-optimiser/SKILL.md +1 -0
- package/payload/platform/plugins/url-get/mcp/dist/index.js +2 -2
- package/payload/platform/plugins/url-get/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/work/PLUGIN.md +2 -0
- package/payload/platform/plugins/work/skills/execute-task/SKILL.md +101 -0
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +46 -2
- package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
- package/payload/server/public/assets/AdminShell-BoJbnn6d.js +1 -0
- package/payload/server/public/assets/{Checkbox-g2WZRfCt.js → Checkbox-G-kyM1P1.js} +1 -1
- package/payload/server/public/assets/admin-B-GDNkoN.js +1 -0
- package/payload/server/public/assets/{arc-DMDAZHAN.js → arc-aUiRP9AS.js} +1 -1
- package/payload/server/public/assets/architecture-YZFGNWBL--v-pJPNp.js +1 -0
- package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BVKxPUIS.js → architectureDiagram-Q4EWVU46-BGgdXdD2.js} +1 -1
- package/payload/server/public/assets/{blockDiagram-DXYQGD6D-BpD-1G1V.js → blockDiagram-DXYQGD6D-DKWoL4Rp.js} +1 -1
- package/payload/server/public/assets/{brand-BGvv6AYI.js → brand-CYcGHhlS.js} +1 -1
- package/payload/server/public/assets/{brand-BQW2DYy_.css → brand-MbpD2Bhr.css} +1 -1
- package/payload/server/public/assets/{c4Diagram-AHTNJAMY-CFl9Dr6U.js → c4Diagram-AHTNJAMY-Ca6rs6F4.js} +1 -1
- package/payload/server/public/assets/channel-BwsV7WTV.js +1 -0
- package/payload/server/public/assets/{chunk-2KRD3SAO-Di4bO8ir.js → chunk-2KRD3SAO-BK3470lx.js} +1 -1
- package/payload/server/public/assets/{chunk-336JU56O-B2Uc6pEC.js → chunk-336JU56O-DcdMZImV.js} +2 -2
- package/payload/server/public/assets/chunk-426QAEUC-9FSP-c1z.js +1 -0
- package/payload/server/public/assets/{chunk-4BX2VUAB-DyEhFk-Z.js → chunk-4BX2VUAB-BOvVdJLf.js} +1 -1
- package/payload/server/public/assets/{chunk-4TB4RGXK-Dvf5uiFR.js → chunk-4TB4RGXK-CC1QpEvA.js} +1 -1
- package/payload/server/public/assets/{chunk-55IACEB6-BRJOZLpU.js → chunk-55IACEB6-BwZyF7vR.js} +1 -1
- package/payload/server/public/assets/{chunk-5FUZZQ4R-DUQR1rF9.js → chunk-5FUZZQ4R-o1b70dEE.js} +1 -1
- package/payload/server/public/assets/{chunk-5PVQY5BW-OTvNug7K.js → chunk-5PVQY5BW-C7VCYRz9.js} +1 -1
- package/payload/server/public/assets/{chunk-67CJDMHE-BG6-9r6c.js → chunk-67CJDMHE-D5bhMrtY.js} +1 -1
- package/payload/server/public/assets/{chunk-7N4EOEYR-BvMbitX7.js → chunk-7N4EOEYR-Si7Lgrwc.js} +1 -1
- package/payload/server/public/assets/{chunk-AA7GKIK3-CE8mGBD5.js → chunk-AA7GKIK3-DMuHtDqO.js} +1 -1
- package/payload/server/public/assets/{chunk-BSJP7CBP-DP7LTBll.js → chunk-BSJP7CBP-L79XKVcb.js} +1 -1
- package/payload/server/public/assets/{chunk-CIAEETIT-VfnIdN-h.js → chunk-CIAEETIT-C0O7Upmg.js} +1 -1
- package/payload/server/public/assets/{chunk-EDXVE4YY-CRAsAWbD.js → chunk-EDXVE4YY-B8d5xj_4.js} +1 -1
- package/payload/server/public/assets/{chunk-ENJZ2VHE-BNllMoi_.js → chunk-ENJZ2VHE-SSbr4n2X.js} +1 -1
- package/payload/server/public/assets/{chunk-FMBD7UC4-w-yBZsN2.js → chunk-FMBD7UC4-C_E43NFJ.js} +1 -1
- package/payload/server/public/assets/{chunk-FOC6F5B3-f9cFywhd.js → chunk-FOC6F5B3-D9lWWHAu.js} +1 -1
- package/payload/server/public/assets/{chunk-ICPOFSXX-vTeVS4qd.js → chunk-ICPOFSXX-Cgj9YSpX.js} +2 -2
- package/payload/server/public/assets/{chunk-K5T4RW27-B_ZUrFUq.js → chunk-K5T4RW27-DuhsNH4c.js} +1 -1
- package/payload/server/public/assets/{chunk-KGLVRYIC-CcWTvRlI.js → chunk-KGLVRYIC-B4-A1Abi.js} +1 -1
- package/payload/server/public/assets/{chunk-LIHQZDEY-D-5-peQw.js → chunk-LIHQZDEY-BxqgHRgT.js} +1 -1
- package/payload/server/public/assets/{chunk-ORNJ4GCN-B4Z5L25I.js → chunk-ORNJ4GCN-DEYQ5WaJ.js} +1 -1
- package/payload/server/public/assets/{chunk-OYMX7WX6-BdSFcvNa.js → chunk-OYMX7WX6-DNEEcCqI.js} +1 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-Bd-GrQM6.js +1 -0
- package/payload/server/public/assets/{chunk-U2HBQHQK-BpFXup4D.js → chunk-U2HBQHQK-DuaoK83q.js} +1 -1
- package/payload/server/public/assets/{chunk-X2U36JSP-DF-6QHx4.js → chunk-X2U36JSP-CzMp0dpq.js} +1 -1
- package/payload/server/public/assets/{chunk-XPW4576I-m1Y_r88I.js → chunk-XPW4576I-CBfZXZDB.js} +1 -1
- package/payload/server/public/assets/{chunk-YZCP3GAM-CKgDoLRT.js → chunk-YZCP3GAM-B2wBDwSa.js} +1 -1
- package/payload/server/public/assets/{chunk-ZZ45TVLE-B0ACvZCZ.js → chunk-ZZ45TVLE-DkyF7XKW.js} +1 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-CEWNfHVo.js +1 -0
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BBjzRxux.js +1 -0
- package/payload/server/public/assets/clone-CXtSonnn.js +1 -0
- package/payload/server/public/assets/{cose-bilkent-S5V4N54A-BhtgY3T7.js → cose-bilkent-S5V4N54A-Br2gjtEO.js} +1 -1
- package/payload/server/public/assets/{dagre-3yl9rfxR.js → dagre-AGj1P0p4.js} +1 -1
- package/payload/server/public/assets/{dagre-KV5264BT-D-Ux4uwL.js → dagre-KV5264BT-Cnrxcu6A.js} +1 -1
- package/payload/server/public/assets/data-Bg0Frxt3.js +1 -0
- package/payload/server/public/assets/{diagram-5BDNPKRD-D-MOMikl.js → diagram-5BDNPKRD-gyFYSx73.js} +1 -1
- package/payload/server/public/assets/{diagram-G4DWMVQ6-DKdFcnhf.js → diagram-G4DWMVQ6-DFmZEv5C.js} +1 -1
- package/payload/server/public/assets/{diagram-MMDJMWI5-ajamR766.js → diagram-MMDJMWI5-DA4qbuEW.js} +1 -1
- package/payload/server/public/assets/{diagram-TYMM5635-D2Pbk2Kk.js → diagram-TYMM5635-D4n-8gig.js} +1 -1
- package/payload/server/public/assets/{erDiagram-SMLLAGMA-v3o948pk.js → erDiagram-SMLLAGMA-CigRMjF1.js} +1 -1
- package/payload/server/public/assets/{flatten-BsWEYbBB.js → flatten-Bo6YRmWl.js} +1 -1
- package/payload/server/public/assets/{flowDiagram-DWJPFMVM-byC1a6GE.js → flowDiagram-DWJPFMVM-ClS27acA.js} +1 -1
- package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-Dybzs-Sw.js → ganttDiagram-T4ZO3ILL-Dr1IulXe.js} +1 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-CI0s_tqn.js +1 -0
- package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-DvzXYLuW.js → gitGraphDiagram-UUTBAWPF-lwLibdPs.js} +1 -1
- package/payload/server/public/assets/graph-CpozZ74I.js +51 -0
- package/payload/server/public/assets/graph-labels-C2y666R0.js +1 -0
- package/payload/server/public/assets/{graphlib-DvH_spaA.js → graphlib-DBvoJMrc.js} +1 -1
- package/payload/server/public/assets/info-OMHHGYJF-g3gYW7Qm.js +1 -0
- package/payload/server/public/assets/infoDiagram-42DDH7IO-D4kReEIV.js +2 -0
- package/payload/server/public/assets/{isEmpty-BWl67LAZ.js → isEmpty-D6Kr-M1M.js} +1 -1
- package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-CpY5W64P.js → ishikawaDiagram-UXIWVN3A-BnteQRQ0.js} +1 -1
- package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-ymMAQVdm.js → journeyDiagram-VCZTEJTY-CXu6hLyJ.js} +1 -1
- package/payload/server/public/assets/{kanban-definition-6JOO6SKY-_NX9hNK3.js → kanban-definition-6JOO6SKY-CGDW2jle.js} +1 -1
- package/payload/server/public/assets/{line-Dz7zKWX-.js → line-pdg5hxC3.js} +1 -1
- package/payload/server/public/assets/{linear-0O14Y6uf.js → linear-DOh_6k2k.js} +1 -1
- package/payload/server/public/assets/{mermaid-parser.core-BCWd1en6.js → mermaid-parser.core-o3lIIK3Z.js} +2 -2
- package/payload/server/public/assets/{mermaid.core-gSoFkVoN.js → mermaid.core-DLR6Q3rV.js} +3 -3
- package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-QuIyK2bd.js → mindmap-definition-QFDTVHPH-CH4cB0Dn.js} +1 -1
- package/payload/server/public/assets/{ordinal-krseTxxN.js → ordinal-BDi6f4xk.js} +1 -1
- package/payload/server/public/assets/packet-4T2RLAQJ-CT0TB9HI.js +1 -0
- package/payload/server/public/assets/pie-ZZUOXDRM-CXLe7TFF.js +1 -0
- package/payload/server/public/assets/{pieDiagram-DEJITSTG-DHK0yy7U.js → pieDiagram-DEJITSTG-Ba4voL_Y.js} +1 -1
- package/payload/server/public/assets/public-C8mil9h0.js +35 -0
- package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-wK1jwWo5.js → quadrantDiagram-34T5L4WZ-Cqn2xehe.js} +1 -1
- package/payload/server/public/assets/radar-PYXPWWZC-DnPLBl-D.js +1 -0
- package/payload/server/public/assets/{reduce-tk-xY6Fv.js → reduce-CGi9ik8i.js} +1 -1
- package/payload/server/public/assets/{requirementDiagram-MS252O5E-W_mH85_d.js → requirementDiagram-MS252O5E-DLfNx99l.js} +1 -1
- package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-nGAM-YLQ.js → sankeyDiagram-XADWPNL6-uck_V-cb.js} +1 -1
- package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-SMfUCwBH.js → sequenceDiagram-FGHM5R23-DmQalqud.js} +1 -1
- package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CYKXFirH.js → stateDiagram-FHFEXIEX-DUdWeVRa.js} +1 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DNTpFwdy.js +1 -0
- package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-DethbNFa.js → timeline-definition-GMOUNBTQ-BgL1wM5n.js} +1 -1
- package/payload/server/public/assets/treeView-SZITEDCU-C3cb7Xwe.js +1 -0
- package/payload/server/public/assets/treemap-W4RFUUIX-Dc7G3Bgm.js +1 -0
- package/payload/server/public/assets/useSelectionMode-BLpVlOgm.js +5 -0
- package/payload/server/public/assets/{vennDiagram-DHZGUBPP-BUlJTe9Z.js → vennDiagram-DHZGUBPP-7FHjIqPi.js} +1 -1
- package/payload/server/public/assets/wardley-RL74JXVD-DtgibWAt.js +1 -0
- package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-CL1TvZ7j.js → wardleyDiagram-NUSXRM2D-BHzo4V1J.js} +1 -1
- package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-Cav10l-Z.js → xychartDiagram-5P7HB3ND-DtuWhqwh.js} +1 -1
- package/payload/server/public/data.html +5 -5
- package/payload/server/public/graph.html +6 -6
- package/payload/server/public/index.html +6 -8
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +544 -488
- package/payload/server/public/assets/admin-DFxXLXfg.js +0 -216
- package/payload/server/public/assets/architecture-YZFGNWBL-COhEvUpo.js +0 -1
- package/payload/server/public/assets/channel-BTEFjudQ.js +0 -1
- package/payload/server/public/assets/chunk-426QAEUC-CcIGcm29.js +0 -1
- package/payload/server/public/assets/chunk-QZHKN3VN-CWh_0JsP.js +0 -1
- package/payload/server/public/assets/classDiagram-6PBFFD2Q-ClkdniKF.js +0 -1
- package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-kjJetfh2.js +0 -1
- package/payload/server/public/assets/clone-BllVm2iY.js +0 -1
- package/payload/server/public/assets/data-DtYcqL1G.js +0 -1
- package/payload/server/public/assets/gitGraph-7Q5UKJZL-DeTNsAO0.js +0 -1
- package/payload/server/public/assets/graph-C-XPyGsO.js +0 -1
- package/payload/server/public/assets/graph-labels-CZin3myX.js +0 -1
- package/payload/server/public/assets/info-OMHHGYJF-DJJ9GlS6.js +0 -1
- package/payload/server/public/assets/infoDiagram-42DDH7IO-C2FeU8nE.js +0 -2
- package/payload/server/public/assets/lib-BEvO-mE0.js +0 -33
- package/payload/server/public/assets/packet-4T2RLAQJ-CGbvGkvF.js +0 -1
- package/payload/server/public/assets/page-CKlvbjKn.js +0 -1
- package/payload/server/public/assets/page-DSj21hpG.js +0 -51
- package/payload/server/public/assets/pie-ZZUOXDRM-BZy8rjFn.js +0 -1
- package/payload/server/public/assets/public-BnS9zREj.js +0 -7
- package/payload/server/public/assets/radar-PYXPWWZC-DcfWIVXr.js +0 -1
- package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-B9ZwM8kx.js +0 -1
- package/payload/server/public/assets/treeView-SZITEDCU-3WugwVdj.js +0 -1
- package/payload/server/public/assets/treemap-W4RFUUIX-Cf5mDLlu.js +0 -1
- package/payload/server/public/assets/wardley-RL74JXVD-Bv4md4b3.js +0 -1
- /package/payload/server/public/assets/{_baseFor-BHtDrjIo.js → _baseFor-Cam2PbSt.js} +0 -0
- /package/payload/server/public/assets/{array-DetWRiSa.js → array-DYRGGQae.js} +0 -0
- /package/payload/server/public/assets/{cytoscape.esm-C9yNhe1u.js → cytoscape.esm-nWsJMTNI.js} +0 -0
- /package/payload/server/public/assets/{defaultLocale-_WRwicXn.js → defaultLocale-Du1XY3Dp.js} +0 -0
- /package/payload/server/public/assets/{dist-Bd4S37oi.js → dist-BzAsli7o.js} +0 -0
- /package/payload/server/public/assets/{init-sTEcj9YX.js → init-B5BXBRcm.js} +0 -0
- /package/payload/server/public/assets/{katex-s61Rgv6l.js → katex-HOUACuRw.js} +0 -0
- /package/payload/server/public/assets/{path-B0Ik7Tu9.js → path-CNO468J-.js} +0 -0
- /package/payload/server/public/assets/{rough.esm-DKRO8IF-.js → rough.esm-DRO6hWPh.js} +0 -0
- /package/payload/server/public/assets/{src-B6XdH6xq.js → src-CWiyyVfn.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -815,8 +815,8 @@ var serveStatic = (options = { root: "" }) => {
|
|
|
815
815
|
};
|
|
816
816
|
|
|
817
817
|
// server/index.ts
|
|
818
|
-
import { readFileSync as
|
|
819
|
-
import { resolve as
|
|
818
|
+
import { readFileSync as readFileSync21, existsSync as existsSync21, watchFile } from "fs";
|
|
819
|
+
import { resolve as resolve23, join as join16, basename as basename6 } from "path";
|
|
820
820
|
import { homedir as homedir2 } from "os";
|
|
821
821
|
import { monitorEventLoopDelay } from "perf_hooks";
|
|
822
822
|
|
|
@@ -1274,7 +1274,7 @@ var credsSaveQueue = Promise.resolve();
|
|
|
1274
1274
|
async function drainCredsSaveQueue(timeoutMs = 5e3) {
|
|
1275
1275
|
console.error(`${TAG2} draining credential save queue\u2026`);
|
|
1276
1276
|
const timer2 = new Promise(
|
|
1277
|
-
(
|
|
1277
|
+
(resolve24) => setTimeout(() => resolve24("timeout"), timeoutMs)
|
|
1278
1278
|
);
|
|
1279
1279
|
const result = await Promise.race([
|
|
1280
1280
|
credsSaveQueue.then(() => "drained"),
|
|
@@ -1402,11 +1402,11 @@ async function createWaSocket(opts) {
|
|
|
1402
1402
|
return sock;
|
|
1403
1403
|
}
|
|
1404
1404
|
async function waitForConnection(sock) {
|
|
1405
|
-
return new Promise((
|
|
1405
|
+
return new Promise((resolve24, reject) => {
|
|
1406
1406
|
const handler = (update) => {
|
|
1407
1407
|
if (update.connection === "open") {
|
|
1408
1408
|
sock.ev.off("connection.update", handler);
|
|
1409
|
-
|
|
1409
|
+
resolve24();
|
|
1410
1410
|
}
|
|
1411
1411
|
if (update.connection === "close") {
|
|
1412
1412
|
sock.ev.off("connection.update", handler);
|
|
@@ -1520,14 +1520,14 @@ ${inspected}`;
|
|
|
1520
1520
|
return inspect2(err, INSPECT_OPTS2);
|
|
1521
1521
|
}
|
|
1522
1522
|
function withTimeout(label, promise, timeoutMs) {
|
|
1523
|
-
return new Promise((
|
|
1523
|
+
return new Promise((resolve24, reject) => {
|
|
1524
1524
|
const timer2 = setTimeout(() => {
|
|
1525
1525
|
reject(new Error(`${label} timed out after ${timeoutMs}ms`));
|
|
1526
1526
|
}, timeoutMs);
|
|
1527
1527
|
promise.then(
|
|
1528
1528
|
(value) => {
|
|
1529
1529
|
clearTimeout(timer2);
|
|
1530
|
-
|
|
1530
|
+
resolve24(value);
|
|
1531
1531
|
},
|
|
1532
1532
|
(err) => {
|
|
1533
1533
|
clearTimeout(timer2);
|
|
@@ -2062,8 +2062,8 @@ async function persistWhatsAppMessage(input) {
|
|
|
2062
2062
|
const { givenName, familyName } = splitName(input.pushName);
|
|
2063
2063
|
const prev = sessionWriteLocks.get(input.cacheKey);
|
|
2064
2064
|
let release;
|
|
2065
|
-
const mine = new Promise((
|
|
2066
|
-
release =
|
|
2065
|
+
const mine = new Promise((resolve24) => {
|
|
2066
|
+
release = resolve24;
|
|
2067
2067
|
});
|
|
2068
2068
|
const chained = (prev ?? Promise.resolve()).then(() => mine);
|
|
2069
2069
|
sessionWriteLocks.set(input.cacheKey, chained);
|
|
@@ -3099,11 +3099,11 @@ async function connectWithReconnect(conn) {
|
|
|
3099
3099
|
console.error(
|
|
3100
3100
|
`${TAG12} reconnecting account=${conn.accountId} in ${delay}ms (attempt ${decision.nextAttempts}/${maxAttempts})`
|
|
3101
3101
|
);
|
|
3102
|
-
await new Promise((
|
|
3103
|
-
const timer2 = setTimeout(
|
|
3102
|
+
await new Promise((resolve24) => {
|
|
3103
|
+
const timer2 = setTimeout(resolve24, delay);
|
|
3104
3104
|
conn.abortController.signal.addEventListener("abort", () => {
|
|
3105
3105
|
clearTimeout(timer2);
|
|
3106
|
-
|
|
3106
|
+
resolve24();
|
|
3107
3107
|
}, { once: true });
|
|
3108
3108
|
});
|
|
3109
3109
|
}
|
|
@@ -3111,16 +3111,16 @@ async function connectWithReconnect(conn) {
|
|
|
3111
3111
|
}
|
|
3112
3112
|
}
|
|
3113
3113
|
function waitForDisconnectEvent(conn) {
|
|
3114
|
-
return new Promise((
|
|
3114
|
+
return new Promise((resolve24) => {
|
|
3115
3115
|
if (!conn.sock) {
|
|
3116
|
-
|
|
3116
|
+
resolve24();
|
|
3117
3117
|
return;
|
|
3118
3118
|
}
|
|
3119
3119
|
const sock = conn.sock;
|
|
3120
3120
|
const handler = (update) => {
|
|
3121
3121
|
if (update.connection === "close") {
|
|
3122
3122
|
sock.ev.off("connection.update", handler);
|
|
3123
|
-
|
|
3123
|
+
resolve24();
|
|
3124
3124
|
}
|
|
3125
3125
|
};
|
|
3126
3126
|
sock.ev.on("connection.update", handler);
|
|
@@ -3382,8 +3382,8 @@ async function handleInboundMessage(conn, msg) {
|
|
|
3382
3382
|
const conversationKey = isGroup ? remoteJid : senderPhone;
|
|
3383
3383
|
const debounceKey = `${conn.accountId}:${conversationKey}:${senderPhone}`;
|
|
3384
3384
|
let resolvePending;
|
|
3385
|
-
const sttPending = new Promise((
|
|
3386
|
-
resolvePending =
|
|
3385
|
+
const sttPending = new Promise((resolve24) => {
|
|
3386
|
+
resolvePending = resolve24;
|
|
3387
3387
|
});
|
|
3388
3388
|
if (conn.debouncer) conn.debouncer.registerPending(debounceKey, sttPending);
|
|
3389
3389
|
try {
|
|
@@ -3787,20 +3787,20 @@ function buildX11Env(chromiumWrapperPath, transport = "vnc") {
|
|
|
3787
3787
|
|
|
3788
3788
|
// server/routes/health.ts
|
|
3789
3789
|
function checkPort(port2, timeoutMs = 500) {
|
|
3790
|
-
return new Promise((
|
|
3790
|
+
return new Promise((resolve24) => {
|
|
3791
3791
|
const socket = createConnection2(port2, "127.0.0.1");
|
|
3792
3792
|
socket.setTimeout(timeoutMs);
|
|
3793
3793
|
socket.once("connect", () => {
|
|
3794
3794
|
socket.destroy();
|
|
3795
|
-
|
|
3795
|
+
resolve24(true);
|
|
3796
3796
|
});
|
|
3797
3797
|
socket.once("error", () => {
|
|
3798
3798
|
socket.destroy();
|
|
3799
|
-
|
|
3799
|
+
resolve24(false);
|
|
3800
3800
|
});
|
|
3801
3801
|
socket.once("timeout", () => {
|
|
3802
3802
|
socket.destroy();
|
|
3803
|
-
|
|
3803
|
+
resolve24(false);
|
|
3804
3804
|
});
|
|
3805
3805
|
});
|
|
3806
3806
|
}
|
|
@@ -4536,12 +4536,12 @@ async function dispatchOnce(input) {
|
|
|
4536
4536
|
});
|
|
4537
4537
|
if (!entry) return { error: "spawn-failed" };
|
|
4538
4538
|
entry.lastInboundAt = Date.now();
|
|
4539
|
-
let
|
|
4539
|
+
let resolve24;
|
|
4540
4540
|
const turnPromise = new Promise((r) => {
|
|
4541
|
-
|
|
4541
|
+
resolve24 = r;
|
|
4542
4542
|
});
|
|
4543
4543
|
const listener = (text) => {
|
|
4544
|
-
|
|
4544
|
+
resolve24(text);
|
|
4545
4545
|
};
|
|
4546
4546
|
entry.subscribers.add(listener);
|
|
4547
4547
|
const writeOk = await writeInput(entry, input.text);
|
|
@@ -4983,8 +4983,8 @@ async function startLogin(opts) {
|
|
|
4983
4983
|
resetActiveLogin(accountId);
|
|
4984
4984
|
let resolveQr = null;
|
|
4985
4985
|
let rejectQr = null;
|
|
4986
|
-
const qrPromise = new Promise((
|
|
4987
|
-
resolveQr =
|
|
4986
|
+
const qrPromise = new Promise((resolve24, reject) => {
|
|
4987
|
+
resolveQr = resolve24;
|
|
4988
4988
|
rejectQr = reject;
|
|
4989
4989
|
});
|
|
4990
4990
|
const qrTimer = setTimeout(
|
|
@@ -7245,24 +7245,6 @@ function openResumeStreamLog(accountId, sessionId) {
|
|
|
7245
7245
|
}
|
|
7246
7246
|
return createWriteStream(resolvePath(dir, `${sessionId}.log`), { flags: "a" });
|
|
7247
7247
|
}
|
|
7248
|
-
var healPending = /* @__PURE__ */ new Map();
|
|
7249
|
-
function markHealPending(accountId, attachmentId) {
|
|
7250
|
-
let set = healPending.get(accountId);
|
|
7251
|
-
if (!set) {
|
|
7252
|
-
set = /* @__PURE__ */ new Set();
|
|
7253
|
-
healPending.set(accountId, set);
|
|
7254
|
-
}
|
|
7255
|
-
set.add(attachmentId);
|
|
7256
|
-
}
|
|
7257
|
-
function clearHealPending(accountId, attachmentId) {
|
|
7258
|
-
const set = healPending.get(accountId);
|
|
7259
|
-
if (!set) return;
|
|
7260
|
-
set.delete(attachmentId);
|
|
7261
|
-
if (set.size === 0) healPending.delete(accountId);
|
|
7262
|
-
}
|
|
7263
|
-
function isHealPending(accountId, attachmentId) {
|
|
7264
|
-
return healPending.get(accountId)?.has(attachmentId) ?? false;
|
|
7265
|
-
}
|
|
7266
7248
|
function validateAndShapeAttachments(raws, conversationAccountId, sessionId, messageId, streamLogPath) {
|
|
7267
7249
|
const chips = [];
|
|
7268
7250
|
let valid = 0;
|
|
@@ -7640,17 +7622,14 @@ app11.post("/:id/resume", async (c) => {
|
|
|
7640
7622
|
textOffset: c2.textOffset
|
|
7641
7623
|
};
|
|
7642
7624
|
if (!c2.artefactAttachmentId || !c2.artefactMimeType || !c2.artefactTitle) return base;
|
|
7643
|
-
markHealPending(accountId, c2.artefactAttachmentId);
|
|
7644
7625
|
try {
|
|
7645
7626
|
const dataObj = JSON.parse(c2.data);
|
|
7646
7627
|
const bytesPick = pickComponentBytes(dataObj);
|
|
7647
7628
|
if (!bytesPick) {
|
|
7648
|
-
clearHealPending(accountId, c2.artefactAttachmentId);
|
|
7649
7629
|
return base;
|
|
7650
7630
|
}
|
|
7651
7631
|
const filename = bytesPick.mimeType === "text/html" ? `${c2.artefactTitle}.html` : `${c2.artefactTitle}.md`;
|
|
7652
7632
|
await storeComponentArtefact(accountId, c2.artefactAttachmentId, bytesPick.mimeType, bytesPick.content, filename);
|
|
7653
|
-
clearHealPending(accountId, c2.artefactAttachmentId);
|
|
7654
7633
|
streamLogPath.write(
|
|
7655
7634
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} mimeType=${bytesPick.mimeType} bytes=${bytesPick.content.length} outcome=ok
|
|
7656
7635
|
`
|
|
@@ -7662,7 +7641,6 @@ app11.post("/:id/resume", async (c) => {
|
|
|
7662
7641
|
artefactTitle: c2.artefactTitle
|
|
7663
7642
|
};
|
|
7664
7643
|
} catch (writeErr) {
|
|
7665
|
-
clearHealPending(accountId, c2.artefactAttachmentId);
|
|
7666
7644
|
const reason2 = writeErr instanceof Error ? writeErr.message : String(writeErr);
|
|
7667
7645
|
streamLogPath.write(
|
|
7668
7646
|
`[${(/* @__PURE__ */ new Date()).toISOString()}] [component-persist] heal componentName=${c2.name} attachmentId=${c2.artefactAttachmentId.slice(0, 8)} outcome=disk-fail reason=${JSON.stringify(reason2.slice(0, 200))}
|
|
@@ -8769,11 +8747,23 @@ async function cascadeDeleteDocument(params) {
|
|
|
8769
8747
|
// app/lib/file-index.ts
|
|
8770
8748
|
import * as fsp from "fs/promises";
|
|
8771
8749
|
import { resolve as resolve13, relative as relative2, join as join11, basename as basename3, extname as extname2, sep as sep3 } from "path";
|
|
8750
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
8772
8751
|
import { execFile as execFile2 } from "child_process";
|
|
8773
8752
|
import { promisify as promisify2 } from "util";
|
|
8774
8753
|
var execFileAsync2 = promisify2(execFile2);
|
|
8775
8754
|
var CONTENT_CAP = 1e5;
|
|
8776
|
-
var
|
|
8755
|
+
var OCR_TIMEOUT_MS = 3e4;
|
|
8756
|
+
var OCR_IMAGE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8757
|
+
".png",
|
|
8758
|
+
".jpg",
|
|
8759
|
+
".jpeg",
|
|
8760
|
+
".gif",
|
|
8761
|
+
".webp",
|
|
8762
|
+
".tiff",
|
|
8763
|
+
".tif",
|
|
8764
|
+
".bmp"
|
|
8765
|
+
]);
|
|
8766
|
+
var RECONCILE_DEBOUNCE_MS = 5 * 60 * 1e3;
|
|
8777
8767
|
var lastReconcileAt = /* @__PURE__ */ new Map();
|
|
8778
8768
|
var TEXT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
8779
8769
|
".txt",
|
|
@@ -8826,26 +8816,60 @@ async function extractPdf(absolute) {
|
|
|
8826
8816
|
});
|
|
8827
8817
|
return stdout.trim();
|
|
8828
8818
|
}
|
|
8819
|
+
async function ocrPdf(absolute) {
|
|
8820
|
+
const outPdf = join11(tmpdir2(), `file-index-ocr-${process.pid}-${Date.now()}.pdf`);
|
|
8821
|
+
try {
|
|
8822
|
+
await execFileAsync2(
|
|
8823
|
+
"ocrmypdf",
|
|
8824
|
+
["--skip-text", "--quiet", "--output-type", "pdf", absolute, outPdf],
|
|
8825
|
+
{ maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
|
|
8826
|
+
);
|
|
8827
|
+
const { stdout } = await execFileAsync2("pdftotext", [outPdf, "-"], {
|
|
8828
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
8829
|
+
timeout: OCR_TIMEOUT_MS
|
|
8830
|
+
});
|
|
8831
|
+
return stdout.trim();
|
|
8832
|
+
} finally {
|
|
8833
|
+
await fsp.rm(outPdf, { force: true });
|
|
8834
|
+
}
|
|
8835
|
+
}
|
|
8836
|
+
async function ocrImage(absolute) {
|
|
8837
|
+
const { stdout } = await execFileAsync2(
|
|
8838
|
+
"tesseract",
|
|
8839
|
+
[absolute, "-", "-l", "eng", "--psm", "3"],
|
|
8840
|
+
{ maxBuffer: 10 * 1024 * 1024, timeout: OCR_TIMEOUT_MS }
|
|
8841
|
+
);
|
|
8842
|
+
return stdout.trim();
|
|
8843
|
+
}
|
|
8829
8844
|
function stripHtml(html) {
|
|
8830
8845
|
return html.replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim();
|
|
8831
8846
|
}
|
|
8832
8847
|
async function extractFileContent(absolute) {
|
|
8833
8848
|
const ext = extname2(absolute).toLowerCase();
|
|
8834
8849
|
try {
|
|
8835
|
-
if (ext === ".pdf")
|
|
8850
|
+
if (ext === ".pdf") {
|
|
8851
|
+
const text = (await extractPdf(absolute)).slice(0, CONTENT_CAP);
|
|
8852
|
+
if (text.length > 0) return { content: text, route: "pdftotext" };
|
|
8853
|
+
const ocred = (await ocrPdf(absolute)).slice(0, CONTENT_CAP);
|
|
8854
|
+
return ocred.length > 0 ? { content: ocred, route: "ocr-pdf" } : { content: "", route: "name-only" };
|
|
8855
|
+
}
|
|
8836
8856
|
if (HTML_EXTENSIONS.has(ext)) {
|
|
8837
8857
|
const raw = await fsp.readFile(absolute, "utf-8");
|
|
8838
|
-
return stripHtml(raw).slice(0, CONTENT_CAP);
|
|
8858
|
+
return { content: stripHtml(raw).slice(0, CONTENT_CAP), route: "html" };
|
|
8839
8859
|
}
|
|
8840
8860
|
if (TEXT_EXTENSIONS.has(ext)) {
|
|
8841
8861
|
const raw = await fsp.readFile(absolute, "utf-8");
|
|
8842
|
-
return raw.slice(0, CONTENT_CAP);
|
|
8862
|
+
return { content: raw.slice(0, CONTENT_CAP), route: "text" };
|
|
8843
8863
|
}
|
|
8844
|
-
|
|
8864
|
+
if (OCR_IMAGE_EXTENSIONS.has(ext)) {
|
|
8865
|
+
const ocred = (await ocrImage(absolute)).slice(0, CONTENT_CAP);
|
|
8866
|
+
return ocred.length > 0 ? { content: ocred, route: "ocr-image" } : { content: "", route: "name-only" };
|
|
8867
|
+
}
|
|
8868
|
+
return { content: "", route: "name-only" };
|
|
8845
8869
|
} catch (err) {
|
|
8846
8870
|
const msg = err instanceof Error ? err.message : String(err);
|
|
8847
8871
|
console.error(`[file-index] extract-failed path="${relative2(DATA_ROOT, absolute)}" err="${msg}"`);
|
|
8848
|
-
return "";
|
|
8872
|
+
return { content: "", route: "name-only" };
|
|
8849
8873
|
}
|
|
8850
8874
|
}
|
|
8851
8875
|
async function readDisplayName(absolute) {
|
|
@@ -8909,9 +8933,86 @@ async function upsertFileArtifact(session, f) {
|
|
|
8909
8933
|
f
|
|
8910
8934
|
);
|
|
8911
8935
|
}
|
|
8936
|
+
async function linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolutePath) {
|
|
8937
|
+
const res = await session.run(
|
|
8938
|
+
`MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
|
|
8939
|
+
OPTIONAL MATCH (d:KnowledgeDocument {accountId: $accountId, sourcePath: $absolutePath})
|
|
8940
|
+
WHERE NOT d:Trashed
|
|
8941
|
+
FOREACH (_ IN CASE WHEN d IS NULL THEN [] ELSE [1] END |
|
|
8942
|
+
MERGE (f)-[:INGESTED_AS]->(d))
|
|
8943
|
+
RETURN elementId(d) AS docId`,
|
|
8944
|
+
{ accountId, relativePath, absolutePath }
|
|
8945
|
+
);
|
|
8946
|
+
const docId = res.records[0]?.get("docId");
|
|
8947
|
+
if (docId) {
|
|
8948
|
+
console.error(
|
|
8949
|
+
`[file-index] linked-kd account=${accountId} relativePath="${relativePath}" docId=${docId.slice(0, 12)}`
|
|
8950
|
+
);
|
|
8951
|
+
}
|
|
8952
|
+
}
|
|
8953
|
+
async function findLinkedKnowledgeDocs(session, accountId, relativePaths) {
|
|
8954
|
+
if (relativePaths.length === 0) return [];
|
|
8955
|
+
const res = await session.run(
|
|
8956
|
+
`MATCH (f:FileArtifact {accountId: $accountId})-[:INGESTED_AS]->(d:KnowledgeDocument)
|
|
8957
|
+
WHERE f.relativePath IN $relativePaths AND NOT d:Trashed
|
|
8958
|
+
OPTIONAL MATCH (d)-[:HAS_SECTION]->(s:Section)
|
|
8959
|
+
OPTIONAL MATCH (s)-[:HAS_CHUNK]->(c:Chunk)
|
|
8960
|
+
WITH d, collect(DISTINCT s) AS ss, collect(DISTINCT c) AS cc
|
|
8961
|
+
RETURN elementId(d) AS docId,
|
|
8962
|
+
[x IN ss WHERE x IS NOT NULL | elementId(x)] AS sectionIds,
|
|
8963
|
+
[x IN cc WHERE x IS NOT NULL | elementId(x)] AS chunkIds`,
|
|
8964
|
+
{ accountId, relativePaths }
|
|
8965
|
+
);
|
|
8966
|
+
return res.records.map((rec) => ({
|
|
8967
|
+
docId: rec.get("docId"),
|
|
8968
|
+
sectionIds: rec.get("sectionIds") ?? [],
|
|
8969
|
+
chunkIds: rec.get("chunkIds") ?? []
|
|
8970
|
+
}));
|
|
8971
|
+
}
|
|
8972
|
+
async function cascadeTrashLinkedKnowledgeDocs(session, accountId, relativePaths) {
|
|
8973
|
+
const linked = await findLinkedKnowledgeDocs(session, accountId, relativePaths);
|
|
8974
|
+
let kds = 0, sections = 0, chunks = 0;
|
|
8975
|
+
for (const { docId, sectionIds, chunkIds } of linked) {
|
|
8976
|
+
try {
|
|
8977
|
+
await trashNode({
|
|
8978
|
+
session,
|
|
8979
|
+
accountId,
|
|
8980
|
+
elementId: docId,
|
|
8981
|
+
by: "file-index:cascade",
|
|
8982
|
+
reason: "physical file removed from FileArtifact index"
|
|
8983
|
+
});
|
|
8984
|
+
kds++;
|
|
8985
|
+
for (const sid of sectionIds) {
|
|
8986
|
+
await trashNode({
|
|
8987
|
+
session,
|
|
8988
|
+
accountId,
|
|
8989
|
+
elementId: sid,
|
|
8990
|
+
by: `file-index:cascade:from-${docId}`,
|
|
8991
|
+
reason: `cascade from KnowledgeDocument ${docId}`
|
|
8992
|
+
});
|
|
8993
|
+
sections++;
|
|
8994
|
+
}
|
|
8995
|
+
for (const cid of chunkIds) {
|
|
8996
|
+
await trashNode({
|
|
8997
|
+
session,
|
|
8998
|
+
accountId,
|
|
8999
|
+
elementId: cid,
|
|
9000
|
+
by: `file-index:cascade:from-${docId}`,
|
|
9001
|
+
reason: `cascade from KnowledgeDocument ${docId}`
|
|
9002
|
+
});
|
|
9003
|
+
chunks++;
|
|
9004
|
+
}
|
|
9005
|
+
} catch (err) {
|
|
9006
|
+
console.error(
|
|
9007
|
+
`[file-index] cascade-trash-failed account=${accountId} docId=${docId.slice(0, 12)} err="${err.message}"`
|
|
9008
|
+
);
|
|
9009
|
+
}
|
|
9010
|
+
}
|
|
9011
|
+
return { kds, sections, chunks };
|
|
9012
|
+
}
|
|
8912
9013
|
async function buildArtifact(accountId, wf, embed2) {
|
|
8913
9014
|
const name = basename3(wf.absolute);
|
|
8914
|
-
const content = await extractFileContent(wf.absolute);
|
|
9015
|
+
const { content, route } = await extractFileContent(wf.absolute);
|
|
8915
9016
|
const displayName = await readDisplayName(wf.absolute);
|
|
8916
9017
|
const embedInput = `${name}
|
|
8917
9018
|
${content}`.slice(0, CONTENT_CAP);
|
|
@@ -8923,7 +9024,7 @@ ${content}`.slice(0, CONTENT_CAP);
|
|
|
8923
9024
|
console.error(`[file-index] embed-failed path="${wf.relativePath}" err="${err.message}"`);
|
|
8924
9025
|
}
|
|
8925
9026
|
console.error(
|
|
8926
|
-
`[file-index] index-file account=${accountId} path="${wf.relativePath}" chars=${content.length} embed-ms=${Date.now() - embedStart}`
|
|
9027
|
+
`[file-index] index-file account=${accountId} path="${wf.relativePath}" extractor=${route} chars=${content.length} embed-ms=${Date.now() - embedStart}`
|
|
8927
9028
|
);
|
|
8928
9029
|
return {
|
|
8929
9030
|
accountId,
|
|
@@ -8982,14 +9083,29 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
8982
9083
|
const unchanged = prior && prior.sizeBytes === wf.sizeBytes && prior.modifiedAt === wf.modifiedAt && prior.hasEmbedding;
|
|
8983
9084
|
if (unchanged) {
|
|
8984
9085
|
skipped++;
|
|
8985
|
-
|
|
9086
|
+
} else {
|
|
9087
|
+
await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
|
|
9088
|
+
upserted++;
|
|
8986
9089
|
}
|
|
8987
|
-
await
|
|
8988
|
-
upserted++;
|
|
9090
|
+
await linkFileArtifactToKnowledgeDocument(session, accountId, wf.relativePath, wf.absolute);
|
|
8989
9091
|
}
|
|
8990
9092
|
let pruned = 0;
|
|
9093
|
+
let cascadeKds = 0, cascadeSections = 0, cascadeChunks = 0;
|
|
8991
9094
|
if (clean) {
|
|
8992
9095
|
const foundPaths = walked.map((w) => w.relativePath);
|
|
9096
|
+
const goneRes = await session.run(
|
|
9097
|
+
`MATCH (f:FileArtifact {accountId: $accountId})
|
|
9098
|
+
WHERE NOT f.relativePath IN $foundPaths
|
|
9099
|
+
RETURN f.relativePath AS rp`,
|
|
9100
|
+
{ accountId, foundPaths }
|
|
9101
|
+
);
|
|
9102
|
+
const gonePaths = goneRes.records.map((r) => r.get("rp"));
|
|
9103
|
+
if (gonePaths.length > 0) {
|
|
9104
|
+
const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, gonePaths);
|
|
9105
|
+
cascadeKds = cascade.kds;
|
|
9106
|
+
cascadeSections = cascade.sections;
|
|
9107
|
+
cascadeChunks = cascade.chunks;
|
|
9108
|
+
}
|
|
8993
9109
|
const pruneRes = await session.run(
|
|
8994
9110
|
`MATCH (f:FileArtifact {accountId: $accountId})
|
|
8995
9111
|
WHERE NOT f.relativePath IN $foundPaths
|
|
@@ -9001,7 +9117,7 @@ async function reconcileFileIndex(accountId, opts) {
|
|
|
9001
9117
|
pruned = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
|
|
9002
9118
|
}
|
|
9003
9119
|
console.error(
|
|
9004
|
-
`[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} clean=${clean} ms=${Date.now() - t0}`
|
|
9120
|
+
`[file-index] reconcile account=${accountId} walked=${walked.length} upserted=${upserted} skipped=${skipped} pruned=${pruned} cascade-kds=${cascadeKds} cascade-sections=${cascadeSections} cascade-chunks=${cascadeChunks} clean=${clean} ms=${Date.now() - t0}`
|
|
9005
9121
|
);
|
|
9006
9122
|
return { walked: walked.length, upserted, skipped, pruned };
|
|
9007
9123
|
});
|
|
@@ -9030,10 +9146,12 @@ async function indexFile(accountId, relativePath, opts) {
|
|
|
9030
9146
|
modifiedAt: st.mtime.toISOString()
|
|
9031
9147
|
};
|
|
9032
9148
|
await upsertFileArtifact(session, await buildArtifact(accountId, wf, embed2));
|
|
9149
|
+
await linkFileArtifactToKnowledgeDocument(session, accountId, relativePath, absolute);
|
|
9033
9150
|
});
|
|
9034
9151
|
}
|
|
9035
9152
|
async function dropFileIndex(accountId, relativePath, opts) {
|
|
9036
9153
|
await withSession(opts, async (session) => {
|
|
9154
|
+
const cascade = await cascadeTrashLinkedKnowledgeDocs(session, accountId, [relativePath]);
|
|
9037
9155
|
const res = await session.run(
|
|
9038
9156
|
`MATCH (f:FileArtifact {accountId: $accountId, relativePath: $relativePath})
|
|
9039
9157
|
DETACH DELETE f
|
|
@@ -9042,7 +9160,9 @@ async function dropFileIndex(accountId, relativePath, opts) {
|
|
|
9042
9160
|
);
|
|
9043
9161
|
const c = res.records[0]?.get("dropped");
|
|
9044
9162
|
const dropped = typeof c?.toNumber === "function" ? c.toNumber() : Number(c ?? 0);
|
|
9045
|
-
console.error(
|
|
9163
|
+
console.error(
|
|
9164
|
+
`[file-index] drop account=${accountId} path="${relativePath}" dropped=${dropped} cascade-kds=${cascade.kds} cascade-sections=${cascade.sections} cascade-chunks=${cascade.chunks}`
|
|
9165
|
+
);
|
|
9046
9166
|
});
|
|
9047
9167
|
}
|
|
9048
9168
|
|
|
@@ -9139,7 +9259,8 @@ async function knowledgeDocOwnedByAccount(accountId, attachmentId) {
|
|
|
9139
9259
|
var app15 = new Hono();
|
|
9140
9260
|
app15.get("/", requireAdminSession, async (c) => {
|
|
9141
9261
|
const cacheKey = c.var.cacheKey;
|
|
9142
|
-
|
|
9262
|
+
const accountId = getAccountIdForSession(cacheKey);
|
|
9263
|
+
if (!accountId) {
|
|
9143
9264
|
console.error(`[data] auth-rejected endpoint="/api/admin/files" reason="no account for session"`);
|
|
9144
9265
|
return c.json({ error: "Account not found for session" }, 401);
|
|
9145
9266
|
}
|
|
@@ -9152,17 +9273,26 @@ app15.get("/", requireAdminSession, async (c) => {
|
|
|
9152
9273
|
return c.json({ error: resolution.error }, resolution.status);
|
|
9153
9274
|
}
|
|
9154
9275
|
const { absolute, relative: relPath2 } = resolution;
|
|
9276
|
+
if (crossesForeignAccountPartition(relPath2, accountId)) {
|
|
9277
|
+
console.error(`[data] account-scope-blocked endpoint="/api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
|
|
9278
|
+
return c.json({ error: "Not found" }, 404);
|
|
9279
|
+
}
|
|
9155
9280
|
try {
|
|
9156
9281
|
const info = await stat4(absolute);
|
|
9157
9282
|
if (!info.isDirectory()) {
|
|
9158
9283
|
return c.json({ error: "Path is not a directory \u2014 use /api/admin/files/download for files" }, 400);
|
|
9159
9284
|
}
|
|
9160
9285
|
const names = await readdir3(absolute);
|
|
9286
|
+
const segs = relPath2.split("/").filter(Boolean);
|
|
9287
|
+
const isAccountParent = segs.length === 1 && (segs[0] === "accounts" || segs[0] === "uploads");
|
|
9161
9288
|
const entries = [];
|
|
9162
9289
|
for (const name of names) {
|
|
9163
9290
|
if (UUID_RE3.test(name.replace(/\.meta\.json$/, "")) && name.endsWith(".meta.json")) {
|
|
9164
9291
|
continue;
|
|
9165
9292
|
}
|
|
9293
|
+
if (isAccountParent && UUID_RE3.test(name) && name !== accountId) {
|
|
9294
|
+
continue;
|
|
9295
|
+
}
|
|
9166
9296
|
try {
|
|
9167
9297
|
const entryPath = join12(absolute, name);
|
|
9168
9298
|
const s = await stat4(entryPath);
|
|
@@ -9340,6 +9470,10 @@ app15.delete("/", requireAdminSession, async (c) => {
|
|
|
9340
9470
|
return c.json({ error: resolution.error }, resolution.status);
|
|
9341
9471
|
}
|
|
9342
9472
|
const { absolute, relative: relPath2 } = resolution;
|
|
9473
|
+
if (crossesForeignAccountPartition(relPath2, accountId)) {
|
|
9474
|
+
console.error(`[data] account-scope-blocked endpoint="DELETE /api/admin/files" path="${relPath2}" account=${accountId.slice(0, 8)}\u2026`);
|
|
9475
|
+
return c.json({ error: "Not found" }, 404);
|
|
9476
|
+
}
|
|
9343
9477
|
const base = basename4(absolute);
|
|
9344
9478
|
const segments = relPath2.split("/").filter(Boolean);
|
|
9345
9479
|
if (base === "account.json" || segments.includes(".git")) {
|
|
@@ -11397,126 +11531,14 @@ var WRITE_CYPHER = `
|
|
|
11397
11531
|
var graph_default_view_default = app21;
|
|
11398
11532
|
|
|
11399
11533
|
// server/routes/admin/sidebar-artefacts.ts
|
|
11400
|
-
import
|
|
11401
|
-
import {
|
|
11402
|
-
import { resolve as resolve16, relative as relative4, isAbsolute, sep as sep6 } from "path";
|
|
11534
|
+
import { readdir as readdir4, stat as stat5 } from "fs/promises";
|
|
11535
|
+
import { resolve as resolve15, relative as relative3, isAbsolute, sep as sep5, basename as basename5 } from "path";
|
|
11403
11536
|
import { existsSync as existsSync16 } from "fs";
|
|
11404
|
-
|
|
11405
|
-
// app/lib/doc-source-resolve.ts
|
|
11406
|
-
import { readdirSync as readdirSync8, realpathSync as realpathSync5, statSync as statSync7 } from "fs";
|
|
11407
|
-
import { relative as relative3, resolve as resolve15, sep as sep5 } from "path";
|
|
11408
|
-
function containedFile(absPath, root) {
|
|
11409
|
-
try {
|
|
11410
|
-
const real = realpathSync5(absPath);
|
|
11411
|
-
const rootReal = realpathSync5(root);
|
|
11412
|
-
if (real !== rootReal && !real.startsWith(rootReal + sep5)) return null;
|
|
11413
|
-
return statSync7(real).isFile() ? real : null;
|
|
11414
|
-
} catch {
|
|
11415
|
-
return null;
|
|
11416
|
-
}
|
|
11417
|
-
}
|
|
11418
|
-
function dataFileInDir(dir) {
|
|
11419
|
-
let entries;
|
|
11420
|
-
try {
|
|
11421
|
-
entries = readdirSync8(dir, { withFileTypes: true }).flatMap((e) => e.isFile() ? [e.name] : []);
|
|
11422
|
-
} catch {
|
|
11423
|
-
return null;
|
|
11424
|
-
}
|
|
11425
|
-
const file = entries.find((e) => !e.endsWith(".meta.json"));
|
|
11426
|
-
return file ? resolve15(dir, file) : null;
|
|
11427
|
-
}
|
|
11428
|
-
function mapToDownloadRoot(absPath, roots) {
|
|
11429
|
-
const underData = containedFile(absPath, roots.dataRoot);
|
|
11430
|
-
if (underData) {
|
|
11431
|
-
return { root: "data", relPath: relative3(realpathSync5(roots.dataRoot), underData), absReal: underData };
|
|
11432
|
-
}
|
|
11433
|
-
if (roots.claudeUploadsRoot) {
|
|
11434
|
-
const underClaude = containedFile(absPath, roots.claudeUploadsRoot);
|
|
11435
|
-
if (underClaude) {
|
|
11436
|
-
return {
|
|
11437
|
-
root: "claude-uploads",
|
|
11438
|
-
relPath: relative3(realpathSync5(roots.claudeUploadsRoot), underClaude),
|
|
11439
|
-
absReal: underClaude
|
|
11440
|
-
};
|
|
11441
|
-
}
|
|
11442
|
-
}
|
|
11443
|
-
return null;
|
|
11444
|
-
}
|
|
11445
|
-
function resolveDocDownload(input, roots) {
|
|
11446
|
-
const { accountId, attachmentId, name, sourcePath, sourceType } = input;
|
|
11447
|
-
if (sourcePath) {
|
|
11448
|
-
const mapped = mapToDownloadRoot(sourcePath, roots);
|
|
11449
|
-
if (mapped) {
|
|
11450
|
-
return { ok: true, absPath: mapped.absReal, root: mapped.root, relPath: mapped.relPath, via: "stored" };
|
|
11451
|
-
}
|
|
11452
|
-
}
|
|
11453
|
-
if (attachmentId) {
|
|
11454
|
-
const uploadsDir = resolve15(roots.attachmentsRoot, accountId, attachmentId);
|
|
11455
|
-
const file = dataFileInDir(uploadsDir);
|
|
11456
|
-
if (file) {
|
|
11457
|
-
const real = containedFile(file, roots.dataRoot);
|
|
11458
|
-
if (real) {
|
|
11459
|
-
return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "uploads" };
|
|
11460
|
-
}
|
|
11461
|
-
}
|
|
11462
|
-
}
|
|
11463
|
-
if (attachmentId && roots.claudeUploadsRoot) {
|
|
11464
|
-
const claudeDir = resolve15(roots.claudeUploadsRoot, attachmentId);
|
|
11465
|
-
const file = dataFileInDir(claudeDir);
|
|
11466
|
-
if (file) {
|
|
11467
|
-
const real = containedFile(file, roots.claudeUploadsRoot);
|
|
11468
|
-
if (real) {
|
|
11469
|
-
return {
|
|
11470
|
-
ok: true,
|
|
11471
|
-
absPath: real,
|
|
11472
|
-
root: "claude-uploads",
|
|
11473
|
-
relPath: relative3(realpathSync5(roots.claudeUploadsRoot), real),
|
|
11474
|
-
via: "claude-uploads"
|
|
11475
|
-
};
|
|
11476
|
-
}
|
|
11477
|
-
}
|
|
11478
|
-
}
|
|
11479
|
-
if (name) {
|
|
11480
|
-
const outputFile = resolve15(roots.accountsDir, accountId, "output", name);
|
|
11481
|
-
const real = containedFile(outputFile, roots.dataRoot);
|
|
11482
|
-
if (real) {
|
|
11483
|
-
return { ok: true, absPath: real, root: "data", relPath: relative3(realpathSync5(roots.dataRoot), real), via: "output" };
|
|
11484
|
-
}
|
|
11485
|
-
}
|
|
11486
|
-
return { ok: false, reason: sourceType === "web" ? "no-persisted-file" : "unresolved" };
|
|
11487
|
-
}
|
|
11488
|
-
|
|
11489
|
-
// server/routes/admin/sidebar-artefacts.ts
|
|
11490
11537
|
var LIMIT = 50;
|
|
11491
|
-
var TEXT_MIME_PREFIXES = ["text/", "application/json", "application/markdown"];
|
|
11492
11538
|
var ADMIN_AGENT_FILES = ["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"];
|
|
11493
11539
|
function toDataRootRelPath(absPath) {
|
|
11494
|
-
if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT +
|
|
11495
|
-
return
|
|
11496
|
-
}
|
|
11497
|
-
function resolveDocDownloadRow(meta) {
|
|
11498
|
-
const result = resolveDocDownload(
|
|
11499
|
-
{
|
|
11500
|
-
accountId: meta.accountId,
|
|
11501
|
-
attachmentId: meta.attachmentId,
|
|
11502
|
-
name: meta.name,
|
|
11503
|
-
sourcePath: meta.sourcePath,
|
|
11504
|
-
sourceType: meta.sourceType
|
|
11505
|
-
},
|
|
11506
|
-
{
|
|
11507
|
-
dataRoot: DATA_ROOT,
|
|
11508
|
-
attachmentsRoot: ATTACHMENTS_ROOT,
|
|
11509
|
-
accountsDir: ACCOUNTS_DIR,
|
|
11510
|
-
claudeUploadsRoot: CLAUDE_UPLOADS_ROOT
|
|
11511
|
-
}
|
|
11512
|
-
);
|
|
11513
|
-
const aid = meta.attachmentId ? meta.attachmentId.slice(0, 8) : "none";
|
|
11514
|
-
if (result.ok) {
|
|
11515
|
-
console.log(`[admin/sidebar-artefacts] download-resolved id=${aid} via=${result.via} root=${result.root}`);
|
|
11516
|
-
return { downloadPath: result.relPath, downloadRoot: result.root };
|
|
11517
|
-
}
|
|
11518
|
-
console.log(`[admin/sidebar-artefacts] not-downloadable id=${aid} reason=${result.reason}`);
|
|
11519
|
-
return { downloadPath: null, downloadRoot: null };
|
|
11540
|
+
if (absPath !== DATA_ROOT && !absPath.startsWith(DATA_ROOT + sep5)) return null;
|
|
11541
|
+
return relative3(DATA_ROOT, absPath);
|
|
11520
11542
|
}
|
|
11521
11543
|
var app22 = new Hono();
|
|
11522
11544
|
app22.get("/", requireAdminSession, async (c) => {
|
|
@@ -11526,118 +11548,64 @@ app22.get("/", requireAdminSession, async (c) => {
|
|
|
11526
11548
|
return c.json({ error: "Account not found for session" }, 401);
|
|
11527
11549
|
}
|
|
11528
11550
|
const start = Date.now();
|
|
11529
|
-
const
|
|
11530
|
-
if (
|
|
11551
|
+
const outputs = await fetchOutputArtefacts(accountId);
|
|
11552
|
+
if (outputs === null) {
|
|
11531
11553
|
return c.json({ error: "Failed to load artefacts" }, 500);
|
|
11532
11554
|
}
|
|
11533
|
-
const accountDir =
|
|
11555
|
+
const accountDir = resolve15(ACCOUNTS_DIR, accountId);
|
|
11534
11556
|
const agents = await fetchAgentTemplateRows(accountDir);
|
|
11535
|
-
const artefacts = [...
|
|
11557
|
+
const artefacts = [...outputs, ...agents].sort(
|
|
11536
11558
|
(a, b) => (b.updatedAt ?? "").localeCompare(a.updatedAt ?? "")
|
|
11537
11559
|
).slice(0, LIMIT);
|
|
11538
11560
|
const ms = Date.now() - start;
|
|
11539
11561
|
console.log(
|
|
11540
|
-
`[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length}
|
|
11562
|
+
`[admin/sidebar-artefacts] account=${accountId} count=${artefacts.length} outputs=${outputs.length} agents=${agents.length} ms=${ms}`
|
|
11541
11563
|
);
|
|
11542
11564
|
return c.json({ artefacts });
|
|
11543
11565
|
});
|
|
11544
|
-
async function
|
|
11566
|
+
async function fetchOutputArtefacts(accountId) {
|
|
11545
11567
|
const session = getSession();
|
|
11546
|
-
let metas = [];
|
|
11547
11568
|
try {
|
|
11548
11569
|
const result = await session.run(
|
|
11549
|
-
`MATCH (
|
|
11550
|
-
WHERE
|
|
11551
|
-
RETURN
|
|
11552
|
-
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
11558
|
-
|
|
11559
|
-
|
|
11560
|
-
|
|
11561
|
-
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11570
|
+
`MATCH (f:FileArtifact { accountId: $accountId })
|
|
11571
|
+
WHERE f.relativePath CONTAINS '/output/'
|
|
11572
|
+
RETURN f.relativePath AS relativePath,
|
|
11573
|
+
f.displayName AS displayName,
|
|
11574
|
+
f.mimeType AS mimeType,
|
|
11575
|
+
f.modifiedAt AS modifiedAt
|
|
11576
|
+
ORDER BY f.modifiedAt DESC
|
|
11577
|
+
LIMIT 50`,
|
|
11578
|
+
{ accountId }
|
|
11579
|
+
);
|
|
11580
|
+
return result.records.map((r) => {
|
|
11581
|
+
const relativePath = r.get("relativePath");
|
|
11582
|
+
const displayName = r.get("displayName") ?? null;
|
|
11583
|
+
const mimeType = r.get("mimeType") ?? "";
|
|
11584
|
+
const modifiedAt = r.get("modifiedAt") ?? "";
|
|
11585
|
+
return {
|
|
11586
|
+
id: `output-file:${relativePath}`,
|
|
11587
|
+
name: displayName ?? basename5(relativePath),
|
|
11588
|
+
kind: "output-file",
|
|
11589
|
+
updatedAt: modifiedAt,
|
|
11590
|
+
editable: false,
|
|
11591
|
+
mimeType,
|
|
11592
|
+
downloadPath: relativePath,
|
|
11593
|
+
downloadRoot: "data"
|
|
11594
|
+
};
|
|
11595
|
+
});
|
|
11567
11596
|
} catch (err) {
|
|
11568
11597
|
const message = err instanceof Error ? err.message : String(err);
|
|
11569
11598
|
console.error(`[admin/sidebar-artefacts] account=${accountId} error="${message}"`);
|
|
11570
|
-
await session.close();
|
|
11571
11599
|
return null;
|
|
11572
11600
|
} finally {
|
|
11573
11601
|
await session.close();
|
|
11574
11602
|
}
|
|
11575
|
-
return Promise.all(metas.map(async (m) => {
|
|
11576
|
-
const { content, skipReason } = await readArtefactContent(accountId, m.attachmentId, m.mimeType, m.name);
|
|
11577
|
-
const { downloadPath, downloadRoot } = resolveDocDownloadRow({
|
|
11578
|
-
accountId,
|
|
11579
|
-
attachmentId: m.attachmentId,
|
|
11580
|
-
name: m.name,
|
|
11581
|
-
sourcePath: m.sourcePath,
|
|
11582
|
-
sourceType: m.sourceType
|
|
11583
|
-
});
|
|
11584
|
-
return {
|
|
11585
|
-
id: m.id,
|
|
11586
|
-
name: m.name,
|
|
11587
|
-
kind: "knowledge-doc",
|
|
11588
|
-
updatedAt: m.updatedAt,
|
|
11589
|
-
content,
|
|
11590
|
-
editable: skipReason === null,
|
|
11591
|
-
mimeType: m.mimeType,
|
|
11592
|
-
skipReason,
|
|
11593
|
-
downloadPath,
|
|
11594
|
-
downloadRoot
|
|
11595
|
-
};
|
|
11596
|
-
}));
|
|
11597
|
-
}
|
|
11598
|
-
async function readArtefactContent(accountId, attachmentId, mimeType, displayName) {
|
|
11599
|
-
if (!attachmentId) {
|
|
11600
|
-
logSkip(displayName, "null-attachmentId", mimeType);
|
|
11601
|
-
return { content: "", skipReason: "null-attachmentId" };
|
|
11602
|
-
}
|
|
11603
|
-
const isText = TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p));
|
|
11604
|
-
if (!isText) {
|
|
11605
|
-
logSkip(displayName, "non-text-mime", mimeType);
|
|
11606
|
-
return { content: "", skipReason: "non-text-mime" };
|
|
11607
|
-
}
|
|
11608
|
-
const accountDir = resolve16(ATTACHMENTS_ROOT, accountId);
|
|
11609
|
-
const dir = resolve16(accountDir, attachmentId);
|
|
11610
|
-
try {
|
|
11611
|
-
validateFilePathInAccount(dir, accountDir);
|
|
11612
|
-
} catch {
|
|
11613
|
-
logSkip(displayName, "containment-rejected", mimeType);
|
|
11614
|
-
return { content: "", skipReason: "containment-rejected" };
|
|
11615
|
-
}
|
|
11616
|
-
try {
|
|
11617
|
-
const entries = await readdir4(dir);
|
|
11618
|
-
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11619
|
-
if (!dataFile) {
|
|
11620
|
-
logSkip(displayName, "missing-on-disk", mimeType);
|
|
11621
|
-
return { content: "", skipReason: "missing-on-disk" };
|
|
11622
|
-
}
|
|
11623
|
-
return { content: await readFile5(resolve16(dir, dataFile), "utf-8"), skipReason: null };
|
|
11624
|
-
} catch (err) {
|
|
11625
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
11626
|
-
console.error(`[admin/sidebar-artefacts] read-failed attachmentId=${attachmentId.slice(0, 8)} error="${message}"`);
|
|
11627
|
-
logSkip(displayName, "missing-on-disk", mimeType);
|
|
11628
|
-
return { content: "", skipReason: "missing-on-disk" };
|
|
11629
|
-
}
|
|
11630
|
-
}
|
|
11631
|
-
function logSkip(name, reason, mimeType) {
|
|
11632
|
-
console.log(
|
|
11633
|
-
`[admin/sidebar-artefacts] content-skipped name="${name}" reason=${reason} mimeType=${mimeType || "none"}`
|
|
11634
|
-
);
|
|
11635
11603
|
}
|
|
11636
11604
|
async function fetchAgentTemplateRows(accountDir) {
|
|
11637
11605
|
const rows = [];
|
|
11638
11606
|
for (const filename of ADMIN_AGENT_FILES) {
|
|
11639
|
-
const overridePath =
|
|
11640
|
-
const bundledPath =
|
|
11607
|
+
const overridePath = resolve15(accountDir, "agents", "admin", filename);
|
|
11608
|
+
const bundledPath = resolve15(PLATFORM_ROOT, "templates", "agents", "admin", filename);
|
|
11641
11609
|
const labelStem = filename.replace(/\.md$/, "");
|
|
11642
11610
|
const row = await readAgentTemplateRow({
|
|
11643
11611
|
id: `agent-template:admin:${filename}`,
|
|
@@ -11651,12 +11619,12 @@ async function fetchAgentTemplateRows(accountDir) {
|
|
|
11651
11619
|
});
|
|
11652
11620
|
if (row) rows.push(row);
|
|
11653
11621
|
}
|
|
11654
|
-
const overrideDir =
|
|
11655
|
-
const bundledDir =
|
|
11622
|
+
const overrideDir = resolve15(accountDir, "specialists", "agents");
|
|
11623
|
+
const bundledDir = resolve15(PLATFORM_ROOT, "templates", "specialists", "agents");
|
|
11656
11624
|
const specialistNames = await unionSpecialistFilenames(overrideDir, bundledDir);
|
|
11657
11625
|
for (const filename of specialistNames) {
|
|
11658
|
-
const overridePath =
|
|
11659
|
-
const bundledPath =
|
|
11626
|
+
const overridePath = resolve15(overrideDir, filename);
|
|
11627
|
+
const bundledPath = resolve15(bundledDir, filename);
|
|
11660
11628
|
const row = await readAgentTemplateRow({
|
|
11661
11629
|
id: `agent-template:specialist:${filename}`,
|
|
11662
11630
|
displayName: filename.replace(/\.md$/, ""),
|
|
@@ -11711,20 +11679,15 @@ async function readAgentTemplateRow(inp) {
|
|
|
11711
11679
|
}
|
|
11712
11680
|
if (!chosenPath) return null;
|
|
11713
11681
|
try {
|
|
11714
|
-
const
|
|
11715
|
-
readFile5(chosenPath, "utf-8"),
|
|
11716
|
-
stat5(chosenPath)
|
|
11717
|
-
]);
|
|
11682
|
+
const st = await stat5(chosenPath);
|
|
11718
11683
|
const dlPath = toDataRootRelPath(chosenPath);
|
|
11719
11684
|
return {
|
|
11720
11685
|
id: inp.id,
|
|
11721
11686
|
name: inp.displayName,
|
|
11722
11687
|
kind: "agent-template",
|
|
11723
11688
|
updatedAt: st.mtime.toISOString(),
|
|
11724
|
-
content,
|
|
11725
11689
|
editable: inp.editable,
|
|
11726
11690
|
mimeType: "text/markdown",
|
|
11727
|
-
skipReason: null,
|
|
11728
11691
|
// Override paths live under <accountDir> (inside DATA_ROOT) and are
|
|
11729
11692
|
// downloadable; bundled-fallback templates live under PLATFORM_ROOT
|
|
11730
11693
|
// (outside DATA_ROOT) → null, so the client shows "not downloadable".
|
|
@@ -11742,17 +11705,15 @@ async function readAgentTemplateRow(inp) {
|
|
|
11742
11705
|
}
|
|
11743
11706
|
}
|
|
11744
11707
|
function isWithin(target, root) {
|
|
11745
|
-
const rel =
|
|
11708
|
+
const rel = relative3(root, target);
|
|
11746
11709
|
return !rel.startsWith("..") && !isAbsolute(rel);
|
|
11747
11710
|
}
|
|
11748
11711
|
var sidebar_artefacts_default = app22;
|
|
11749
11712
|
|
|
11750
11713
|
// server/routes/admin/sidebar-artefact-save.ts
|
|
11751
|
-
import { mkdir as mkdir4,
|
|
11752
|
-
import { resolve as
|
|
11753
|
-
import { existsSync as existsSync17 } from "fs";
|
|
11714
|
+
import { mkdir as mkdir4, stat as stat6, writeFile as writeFile5 } from "fs/promises";
|
|
11715
|
+
import { resolve as resolve16 } from "path";
|
|
11754
11716
|
var ADMIN_AGENT_FILES2 = /* @__PURE__ */ new Set(["IDENTITY.md", "SOUL.md", "KNOWLEDGE.md"]);
|
|
11755
|
-
var UUID_RE4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
11756
11717
|
var app23 = new Hono();
|
|
11757
11718
|
app23.post("/", requireAdminSession, async (c) => {
|
|
11758
11719
|
const cacheKey = c.var.cacheKey;
|
|
@@ -11762,12 +11723,8 @@ app23.post("/", requireAdminSession, async (c) => {
|
|
|
11762
11723
|
if (!body || typeof body.id !== "string" || typeof body.content !== "string") {
|
|
11763
11724
|
return c.json({ error: "id and content required" }, 400);
|
|
11764
11725
|
}
|
|
11765
|
-
const accountDir =
|
|
11766
|
-
const resolved = await resolveSavePath(body.id,
|
|
11767
|
-
if (resolved.kind === "heal-race") {
|
|
11768
|
-
c.header("Retry-After", "2");
|
|
11769
|
-
return c.json({ error: "heal-race", reason: resolved.reason }, 503);
|
|
11770
|
-
}
|
|
11726
|
+
const accountDir = resolve16(ACCOUNTS_DIR, accountId);
|
|
11727
|
+
const resolved = await resolveSavePath(body.id, accountDir);
|
|
11771
11728
|
if (resolved.kind === "reject") {
|
|
11772
11729
|
console.error(
|
|
11773
11730
|
`[admin/sidebar-artefact-save] auth-rejected reason=${resolved.reason} path=${body.id}`
|
|
@@ -11796,129 +11753,122 @@ app23.post("/", requireAdminSession, async (c) => {
|
|
|
11796
11753
|
);
|
|
11797
11754
|
return c.json({ updatedAt });
|
|
11798
11755
|
});
|
|
11799
|
-
async function resolveSavePath(id,
|
|
11800
|
-
if (id.startsWith("agent-template:")) {
|
|
11801
|
-
|
|
11802
|
-
if (parts.length !== 3) return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11803
|
-
const [, role, filename] = parts;
|
|
11804
|
-
if (role === "specialist") {
|
|
11805
|
-
return { kind: "reject", status: 403, reason: "specialist-write-blocked" };
|
|
11806
|
-
}
|
|
11807
|
-
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
11808
|
-
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11809
|
-
}
|
|
11810
|
-
const parent = resolve17(accountDir, "agents", "admin");
|
|
11811
|
-
await mkdir4(parent, { recursive: true });
|
|
11812
|
-
try {
|
|
11813
|
-
validateFilePathInAccount(parent, accountDir);
|
|
11814
|
-
} catch {
|
|
11815
|
-
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11816
|
-
}
|
|
11817
|
-
return { kind: "admin-template", path: resolve17(parent, filename) };
|
|
11756
|
+
async function resolveSavePath(id, accountDir) {
|
|
11757
|
+
if (!id.startsWith("agent-template:")) {
|
|
11758
|
+
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11818
11759
|
}
|
|
11819
|
-
|
|
11820
|
-
|
|
11821
|
-
|
|
11822
|
-
|
|
11823
|
-
|
|
11824
|
-
|
|
11825
|
-
|
|
11826
|
-
|
|
11827
|
-
|
|
11828
|
-
|
|
11829
|
-
|
|
11830
|
-
|
|
11831
|
-
|
|
11832
|
-
|
|
11833
|
-
|
|
11834
|
-
const cnt = result.records[0]?.get("cnt")?.toNumber?.() ?? 0;
|
|
11835
|
-
if (cnt > 0) {
|
|
11836
|
-
console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=503-retry source=neo4j-row`);
|
|
11837
|
-
return { kind: "heal-race", reason: "dir-pending" };
|
|
11838
|
-
}
|
|
11839
|
-
console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=400-terminal`);
|
|
11840
|
-
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11841
|
-
} catch (neoErr) {
|
|
11842
|
-
const reason = neoErr instanceof Error ? neoErr.message.slice(0, 120) : String(neoErr).slice(0, 120);
|
|
11843
|
-
console.error(`[admin/sidebar-artefact-save] heal-race attachmentId=${attShort} outcome=neo4j-fail reason="${reason}"`);
|
|
11844
|
-
return { kind: "heal-race", reason: "neo4j-fail" };
|
|
11845
|
-
} finally {
|
|
11846
|
-
try {
|
|
11847
|
-
await session.close();
|
|
11848
|
-
} catch {
|
|
11849
|
-
}
|
|
11850
|
-
}
|
|
11851
|
-
}
|
|
11852
|
-
try {
|
|
11853
|
-
validateFilePathInAccount(dir, resolve17(ATTACHMENTS_ROOT, accountId));
|
|
11854
|
-
} catch {
|
|
11855
|
-
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11856
|
-
}
|
|
11857
|
-
const entries = await readdir5(dir);
|
|
11858
|
-
const dataFile = entries.find((f) => !f.endsWith(".meta.json"));
|
|
11859
|
-
if (!dataFile) {
|
|
11860
|
-
return { kind: "reject", status: 400, reason: "not-found" };
|
|
11861
|
-
}
|
|
11862
|
-
return { kind: "knowledge-doc", path: resolve17(dir, dataFile) };
|
|
11760
|
+
const parts = id.split(":");
|
|
11761
|
+
if (parts.length !== 3) return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11762
|
+
const [, role, filename] = parts;
|
|
11763
|
+
if (role === "specialist") {
|
|
11764
|
+
return { kind: "reject", status: 403, reason: "specialist-write-blocked" };
|
|
11765
|
+
}
|
|
11766
|
+
if (role !== "admin" || !ADMIN_AGENT_FILES2.has(filename)) {
|
|
11767
|
+
return { kind: "reject", status: 400, reason: "invalid-id" };
|
|
11768
|
+
}
|
|
11769
|
+
const parent = resolve16(accountDir, "agents", "admin");
|
|
11770
|
+
await mkdir4(parent, { recursive: true });
|
|
11771
|
+
try {
|
|
11772
|
+
validateFilePathInAccount(parent, accountDir);
|
|
11773
|
+
} catch {
|
|
11774
|
+
return { kind: "reject", status: 400, reason: "containment-rejected" };
|
|
11863
11775
|
}
|
|
11864
|
-
return { kind: "
|
|
11776
|
+
return { kind: "admin-template", path: resolve16(parent, filename) };
|
|
11865
11777
|
}
|
|
11866
11778
|
function relPath(absPath, root) {
|
|
11867
11779
|
return absPath.startsWith(root) ? absPath.slice(root.length + 1) : absPath;
|
|
11868
11780
|
}
|
|
11869
11781
|
var sidebar_artefact_save_default = app23;
|
|
11870
11782
|
|
|
11871
|
-
// server/routes/admin/sidebar-
|
|
11872
|
-
import {
|
|
11873
|
-
import {
|
|
11874
|
-
|
|
11875
|
-
var
|
|
11783
|
+
// server/routes/admin/sidebar-sessions.ts
|
|
11784
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync14, statSync as statSync7 } from "fs";
|
|
11785
|
+
import { join as join13 } from "path";
|
|
11786
|
+
var RC_URL_RE = /https:\/\/claude\.ai\/code\/session_[A-Za-z0-9_-]+/;
|
|
11787
|
+
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
11788
|
+
var TITLE_MAX = 80;
|
|
11876
11789
|
var app24 = new Hono();
|
|
11790
|
+
function projectsDirForAccount(accountId) {
|
|
11791
|
+
const configDir2 = process.env.CLAUDE_CONFIG_DIR;
|
|
11792
|
+
if (!configDir2) return null;
|
|
11793
|
+
const accountCwd = join13(DATA_ROOT, "accounts", accountId);
|
|
11794
|
+
const slug = accountCwd.replace(/\//g, "-");
|
|
11795
|
+
return join13(configDir2, "projects", slug);
|
|
11796
|
+
}
|
|
11797
|
+
function firstUserMessage(body) {
|
|
11798
|
+
for (const line of body.split("\n")) {
|
|
11799
|
+
if (!line || line[0] !== "{") continue;
|
|
11800
|
+
let parsed;
|
|
11801
|
+
try {
|
|
11802
|
+
parsed = JSON.parse(line);
|
|
11803
|
+
} catch {
|
|
11804
|
+
continue;
|
|
11805
|
+
}
|
|
11806
|
+
if (!parsed || typeof parsed !== "object") continue;
|
|
11807
|
+
const row = parsed;
|
|
11808
|
+
if (row.type !== "user" || !row.message || row.message.role !== "user") continue;
|
|
11809
|
+
const content = row.message.content;
|
|
11810
|
+
if (typeof content !== "string") continue;
|
|
11811
|
+
const trimmed = content.trim().replace(/\s+/g, " ");
|
|
11812
|
+
if (!trimmed) continue;
|
|
11813
|
+
return trimmed.length > TITLE_MAX ? trimmed.slice(0, TITLE_MAX - 1) + "\u2026" : trimmed;
|
|
11814
|
+
}
|
|
11815
|
+
return null;
|
|
11816
|
+
}
|
|
11877
11817
|
app24.get("/", requireAdminSession, async (c) => {
|
|
11878
11818
|
const cacheKey = c.var.cacheKey;
|
|
11879
11819
|
const accountId = getAccountIdForSession(cacheKey);
|
|
11880
|
-
if (!accountId)
|
|
11881
|
-
|
|
11882
|
-
if (!UUID_RE5.test(id)) {
|
|
11883
|
-
console.error(`[admin/sidebar-artefact-content] not-found id=${id.slice(0, 8)}`);
|
|
11884
|
-
return new Response("Not found", { status: 404 });
|
|
11820
|
+
if (!accountId) {
|
|
11821
|
+
return c.json({ error: "Account not found for session" }, 401);
|
|
11885
11822
|
}
|
|
11886
|
-
const
|
|
11887
|
-
if (!
|
|
11888
|
-
console.error(`[admin
|
|
11889
|
-
return
|
|
11823
|
+
const projectsDir = projectsDirForAccount(accountId);
|
|
11824
|
+
if (!projectsDir) {
|
|
11825
|
+
console.error(`[admin-sessions-list] account=${accountId} no-claude-config-dir`);
|
|
11826
|
+
return c.json({ sessions: [] });
|
|
11890
11827
|
}
|
|
11891
|
-
let
|
|
11828
|
+
let entries;
|
|
11892
11829
|
try {
|
|
11893
|
-
|
|
11894
|
-
} catch {
|
|
11895
|
-
|
|
11896
|
-
|
|
11897
|
-
|
|
11898
|
-
|
|
11899
|
-
|
|
11900
|
-
|
|
11901
|
-
console.error(`[admin
|
|
11902
|
-
return
|
|
11830
|
+
entries = readdirSync8(projectsDir);
|
|
11831
|
+
} catch (err) {
|
|
11832
|
+
const code = err.code;
|
|
11833
|
+
if (code === "ENOENT") {
|
|
11834
|
+
console.log(`[admin-sessions-list] account=${accountId} rows=0 reason=no-projects-dir`);
|
|
11835
|
+
return c.json({ sessions: [] });
|
|
11836
|
+
}
|
|
11837
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11838
|
+
console.error(`[admin-sessions-list] account=${accountId} readdir-failed error="${message}"`);
|
|
11839
|
+
return c.json({ error: "projects-dir-unreadable" }, 500);
|
|
11903
11840
|
}
|
|
11904
|
-
const
|
|
11905
|
-
const
|
|
11906
|
-
|
|
11907
|
-
|
|
11908
|
-
|
|
11909
|
-
|
|
11910
|
-
|
|
11911
|
-
|
|
11912
|
-
"
|
|
11913
|
-
|
|
11914
|
-
|
|
11841
|
+
const rows = [];
|
|
11842
|
+
for (const name of entries) {
|
|
11843
|
+
if (!SESSION_ID_RE.test(name)) continue;
|
|
11844
|
+
const sessionId = name.slice(0, -".jsonl".length);
|
|
11845
|
+
const path2 = join13(projectsDir, name);
|
|
11846
|
+
let body;
|
|
11847
|
+
let mtimeMs;
|
|
11848
|
+
try {
|
|
11849
|
+
body = readFileSync14(path2, "utf8");
|
|
11850
|
+
mtimeMs = statSync7(path2).mtimeMs;
|
|
11851
|
+
} catch {
|
|
11852
|
+
continue;
|
|
11915
11853
|
}
|
|
11916
|
-
|
|
11854
|
+
const urlMatch = body.match(RC_URL_RE);
|
|
11855
|
+
if (!urlMatch) continue;
|
|
11856
|
+
const title = firstUserMessage(body) ?? sessionId.slice(0, 8);
|
|
11857
|
+
rows.push({
|
|
11858
|
+
sessionId,
|
|
11859
|
+
title,
|
|
11860
|
+
url: urlMatch[0],
|
|
11861
|
+
capturedAt: new Date(mtimeMs).toISOString()
|
|
11862
|
+
});
|
|
11863
|
+
}
|
|
11864
|
+
rows.sort((a, b) => a.capturedAt < b.capturedAt ? 1 : -1);
|
|
11865
|
+
console.log(`[admin-sessions-list] account=${accountId} rows=${rows.length}`);
|
|
11866
|
+
return c.json({ sessions: rows });
|
|
11917
11867
|
});
|
|
11918
|
-
var
|
|
11868
|
+
var sidebar_sessions_default = app24;
|
|
11919
11869
|
|
|
11920
11870
|
// server/routes/admin/system-stats.ts
|
|
11921
|
-
import { readFile as
|
|
11871
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
11922
11872
|
import { cpus, loadavg, totalmem, freemem } from "os";
|
|
11923
11873
|
function parseCpuStat(blob) {
|
|
11924
11874
|
const line = blob.split("\n", 1)[0] ?? "";
|
|
@@ -11962,11 +11912,11 @@ function parseMeminfo(blob) {
|
|
|
11962
11912
|
}
|
|
11963
11913
|
var SAMPLE_DELAY_MS = 100;
|
|
11964
11914
|
async function sampleLinux() {
|
|
11965
|
-
const a = parseCpuStat(await
|
|
11915
|
+
const a = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
|
|
11966
11916
|
await new Promise((r) => setTimeout(r, SAMPLE_DELAY_MS));
|
|
11967
|
-
const b = parseCpuStat(await
|
|
11968
|
-
const meminfo = parseMeminfo(await
|
|
11969
|
-
const loadavgRaw = (await
|
|
11917
|
+
const b = parseCpuStat(await readFile5("/proc/stat", "utf-8"));
|
|
11918
|
+
const meminfo = parseMeminfo(await readFile5("/proc/meminfo", "utf-8"));
|
|
11919
|
+
const loadavgRaw = (await readFile5("/proc/loadavg", "utf-8")).trim();
|
|
11970
11920
|
const [l1, l5, l15] = loadavgRaw.split(/\s+/, 3).map(Number);
|
|
11971
11921
|
if (![l1, l5, l15].every(Number.isFinite)) {
|
|
11972
11922
|
throw new Error(`malformed /proc/loadavg: ${loadavgRaw}`);
|
|
@@ -12039,24 +11989,24 @@ app25.get("/", async (c) => {
|
|
|
12039
11989
|
var system_stats_default = app25;
|
|
12040
11990
|
|
|
12041
11991
|
// server/routes/admin/health.ts
|
|
12042
|
-
import { existsSync as
|
|
12043
|
-
import { resolve as
|
|
12044
|
-
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ??
|
|
11992
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
11993
|
+
import { resolve as resolve17, join as join14 } from "path";
|
|
11994
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
|
|
12045
11995
|
var brandHostname = "maxy";
|
|
12046
|
-
var brandJsonPath =
|
|
12047
|
-
if (
|
|
11996
|
+
var brandJsonPath = join14(PLATFORM_ROOT6, "config", "brand.json");
|
|
11997
|
+
if (existsSync17(brandJsonPath)) {
|
|
12048
11998
|
try {
|
|
12049
|
-
const brand = JSON.parse(
|
|
11999
|
+
const brand = JSON.parse(readFileSync15(brandJsonPath, "utf-8"));
|
|
12050
12000
|
if (brand.hostname) brandHostname = brand.hostname;
|
|
12051
12001
|
} catch {
|
|
12052
12002
|
}
|
|
12053
12003
|
}
|
|
12054
|
-
var VERSION_FILE =
|
|
12004
|
+
var VERSION_FILE = resolve17(PLATFORM_ROOT6, `config/.${brandHostname}-version`);
|
|
12055
12005
|
var PROCESS_STARTED_AT = (/* @__PURE__ */ new Date()).toISOString();
|
|
12056
12006
|
var PROBE_TIMEOUT_MS = 1e3;
|
|
12057
12007
|
function readVersion() {
|
|
12058
|
-
if (!
|
|
12059
|
-
return
|
|
12008
|
+
if (!existsSync17(VERSION_FILE)) return "unknown";
|
|
12009
|
+
return readFileSync15(VERSION_FILE, "utf-8").trim() || "unknown";
|
|
12060
12010
|
}
|
|
12061
12011
|
async function probeConversationDb() {
|
|
12062
12012
|
let session;
|
|
@@ -12258,7 +12208,7 @@ app27.post("/", requireAdminSession, async (c) => {
|
|
|
12258
12208
|
var linkedin_ingest_default = app27;
|
|
12259
12209
|
|
|
12260
12210
|
// server/routes/admin/post-turn-context.ts
|
|
12261
|
-
import
|
|
12211
|
+
import neo4j2 from "neo4j-driver";
|
|
12262
12212
|
var TAG21 = "[post-turn-context]";
|
|
12263
12213
|
var STRIPPED_PROPERTIES2 = /* @__PURE__ */ new Set([
|
|
12264
12214
|
"embedding",
|
|
@@ -12271,10 +12221,10 @@ function isLoopbackAddr2(addr) {
|
|
|
12271
12221
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12272
12222
|
}
|
|
12273
12223
|
function convertValue(value) {
|
|
12274
|
-
if (
|
|
12224
|
+
if (neo4j2.isDateTime(value) || neo4j2.isDate(value) || neo4j2.isLocalDateTime(value) || neo4j2.isLocalTime(value) || neo4j2.isTime(value) || neo4j2.isDuration(value)) {
|
|
12275
12225
|
return value.toString();
|
|
12276
12226
|
}
|
|
12277
|
-
if (
|
|
12227
|
+
if (neo4j2.isInt(value)) {
|
|
12278
12228
|
return value.inSafeRange() ? value.toNumber() : value.toString();
|
|
12279
12229
|
}
|
|
12280
12230
|
if (Array.isArray(value)) {
|
|
@@ -12361,7 +12311,7 @@ app28.get("/", async (c) => {
|
|
|
12361
12311
|
var post_turn_context_default = app28;
|
|
12362
12312
|
|
|
12363
12313
|
// server/routes/admin/public-session-context.ts
|
|
12364
|
-
import
|
|
12314
|
+
import neo4j3 from "neo4j-driver";
|
|
12365
12315
|
var TAG22 = "[public-session-context]";
|
|
12366
12316
|
var STRIPPED_PROPERTIES3 = /* @__PURE__ */ new Set([
|
|
12367
12317
|
"embedding",
|
|
@@ -12374,10 +12324,10 @@ function isLoopbackAddr3(addr) {
|
|
|
12374
12324
|
return addr === "127.0.0.1" || addr === "::1" || addr === "::ffff:127.0.0.1";
|
|
12375
12325
|
}
|
|
12376
12326
|
function convertValue2(value) {
|
|
12377
|
-
if (
|
|
12327
|
+
if (neo4j3.isDateTime(value) || neo4j3.isDate(value) || neo4j3.isLocalDateTime(value) || neo4j3.isLocalTime(value) || neo4j3.isTime(value) || neo4j3.isDuration(value)) {
|
|
12378
12328
|
return value.toString();
|
|
12379
12329
|
}
|
|
12380
|
-
if (
|
|
12330
|
+
if (neo4j3.isInt(value)) {
|
|
12381
12331
|
return value.inSafeRange() ? value.toNumber() : value.toString();
|
|
12382
12332
|
}
|
|
12383
12333
|
if (Array.isArray(value)) {
|
|
@@ -12553,7 +12503,7 @@ app32.route("/graph-labels-in-graph", graph_labels_in_graph_default);
|
|
|
12553
12503
|
app32.route("/graph-default-view", graph_default_view_default);
|
|
12554
12504
|
app32.route("/sidebar-artefacts", sidebar_artefacts_default);
|
|
12555
12505
|
app32.route("/sidebar-artefact-save", sidebar_artefact_save_default);
|
|
12556
|
-
app32.route("/sidebar-
|
|
12506
|
+
app32.route("/sidebar-sessions", sidebar_sessions_default);
|
|
12557
12507
|
app32.route("/system-stats", system_stats_default);
|
|
12558
12508
|
app32.route("/health-brand", health_default2);
|
|
12559
12509
|
app32.route("/linkedin-ingest", linkedin_ingest_default);
|
|
@@ -12564,17 +12514,17 @@ app32.route("/access-session-evict", access_session_evict_default);
|
|
|
12564
12514
|
var admin_default = app32;
|
|
12565
12515
|
|
|
12566
12516
|
// app/lib/access-gate.ts
|
|
12567
|
-
import
|
|
12568
|
-
import { readFileSync as
|
|
12569
|
-
import { resolve as
|
|
12517
|
+
import neo4j4 from "neo4j-driver";
|
|
12518
|
+
import { readFileSync as readFileSync16 } from "fs";
|
|
12519
|
+
import { resolve as resolve18 } from "path";
|
|
12570
12520
|
import { randomUUID as randomUUID8 } from "crypto";
|
|
12571
|
-
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12521
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve18(process.cwd(), "..");
|
|
12572
12522
|
var driver = null;
|
|
12573
12523
|
function readPassword() {
|
|
12574
12524
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
12575
|
-
const passwordFile =
|
|
12525
|
+
const passwordFile = resolve18(PLATFORM_ROOT7, "config/.neo4j-password");
|
|
12576
12526
|
try {
|
|
12577
|
-
return
|
|
12527
|
+
return readFileSync16(passwordFile, "utf-8").trim();
|
|
12578
12528
|
} catch {
|
|
12579
12529
|
throw new Error(
|
|
12580
12530
|
`Neo4j password not found. Expected at ${passwordFile} or in NEO4J_PASSWORD env var.`
|
|
@@ -12592,7 +12542,7 @@ function getDriver() {
|
|
|
12592
12542
|
const user = process.env.NEO4J_USER ?? "neo4j";
|
|
12593
12543
|
const password = readPassword();
|
|
12594
12544
|
console.error(`[ui/access-gate] resolved neo4j_uri=${uri}`);
|
|
12595
|
-
driver =
|
|
12545
|
+
driver = neo4j4.driver(uri, neo4j4.auth.basic(user, password), {
|
|
12596
12546
|
maxConnectionPoolSize: 5
|
|
12597
12547
|
});
|
|
12598
12548
|
}
|
|
@@ -12862,9 +12812,9 @@ var verify_token_default = app33;
|
|
|
12862
12812
|
|
|
12863
12813
|
// app/lib/access-email.ts
|
|
12864
12814
|
import { spawn as spawn2 } from "child_process";
|
|
12865
|
-
import { resolve as
|
|
12866
|
-
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ??
|
|
12867
|
-
var SEND_SCRIPT =
|
|
12815
|
+
import { resolve as resolve19 } from "path";
|
|
12816
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
|
|
12817
|
+
var SEND_SCRIPT = resolve19(
|
|
12868
12818
|
PLATFORM_ROOT8,
|
|
12869
12819
|
"plugins",
|
|
12870
12820
|
"email",
|
|
@@ -13006,8 +12956,8 @@ app35.route("/request-magic-link", request_magic_link_default);
|
|
|
13006
12956
|
var access_default = app35;
|
|
13007
12957
|
|
|
13008
12958
|
// server/routes/sites.ts
|
|
13009
|
-
import { existsSync as
|
|
13010
|
-
import { resolve as
|
|
12959
|
+
import { existsSync as existsSync18, readFileSync as readFileSync17, realpathSync as realpathSync5, statSync as statSync8 } from "fs";
|
|
12960
|
+
import { resolve as resolve20 } from "path";
|
|
13011
12961
|
var SAFE_SEG_RE = /^[a-z0-9_][a-z0-9_.-]{0,99}$/i;
|
|
13012
12962
|
var MIME = {
|
|
13013
12963
|
".html": "text/html; charset=utf-8",
|
|
@@ -13064,19 +13014,19 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
13064
13014
|
}
|
|
13065
13015
|
segments.push(seg);
|
|
13066
13016
|
}
|
|
13067
|
-
const rootDir =
|
|
13068
|
-
let filePath = segments.length === 0 ? rootDir :
|
|
13017
|
+
const rootDir = resolve20(account.accountDir, "sites");
|
|
13018
|
+
let filePath = segments.length === 0 ? rootDir : resolve20(rootDir, ...segments);
|
|
13069
13019
|
if (filePath !== rootDir && !filePath.startsWith(rootDir + "/")) {
|
|
13070
13020
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
13071
13021
|
return c.text("Forbidden", 403);
|
|
13072
13022
|
}
|
|
13073
|
-
let
|
|
13023
|
+
let stat8;
|
|
13074
13024
|
try {
|
|
13075
|
-
|
|
13025
|
+
stat8 = existsSync18(filePath) ? statSync8(filePath) : null;
|
|
13076
13026
|
} catch {
|
|
13077
|
-
|
|
13027
|
+
stat8 = null;
|
|
13078
13028
|
}
|
|
13079
|
-
if (
|
|
13029
|
+
if (stat8?.isDirectory() && !reqPath.endsWith("/")) {
|
|
13080
13030
|
const search = new URL(c.req.url).search;
|
|
13081
13031
|
const target = `${reqPath}/${search}`;
|
|
13082
13032
|
console.log(
|
|
@@ -13084,22 +13034,22 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
13084
13034
|
);
|
|
13085
13035
|
return c.redirect(target, 301);
|
|
13086
13036
|
}
|
|
13087
|
-
if (
|
|
13088
|
-
filePath =
|
|
13037
|
+
if (stat8?.isDirectory()) {
|
|
13038
|
+
filePath = resolve20(filePath, "index.html");
|
|
13089
13039
|
}
|
|
13090
13040
|
if (!filePath.startsWith(rootDir + "/")) {
|
|
13091
13041
|
console.error(`[sites] path-traversal-rejected path=${reqPath} reason=escape status=403`);
|
|
13092
13042
|
return c.text("Forbidden", 403);
|
|
13093
13043
|
}
|
|
13094
|
-
if (!
|
|
13044
|
+
if (!existsSync18(filePath)) {
|
|
13095
13045
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
13096
13046
|
return c.text("Not found", 404);
|
|
13097
13047
|
}
|
|
13098
13048
|
let realPath;
|
|
13099
13049
|
let realRoot;
|
|
13100
13050
|
try {
|
|
13101
|
-
realPath =
|
|
13102
|
-
realRoot =
|
|
13051
|
+
realPath = realpathSync5(filePath);
|
|
13052
|
+
realRoot = realpathSync5(rootDir);
|
|
13103
13053
|
} catch {
|
|
13104
13054
|
console.error(`[sites] not-found path=${reqPath} status=404`);
|
|
13105
13055
|
return c.text("Not found", 404);
|
|
@@ -13110,7 +13060,7 @@ app36.get("/:rel{.*}", (c) => {
|
|
|
13110
13060
|
}
|
|
13111
13061
|
let body;
|
|
13112
13062
|
try {
|
|
13113
|
-
body =
|
|
13063
|
+
body = readFileSync17(realPath);
|
|
13114
13064
|
} catch (err) {
|
|
13115
13065
|
const code = err?.code;
|
|
13116
13066
|
if (code === "EISDIR") {
|
|
@@ -13146,7 +13096,7 @@ var sites_default = app36;
|
|
|
13146
13096
|
|
|
13147
13097
|
// app/lib/visitor-token.ts
|
|
13148
13098
|
import { createHmac, randomBytes, timingSafeEqual } from "crypto";
|
|
13149
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
13099
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync18, writeFileSync as writeFileSync6 } from "fs";
|
|
13150
13100
|
import { dirname as dirname4 } from "path";
|
|
13151
13101
|
var TOKEN_PREFIX = "v1.";
|
|
13152
13102
|
var SECRET_BYTES = 32;
|
|
@@ -13155,7 +13105,7 @@ var cachedSecret = null;
|
|
|
13155
13105
|
function getSecret() {
|
|
13156
13106
|
if (cachedSecret) return cachedSecret;
|
|
13157
13107
|
try {
|
|
13158
|
-
const hex2 =
|
|
13108
|
+
const hex2 = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
13159
13109
|
if (hex2.length === SECRET_BYTES * 2) {
|
|
13160
13110
|
cachedSecret = Buffer.from(hex2, "hex");
|
|
13161
13111
|
return cachedSecret;
|
|
@@ -13169,7 +13119,7 @@ function getSecret() {
|
|
|
13169
13119
|
console.log(`[visitor-token] secret minted path=${VISITOR_TOKEN_SECRET_FILE}`);
|
|
13170
13120
|
} catch {
|
|
13171
13121
|
}
|
|
13172
|
-
const hex =
|
|
13122
|
+
const hex = readFileSync18(VISITOR_TOKEN_SECRET_FILE, "utf-8").trim();
|
|
13173
13123
|
cachedSecret = Buffer.from(hex, "hex");
|
|
13174
13124
|
return cachedSecret;
|
|
13175
13125
|
}
|
|
@@ -13222,8 +13172,8 @@ var VISITOR_COOKIE_NAME = "mxy_v";
|
|
|
13222
13172
|
var VISITOR_COOKIE_MAX_AGE_SECONDS = Math.floor(DEFAULT_TTL_MS / 1e3);
|
|
13223
13173
|
|
|
13224
13174
|
// app/lib/brand-config.ts
|
|
13225
|
-
import { existsSync as
|
|
13226
|
-
import { join as
|
|
13175
|
+
import { existsSync as existsSync19, readFileSync as readFileSync19 } from "fs";
|
|
13176
|
+
import { join as join15 } from "path";
|
|
13227
13177
|
var cached2 = null;
|
|
13228
13178
|
var cachedAttempted = false;
|
|
13229
13179
|
function readBrandConfig() {
|
|
@@ -13231,10 +13181,10 @@ function readBrandConfig() {
|
|
|
13231
13181
|
cachedAttempted = true;
|
|
13232
13182
|
const platformRoot = process.env.MAXY_PLATFORM_ROOT;
|
|
13233
13183
|
if (!platformRoot) return null;
|
|
13234
|
-
const brandPath =
|
|
13235
|
-
if (!
|
|
13184
|
+
const brandPath = join15(platformRoot, "config", "brand.json");
|
|
13185
|
+
if (!existsSync19(brandPath)) return null;
|
|
13236
13186
|
try {
|
|
13237
|
-
cached2 = JSON.parse(
|
|
13187
|
+
cached2 = JSON.parse(readFileSync19(brandPath, "utf-8"));
|
|
13238
13188
|
return cached2;
|
|
13239
13189
|
} catch {
|
|
13240
13190
|
return null;
|
|
@@ -13802,6 +13752,109 @@ function startGraphHealthTimer() {
|
|
|
13802
13752
|
if (typeof timer.unref === "function") timer.unref();
|
|
13803
13753
|
}
|
|
13804
13754
|
|
|
13755
|
+
// app/lib/file-watcher.ts
|
|
13756
|
+
import * as fsp2 from "fs/promises";
|
|
13757
|
+
import { resolve as resolve21, sep as sep6 } from "path";
|
|
13758
|
+
var ACCOUNT_UUID_RE2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
13759
|
+
var DEFAULT_COALESCE_MS = 500;
|
|
13760
|
+
var ROOTS = ["uploads", "accounts"];
|
|
13761
|
+
function _routeEvent(rootName, filename) {
|
|
13762
|
+
if (!filename) return null;
|
|
13763
|
+
const segs = filename.split(sep6).filter(Boolean);
|
|
13764
|
+
if (segs.length < 2) return null;
|
|
13765
|
+
const accountId = segs[0];
|
|
13766
|
+
if (!ACCOUNT_UUID_RE2.test(accountId)) return null;
|
|
13767
|
+
return { accountId, relativePath: `${rootName}/${segs.join("/")}` };
|
|
13768
|
+
}
|
|
13769
|
+
async function startFileWatcher(opts = {}) {
|
|
13770
|
+
const dataRoot = opts.dataRoot ?? DATA_ROOT;
|
|
13771
|
+
const coalesceMs = opts.coalesceMs ?? DEFAULT_COALESCE_MS;
|
|
13772
|
+
const indexFn = opts.index ?? indexFile;
|
|
13773
|
+
const dropFn = opts.drop ?? dropFileIndex;
|
|
13774
|
+
for (const r of ROOTS) {
|
|
13775
|
+
try {
|
|
13776
|
+
await fsp2.mkdir(resolve21(dataRoot, r), { recursive: true });
|
|
13777
|
+
} catch (err) {
|
|
13778
|
+
console.error(
|
|
13779
|
+
`[file-watcher] start-failed root="${r}" err="${err.message}" \u2014 index will be maintained by the 5-min reconcile backstop only`
|
|
13780
|
+
);
|
|
13781
|
+
return { stop: async () => {
|
|
13782
|
+
} };
|
|
13783
|
+
}
|
|
13784
|
+
}
|
|
13785
|
+
const controller = new AbortController();
|
|
13786
|
+
const timers = /* @__PURE__ */ new Map();
|
|
13787
|
+
function schedule(relativePath, accountId) {
|
|
13788
|
+
const existing = timers.get(relativePath);
|
|
13789
|
+
if (existing) clearTimeout(existing);
|
|
13790
|
+
const t = setTimeout(() => {
|
|
13791
|
+
timers.delete(relativePath);
|
|
13792
|
+
void runHook(relativePath, accountId);
|
|
13793
|
+
}, coalesceMs);
|
|
13794
|
+
timers.set(relativePath, t);
|
|
13795
|
+
}
|
|
13796
|
+
async function runHook(relativePath, accountId) {
|
|
13797
|
+
const absolute = resolve21(dataRoot, relativePath);
|
|
13798
|
+
let exists = false;
|
|
13799
|
+
try {
|
|
13800
|
+
const st = await fsp2.stat(absolute);
|
|
13801
|
+
exists = st.isFile();
|
|
13802
|
+
} catch (err) {
|
|
13803
|
+
const code = err.code;
|
|
13804
|
+
if (code !== "ENOENT") {
|
|
13805
|
+
console.error(`[file-watcher] stat-error path="${relativePath}" err="${err.message}"`);
|
|
13806
|
+
}
|
|
13807
|
+
}
|
|
13808
|
+
try {
|
|
13809
|
+
if (exists) {
|
|
13810
|
+
await indexFn(accountId, relativePath);
|
|
13811
|
+
} else {
|
|
13812
|
+
await dropFn(accountId, relativePath);
|
|
13813
|
+
}
|
|
13814
|
+
} catch (err) {
|
|
13815
|
+
console.error(`[file-watcher] hook-failed path="${relativePath}" err="${err.message}"`);
|
|
13816
|
+
} finally {
|
|
13817
|
+
opts.onSettled?.(relativePath);
|
|
13818
|
+
}
|
|
13819
|
+
}
|
|
13820
|
+
async function watchRoot(rootName) {
|
|
13821
|
+
const absRoot = resolve21(dataRoot, rootName);
|
|
13822
|
+
try {
|
|
13823
|
+
const iter = fsp2.watch(absRoot, { recursive: true, signal: controller.signal });
|
|
13824
|
+
for await (const event of iter) {
|
|
13825
|
+
const routed = _routeEvent(rootName, event.filename);
|
|
13826
|
+
if (!routed) continue;
|
|
13827
|
+
schedule(routed.relativePath, routed.accountId);
|
|
13828
|
+
}
|
|
13829
|
+
} catch (err) {
|
|
13830
|
+
const code = err.code;
|
|
13831
|
+
const name = err.name;
|
|
13832
|
+
if (code === "ABORT_ERR" || name === "AbortError") return;
|
|
13833
|
+
console.error(`[file-watcher] loop-error root="${rootName}" err="${err.message}"`);
|
|
13834
|
+
}
|
|
13835
|
+
}
|
|
13836
|
+
for (const r of ROOTS) void watchRoot(r);
|
|
13837
|
+
const sigHandler = () => {
|
|
13838
|
+
void stop();
|
|
13839
|
+
};
|
|
13840
|
+
process.once("SIGTERM", sigHandler);
|
|
13841
|
+
process.once("SIGINT", sigHandler);
|
|
13842
|
+
let stopped = false;
|
|
13843
|
+
async function stop() {
|
|
13844
|
+
if (stopped) return;
|
|
13845
|
+
stopped = true;
|
|
13846
|
+
process.off("SIGTERM", sigHandler);
|
|
13847
|
+
process.off("SIGINT", sigHandler);
|
|
13848
|
+
controller.abort();
|
|
13849
|
+
for (const t of timers.values()) clearTimeout(t);
|
|
13850
|
+
timers.clear();
|
|
13851
|
+
}
|
|
13852
|
+
console.error(
|
|
13853
|
+
`[file-watcher] started roots=${ROOTS.join(",")} dataRoot="${dataRoot}" coalesce-ms=${coalesceMs}`
|
|
13854
|
+
);
|
|
13855
|
+
return { stop };
|
|
13856
|
+
}
|
|
13857
|
+
|
|
13805
13858
|
// app/lib/whatsapp/inbound/claude-bridge.ts
|
|
13806
13859
|
var TAG27 = "[whatsapp-adaptor]";
|
|
13807
13860
|
function whatsappTurnTimeoutMs() {
|
|
@@ -13863,8 +13916,8 @@ function broadcastAdminShutdown(reason) {
|
|
|
13863
13916
|
|
|
13864
13917
|
// ../lib/entitlement/src/index.ts
|
|
13865
13918
|
import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
|
|
13866
|
-
import { existsSync as
|
|
13867
|
-
import { resolve as
|
|
13919
|
+
import { existsSync as existsSync20, readFileSync as readFileSync20, statSync as statSync9 } from "fs";
|
|
13920
|
+
import { resolve as resolve22 } from "path";
|
|
13868
13921
|
|
|
13869
13922
|
// ../lib/entitlement/src/canonicalize.ts
|
|
13870
13923
|
function canonicalize(value) {
|
|
@@ -13899,7 +13952,7 @@ var PUBKEY_SHA256 = "8eee6bcb33545fd13b16d3199a5735ca5db5062834c7b49dfe4f23801d9
|
|
|
13899
13952
|
var GRACE_DAYS = 7;
|
|
13900
13953
|
var GRACE_MS = GRACE_DAYS * 24 * 60 * 60 * 1e3;
|
|
13901
13954
|
function pubkeyPath(brand) {
|
|
13902
|
-
return
|
|
13955
|
+
return resolve22(brand.platformRoot, "lib", "entitlement", "rubytech-pubkey.pem");
|
|
13903
13956
|
}
|
|
13904
13957
|
var memo = null;
|
|
13905
13958
|
function memoKey(mtimeMs, account) {
|
|
@@ -13911,12 +13964,12 @@ function resolveEntitlement(brand, account) {
|
|
|
13911
13964
|
if (brand.commercialMode !== true) {
|
|
13912
13965
|
return logResolved(implicitTrust(account), null);
|
|
13913
13966
|
}
|
|
13914
|
-
const entitlementPath =
|
|
13915
|
-
if (!
|
|
13967
|
+
const entitlementPath = resolve22(brand.configDir, "entitlement.json");
|
|
13968
|
+
if (!existsSync20(entitlementPath)) {
|
|
13916
13969
|
return logResolved(anonymousFallback("missing"), { reason: "missing" });
|
|
13917
13970
|
}
|
|
13918
|
-
const
|
|
13919
|
-
const key = memoKey(
|
|
13971
|
+
const stat8 = statSync9(entitlementPath);
|
|
13972
|
+
const key = memoKey(stat8.mtimeMs, account);
|
|
13920
13973
|
if (memo && memo.key === key) {
|
|
13921
13974
|
return memo.result;
|
|
13922
13975
|
}
|
|
@@ -13927,7 +13980,7 @@ function resolveEntitlement(brand, account) {
|
|
|
13927
13980
|
function verifyAndResolve(brand, entitlementPath, account) {
|
|
13928
13981
|
let pubkeyPem;
|
|
13929
13982
|
try {
|
|
13930
|
-
pubkeyPem =
|
|
13983
|
+
pubkeyPem = readFileSync20(pubkeyPath(brand), "utf-8");
|
|
13931
13984
|
} catch (err) {
|
|
13932
13985
|
return logResolved(anonymousFallback("pubkey-missing"), {
|
|
13933
13986
|
reason: "pubkey-missing"
|
|
@@ -13941,7 +13994,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
|
|
|
13941
13994
|
}
|
|
13942
13995
|
let envelope;
|
|
13943
13996
|
try {
|
|
13944
|
-
envelope = JSON.parse(
|
|
13997
|
+
envelope = JSON.parse(readFileSync20(entitlementPath, "utf-8"));
|
|
13945
13998
|
} catch {
|
|
13946
13999
|
return logResolved(anonymousFallback("malformed"), { reason: "malformed" });
|
|
13947
14000
|
}
|
|
@@ -14102,14 +14155,14 @@ function clientFrom(c) {
|
|
|
14102
14155
|
);
|
|
14103
14156
|
}
|
|
14104
14157
|
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
14105
|
-
var BRAND_JSON_PATH = PLATFORM_ROOT9 ?
|
|
14158
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT9 ? join16(PLATFORM_ROOT9, "config", "brand.json") : "";
|
|
14106
14159
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", marketingUrl: "https://getmaxy.com" };
|
|
14107
|
-
if (BRAND_JSON_PATH && !
|
|
14160
|
+
if (BRAND_JSON_PATH && !existsSync21(BRAND_JSON_PATH)) {
|
|
14108
14161
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|
|
14109
14162
|
}
|
|
14110
|
-
if (BRAND_JSON_PATH &&
|
|
14163
|
+
if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
|
|
14111
14164
|
try {
|
|
14112
|
-
const parsed = JSON.parse(
|
|
14165
|
+
const parsed = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
14113
14166
|
BRAND = { ...BRAND, ...parsed };
|
|
14114
14167
|
} catch (err) {
|
|
14115
14168
|
console.error(`[brand] Failed to parse brand.json: ${err.message}`);
|
|
@@ -14128,11 +14181,11 @@ var brandLoginOpts = {
|
|
|
14128
14181
|
bodyFont: BRAND.defaultFonts?.body,
|
|
14129
14182
|
logoContainsName: !!BRAND.logoContainsName
|
|
14130
14183
|
};
|
|
14131
|
-
var ALIAS_DOMAINS_PATH =
|
|
14184
|
+
var ALIAS_DOMAINS_PATH = join16(homedir2(), BRAND.configDir, "alias-domains.json");
|
|
14132
14185
|
function loadAliasDomains() {
|
|
14133
14186
|
try {
|
|
14134
|
-
if (!
|
|
14135
|
-
const parsed = JSON.parse(
|
|
14187
|
+
if (!existsSync21(ALIAS_DOMAINS_PATH)) return null;
|
|
14188
|
+
const parsed = JSON.parse(readFileSync21(ALIAS_DOMAINS_PATH, "utf-8"));
|
|
14136
14189
|
if (!Array.isArray(parsed)) {
|
|
14137
14190
|
console.error("[alias-domains] malformed alias-domains.json \u2014 expected array");
|
|
14138
14191
|
return null;
|
|
@@ -14496,20 +14549,20 @@ app40.get("/agent-assets/:slug/:filename", (c) => {
|
|
|
14496
14549
|
console.error(`[agent-assets] no-account slug=${slug} file=${filename}`);
|
|
14497
14550
|
return c.text("Not found", 404);
|
|
14498
14551
|
}
|
|
14499
|
-
const filePath =
|
|
14500
|
-
const expectedDir =
|
|
14552
|
+
const filePath = resolve23(account.accountDir, "agents", slug, "assets", filename);
|
|
14553
|
+
const expectedDir = resolve23(account.accountDir, "agents", slug, "assets");
|
|
14501
14554
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14502
14555
|
console.error(`[agent-assets] path-traversal-rejected slug=${slug} file=${filename}`);
|
|
14503
14556
|
return c.text("Forbidden", 403);
|
|
14504
14557
|
}
|
|
14505
|
-
if (!
|
|
14558
|
+
if (!existsSync21(filePath)) {
|
|
14506
14559
|
console.error(`[agent-assets] serve slug=${slug} file=${filename} status=404`);
|
|
14507
14560
|
return c.text("Not found", 404);
|
|
14508
14561
|
}
|
|
14509
14562
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
14510
14563
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
14511
14564
|
console.log(`[agent-assets] serve slug=${slug} file=${filename} status=200`);
|
|
14512
|
-
const body =
|
|
14565
|
+
const body = readFileSync21(filePath);
|
|
14513
14566
|
return c.body(body, 200, {
|
|
14514
14567
|
"Content-Type": contentType,
|
|
14515
14568
|
"Cache-Control": "public, max-age=3600"
|
|
@@ -14526,20 +14579,20 @@ app40.get("/generated/:filename", (c) => {
|
|
|
14526
14579
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14527
14580
|
return c.text("Not found", 404);
|
|
14528
14581
|
}
|
|
14529
|
-
const filePath =
|
|
14530
|
-
const expectedDir =
|
|
14582
|
+
const filePath = resolve23(account.accountDir, "generated", filename);
|
|
14583
|
+
const expectedDir = resolve23(account.accountDir, "generated");
|
|
14531
14584
|
if (!filePath.startsWith(expectedDir + "/")) {
|
|
14532
14585
|
console.error(`[generated] serve file=${filename} status=403`);
|
|
14533
14586
|
return c.text("Forbidden", 403);
|
|
14534
14587
|
}
|
|
14535
|
-
if (!
|
|
14588
|
+
if (!existsSync21(filePath)) {
|
|
14536
14589
|
console.error(`[generated] serve file=${filename} status=404`);
|
|
14537
14590
|
return c.text("Not found", 404);
|
|
14538
14591
|
}
|
|
14539
14592
|
const ext = "." + filename.split(".").pop()?.toLowerCase();
|
|
14540
14593
|
const contentType = IMAGE_MIME[ext] || "application/octet-stream";
|
|
14541
14594
|
console.log(`[generated] serve file=${filename} status=200`);
|
|
14542
|
-
const body =
|
|
14595
|
+
const body = readFileSync21(filePath);
|
|
14543
14596
|
return c.body(body, 200, {
|
|
14544
14597
|
"Content-Type": contentType,
|
|
14545
14598
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -14552,9 +14605,9 @@ app40.route("/v", visitor_consent_default);
|
|
|
14552
14605
|
var htmlCache = /* @__PURE__ */ new Map();
|
|
14553
14606
|
var brandLogoPath = "/brand/maxy-monochrome.png";
|
|
14554
14607
|
var brandIconPath = "/brand/maxy-monochrome.png";
|
|
14555
|
-
if (BRAND_JSON_PATH &&
|
|
14608
|
+
if (BRAND_JSON_PATH && existsSync21(BRAND_JSON_PATH)) {
|
|
14556
14609
|
try {
|
|
14557
|
-
const fullBrand = JSON.parse(
|
|
14610
|
+
const fullBrand = JSON.parse(readFileSync21(BRAND_JSON_PATH, "utf-8"));
|
|
14558
14611
|
if (fullBrand.assets?.logo) brandLogoPath = `/brand/${fullBrand.assets.logo}`;
|
|
14559
14612
|
brandIconPath = fullBrand.assets?.icon ? `/brand/${fullBrand.assets.icon}` : brandLogoPath;
|
|
14560
14613
|
} catch {
|
|
@@ -14571,9 +14624,9 @@ var brandScript = `<script>window.__BRAND__=${JSON.stringify({
|
|
|
14571
14624
|
function readInstalledVersion() {
|
|
14572
14625
|
try {
|
|
14573
14626
|
if (!PLATFORM_ROOT9) return "unknown";
|
|
14574
|
-
const versionFile =
|
|
14575
|
-
if (!
|
|
14576
|
-
const content =
|
|
14627
|
+
const versionFile = join16(PLATFORM_ROOT9, "config", `.${BRAND.hostname}-version`);
|
|
14628
|
+
if (!existsSync21(versionFile)) return "unknown";
|
|
14629
|
+
const content = readFileSync21(versionFile, "utf-8").trim();
|
|
14577
14630
|
return content || "unknown";
|
|
14578
14631
|
} catch {
|
|
14579
14632
|
return "unknown";
|
|
@@ -14614,7 +14667,7 @@ var clientErrorReporterScript = `<script>
|
|
|
14614
14667
|
function cachedHtml(file) {
|
|
14615
14668
|
let html = htmlCache.get(file);
|
|
14616
14669
|
if (!html) {
|
|
14617
|
-
html =
|
|
14670
|
+
html = readFileSync21(resolve23(process.cwd(), "public", file), "utf-8");
|
|
14618
14671
|
const productNameEsc = escapeHtml(BRAND.productName);
|
|
14619
14672
|
html = html.replace(/<title>([^<]*)<\/title>/, (_match, inner) => `<title>${escapeHtml(inner).replace(/Maxy/g, productNameEsc)}</title>`);
|
|
14620
14673
|
html = html.replace('href="/favicon.ico"', `href="${escapeHtml(brandFaviconPath)}"`);
|
|
@@ -14630,26 +14683,26 @@ ${clientErrorReporterScript}
|
|
|
14630
14683
|
}
|
|
14631
14684
|
var brandedHtmlCache = /* @__PURE__ */ new Map();
|
|
14632
14685
|
function loadBrandingCache(agentSlug) {
|
|
14633
|
-
const configDir2 =
|
|
14686
|
+
const configDir2 = join16(homedir2(), BRAND.configDir);
|
|
14634
14687
|
try {
|
|
14635
|
-
const accountJsonPath =
|
|
14636
|
-
if (!
|
|
14637
|
-
const account = JSON.parse(
|
|
14688
|
+
const accountJsonPath = join16(configDir2, "account.json");
|
|
14689
|
+
if (!existsSync21(accountJsonPath)) return null;
|
|
14690
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
14638
14691
|
const accountId = account.accountId;
|
|
14639
14692
|
if (!accountId) return null;
|
|
14640
|
-
const cachePath =
|
|
14641
|
-
if (!
|
|
14642
|
-
return JSON.parse(
|
|
14693
|
+
const cachePath = join16(configDir2, "branding-cache", accountId, `${agentSlug}.json`);
|
|
14694
|
+
if (!existsSync21(cachePath)) return null;
|
|
14695
|
+
return JSON.parse(readFileSync21(cachePath, "utf-8"));
|
|
14643
14696
|
} catch {
|
|
14644
14697
|
return null;
|
|
14645
14698
|
}
|
|
14646
14699
|
}
|
|
14647
14700
|
function resolveDefaultSlug() {
|
|
14648
14701
|
try {
|
|
14649
|
-
const configDir2 =
|
|
14650
|
-
const accountJsonPath =
|
|
14651
|
-
if (!
|
|
14652
|
-
const account = JSON.parse(
|
|
14702
|
+
const configDir2 = join16(homedir2(), BRAND.configDir);
|
|
14703
|
+
const accountJsonPath = join16(configDir2, "account.json");
|
|
14704
|
+
if (!existsSync21(accountJsonPath)) return null;
|
|
14705
|
+
const account = JSON.parse(readFileSync21(accountJsonPath, "utf-8"));
|
|
14653
14706
|
return account.defaultAgent || null;
|
|
14654
14707
|
} catch {
|
|
14655
14708
|
return null;
|
|
@@ -14722,7 +14775,7 @@ app40.use("/vnc-popout.html", logViewerFetch);
|
|
|
14722
14775
|
app40.get("/vnc-popout.html", (c) => {
|
|
14723
14776
|
let html = htmlCache.get("vnc-popout.html");
|
|
14724
14777
|
if (!html) {
|
|
14725
|
-
html =
|
|
14778
|
+
html = readFileSync21(resolve23(process.cwd(), "public", "vnc-popout.html"), "utf-8");
|
|
14726
14779
|
const name = escapeHtml(BRAND.productName);
|
|
14727
14780
|
html = html.replace("<title>Browser \u2014 Maxy</title>", `<title>${name}</title>`);
|
|
14728
14781
|
html = html.replace("</head>", ` ${brandScript}
|
|
@@ -14847,8 +14900,8 @@ try {
|
|
|
14847
14900
|
}
|
|
14848
14901
|
(async () => {
|
|
14849
14902
|
try {
|
|
14850
|
-
if (!
|
|
14851
|
-
const usersRaw =
|
|
14903
|
+
if (!existsSync21(USERS_FILE)) return;
|
|
14904
|
+
const usersRaw = readFileSync21(USERS_FILE, "utf-8").trim();
|
|
14852
14905
|
if (!usersRaw) return;
|
|
14853
14906
|
const users = JSON.parse(usersRaw);
|
|
14854
14907
|
const userId = users[0]?.userId;
|
|
@@ -14864,6 +14917,9 @@ try {
|
|
|
14864
14917
|
}
|
|
14865
14918
|
})();
|
|
14866
14919
|
startGraphHealthTimer();
|
|
14920
|
+
void startFileWatcher().catch((err) => {
|
|
14921
|
+
console.error(`[file-watcher] start-failed err="${err instanceof Error ? err.message : String(err)}"`);
|
|
14922
|
+
});
|
|
14867
14923
|
try {
|
|
14868
14924
|
const accounts = enumerateValidAccountIds(getAccountsDirFromEnv());
|
|
14869
14925
|
console.error(`[graph-health] account-enumeration accounts=${accounts.length}`);
|
|
@@ -14882,7 +14938,7 @@ try {
|
|
|
14882
14938
|
} catch (err) {
|
|
14883
14939
|
console.error(`[graph-health] account-enumeration unavailable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
14884
14940
|
}
|
|
14885
|
-
var configDirForWhatsApp =
|
|
14941
|
+
var configDirForWhatsApp = basename6(MAXY_DIR) || ".maxy";
|
|
14886
14942
|
var bootAccount = resolveAccount();
|
|
14887
14943
|
var bootAccountConfig = bootAccount?.config;
|
|
14888
14944
|
var bootPublicAgent = bootAccount ? resolvePublicAgent(bootAccount.accountDir, { accountId: bootAccount.accountId })?.slug ?? null : null;
|
|
@@ -14940,7 +14996,7 @@ if (bootAccountConfig?.whatsapp) {
|
|
|
14940
14996
|
}
|
|
14941
14997
|
init({
|
|
14942
14998
|
configDir: configDirForWhatsApp,
|
|
14943
|
-
platformRoot:
|
|
14999
|
+
platformRoot: resolve23(process.env.MAXY_PLATFORM_ROOT ?? join16(__dirname, "..")),
|
|
14944
15000
|
accountConfig: bootAccountConfig,
|
|
14945
15001
|
onMessage: async (msg) => {
|
|
14946
15002
|
if (process.env.WHATSAPP_PTY_BRIDGE_ENABLED === "true" && msg.text && !msg.isOwnerMirror) {
|